文章总结: 该文档介绍了YakProject的IRify引擎更新,重点展示了SyntaxFlow在JS/TS静态分析中的深度语义能力。通过CWE-22ZipSlip和CWE-178Express中间件绕过两个案例,详细解析了规则如何利用数据流分析精准识别漏洞并降低误报。文章强调了跨过程分析与闭包建模的突破,提出未来将扩展规则覆盖现代前端框架与Serverless环境,旨在为安全工程师提供高性能、高保真的规模化审计利器。 综合评分: 85 文章分类: 代码审计,漏洞分析,安全工具,应用安全
解析提速+语义深耕!解锁 JS/TS 规模化审计新能力
原创
YAK YAK
Yak Project
2026年3月6日 17:53 北京
#
CWE-22 Zip Slip(压缩包解压目录穿越)
这条 SyntaxFlow 规则用于检测 Zip Slip 类风险:当应用在解压 ZIP/TAR 等压缩包时,将压缩包条目中的路径(例如 ../../evil.sh)直接或间接拼接到本地文件写入 API(如 fs.createWriteStream()、fs.writeFile()、fs.open() 等)上,导致解压结果突破预期目录边界,进而引发任意文件写入、覆盖配置、甚至 RCE 链路。
该问题在 Node.js 生态中非常常见,尤其是“上传压缩包 → 后端解压导入”的业务场景,攻击面清晰、危害上限高,因此规则默认按高风险漏洞处理。
规则的覆盖面针对实际工程做了库级适配,优先抓取常见压缩处理链路中的“条目路径来源”,包括 unzip/unzipper/tar-stream 的 on(‘entry’, …) 回调参数、adm-zip 的 entry.entryName、以及 jszip 的 forEach(relativePath, …) / loadAsync().then(…) 形态。
针对上图不安全代码,内置规则会使用如下 SyntaxFlow 规则匹配 source 点,也就是是 zip 中的 entry 名。
.on('entry', * as $onEntryCallbackAll)$onEntryCallbackAll?{<self><getCall><getCallee><getObject>?{have: /(?i)(unzip|unzipper|tar)/}} as $onEntryCallback$onEntryCallback<getFormalParams> as $entryParam$entryParam.path as $source$entryParam.name as $source$entryParam.linkname as $source
再匹配 sink 点,也就是文件写入函数的路径参数:
// fs.createWriteStream(path) - path is first argumentfs.createWriteStream(* as $fsSink,)// fs.writeFile(path, data, ...) - capture the path argument (first arg)fs.writeFile(* as $fsSink,)fs.writeFileSync(* as $fsSink,)// fs.appendFile(path, data, ...) - capture the path argument (first arg)fs.appendFile(* as $fsSink,)fs.appendFileSync(* as $fsSink,)// fs.open(path, flags, ...) / fs.openSync - capture the path argumentfs.open(* as $fsSink,)fs.openSync(* as $fsSink,)
为了减少误报和提升检测效果,我们这里还定义了 filter 和 guard 用来区分存在过滤和潜在可利用的场景。当 entry 的名称被 basename 处理则不构成 zip-slip,我们把存在 basename 过滤的情况划分为 filter。
但当使用不完全的过滤方式或者使用 if-else 结合 startsWith 和 endsWith 函数判断时则存在利用可能,我们把这类情况归类为存在 guard 条件。
path.basename() as $filter$source?{<self>.indexOf(*)} as $guardedSources$source?{<self>.includes(*)} as $guardedSources$source?{<self>.startsWith(*)} as $guardedSources$source?{<self>.endsWith(*)} as $guardedSources
最后进行检测。检测上采用“来源 → 文件写入”的数据流联通来定位风险,并对结果做了分级:当条目路径几乎“原样”进入文件写入点、且缺少明显校验且不存在PHI 节点时归为 HIGH。
若中间经过 path.join/path.resolve 等路径处理但未能确认形成有效防护,则归为 MID,提示需要进一步人工复核(因为仅做路径拼接/规范化并不等价于安全)。
$fsSink?{<self> #{include: <<<CODE* & $sourceCODE, exclude: <<<CODE*?{opcode: phi} as $__next__CODE}->} as $sink$highCandidates?{<self> #{include: <<<CODE* & $guardedSourcesCODE}->} as $guardedHigh$highCandidates - $guardedHigh as $high$sink - $source as $midAll$midAll - $high as $midCandidates$midCandidates<dataflow(include=<<<CODE* & $filter as $__next__CODE)> as $filteredMid$midCandidates - $filteredMid as $mid
在安全工程实践中,这条规则也强调了可操作的修复模式:推荐使用 path.resolve(TARGET_DIR, entryPath) 并验证结果是否仍在 TARGET_DIR 下(前缀校验),或在业务允许时用 path.basename() 丢弃目录结构,仅保留文件名。
需要注意的是,规则对“条件守卫”属于近似识别(例如 indexOf/includes/startsWith 这类检查)。
它更适合在批量扫描中快速筛出高风险点与可疑点,最终仍建议结合代码语境确认:是否存在可靠的目录边界验证、是否存在符号链接/覆盖等额外风险,以及解压目录是否具备敏感写入权限。
CWE-178 Express 中间件路径大小写绕过
(中间件路径匹配大小写敏感)
这条规则关注的是一个非常“工程化”的授权绕过问题。
在 Express.js 中,字符串路径(如 ‘/admin’)的匹配行为与 正则路径(如 /\/admin\/.*/)在大小写处理上存在差异。很多开发者习惯用 app.use() 在路由入口挂载认证/鉴权/审计等安全中间件,如果此时使用了不带 i 标志的正则表达式作为路径条件,那么该中间件默认是大小写敏感的。
攻击者只要把 URL 改成大写或混合大小写(例如把 /admin/users/45 变成 /ADMIN/users/45),就可能实现“端点仍然命中,但中间件不命中”,从而绕过权限校验,形成典型的 authorization-bypass 风险(对应 CWE-178)。
这条规则的价值在于它并不做“粗暴共现”的启发式判断,而是尽量复现真实绕过关系:先锁定 *.use(regex, handler) 且 regex 不含 i 标志的中间件,再收集同一程序内 *.get/post/… 这类字符串路径端点,最后通过
这种设计可以显著降低误报,避免出现“项目里既有正则中间件、又有字符串路由就一股脑报”的噪声,让告警更贴近真实可利用链路,也更方便研发快速理解与修复。
修复方向同样非常明确,且对业务侵入小:如果确实需要用正则来表达路径范围,优先在正则末尾添加 i 标志以统一大小写匹配;如果不需要正则的表达能力,则建议改用字符串路径(在 Express 默认未开启大小写敏感路由时为不区分大小写),或者在入口统一规范化请求路径(如转小写)来消除“匹配语义不一致”的根因。
我们先编写 SyntaxFlow 规则用于匹配使用大小写敏感正则表达式的中间件参数使用点:
*.use(* as $middlewareArg,)$middlewareArg?{<self><getCall><getCallee><getObject>?{have: /express/}} as $middlewareArg$middlewareArg?{opcode: const}?{have: /^\/.*\/[^i]*$/} as $caseSensitiveRegex
再匹配 GET, POST 等 API 端点的路径参数:
*.get(* as $endpointArg,)*.post(* as $endpointArg,)*.put(* as $endpointArg,)*.delete(* as $endpointArg,)*.patch(* as $endpointArg,)*.all(* as $endpointArg,)$endpointArg?{<self><getCall><getCallee><getObject>?{have: /express/}} as $endpointArg$endpointArg?{opcode: const}?{have: /^\//}?{!have: /^\/.*\/[gimsuy]*$/} as $stringEndpoint
最终判断是否存在绕过风险:
$caseSensitiveRegex<matchRegexpPath(target="$stringEndpoint")> as $bypassRisk
本次 IRify 引擎的更新标志着 JavaScript/TypeScript 静态分析正式进入了“深水区”。我们不仅在前端解析速度上取得了量级突破,更在跨过程分析、闭包建模等语义层面填补了诸多空白。
迭代方向:从“覆盖”到“精控”
在安全工程实践中,静态分析工具的生命力取决于其规则的生命力。后续我们将从以下两个维度持续打磨:
- 规则集的持续扩容:
目前的内置规则已覆盖了 OWASP Top 10 及 Node.js 生态中的部分高频风险(如 Zip Slip、Express 鉴权绕过)。未来,我们将针对现代前端框架(如 React/Vue 的服务端渲染安全)以及 Serverless 函数计算环境下的特有风险进行规则扩充。
- SyntaxFlow 分析效果的打磨:
跨语言调用链分析:随着 IRify 多语言能力的并进,我们将探索在混合栈(如 Java 后端 + JS 前端)场景下的全链路追踪能力。
安全审计不应是孤立的代码匹配,而应是基于对程序语义深刻理解的逻辑推演。IRify 致力于构建一个高性能、高保真、且对开发者友好的静态分析基座。随着 SyntaxFlow 语法的不断演进与内置规则集的日益丰满,我们希望让“规模化审计”不再是一句口号,而是每一位安全工程师手中的利器。
END
YAK官方资源
Yak 语言官方教程: https://yaklang.com/docs/intro/ Yakit 视频教程: https://space.bilibili.com/437503777 Github下载地址: https://github.com/yaklang/yakit Yakit官网下载地址: https://yaklang.com/ Yakit安装文档: https://yaklang.com/products/download_and_install Yakit使用文档: https://yaklang.com/products/intro/ 常见问题速查: https://yaklang.com/products/FAQ
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Yak Project YAK YAK《解析提速+语义深耕!解锁 JS/TS 规模化审计新能力》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论