SIFT-CRS is a Team-Atlanta-inspired DFIR cyber reasoning system for Protocol SIFT. It turns forensic investigation into a controlled loop:
Register evidence -> run typed MCP tools -> generate candidate findings -> verify -> self-correct -> report
Non-negotiable rule:
No evidence reference -> no finding.
No verifier pass -> no confirmed claim.
No execution log -> no report inclusion.
This repository currently implements Phases 1 through 6:
- repository and Python package skeleton
- JSON Schemas and validating example JSON files
- evidence registration with SHA-256 hashing
- evidence ledger writing
- append-only execution log JSONL
- typed mock forensic tools and one real EVTX parser wrapper with schema-validated inputs and outputs
- candidate finding generation from tool output
- deterministic verifier decisions for finding status and confidence
- bounded deterministic self-correction with targeted typed-tool follow-up
- structured markdown investigative report generation from verified and downgraded findings only
- audit-package export containing the report, ledgers, verifier output, and append-only execution log
- deterministic unit tests for schemas, evidence registration, typed tools, and verifier/self-correction/reporting behavior
It does not implement arbitrary shell execution or a web UI.
Use Python 3.10 or newer. On this box, the interpreter is python3. A virtual
environment is recommended because some Linux distributions block direct
system-wide pip installs.
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -e '.[test]'Run the test suite:
python3 -m pytest -qFor a no-install checkout using the system packages already on this box, prefix
CLI commands with PYTHONPATH=src.
Register the included demo evidence fixture:
python -m sift_crs demo examples/demo_evidence.txt --case-id case-001 --analysis-root analysisNo-install equivalent:
PYTHONPATH=src python3 -m sift_crs demo examples/demo_evidence.txt --case-id case-001 --analysis-root analysisExpected Phase 2 outputs:
analysis/
case-001/
evidence_ledger.json
execution_log.jsonl
Register any local evidence file explicitly:
python -m sift_crs register-evidence /path/to/evidence.bin --case-id case-001 --analysis-root analysis --evidence-id ev-disk01Registration reads evidence bytes to compute SHA-256 and file size. It does not write to the evidence path. Mutable project outputs are kept under the selected analysis root.
Run the Phase 4 verifier demo:
PYTHONPATH=src python3 -m sift_crs verifier-demo examples/demo_evidence.txt --case-id case-001 --analysis-root analysis --evidence-id ev-demoThe demo registers evidence, runs the typed mock_analyze_prefetch tool, writes
a candidate finding that overclaims persistence from Prefetch execution output,
and deterministically downgrades it. Expected outputs include:
analysis/
case-001/
evidence_ledger.json
execution_log.jsonl
findings.json
verifier_report.json
tool_runs/
Verifier decisions are appended to execution_log.jsonl as
finding_verifier_decision events. Rejected findings are not reportable;
report-facing findings are limited to verified, downgraded, or
needs_human_review.
parse_evtx_real is a typed read-only wrapper around python-evtx's
evtx_dump. It accepts only a registered evidence_id; raw evidence paths are
rejected by the input schema.
The wrapper looks for evtx_dump or evtx_dump.py on PATH, then in
~/.local/bin, then at /usr/local/bin/evtx_dump.py. It runs the binary as a
read-only subprocess against the registered evidence path and writes normalized
JSON only under the case analysis tool_runs/ directory.
Example:
PYTHONPATH=src python3 -m sift_crs register-evidence /cases/security.evtx --case-id case-001 --analysis-root analysis --evidence-id ev-securityPYTHONPATH=src python3 - <<'PY'
from pathlib import Path
from sift_crs.evidence import CaseStore
from sift_crs.tools import builtin_tool_registry
store = CaseStore(Path("analysis"), "case-001")
tool_run = builtin_tool_registry().run(
"parse_evtx_real",
{"case_id": "case-001", "evidence_id": "ev-security"},
store,
)
print(tool_run["status"])
print(tool_run["output_path"])
PYThe parser output contains artifact_type, evidence_id, source SHA-256,
record count, and normalized records with EventID, channel, provider, timestamp,
and source record metadata. If evtx_dump is missing, the file is empty, the
input is not .evtx, or the subprocess fails, the typed tool-run returns
status: failure and writes a structured failure artifact instead of crashing.
Run the Phase 5 bounded self-correction demo:
PYTHONPATH=src python3 -m sift_crs self-correction-demo examples/demo_evidence.txt --case-id case-001 --analysis-root analysis --evidence-id ev-demo --max-iterations 2The demo registers evidence, runs mock_analyze_prefetch, writes a candidate
persistence finding that overclaims from Prefetch output, lets the verifier
downgrade it, then runs a targeted mock_parse_registry_run_key follow-up. The
finding is re-verified only after that typed tool output exists.
Expected Phase 5 outputs include:
analysis/
case-001/
evidence_ledger.json
execution_log.jsonl
findings.json
verifier_report.json
correction_trace.json
tool_runs/
Correction actions are appended to execution_log.jsonl as
self_correction_iteration events. Each event records the iteration index,
trigger, typed action selected, and outcome. The hard --max-iterations cap
prevents runaway correction attempts.
Generate the Phase 6 structured investigative report after verifier or self-correction output exists:
PYTHONPATH=src python3 -m sift_crs report --case-id case-001 --analysis-root analysis --reports-root reportsExpected output:
reports/
case-001/
final_report.md
The report is a narrative, not a raw execution log. It includes:
- an executive summary
- a structured investigative narrative built only from
verifiedanddowngradedfindings - a findings table with evidence-ID links and selected audit-log links
- a self-correction summary
- an evidence appendix with ID, path, SHA-256, size, and read-only intent
Rejected findings are excluded from report conclusions.
Bundle the case audit artifacts into one export directory:
PYTHONPATH=src python3 -m sift_crs audit-package --case-id case-001 --analysis-root analysis --reports-root reports --export-root exportsExpected output:
exports/
case-001/
audit_package/
evidence_ledger.json
execution_log.jsonl
findings.json
verifier_report.json
correction_trace.json
final_report.md
The export command regenerates final_report.md first, appends an
audit_package_exported event, and copies the append-only execution log into the
package.
The examples in examples/*.json validate against the schemas in schemas/.
The unit tests perform this validation with jsonschema.