Loading...
Loading...
Analyzes individual security findings to identify and construct complex exploit chains. Use after validation stages to see if multiple low-severity bugs can be combined into a higher impact vulnerability. Don't use for initial codebase auditing or writing patch code.
npx skill4agent add google/mantis mantis-chain/mantis-chainschema.json--snapshot_rootSNAPSHOT_ROOT--snapshot_idSNAPSHOT_ID--state_rootworkspace/--target_rootactive_snapshotworkspace/.mantis_state.jsonsnapshot_pinned = falseworkspace/findings/"VALID""VIABLE""CONDITIONAL_VIABLE""SAMPLE_OR_TEST"workspace/kb/entities/*.mdworkspace/kb/vulnerabilities/*.mdworkspace/.mantis_state.jsonworkspace/findings/<new_uuid>.jsonworkspace/findings/workspace/findings/workspace/archive/findings_pass_*/workspace/archive/loop*_findings/CODE_ROOTconstituent_findingssignaturesignaturesignaturecode_paths:linecode_pathssignaturesignaturesignatureworkspace/archive/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.[!NOTE] CURRENT-PASS CHECK (defensive; the binding guarantee is on the harness perScenario 2): ifmantis-pipeline-adapteris present ANDactive_snapshot, treat the snapshot as STALE for this pass — STOP "stale active_snapshot: pass mismatch" or degrade as HALT (active_snapshot.pass != state.pass_numbereffectively false: no authoritative verdicts, Block B NOT_MATCHED, reproducesnapshot_pinned). This catches a custom harness that preservednot_attemptedacross the Stage 15 pass increment without re-pinning. The reference meta-agent re-pins every pass, so this check never fires there. Block B itself cannot detect this (it isactive_snapshot-only, notsnapshot_id-aware).pass
mantis-chainCODE_ROOTSNAPSHOT_IDsnapshot_pinned--snapshot_root--snapshot_idactive_snapshotworkspace/findings/CODE_ROOTcode_pathsCODE_ROOTcode_pathsSNAPSHOT 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.)discovery_commitF.discovery_commitworkspace/findings/"VALID""VIABLE""CONDITIONAL_VIABLE""SAMPLE_OR_TEST"active_snapshotactive_snapshot--syncactive_snapshotsnapshot_pinned == falseactive_snapshotsnapshot_pinned == truediscovery_commitdiscovery_commit"MIXED"SNAPSHOT_IDfile:lineworkspace/kb/entities/workspace/kb/vulnerabilities//tmp/tmpworkspace/findings/workspace/archive/findings_pass_*/workspace/archive/loop*_findings/signaturesignaturecode_pathssignatureworkspace/findings/<new_uuid>.json"constituent_findings"["UUID_A", "UUID_B"]discovery_commitdiscovery_commitactive_snapshot--syncdiscovery_commitdiscovery_commitresearcher:317,340-341discovery_commit"MIXED"schema.json:4chain:307"MIXED"schema.json:232"MIXED""MIXED"discovery_commitdiscovery_commitdiscovery_commitdiscovery_commitdiscovery_commit"MIXED""MIXED"SNAPSHOT_ID"MIXED""MIXED"signaturesignaturediscovery_commitsignaturesignaturesha256("chain|" + "|".join(sorted_constituent_signatures))"chain|"signaturesignaturesignatureschema.json:256discovery_commitresearcher:241-242lineage_idworkspace/archive/findings_pass_*/workspace/archive/loop*_findings/constituent_findingssignaturesignaturelineage_idlineage_idsignaturesignaturelineage_idsignaturelineage_id--state_root/workspace/archive/researcher:246-272privileges_requiredprivileges_requiredNONEattacker_positionuser_interactionREQUIREDNONEstatus"VALID""SAMPLE_OR_TEST""SAMPLE_OR_TEST""CONDITIONAL_VIABLE""CONDITIONAL_VIABLE""VIABLE"repro_status"failed_to_reproduce""not_attempted""not_attempted"repro_status"reproduced""statically_confirmed""statically_confirmed""reproduced"{
"id": "A unique identifier generated for this chain finding. Must match filename.",
"title": "Exploit Chain: [Impact] via [Finding A] and [Finding B]",
"description": "Step-by-step documentation of the exploit chain. Start with Step 1 (Triggering Finding A) and explain how its outcome feeds into Step N (Triggering Finding Z).",
"impact": "The combined, escalated impact of the chain (e.g., Remote Code Execution, Full Database Exfiltration). This should be higher than the individual findings.",
"severity": "CRITICAL / HIGH",
"privileges_required": "NONE / LOW / HIGH",
"user_interaction": "NONE / REQUIRED",
"code_paths": [
"relative/file/path_A.c:line_number",
"relative/file/path_B.c:line_number"
],
"attacker_position": "EXTERNAL / LOCAL / etc. (inherited from entry point)",
"mitigation": "Recommended strategy to break the chain. Usually involves fixing at least one, if not all, of the underlying links.",
"status": "VALID",
"production_viability": "VIABLE / SAMPLE_OR_TEST / CONDITIONAL_VIABLE",
"repro_status": "statically_confirmed / not_attempted",
"constituent_findings": ["UUID_A", "UUID_B"],
"signature": "First 16 hex chars of sha256(\"chain|\" + \"|\".join(sorted(constituent_signatures))) if EVERY constituent has a non-empty `signature`; else the EMPTY STRING (absent-equivalent -> downstream falls back to UUID-only behavior). Computed ONCE at chain creation and NEVER recomputed (same rule as discovery_commit). See the signature/lineage computation steps above.",
"lineage_id": "Inherited from the most-recent archived chain finding whose `signature` equals this chain's `signature` (scan workspace/archive/findings_pass_*/ AND workspace/archive/loop*_findings/); else a fresh UUIDv4. See the signature/lineage computation steps above.",
"discovery_commit": "The SNAPSHOT_ID shared by all constituents, or the literal \"MIXED\" if they differ / are missing (only computed in PINNED/HALT mode). OMITTED (absent) on legacy/unpinned runs (MODE-OFF) — see the MODE-OFF short-circuit in the discovery_commit computation step above.",
"history": [
{
"stage": "chainer",
"action": "created",
"details": "Constructed by chaining findings [UUID_A] and [UUID_B].",
"pass_number": <current_pass_number>,
"timestamp": "<current_iso8601_timestamp>"
}
]
}/mantis-dedupe"title""history"workspace/findings/