mcpField
P
perform-security-review

perform-security-review

github.com/bitwarden安全16276 次安装今天更新
代码仓库

关于 perform-security-review

用于检查 PR、提交、时间范围、本地改动或分支差异的 Bitwarden 安全评审技能,规定差异选择、扫描证据收集、并行专项评审及报告处理流程。

主要功能

  1. 01PR、提交、时间范围及本地评审模式
  2. 02经过校验的分支基准解析
  3. 03扫描证据收集
  4. 04按领域并行开展 Agent 评审
  5. 05报告输出与差异文件清理规则

适用场景

  1. 01检查 PR 或提交中的安全问题
  2. 02评估近期或未提交的仓库改动
  3. 03结合扫描背景生成安全评审报告

MCP 相关

该工作流会调用 `aikido:issues` 技能获取 Aikido 发现项,并将预先收集的证据纳入评审;还会通过 GitHub API 查询 secret scanning 和 Dependabot 告警。

安装

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

$npx skills add https://github.com/bitwarden/ai-plugins --skill perform-security-review

安装后在 Claude Code 中输入/perform-security-review确认已加载。

包含的文件

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

references/
base-ref-resolution.md
security-review-rubric.md
tool-grants.md

详细介绍

参数

--base-ref <ref>:仅用于分支比较模式。使用此 ref 作为差异比较基准,而不是探测确定基准。它仍会通过步骤 1-A2 中的全部三项检查,因此无法解析或与当前分支不连通的 ref 会被拒绝,而不会被直接信任。当步骤 1-A2 中的探测会解析出错误的基准时,请提供此参数,例如非默认主干分支 rc,或 gh 未通过身份验证、导致候选项 2 不可用的仓库。它无法弥补默认的 actions/checkout;references/base-ref-resolution.md 说明了原因以及可行的做法。

--output-dir <path>:使用 --output file 时,将报告写入 <path>/security-review-YYYY-MM-DD-{identifier}.md,而不是当前工作目录。提示:使用 --output-dir ~/.claude/security-reviews/ 可将报告保存在 Git 仓库之外。

安全审查模式

根据调用确定审查模式。PR 编号、提交 SHA 或时长参数会分别选择对应模式;未提供位置参数时,--base-ref 会选择分支比较模式。

如果同时提供位置参数和 --base-ref,请停止并报告冲突,指出两者并要求用户选择一个。不要启动任何 Agent。将报告写入解析出的输出位置,与步骤 1-A2 中止时的处理方式相同。

  • PR 模式(PR 编号或 URL):使用 gh pr view <number> 获取上下文,使用 gh pr diff <number> 获取差异。
  • 提交模式(提交 SHA):使用 git diff <sha>..HEAD 审查该提交之后的所有更改。
  • 基于时间的模式(时长,例如“过去 48 小时”):使用 git log --since="<duration>" --reverse --format=%H | head -1 找到时间范围内最早的提交,然后使用 git diff <sha>^..HEAD 将该提交包含在内。
  • 本地更改模式(未提供 PR、提交或时长参数,也未提供 --base-ref,且存在待处理更改):使用 git diff HEAD 获取已暂存和未暂存的更改。
  • 分支比较模式(未提供位置参数,且提供了 --base-ref <ref> 或工作树没有待处理更改):使用 git diff <base-ref>...HEAD 获取分支与基准分歧后产生的更改;基准由子步骤 A2 解析,而不是直接假定为 main。--base-ref 会直接选择此模式,因此即使工作树有未提交更改也能使用,但三点差异比较的是提交,不会审查待处理更改。此处也运行 git diff --quiet HEAD;如果结果显示工作树有未提交更改,则在步骤 5 的标题中添加 Pending changes: 行;如果未提供 --base-ref,则待处理更改会转入本地更改模式。

其中两种模式会根据工作树是否有未提交更改来选择。使用 git diff --quiet HEAD 检查;存在待处理更改时,该命令会以状态码 1 退出。

