XERJ 漏洞发现研究:AST 污点图 + XERJ 索引的跨文件 AI 代码审计方法
【免费下载链接】xerjXERJ 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.项目地址https://gitcode.com/GitHub_Trending/xe/xerj点击查看免费下载本文基于 XERJ 仓库中的研究文档 ast-graph-vuln-detection.md完整还原其AST → 污点图 → XERJ 索引 → AI 复核三层漏洞发现架构为什么逐文件 grep/单文件规则扫描会漏掉真实漏洞如何用每语言的污点配置表在 PHP、Python、Go、Rust、Java 五种语言上复用同一个 tree-sitter 引擎以及如何在真实 WordPress 核心1,492 个文件、约 619k 行上把 5.2M token 的全量加载压缩为 129 条可复核的候选路径。读完本文你可以理解并复现这套精确度来自 AST、导航来自检索、判断来自 AI的分工方案。一、问题AI 擅长判断不擅长规模和跨文件追踪文档的开篇把 AI 做漏洞发现的能力边界讲得很清楚AI 判断某段代码是否可利用是强项但漏洞发现还有两个 AI 单独无法解决的短板Scale规模。一个真实 WordPress 安装core 插件有 10 万行以上代码装不进任何上下文窗口全部读进来找 bug既不可能在部分可行的场景下也非常昂贵。Reach跨文件可达性。真正有价值的漏洞多为**过程间interprocedural**漏洞请求值在一个函数里被读取经过一个 helper 传递最终到达另一个文件里的$wpdb查询。逐文件扫描grep或单文件 semgrep 规则在源点和汇点分属两个文件时会把它们看作毫无关联。此外它也无法推理缺失属性——比如一个有状态变更的管理端 handler 完全没有 nonce 校验CSRF。于是两个朴素选项都失败全量加载不 scalegrep/逐文件匹配会漏掉真实漏洞同时淹没在误报里每一个$wpdb调用、每一个$_GET都会命中。二、三层架构把精确度、导航和判断拆开研究文档给出的核心思路是把精确度precision与导航navigation分离让每一层只做自己擅长的事。原文档中的架构图如下PHP source │ parse (phply / tree-sitter) ▼ AST ──► taint GRAPH ← precision: interprocedural source→sink, │ (functions, reachability, sanitizer-on-path, nonce-absence │ call edges, │ sources/sinks/ │ sanitizers, │ hook entry points) ▼ findings function facts ──► XERJ index ← navigation: FTS / semantic │ search keyword filters ▼ (noprivtrue, sinkssql, …) AI reviews ONLY the code on flagged paths ← judgement: confirm exploitability, cite file:line三层各自的职责AST → 污点图精确度。解析每个文件按函数记录它读取的请求源$_GET/$_POST/$_REQUEST/…、它命中的危险汇点$wpdb-query、echo、include、unserialize、状态变更如update_option、路径上存在的净化函数esc_html、sanitize_*、$wpdb-prepare、wp_verify_nonce以及它的调用边。然后从每个带源的函数出发在调用图上做遍历如果一个汇点被到达、且路径上没有任何净化函数就是一个 finding——跨越函数与文件边界。Hook 接线add_action(wp_ajax_nopriv_…)用于标记未认证入口点。XERJ 索引导航。把 findings 和函数事实存为文档——事实文本可被 FTS/语义搜索图属性nopriv、sinks、entry可被关键字过滤。AI Agent 现在可以用自然语言问未认证 handler 中到达 SQL 查询的函数或者直接过滤nopriv:true AND sinks:sql。AI 复核判断。AI 只读被标记路径上的代码确认可利用性并给出file:line引用。三、源码里的污点模型数据不是代码理解这套架构最快的方式是看原型实现。两个提取器都在 docs/examples/ast-vuln-graph/ 下extract_ast.py基于phply的 PHP/WordPress 提取器污点模型最丰富hook 图、nonce 缺失 CSRF、路径净化检测scan_multilang.py基于 tree-sitter 的多语言引擎一个引擎覆盖 PHP/Python/Go/Rust/Java靠每语言的污点配置驱动。3.1 WordPress 污点清单extract_ast.py在 extract_ast.py 中整个 WordPress 污点模型就是五张名单SRC {$_GET,$_POST,$_REQUEST,$_COOKIE,$_SERVER,$_FILES} SQL {query,get_results,get_var,get_row,get_col} DANGER {unserialize,eval,exec,system,shell_exec,passthru,assert,include,require} STATE {update_option,delete_option,add_option,wp_insert_post, wp_update_post,wp_delete_post,update_user_meta,add_user_meta} SANIT {esc_html,esc_attr,esc_url,sanitize_text_field,sanitize_key,absint, intval,wp_verify_nonce,check_admin_referer,wp_kses,prepare}对应关系很直白SRC是污点源analyse_body里匹配Variable节点SQL/DANGER与echo/include语句构成各类汇点sql / xss / lfi / 代码执行等STATE标记状态变更用于 CSRF 判定SANIT是净化函数含$wpdb-prepare的prepare方法名。3.2 过程间污点遍历一次 DFS 找出跨文件源→汇核心算法在 extract_ast.py从每个带源的函数出发用显式栈做深度优先搜索把路径上所有净化函数并入san集合当到达一个含汇点的函数且san为空时记为一条 findingdef dfs(start): stack[(start,[start],set(funcs[start][sanitizers]))] seenset() while stack: cur,path,sanstack.pop() if cur in seen: continue seen.add(cur) ffuncs[cur] if f[sinks] and not san: findings.append({sink_type:f[sinks], sink_func:cur, sink_file:f[file], path:path, source_func:path[0], unauth: funcs[path[0]].get(nopriv, False)}) for cal in f[calls]: stack.append((cal, path[cal], san|set(funcs[cal][sanitizers]))) for name,f in funcs.items(): if f[sources]: # a taint source starts a path dfs(name)注意几个实现细节它们直接决定了误报率调用边先解析为用户函数extract_ast.py 中f[calls] [c for c in f[calls] if c in funcs]避免把系统函数当调用边nopriv标记随路径起点传递finding 的unauth字段取自路径起点函数是否挂在wp_ajax_nopriv_*hook 上hook 信息来自对add_action/add_filter调用字面量参数的解析finding 最后按(source_func, sink_func, sink_type)去重。3.3 缺失属性检测nonce 缺失的 CSRF单看污点流找不到 CSRF——它的特征是该有而没有。extract_ast.py 的做法是对每个 entry handlerhook 注册的回调计算其可达函数集合若集合内存在state类汇点、且整条可达路径上没有任何wp_verify_nonce/check_admin_referer即报一条csrffindingdoes_state any(state in funcs[c][sinks] for c in sub) has_nonce any((wp_verify_nonce in funcs[c][sanitizers] or check_admin_referer in funcs[c][sanitizers]) for c in sub) if does_state and not has_nonce: findings.append({sink_type:[csrf], ...})四、验证66 文件的 WordPress 插件6/6 命中文档强调所有数字都可复现入口是 docs/examples/ast-vuln-graph/。make_demo_plugin.py 生成一个真实感插件66 个 PHP 文件中埋了 6 个漏洞2 个 SQLi——其中一个未认证且跨文件、1 个跨文件 XSS、1 个 LFI、1 个不安全反序列化、1 个缺 nonce 的 CSRF其余约 60 个是良性 helper。运行方式摘自 READMEpip install phply python3 make_demo_plugin.py ./acme-plugin # 6 个植入漏洞混在约 60 个良性文件中 python3 extract_ast.py ./acme-plugin # AST - 污点图 - findings原文档给出的验证结果表metricresultplanted vulnerabilities6found by the AST graph6 / 6— of whichinterprocedural(source and sink in different files)2 (SQLi, XSS)— of whichunauthenticated-critical(nopriv hook → SQL sink)1— of whichabsence property(state change, no nonce)1 (CSRF)tokens to review the flagged paths~412tokens to load the whole plugin~5,491reduction~13×且随代码库增长而无界放大其中最关键的一条 finding——acme_ajax_search → acme_find_items [UNAUTH]——需要三张图同时工作hook 图入口是wp_ajax_nopriv因此未认证、调用图源和汇在不同文件、污点$_GET值无prepare/净化地到达$wpdb-get_results。植入的代码本身就说明了跨文件形态见 make_demo_plugin.py// includes/search.php function acme_ajax_search() { $term $_GET[term]; // taint source return acme_find_items($term); // flows to db.php } // includes/db.php function acme_find_items($needle) { global $wpdb; return $wpdb-get_results(SELECT * FROM {$wpdb-prefix}items WHERE name $needle); }单文件工具无法产生这条结果因为源search.php的$_GET和汇db.php的get_results不在同一个函数里。塑造架构的那次失败FTS 过原始代码是有噪声的研究文档专门记录了塑造架构的那个发现第一版实现直接把函数原文建索引让 AI 用 FTS 查询。问echo 请求输入且未转义时FTS 返回了两个良性helper 函数——因为它们恰好包含esc_html这个词。对原始代码做 FTS在安全场景下是有噪声的词面匹配到esc_html恰恰意味着安全而检索把安全函数当相关结果捞了回来。修正方式就是上述顺序图先决定什么才是真 findingFTS/语义搜索只在图验证过的 findings 里做导航。精确度来自 AST自然语言召回来自 XERJ两者缺一不可。这个经验在后来的 wp_audit_index.py 中落成了索引结构每个函数一份 XERJ 文档codetext与sources[]/sinks[]/sanit/has_source/has_sinkkeyword/boolean并存mappings 见 wp_audit_index.py使含汇点且带源且已净化这类结构化过滤与全文检索可以在同一次查询里组合。五、为什么这套方法优于 grep 和单文件 semgrep原文档给出了逐条对比grep$wpdb→ 每个数据库调用都会命中但没有可达性、没有污点。在 demo 上它只能指出文件无法区分可注入的查询和已 prepare 的查询更不会把跨文件的源关联起来。单文件 semgrep→ 能抓同一个函数内的直接 source→sink但本文档中的跨文件 SQLi 和 XSS 恰好跨文件要把它们连起来需要调用图而 semgrep 的过程间分析能力有限/属于付费功能。本方案→ 6/6 全部命中含跨文件、未认证、缺失属性三类并且交给 AI 阅读的 token 减少约 13 倍。六、多语言新语言 新配置行不是新引擎污点模型是数据而非代码这一论断由 scan_multilang.py 证明。它用一个 tree-sitter 引擎加一个LANGS配置字典承载每语言的 sources / sinks / sanitizers然后跑同一套过程间遍历。scan_multilang.py 中的LANGS结构示意摘录 Python 与 PHP 两行LANGS { python: dict(modtree_sitter_python, ext[.py], func[function_definition], call[call], sources[request.args,request.form,request.values,request.GET,request.POST, os.environ,sys.argv,input(], sinks{sql:[.execute(,.executemany(,.raw(], cmd:[os.system(,os.popen(,subprocess], code:[eval(,exec(], deser:[pickle.load,yaml.load(,marshal.load]}, sanit[shlex.quote,html.escape,escape(,parameterize]), ... php: dict(modtree_sitter_php, ext[.php], lang_fnlanguage_php, func[function_definition,method_declaration], call[function_call_expression,member_call_expression,scoped_call_expression], sources[$_GET,$_POST,$_REQUEST,$_COOKIE,$_SERVER,$_FILES], sinks{sql:[-query(,-get_results(,-get_var(,-get_row(,-get_col(], cmd:[exec(,system(,shell_exec(,passthru(,popen(], code:[eval(], deser:[unserialize(]}, stmt_sinks{echo_statement:xss,include_expression:lfi,require_expression:lfi, include_once_expression:lfi,require_once_expression:lfi}, sanit[esc_html,esc_attr,esc_url,sanitize_,wp_verify_nonce, check_admin_referer,-prepare(,absint,intval,wp_kses]), }引擎本身scan_multilang.py对每种语言做四件事按func节点类型切出函数作用域、收集call节点形成调用边、把调用点被调文本的最后一段callee_text按-/::/.切分取末段与 sink 名单匹配、再对含 source 的函数做与extract_ast.py相同思路的 DFS。语法依赖锁定在 requirements.txttree-sitter0.26.0 tree-sitter-python0.25.0 tree-sitter-go0.25.0 tree-sitter-rust0.24.2 tree-sitter-java0.23.5 tree-sitter-php0.24.1samples/ 下每个语言目录埋了一个过程间漏洞handler 里的 source 到达另一个函数里的 sink。README 中的验证结果languageplanted flowfoundPythonhandler → do_query.execute拼 SQL另含一个已净化的负例✅Gohandler → runexec.Command命令注入✅Rusthandler → runCommand::new命令注入✅Javahandler → queryexecuteQuerySQLi✅PHP5 条流含跨文件 SQLi XSS、LFI、不安全反序列化✅以 samples/py/app.py 为例一个文件同时包含正例与负例负例验证路径上有净化即不报的逻辑def handler(request): uid request.args.get(id) # source return do_query(uid) # - sink in another function def do_query(v): cur.execute(SELECT * FROM u WHERE id v) # sql sink def safe(request): v html.escape(request.args.get(x)) # sanitized - not a finding cur.execute(SELECT v)运行命令摘自 READMEpip install -r requirements.txt python3 scan_multilang.py python samples/py python3 scan_multilang.py go samples/go python3 scan_multilang.py rust samples/rs python3 scan_multilang.py java samples/java python3 scan_multilang.py php ./acme-plugin值得注意的实现细节scan_multilang.py 中调用边只解析到全库唯一定义的名字name_count.get(c,0)1。这是原文档诚实局限一节里提到的两项已完成修正之一——在 11,940 个函数规模下避免同名函数碰撞另一项是 sink 匹配改用 AST 被调节点而非子串不再出现exec(误匹配fsockopen(这类问题。七、规模测试真实 WordPress 核心上的 129 条候选换用 tree-sitter 同时修掉了解析器覆盖问题。研究文档指出在完整 WordPress checkout 上phply约37%的文件解析失败新语法且被静默跳过——对安全审计来说是最糟糕的失败模式而tree-sitter-php解析率 100%。原文档的对照表phplytree-sitter-phpfiles parsed (real WP core)~63%100%full-tree scan time—~3.6 s~619k 行functions/methods indexed—11,940whole-tree tokens (load it all)—~5.2 M无法复核candidate findings to triage—129测试规模为一整棵 WordPress 核心1,492 个 PHP 文件、约 619k 行。文档强调最后两行才是重点图把一棵任何上下文窗口都装不下的 5.2M-token 树压缩成129 条 AI 实际可以逐条处理的候选路径。文档也给出了诚实校验这 129 条是候选不是确认的漏洞。WordPress core 被大量审计过其中大部分主要是echo/include命中实际上已经被转义或做了路径校验只是基于函数名的净化名单尚未识别出这些方式。后续工作是建一个上下文感知的 WP 净化模型——而它从可处理的 129 条开始而不是从 5.2M token 开始。这条先压缩、再复核的路线在 wp_audit_index.py 与姊妹篇 wordpress-audit-with-xerj.md 中延伸成了完整的审计基座把 11,990 个函数wpaudit索引和 1,343 个 hook 注册wphooks索引含unauth布尔字段批量写入 XERJ 的 ES 兼容_bulk接口agent 从此查询代码库而不是加载它。该实测记录的结论转引自该文档同一项审计成本约2,150 tokensXERJ 查询对约 864,000 tokensgrep 通读且每条候选都被完整读取而非略读。八、面向真实 WordPress 规模的设计要点原文档Design for real WordPress scale一节列出的设计原则逐条对应到实现提取是逐文件的、可平凡并行的。每个文件独立解析出函数事实10 万行的树是一个扇出任务而不是全程序加载。增量友好只重解析变更文件。污点模型是数据不是代码。source/sink/sanitizer/state-changer 都是按框架划分的清单——WP 的集合在 extract_ast.py 中多语言的在 scan_multilang.py 的LANGS中。要支持 React 的dangerouslySetInnerHTML、Java 的Runtime.exec、Django 的.raw()就是加一行配置。finding 是工作单元。把 finding路径 严重度 unauth 标记 路径上的代码作为一等 XERJ 文档建索引。Agent 查询的是 finding 而非原始代码——可按严重度排序、可过滤且每条自带待复核的最小代码。XERJ 是合适的存储因为它已经把关键字过滤nopriv、sinks、全文与语义搜索统一在一个索引里并返回带file:line的分块代码——同一个索引同时承载仓库里的文档、配置和 SQL。九、诚实的局限这是分诊不是可信分析器原文档Honest limitations一节对能力边界的界定值得完整保留因为它定义了这套方法的适用范围静态近似。污点沿调用边和直接参数流传播不建模数组/对象内的数据流、别名、动态分发。它会漏掉把污点洗过$GLOBALS数组或变变量variable-variable的流也会在值被某种名字清单无法识别的方式校验时过度上报。净化检测基于名字。它按名字信任esc_html/prepare不检查上下文是否匹配——典型真实漏洞就是用esc_html转义的值被放进 HTML 属性。CSRF/nonce 是启发式——状态变更从入口可达且路径上没有任何 nonce 调用。真实的 nonce 逻辑可能是条件性的。解析器覆盖。PHP 上已通过迁移 tree-sitter 解决100% vs phply 约 63%。规则对任何解析器都成立解析失败的文件必须被报告而不是静默丢弃——这与 autoindex 的 loud-skip 原则一致安全审计里被静默丢掉的文件是最坏情况。真实代码上的精确度需要按框架的净化模型。五语言 demo 干净是因为样本最小在真实 WP core 上同一套子串/名字式 sink 会过度上报129 条候选大部分已被转义。两项已知修正已完成sink 匹配到 AST 被调节点不再exec(匹配到fsockopen(调用边只解析到无歧义名字11,940 个函数不再有碰撞。剩下的是每框架的上下文感知净化识别。每条 finding 都需要确认。目标是缩小 AI和人需要阅读的范围让他们能够推理可利用性——而不是自动提 CVE。十、对 XERJ 的具体落地方向原文档Concrete next steps for XERJ列了五项其中多语言一项已标记完成在autoindex中内置 AST 感知的代码提取器——按函数而非固定行窗口切块把调用边 污点事实作为字段存储使xerj autoindex ./code直接产出 graph-ready 索引。把 finding 做成一等索引带 severity/route schemaagent 循环即查询 finding → 拉取路径代码 → 确认。多语言 via tree-sitter已完成——scan_multilang.py 在一个引擎上覆盖 PHP/Python/Go/Rust/Java新增语言即加一行配置。Loud skip——解析失败的文件必须被报告匹配 autoindex 的诚实性规则。按框架的净化模型——下一个精确度杠杆把真实 WordPress 的候选集从 129 向其真实正例数压缩上下文感知的转义函数匹配、include/模板加载器的路径白名单。结语这套方法的适用边界回到研究文档的定位这是一个working prototype价值在于证明了图决定看什么、索引负责怎么找、AI 只负责判断这一分工在真实规模下的可行性——66 文件插件上 6/6 命中且 token 减 13 倍真实 WordPress 核心上 3.6 秒扫描、5.2M token 压成 129 条候选。它给出的工程结论可以直接迁移到任何大代码库 AI 审计场景过程间可达性与缺失属性检测必须由 AST 图完成对原始代码的全文检索只能用于导航而非精确判定解析失败必须显式上报且一切 finding 最终仍需要人工或 AI 确认。复现入口统一在 docs/examples/ast-vuln-graph/研究全文见 docs/research/ast-graph-vuln-detection.md。赞分享【免费下载链接】xerjXERJ 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.项目地址https://gitcode.com/GitHub_Trending/xe/xerj点击查看免费下载相关推荐XERJ AST图结构漏洞挖掘用调用图、钩子图与污点图把大型代码库审计压缩到可读的候选路径XERJ AST图结构漏洞挖掘用调用图、钩子图与污点图把大型代码库审计压缩到可读的候选路径 本篇技术指南围绕 XERJ 仓库中的原型项目 docs/exa用 XERJ 审计 XERJ 自己的 Rust 引擎AST 调用图基座如何以 1/171 的 token 成本挖出真实 Critical 漏洞用 XERJ 审计 XERJ 自己的 Rust 引擎AST 调用图基座如何以 1/171 的 token 成本挖出真实 Critical 漏洞 导读 本文完整XERJ 白盒安全审计技能xerj-security-audit用覆盖率可证明的 AST 普查 索引问答循环审计 PHP 代码库XERJ 白盒安全审计技能xerj security audit用覆盖率可证明的 AST 普查 索引问答循环审计 PHP 代码库 导读 xerj se上一篇如何利用Temporal构建可靠的AI工作流机器学习任务的终极执行方案下一篇Ariakit 实战用 Combobox 组件构建自定义内容 Selectselect-item-custom 示例全解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考