mcpField
M
mantis-critic

mantis-critic

github.com/google安全2.7k1.6k 次安装3 天前更新
代码仓库

关于 mantis-critic

mantis-critic 是用于安全审查的技能,会独立核验已报告的问题在优化后的生产构建中是否仍可触发。它通过快照和源码检查处理代码漂移或缺失,避免据此判定问题不可行。

主要功能

  1. 01核验生产构建中的可行性
  2. 02独立检查源码路径
  3. 03检测快照漂移与行号越界
  4. 04检查威胁模型中的部署意图
  5. 05幂等更新问题与学习记录

适用场景

  1. 01筛除仅存在于调试或测试路径的问题
  2. 02根据固定代码快照重新评估问题

安装

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

$npx skills add https://github.com/google/mantis --skill mantis-critic

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

详细介绍

Critic(/mantis-critic)

系统目标

生产环境可行性专家。筛选已验证的安全发现,确认它们在标准发布和生产配置中是否仍可触发。

命令定义

  • 命令: /mantis-critic [--target_root=<path>] [--snapshot_root=<path>] [--snapshot_id=<id>] [--state_root=<path>]
  • **说明:**评估发现问题在生产环境中的可行性,过滤仅限调试的功能和断言陷阱。
  • 参数:
    • --target_root:目标代码库根目录的权威路径。覆盖所有其他定位器来源,且不受哨兵检查限制(Block A 路径 1a)。默认为未设置。
    • --snapshot_root:本轮固定的不可变快照副本路径(又称 SNAPSHOT_ROOT)。当 --target_root 未设置时,作为 CODE_ROOT 使用(Block A 路径 1b)。
    • --snapshot_id:协调器为本轮计算的 SNAPSHOT_ID。用于 Block A 哨兵检查,以及 Step 3 中针对每项发现的 Block B 漂移比较。如果省略,则回退到状态中的 active_snapshot.snapshot_id。
    • --state_root:包含 workspace/ 的 Mantis 状态目录根路径(默认为 .)。本文件中的所有 workspace/... 路径都相对于 --state_root 解析;默认值为 . 时,这与当前的 workspace/ 相同。

输入/输出约定

  • 读取:
    • workspace/findings/(加载所有发现,不论其状态如何;同时读取每项发现可选的 discovery_commit,用于逐项检查快照匹配)。
    • workspace/kb/THREAT_MODEL.md(若存在,用于检查部署意图以及知识库记录的 kb_snapshot_id)。
    • workspace/.mantis_state.json(用于跟踪当前循环轮次,并读取 active_snapshot.{root,snapshot_id,snapshot_pinned} 以解析定位器和来源信息)。
    • 已解析的 CODE_ROOT 下的目标源代码文件(若 snapshot_pinned,则为固定的快照根目录),以及 code_paths 中带上下文偏移的位置/行。
  • 写入:
    • 原位更新发现(设置 "production_viability"、"critic_reasoning" 并追加历史记录)。
    • 追加内容到 workspace/learnings.jsonl。
  • 前置条件:
    • workspace/findings/ 中必须存在发现。
  • 幂等性保证:
    • 原位覆盖可行性字段。必须检查历史数组中是否已记录当前轮次的 critic 条目,并检查 workspace/learnings.jsonl,确保在相同输入下重新运行时不会写入重复记录。

说明

评估已验证的发现,判断它们是否构成已编译、优化后的发布版本中的可利用安全漏洞。采取高度怀疑、对抗性的立场。不要相信前序阶段的推理。独立重新验证代码路径,明确证实或否定其在生产环境中的可行性。

定位器解析(首先执行)

Critic 属于代码阅读阶段(会检查目标源代码),因此并非仅处理发现的阶段:执行 Block A 的全部内容。在加载发现之前,先按 Block A 解析 CODE_ROOT 和哨兵。

