mantis-researcher
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMantis Researcher (/mantis-researcher)
Mantis Researcher(/mantis-researcher)
System Goal
系统目标
Resilience Code Auditor. Performs rapid triage and deep-dive reviews of source
files to identify boundary checks, preconditions, missing sanitization, and
interface violations.
韧性代码审计工具。对源文件执行快速分类和深度评审,以识别边界检查、前置条件、缺失的 sanitization 以及接口违规问题。
Command Definition
命令定义
- Command:
/mantis-researcher - Description: Audits production source code files based on the strategy in
.
workspace/plan.json - Arguments (optional; supplied by the orchestrator, consumed by Block A):
- /
--snapshot_root: absolute path to the pinned, read-only code snapshot for this pass. This is the CODE_ROOT that all snapshot-relative path fields resolve against (Block A step 1b).SNAPSHOT_ROOT - /
--snapshot_id: the pass snapshot identifier. Used for the sentinel check (Block A step 2) and stamped verbatim into every finding'sSNAPSHOT_ID.discovery_commit - : absolute path to the
--state_rootstate directory (workspace/,plan.json,.mantis_state.json,findings/). State paths are STATE-RELATIVE and are NEVER prefixed with CODE_ROOT (Block A step 3).kb/ - (authoritative override, Block A step 1a) is also honored if supplied.
--target_root - All flags absent -> DEGRADED/legacy mode: CODE_ROOT falls back to the
current directory, is treated as false, and behavior matches today's exactly (no
snapshot_pinnedis written).discovery_commit
- 命令:
/mantis-researcher - 描述: 基于中的策略审计生产源代码文件。
workspace/plan.json - 参数(可选;由编排器提供,供A模块使用):
- /
--snapshot_root:本次审计所用的固定只读代码快照的绝对路径。这是所有快照相对路径字段的解析根目录(见A模块第1b步)。SNAPSHOT_ROOT - /
--snapshot_id:本次审计的快照标识符。用于哨兵检查(A模块第2步),并原样标记到每个发现结果的SNAPSHOT_ID字段中。discovery_commit - :
--state_root状态目录的绝对路径(包含workspace/、plan.json、.mantis_state.json、findings/)。状态路径为状态相对路径,绝对不能以CODE_ROOT作为前缀(见A模块第3步)。kb/ - 如果提供(权威覆盖,A模块第1a步),也会生效。
--target_root - 所有标志缺失 -> 降级/遗留模式: CODE_ROOT回退到当前目录,视为false,行为与当前完全一致(不会写入
snapshot_pinned)。discovery_commit
Input/Output Contract
输入/输出约定
- Reads:
- (falls back to codebase sweep if missing/empty).
workspace/plan.json - (to track current loop pass).
workspace/.mantis_state.json - referenced Markdown files in (e.g.
"kb_references").workspace/kb/entities/*.md - Target source code files.
- (to check structural index availability/status).
workspace/kb/structural_index/manifest.json - (to invoke bounded structural-index queries).
workspace/helpers/query_structural_index.py
- Writes:
- Raw finding files to (creates
workspace/findings/<uuid>.jsonif missing).workspace/findings/
- Raw finding files to
- Preconditions:
- Target files must be accessible.
- Idempotency Guarantee:
- Writes new findings as separate files with unique UUIDs. Rely on
to cluster and merge duplicate findings on subsequent steps.
mantis-dedupe
- Writes new findings as separate files with unique UUIDs. Rely on
- 读取:
- (若缺失/为空则回退到代码库全量扫描)。
workspace/plan.json - (用于跟踪当前循环审计次数)。
workspace/.mantis_state.json - 中引用的Markdown文件(例如
"kb_references")。workspace/kb/entities/*.md - 目标源代码文件。
- (用于检查结构索引的可用性/状态)。
workspace/kb/structural_index/manifest.json - (用于调用有限范围的结构索引查询)。
workspace/helpers/query_structural_index.py
- 写入:
- 将原始发现结果写入(若
workspace/findings/<uuid>.json目录不存在则自动创建)。workspace/findings/
- 将原始发现结果写入
- 前置条件:
- 目标文件必须可访问。
- 幂等性保证:
- 将新发现结果写入带有唯一UUID的独立文件。后续步骤依赖来聚类和合并重复的发现结果。
mantis-dedupe
- 将新发现结果写入带有唯一UUID的独立文件。后续步骤依赖
Instructions
操作说明
Step 0: Locator Resolution (Snapshot-Aware Path Handling)
步骤0:定位器解析(快照感知路径处理)
Run this BEFORE the numbered research steps below. It fixes the single CODE_ROOT
that every / reference in this stage resolves
against, so all sub-agents audit the same pinned snapshot.
target_filescode_pathsLOCATOR 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.Skill-specific notes for the researcher:
- The researcher is a CODE-READING stage, so Block A step 0's findings-only skip does NOT apply here — you MUST resolve CODE_ROOT and honor the sentinel.
workspace/plan.jsonand findingtarget_filesare SNAPSHOT-RELATIVE: resolve them under CODE_ROOT (Block A step 3).code_paths- ,
kb_references,workspace/plan.json, and everything underworkspace/.mantis_state.jsonare STATE-RELATIVE: read/write them under --state_root, NEVER under CODE_ROOT.workspace/findings/ - Never write, compile, or generate anything under CODE_ROOT when pinned (Block A step 4).
Perform a thorough memory-safety, logical-correctness, and robustness review of
the targeted codebase.
Execute the research stage as follows:
-
Load Reviewing Plan & Context: Read the active pass number fromand resolve the current ISO 8601 timestamp. Read the
workspace/.mantis_state.jsonfile to retrieve the target investigations. Ifworkspace/plan.jsonis missing or empty, perform a general list of the directories and review any primary source files. If the investigation contains aworkspace/plan.jsonarray, explicitly read those Markdown files (e.g.,"kb_references") to gain compounded historical context before you begin auditing theworkspace/kb/entities/auth.md. Also read"target_files"fromactive_snapshot(workspace/.mantis_state.json,root,snapshot_id). Holdsnapshot_pinnedin memory: it is the value you will stamp into every finding'sactive_snapshot.snapshot_id(see Findings Schema Format). Ifdiscovery_commitis absent oractive_snapshotis false, you are in DEGRADED/legacy mode — do NOT stop (Block A step 1d); you will simply omitsnapshot_pinned.discovery_commit -
Sub-Agent Delegation (Wave-Based Swarm Parallelization): If the CLI or agent platform supports spawning sub-agents (e.g., using specialized sub-agent tools or multi-agent orchestrator directives):
-
Do not execute investigations sequentially if sub-agents are supported. Split the investigations ininto parallel waves to maximize throughput and context efficiency.
workspace/plan.json -
Wave 1: Lightweight Rapid Triage (Concurrency Peak): Spawn concurrent, lightweight sub-agents (e.g. up to 10-20 in parallel) to sweep all files listed in. Each sub-agent should only output a fast classification:
workspace/plan.json.{"potentially_flawed": true/false, "reason": "..."} -
Wave 2: Deep Security Flaw Hotspot Audits & Parallel Trajectory Search: Collect all files flagged in Wave 1. Spawn a wave of concurrent deep auditor sub-agents (e.g. up to 4-8 in parallel) to focus exclusively on those identified hotspots. For particularly complex files, spawn multiple subagents targeting the same file using either different prompt constraints or a diverse set of less expensive LLMs to explore parallel attack vectors. Rely on the subsequent deduplication stage to merge any overlapping findings.
-
Token Optimization (Distributed Writes): Instruct the Wave 2 sub-agents to generate unique UUIDs and write their findings directly to individualfiles on disk. Do not ask them to return the full JSON payload in their messages back to you, as aggregating them will blow out your context window. Ask them to only return the list of UUIDs they created.
workspace/findings/<id>.json -
Snapshot Isolation (Wave Pinning) — MANDATORY: Pass the SAME(CODE_ROOT resolved in Step 0) and the same
--snapshot_rootvalue to EVERY Wave-1 and Wave-2 sub-agent, and instruct each of them to obey Block A (resolve--snapshot_id/target_filesunder that CODE_ROOT, honor the sentinel). Any Wave-2 sub-agent that writes a finding MUST stampcode_pathswith that snapshot_id, exactly as specified in the Findings Schema Format. Sub-agents MUST NOT rundiscovery_commit/git pull/fetch/checkout,reset/hg pull,update, or any command that changes the working tree or switches revisions — the snapshot is immutable for the whole pass. A sub-agent that cannot see the snapshot must report that, not re-sync.repo sync -
If sub-agents or concurrency are not supported by the current environment, fall back to performing the sweeps and deep-dives sequentially.
-
Structural Index (HINT-only enhancement): When a structural index is available (exists), use it to SUPPLEMENT the wave-based swarm above. The structural index decides ORDER, never MEMBERSHIP. It MUST NEVER replace the exhaustive Step-3 call-site sweep.
workspace/kb/structural_index/manifest.json-
Resolution-first protocol (MANDATORY before any structural query):
- Resolve the symbol first:
python3 workspace/helpers/query_structural_index.py resolve_symbol --name "<function_name>" [--language "<lang>"] [--file "<path>"] --state_root <state_root> - If the response has , investigate ALL matched symbols — never silently pick one. Narrow with
ambiguous: true/--fileif possible, or schedule investigations for every matched symbol.--language - Use the resolved for bounded queries:
symbol_idpython3 workspace/helpers/query_structural_index.py find_callers --symbol_id "<id>" --limit 100 --offset 0 --state_root <state_root>
- Resolve the symbol first:
-
Coverage-aware interpretation: Checkin every structural index response:
coverage.partition_status- +
complete+ empty results = "no indexed callers" (authoritative for indexed code — still run grep per the HINT-only rule).precision == semantic - +
complete+ empty results = "no indexed callers" — NOT authoritative. MUST run exhaustive grep.precision != semantic - /
partial/empty+ empty results = "not fully indexed" — MUST run exhaustive grep.failed - Use the and
precisionfields on every result to weight trust (backend>semantic>typecheck>ast>symbol-only>heuristic>deferred).coverage-only
-
Wave 1 (Rapid Triage): Useto SUPPLEMENT grep as a ranking HINT — ORDER, never MEMBERSHIP. Structural index results prioritize which files to flag as
find_callers(); they MUST NEVER replace the exhaustive Step-3 call-site sweep. Audit the union of grep results and structural index results.potentially_flawed -
Wave 2 (Deep Audit): Useto start with the enclosing function, then expand to callers/callees/file as needed for deep dive context.
get_function_boundary(file, line) -
Graceful degradation: If the structural index is absent (no), empty, or the query helper is missing, fall back to grep-based discovery (today's behavior). The structural index is a coverage HINT only.
manifest.json
-
-
-
Exhaustive Interface and Call-Site Reviewing: If a target source file defines public or API functions (such as numeric parsers, decoders, encoders, or converters) that document explicit size constraints or safety requirements (e.g., expecting callers to allocate buffers of a certain size):
- Run a repo-wide grep for the function name to build the exhaustive set of
candidate call-sites — this is the mandatory floor. Then use the structural
index query helper (then
resolve_symbol) to RANK and prioritize which call-sites to audit first (the index distinguishes actual calls from comments/strings/variable names). Audit the union of both result sets — the structural index may miss macro-based calls, function pointers, and dynamic dispatch, so grep remains the floor.find_callers - Search the codebase to find and review all call-sites of these functions across the entire repository to ensure the safety contracts are respected globally.
- Read the calling files and verify if every call-site strictly adheres to input constraints, properly manages bounds, and checks sizes.
- Flag any discrepancies as contract alignment bugs or missing checks.
- Run a repo-wide grep for the function name to build the exhaustive set of
candidate call-sites — this is the mandatory floor. Then use the structural
index query helper (
-
Unconstrained / Exploratory Investigations: If the investigation plan incontains instructions or a question explicitly asking for an unconstrained sweep, adversarial audit, or random exploration:
workspace/plan.json- Ignore existing assumptions of safety and documented trust boundaries in
.
workspace/kb/THREAT_MODEL.md - Treat all inputs and boundaries as untrusted and potentially malformed.
- Analyze implementation from scratch with full freedom and autonomy.
- If it is a random exploration/digging task with minimal instructions, focus on mapping the behavior of the target files, identifying key entry points, and looking for unexpected side effects or boundary cases without being constrained by a specific threat model.
- Ignore existing assumptions of safety and documented trust boundaries in
-
Compile and Write Findings: Instead of a single monolithic file, create adirectory if it does not exist. For each potential finding, generate a unique UUID and write a valid JSON object into an individual file named
workspace/findings/. This keeps findings isolated and prevents token limit issues during subsequent analysis. Do not include any text before or after the JSON in the files.workspace/findings/<id>.jsonSignature & Lineage Computation (Step 5a):For each finding, compute,signature, and optionallylineage_idBEFORE writing the JSON file:cwe-
(optional): If the vulnerability maps to a known CWE, set the
cwefield to the identifier (e.g.,cwe,"CWE-787"). If no CWE applies, omit the field. This is an input to the signature."CWE-416" -
Compute(deterministic content-identity hash):
signature- = finding
normalized_title, lowercased, with ALL non-alphanumeric characters stripped (ASCIItitleonly; all other characters including Unicode letters, punctuation, and spaces are removed). If stripping leaves[a-zA-Z0-9]EMPTY (e.g. a title composed entirely of non-ASCII/Unicode characters), setnormalized_title= the first 16 hex chars ofnormalized_titleso two distinct non-ASCII titles do not collide on the empty string.sha256(<original raw title as UTF-8 bytes>) - = the finding's
cwe_partfield if present and non-null, else the empty string.cwe - = first
primary_targetentry with trailingcode_pathsstripped (e.g.,:line→src/auth.c:145). Ifsrc/auth.cis empty, or the first entry is a non-source LOCATOR (URL containingcode_paths, or a non-file symbol/offset per Block A step 3), use the empty string. Order://deterministically (primary sink first, kept stable across passes) socode_paths— and thus theprimary_target— does not drift between passes. (If the order is unstable the only cost is a missed lineage inheritance → the finding is over-reported as new, never hidden — but stable order preserves the cross-pass fold.)signature - If is non-empty:
primary_target= first 16 hex characters ofsignature.sha256(normalized_title + "|" + cwe_part + "|" + primary_target) - If is empty:
primary_target= first 16 hex characters ofsignature.sha256(normalized_title + "|" + cwe_part + "|" + sorted(code_paths).join(",")) - Compute the signature ONCE at finding creation and NEVER recompute,
edit, or invent it (same rule as ).
discovery_commit
-
Compute(cross-pass lineage chain):
lineage_id- Scan and
workspace/archive/findings_pass_*/for any archived finding JSON whoseworkspace/archive/loop*_findings/field equals this finding's computedsignature.signature - If a match is found: = the archived ancestor's
lineage_id(inherit the lineage chain so consumers can fold across passes). If MULTIPLE archived findings share the same signature, inherit from the MOST RECENT (highest pass number) ancestor. All ancestors with the same signature SHOULD share the same lineage_id; if they don't, inherit from the most recent one and log a warning.lineage_id - If no match by exact signature: attempt a basename rename fallback
for TRUE renames ONLY. Do NOT recompute the signature (it is computed
ONCE at creation and never recomputed — invariant #4). Instead:
- Compute the CURRENT finding's basename: take (the first
primary_targetentry with trailingcode_pathsstripped, already computed for the signature at step 2) and take its basename (e.g.:line→src/auth.c). Ifauth.cis empty (non-source LOCATOR or empty code_paths), SKIP this fallback — go to fresh UUIDv4 below.primary_target - For each archived finding located in step 3's archive scan:
reconstruct the ARCHIVED finding's basename from its stored
(strip the trailing
code_paths[0], take the basename — e.g.:line→lib/old_auth.c:88). Do NOT recompute the archived finding's signature and do NOT compare signatures; compare the two basenames as STRINGS.old_auth.c - Inherit an ancestor's via this fallback ONLY IF (i) the basenames match AND (ii) the ancestor's full
lineage_id(itsprimary_targetwithcode_paths[0]stripped) NO LONGER EXISTS on the current snapshot (check that the old full path is absent under CODE_ROOT — this distinguishes a real rename from a second, independent file that merely shares a basename). If MULTIPLE archived ancestors satisfy (i) and (ii), inherit from the MOST RECENT (highest pass number). When you inherit via basename, ALSO add a finding history note:lineso downstream consumers treat the link as basename-derived (the report folds two findings only when their fulllineage-via-basename-renames ALSO match per the SAME-BUG predicate, so a basename-derived lineage link never collapses distinct bugs).signature - If the old full path still exists on the current snapshot, do NOT inherit — treat as no match (fresh UUIDv4 below).
- Compute the CURRENT finding's basename: take
- If no match by either exact signature or basename rename: = a fresh UUIDv4.
lineage_id - These are STATE-RELATIVE paths (Block A step 3) — read under
, NEVER under CODE_ROOT.
--state_root/workspace/archive/
- Scan
-
Write all three fields (,
cwe,signature) into the finding JSON alongsidelineage_id.discovery_commit
Mode-independence: Unlike(which is omitted in DEGRADED/legacy mode),discovery_commitandsignatureare ALWAYS computed — they are content-identity fields, not snapshot-identity. They work regardless of whether the snapshot is pinned, unpinned, or absent (MODE-OFF). This is a deliberate Phase-3 improvement: in MODE-OFF, the finding JSON now carries 2–3 extra optional keys (lineage_id,cwe,signature) that did not exist in Phase 1. This does NOT change the snapshot model (nolineage_id, no sync, no pin — the 3-state rule is unaffected). The dedupe MODE-OFF fallback now prefersactive_snapshotoversignaturewhen present, which is strictly MORE discriminating (signature addsstable_keyto the title+path mix), so it can only SPLIT entries thatcwewould have merged (fresh budgets, more conservative = safe) — it can never produce a false merge or suppress a legitimate finding. Absentstable_key→ today'ssignaturebehavior exactly.stable_keyMissing or unreadable target file: If a path indoes not exist or cannot be read under CODE_ROOT (e.g. it was deleted or renamed since the plan was written), do NOT fabricate a finding, a line number, or file contents. Skip that target and record the skip plainly in thetarget_filesof any finding it relates to (or omit it entirely). Never invent code you did not read.descriptionNon-source targets: For a non-source LOCATOR (binary, firmware, or a URL endpoint — see Block A step 3), theentry MUST be a STABLE LOCATOR (symbol name, offset, or the bare path) WITHOUT acode_pathssuffix. Do not attach a fabricated line number to a target you cannot open as text.:line -
在执行以下编号的研究步骤之前先运行此步骤。它会确定一个统一的CODE_ROOT,此阶段中所有/引用都将基于该根目录解析,确保所有子代理审计同一个固定快照。
target_filescode_pathsLOCATOR RESOLUTION(读取任何目标代码或工件之前执行):
0. 角色:如果此技能从不读取目标源代码(仅用于报告、校准、反思),则属于仅处理发现结果的阶段:跳过步骤2-6;仍需从状态中读取active_snapshot以用于来源追踪/标注;绝不能仅因代码根目录未设置而停止。
1. 按以下优先级确定CODE_ROOT:
a. 如果本次调用传入了--target_root,则CODE_ROOT = --target_root。它具有权威性,会覆盖SNAPSHOT_ROOT和状态回退值(用于调用者提供预先生成的代码树的场景,例如已打补丁的影子副本)。
b. 否则,如果传入了--snapshot_root(或SNAPSHOT_ROOT),则使用该值。
c. 否则读取state_root/workspace/.mantis_state.json(若传入了--state_root则使用该路径下的state_root,否则使用当前目录下的./workspace/...)中的active_snapshot.root / .snapshot_id / .snapshot_pinned。
d. 否则(无参数且无可用的active_snapshot):CODE_ROOT = 当前目录,视为snapshot_pinned = false(模式关闭)。请勿停止。
2. 哨兵检查(仅当snapshot_pinned为true且未采用路径1a时执行):
验证CODE_ROOT/.mantis_snapshot_id存在且等于SNAPSHOT_ID。若缺失或不匹配 -> 停止并提示"snapshot sentinel mismatch"。(--target_root对应的代码树(1a)是故意修改的,无需进行哨兵检查。)
3. 路径字段:
- 快照相对路径(在CODE_ROOT下读取):code_paths条目;plan中的target_files(文件路径形式)。仅去除末尾的":<数字>"。包含"://"的code_paths条目是URL/端点,而非文件路径。不符合<现有路径>:<整数>格式的code_paths条目是非源代码定位器(符号/偏移量/端点):仅需检查工件/符号是否存在;跳过所有行范围和行存在性逻辑。
- 状态相对路径(在state_root/workspace下读取/写入,绝不能以CODE_ROOT为前缀):kb_references、repro_file_path、reattack_file_path、辅助脚本、报告文件,以及所有状态/发现结果JSON。
4. 当snapshot_pinned为true时,绝不能在CODE_ROOT下写入内容。任何编译、生成或写入工件的命令必须在CODE_ROOT的私有影子副本(使用mktemp -d创建)中运行,绝不能以CODE_ROOT为工作目录。仅只读检查可以进入CODE_ROOT目录。
5. VCS元数据例外:历史日志提取以及任何VCS diff/blame命令必须在实时仓库根目录(包含.git/.hg/.repo)中运行,而非CODE_ROOT(快照副本会剥离VCS元数据)。绝不能仅因CODE_ROOT缺少.git/.hg/.repo而停止。
6. 所有shell命令使用绝对路径,并在调用时设置自己的工作目录。请勿假设工作目录在调用之间保持不变。针对Researcher的技能特定说明:
- Researcher属于代码读取阶段,因此A模块步骤0中仅处理发现结果的跳过规则不适用——必须解析CODE_ROOT并遵守哨兵检查。
- 中的
workspace/plan.json和发现结果的target_files均为快照相对路径:需在CODE_ROOT下解析(见A模块第3步)。code_paths - 、
kb_references、workspace/plan.json以及workspace/.mantis_state.json下的所有内容均为状态相对路径:需在--state_root下读取/写入,绝不能在CODE_ROOT下操作。workspace/findings/ - 当快照固定时,绝不能在CODE_ROOT下写入、编译或生成任何内容(见A模块第4步)。
对目标代码库进行全面的内存安全、逻辑正确性和健壮性评审。
按以下步骤执行研究阶段:
-
加载评审计划与上下文: 从读取当前审计次数,并解析当前ISO 8601时间戳。读取
workspace/.mantis_state.json文件以获取目标调查内容。如果workspace/plan.json缺失或为空,则列出所有目录并评审主要源代码文件。如果调查内容包含workspace/plan.json数组,在开始审计"kb_references"之前,需明确读取这些Markdown文件(例如"target_files")以获取综合历史上下文。同时从workspace/kb/entities/auth.md读取workspace/.mantis_state.json(active_snapshot、root、snapshot_id)。将snapshot_pinned保存在内存中:该值将标记到每个发现结果的active_snapshot.snapshot_id字段中(见发现结果 Schema 格式)。如果discovery_commit缺失或active_snapshot为false,则处于降级/遗留模式——请勿停止(见A模块第1d步);只需省略snapshot_pinned字段即可。discovery_commit -
子代理委派(基于波次的集群并行化): 如果CLI或代理平台支持生成子代理(例如使用专用子代理工具或多代理编排指令):
-
如果支持子代理,请勿按顺序执行调查任务。将中的调查任务拆分为并行波次,以最大化吞吐量和上下文效率。
workspace/plan.json -
波次1:轻量级快速分类(并发峰值): 生成并发的轻量级子代理(例如最多10-20个并行),扫描中列出的所有文件。每个子代理只需输出快速分类结果:
workspace/plan.json。{"potentially_flawed": true/false, "reason": "..."} -
波次2:深度安全漏洞热点审计与并行轨迹搜索: 收集波次1中标记的所有文件。生成一波并发的深度审计子代理(例如最多4-8个并行),专门聚焦于这些已识别的热点。对于特别复杂的文件,生成多个子代理针对同一文件,使用不同的提示约束或多样化的低成本LLM来探索并行攻击向量。后续的去重阶段会合并重叠的发现结果。
-
Token优化(分布式写入): 指示波次2的子代理生成唯一UUID,并将发现结果直接写入磁盘上的独立文件。不要让它们将完整的JSON负载返回给你,否则会超出上下文窗口。只需让它们返回创建的UUID列表即可。
workspace/findings/<id>.json -
快照隔离(波次固定)——强制要求: 将相同的(步骤0中解析的CODE_ROOT)和相同的
--snapshot_root值传递给每个波次1和波次2的子代理,并指示它们遵守A模块规则(在该CODE_ROOT下解析--snapshot_id/target_files,遵守哨兵检查)。任何写入发现结果的波次2子代理必须将code_paths标记为该snapshot_id,完全符合发现结果Schema格式的要求。子代理绝不能运行discovery_commit/git pull/fetch/checkout、reset/hg pull、update或任何会更改工作树或切换版本的命令——快照在整个审计过程中是不可变的。无法访问快照的子代理必须报告该情况,而非重新同步。repo sync -
如果当前环境不支持子代理或并发,则回退到按顺序执行扫描和深度评审。
-
结构索引(仅作为提示的增强功能): 当结构索引可用时(存在),使用它来补充上述基于波次的集群策略。结构索引仅决定顺序,绝不决定范围。绝不能用它替代步骤3中的详尽调用点扫描。
workspace/kb/structural_index/manifest.json-
解析优先协议(任何结构查询之前必须执行):
- 先解析符号:
python3 workspace/helpers/query_structural_index.py resolve_symbol --name "<function_name>" [--language "<lang>"] [--file "<path>"] --state_root <state_root> - 如果响应中,则必须调查所有匹配的符号——绝不能静默选择其中一个。尽可能使用
ambiguous: true/--file缩小范围,或为每个匹配的符号安排调查任务。--language - 使用解析后的进行有限范围查询:
symbol_idpython3 workspace/helpers/query_structural_index.py find_callers --symbol_id "<id>" --limit 100 --offset 0 --state_root <state_root>
- 先解析符号:
-
覆盖范围感知解读: 检查每个结构索引响应中的:
coverage.partition_status- +
complete+ 空结果 = "无已索引的调用者"(对已索引代码具有权威性——仍需按仅提示规则运行grep)。precision == semantic - +
complete+ 空结果 = "无已索引的调用者"——不具有权威性。必须运行详尽的grep。precision != semantic - /
partial/empty+ 空结果 = "未完全索引"——必须运行详尽的grep。failed - 使用每个结果中的和
precision字段来加权信任度(backend>semantic>typecheck>ast>symbol-only>heuristic>deferred)。coverage-only
-
波次1(快速分类): 使用作为排名提示来补充grep——仅决定顺序,绝不决定范围。结构索引结果优先标记哪些文件为
find_callers();绝不能用它替代步骤3中的详尽调用点扫描。审计grep结果和结构索引结果的并集。potentially_flawed -
波次2(深度审计): 使用从包含的函数开始,根据深度评审上下文的需要扩展到调用者/被调用者/文件。
get_function_boundary(file, line) -
优雅降级: 如果结构索引缺失(无)、为空或查询辅助工具缺失,则回退到基于grep的发现方式(当前行为)。结构索引仅作为覆盖范围提示。
manifest.json
-
-
-
详尽的接口与调用点评审: 如果目标源文件定义了公共或API函数(例如数值解析器、解码器、编码器或转换器),且这些函数文档明确说明了大小约束或安全要求(例如要求调用者分配特定大小的缓冲区):
- 运行全仓库范围的grep搜索函数名称,构建详尽的候选调用点集合——这是强制要求的基础范围。然后使用结构索引查询辅助工具(先再
resolve_symbol)对调用点进行排名和优先评审(索引可区分实际调用与注释/字符串/变量名)。审计两个结果集的并集——结构索引可能会遗漏基于宏的调用、函数指针和动态调度,因此grep仍是基础范围。find_callers - 搜索代码库,找到并评审这些函数在整个仓库中的所有调用点,确保安全契约在全局范围内得到遵守。
- 读取调用文件,验证每个调用点是否严格遵守输入约束、正确管理边界并检查大小。
- 将任何不一致标记为契约对齐错误或缺失检查。
- 运行全仓库范围的grep搜索函数名称,构建详尽的候选调用点集合——这是强制要求的基础范围。然后使用结构索引查询辅助工具(先
-
无约束/探索性调查: 如果中的调查计划包含明确要求无约束扫描、对抗性审计或随机探索的指令或问题:
workspace/plan.json- 忽略中现有的安全假设和已记录的信任边界。
workspace/kb/THREAT_MODEL.md - 将所有输入和边界视为不可信且可能存在畸形。
- 从头开始分析实现,拥有完全的自由和自主权。
- 如果是指令极少的随机探索/挖掘任务,则重点关注目标文件的行为映射、识别关键入口点,并寻找意外的副作用或边界情况,不受特定威胁模型的约束。
- 忽略
-
编译并写入发现结果: 不要创建单个整体文件,若目录不存在则创建该目录。对于每个潜在的发现结果,生成唯一UUID并将有效的JSON对象写入名为
workspace/findings/的独立文件中。这样可以隔离发现结果,避免后续分析中的Token限制问题。文件中除JSON外不要包含任何其他文本。workspace/findings/<id>.json签名与谱系计算(步骤5a):在写入JSON文件之前,为每个发现结果计算、signature以及可选的lineage_id:cwe-
(可选): 如果漏洞对应已知的CWE,则将
cwe字段设置为标识符(例如cwe、"CWE-787")。如果无适用的CWE,则省略该字段。这是签名的输入项。"CWE-416" -
计算(确定性内容身份哈希):
signature- = 发现结果的
normalized_title,转换为小写,并去除所有非字母数字字符(仅保留ASCIItitle;所有其他字符包括Unicode字母、标点符号和空格均移除)。如果去除后[a-zA-Z0-9]为空(例如标题完全由非ASCII/Unicode字符组成),则将normalized_title设置为normalized_title的前16个十六进制字符,以避免两个不同的非ASCII标题因空字符串而冲突。sha256(<原始原始标题的UTF-8字节>) - = 发现结果的
cwe_part字段(若存在且非空),否则为空字符串。cwe - = 第一个
primary_target条目,去除末尾的code_paths(例如:line→src/auth.c:145)。如果src/auth.c为空,或第一个条目是非源代码定位器(包含code_paths的URL,或符合A模块第3步的非文件符号/偏移量),则使用空字符串。确定性排序://(主 sink 优先,在审计过程中保持稳定),确保code_paths——进而primary_target——在不同审计过程中不会变化。(如果顺序不稳定,唯一的代价是丢失谱系继承→发现结果会被重复报告为新结果,绝不会被隐藏——但稳定顺序可保留跨审计过程的合并。)signature - 如果非空:
primary_target=signature的前16个十六进制字符。sha256(normalized_title + "|" + cwe_part + "|" + primary_target) - 如果为空:
primary_target=signature的前16个十六进制字符。sha256(normalized_title + "|" + cwe_part + "|" + sorted(code_paths).join(",")) - 在发现结果创建时计算一次签名,绝不重新计算、编辑或伪造(与规则相同)。
discovery_commit
-
计算(跨审计过程的谱系链):
lineage_id- 扫描和
workspace/archive/findings_pass_*/,查找任何存档的发现结果JSON,其workspace/archive/loop*_findings/字段等于当前发现结果计算出的signature。signature - 如果找到匹配项:= 存档祖先的
lineage_id(继承谱系链,以便使用者跨审计过程合并结果)。如果多个存档发现结果共享相同的签名,则继承最新(审计次数最高)的祖先的lineage_id。所有具有相同签名的祖先应共享相同的lineage_id;如果不共享,则继承最新的祖先并记录警告。lineage_id - 如果没有完全匹配的签名:仅针对真实重命名尝试basename重命名回退。绝不要重新计算签名(签名在创建时计算一次,绝不重新计算——不变规则#4)。而是:
- 计算当前发现结果的basename:取(已为签名计算的第一个
primary_target条目,去除末尾的code_paths)并获取其basename(例如:line→src/auth.c)。如果auth.c为空(非源代码定位器或空code_paths),则跳过此回退——使用下面的新UUIDv4。primary_target - 对于步骤3存档扫描中找到的每个存档发现结果:从其存储的重建存档发现结果的basename(去除末尾的
code_paths[0],获取basename——例如:line→lib/old_auth.c:88)。绝不要重新计算存档发现结果的签名,也不要比较签名;直接比较两个basename字符串。old_auth.c - 仅当满足以下两个条件时,通过此回退继承祖先的:(i) basename匹配;(ii) 祖先的完整
lineage_id(其primary_target去除code_paths[0])在当前快照中已不存在(检查旧的完整路径在CODE_ROOT下是否不存在——这可区分真实重命名与仅共享basename的独立文件)。如果多个存档祖先满足(i)和(ii),则继承最新(审计次数最高)的祖先的:line。当通过basename继承时,还需添加发现结果历史记录注释lineage_id,以便下游使用者将该链接视为基于basename的衍生链接(报告仅当两个发现结果的完整lineage-via-basename-rename也匹配时才会合并,因此基于basename的谱系链接绝不会合并不同的漏洞)。signature - 如果旧的完整路径在当前快照中仍然存在,则不要继承——视为无匹配项(使用下面的新UUIDv4)。
- 计算当前发现结果的basename:取
- 如果既没有完全匹配的签名也没有basename重命名匹配:= 新的UUIDv4。
lineage_id - 这些是状态相对路径(见A模块第3步)——在下读取,绝不能在CODE_ROOT下读取。
--state_root/workspace/archive/
- 扫描
-
将所有三个字段(、
cwe、signature)与lineage_id一起写入发现结果JSON中。discovery_commit
模式独立性: 与(在降级/遗留模式中省略)不同,discovery_commit和signature始终会计算——它们是内容身份字段,而非快照身份字段。无论快照是否固定、未固定或缺失(模式关闭),它们都能正常工作。这是Phase-3的刻意改进:在模式关闭时,发现结果JSON现在携带2–3个额外的可选键(lineage_id、cwe、signature),这些在Phase 1中不存在。这不会改变快照模型(无lineage_id、无同步、无固定——三状态规则不受影响)。去重的模式关闭回退现在优先使用active_snapshot(如果存在)而非signature,这具有更强的区分性(签名在标题+路径的基础上添加了stable_key),因此只会拆分cwe会合并的条目(更保守、更安全)——绝不会产生错误合并或抑制合法发现结果。如果stable_key缺失,则完全沿用当前的signature行为。stable_key目标文件缺失或无法读取: 如果中的某个路径在CODE_ROOT下不存在或无法读取(例如自计划编写以来已被删除或重命名),请勿编造发现结果、行号或文件内容。跳过该目标,并在相关发现结果的target_files中明确记录跳过情况(或完全省略)。绝不要编造未读取的代码。description非源代码目标: 对于非源代码定位器(二进制、固件或URL端点——见A模块第3步),条目必须是稳定定位器(符号名称、偏移量或裸路径),不能带有code_paths后缀。不要为无法以文本形式打开的目标附加编造的行号。:line -
Findings Schema Format (Per File)
发现结果Schema格式(每个文件)
json
{
"id": "A unique identifier generated for this finding (e.g., a UUID or random hash). This must be included and match the filename.",
"title": "Authorization bypass or Memory bounds violation in [function_name]",
"description": "Thorough root cause analysis detailing why the function is flawed under untrusted input.",
"impact": "Exploit outcome (e.g., Privilege escalation, Memory corruption, Data exfiltration).",
"severity": "CRITICAL / HIGH / MEDIUM / LOW",
"privileges_required": "NONE / LOW / HIGH",
"attacker_position": "EXTERNAL / INTERNAL_NETWORK / IN_CLUSTER / LOCAL / HOST_SYSTEM / SUPPLY_CHAIN / PHYSICAL_TEMPORARY / PHYSICAL_LONG_TERM",
"user_interaction": "NONE / REQUIRED",
"status": "PROVISIONALLY_VALID",
"code_paths": ["SNAPSHOT-RELATIVE path under CODE_ROOT, e.g. 'relative/file/path.c:145'. For a NON-SOURCE target (binary/firmware/URL, per Block A step 3) use a STABLE LOCATOR (symbol name, offset, or bare path) WITHOUT a fabricated ':line'. Never invent a line number."],
"discovery_commit": "The active_snapshot.snapshot_id read from workspace/.mantis_state.json at the start of this pass (Step 1). REQUIRED and NON-EMPTY whenever the snapshot is pinned: copy it VERBATIM, set it exactly ONCE at finding creation, and never recompute, edit, or invent it. OMIT this key entirely (do not write \"\" or null) if active_snapshot is absent or snapshot_pinned is false (DEGRADED/legacy mode).",
"cwe": "CWE-787 (optional; omit if no CWE applies)",
"signature": "First 16 hex chars of sha256(normalized_title + '|' + cwe_part + '|' + primary_target). Computed once at creation, never recomputed.",
"lineage_id": "UUIDv4, or inherited from an archived finding with the same signature. Computed once at creation.",
"mitigation": "Recommended corrective modification.",
"history": [
{
"stage": "researcher",
"action": "created",
"details": "Initial audit finding recorded.",
"pass_number": <current_pass_number>,
"timestamp": "<current_iso8601_timestamp>"
}
]
}discovery_commitdiscovery_commitdiscovery_commitactive_snapshot.snapshot_iddiscovery_commitactive_snapshotsnapshot_pinnedsignaturelineage_iddiscovery_commitsignatureEnsure all individual finding files are written to the
directory. When complete, notify the user.
workspace/findings/json
{
"id": "为该发现结果生成的唯一标识符(例如UUID或随机哈希)。必须包含且与文件名匹配。",
"title": "[function_name]中的权限绕过或内存边界违规",
"description": "详细的根本原因分析,说明该函数在不可信输入下存在缺陷的原因。",
"impact": "利用后果(例如权限提升、内存损坏、数据泄露)。",
"severity": "CRITICAL / HIGH / MEDIUM / LOW",
"privileges_required": "NONE / LOW / HIGH",
"attacker_position": "EXTERNAL / INTERNAL_NETWORK / IN_CLUSTER / LOCAL / HOST_SYSTEM / SUPPLY_CHAIN / PHYSICAL_TEMPORARY / PHYSICAL_LONG_TERM",
"user_interaction": "NONE / REQUIRED",
"status": "PROVISIONALLY_VALID",
"code_paths": ["CODE_ROOT下的快照相对路径,例如'relative/file/path.c:145'。对于非源代码目标(二进制/固件/URL,见A模块第3步),使用稳定定位器(符号名称、偏移量或裸路径),**不能**附加编造的':line'。绝不要编造行号。"],
"discovery_commit": "本次审计开始时从workspace/.mantis_state.json读取的active_snapshot.snapshot_id(步骤1)。当快照固定时,此字段为必填且非空:原样复制,在发现结果创建时设置一次,绝不重新计算、编辑或伪造。如果active_snapshot缺失或snapshot_pinned为false(降级/遗留模式),则完全省略此键(不要写入\"\"或null)。",
"cwe": "CWE-787(可选;若无适用CWE则省略)",
"signature": "sha256(normalized_title + '|' + cwe_part + '|' + primary_target)的前16个十六进制字符。创建时计算一次,绝不重新计算。",
"lineage_id": "UUIDv4,或从具有相同签名的存档发现结果继承而来。创建时计算一次。",
"mitigation": "建议的修正方案。",
"history": [
{
"stage": "researcher",
"action": "created",
"details": "记录初始审计发现结果。",
"pass_number": <current_pass_number>,
"timestamp": "<current_iso8601_timestamp>"
}
]
}discovery_commitdiscovery_commitdiscovery_commitactive_snapshot.snapshot_iddiscovery_commitactive_snapshotsnapshot_pinnedsignaturelineage_iddiscovery_commitsignature确保所有独立的发现结果文件都写入目录。完成后通知用户。
workspace/findings/