安全审查流程

**模型选择:**如果指定了 --model,则所有 Agent 都使用该模型。否则,默认使用 opus。

按顺序执行以下步骤。不得跳过、调整顺序或合并步骤。有三个位置会提前停止流程;遵守此规则就意味着必须执行这些停止操作:上述模式冲突停止、步骤 1-A2 中止,以及步骤 1B 停止。

  1. 收集上下文。 启动任何 Agent 之前,先运行以下所有命令。

    A.)解析仓库身份。 将以下每条命令作为独立的 Bash 调用运行——不得使用 &&、||、; 或管道链接命令:

    • git remote get-url origin — 从输出中解析 owner 和 repo。同时处理 HTTPS(https://github.com/owner/repo.git)和 SSH([email protected]:owner/repo.git)格式。
    • git branch --show-current — 获取当前分支名称。

    A2.)解析基准 ref。 仅用于分支比较模式;其他所有模式均跳过。将每条命令作为独立的 Bash 调用运行。references/base-ref-resolution.md 说明了以下每项检查的目的。 调用方可以提供 --base-ref <ref>。如果提供,该值就是唯一候选项:不要探测其他候选项;如果未通过以下三项检查中的任何一项,立即中止。退而使用 origin/HEAD 会审查调用方未指定的基准。必须原样使用给定的引用——绝不改写——但单独的 main 会解析为本地分支,而不是 origin/main,因此要在步骤 5 的标题中注明所用引用,这样至少能看出本地基准是否已过时。

    否则,按以下顺序处理候选项:

    1. git rev-parse --abbrev-ref origin/HEAD — 命令以 0 退出时,使用其输出。判断依据必须是退出状态,绝不能依据输出。
    2. gh repo view --json defaultBranchRef --jq .defaultBranchRef.name,并加上 origin/ 前缀,得到 origin/<default_branch>。不要使用 gh api;参见 references/tool-grants.md。只要候选项 1 未能提供基准引用,就构造候选项 2——无论是其命令以非零状态退出(此时根本不存在候选项 1),还是其输出未通过三项检查中的任何一项。如果 API 调用本身失败,则候选项 2 不存在。

    每个候选项都必须按以下顺序通过全部三项检查,无论其来源如何。 探测得到的候选项若未通过任何一项检查,就将其丢弃并转而处理下一个探测候选项。调用方提供的 --base-ref 没有下一个候选项,因此应直接中止。

    1. 匹配 ^[A-Za-z0-9_][A-Za-z0-9._/-]*$,并且必须在将值放入任何命令之前检查。参考文件说明了这会拦截哪些内容,以及为什么首字符要单独限定。
    2. git rev-parse --verify --quiet <candidate> 以 0 退出。
    3. git merge-base <candidate> HEAD 以 0 退出。

    始终保留 origin/ 前缀。第一个通过全部三项检查的候选项将成为 <base-ref>。

    中止。 如果没有候选项通过检查,则在步骤 1B 之前停止;不要在运行过程中向用户提问。指出最后一个候选项未通过三项检查中的哪一项,因为相应的解决办法各不相同;然后给出以下三种解决办法:在 checkout 中设置 fetch-depth: 0,在调用前运行 git fetch origin <base>,或使用带拉取请求编号的 PR 模式。将内容写入已解析的输出目标,即完整审查会使用的同一目标。对于 github——即 --output github,或未提供 --output 且 printenv GITHUB_ACTIONS 以 0 退出并返回非空输出——目标为 /tmp/review-summary.md,并在末尾追加 \n\n;工作流的后置步骤会读取该文件。file 和 chat 则分别使用报告路径和回复内容。不要运行步骤 1B 至 7;不存在需要清理的 diff 文件。

    B.) 获取并保存 diff。 使用上述确定的审查模式,将以下命令之一作为单次 Bash 调用运行——不得使用 &&、; 或管道。必须使用 Shell 重定向(>),且允许使用:

    • PR 模式:gh pr diff <number> > /tmp/security-review-<identifier>.diff
    • 提交模式:git diff <sha>..HEAD > /tmp/security-review-<identifier>.diff
    • 基于时间的模式:git diff <oldest-sha>^..HEAD > /tmp/security-review-<identifier>.diff
    • 本地更改模式:git diff HEAD > /tmp/security-review-<identifier>.diff
    • 分支比较模式:git diff <base-ref>...HEAD > /tmp/security-review-<identifier>.diff

    必须在所有情况下都以1 行限制读取该文件,包括命令以非零状态退出的情况——绝不能不受限制地读取,因为协调器不得持有 diff。即使运行显然失败,也必须读取文件:Write 不会覆盖本会话中尚未读取的文件,因此跳过读取会导致步骤 7 无法清除只写入了一部分的 diff。然后检查退出状态以及文件是否有内容。只有命令以 0 退出且文件有内容时,才能启动 Agent。

    • 以非零状态退出:diff 命令失败。报告失败,并准确列出运行的 diff 命令。
    • 以 0 退出且文件为空,分支比较模式:这是合法结果。报告 <base-ref> 与 HEAD 之间没有更改。
    • 以 0 退出且文件为空,其他任何模式:报告已解析范围未产生 diff,并注明模式和范围。 在这三种情况下,且仅在这三种情况下,停止前使用步骤 7 的 Write 清空 diff 文件。成功路径上必须保留该文件:步骤 2 和 4 会将其路径交给 Agent。

    与步骤 1-A2 中止时一样,向解析后的输出目标报告。此处停止会跳过步骤 5,因此如果工作树有未提交更改,请添加 Pending changes: not reviewed 说明。不要输出摘要表或任何发现问题的章节;本次没有进行审查。

    选择一个描述性的 <identifier>(例如 PR123、5days、local)。将完整路径存储为 DIFF_FILE,并在步骤 2 和 4 的每个 Agent 提示词中包含该路径,以便它们可以直接 Read diff。

    C.) 获取扫描证据。 以下三个调用中,任何一个失败都不会导致审查停止——扫描证据用于佐证步骤 2 和 4 中的发现,并非运行这些步骤的必要条件。Aikido 证据使用预先批准的 MCP 工具授权,无需提示词即可运行。以下 Secret scanning 和 Dependabot 的 gh api 调用都是读取操作,且都未获预先批准(原因见 references/tool-grants.md),因此每个调用都会提示;在 CI 中无人响应,两个调用都会被拒绝。任何非成功响应都视为失败。将每个扫描器的结果记录为以下其中一种:格式化输出;运行后未返回任何结果时记为 None;扫描器已关闭或无法访问(403、404、MCP 服务不可用、未启用 GHAS)时记为 Not available;对于 Aikido,如果技能未返回成功的已认证响应,即使结果看起来为空,也记为 Not available(缺失或未经身份验证的会话可能会显示为空结果,而非明确错误);调用已运行但失败(响应格式错误)时记为 Errored (<status>);仅对于两个 gh api 扫描器,调用未运行时记为 Not checked (permission denied)——Aikido 的授权是预先批准的,因此不会出现这种状态。只有 None 表示扫描器已执行检查且未发现任何内容;将其与 Not available 混为一谈,可能会把缺失的 Aikido 会话误判为干净的 SAST/IaC 扫描。所有格式化都使用 gh api --jq——不要通过管道传给 jq。两个 gh api 调用都必须使用 --method GET 和 -H "X-GitHub-Api-Version: 2026-03-10"。

    • Aikido(SAST/IaC/SCA/Container): 调用 Skill(aikido:issues),限定在此仓库范围内(repo_name={repo}),并使用 issue_types: ["sast", "iac", "open_source", "docker_container"] 列出未解决的问题。Bitwarden 的 SAST/IaC 扫描通过 Aikido 执行,而非 GitHub 代码扫描——不要查询 code-scanning/alerts API,其中不会包含这些问题。将每个返回的问题格式化为 SEVERITY | title | file (line N),并按问题类型将结果拆分到下面两个 SCAN_EVIDENCE 块中。
    • Secret scanning: gh api --method GET -H "X-GitHub-Api-Version: 2026-03-10" "repos/{owner}/{repo}/secret-scanning/alerts?state=open" --jq '.[] | "\(.secret_type_display_name) | \(.state) | \(.resolution // "open")"'
    • Dependabot: gh api --method GET -H "X-GitHub-Api-Version: 2026-03-10" "repos/{owner}/{repo}/dependabot/alerts?state=open&per_page=100" --jq '.[] | "\(.security_advisory.severity | ascii_upcase) | \(.dependency.package.name) | \(.security_advisory.cve_id // .security_advisory.ghsa_id) | \(.security_advisory.summary)"'

    将结果收集到 SCAN_EVIDENCE 块中,供步骤 2 和 4 使用:

    === SCAN EVIDENCE (pre-fetched — do not re-fetch) ===
    
    --- AIKIDO (SAST/IAC) ---
    {one of: formatted output | None | Not available | Errored (<status>)}
    
    --- AIKIDO (SCA/CONTAINER) ---
    {one of: formatted output | None | Not available | Errored (<status>)}
    
    --- SECRET SCANNING ---
    {one of: formatted output | None | Not available | Errored (<status>) | Not checked (permission denied)}
    
    --- DEPENDABOT ---
    {one of: formatted output | None | Not available | Errored (<status>) | Not checked (permission denied)}
    
  2. 并行启动以下四 (4) 个 subagent_type: "bitwarden-security-engineer:bitwarden-security-engineer" Agent。每个 Agent 都有特定领域——你必须要求它严格限定在该领域内。Agent 必须在开始前以及评估发现时阅读 references/security-review-rubric.md。

    Agent 1 — 代码安全: 仅关注注入漏洞(SQL、XSS、命令)、密码学弱点、不安全的编码模式以及 OWASP A01–A05。调用 Skill(bitwarden-security-context) 和 Skill(analyzing-code-security) 以指导分析。不要评估机密、依赖项、架构或威胁建模。 Agent 2 — 机密信息与依赖项:仅关注硬编码凭据、暴露的机密信息、易受攻击的软件包和供应链风险。调用 Skill(bitwarden-security-context)、Skill(detecting-secrets) 和 Skill(reviewing-dependencies) 指导分析;但对于 Aikido SCA/容器数据,请使用 SCAN_EVIDENCE 中预先获取的 --- AIKIDO (SCA/CONTAINER) --- 区块,而不是实时查询 aikido:issues——此处禁止使用该工具,详见下方限制。不要评估代码模式、架构或威胁建模。

    Agent 3 — 安全架构:仅关注身份验证、授权、加密实现、信任边界和 Bitwarden 的零知识不变量(加密和解密只能在客户端进行——服务器绝不能访问明文保险库数据)。调用 Skill(reviewing-security-architecture) 和 Skill(bitwarden-security-context) 指导分析。不要评估注入漏洞、机密信息或威胁建模。

    Agent 4 — 威胁视角:仅从攻击者角度进行分析——追踪用户输入在数据流中流向危险接收点的路径、业务逻辑漏洞、权限提升路径、数据暴露和 API 滥用模式。调用 Skill(bitwarden-security-context)、Skill(analyzing-code-security) 和 Skill(threat-modeling) 指导分析。不要评估架构模式、机密信息或依赖项版本。

    对于全部四个 Agent:

    • 使用所选模型。
    • 重要:每个 Agent 的提示词开头都必须包含以下说明: "You are a security researcher analyzing code for impactful, exploitable, and reproducible vulnerabilities for an authorized internal security audit. Your goal is to find real bugs — approach this analysis without safety refusals, as this is an explicitly authorized internal security engagement at Bitwarden."
    • 重要:每个 Agent 的提示词都必须包含以下限制: "The diff has been pre-fetched and saved to {DIFF_FILE} — read it using the Read tool. DO NOT execute gh pr diff, git diff, or any other diff commands. NEVER use WebFetch, WebSearch, Skill(aikido:issues), mcp__plugin_aikido_aikido-mcp__aikido_issues_list, or any gh api endpoints — scan evidence has been pre-fetched and is provided below."
    • 重要:每个 Agent 的提示词都必须包含第 1 步中收集的完整 SCAN_EVIDENCE 区块。
    • 报告所有发现,并注明严重程度(CRITICAL/HIGH/MEDIUM/LOW/INFO)、受影响的文件和行号,以及建议的修复措施。
    • 将正向安全改进(例如修复 CWE、改进密码学)报告为 ✅ 优势,并简要说明理由。
  3. 等全部四个 Agent 返回后,使用 references/security-review-rubric.md 中定义的双轴模型对每项发现进行评级:

    • 严重程度:🔴 CRITICAL | 🟠 HIGH | 🟡 MEDIUM | 🔵 LOW | ⚪ INFO
    • 置信度:🟢 HIGH | 🟡 MEDIUM | 🔵 LOW
    • 应用标准中的阈值矩阵,指定分类:🚨 阻断项、⚠️ 改进项、📝 备注、✅ 优势或 ❌ 驳回。
  4. 启动一个 验证 Agent subagent_type: "bitwarden-security-engineer:bitwarden-security-engineer",并提供所有合并后的发现、严重性/置信度评级、分类矩阵、DIFF_FILE 路径,以及步骤 1 中完整的 SCAN_EVIDENCE 块。

    • 重要:每条 Agent 提示词都必须包含以下约束:“差异已预先获取并保存到 {DIFF_FILE} — 使用 Read 工具读取。不要运行 gh pr diff、git diff 或任何其他差异命令。绝不要使用 WebFetch、WebSearch、Skill(aikido:issues)、mcp__plugin_aikido_aikido-mcp__aikido_issues_list 或任何 gh api 端点 — 扫描证据已预先获取并在上文提供。”
    • 验证 Agent 必须审查、评估、验证并确认所有发现和评级。
    • 使用扫描证据进行交叉验证:扫描器警报佐证的发现 → 提高置信度;位于扫描器已排除问题区域的发现 → 进行额外审查。
    • 验证 Agent 必须依据步骤 2 中的阈值矩阵,将每项发现分类为:🚨 阻断项、⚠️ 改进项、📝 备注、✅ 优点或 ❌ 驳回。
    • 验证 Agent 必须简要说明每项发现的分类理由。
    • 验证 Agent 不得删除任何发现。
    • 验证 Agent 不得引入任何新发现。
  5. 格式化摘要报告。

    首先,根据审查模式设置报告标题:

    • PR 模式:PR: (#{number}) - {PR title} — {YYYY-MM-DD}
    • 提交模式:Code Review: {short SHA}..HEAD — {YYYY-MM-DD}
    • 按时间范围模式:Code Review: Changes since {duration} — {YYYY-MM-DD}
    • 本地更改模式:Code Review: Local Changes — {YYYY-MM-DD}
    • 分支比较模式:Code Review: {branch} vs {base-ref} — {YYYY-MM-DD};如果工作树有未提交更改,则随后添加一行 Pending changes: not reviewed;如果没有,则省略此行。

    然后按以下格式编写报告:

    # 🤖 Claude Security Code Review 🤖
    
    {header}
    
    **Date:** {YYYY-MM-DD}
    
    <details>
    <summary><strong>Commits reviewed:</strong> {short-sha}..HEAD · {n} commits · {path1}, {path2}</summary>
    
    | SHA     | Title          |
    | ------- | -------------- |
    | `{sha}` | {commit title} |
    
    </details>
    
    ## Summary
    
    | Category        | Count |
    | --------------- | ----- |
    | 🚨 Blockers     | {n}   |
    | ⚠️ Improvements | {n}   |
    | 📝 Notes        | {n}   |
    | ✅ Strengths    | {n}   |
    | ❌ Dismissed    | {n}   |
    
    {Up to 6 bullets. Include: overall security posture, zero-knowledge invariant status, notable positive changes, key risks or patterns worth watching, and any context that affects how findings should be interpreted. Each bullet should be one tight sentence.}
    
    ## 🚨 Blockers
    
    {Each finding: "- [Description]\n - Location: `filename.ts:42`\n - Severity: 🔴 CRITICAL | 🟠 HIGH\n - Confidence: 🟢 HIGH | 🟡 MEDIUM\n - Rationale: [Why classified as Blocker]"}
    
    ## ⚠️ Improvements
    
    {Each finding: "- [Description]\n - Location: `filename.ts:42`\n - Severity: 🔴 CRITICAL | 🟠 HIGH | 🟡 MEDIUM\n - Confidence: 🟢 HIGH | 🟡 MEDIUM\n - Rationale: [Why classified as Improvement]"}
    
    ## 📝 Notes
    
    {Each finding: "- [Description]\n - Location: `filename.ts:42`\n - Severity: 🟡 MEDIUM | 🔵 LOW | ⚪ INFO\n - Confidence: 🟢 HIGH | 🟡 MEDIUM\n - Rationale: [Why classified as Note]"}
    
    ## ✅ Strengths
    
    <details>
    <summary>Expand for details on ({n}) strengths</summary>
    
    {Each strength: "- [Description]\n - Location: `filename.ts:42`\n - Rationale: [Why this is a positive security change]"}
    
    </details>
    
    ## ❌ Dismissed
    
    <details>
    <summary>Expand for details on ({n}) dismissed findings</summary>
    
    {Each finding: "- [Description]\n - Location: `filename.ts:42`\n - Severity: 🔴 CRITICAL | 🟠 HIGH | 🟡 MEDIUM | 🔵 LOW | ⚪ INFO\n - Confidence: 🔵 LOW\n - Rationale: [Why dismissed]"}
    
    </details>
    

    如果某个部分没有发现,则完全省略该部分 — 不要输出空标题。对于 <details> 部分,如果数量为零,则完全省略。

  6. 检查 --output 参数以确定输出目的地。如果未指定 --output,则单独调用 Bash 执行 printenv GITHUB_ACTIONS,并检查其退出码是否为 0 且输出是否非空 — 如果已设置,则默认为 github;否则默认为 chat。

    输出:chat

    未指定 --output 且不在 CI 中运行时的默认值。

    1. 在聊天中直接向用户返回报告。
    2. 不要写入任何文件。
    输出:file
    1. 如果指定了 --output-dir <path>,则写入 <path>/security-review-YYYY-MM-DD-{identifier}.md。否则写入当前工作目录。
    2. {identifier} 为 PR 编号(例如 PR123)、提交 SHA(短格式)或 local。
    3. 不要使用 gh pr comment、gh api 或任何 MCP 发布工具。
    4. 写入后向用户确认文件路径。
    输出:github

    如果未指定 --output,且步骤 6 中 printenv GITHUB_ACTIONS 以退出码 0 结束并输出非空,则此项为默认值。

    1. 使用 Write 工具将报告写入 /tmp/review-summary.md。
    2. 在文件内容末尾追加 \n\n。
    3. 不要使用 gh pr comment、gh api 或任何 MCP 发布工具。
    4. 向用户确认:“已将报告写入 /tmp/review-summary.md,供工作流提取。”

    工作流的后续步骤将读取此文件,并自动更新占位评论。

  7. 清空临时差异文件。如果步骤 1B 创建了 DIFF_FILE,则在所有输出模式下,无论是否报告了发现,都使用 Write 工具将其覆盖为空字符串。该文件保留,但内容为空。步骤 1-A2 中止的运行不会创建此文件。不要报告此步骤。

评论

支持 Markdown