mcpField
S
scanner-pipeline

scanner-pipeline

github.com/briiirussell安全51455 次安装1 周前更新
代码仓库

关于 scanner-pipeline

scanner-pipeline 是一项技能,可按仓库中的不同层面选择确定性安全扫描器、运行扫描、规范化发现结果,并为修复准备交接信息。它还会在目标仓库内创建扫描计划、工作流和扫描器配置;扫描在线应用则需要授权。

主要功能

  1. 01按仓库层面选择扫描器
  2. 02以非交互命令运行扫描器
  3. 03规范化并去重发现结果
  4. 04将规范化结果导出为 SARIF
  5. 05编写扫描计划和 CI 工作流文件

适用场景

  1. 01对包含多种生态的仓库执行安全检查
  2. 02汇总依赖、代码、机密和基础设施问题
  3. 03整理扫描结果以便分流和修复

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/briiirussell/cybersecurity-skills --skill scanner-pipeline

安装后在 Claude Code 中输入/scanner-pipeline确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
betterleaks-to-findings.mjs
findings-format.md
findings.schema.json
security-scan.yml
to-sarif.mjs
validate-findings.mjs
references/semgrep/
supabase-rls.yml

详细介绍

扫描器流水线 — 运行实际工具、规范化并移交

别再让 Agent 改你的应用了。让它为每一层选用合适的确定性扫描器,以非交互方式运行,将输出合并为统一格式,然后把每项发现分派给修复 Agent。扫描器每次都会发现同样的问题;Agent 审查则只有在恰好检查到时才会发现。

来自 2026 年 10 月对 45 个应用进行排查的实地证据(patch-sweep)。Agent 主导的审查最终发现了所有这些问题;而扫描器在首次运行时就会标出每一项:

  • 10 个 Supabase 表未启用 RLS,可使用公开的 anon 密钥读取和写入,其中包括 472k 封入站电子邮件正文。Supabase 自带的安全顾问会将此报告为 rls_disabled_in_public。
  • 始终为真的 RLS 策略:csr.collection_id = csr.collection_id。针对迁移文件运行的自定义 semgrep 规则可以捕获此问题(见下文)。
  • 每个仓库分别运行 npm audit、pnpm audit 和 bundle-audit,而一次 osv-scanner 扫描就能覆盖所有锁定文件。

此技能负责协调。 深入解读工具的发现结果仍由对应层级的技能负责:secrets-audit、dependency-audit、container-audit、cloud-audit、owasp-audit。主动利用由 web-pentest / red-team-engagement 负责。

为何预先批准 Write: 此技能的主要输出是文件——scanner-pipeline.md 计划、.github/workflows/security-scan.yml 以及扫描器配置。它只会写入这些文件,而且仅限目标仓库。实时应用扫描器(nuclei、zap)不预先批准;它们始终会提示确认。

交叉引用:finding-triage(排序和处置)、secrets-audit、dependency-audit、container-audit、cloud-audit(Supabase 顾问、PaaS)、web-pentest(实时扫描授权)、cicd-security(固定 SHA 的 Actions、工作流强化;zizmor / actionlint 结果)、patch-sweep(跨项目群 CVE 响应)、log-watch。

授权

运行任何操作前,请确认:

  1. 仓库: 用户拥有每个仓库,或已获准扫描。若采用项目群模式,请确认要枚举的所有者 / 组织。
  2. 实时目标: 仅扫描用户拥有且在本次对话中明确指定名称的域名。不能自行推断“仓库部署到的任何地址”——用户必须明确给出主机名。
  3. 结果去向: 仅存放于本地文件、GitHub 代码扫描,还是 issue。密钥发现结果绝不能写入公开 issue。

拒绝以下操作:使用实时扫描器(nuclei、ZAP、Burp)扫描用户不拥有或未指定的主机;扫描第三方或共享主机(共享的 Supabase/Vercel/Heroku 主机名、CDN、用户调用的 API);未经过 web-pentest 授权步骤就运行 ZAP 主动扫描或 nuclei 入侵性模板;在任何地方打印密钥值。

1. 检测技术栈,并为每一层选择工具

先检查代码仓库:锁文件、Dockerfile、*.tf、supabase/、Gemfile、go.mod、.github/workflows/。然后为每一层选择一个工具,优先选择支持多种生态系统的工具,而非针对单个包管理器的工具。

