【免费下载链接】xerj
XERJ is the new way for AI to search data. Its autoindex capability activates agents to know your data without the token waste of grep and sed. One command indexes code, docs, logs and PDFs for search, RAG, security audits and agent memory, using 40x fewer tokens than grep. Elasticsearch compatible, so existing clients just work.
本篇技术指南讲解 XERJ 仓库中可复用的xerj-security-auditClaude Code Skill(见 skill/SKILL.md):把 XERJ 当作 Agent 的"第二大脑",对 PHP(或其他语言)代码库做覆盖保证(coverage-guaranteed)的白盒安全审计——用 tree-sitter AST 穷举每一个危险函数调用点、用 grep 对账证明零遗漏、再通过 XERJ 的 ES 兼容 API 索引与追踪,最终让 Agent 基于可引用的file:line证据输出"枚举了每个危险调用"的可辩护声明。读完本文,你将掌握整套"索引一次、查询—精读—推理"的审计循环,以及如何把它复用到你自己的代码栈。
该 Skill 由 XERJ 团队对真实 WordPress 核心(1,492 个 PHP 文件、约 61.9 万行)的一次可复现审计沉淀而来。审计的完整背景、量化对比与全部脚本位于 docs/case-studies/wordpress-security-audit/,本指南以 Skill 为骨架,结合这些源码与数据逐层展开。
一、这个 Skill 解决什么问题:审计的"覆盖声明"困境
任何安全审计都会面临一个无法回避的问题:"我们真的看完了每一个危险调用吗?"通读文件做不到——61.9 万行代码仅"加载"就要约 520 万 token,超出上下文窗口 26 倍,必须分块,分块即丢失跨文件调用关系;grep 也做不到——grep 是文件作用域的,它无法表达"同一函数里同时存在请求来源和未消毒的 SQL sink"这类跨文件、跨函数的问题。
xerj-security-audit 的答案是把 XERJ 变成 Agent 的检索与推理基座:
索引一次,然后**审讯(interrogate)**它——查询 → 只读查询指向的代码 → 推理 → 下一次查询,绝不整文件加载。
在 WordPress 核心上的实测对比(见 README.md):
| 维度 | Claude 通读全部文件 | XERJ 辅助审计(本文方法) |
|---|---|---|
| 通读一次核心所需 token | 约 5,200,000(仅加载) | 约 26,000(整个审计) |
| 能否放入一个上下文窗口 | 不能(约 26 倍超窗,必须分块) | 能(定向查询 + 只读被标记代码) |
| 跨文件调用可达性 | 分块即丢失(漏洞常跨文件) | 调用/钩子图完整保留 |
| 依据 / 幻觉 | 扫读 520 万 token → 靠猜 | 每条结论引用拉取的file:line代码 |
| 可复现性 | 不可(取决于读到了什么) | 可(脚本 + 查询精确复现) |
约199 倍更少的 token,且质量更高——因为由"图"决定读什么,Agent 精读每个候选而非扫读一个装不下的草堆。这与仓库核心描述一致:XERJ 的 autoindex 让 Agent 以比 grep 少 40 倍的 token 了解数据;而本 Skill 则进一步把"了解"升级为"带覆盖证明的审计"。
二、何时使用(When to use)
当用户提出以下三类请求时,应当触发本 Skill:
- "对这份代码 / 这个 WordPress 插件 / PHP 应用做带覆盖证明的安全审查";
- "找出危险调用 / sink,并证明我们没有漏掉任何一个";
- "运行 sink 普查 / 审计台账(audit ledger)"。
核心交付物是一句可辩护的覆盖声明(defensible coverage claim),而不是一句"我们读过代码了"的观感。
三、前置条件:状态并搭建环境
Skill 要求先明确以下前置条件并完成环境搭建:
- XERJ 正在运行,且以 ES 兼容协议监听
http://127.0.0.1:9200:xerj --insecure --data-dir ./data其中
--insecure关闭 TLS 与认证,--data-dir指定数据目录。这与 engine/xerj.default.toml 的默认配置一致:ES 兼容端口es_compat_port = 9200、默认绑定bind_address = "127.0.0.1"(仅本机可达,TLS 默认关闭时不会把数据暴露到网络上)。 - 安装 tree-sitter 语法(PHP 及目标语言的语法包):
pip install tree-sitter tree-sitter-php其他语言见 docs/examples/ast-vuln-graph/requirements.txt。
- 目标源码树在磁盘上(例如 WordPress 核心的完整 checkout)。
安装为可复用 Skill 的方式见 skill/README.md:
mkdir -p ~/.claude/skills/xerj-security-audit cp SKILL.md ~/.claude/skills/xerj-security-audit/SKILL.md cp -r ../sink-census ~/.claude/skills/xerj-security-audit/之后即可在 Claude Code 中调用/xerj-security-audit,或直接说"对目标代码库运行 XERJ 安全审计"。
四、主循环:六步(按顺序执行,每步如实汇报)
整套工作流是一个有严格顺序的循环,任何一步都不允许跳过:
1. 绘制危险函数地图(知识层)
使用 php_dangerous_functions.json——275 个 PHP 内建函数/语法结构、覆盖 28 个类别的完整地图,每个条目都标注了漏洞类别(vuln class)和与污点相关的参数(taint-relevant argument)。类别包括:命令/代码执行(command exec、code eval)、unserialize反序列化、include 加载器、文件读写删、SSRF、SQL 驱动、XXE、回调调用(dynamic callables)、变量注入、反射调用、弱加密/弱随机、类型混淆(type-juggling)等。
该 JSON 中每个函数用kind区分是普通函数调用(call)、方法(method)还是语言构造(construct),并给出危险参数下标(arg)、'*'(任意参数)、'recv'(接收者即危险点)、'self'(语言构造操作数)或'cb:N'(第 N 个参数是可调用对象,构成二阶 RCE)——这个 taint-arg 标注是后续污点分析的关键。可读版本见 PHP-DANGEROUS-FUNCTIONS.md(扩展至 287 个函数、30 个类别)。
关键认知:这份地图是"数据,不是代码"。换框架/换语言时,扩展这份 JSON(或新增一份语言语法 + 平行目录)即可,普查、证明、富化循环本身完全与语言无关。
2. 用 AST 普查每一个调用点
python3 sink_census.py <src>sink_census.py 用 tree-sitter 解析每个文件,记录每一个已登记函数的调用点。源码中可以看到它的节点处理逻辑:
function_call_expression——普通函数调用(exec(...)、\fopen(...)带命名空间);member_call_expression/scoped_call_expression——方法调用(->loadXML());- 语言构造节点
CONSTRUCT_NODES:echo_statement、include_expression、require_once_expression、print_intrinsic、shell_command_expression(反引号),并映射到漏洞类别(echo/print→XSS,反引号→RCE-command,include/require系列→LFI-RFI); object_creation_expression构造函数 sink(CTOR_SINKS):new ReflectionFunction(动态调用)、new SoapClient(SSRF)、new SplFileObject(文件读)、new SimpleXMLElement(XXE)、new Phar(反序列化)、new Imagick(图片 RCE/SSRF)、new ZipArchive(zip-slip 路径穿越)等;exit/die作为语言构造单独处理。
tree-sitter 能捕获真实调用,无论格式、换行或命名空间如何——这是 grep 做不到的(docs/examples/ast-vuln-graph/README.md的实测:phply 对真实 WordPress 核心只有约 63% 解析率,tree-sitter-php 达到100%)。
2b. 普查危险"模式"(sink 清单覆盖不到的类别)
python3 pattern_census.py <src>pattern_census.py 用 AST 检测非函数型漏洞,写入wppatterns索引,包括:宽松==/魔法哈希(timing-compare秘密比较、loose-eq)、非严格in_array、strcmp遇数组(?login[]=攻击)、@错误抑制、可变变量、不安全的setcookie(缺 HttpOnly/Secure/SameSite)、基于请求值的switch、Host 头信任、ORDER BY 注入。源码中还体现了正则辅助检测(ORDER BY/LIMIT 与$_同现、$_SERVER['HTTP_HOST']等)。
无法用普查程序判定的语义类(SQL 截断、字符集 SQLi、二阶注入、phar、TOCTOU、上传即执行、is_numeric绕过、正则锚点、会话固定、错误上下文 XSS)在 php_dangerous_patterns.json 中逐一登记,给出检测指引(ast/regex/query/manual四种检测方式),由推理扫过。完整参考见 PHP-SECURITY-GUIDE.md——每个函数和每个模式都映射到攻击手法 + 检测方法 + 安全写法,这是"我们认识所有危险函数和模式"这句声明的诚实依据。
3. 证明覆盖(保证环节——不可跳过)
普查必须以可证明的零缺口收尾。sink_census.py把 grep 找到的每一个出现与 AST 逐一对账,并以 AST 的字符串/注释/内联 HTML 节点区间作为"真值预言机"(oracle)来判断某个 grep 命中是否真的是调用。一次干净运行的输出是:
UNEXPLAINED: 0 => coverage PROVEN (0 gaps)在 WordPress 核心上的实测(见 COVERAGE-AUDIT.md):
| 项目 | 数量 |
|---|---|
| AST 调用点 | 11,191(其中 10,778 为 AST 调用) |
| grep 出现 | 12,214 |
| 被证明"非调用" | 1,436(字符串/注释/内联 HTML/函数定义/他人对象的方法/变量名/已在 AST 中的命名空间调用) |
| UNEXPLAINED 残留 | 0 |
对账逻辑在sink_census.py的classify_nonast()中逐条落地:注释与字符串剔除、函数定义识别、->fn(/::fn(判定为"方法而非内建函数"、变量名$echo识别、命名空间调用归入 AST 已覆盖等。如果UNEXPLAINED > 0,每一个残留都必须解决——它要么是提取器漏掉的真实调用(修复目录或节点处理),要么是已被证明的非调用。绝不能在残留未解释的情况下报告覆盖。
4. 索引 + 分诊(XERJwpsinks索引)
普查把每个调用点批量索引为一个文档(file, line, fn, kind, class, arg),sink_census.py底部通过DELETE/PUT wpsinks建索引(keyword 字段file/fn/kind/class、integer 字段line、text 字段arg,并预留reachable/guarded/severity/ai_verdict/enriched富化字段),再用_bulk批量写入。风险画像现在是一条聚合查询的事(WordPress 核心实测):
| 类别 | 调用点数 | 类别 | 调用点数 |
|---|---|---|---|
XSS(echo/print) | 3,799 | 文件写入 | 178 |
LFI/RFI(include/require) | 1,385 | 变量注入 | 166 |
| 类型混淆认证绕过 | 1,240 | 弱加密 | 142 |
XSS-or-info(printf/var_dump) | 1,034 | SSRF | 107 |
| 动态回调(二阶 RCE) | 954 | 文件删除 | 61 |
| ReDoS/ereg | 709 | XXE | 26 |
RCE-code(eval/assert) | 467 | 反序列化 | 13 |
| 文件读取 | 328 | RCE-command | 11 |
| SQL 驱动 | 200 | 动态调用(反射) | 6 |
稀有类别才是重点:整个核心只有11个命令执行、13个反序列化 sink——现在你可以保证自己看完了每一个。大类别(XSS/输出、类型混淆、回调)则交给污点图 + 授权图决定"可达性"。
5. 用 AI 富化成台账(enrich_pipeline.py)
python3 enrich_pipeline.pyenrich_pipeline.py 是嵌入管线的对偶——区别在于:不是model.encode产出向量,而是AI Agent 产出推理式的安全判定并写回 XERJ。对每个高危位点:读取参数与包围代码,判定reachable(request/cache/trusted/literal 四档来源)、guarded(哪道防御:escapeshellarg、HMAC、is_serialized、allow-list、字面量)、severity,以及一条verdict,写回wpsinks。
脚本里内置了对 WordPress 核心高危险类别的完整人工判定表(例如class-snoopy.php:1018的 curl 参数注入、class-wp-image-editor-imagick.php:158的 ImageTragick 面、SimplePie 各缓存后端的反序列化隐患点)。稀有类别 100% 富化,长尾排队:实测 RCE-command 11/11、反序列化 13/13 全部带完整判定,其余高危类别带类别 + 严重度进入队列。富化跨轮次累积——每条已审判定都是下一次审计与 CI 可直接复用的存储结论。
5b. 通过 API 追踪任意 sink(trace_sink.py)
python3 trace_sink.py <file> <line>trace_sink.py完全不访问本地文件,纯通过 ES 兼容 API 做四段 join:
wpsinks -> 该 sink + AI 富化(class、arg、reachable、guarded、verdict) wpaudit -> 包围它的函数(sources / sinks / sanitizer / calls) wpauthz -> 该函数的 cap/nonce 守卫 wpaudit -> 反向调用者(谁调用它)+ 他们是否读取请求来源当"某函数同时存在请求来源与未消毒 sink"时,verdict 会标记为SOURCE→SINK 候选。VERIFY-AND-TRACE.md 给出的实战例子是 REST widget-instance 的unserialize(class-wp-rest-widgets-controller.php:589):追踪正确揭示save_widget()读取$_POST/$_REQUEST且无函数内消毒、无对象作用域 cap,随后由人工读取真实守卫(show_instance_in_rest开关 + widget hash)来确认或排除。两点实现细节:反向调用者扫描_source的calls数组(因为 XERJ 当前存在"keyword 数组上的term只匹配首元素"的引擎缺陷),并把load/query这类常见方法名限制在同一文件以避免类名冲突——这两点都写进了输出,绝不隐瞒。
6. 推理真实漏洞(普查看不见的部分)
Sink 覆盖 ≠ 全部覆盖。还需结合案例研究中的其他检测器扫过:
- 对象作用域授权 / IDOR:
wp_authz_graph.py、wp_checkuse_idor.py(docs/examples/ast-vuln-graph/); - 校验器完整性:
wp_ssrf_ranges.py——不完整的 allow/deny 清单,例如 SSRF 校验漏掉169.254(云元数据); - 消毒器组合顺序:
wp_compose_index.py。
每个候选先读验证,再执行验证(把流程移植到可运行语言)。追踪到真实输入的可达性。
WordPress 核心审计中正是这一步发现了唯一一个真实弱点——wp_http_validate_url放行169.254.169.254(云元数据)导致的 SSRF(详见 FINDINGS.md):模式扫描器只能看到"有一个 IP 检查",而缺陷在于清单不完整——Agent 通过阅读区间覆盖发现169.254缺席,再把它编译成可复用的wp_ssrf_ranges.py检测器,并用 verify_ssrf.py 逐行移植执行验证(127.0.0.1/10.x/192.168.x被拒绝,169.254.169.254被放行),最后追溯到未认证入口pingback_ping。这是"读"而非"模式匹配"才能发现的语义完整性弱点。
6b. 过程间污点分析(taint_analysis.py)
python3 taint_analysis.pytaint_analysis.py 在 AST 导出后,基于 XERJ 调用图做source→sink 污点分析:每个读取请求来源的函数,沿调用图走到它能到达的每个危险 sink,路径上无消毒器即记为一条流,按严重度排序、每条流附完整路径(source -> f1 -> f2 -> ... -> sink)。在 WordPress 核心上:约 1.2 万函数中,70 条候选流(deser 1、sql 11、lfi 30、xss 34)。详细说明与对顶部流的诚实验证(真实候选 vs 误报)见 sink-census/TAINT-ANALYSIS.md。
定性必须诚实:这是高召回的可达性分诊,不是精确的逐参数数据流——SQL 流可能终止于WP_Query(安全)或intval守卫的 sink,每条仍需人工阅读确认。它的价值在于把 1.2 万函数压缩到 70 个可读候选。
6c. POP-gadget 狩猎(gadget_hunt.py)
python3 gadget_hunt.pygadget_hunt.py 把每个魔法方法(__wakeup/__destruct/__toString/__call/...)沿调用图 DFS 追到危险调用——即反序列化的 gadget 攻击面,并单独标记自动触发的(unserialize/free/print 会触发的__wakeup/__unserialize/__destruct/__toString)为"活 gadget"。WordPress 核心结果(见 GADGET-CHAINS.md):
| 项 | 数量 |
|---|---|
| 核心中的魔法方法 | 131 |
| 自动触发且到达 sink | 0 |
| 任何魔法方法到达危险调用 | 2(均为__callStatic弃用提示,读到即证伪) |
结论是干净的正向结果:WordPress 核心没有反序列化 POP-gadget 链,即使存在任意对象注入也没有核心魔法方法能升级为 RCE/写文件/SSRF(生态中的活 gadget 风险来自 vendored 库和插件)。后续按方法逐个体扫描修正了首轮byname名称冲突导致的漏报:44 个魔法方法体内含危险调用,唯一真正的 gadget 形状WP_HTML_Token::__destruct → call_user_func($this->on_destroy, ...)已被__wakeup(){ throw ... }在真实 PHP 7.4/8.0/8.3 上经验证地抑制。这个"修正记录"本身就是审计可信度的示范。
五、诚实规则(让结果可信的六条铁律)
- 精度来自图与事实,全文检索只用于导航——对原始代码做全文检索做安全判断太嘈杂。
- 先读验证、再执行验证(无运行环境则移植到可运行语言)。不得凭对框架的记忆下结论,必须引用拉取的
file:line。 - 称得上 bug 之前,先追踪到攻击者可控输入的可达性。
- 不制造发现——干净的否定(clean negative)是有效结果。
- 上报基座/引擎缺陷:不可能出现的查询结果意味着工具错了,不是现实错了。已知 XERJ 注意事项:keyword数组上的
term只匹配首元素——反向调用图请在代码中遍历_source;布尔term按字符串匹配——请用"true"。这条缺陷的根因、证明与修复见 docs/research/xerj-keyword-array-term-fix.md。 - 精确陈述覆盖声明:"目录中每个危险函数的每次调用都被枚举,且经过零缺口证明的普查"——而不是"我们读完了代码"。
六、这份工作流换来的覆盖声明
完整审计完成后,你可以给出如下精确声明:
目标代码库中每个已知危险内建函数的调用(N 个位点、M 个函数、K 个文件)都已被零缺口证明的 AST 普查枚举、按漏洞类别索引并分诊;命令执行 / 反序列化 / SQL sink 的100%都带有人工/ AI 判定结论。
这就是"我们读完了代码"永远给不出的可辩护声明。VERIFY-AND-TRACE.md 提供了验证命令,证明这些数据在 XERJ 里是活的且正确的:
curl -s 'http://127.0.0.1:9200/_cat/indices?h=index,docs.count' | grep '^wp'| 索引 | 文档数 | 内容 |
|---|---|---|
wpsinks | 11,191 | 每个危险调用位点 + AI 富化 |
wpaudit | 11,990 | 每个函数:代码 + 污点事实(sources/sinks/calls) |
wpauthz | 11,990 | 每个函数的 cap/nonce 参数形态 + 调用边 |
wphooks | 1,343 | 钩子注册(未认证入口点) |
wpcompose | 2,445 | 消毒器序列指纹 |
七、复用到另一个仓库
- 把
docs/case-studies/wordpress-security-audit/sink-census/*连同本 Skill 复制到目标仓库(或~/.claude/skills/); - 把
sink_census.py指向你的源码树; - 为你的技术栈扩展
php_dangerous_functions.json(或加一份语言语法 + 平行目录)。
证明步骤与富化循环完全与语言无关。多语言基座已具备:docs/examples/ast-vuln-graph/scan_multilang.py用同一 tree-sitter 引擎覆盖 PHP/Python/Go/Rust/Java,每种语言的 sources/sinks/sanitizers 都在LANGS配置里——加框架 = 加一行配置,不改引擎。换语言时还可参考 PLAYBOOK.md 中面向任意代码库的主提示词与分阶段查询(未认证攻击面、请求来源可达的注入、对象作用域授权、校验器完整性、消毒器组合),以及 IMPROVEMENTS.md 中的改进路线图(AST 感知的 autoindex、findings-as-index、MCP 原生审计、CI 模式)。
八、诚实的边界(为什么这些数字可信)
最后,回到这个案例研究反复强调的诚实边界,它们同样约束本 Skill 的使用:
- XERJ 不"发现"漏洞——AST 污点/授权模型产生候选,XERJ 负责存储与查询;直接把它指向原始文件分块,其安全价值会大幅蒸发;
- 召回受模型约束——文件作用域来源或经
$GLOBALS清洗的来源不可见;grep 有完美的字面召回和极差的精度,本方法用精度和 token 换召回; - 精度检测器有已知的误报来源(接收者类型 sink、自作用域写入、多态 cap)——每个候选仍需人工/AI 精读,价值在于候选少到全部可读。
把这条循环指向你的代码,为你的框架补充事实清单,把学到的每个不变量编译成指纹——下一次审计就能免费查询它。
【免费下载链接】xerj
XERJ is the new way for AI to search data. Its autoindex capability activates agents to know your data without the token waste of grep and sed. One command indexes code, docs, logs and PDFs for search, RAG, security audits and agent memory, using 40x fewer tokens than grep. Elasticsearch compatible, so existing clients just work.
相关推荐
XERJ 驱动零遗漏白盒审计:用 AST 危险函数普查证明 WordPress 安全审查覆盖度
XERJ 驱动零遗漏白盒审计:用 AST 危险函数普查证明 WordPress 安全审查覆盖度 这篇技术指南基于 COVERAGE AUDIT.md https
XERJ 白盒安全审计技能(xerj-security-audit):用覆盖率可证明的 AST 普查 + 索引问答循环审计 PHP 代码库
XERJ 白盒安全审计技能(xerj security audit):用覆盖率可证明的 AST 普查 + 索引问答循环审计 PHP 代码库 导读 xerj se
用 XERJ 构建可证明零遗漏的 PHP 白盒安全审计:sink 普查与危险模式双轴完全指南
用 XERJ 构建可证明零遗漏的 PHP 白盒安全审计:sink 普查与危险模式双轴完全指南 本指南围绕 xerj security audit 技能(源于对真
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考