Skip to content
Back to skills

Security Scan

ASecurity

Use when automated scanners drive a security sweep of a repo or app — SAST, dependency/lockfile CVEs, secrets in the tree or git history, IaC misconfig — and the raw output has to become one deduped, exploitability-ranked report CI can gate on. NOT threat-modeling, OWASP design reasoning, or hand-authoring the fix (that is `secure-coding`).

  • 134 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 2, 2026
ai-agentspythonrustgoshellbashsqlnodedockerterraformcode-review

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 6 files and shows the line behind each finding

Scanned September 2, 2026

npx -y skills add ericrisco/rsc-harness --skill security-scan --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Security Scan?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Security Scan
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ericrisco-security-scan/badge)](https://www.skillsdirectory.com/skills/ericrisco-security-scan)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: security-scan
description: "Use when automated scanners drive a security sweep of a repo or app — SAST, dependency/lockfile CVEs, secrets in the tree or git history, IaC misconfig — and the raw output has to become one deduped, exploitability-ranked report CI can gate on. NOT threat-modeling, OWASP design reasoning, or hand-authoring the fix (that is `secure-coding`)."
tags: [security, sast, sca, secrets, scanning, owasp]
recommends: [secure-coding, github-actions, verify]
origin: risco
---

# Security scan — orchestrate scanners, triage the noise, emit a gate

A **machine-first** vulnerability sweep. Point automated scanners at a codebase,
collect SARIF/JSON, then do the work that has actual value: dedupe cross-tool
overlap, rank by exploitability, and emit one gate artifact CI can act on. The
finding comes from a **tool run**, not a hunch — if you are reasoning about a
design or hand-writing a fix, that is [`secure-coding`](../secure-coding/SKILL.md),
not this skill.

Your job is orchestration + triage: every finding traces to a scanner run with a
`ruleId` and a source location, so the output is reproducible. Not eyeballing
code, not authoring patches.

**Read-only by default.** Scan, triage, report. Apply fixes (version bumps,
rotation, `.gitignore` edits) only when the user asks — a security sweep that
silently mutates the tree destroys the evidence and the trust.

**Pin and verify your scanners.** Exact versions, verified checksums/SHAs, never
`@latest` GitHub Actions. In **March 2026 Trivy was supply-chain compromised** —
malicious releases `v0.69.4/0.69.5/0.69.6` and a hijacked
`aquasecurity/trivy-action` exfiltrated CI secrets. Your scanner runs with repo +
CI-secret access; an unpinned scanner is itself the attack surface.

## Scan classes and tool selection

Four classes: **SAST** (injection, XSS, path traversal in *first-party* code),
**SCA / deps** (known CVEs in dependency manifests + lockfiles), **Secrets**
(credentials in the tree **or git history**), **Misconfig / IaC** (Dockerfile,
k8s, Terraform, exposed config). Pick tools by what is in the repo. This is the
real branch point — match the tool to the manifest, do not run everything
everywhere.

| Repo contains | SAST | SCA | Secrets | Misconfig |
|---|---|---|---|---|
| Node (`package-lock.json`/`pnpm-lock.yaml`) | Semgrep | osv-scanner + `npm audit` (fast) | gitleaks → TruffleHog | Trivy |
| Python (`poetry.lock`/`requirements.txt`) | Semgrep | osv-scanner + `pip-audit` (fast) | gitleaks → TruffleHog | Trivy |
| Go (`go.mod`/`go.sum`) | Semgrep | osv-scanner (+ `govulncheck` for reachability) | gitleaks | Trivy |
| Containers (`Dockerfile`, images) | — | Trivy `fs`/`image` | Trivy `--scanners secret` | Trivy `config` |
| IaC (Terraform/k8s/Helm) | Semgrep (IaC rules) | — | gitleaks | Trivy `config`, Semgrep rulesets |
| Monorepo (mixed) | Semgrep `auto` | osv-scanner (multi-ecosystem) | gitleaks → TruffleHog | Trivy |

Full install (pinned), flag matrix, and suppression syntax: `references/tools.md`.

## Run recipes

All recipes emit SARIF (or JSON you normalize to it) — a common schema is what
lets you merge four tools, dedupe, and feed one artifact into CI instead of four
incompatible logs. **Pin the version shown**; the placeholders below mark where
to lock an exact tag/digest.

### SAST — Semgrep

Free OSS edition (latest `1.164.0`, 2026-05-27): 30+ languages, ~2,000 community
rules. SCA + Secrets rulesets are gated behind the hosted platform — use the
dedicated tools below for those, not Semgrep.

```bash
# Pin via the CLI version, not @latest. OWASP ruleset, SARIF out.
semgrep scan --config p/owasp-top-ten --sarif --output sast.sarif .
# Broader local sweep (community rules), no telemetry:
semgrep scan --config auto --sarif --output sast.sarif --metrics off .
```

### SCA — osv-scanner (primary), native auditors (fast pass)

osv-scanner (OpenSSF/Google) checks lockfiles against OSV.dev across ecosystems
and catches **transitive** CVEs the native auditors miss. Run native first for
speed, osv-scanner for coverage — never native alone.

```bash
# Primary: lockfile-aware, multi-ecosystem, SARIF.
osv-scanner scan source --format sarif --output sca.sarif .
# Fast first pass (ecosystem-native, weaker on transitive):
npm audit --omit=dev --audit-level=high --json > npm-audit.json   # Node
pip-audit --format json --output pip-audit.json                   # Python
```

### Secrets — gitleaks (tree + history), TruffleHog (verified)

gitleaks (~150+ patterns, sub-second on diffs) is the pre-commit/CI workhorse.
TruffleHog (800+ types) adds **live credential verification** — it auth-tests a
hit to tell a real leaked key from a sample. Scan **history, not just the tree**:
a key deleted in HEAD is still in the pack files and still rotatable.

```bash
# gitleaks: redacted SARIF over the working tree AND full git history.
gitleaks detect --redact --report-format sarif --report-path secrets.sarif
# TruffleHog: only verified (live) secrets across history.
trufflehog git file://. --only-verified --json > trufflehog.json
```

### Misconfig / IaC — Trivy (PINNED — see the caveat above)

Trivy scans filesystems, images, and IaC and finds transitive CVEs `npm audit`
misses.

```bash
# Pin the EXACT version (NOT v0.69.4/.5/.6 — those were the malicious releases).
# Verify the checksum/cosign signature before first use. See references/tools.md.
trivy fs --scanners vuln,secret,misconfig --format sarif --output trivy.sarif .
```

## Triage — turn noise into a ranked report

The deliverable is not the four SARIF files. It is a deduped, ranked report.

1. **Merge + dedupe.** Cross-tool overlap is real (osv-scanner and Trivy both
   flag the same CVE; gitleaks and TruffleHog both flag the same key). Key on
   `(class, normalized-id, path, line)` and keep the richest record — prefer the
   one with verification (TruffleHog) or reachability (govulncheck).
2. **Normalize severity.** SARIF `level` and tool-native severities disagree;
   map them all to one `critical/high/medium/low` scale (`references/triage.md`).
3. **Rank by exploitability, not by count.** A finding scores higher when it is
   **reachable** (called, not just present) **+ exposed** (on an untrusted path)
   **+ a sensitive sink** (auth, money, PII, RCE). A verified live secret or a
   reachable RCE CVE outranks a theoretical lib finding behind a feature flag.
4. **Suppress with a written justification, never a blanket ignore.** Each
   suppression records *who, why, and an expiry* — not a silent `.semgrepignore`
   that hides the next real bug too.

```text
BAD  — dump 412 raw findings from four tools, sorted alphabetically, no ranking.
GOOD — 3 unsuppressed criticals first:
       1. [secrets] VERIFIED live Stripe sk_live_… in config/.env (history) → ROTATE NOW
       2. [sca] CVE-2024-… lodash 4.17.20 transitive, reachable in src/api/parse.ts → bump 4.17.21
       3. [sast] SQL built from req.query in routes/search.js:48 → parameterize
       + 7 mediums summarized, + 18 suppressed (each with justification + expiry).
```

Full ranking rubric, dedupe keying, and severity-normalization map:
`references/triage.md`.

## The gate artifact

Emit one `security-scan-report.json` — the machine-checkable contract CI gates on.

```json
{
  "schemaVersion": "1.0",
  "scannedAt": "2026-06-02T10:00:00Z",
  "target": ".",
  "tools": [{ "name": "osv-scanner", "version": "2.0.2" }],
  "summary": { "critical": 1, "high": 2, "medium": 7, "low": 14, "suppressed": 18 },
  "findings": [
    {
      "class": "sca",
      "ruleId": "CVE-2024-XXXXX",
      "path": "package-lock.json",
      "line": 0,
      "severity": "critical",
      "status": "open",
      "tool": "osv-scanner",
      "exploitability": "reachable",
      "title": "Prototype pollution in lodash <4.17.21"
    }
  ]
}
```

- `status` is one of `open | suppressed | fixed`; `severity` one of
  `critical | high | medium | low`. Schema in full: `references/triage.md`.
- **Gate rule:** any `open` finding at `critical` (and, on a strict gate, `high`)
  → fail. `suppressed` never fails. `scripts/verify.sh` enforces exactly this and
  exits `0` on a clean/empty report (no false failure).

## CI wiring (brief)

- Pin actions to a **full commit SHA**, not a tag, never `@latest` — the hijacked
  `aquasecurity/trivy-action` was pulled by tag.

```yaml
# .github/workflows/security-scan.yml — pin the SHA, verify before bumping.
- uses: aquasecurity/trivy-action@<full-40-char-sha>  # NEVER @latest / @master
  with: { scan-type: fs, format: sarif, output: trivy.sarif }
- run: ./scripts/verify.sh   # gate on security-scan-report.json
```

- Upload SARIF to code scanning; gate the merge on `verify.sh`, not on a human
  reading logs. See [`github-actions`](../github-actions/SKILL.md) for the
  pipeline shell and [`verify`](../verify/SKILL.md) for the broader green gate
  this feeds.

## Anti-patterns

| Anti-pattern | Do instead |
|---|---|
| Pulling `aquasecurity/trivy-action@latest` because it is the official action | March 2026: a hijacked tag stole CI secrets. Pin a full SHA, verify provenance. |
| Treating every scanner finding as a bug to fix | Most are noise. Rank by reachable + exposed + sensitive sink; report the few that matter. |
| Calling the repo clean after scanning only the working tree | History holds the deleted keys. Scan git history; a removed-in-HEAD key is still leaked and live. |
| Declaring deps fine because `npm audit` is clean | Native auditors miss transitive CVEs. Run osv-scanner/Trivy too; native is the fast pass, not the only pass. |
| Committing a blanket `.semgrepignore` to quiet CI | A blanket ignore hides the next real bug. Suppress per-finding with a written justification + expiry. |
| Deleting a verified key from the file and moving on | Deleting ≠ safe. Rotate the credential first, then scrub history. The committed value is already compromised. |
| Taking the SARIF `level` as the severity | Tools disagree. Normalize to one scale before you rank or gate. |
| Dumping all four tool outputs in the PR for the reviewer to sort | The reviewer won't. Merge, dedupe, rank, and emit one report. |
| Letting the scan auto-fix the deps it finds | Read-only by default. Propose bumps; apply only when asked — never mutate during a sweep. |

## Project grounding (02-DOCS + CLAUDE.md)

In a project with a `02-DOCS/` layer (the [`harness`](../harness/SKILL.md)
Karpathy wiki), record the scanner choices, pinned versions, gate thresholds, and
any accepted-risk suppressions in `02-DOCS/wiki/stack/security-scan.md`, and index
it in `02-DOCS/wiki/index.md` (the Knowledge map; root `CLAUDE.md` keeps only a short
pointer to it). Read it first on every run so the next agent inherits the pinned
tools and thresholds instead of re-deriving them. No `02-DOCS/`? Skip silently.
Conventions are recorded, not gated — never block the scan on this.

## See Also

Reviewing a diff rather than scanning a repo: [`code-review`](../code-review/SKILL.md)
for correctness and quality, [`review`](../review/SKILL.md) for adversarial review
against a spec.

Files in this skill

  • SKILL.md10.9 KB
  • evals/README.md909 B
  • evals/cases.yaml3 KB
  • references/tools.md4 KB
  • references/triage.md3.8 KB
  • scripts/verify.sh4.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.