| name | arn-code-batch-cve-scan |
| description | This skill should be used when the user says "scan for CVEs", "CVE scan", "check for vulnerabilities", "find vulnerabilities", "check security advisories", "dependabot triage", "dependabot scan", "scan dependencies for security issues", "audit dependencies", "vulnerability triage", "security scan", "run security audit", "arness CVE scan", "arn-code-batch-cve-scan", "run a scheduled CVE scan", "produce a CVE proposal", "finalize a CVE proposal", or wants Arness to discover, triage, and ticket security advisories across project dependencies. Supports three modes (interactive, proposal, finalize) so the workflow is safe to schedule unattended via Claude Code Routines and tool-agnostic enough to drive headlessly via claude -p or any LLM-running CLI. Dual-source discovery (GitHub Dependabot plus locally-detected scanners), per-CVE triage via arn-code-cve-analyst, project-native waiver capture, and pre-triage parent plus per-group sub-issue ticketing on GitHub or Jira. Reads from the configured Security branch. |
Arness Batch CVE Scan
Discover, triage, and ticket security advisories across project dependencies. Dual-source CVE discovery (GitHub Dependabot + locally-detected scanners), per-CVE triage via the arn-code-cve-analyst agent, project-existing waiver capture, and pre-triage parent + per-group sub-issue ticketing. All scanner / waiver / tracker detection is evidence-based — this skill never enumerates tool names; everything delegates to <code-patterns-dir>/security-scanning.md (written by Phase 1's codebase-analyzer extension).
Three modes (--mode=<value>):
interactive (default): full ergonomics, inline ticketing, prompts for waivers, AskUserQuestion available.
proposal: ZERO user prompts, ZERO writes to GitHub / Jira / project waiver files; emits a CVE_SCAN_PROPOSAL.md + .json sidecar that finalize mode consumes. Safe to schedule via Claude Code Routines or claude -p headless.
finalize: reads a previously-emitted proposal, walks needs-review items via AskUserQuestion, applies deferred writes behind a single consent gate.
This skill is a sequencer — it MUST NOT duplicate sub-reference logic. All procedural detail lives in references/. The body below names the steps and points to the right reference.
Pipeline position:
arn-code-codebase-analyzer (security-scanning.md)
│
▼
**arn-code-batch-cve-scan** (3 modes)
│
dispatches per-CVE
│
▼
arn-code-cve-analyst (per-CVE triage)
│
▼
Ticketing (interactive / proposal-render / finalize-POST)
│
▼
arn-code-batch-cve-fix (consumes tickets)
Step 0: Ensure Configuration
Read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-ensure-config/references/step-0-fast-path.md and follow its instructions. This guarantees ## Arness is configured with Arness Code fields.
After ensure-config completes, extract from ## Arness:
- Security scanning —
enabled | none | skip. If none or skip, inform the user: "Security scanning is set to <value>. Update the Security scanning: field in ## Arness to enabled to run this skill." STOP.
- Security branch — branch name or
auto. If auto, resolve via git symbolic-ref --short refs/remotes/origin/HEAD. This is the branch the skill scans (NEVER the working tree).
- Code patterns — directory containing
security-scanning.md.
- Issue tracker —
github | jira | none. Drives the ticketing or no-tracker-fallback branch.
- Plans directory — base path for proposal-artifact and no-tracker-fallback outputs.
- Platform —
github | jira | none. Used for visibility detection.
If Security scanning: or Security branch: is missing, Layer 2c will have prompted the user via AskUserQuestion and written the chosen value (see Phase 5 wiring). This skill consumes the field — it does not write it.
If <code-patterns-dir>/security-scanning.md is missing, inform the user: "Security scanning is enabled but security-scanning.md was not found. Re-run arn-code-ensure-config or invoke arn-code-codebase-analyzer directly to generate it." STOP.
Step 1: Mode Selection
Read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/modes.md and follow its CLI parsing procedure. Set the mode variable (one of interactive, proposal, finalize) used throughout the rest of the skill.
The hard rules per mode are the canonical contract every other reference must satisfy. If you find yourself unsure about reachability of AskUserQuestion or write semantics at any later step, return to modes.md.
Step 2: Stale Advisory-DB Gate
Read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/stale-db.md and follow its procedure. Mode-aware:
interactive: prompt the user for --allow-stale-db justification on stale DB.
proposal: refuse without prompting; record needs-review: stale-db in the proposal; exit non-zero so the scheduler flags the failure.
finalize: not relevant (finalize works against the proposal's frozen scanner output).
Step 3: Branch Existence Validation
Validate that <security-branch> exists on the remote:
git ls-remote --exit-code origin "<security-branch>"
If the branch is missing:
interactive: prompt the user for a corrected branch name (AskUserQuestion).
proposal: write needs-review: branch-missing to the proposal and exit non-zero.
finalize: not relevant.
This validation runs only in interactive and proposal modes (finalize consumes a frozen proposal that already passed this check).
Step 4: Discovery (Dual-Source)
Skip in finalize mode (the proposal already contains the frozen discovery output).
Read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/discovery.md and follow its dual-source procedure. Discovery is read-only and identical in interactive and proposal modes.
All manifest and lockfile reads MUST go through git show <security-branch>:<path> — NEVER the working tree.
Step 5: Dedupe
Apply the dedupe key (advisory-id, ecosystem, package, installed-version) to merge the two sources into a unified CVE list. Procedure detailed in references/discovery.md (Dedupe section).
Skip in finalize mode.
Step 6: Deterministic Parsing
Read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/parser.md and follow its procedure. Extract version-bound fields (installed-version, fixed-version, vulnerable-range) per scanner's documented JSON schema. Compute and store the SHA-256 checksum of each raw scanner output (sha256sum || shasum -a 256) — the triage agent echoes this checksum back to prove input integrity.
The parser is the strict boundary: the arn-code-cve-analyst agent annotates contextual fields ONLY and NEVER re-derives version bounds.
Skip in finalize mode.
Step 7: Triage Dispatch
Read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/triage-dispatch.md and follow its dispatch procedure. For each CVE, dispatch the arn-code-cve-analyst agent via the Task tool, passing the model from .arness/agent-models/code.md as the model parameter (see plugins/arn-code/skills/arn-code-ensure-config/references/ensure-config.md "Dispatch convention" for fallback).
Mode-aware graceful degradation: if the LLM runner does not support subagent dispatch (opencode, some claude -p configurations), fall back to inline reasoning in the main loop. This preserves tool-agnosticism.
Skip in finalize mode (triage records are already in the proposal).
Step 8: Idempotency Check (Diff Mode)
Before any ticketing writes, query open issues with the arness-security label and build the existing-CVE set. Dedupe key: (advisory-id, status).
- For interactive ticketing: skip CVEs already represented by an open ticket with matching status.
- For proposal rendering: still RENDER existing-CVE entries into the proposal, but mark them as
existing-ticket: #N so finalize's POST step can skip them.
- For finalize: only POST entries whose
existing-ticket is null.
For Jira: same logic using the configured Jira project's open issues filtered by the arness-security label.
Step 9: Mode-Aware Ticketing OR Proposal Branch
Branch on mode:
9a. interactive Mode
Read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/ticketing.md and follow its interactive-branch procedure. Writes ticketing inline. Prompts for waivers via AskUserQuestion.
If Issue tracker: none, instead read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/no-tracker-fallback.md and follow its procedure (writes CVE_RESOLUTION_PLAN.md under a per-scan dated directory).
Then read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/waivers.md (interactive branch) for any approved waiver writes, and ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/visibility.md for default-redacted summary handling.
9b. proposal Mode
Read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/proposal-artifact.md and follow its rendering procedure. Render the fully-formed ticket bodies, waiver needs-review entries, and visibility decisions into the proposal artifact's Markdown surface AND JSON sidecar.
Per the canonical contract in modes.md: this mode emits ZERO AskUserQuestion invocations and ZERO writes to GitHub / Jira / project waiver files. Waiver actions are ALWAYS noop in proposal mode (per spec FR §24; documented in references/waivers.md).
Also write CVE_RESOLUTION_PLAN.md per references/no-tracker-fallback.md IF Issue tracker: none — it lives alongside the proposal artifact in the same per-scan dated directory.
9c. finalize Mode
Read ${CLAUDE_PLUGIN_ROOT}/skills/arn-code-batch-cve-scan/references/finalize.md and follow its re-entry procedure. Load the proposal (default: most recent under .arness/plans/CVE_*/; explicit via --proposal-path=<...>). Walk needs-review items via AskUserQuestion. Apply pre-rendered ticket bodies and confirmed waiver writes behind a single consent confirmation gate.
Step 10: Proposal-Mode Completion Signal
When mode == "proposal" AND the proposal artifact was written successfully:
Emit a single stable stdout line so schedulers (Claude Code Routines, cron + claude -p, etc.) can detect completion machine-readably:
arness::cve-scan::proposal-ready path=<absolute-path-to-CVE_SCAN_PROPOSAL.md>
This line MUST be emitted exactly once, AFTER all writes are flushed and AFTER the atomic .tmp → mv rename completes. Schedulers parse this line to attach the artifact to a notification or to chain into a finalize invocation.
In interactive and finalize modes, no stable stdout completion line is emitted — those modes complete with a human-facing summary.
Step 11: Scheduling
This skill supports unattended scheduled invocation. Recommended mechanisms:
- Claude Code Routines (cloud, recommended) — native cron-like trigger with 1h minimum interval. Configure a routine that invokes this skill with
--mode=proposal, attaches the emitted proposal artifact (parse the arness::cve-scan::proposal-ready line), and notifies the user. On user response, invoke a follow-up routine with --mode=finalize --proposal-path=<...>.
claude -p headless (tool-agnostic) — for users on opencode, Aider, or other LLM-running CLIs, or for users wiring into external schedulers (cron, GitHub Actions, etc.). Invoke claude -p "/arn-code-batch-cve-scan --mode=proposal"; capture stdout; grep for arness::cve-scan::proposal-ready path=; attach the artifact to the desired notification channel.
Both paths inherit the same idempotency + diff-mode behavior — re-running a scan against the same scanner output is a no-op (subject to the (advisory-id, status) dedupe key from Step 8). This skill ships NO built-in scheduler.