层首选特定技术栈的附加工具备选
密钥betterleaks git 或 gitleaks git(完整历史记录)活跃性检查,询问后再进行:betterleaks … -v / -a,或 trufflehog git --only-verified—
代码(SAST)semgrep scanBrakeman(Rails)、gosec(Go)、CodeQL(如果有 GHAS)、自定义 semgrep 规则(见下文)—
依赖项osv-scanner scan source -r .(一次性扫描 npm、pnpm、yarn、Bundler、pip、Go、Cargo、Composer、Maven)bundle-audit(Rails)、govulncheck(Go,可达性调用图)、pip-auditnpm audit / pnpm audit --prod / yarn audit
基础设施 / IaC / 容器trivy fs --scanners vuln,misconfig,secret .checkov -d .(Terraform、CloudFormation、k8s、Actions)、trivy image <img>—
数据库(Supabase)Supabase 安全顾问(supabase db advisors,或 Management API GET /v1/projects/{ref}/advisors/security)supabase db lint(plpgsql_check:函数错误,不检查 RLS)、针对 supabase/migrations/ 的 semgrep 规则pg_policies / pg_tables.rowsecurity 查询
线上应用(受保护)ZAP 基线(被动扫描)使用 -rl 的 nuclei—

命令均为非交互式,每种工具在 .security/out/ 下生成一个输出文件:

mkdir -p .security/out
# Secrets — full history, values redacted
gitleaks git . --redact --report-format sarif --report-path .security/out/gitleaks.sarif
# or Betterleaks: detection only and silent, so a stray BETTERLEAKS_VALIDATE=true can't use found keys and nothing prints.
# v2 has no SARIF reporter: convert, then export (needs_validation records go out at level "note")
betterleaks git . --validate=false --analyze=false --redact --silent -o .security/out/betterleaks.json
node references/betterleaks-to-findings.mjs .security/out/betterleaks.json -o .security/out/betterleaks.findings.json
node references/to-sarif.mjs .security/out/betterleaks.findings.json --include-needs-validation -o .security/out/betterleaks.sarif
trufflehog git file://. --only-verified --json --no-update | jq -c 'del(.Raw, .RawV2)' > .security/out/trufflehog.jsonl
# Code
semgrep scan --config p/default --config .semgrep/ --metrics=off --sarif --output .security/out/semgrep.sarif
# Dependencies (all lockfiles at once)
osv-scanner scan source -r . --format sarif --output .security/out/osv.sarif
# Infra / IaC / container files
trivy fs --scanners vuln,misconfig,secret --format sarif --output .security/out/trivy.sarif .
checkov -d . -o sarif --output-file-path .security/out/
# Rails
bundle-audit check --update
brakeman --no-pager -f sarif -o .security/out/brakeman.sarif
# Go
govulncheck -format sarif ./... > .security/out/govulncheck.sarif
gosec -fmt sarif -out .security/out/gosec.sarif ./...
# Supabase (check flags with `supabase db advisors --help` for your CLI version)
supabase db advisors --linked
curl -s -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
  "https://api.supabase.com/v1/projects/<ref>/advisors/security" > .security/out/supabase-advisors.json

将 .security/out/ 添加到 .gitignore。注意,不同主版本的工具命令可能不同(gitleaks 8.19+ 使用 git / dir 子命令;osv-scanner v2 使用 scan source;Betterleaks v2 将 dir 重命名为 fs、将 --report-path 重命名为 -o,并将 --validation 重命名为 -v,而 v1 中 --verbose 表示详细输出);运行一次 <tool> --help,然后按需调整,不要猜测。

自定义规则:始终为真的 RLS 策略

引发问题的漏洞:using (csr.collection_id = csr.collection_id) 将列与其自身比较,因此该策略对每一行都会通过。作者原本想用外层表的列。Semgrep 没有一流的 SQL 解析器,因此使用通用模式:pattern-inside: create policy ... ;,再加上一个能反向引用左侧的正则表达式。完整规则文件(检查自比较、字面值 true,以及用于交叉检查 RLS 的公开表):references/semgrep/supabase-rls.yml。将其复制到代码仓库的 .semgrep/,以便 CI 工作流发现并使用它。若要根据已确认的问题编写此类规则并添加测试,请参阅 variant-analysis。

