Skip to content

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.

BundleCodenameRoleInspired by
sec-audit-sourceSekiSource-code audit + verified remediationvercel-labs/deepsec, anthropics/defending-code-reference-harness
sec-audit-depsDepsyInstalled third-party dependency auditSocialGouv/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:

#CapabilityVars (default)Skill
1Project 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=falseproject-context.md, threat-model.md
2N-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
3Diff/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)
4Optional 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 Node 22 + a deepsec clone. 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
5Integrated remediation phase — per confirmed finding: patch_authorbuild_rungreproduce_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/patchessecurity-patcher.md, patch.md, reattack-oracles.md, reviewer-isolation.md, crypto-handling.md
6Env-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)

End-to-end on the subscription, no API key, with the default gated remediation:

bash
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 \
ITERION_REFERENCES_ROOT=$HOME/lab/ai/references \
  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 kept

See 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 deepsec + harness clone setup and ITERION_REFERENCES_ROOT.

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.

Two-phase judge for FP reduction

sec-audit-source.revalidate runs the deepsec-inspired pass-1 (promote/dismiss/uncertain) followed by pass-2 self-critique that specifically hunts for façades and over-relied dismissals. See the revalidate_system prompt and memory feedback_judge_two_phase.

Cross-run memory

Each bot retains state between runs so it doesn't repeat itself:

BotMemoryLocationVisibility
sec-audit-sourceFalse positives.sec-audit/fp-known.yaml in the scanned repoCommitted; human-reviewable
sec-audit-depsPer-package verdictscache_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 structure their language coverage as one skill + one router branch per language. Adding a language:

  1. Add skills/lang-<id>.md describing scanners / heuristics for the language.
  2. Add a run_<id>_scanners (or _heuristics) tool node in main.bot.
  3. Wire the router with a when has_<id> condition.

V1 ships:

  • sec-audit-source: JS/TS, Go, Python + always-on generic (gitleaks
    • trivy + semgrep p/default).
  • 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

LabelEmitted byPurpose
severity:<low|medium|high|critical>bothBucketing on the board
type:<finding-type>sec-audit-sourceOne of 12 from [[finding-taxonomy]]
type:supply-chain-<signal-id>sec-audit-depsPrimary signal that triggered the issue
scanner:<id>sec-audit-sourcePrimary scanner (semgrep, gosec, bandit, gitleaks, trivy)
ecosystem:<id>sec-audit-depsnpm, pypi, gomod
source:sec-audit-source / source:sec-audit-depsbothLets a remediation bot filter to security findings
triage-uncertainsec-audit-sourceRevalidate voters split or all uncertain; human review needed
patched:seki / patch-proposed:sekisec-audit-sourceRemediation phase landed a verified patch (committed/merged) vs only wrote a draft diff
seki-verdict:<verified|uncertain>sec-audit-sourceRemediation ladder outcome
seki-fix-pending / seki-fix-rejected:<reason>sec-audit-sourceapply_gated abandoned by operator / ladder rejected the diff
seki-hard-stop:<finding_type> + human-reviewsec-audit-sourceCrypto / secrets finding routed to human (never auto-patched)
patch-artifact:<path> / seki-temp-branch:<branch> / seki-merge-sha:<sha>sec-audit-sourceAudit-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:

Propertydeepsecsec-audit-source (Seki)
DistributionVercel AI Gateway + Vercel Sandbox; npx installSelf-hosted; bundled with iterion
Distributed executionYes (--sandboxes N --concurrency M)Sharded (shard_size > 0 dispatches N child runs); cloud queue fan-out via iterion's cloud mode
Matcher expressivenessProgrammatic 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 recordsYes (atomic locking, distributed-ready).sec-audit/files/<sha1(path)>.json — append-only history, scoped to single-process runs + sharded children
Project context fileINFO.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 reductionrevalidate consults git history3-voter "disprove" majority + git-log/blame signal per voter (see disprove-voting.md)
Remediation / patchnone (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 suppressionrevalidate verdicts in workspaceCommitted .sec-audit/fp-known.yaml (appended only on unanimous-dismiss + cited guard)
Integration with project boards / dispatcherNone built-inNative: each finding is a kanban issue, then remediation_report labels it with the per-finding ladder outcome
Budget headroomTuned for $1000s/scan large monoreposmax_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.

Propertyno-package-malwaresec-audit-deps
PositionIn front of npm install (registry proxy)Post-install audit
EnforcementFail-closed: blocks npm install of risky versionsAdvisory: surfaces issues, doesn't block install
Ecosystemsnpm onlynpm + pip + go modules (extensible)
StateMongoDB + Redis + Verdaccio + workersBundle-only (one JSONL file)
Org / token / budget modelYes (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