Threat modeling asks what could go wrong in a design. Vulnerability assessment asks what *is* wrong in the thing you actually built and deployed. The first is predictive and cheap; the second is empirical and finds the gap between the design you modeled and the system you shipped — the misconfigured bucket, the forgotten staging host, the library that went stale.
Scanned 9/10/2026
Install to Claude Code
npx -y skills add snoodleboot-io/prompticorn --skill verbose --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Verbose?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/snoodleboot-io-verbose-f665d7a8)More formats (shields.io, HTML) on the badges page.
# Vulnerability Assessment (Verbose)
## Core Patterns
### Assessment Is Not Threat Modeling
Threat modeling asks what could go wrong in a design. Vulnerability assessment
asks what *is* wrong in the thing you actually built and deployed. The first is
predictive and cheap; the second is empirical and finds the gap between the design
you modeled and the system you shipped — the misconfigured bucket, the forgotten
staging host, the library that went stale.
Run both. A finding that appears in the scan but not in the model means the model
missed something; a threat in the model that no scan can detect needs a manual test
or a design change.
### Cover Every Layer
Each tool class sees exactly one layer of the stack, and a missing layer is a
blind spot rather than a known gap.
| Layer | Detects | Misses | Tooling |
|---|---|---|---|
| SAST | Injection sinks, unsafe APIs, hardcoded secrets | Runtime config, authz logic | `semgrep`, CodeQL, Bandit |
| Secret scanning | Committed credentials, including in history | Secrets in env/CI only | `gitleaks`, `trufflehog` |
| SCA | Known CVEs in direct and transitive deps | Unpublished flaws, your own code | `pip-audit`, `npm audit`, Dependabot |
| Image / OS | CVEs in base-image packages | Application logic | `trivy image`, `grype` |
| IaC | Public buckets, permissive SGs, missing encryption | Drift from what is deployed | `checkov`, `tfsec`, `trivy config` |
| Cloud posture | Actual live misconfiguration and drift | Application flaws | `prowler`, `scoutsuite` |
| DAST | Exposed endpoints, headers, some injection | Anything needing business context | OWASP ZAP, `nuclei` |
| Manual | Business-logic abuse, authorization flaws, chains | Volume — it does not scale | A human with the abuse cases |
The last row is not optional. No scanner will report that an approver can approve
their own expense, because nothing about that request looks malformed. Take the
abuse cases from threat identification and test each one by hand.
```bash
gitleaks detect --source . --redact
semgrep --config=auto --error .
pip-audit -r requirements.txt --strict
trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 myapp:1.4.2
trivy config ./infra
nuclei -u https://staging.example.com -severity high,critical
```
`--ignore-unfixed` deserves a note: it hides vulnerabilities with no available
patch. That is the right default for a blocking CI gate (you cannot fix them, so
blocking merges achieves nothing) and the wrong default for your risk register
(you still need to know they exist and possibly mitigate another way). Run both
modes, gate on one, report on the other.
### CVSS Is Severity; Priority Needs Context
A CVSS Base score describes an abstract worst case for the vulnerability in
isolation. It has no knowledge of whether you deploy the affected component, call
the affected function, or expose it to anyone.
| Signal | What it tells you | Where it comes from |
|---|---|---|
| CVSS Base | Intrinsic technical severity | The advisory |
| CVSS Environmental | Severity adjusted for your deployment | You compute it |
| EPSS | Probability of exploitation in the next 30 days | FIRST, updated daily |
| CISA KEV | Confirmed exploited in the wild | CISA catalog |
| Reachability | Whether your code calls the vulnerable path | SCA with call-graph analysis |
| Exposure | Internet-facing, authenticated, or internal only | Your architecture |
Two findings, same week:
```
Finding A CVSS Base 9.8 "Critical" — RCE in an XML parser
Present as: transitive dev dependency
Shipped to prod: no (build-time only, dropped by the multi-stage build)
Vulnerable path: never invoked
EPSS: low
Priority: LOW — routine dependency bump
Finding B CVSS Base 6.5 "Medium" — SSRF in an image-proxy handler
Reachable: yes, unauthenticated
Pod has cloud role: yes, with secrets-read
IMDS version: v1 (no session token required)
Chains to: credential theft -> production database
Priority: CRITICAL — fix today
```
Sorting by Base score puts A above B and is exactly backwards. The chain in B is
the point: individually medium findings that compose into credential theft outrank
an isolated critical you do not deploy.
### A Triage Pipeline That Terminates
Raw scanner output is not a work queue. Reduce it in stages.
```
4,812 raw findings
-> dedupe across tools, group by package+version 1,140
-> drop findings in images/paths not deployed 610
-> drop unreachable code paths (call-graph analysis) 180
-> rank: KEV, then EPSS x exposure, then severity 180 ordered
-> SLA-assign the top band 14 actionable this week
```
Work order, once ranked:
1. **In KEV and internet-facing** — treat as an active incident.
2. **High EPSS and reachable** — this sprint.
3. **Critical/High, reachable, not exposed** — within the standard SLA.
4. **Everything else** — batched into routine dependency upgrades, not tracked individually.
Example remediation SLAs — pick numbers you will actually meet, then measure:
| Band | Internet-facing | Internal | Not deployed |
|---|---|---|---|
| KEV / actively exploited | 24 hours | 7 days | Next cycle |
| Critical, reachable | 7 days | 30 days | Next cycle |
| High, reachable | 30 days | 90 days | Next cycle |
| Medium / Low | Next cycle | Next cycle | Next cycle |
An SLA you routinely blow is worse than none — it trains everyone to ignore the
dashboard. Track the aging distribution, not just the open count.
### Scan Placement in the Pipeline
```yaml
# .github/workflows/security.yml (illustrative)
jobs:
static:
steps:
- uses: actions/checkout@v4
with: {fetch-depth: 0} # gitleaks needs history
- run: gitleaks detect --source . --redact
- run: semgrep --config=auto --error .
- run: pip-audit -r requirements.txt --strict
image:
steps:
- run: docker build -t myapp:${{ github.sha }} .
- run: |
trivy image --severity HIGH,CRITICAL --ignore-unfixed \
--exit-code 1 myapp:${{ github.sha }}
- run: trivy image --format cyclonedx --output sbom.json myapp:${{ github.sha }}
- uses: actions/upload-artifact@v4
with: {name: sbom, path: sbom.json}
```
Three placement rules that decide whether this is useful or ignored:
- **Pre-merge gates must be fast and near-zero false positive.** Secrets and
reachable Critical/High findings block. Everything else reports.
- **Scan the built artifact, not just the source.** The base image contributes most
OS-level CVEs and is invisible to source scanning.
- **Re-scan deployed images on a schedule.** A CVE published after your last deploy
will never be found by a build-time-only scan. Nightly re-scan of what is
actually running catches it. Keeping the SBOM makes the answer to "are we
affected?" a query instead of a rebuild.
### Suppress With Justification and an Expiry
Some findings genuinely cannot be fixed now. Record the decision where the tool
enforces it.
```yaml
# .trivyignore.yaml
vulnerabilities:
- id: CVE-0000-00000 # use the real advisory id
statement: >
Present only in the builder stage; the vulnerable binary is not copied
into the runtime image. Verified via `docker run --rm app find / -name ...`.
expiredAt: 2026-10-01
```
Non-negotiables: a reason someone else can evaluate, a named owner, and an expiry
that forces re-review. Open-ended suppressions accumulate until the scan is
decorative and the gate is a formality nobody trusts.
### Fix the Class
When the same weakness appears repeatedly, the instances are symptoms.
```
Symptom: SQL injection reported in 6 request handlers
Finding: string-interpolated SQL is possible anywhere in the codebase
Fix: parameterized query helper as the only DB entry point
Guard: semgrep rule failing CI on f-string/format SQL construction
Result: instance seven cannot merge
```
The scanner found six bugs. The assessment found one missing control. Report the
second — and pair the fix with a rule so the class stays closed.
## Common Anti-Patterns
❌ **Dumping raw scanner output into a ticket queue** — thousands of findings,
none triaged, all ignored.
✅ Dedupe, filter to deployed and reachable, rank, then assign the top band only.
❌ **Sorting by CVSS Base** — you fix an unreachable Critical while a chainable
Medium takes production.
✅ Rank on exploitation evidence (KEV, EPSS), reachability, and exposure.
❌ **Treating EPSS or CVSS as truth rather than input** — both are models with
error bars.
✅ Use them to order the queue; use judgement on the top of it.
❌ **Scanning only dependencies** — the most damaging flaws are usually
authorization and misconfiguration, which SCA cannot see.
✅ Cover source, secrets, deps, image, IaC, live cloud config, and the running app.
❌ **Build-time scanning only** — a CVE published tomorrow against today's running
image is never detected.
✅ Re-scan deployed images nightly; retain SBOMs so exposure is a query.
❌ **Suppressions with no expiry** — the ignore file becomes the real security
posture and nobody reviews it.
✅ Every suppression carries a reason, an owner, and an expiry date.
❌ **Blocking merges on every severity** — teams learn to bypass the gate.
✅ Block on secrets and reachable Critical/High; report the rest.
❌ **Assigning findings to "the security team"** — they cannot fix code they do
not own.
✅ Route to the owning service team; security sets policy and tracks aging.
❌ **Measuring open-finding count** — it rewards suppression over fixing.
✅ Measure mean time to remediate and the aging distribution per band.
## Vulnerability Assessment Checklist
- [ ] SAST, secret, SCA, image, IaC, cloud-posture, and DAST scanning all in place
- [ ] Manual testing covers the abuse cases from threat identification
- [ ] Scans run pre-merge and on a nightly schedule against deployed artifacts
- [ ] SBOM generated per build and retained for exposure queries
- [ ] Findings deduplicated across tools before triage
- [ ] Not-deployed and unreachable findings filtered out, with the filter auditable
- [ ] Ranking uses KEV and EPSS, not CVSS Base alone
- [ ] Exposure (internet-facing vs internal) factored into priority
- [ ] Chained medium findings reviewed as combinations, not in isolation
- [ ] Remediation SLAs defined per band and measured against reality
- [ ] Findings routed to the owning team, not a central queue
- [ ] Suppressions carry justification, owner, and expiry
- [ ] Recurring weaknesses fixed as a class and guarded by a CI rule
- [ ] Metrics track MTTR and aging, not open-finding count
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!