semgrep scan --metrics=off --config .semgrep/supabase-rls.yml supabase/migrations/
# quick check without semgrep (PCRE backreference)
rg -nP '(?i)\b([a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*)\s*=\s*\1\b' supabase/migrations/
# portable: BSD and GNU grep -E accept \1 in practice (ugrep aliased as grep does not; use /usr/bin/grep)
grep -rEni '([a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*)[[:space:]]*=[[:space:]]*\1([^a-z0-9_]|$)' supabase/migrations/

将其与 RLS 未启用检查配合使用;该检查需要数据库,而非源代码:

select schemaname, tablename from pg_tables
where schemaname = 'public' and rowsecurity = false;

2. 谨慎处理密钥

  • Betterleaks 或 gitleaks。 规则 ID 相同,gitleaks:allow 注释也相同。Betterleaks 产生的通用密钥误报更少,会为每条发现生成一个值哈希,并检查密钥是否有效。v2 仍是候选发布版(betterleaks version;包管理器可能会安装 v1.9.0),已移除 SARIF,且不会读取 .betterleaks.toml,除非你传入 --config。标志、安全规则和报告字段:secrets-audit → references/betterleaks.md。
  • 扫描完整历史记录,而不只是 HEAD。 gitleaks git 会遍历每次提交;gitleaks dir 只检查工作树。去年删除的密钥仍存在于每个克隆中。
  • 验证通过意味着密钥仍有效。 trufflehog --only-verified 会针对每个候选密钥向提供方的 API 发起检查。这会将找到的密钥发送给其提供方并使用该密钥,因此 trufflehog 不会预先获批:运行时会提示用户,用户需同意检查。经验证的命中属于 P0:将其记录为 confirmed,并在清理历史记录之前先轮换密钥。先重写历史记录,会导致你处理期间每个分支和克隆中的密钥仍然有效。将轮换工作交给 secrets-audit。
  • 绝不打印密钥值。 gitleaks 使用 --redact;trufflehog JSON 中删除 Raw/RawV2。报告、问题、PR 评论和 Agent 交接记录只写检测器、文件、提交和密钥值的哈希——绝不包含密钥值。将密钥指纹设为 secret:<detector>:<sha256(value)[:12]>,这样同一个密钥出现在五个文件中时会合并为一条发现。Betterleaks 会在 match.fingerprint 中记录该哈希,并在脱敏前计算;references/betterleaks-to-findings.mjs 会据此构建记录。gitleaks 的 --redact 和移除 Raw 后的 trufflehog 不会保留密钥值,因此它们的命中会改为按文件和提交去重。对于会离开本机的哈希(CI 产物、问题),请使用 --hmac-key:短令牌的普通 SHA-256 哈希可能会被暴力破解。
  • 未验证的命中应标记为 needs_validation,而不是当作噪声:提供方可能不支持验证,密钥也可能用于内部系统。

3. 实时应用扫描护栏

实时扫描会向运行中的系统发送流量。请先遵循 web-pentest 的授权步骤;本节仅规定扫描速率和顺序。

  1. 仅扫描用户拥有且明确指定的主机名。 绝不扫描平台共用的域名根、CDN、身份验证提供方或应用调用的第三方 API。使用 -u https://app.example.com,不要使用通配符或从 DNS 抓取的列表。
  2. 先被动扫描,再主动扫描。 ZAP baseline 会以被动方式爬取并生成报告:
    docker run --rm -t ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t https://app.example.com -J zap.json
    
    仅在用户确认后才运行主动扫描(zap-full-scan.py),最好在预发布环境的副本上运行。
  3. 低速率。 nuclei -u https://app.example.com -rl 10 -c 5 -severity medium,high,critical -exclude-tags intrusive,dos,fuzz -jsonl -o nuclei.jsonl。仅在用户同意后提高速率。
  4. 发现损害迹象时立即停止: 5xx 错误激增、收到速率限制响应、账户被锁定。报告情况并暂停。

4. 规范化输出

每个工具使用不同的格式。将所有结果转换为仓库的发现项格式,然后统一导出 SARIF。格式、字段和验证器:references/findings-format.md;使用 references/validate-findings.mjs 验证;使用 references/to-sarif.mjs 导出。

导入规则:

  • 扫描器命中项导入时设为 verdict: "needs_validation",并设置 blockers: ["scanner result, not yet triaged"] 和 validation_plan,其中写明准确的重新运行命令。工具自身的严重性写入 evidence[].description;只有在 finding-triage 确认后,记录才设置实际的 severity。例外:已验证的有效密钥导入时设为 confirmed。
  • 设置 skill: "scanner-pipeline",并在 evidence 中注明工具名称和规则 ID。
  • cwe 使用规则元数据中的值(semgrep、Brakeman、gosec、checkov 均包含该值)。对于依赖项安全公告,若公告中有 CWE,则使用该值;否则留空,不要猜测。
  • confidence:仅基于静态分析的结果最高设为 medium。已验证的密钥和经可达性确认的 govulncheck 结果可以设为 high。

