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.