Triage reference
Contents
- Per-tool noise patterns
- OWASP Top 10:2025 mapping
- Severity calibration
Per-tool noise patterns
Semgrep
p/security-auditis deliberately high-recall. Expect a lot of low-value hits.- Taint rules stop at function boundaries in dynamic languages. A “user input reaches SQL” hit may pass through a sanitiser Semgrep couldn’t follow — and the reverse: a missed flow doesn’t mean the path is clean.
- Test files, fixtures and migration scripts produce constant false positives. Check the path before caring.
- ORM query builders often trip raw-SQL rules. Parameterised is parameterised.
Trivy
--scanners vulnreports every CVE in the dependency tree, including ones in code paths you never call. Prioritise by whether the vulnerable function is reachable, not by CVSS alone.- Base-image CVEs with no fix available (
--ignore-unfixedfilters these) will otherwise dominate the count and drown out application findings. - Transitive dev dependencies matter far less than direct runtime ones.
Gitleaks
- Scans full git history, so it finds secrets that were committed and later removed. Those are still real — the credential is in the history and must be rotated, not just deleted. Removing the commit does not un-leak it.
- High-entropy strings that aren’t secrets: test fixtures, example keys in docs, UUIDs, base64 test data.
- If a real secret is found, rotation comes first, before any other finding.
Checkov
- Flags cloud defaults that may be intentional. Public read on a bucket serving static assets is fine; on one holding user uploads it is not. Checkov can’t tell the difference.
ZAP
- The baseline scan reports many missing-header findings. Real, but usually low severity and fixable in one place.
- Alerts on error pages and redirects are frequently artefacts of unauthenticated crawling rather than genuine issues.
- Without an authenticated session, ZAP only sees the logged-out surface — which is a small fraction of the app. Note this as a coverage gap.
Nuclei
- Very low false positive rate by design — templates check for specific responses. Treat a Nuclei hit as probably real.
- But it only finds what a template exists for. Silence means nothing about custom application logic.
OWASP Top 10:2025 mapping
Use these categories in the report so findings group sensibly.
| Category | Typically found by |
|---|---|
| A01 Broken Access Control (now includes SSRF) | manual review; scanners rarely |
| A02 Security Misconfiguration | Checkov, ZAP, Trivy misconfig |
| A03 Software Supply Chain Failures | Trivy, SBOM review |
| A04 Cryptographic Failures | Semgrep, manual review |
| A05 Injection | Semgrep, ZAP, Nuclei |
| A06 Insecure Design | manual review only |
| A07 Authentication Failures | manual review; some Semgrep |
| A08 Software/Data Integrity Failures | Trivy, CI config review |
| A09 Logging & Alerting Failures | manual review only |
| A10 Mishandling of Exceptional Conditions | manual review; error-path reading |
Note the pattern: half these categories have no automated coverage at all. A report built purely from scanner output has a hole in exactly the places that rank highest.
Severity calibration
Rate by exploitability in this application, not the scanner default.
Critical — unauthenticated remote access to data or code execution. Live credentials in a repo that a third party can read.
High — authenticated but cross-tenant. Privilege escalation. Injection reachable from a user-controllable field.
Medium — requires an unlikely precondition, or exposes non-sensitive internals. Missing defence-in-depth on a path already protected elsewhere.
Low — hardening. Missing headers, verbose errors, outdated dependency with no reachable vulnerable path.
Dismissed — not reachable, not applicable, or the flagged pattern is already mitigated. Always record why; the next run will flag it again.
When downgrading a scanner’s own rating, say so explicitly in the report and give the reason. Silent downgrades are how real findings get lost.