按指纹跨工具去重——使用稳定的根因 ID,不包含行号、严重性或判定结果:

类型指纹合并范围
依赖项dep:<ecosystem>:<package>:<canonical-id>(存在 CVE 时用 CVE,否则用 GHSA,再否则用 OSV ID)同一安全公告的 osv-scanner + trivy + npm audit + Dependabot
密钥secret:<detector>:<sha256(value)[:12]>跨文件和提交的同一密钥;Betterleaks 和 gitleaks 使用相同的规则 ID
代码sast:<cwe>:<file>:<enclosing function or route>同一漏洞点的 semgrep + CodeQL + Brakeman
IaCiac:<resource address>:<check family>同一资源上的 trivy misconfig + checkov
数据库db:<project>:<schema.table>:<lint name>同一表上的 advisor + 自定义 semgrep 规则

在合并后的记录中保留每个工具的所有证据;修复 Agent 需要这些信息。

抑制文件——.security/suppressions.yml,每个指纹对应一条记录:

- fingerprint: dep:npm:next:CVE-2026-XXXXX
  tool: osv-scanner
  reason: "Static export; no server runtime. Advisory requires Server Actions."
  owner: "@jdoe"
  added: 2026-10-26
  expires: 2027-01-24      # max 90 days; expired entries resurface the finding

规则:每条记录都必须包含原因、负责人和过期时间(最长 90 天);过期记录会被忽略,发现项也会重新出现;Agent 提议抑制项,但每一项都必须由用户批准。仅在工具支持填写原因和过期时间时,才将记录同步到原生忽略文件——osv-scanner.toml 中带有 ignoreUntil 和 reason 的 [[IgnoredVulns]],以及含 exp:YYYY-MM-DD 的 .trivyignore——并将 suppressions.yml 作为事实来源。被抑制的发现项写入 findings.json 时,设为 rejected,并将 ruled_out_by 指向对应的抑制记录,以免重新运行时再次报告。

5. 集群模式

gh repo list <owner> --limit 1000 --source --no-archived --json nameWithOwner,defaultBranchRef,pushedAt

--source 会排除 fork;--no-archived 会排除已归档的仓库。对每个所有者/组织重复执行。仅扫描密钥时,Betterleaks 可以在一个进程中扫描组织内的所有仓库及其完整历史记录,而不必逐个克隆:betterleaks github --validate=false --analyze=false --redact --silent -o .security/out/fleet-betterleaks.json https://github.com/<org>(需要在环境中设置 GitHub 令牌;--exclude-repo '<org>/*-archive';仅使用 --include=forks 才会扫描 fork)。此命令会提示操作。对每个仓库:浅克隆生产分支(如果有 patch-sweep 第 2 步的结果,则使用其中的分支;否则使用默认分支)——但密钥扫描除外,因为它需要完整历史记录(对 gitleaks 来说,git clone --filter=blob:none 已足够)。并行运行每个仓库的流水线(一次处理少量仓库),将输出写入 .security/out/<repo>/。然后汇总:

| repo | layer | tool | sev | finding | status |
|---|---|---|---|---|---|
| acme/api | db | supabase advisor | (tool: ERROR) | rls_disabled_in_public: public.inbound_emails | needs_validation |
| acme/api | secrets | trufflehog | P0 | verified Stripe live key in commit a1b2c3d (value hash 3f9e…) | confirmed — rotate |
| acme/web | deps | osv-scanner | (tool: CRITICAL) | next 15.5.10 — CVE-2026-XXXXX | needs_validation |

在 finding-triage 设置实际严重性之前,sev 会在括号中显示工具标注的严重性。优先列出 P0 密钥,然后按工具标注的严重性排序。

6. CI 和计划任务

