Security bots — sec-audit-source (Seki) + sec-audit-deps (Depsy)
This page covers the two manual audit bundles — CLI-launched, whole-tree or diff_base-scoped, reporting to the kanban board. For the push-driven supply-chain shields that gate every PR/push — supply-shield (Shieldy, malware axis) and supply-shield-cve (Vulny, CVE axis), diff-scoped by default and reporting back onto the forge (sticky comment + inline reviews + SARIF) — see their run bilans (supply-shield.md, supply-shield-cve.md).
Iterion ships two complementary manual audit bundles. They share a threat-model vocabulary, kanban label conventions, and FP-memory discipline, but address different layers of the application.
| Bundle | Codename | Role | Inspired by |
|---|---|---|---|
sec-audit-source | Seki | Source-code audit + verified remediation | vercel-labs/deepsec, anthropics/defending-code-reference-harness |
sec-audit-deps | Depsy | Installed third-party dependency audit | SocialGouv/no-package-malware |
When to run which
Run both as part of pre-release hardening, or pick the one matching the threat you're chasing.
Seki — what landed in this branch
Seki was previously an audit-only bot (scanners → triage → single-judge revalidate → kanban). This branch turns it into an audit + verified remediation bot. Six new capabilities, all in bots/sec-audit-source/main.bot:
| # | Capability | Vars (default) | Skill |
|---|---|---|---|
| 1 | Project security context — .sec-audit/context.md (threat model + auth model + known-FP sources). Loaded or auto-generated on first run; injected into triage + every voter prompt as authoritative scoping (NOT instructions). | enable_project_context=true, context_path=…/.sec-audit/context.md, context_ttl_days=90, force_context_refresh=false | project-context.md, threat-model.md |
| 2 | N-vote adversarial revalidation — the single judge becomes a fixed pool of 3 independent "disprove" voters (all three default to claude_code + claude-opus-5 — opus-first out of the box; restore cross-family diversity by overriding the per-voter model/backend env vars, or the opt-in ITERION_SEC_AUDIT_PROVIDER_CHAIN GLM lane). Majority drives the verdict; await: best_effort tolerates one crashed voter. Voters consult git log/git blame for fixed_recently / introduced_recently signals. Unanimous dismiss with a cited guard appends to fp-known.yaml. | confirm_threshold=2, fp_append_policy="unanimous_dismiss" | disprove-voting.md |
| 3 | Diff/incremental mode — when set, scopes triage to files changed between the working tree and git merge-base <diff_base> HEAD (mirrors code-review diff_precheck shape). Scanners still walk the whole tree; the narrowing happens at triage via file_filter. Empty = full scan. | diff_base="" (try main, origin/main, or HEAD for uncommitted-only) | (handled by diff_scope tool node) |
| 4 | Optional deepsec scanner backend — runs deepsec init/scan/process/export headlessly from the workspace; the JSON is ingested by triage like any other scanner. Reuses the local claude subscription when no AI Gateway key is set. Requires the deepsec CLI on PATH and Node 22+. deepsec_root only names an optional reference clone used to diagnose a missing CLI; it is never executed as a fallback. ALWAYS exits 0 — a missing binary degrades gracefully. | enable_deepsec=false, deepsec_concurrency=4, deepsec_process_limit=50, deepsec_root="${HOME}/lab/ai/references/deepsec" | scanner-deepsec.md, see also references-bootstrap.md |
| 5 | Integrated remediation phase — per confirmed finding: patch_author → build_rung → reproduce_rung (original scanner rule no longer fires) → regress_rung (tests pass) → reattack (fresh judge probes the vuln CLASS across the package) → reviewer_isolation (judge sees only {file, line, category, diff} — schema-enforced) → aggregate_verdict. In apply modes, verified patches commit to a temp branch iterion/sec-fix/<run-id>; apply_gated PAUSES on a human gate (iterion resume --run-id <id> --answer approved=true|false) before merging --no-ff to the user's branch; apply_auto skips the gate; propose only writes diffs. Crypto + secrets are always hard-stopped. | remediate=false (opt-in), remediation_mode="apply_gated", hard_stop_categories="crypto,secrets", max_fix_per_run=10, patch_attempts=1, patch_dir=…/.sec-audit/patches | security-patcher.md, patch.md, reattack-oracles.md, reviewer-isolation.md, crypto-handling.md |
| 6 | Env-overridable cost-tier backend — the whole bot runs on a Claude Code subscription with no API key by default (ITERION_SEC_AUDIT_BACKEND defaults to claude_code, models to claude-opus-5 — opus-first out of the box, so triage stops stalling on the heavy input). Flip the spine (triage/voters/report) to the metered claw + openai/gpt-5.5 / GLM lane via the env vars (or the opt-in ITERION_SEC_AUDIT_PROVIDER_CHAIN). detect_tech keeps its own cheap claw/gpt-5.5 default so flipping the spine can't drag the classifier onto an incompatible model. | ITERION_SEC_AUDIT_BACKEND (claude_code), ITERION_SEC_AUDIT_MODEL (claude-opus-5), ITERION_SEC_AUDIT_VOTER_V1_MODEL / _V2_MODEL / _V3_MODEL (claude-opus-5), ITERION_SEC_AUDIT_DETECT_BACKEND (claw) / ITERION_SEC_AUDIT_DETECT_MODEL (openai/gpt-5.5), ITERION_SEC_PATCH_MODEL (claude-opus-5) | (env overrides on agent/judge declarations in main.bot) |
Deepsec 2.3.9 export ingestion
The real export is a bare array whose findings carry a [SEVERITY]-prefixed title, description, severity and labels at the root. labels begins with security, project:<projectId>, severity:<severity>, slug:<vulnSlug>, confidence:<confidence>; owning-team:<slug> entries exist only when the exporter supplied teams. filePath, lineNumbers, vulnSlug, confidence, runId, projectId, severity, discoveredAt and an OwnerSummary owners object are under metadata; every owner array may be empty (teams[] {name, slug}, oncall[] {name, email, …}, managers[] {email, …}, contributors[] {name, email, score, …}, and recentCommitters[] {name, email, date}). Root/owner assignee fields are exporter-derived only when oncall exists. Description starts with **File:** \<path>` (lines n, n, …), Project:, a Severity: / Confidence: / Slug:line, and an exporter-supplied## Ownerssection only when owner data exists, before## Findingand## Recommendation. Seki's [scanner-deepsec` skill](../bots/sec-audit-source/skills/scanner-deepsec.md) maps that schema to candidates from either the inline group or the published file.
cap_findings selects deepsec by the inode identity of the path in DEEPSEC_PATHS, not by basename, realpath spelling, or JSON shape. It severity-sorts and caps the export without trimming it, so metadata and descriptions over 300 characters survive both transports. HIGH_BUG sorts with HIGH, BUG with MEDIUM; all other scanners retain their existing trimming.
End-to-end on the subscription, no API key, with the default gated remediation:
ITERION_SEC_AUDIT_BACKEND=claude_code \
ITERION_SEC_AUDIT_MODEL=claude-opus-5 \
ITERION_SEC_AUDIT_VOTER_V1_MODEL=claude-opus-5 \
ITERION_SEC_AUDIT_VOTER_V3_MODEL=claude-opus-5 \
devbox run -- iterion run bots/sec-audit-source/main.bot \
--var workspace_dir=$(pwd) \
--var enable_deepsec=true \
--var diff_base=origin/main
# When the workflow pauses on approve_fixes, inspect the temp branch then:
# iterion resume --run-id <id> --answer approved=true # merge to your branch
# iterion resume --run-id <id> --answer approved=false # abandon; temp branch keptSee security-patcher.md for the remediation phase in depth (ladder rungs, modes, temp-branch + human-gate flow, crypto/secrets hard-stop) and references-bootstrap.md for the optional deepsec/harness reference checkouts and ITERION_REFERENCES_ROOT. The executable prerequisites are the deepsec CLI on PATH and Node 22+.
The budget headroom is max_cost_usd: 150 per run (bumped 25 → 70 → 150) to absorb the 3-voter pool + a full remediation ladder on large repos; lower ITERION_SEC_AUDIT_EFFORT_VOTER to claw budget back on small repos.
Architectural patterns shared by both
Static signals → LLM with strict JSON schema
Both bots delegate pattern matching to deterministic tools (scanners or heuristic extractors) and use the LLM only for normalisation + reasoning + emission:
Rationale:
- Determinism: scanner output is identical on identical input.
- Coverage attribution: a missed vuln is traceable to a ruleset.
- Cost: scanners are free; LLM tokens are not.
Three-voter majority for FP reduction
sec-audit-source sends fresh candidates through three independent adversarial disprove voters. majority_verdict confirms at confirm_threshold (2 of 3 by default), while a unanimous dismissal backed by a cited guard can append a reviewed false positive. See disprove-voting.md.
Cross-run memory
Each bot retains state between runs so it doesn't repeat itself:
| Bot | Memory | Location | Visibility |
|---|---|---|---|
sec-audit-source | False positives | .sec-audit/fp-known.yaml in the scanned repo | Committed; human-reviewable |
sec-audit-deps | Per-package verdicts | cache_path (default: out-of-tree run scratch; --var cache_path=$HOME/.iterion/security-cache/packages.jsonl opts into the host-wide store) | Per-run by default; the host-wide opt-in is auto-mounted in sandbox via host_state: auto |
The sec-audit-source FP memory is per-repo because false positives are pattern-specific: urlSafe(input) may guard an SSRF in repo A and miss in repo B. The sec-audit-deps package cache is portable across repos because a published name@version+checksum is universally identifiable — hence the host-wide cache_path opt-in (the scratch default exists because sandbox images pinning a non-host user cannot write the auto-mounted ~/.iterion tree).
Capability-gated board writes
The node that creates kanban issues declares the minimum capability set it needs:
report_card / llm_review:
capabilities: [board.read, board.create, board.label]Everything else (detect_tech, scanner tools, triage, revalidate, …) runs readonly: true without board capabilities. A capability-denied attempt to write the board surfaces as a hard runtime error.
Per-language extensibility
Both bots use skill-driven language coverage: one deterministic node enumerates detected ids and reads the machine block from each skills/lang-<id>.md. Adding a language or ecosystem means dropping a skill with its iterion:scanners (Seki) or iterion:heuristics (Depsy) block — no new tool node, router branch, or when has_<id> DSL edit. Depsy needs a parser only when the scanner's output shape is new.
V1 ships:
sec-audit-source: JS/TS, Go, Python + always-on generic (gitleaks- trivy + semgrep
p/default).
- trivy + semgrep
sec-audit-deps: npm/yarn/pnpm, pip/poetry/uv, go modules + always-on generic.
Roadmap candidates: PHP, Ruby, Rust, JVM (Maven/Gradle), .NET (NuGet).
Kanban label conventions
| Label | Emitted by | Purpose |
|---|---|---|
severity:<low|medium|high|critical> | both | Bucketing on the board |
type:<finding-type> | sec-audit-source | One of 12 from [[finding-taxonomy]] |
type:supply-chain-<signal-id> | sec-audit-deps | Primary signal that triggered the issue |
scanner:<id> | sec-audit-source | Primary scanner (semgrep, gosec, bandit, gitleaks, trivy) |
ecosystem:<id> | sec-audit-deps | npm, pypi, gomod |
source:sec-audit-source / source:sec-audit-deps | both | Lets a remediation bot filter to security findings |
triage-uncertain | sec-audit-source | Revalidate voters split or all uncertain; human review needed |
patched:seki / patch-proposed:seki | sec-audit-source | Remediation phase landed a verified patch (committed/merged) vs only wrote a draft diff |
seki-verdict:<verified|uncertain> | sec-audit-source | Remediation ladder outcome |
seki-fix-pending / seki-fix-rejected:<reason> | sec-audit-source | apply_gated abandoned by operator / ladder rejected the diff |
seki-hard-stop:<finding_type> + human-review | sec-audit-source | Crypto / secrets finding routed to human (never auto-patched) |
patch-artifact:<path> / seki-temp-branch:<branch> / seki-merge-sha:<sha> | sec-audit-source | Audit-trail anchors written by remediation_report |
A downstream remediation router can list issues via mcp__iterion_board__list_issues with labels: ["source:sec-audit-source"] to scope itself to security findings, and filter further on patched:seki / patch-proposed:seki / seki-hard-stop:* to decide whether human follow-up is still needed.
Comparison with deepsec
deepsec and sec-audit-source target similar problems with different ergonomics. Trade-offs:
| Property | deepsec | sec-audit-source (Seki) |
|---|---|---|
| Distribution | Vercel AI Gateway + Vercel Sandbox; npx install | Self-hosted; bundled with iterion |
| Distributed execution | Yes (--sandboxes N --concurrency M) | Sharded (shard_size > 0 dispatches N child runs); cloud queue fan-out via iterion's cloud mode |
| Matcher expressiveness | Programmatic TS plugins (full filtering / multi-pattern / per-file state) | Scanner-based (semgrep / gosec / bandit / gitleaks / trivy) + custom programmatic matchers under .sec-audit/matchers/ + optional deepsec backend via --var enable_deepsec=true (see references-bootstrap.md) |
| Per-file append-only records | Yes (atomic locking, distributed-ready) | .sec-audit/files/<sha1(path)>.json — append-only history, scoped to single-process runs + sharded children |
| Project context file | INFO.md per project | .sec-audit/context.md — auto-generated on first run via the harness-derived threat-model skill, committable, TTL-refreshed (context_ttl_days=90) |
| Diff / PR mode | --diff origin/main | --var diff_base=origin/main (merge-base scoped triage) |
| FP reduction | revalidate consults git history | 3-voter "disprove" majority + git-log/blame signal per voter (see disprove-voting.md) |
| Remediation / patch | none (scan only) | Verification-ladder remediation phase (build → reproduce → regress → reattack → reviewer-isolation), three modes (propose / apply_gated / apply_auto), crypto/secrets hard-stop. See security-patcher.md |
| Operator-visible FP suppression | revalidate verdicts in workspace | Committed .sec-audit/fp-known.yaml (appended only on unanimous-dismiss + cited guard) |
| Integration with project boards / dispatcher | None built-in | Native: each finding is a kanban issue, then remediation_report labels it with the per-finding ladder outcome |
| Budget headroom | Tuned for $1000s/scan large monorepos | max_cost_usd=150 per run (bumped 25 → 70 → 150 to absorb voters + ladder); cloud mode scales further |
Comparison with no-package-malware
no-package-malware solves a different problem: it's a Verdaccio gateway that intercepts npm installs at the registry boundary. sec-audit-deps runs after the install, on the installed tree.
| Property | no-package-malware | sec-audit-deps |
|---|---|---|
| Position | In front of npm install (registry proxy) | Post-install audit |
| Enforcement | Fail-closed: blocks npm install of risky versions | Advisory: surfaces issues, doesn't block install |
| Ecosystems | npm only | npm + pip + go modules (extensible) |
| State | MongoDB + Redis + Verdaccio + workers | Bundle-only (one JSONL file) |
| Org / token / budget model | Yes (per-org Verdaccio tokens) | None; runs on whoever invoked iterion run |
Use both if you can: the gateway prevents known-bad versions from ever landing on disk; the audit-bundle catches what slipped through
- provides a board-integrated remediation surface.
See also
docs/security-patcher.md— Seki's verification-ladder remediation phase (modes, temp-branch + human-gate flow, crypto/secrets hard-stop)docs/references-bootstrap.md— clone deepsec + the harness into~/lab/ai/references/, theITERION_REFERENCES_ROOToverride, Node 22 requirement, on-demand harness invocationbots/sec-audit-source/README.mdbots/sec-audit-deps/README.mdbots/supply-shield/README.md·bots/supply-shield-cve/README.md— the push-driven diff-scoped supply-chain shieldsdocs/bundles.md— bundle layout + runtime resolutiondocs/native-tracker.md— the kanban board where findings landdocs/workflow_authoring_pitfalls.md— Goodhart's law in workflow design (façade resistance), required reading if you author.botfiles