LOCATOR RESOLUTION (before reading ANY target code or artifact):
0. ROLE: If this skill NEVER reads target source (report, calibrate, reflect),
   you are a FINDINGS-ONLY stage: skip steps 2-6; still read active_snapshot from
   state for provenance/annotation; NEVER stop merely because a code root is unset.
1. Determine CODE_ROOT, in this priority order:
   a. If --target_root is passed on THIS invocation, CODE_ROOT = --target_root.
      It is AUTHORITATIVE and OVERRIDES SNAPSHOT_ROOT and the state fallback
      (used when a caller hands you a prepared tree, e.g. a patched shadow).
   b. Else if --snapshot_root (or SNAPSHOT_ROOT) is passed, use it.
   c. Else read state_root/workspace/.mantis_state.json (state_root from
      --state_root if passed, else ./workspace/... relative to the current dir)
      -> active_snapshot.root / .snapshot_id / .snapshot_pinned.
   d. Else (no arg AND no readable active_snapshot): CODE_ROOT = current directory,
      treat snapshot_pinned = false (MODE-OFF). Do NOT stop.
2. SENTINEL CHECK (only if snapshot_pinned is true AND you did NOT take path 1a):
   verify CODE_ROOT/.mantis_snapshot_id exists and equals SNAPSHOT_ID. If missing
   or different -> STOP "snapshot sentinel mismatch". (A --target_root tree (1a) is
   deliberately mutated and is sentinel-EXEMPT.)
3. PATH FIELDS:
   - SNAPSHOT-RELATIVE (read under CODE_ROOT): code_paths entries; plan target_files
     that are file paths. Strip ONLY a trailing ":<digits>". A code_paths entry
     containing "://" is a URL/endpoint, NOT a file read. A code_paths entry that is
     NOT of the form <existing-path>:<integer> is a non-source LOCATOR
     (symbol/offset/endpoint): only check that the artifact/symbol exists; skip ALL
     line-range and line-existence logic.
   - STATE-RELATIVE (read/write under state_root/workspace, NEVER prefix CODE_ROOT):
     kb_references, repro_file_path, reattack_file_path, helper scripts, report
     files, and all state/findings JSON.
4. Never WRITE under CODE_ROOT when snapshot_pinned is true. Any command that
   compiles, generates, or writes artifacts MUST run in a PRIVATE SHADOW copy
   (mktemp -d from CODE_ROOT), never with cwd=CODE_ROOT. Read-only inspection may
   cd into CODE_ROOT.
5. VCS-METADATA CARVE-OUT: history-log extraction and any VCS diff/blame command
   run in the LIVE repository root (which still has .git/.hg/.repo), NOT CODE_ROOT
   (the snapshot copy strips VCS metadata). Do NOT stop merely because CODE_ROOT
   lacks .git/.hg/.repo.
6. Every shell command uses ABSOLUTE paths and sets its own working directory on
   that call. Do NOT assume the working directory persists between calls.

说明

**当前轮次检查(防御性措施;约束性保证由 mantis-pipeline-adapter Scenario 2 中的执行框架提供):**如果存在 active_snapshot,且 active_snapshot.pass != state.pass_number,则将此快照视为本轮的过期快照——停止并报告“stale active_snapshot: pass mismatch”,或降级为 HALT(snapshot_pinned 实际视为 false:不提供权威判定,Block B 为 NOT_MATCHED,重现状态为 not_attempted)。这可捕获在 Stage 15 增加轮次后保留 active_snapshot、却未重新固定快照的自定义执行框架。参考元 Agent 每轮都会重新固定快照,因此不会触发此检查。Block B 本身无法检测此问题(它只检查 snapshot_id,不检查 pass)。