将 references/security-scan.yml 搭建到仓库中,路径为 .github/workflows/security-scan.yml。它会:

  • 在每个 PR 上运行 gitleaks、osv-scanner 和 semgrep(gitleaks 扫描 PR 的提交;semgrep 针对基线执行感知差异的扫描);
  • 每周执行一次完整扫描(完整 Git 历史、所有规则),并支持通过 workflow_dispatch 手动触发;
  • 可通过 workflow_call 由其他仓库调用,因此一份配置即可服务于整个集群;
  • 在可用时将 SARIF 上传到 GitHub 代码扫描(公共仓库,或启用了 GitHub Advanced Security 的私有仓库);否则始终将 SARIF 保留为工作流产物;
  • 将每个 Action 固定到完整提交 SHA,并将每个容器镜像固定到摘要(cicd-security)。模板中的 SHA 根据上游发布标签于 2026-10-26 查找;请使用 Dependabot 或 Renovate 更新,不要改用标签。

在 CI 中使用 Betterleaks(可选,v2 稳定后启用)。在 gitleaks 旁边添加一个作业:

  • 像其他镜像一样,按摘要固定镜像。不要使用 :latest 或会变动的 :v2 标签。
  • 在步骤的 env 中设置 BETTERLEAKS_VALIDATE: "false" 和 BETTERLEAKS_ANALYZE: "false",并传入 --validate=false --analyze=false。绝不要在 CI 中执行验证。 对 fork 的拉取请求执行验证时,任何由 PR 作者植入的密钥都可能被发送到规则调用的任意端点。
  • 从基线分支读取 .betterleaks.toml(git show "origin/$GITHUB_BASE_REF:.betterleaks.toml"),并通过 --config 传入。PR 可以在自己的 head 中修改该文件,从而关闭本应检测问题的规则(cicd-security)。
  • 使用密钥中的 --hmac-key(BETTERLEAKS_FINGERPRINT_HMAC_KEY),因为报告产物会离开本机。
  • 使用 betterleaks-to-findings.mjs 转换,再使用 to-sarif.mjs --include-needs-validation,然后上传。v2 发现问题时会以 1 退出:像 gitleaks 步骤一样保留 continue-on-error: true。

无法使用代码扫描时,针对每个指纹创建或更新一个 issue(标题 = 指纹,正文 = 不包含密钥值的证据),不要把 SARIF 直接倾倒到评论中。

7. 移交给修复流程

  1. 先分诊。 每条 needs_validation 记录都交给 finding-triage,由其判定结论、严重性和处置方式。已验证的 P0 密钥跳过队列,直接进入轮换流程。
  2. 每个修复 Agent 处理一个发现项。 提供合并后的记录:指纹、每个工具的证据、确切的重新运行命令,以及需要查阅的所属层技能。不要提供整个看板。
  3. 在修复分支上重新运行指定工具进行确认——使用相同命令和相同配置,并记录修复前后的结果。只有当该工具不再报告此指纹时,发现项才算关闭。
  4. 绝不要通过修改扫描器配置来让发现项消失。 不得删除规则、缩小路径范围,也不得由修复 Agent 添加抑制规则。若修复 Agent 认为某项发现是误报,应反馈给用户,由用户批准是否添加抑制规则。

输出格式

在仓库根目录编写 scanner-pipeline.md(或为集群中的每个仓库各编写一份):

# Scanner Pipeline — [repo or fleet] — [date]

## Stack detected
[languages, package managers, frameworks, IaC, Supabase y/n, live targets named by user]

## Tools per layer
| layer | tool | version | command | output |
|---|---|---|---|---|

## Schedule
- PR: gitleaks (PR commits), osv-scanner, semgrep (diff-aware)
- Weekly: full history + all layers, [day/time UTC]
- Results: code scanning / artifact + issues

## Suppression policy
`.security/suppressions.yml`; reason + owner + expiry ≤ 90 days; user approves each entry.

## First run
- findings.json: [N] records ([c] confirmed, [n] needs_validation, [r] rejected)
- Roll-up board: [table above]

## Hand-off
[fingerprints sent to finding-triage; P0 secrets sent to rotation]

可选的搭建文件:.github/workflows/security-scan.yml、.gitleaks.toml(在默认配置基础上扩展组织专用规则)、.semgrep/(例如 RLS 规则等自定义规则)、osv-scanner.toml。

边界

  • 仅负责编排。深入解读由各层技能负责。
  • 不进行主动利用。需要验证的扫描器命中项应交给 web-pentest,并执行其授权步骤。
  • 实时扫描:仅扫描自有且已命名的主机,先进行被动扫描,并限制速率。
  • 绝不打印、提交或发布密钥值。
  • 绝不削弱扫描器配置来通过 CI。

参考资料

评论

支持 Markdown