CASE STUDY · WORDPRESS SECURITY AUDIT

619K LINES AUDITED.
~26K TOKENS SPENT.

An AI agent security-audited real WordPress core — 1,492 PHP files, ~619k lines — for ~26,000 tokens instead of the ~5,200,000 it costs just to load the code once. A queryable code graph in XERJ decided what deserved reading; the agent read only candidates. Per-phase measured costs, published end to end.

FOR·SECURITY ENGINEERS·AI-AGENT BUILDERS·PLATFORM TEAMS
MEASURED · WORDPRESS CORE · 1,492 FILES / ~619K LINES
XERJ-ASSISTED AUDIT READ EVERY FILE
Tokens, entire audit ~26,000 ~5,200,000 ~199×

TOKEN ACCOUNTING: SUMMED PER-PHASE MEASURED COSTS VS THE READ-EVERYTHING BASELINE · NOT A LATENCY BENCHMARK · SOURCES IN docs/case-studies/wordpress-security-audit →

THE CONTEXT-WINDOW WALL

THE XERJ ANSWER

~3.6 s
TO INDEX THE CODEBASE
11,990 functions + 1,343 hooks · 100% tree-sitter-php parse rate
2 / 1,343
HOOKS UNAUTHENTICATED
the entire unauth entry surface, answered by one query
0
MISSING-CAPABILITY IDOR
226 handlers swept: 95 AJAX + 90 REST + 41 wp-admin
MEASURED · SQL-INJECTION TRIAGE · REPRODUCE.md STEP 2
XERJ QUERY GREP
Must read after triage 4 functions 51 whole files

ONE BOOL-FILTER QUERY VS grep · UNITS DIFFER: FUNCTION-LEVEL CANDIDATES VS WHOLE FILES · GREP'S LITERAL RECALL IS PERFECT

ASK FOR THE BUG SHAPE.

Locate the gatekeepers, read their real implementations, compile the correct-vs-buggy shape into a query, and pull back only the violators. Precision comes from reading; scale comes from the index.

INDEX ONCE · ~3.6 S · 100% PARSE RATE
$ python3 wp_audit_index.py   ./wordpress   # 11,990 functions + 1,343 hooks
$ python3 wp_authz_graph.py   ./wordpress   # capability/nonce shapes + call edges
$ python3 wp_compose_index.py ./wordpress   # sanitizer-sequence fingerprints
ONE QUERY · 11,990 FUNCTIONS → 4 CANDIDATES
$ curl -s "$XERJ/wpaudit/_search" -H 'Content-Type: application/json' \
  -d '{"query":{"bool":{"filter":[
        {"term":{"has_source":"true"}},
        {"term":{"sinks":"sql"}},
        {"term":{"sanit":"false"}}]}}}'
# a request source + SQL sink in the same function, no sanitizer between
THE UNAUTH SURFACE · ONE REQUEST
$ curl -s "$XERJ/wphooks/_search" -H 'Content-Type: application/json' \
  -d '{"query":{"term":{"unauth":true}},"_source":["hook","callback"]}'
# 2 of 1,343 hooks

THE CLEAN RESULT IS THE RESULT.

Core came back hardened: 0 missing-capability IDOR across 226 handlers, 0 auto-triggered deserialization gadgets. The one real weakness: wp_http_validate_url does not block 169.254.169.254 (cloud metadata), reachable unauthenticated via pingback_ping. A known-class limitation, not a novel 0-day.

WHAT THIS DOES NOT SHOW.

01
XERJ DOES NOT FIND BUGS.
The AST taint/authz model produces the candidates; XERJ stores and queries them. Pointed at raw file chunks instead of facts, the security value largely evaporates.
02
RECALL IS MODEL-BOUNDED.
A source at file scope, or laundered through $GLOBALS, is invisible to the graph. The trade is recall for precision and tokens — grep counter-examples are published in the walkthrough.
03
EVERY CANDIDATE STILL NEEDS A READ.
The detectors have known false-positive drivers — receiver-typed sinks, self-scoped writes, polymorphic capabilities. The value is that survivors are few enough to read them all.
04
THE AUDIT AUDITED XERJ TOO.
Two real engine bugs surfaced mid-run — term over keyword arrays, boolean term matching. Conclusions were recomputed over pulled _source, so they hold; both are published, and the keyword-array fix landed as a PR.

READ THE WALKTHROUGH.

The research journals, reproduction scripts, and verification harness are all published. The method ships as a copyable Claude Code skill — the taint model is data, not code, so it retargets to your stack.

OPEN THE WALKTHROUGH GET THE SKILL
READY?·REQUEST ACCESS

AUDIT YOUR
OWN CODE.

The skill runs the same audit — index, triage queries, verification prompts — on your codebase. Want a walkthrough with your team? Leave an email.

We only use this email to send you the binary. Ever. ✓ THANKS. CHECK YOUR INBOX WITHIN 24 HOURS.