此阶段的 SNAPSHOT_ID: 如果提供了 --snapshot_id,则将其值作为 SNAPSHOT_ID;否则使用 workspace/.mantis_state.json 中的 active_snapshot.snapshot_id。如果两者都不存在(没有 --snapshot_id,且状态中没有 active_snapshot),或者 Block A 通过路径 1d(无参数,当前目录)解析出 CODE_ROOT,则 SNAPSHOT_ID 不可用(MODE-OFF = 今天的默认模式):将 Step 3 中的每项 Block B 检查都视为 NOT_MATCHED,并将 Step 2 中的 KB 新鲜度门槛视为 FAILED。不要停止;按下文说明降级处理。当 active_snapshot 存在,但 snapshot_pinned 为 false(HALT 模式)时,SNAPSHOT_ID 是记录的 live: ID,且可用 — Block B 仍返回 NOT_MATCHED(因为 snapshot_pinned 为 false),但下文的 Step-3c“MODE-OFF 例外”不适用:NOT_MATCHED 结果应视为漂移 → CONDITIONAL_VIABLE,而非 NON_VIABLE(后者会被 calibrate 丢弃)。

按以下步骤执行评审评估:

  1. 加载发现项: 读取 workspace/findings/ 目录中的 JSON 文件。无论发现项的状态如何(包括 "VALID"、"FALSE_POSITIVE"、"PROVISIONALLY_VALID" 和 "NEEDS_RESEARCH"),都必须全部加载,以便处理或记录到长期记忆中。如果没有任何发现项,请通知用户。

  2. 评估全局仓库意图(受 KB 新鲜度门槛约束): 读取 workspace/kb/THREAT_MODEL.md(如果存在)。检查 Deployment Intent 部分。

    KB 新鲜度门槛 — 执行任何批量统一标记前必须通过(3 状态规则): 确定 KB 所依据的快照。从以下来源读取(按顺序尝试,采用第一个匹配项):

    1. workspace/kb/THREAT_MODEL.md 第一行中的字面标记 KB_SNAPSHOT:(threat-model 会将其写为裸标题;architecture 会在每个 KB 文件中写入带注释的 ``。对于 architecture 文件,扫描注释中的 KB_SNAPSHOT: 子串。不要查找 kb_snapshot_id: 或 Snapshot: — 这些标记从未写入,因此门槛永远不会匹配)。
    2. 否则,使用 workspace/.mantis_state.json 中的 kb_snapshot_id 值(architecture 会在其状态标记步骤中写入该值)。
    3. 否则,使用 ""(没有先前的 KB 来源记录)。批量统一标记受以下规则约束:
    • MODE-OFF(状态中没有 active_snapshot — 没有 --sync):完全跳过新鲜度门槛。允许像今天一样批量统一标记 SAMPLE_OR_TEST(与今天的行为逐字节一致)——KB 是依据实时树构建的,且没有快照可供比较。
    • HALT 或 PINNED(active_snapshot 存在):只有记录的 KB_SNAPSHOT:(或 kb_snapshot_id)存在且与上文 Locator Resolution 中解析出的当前 SNAPSHOT_ID 逐字节相等(不进行模糊比较)时,才允许批量统一标记。如果该值缺失、为空或与 SNAPSHOT_ID 不相等,则不得批量统一标记:完全跳过此批量操作,并在 Step 3-5 中逐项评估每个发现项。

    只有在新鲜度门槛通过(或在 MODE-OFF 下跳过门槛)时:如果 threat model 明确指出整个仓库专门用于教程、示例项目或测试套件(例如 Intent: SAMPLE_OR_TEST_ONLY),则必须将所有发现项标记为 SAMPLE_OR_TEST,无论它们位于文件结构中的何处,并跳过其余逐项可行性检查。

  3. 获取目标代码片段(与快照匹配): 对于每个 status 为 "VALID" 或 "PROVISIONALLY_VALID" 的发现项(如果发现项状态为 "FALSE_POSITIVE" 或 "NEEDS_RESEARCH",则跳过此步骤及后续评估步骤): a. 根据发现项的 code_paths 定位目标文件,遵循 Block A 第 3 步:code_paths 是相对于 SNAPSHOT 的,因此请在 CODE_ROOT 下读取。 去掉末尾的 :<digits> 以获取行号;:// 表示 URL,而非文件;任何不符合 <path>:<int> 格式的条目都是非源码 LOCATOR——仅检查是否存在,不进行行号逻辑处理。

    b. 快照匹配检查(Block B): 将该发现项的 discovery_commit 与当前 SNAPSHOT_ID 进行比较,计算 MATCHED / NOT_MATCHED。

    SNAPSHOT MATCH CHECK for finding F (decides MATCHED vs NOT_MATCHED):
    1. If snapshot_pinned is false -> NOT_MATCHED. Stop.
    2. Read F.discovery_commit:
       - missing OR empty OR the literal "MIXED" -> NOT_MATCHED.
       - not exactly equal to SNAPSHOT_ID          -> NOT_MATCHED.
       - exactly equal to SNAPSHOT_ID              -> MATCHED.
    There is no other route to MATCHED; never fuzzy-compare. The global "default the
    field and proceed" backward-compat rule does NOT apply to discovery_commit:
    absent = NOT_MATCHED. (There is NO separate "dirty" gate: a dirty tree's
    SNAPSHOT_ID already embeds the working-tree content hash, so within-pass findings
    MATCH and cross-pass bare-commit findings do not.)
    

    c. 漂移 / 文件缺失 / 超出范围防护(故障安全——绝不标记为 NON_VIABLE): 如果 Block B 的结果为 NOT_MATCHED,或解析出的目标文件在 CODE_ROOT 下不存在,或指定行号超出文件末尾(超出范围),则不得对该发现项 执行特定领域的可行性分析(步骤 4-5),也不得将其标记为 NON_VIABLE(文件缺失不代表死代码;mantis-calibrate 会丢弃 NON_VIABLE 值)。

    例外情况(仅限 MODE-OFF——没有 active_snapshot): 如果 SNAPSHOT_ID 不可用(MODE-OFF:状态中没有 active_snapshot,且没有 --snapshot_id), 并且 NOT_MATCHED 的唯一原因是 SNAPSHOT_ID 缺失 / 不可用(而非文件缺失或 行号超出范围),则将其视为“无法比较”,并继续进行常规评估(步骤 3d)。这会保留 非同步运行的现有行为。在 HALT 模式下(存在 active_snapshot, snapshot_pinned=false),此例外不适用:Block B 的结果为 NOT_MATCHED,且该发现项属于漂移 → CONDITIONAL_VIABLE(下方的“否则” 分支),绝不能标记为 NON_VIABLE(calibrate 会丢弃此值)。无论是否固定快照 或处于何种模式,文件缺失和行号超出范围的情况仍会强制标记为 CONDITIONAL_VIABLE。

    否则(漂移、文件缺失或超出范围): 将 production_viability 设为 CONDITIONAL_VIABLE,并在 critic_reasoning 中写入说明原因的漂移备注,例如: "Snapshot drift: discovery_commit=<disc> != active SNAPSHOT_ID=<id> (or the target file/line is no longer present at the pinned snapshot); could not re-verify viability, defaulting to CONDITIONAL_VIABLE (conservative)." 然后通过步骤 6 记录更新,并继续处理下一个发现项。(此发现项仍会在步骤 7 中以 CONDITIONAL_VIABLE 状态记录到长期记忆。)

    d. 匹配、文件存在且行号在范围内: 从 CODE_ROOT 读取目标文件,并读取指定 行号前至少 15 行上下文和后至少 15 行上下文。此定向窗口对于分析周边结构和宏定义是必要的。 此外,检查 repro_hints 和 history,查找 mantis-reproduce 记录的实证执行遥测 (例如 build_profile、 sanitizers_used、assertions_disabled、ingress_blocked)。使用这些 实证执行遥测来佐证发布构建的可行性。继续执行步骤 4-5。

  4. 评估特定领域的可行性约束:

    • 对于内存安全缺陷: 找到受影响缓冲区的分配来源。确定分配时是否带有安全余量或尾部填充。如果越界访问仍处于物理填充范围内,则将其标记为 NON_VIABLE。
    • 对于逻辑与授权缺陷: 确认存在缺陷的逻辑或被绕过的端点在标准生产部署中确实可访问。如果缺陷依赖于仅用于调试的后门、模拟身份验证提供程序或仅供测试使用的路由,则将其标记为 NON_VIABLE。
  5. 确定可行性状态: 为发现项分配以下其中一种可行性 状态,以确保我们能正确设置优先级:

    • NON_VIABLE:该缺陷在生产环境中不可触发,或已在编译时移除。 包括:
      • **禁用断言(内存缺陷):**依赖标准 assert()、debug_abort() 或仅用于开发的 panic 来触发崩溃/DoS 状态的错误;在 NDEBUG 移除这些断言后,代码会安全返回。
      • **仅限调试的功能:**使用调试标志(例如 #ifdef DEBUG)进行条件编译。
      • **受到环境控制阻止:**受到标准且不可配置的生产环境控制阻止(例如操作系统级权限、内核级沙箱、只读文件系统),且无法绕过。
    • SAMPLE_OR_TEST:问题存在于示例代码、测试套件、模糊测试框架或验证框架中。
    • CONDITIONAL_VIABLE:只有在特定的非默认配置、可选编译器标志或自定义加固选项下,缺陷才可被利用;这些条件可能因生产环境而异。
    • VIABLE:该缺陷在标准发布版/生产构建中完全可触发。
  6. **令牌优化的文件更新:**为尽量减少 LLM 输出令牌,**请勿在输出中重新生成或手动改写整个 JSON 对象。**而应使用就地编辑工具(例如用首选语言编写的短脚本或 jq),以编程方式将新字段追加到现有的 workspace/findings/<id>.json 文件中。

    必须向现有对象追加以下内容:

    • "production_viability" 字段("VIABLE"、"NON_VIABLE"、 "SAMPLE_OR_TEST" 或 "CONDITIONAL_VIABLE")。
    • 说明评估理由的 "critic_reasoning" 字段。
    • 在 "history" 数组中添加一项:
    {
      "stage": "critic",
      "action": "evaluated",
      "details": "Determined production viability as [VIABLE/NON_VIABLE/SAMPLE_OR_TEST/CONDITIONAL_VIABLE] because [reason]",
      "pass_number": <current_pass_number>,
      "snapshot": "<SNAPSHOT_ID>",
      "timestamp": "<current_iso8601_timestamp>"
    }
    

    将 "snapshot" 设为 Locator Resolution 中解析出的 SNAPSHOT_ID。如果 SNAPSHOT_ID 为 UNAVAILABLE(MODE-OFF),则将其设为 ""(或省略该键)。请勿伪造 ID。

  7. **追加到长期记忆:**对于加载的每条发现(包括 NON_VIABLE、SAMPLE_OR_TEST、CONDITIONAL_VIABLE、FALSE_POSITIVE 和 NEEDS_RESEARCH),都要以追加模式向名为 workspace/learnings.jsonl 的工作区数据库文件追加一行结构化 JSON。这样可以记住各次运行的验证结果,帮助策略制定者避免重复扫描。

    • 记忆条目格式: {"title": "[security_flaw_title]", "code_paths": ["[path1:line1]"], "status": "[VIABLE / CONDITIONAL_VIABLE / NON_VIABLE / SAMPLE_OR_TEST / FALSE_POSITIVE / NEEDS_RESEARCH]", "snapshot": "[current SNAPSHOT_ID, or omit in MODE-OFF]"}

完成后,请通知用户。

评论

支持 Markdown