A defensive, read-only security assessment for any local project — with a reproducible reliability grade.
Point it at a codebase. It runs best-in-class open-source scanners and returns a graded report (0–100, A–F) where the number comes from a deterministic rubric, not model opinion.
- Why
- Quick start
- What it runs
- The grade
- Noise reduction
- Requirements
- Works offline / behind a proxy
- Use it without Claude
- Guarantees
- License
Ask an AI "is this project secure?" and you get a plausible-sounding essay that changes every time you ask. That is not a measurement.
deep-security-check gives you a number you can reproduce and defend: a
small Python script owns the scoring with fixed penalties and hard ceilings, so
the same findings always produce the same grade. The model narrates the results
— it never touches the number.
/plugin marketplace add give-jd/deep-security-check
/plugin install deep-security-check@give-security
Then just ask Claude:
"How reliable is this project, security-wise?"
Installing from a local clone instead? /plugin marketplace add /path/to/security-scan-skill
| Layer | Tool | What it catches |
|---|---|---|
| SAST | Semgrep — or Opengrep (LGPL fork) as a drop-in when semgrep is absent | Injection, unsafe deserialization, weak crypto, CI/CD & IaC misconfig |
| SCA | osv-scanner — trivy as fallback | Known CVEs and malicious packages in your dependencies |
| Secrets | gitleaks (vendored fallback bundled) | Hardcoded credentials — in the working tree and the git history |
Plus three advisory passes that never change the grade:
- 🌐 Dynamic-test surface (
surface.py) — a static, zero-traffic map of where a DAST run would look (permissive CORS, missing security headers, insecure cookies, debug/bind-all). - 🧱 Structure hygiene (
structure.py) — README, LICENSE, tests, CI, lockfiles, pinned deps, committed.env,SECURITY.md, dependabot/renovate, Dockerfile root user. - 📦 SBOM — a CycloneDX artifact via
syftwhen present.
score.py owns the number, so it is deterministic. Start at 100, subtract capped
per-severity penalties over all findings, then apply hard ceilings:
| severity | penalty each | cap |
|---|---|---|
| 🔴 critical | 25 | 60 |
| 🟠 high | 8 | 30 |
| 🟡 medium | 1.5 | 15 |
| ⚪ low | 0.25 | 6 |
Ceilings — any critical → score ≤ 69 · any high → score ≤ 84 · any malicious package → score ≤ 20.
| Grade | Range | Meaning |
|---|---|---|
| A | 90–100 | Solid |
| B | 80–89 | Solid, minor debt |
| C | 70–79 | Manageable debt |
| D | 60–69 | Real risk |
| E | 50–59 | Real risk |
| F | <50 | Not trustworthy until fixed |
assessment_confidence tells you how much to trust the score itself: high only
when all three scanner classes ran. A one-shot static scan is never a substitute
for a pentest — and the report says so.
Reproduce the rubric by hand, or run
python3 …/score.py --selfcheck.
So the grade reflects real risk, not scanner overlap:
- Cross-scanner dedup — the same
file:lineflagged by two tools counts once. - Path-aware triage — findings under
tests/,fixtures/,examples/,mocks/… are downgraded one severity step (a hardcoded credential in a fixture is not a production leak). - Malware first — OSV
MAL-*advisories are surfaced asmalicious_packages, separate from CVEs, and hard-cap the score. A malicious dependency dominates everything.
The triage field in the report shows exactly what was deduped and downgraded.
Two more passes are opt-in via --project <dir> on score.py:
- Reachability-lite — an SCA finding whose package name never appears in the source is downgraded one step (
declared-but-unused). Heuristic and conservative: it never drops a finding, only downgrades on a confident zero-match. .dsc-ignore— suppress accepted findings with a mandatory justification (<path-glob>:<ruleId-or-*> -- why). Suppressed findings are listed in the report with their reason; a rule missing its justification is rejected, not applied. Suppression stays visible, never silent.
At least one scanner on your PATH — more installed → higher confidence:
| Tool | Install |
|---|---|
| Semgrep | pipx install semgrep |
| osv-scanner | releases |
| gitleaks | releases — vendored fallback included |
Optional, used automatically when present: trivy (SCA fallback), syft (SBOM), opengrep (drop-in SAST engine).
Egress-restricted environments (corporate proxies, the Claude Cowork sandbox) degrade gracefully instead of hanging or failing:
- Semgrep registry unreachable → falls back to a bundled MIT ruleset (
rules/offline-basic.yaml, ~30 high-precision rules incl. CI/CD & IaC). Reduced coverage — and the report says so. - gitleaks missing on Linux x86_64 → a checksum-verified vendored binary is used.
- No SCA tool / OSV blocked → the report states plainly that dependency CVEs were not checked, and confidence is capped.
Knobs: DSC_SEMGREP_CONFIG (local ruleset), DSC_SEMGREP_TIMEOUT, DSC_TRIVY_OFFLINE, DSC_SKIP_HISTORY.
The scripts are plain bash + Python with no project-specific dependencies:
OUT=$(bash skills/deep-security-check/scripts/scan.sh <project-dir> | grep '^OUT=' | cut -d= -f2)
python3 skills/deep-security-check/scripts/score.py --sarif-dir "$OUT"
python3 skills/deep-security-check/scripts/structure.py <project-dir> # advisoryRe-scanning over time? Add --baseline <old.json> to score.py for a
vs_baseline delta (improved / worsened, per-severity).
- Read-only. It reads the project and never modifies it.
- Zero traffic to targets. It never sends network traffic to live or remote hosts. (Scanners may query package databases like OSV.dev with dependency names — see PRIVACY.md.)
- Defensive only. For code you own or are authorized to inspect. Not a penetration test; DAST is intentionally out of scope for a publicly installable skill.
MIT. See also PRIVACY.md · ROADMAP.md · SECURITY.md.
