| Audit returns malformed output |
INFO
Audit a Python project's installed dependencies for known CVEs by wrapping pip-audit (PyPA's official vulnerability auditor) and emitting findings in the canonical penetration-tester schema.
ReadBash(pip:*)Bash(pip-audit:*)Bash(python3:*)Glob
Auditing Python Dependencies
Overview
PyPI hosts north of 500,000 packages, with several thousand new releases every day. The package-install model is identical to npm in the relevant ways: a pip install resolves a transitive graph, runs each package's setup.py (which executes arbitrary Python at install time), and writes the result to your site-packages. The CVE attack surface is therefore the same shape: known vulnerabilities, maintainer-account takeovers, typosquats, and protestware.
The PyPA-blessed auditor is pip-audit. It queries the Open Source Vulnerabilities (OSV) database (which mirrors PyPA's advisory feed plus aggregated CVE / GHSA records) and reports per-package vulnerable versions. pip-audit integrates with requirements.txt, pyproject.toml, Pipfile.lock, and poetry.lock, so most Python project layouts are first-class.
This skill wraps pip-audit, normalizes its severity vocabulary to the shared Severity enum, and emits Findings in the canonical penetration-tester schema. If pip-audit isn't installed on the host, the skill falls back to pip list --outdated and emits INFO-level findings recommending the operator install pip-audit for accurate vulnerability detection.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Critical CVE in installed package |
CRITICAL |
OSV severity band corresponds to CVSS ≥ 9.0 |
CWE-1104 |
| High CVE in installed package |
HIGH |
OSV severity band corresponds to CVSS 7.0–8.9 |
CWE-1104 |
| Medium CVE in installed package |
MEDIUM |
OSV severity band corresponds to CVSS 4.0–6.9 |
CWE-1104 |
| Low CVE in installed package |
LOW |
OSV severity band corresponds to CVSS 0.1–3.9 |
CWE-1104 |
| Vulnerable package with no patch |
HIGH |
finding has no fix_versions and severity ≥ medium |
CWE-1395 |
| Outdated package (no CVE) |
INFO |
pip list --outdated reports a newer version |
(operational) |
| pip-audit not installed |
INFO |
binary not on PATH; scanner fell back to pip list |
(operational) |
| Audit DB unreachable |
INFO |
pip-audit network error reaching OSV |
(operational) |
OSV is the upstream of record. pip-audit also consults the PyPA advisory database for Python-specific records that may not yet have a CVE assigned.
Prerequi
Audit a target's HTTP security headers — CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and the Cross-Origin trio (COOP, COEP, CORP).
ReadBash(python3:*)Bash(curl:*)
Checking HTTP Security Headers
Overview
HTTP response headers are the cheapest defense-in-depth layer most web apps ship. Each header closes one specific attack class — HSTS forces HTTPS, CSP blocks script injection, X-Frame-Options blocks clickjacking, etc. Missing headers don't break the app; they just leave the attack class open. This skill probes for the presence + value correctness of the canonical security-relevant headers.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| HSTS header missing |
HIGH |
No Strict-Transport-Security on HTTPS response |
OWASP A05:2021 |
| HSTS max-age below preload threshold |
MEDIUM |
max-age under 31536000s (1y) |
hstspreload.org |
| HSTS includeSubDomains missing for preload |
LOW |
preload directive without includeSubDomains |
hstspreload.org |
| CSP header missing |
HIGH |
No Content-Security-Policy header |
OWASP A03:2021 |
| CSP allows unsafe-inline |
MEDIUM |
script-src or style-src includes 'unsafe-inline' |
OWASP A03:2021 |
| CSP allows unsafe-eval |
MEDIUM |
script-src includes 'unsafe-eval' |
OWASP A03:2021 |
| CSP frame-ancestors AND X-Frame-Options both missing |
HIGH |
Clickjacking open |
CWE-1021 |
| X-Content-Type-Options:nosniff missing |
MEDIUM |
MIME-sniff attack open |
OWASP A05:2021 |
| Referrer-Policy missing or unsafe-url |
MEDIUM |
Cross-origin URL leakage |
OWASP A05:2021 |
| Permissions-Policy missing |
LOW |
Camera/mic/geo permissions unrestricted |
Permissions Policy spec |
| Server: header discloses version |
LOW |
nginx/1.18.0 → fingerprintable |
CWE-200 |
| Cache-Control public on authenticated response |
HIGH |
Shared cache may serve user A's response to user B |
CWE-525 |
Prerequisites
- Python 3.9+
- Authorization for non-local targets
Instructions
Step 1 — Confirm authorization
"Do you have authorization to perform header testing on this target?
I need confirmation before proceeding."
Step 2 — Run the scanner
python3 ${CLAUDE_PLUGIN_ROOT}/skills/checking-http-security-headers/scripts/check_headers.py \
https://example.
Audit a project's dependency licenses against an explicit policy (allow-list / deny-list / review-required) and flag incompatibilities before they ship to production.
ReadBash(python3:*)Bash(pip:*)Bash(npm:*)Glob
Checking License Compliance
Overview
License compliance is a security concern only in the indirect sense that an unintended license obligation can force you to release proprietary source code, retroactively invalidate a customer contract, or render an M&A transaction infeasible. The cost is legal and contractual rather than exploitative — but the consequence ladder is real.
The most-stepped-on landmine is copyleft contamination: unintentionally including a GPL or AGPL-licensed package in a codebase the rest of which is permissively licensed (MIT, Apache-2.0, BSD). The terms of the GPL family say that any project distributing GPL code MUST itself release source under a GPL-compatible license. If your package.json says MIT and one of your transitive deps is GPL-2.0, you may be obligated to either re-license your code or remove the dep.
This skill audits the resolved dependency tree against an explicit policy file and emits findings for:
- Direct deps with deny-listed licenses
- Transitive deps with deny-listed licenses
- Packages with UNKNOWN license metadata (no SPDX identifier)
- License conflicts between metadata and source headers
- Combinations of licenses that are mutually incompatible (e.g. GPL-2.0 + Apache-2.0 without a patent grant)
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Strong-copyleft in permissive project |
CRITICAL |
GPL-2.0/3.0, AGPL-3.0, or similar in a project declaring MIT/Apache-2.0/BSD |
(legal) |
| Weak-copyleft requiring source disclosure |
HIGH |
LGPL family in a project where the obligation isn't being met (no source-availability commitment) |
(legal) |
| Custom / non-SPDX license |
HIGH |
License field doesn't match SPDX expression syntax; requires legal review |
(legal) |
| Unknown license |
MEDIUM |
Package has no license field, no LICENSE file detected |
(legal) |
| Deny-listed license (per policy) |
HIGH |
Package license is in the explicit deny-list in the policy file |
(legal) |
| Review-required license (per policy) |
MEDIUM |
Package license is in the review-list (e.g. MPL-2.0) |
(legal) |
| Incompatible license combination |
HIGH |
Detected pair of licenses known to conflict (e.g. GPL-2.0-only + Apache-2.0) |
(legal) |
| License declared differently in metadata vs source headers |
MEDIUM |
LICENSE file says one license; per-file SPDX-License-Identi
Read findings JSONL files from cluster 1-4 skills, deduplicate by fingerprint, group by severity, and compose a deliverable- grade markdown vulnerability report with per-finding sections (title, severity, target, detail, remediation, evidence) and a top-level summary table.
ReadWriteBash(python3:*)Glob
Composing Vulnerability Report
Overview
After cluster 1-4 scan skills run, each one produces a Findings file. A typical engagement ends up with eight to twenty such files across the different skill categories. The customer wants ONE vulnerability report — comprehensive, deduplicated, organized by severity, with each finding cross-referenced to its source skill and target.
This skill consumes one or more findings files (JSONL preferred, JSON list also accepted), deduplicates entries by the canonical fingerprint defined in lib/finding.py, enriches each finding with a CVSS v3.1 vector when one isn't present (using a deterministic heuristic based on severity + category — explicitly noted as "derived, not assigned by NVD" in the output), and emits a single markdown report with per-finding sections plus a top-level summary table.
The report has a defined structure that downstream tools (next two skills in cluster 6) consume:
- Header — engagement ID, generation timestamp, source files
- Summary table — finding count by severity
- Per-severity sections — CRITICAL first, then HIGH, MEDIUM, LOW, INFO
- Per-finding subsections — title, severity, target, detail, remediation, evidence, references
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Source file unparseable |
HIGH |
JSON/JSONL parse fails |
(operational) |
| Finding missing required field |
HIGH |
A finding record is missing title, severity, target, detail, or remediation |
(operational) |
| Duplicate fingerprint across files |
INFO |
Same finding appears in N>1 sources; reported as deduplication count |
(informational) |
| Source file has zero findings |
INFO |
Empty or all-info-only file; reported but not an error |
(informational) |
| Report generated cleanly |
INFO |
Positive confirmation |
(informational) |
Prerequisites
- Python 3.9+
- One or more findings files in JSON or JSONL format produced by any cluster 1-4 scan skill (which all share
lib/finding.py schema)
Instructions
Step 1 — Gather findings sources
By default the skill reads every file matching engagement/findings/*.json and engagement/findings/*.jsonl. Override with --source FILE (repeatable).
Step 2 — Run the composer
python3 ./scripts/compose_report.py engagements/acme-2026-q2/
Options:
Verify that a penetration test has explicit, written, signed authorization before any scanning begins.
ReadBash(python3:*)Glob
Confirming Pentest Authorization
Overview
Penetration testing is computer access. Without explicit authorization from the owner of the system under test, that access is a crime — Computer Fraud and Abuse Act in the US, Computer Misuse Act in the UK, equivalent laws everywhere else. The line between an authorized pentester and an unauthorized attacker is one signature on one document.
The penetration-tester pack's other skills (TLS analysis, CORS audit, dependency CVE scan, etc.) all assume that line has been crossed correctly. This skill is the first gate the orchestrator routes to. It refuses to declare an engagement authorized until a Rules of Engagement (ROE) attestation file exists, is signed, and contains the fields any real-world legal review will look for.
This is not paranoia or paperwork theater. Engagements DO go sideways: scope creeps mid-test, a tester probes an out-of-scope adjacent system, an SOC team escalates a "real" attack to legal, and the question "show us the ROE" comes up. If the ROE is in order, the answer is "here it is." If not, the conversation gets expensive fast.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| ROE file missing |
CRITICAL |
No attestation file at the expected path |
(legal) |
| Required field missing |
CRITICAL |
authorizer, in_scope_targets, time_window, emergency_contact, or signature absent |
(legal) |
| Signature missing |
CRITICAL |
No signature_block in ROE |
(legal) |
| Signer not in allowlist |
CRITICAL |
signer email/key id not in .allowed-authorizers |
(legal) |
| Time window expired |
HIGH |
current time outside time_window.start / time_window.end |
(legal) |
| Time window not yet active |
HIGH |
current time before time_window.start |
(legal) |
| In-scope target list empty |
HIGH |
in_scope_targets field present but empty |
(legal) |
| Out-of-scope override (manual flag) |
MEDIUM |
tester requests a target not in the in-scope list |
(legal) |
| Stale ROE (>30 days from sign date) |
MEDIUM |
last_signed_at older than 30 days; suggests refresh |
(operational) |
| ROE present + signed + in window |
INFO |
All gates pass; engagement is authorized |
(positive confirmation) |
Prerequisites
Parse the ROE scope definition, enumerate every in-scope target (hostnames, IPs, CIDRs, URLs, cloud accounts, SaaS tenants), validate syntax, detect overlap with out-of-scope or known third-party SaaS ranges, and emit a normalized target list plus IP allowlist for scanning tools.
ReadBash(python3:*)Glob
Defining Pentest Scope
Overview
A pentest scope is a list of permission boundaries. Get it wrong and you either (a) miss real exposure by failing to test something the customer expected covered, or (b) probe something you weren't allowed to touch and turn the engagement into a liability event. Both failure modes share a root cause: the scope list was a vague narrative ("test the marketing site and the API") rather than a machine-readable, syntactically-validated, conflict-checked artifact.
This skill takes the in-scope and out-of-scope sections from a ROE and produces three deliverables:
- Normalized target list — every entry parsed into a structured form (host vs CIDR vs URL path vs cloud account vs SaaS tenant), with explicit type tagging. Downstream cluster 1-4 skills consume this list rather than raw strings.
- IP allowlist — flat list of IPv4 and IPv6 addresses / CIDRs ready to paste into scanner configurations (nmap target list, Burp scope file, AWS WAF allowlist, etc.).
- Conflict report — Findings flagging syntactically-malformed entries, overlap between in-scope and out-of-scope, inclusion of reserved ranges (RFC1918, link-local, multicast), and known third-party SaaS infrastructure that needs separate authz.
The skill does NOT perform DNS resolution or network probing — that would itself be a "first probe" of the target, which by the governance model must happen AFTER scope is locked.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Malformed target syntax |
HIGH |
Entry doesn't parse as host / CIDR / URL / account-id |
(legal) |
| In-scope overlaps out-of-scope |
CRITICAL |
An in-scope target falls within an out-of-scope CIDR |
(legal) |
| Reserved range without acknowledgement |
HIGH |
RFC1918, link-local (169.254/16), multicast (224/4), broadcast in in-scope list |
(operational) |
| Known third-party SaaS in scope |
HIGH |
In-scope IP matches a known SaaS range (AWS, Cloudflare, GitHub, etc.) without separate authz |
(legal) |
| Duplicate target |
INFO |
Same target appears multiple times |
(operational) |
Wildcard subdomain (e.g. *.acme.example) |
INFO |
Wildcards expand at scan time |
(informational) |
| All targets validated cleanly |
INFO |
Positive confirmation |
(informational) |
Prerequisites
- Python 3.9+
- ROE file at
./roe.yaml
Scan a source tree for command-injection vulnerable patterns: shell=True calls in Python subprocess, os.
ReadBash(python3:*)GlobGrep
Detecting Command Injection Patterns
Overview
Command injection (CWE-78, OWASP A03:2021) shows up wherever an application shells out to a binary. Image conversion (convert), archive extraction (tar, unzip), video processing (ffmpeg), DNS lookup (dig), and "we just need to call this CLI tool once" are the common origins.
The vulnerability shape is universal: a string is built including user input, then handed to a shell interpreter. The shell parses the string with normal shell semantics — including ;, |, &, $(), backticks. Any of those in the user-controlled portion becomes shell-executable.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
Python subprocess.run(..., shell=True) with interpolation |
CRITICAL |
f-string / concat / format argument with shell=True |
CWE-78 |
Python os.system(...) with interpolation |
CRITICAL |
non-literal argument |
CWE-78 |
Python os.popen(...) with interpolation |
CRITICAL |
non-literal argument |
CWE-78 |
Node child_process.exec(...) with template literal |
CRITICAL |
${...} in the command string |
CWE-78 |
Node child_process.execSync(...) with template |
CRITICAL |
same |
CWE-78 |
| Ruby backticks with interpolation |
CRITICAL |
`cmd #{var}` |
CWE-78 |
Ruby Kernel#system(string) with interpolation |
CRITICAL |
system("cmd #{var}") |
CWE-78 |
Go exec.Command("sh", "-c", ...) with interpolation |
HIGH |
shell wrapper with var |
CWE-78 |
PHP system / exec / passthru / shell_exec with $-interp |
CRITICAL |
system("cmd $var") |
CWE-78 |
Java Runtime.exec(String) with concat |
HIGH |
single-string form (vs array) with var |
CWE-78 |
Prerequisites
- Python 3.9+
- Target source tree on local filesystem
Instructions
Step 1 — Run the scanner
python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-command-injection-patterns/scripts/scan_cmdi.py /path/to/repo
Options:
Probe a target for accidentally-public admin / debug / introspection endpoints — Spring Boot Actuator, Apache server-status, Prometheus metrics, GraphQL playground, Swagger UI, phpMyAdmin, JMX-over-HTTP (Jolokia), Elasticsearch _cat, Kibana / Grafana / Eureka / Consul panels.
ReadBash(python3:*)Bash(curl:*)
Detecting Debug Endpoints
Overview
Modern web stacks ship rich introspection by default. Spring Boot Actuator exposes /actuator/env (every environment variable), /actuator/heapdump (a live heap snapshot that contains credentials), /actuator/jolokia (JMX bean invocation = pre-auth RCE in some configurations). Apache mod_status exposes /server-status with internal IPs, request counts, and the URL of every active request. Prometheus /metrics exposes operational telemetry that often includes connection-string-bearing labels by accident. phpMyAdmin exposes the entire database if unauthenticated.
These are not bugs in the frameworks. They're features that ship enabled-by-default for development convenience and stay enabled in production because nobody disabled them at install time. The probe set covers the canonical 40+ paths and grades each by the response fingerprint specific to that framework.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
Spring Boot Actuator /env exposed |
CRITICAL |
200 + body has "propertySources" |
OWASP A05:2021 |
Spring Boot Actuator /heapdump exposed |
CRITICAL |
200 + Content-Type: application/octet-stream + multi-MB body |
CWE-200 |
Spring Boot Actuator /jolokia exposed |
CRITICAL |
200 + body has "agent":"jolokia" |
CWE-749 |
| phpMyAdmin reachable |
CRITICAL |
200 + HTML body contains "phpMyAdmin" + login form |
OWASP A07:2021 |
Prometheus /metrics exposed |
HIGH |
200 + body has # HELP or # TYPE lines |
CWE-200 |
Apache mod_status exposed |
HIGH |
200 + body contains "Apache Server Status" |
CWE-200 |
Spring Boot Actuator /actuator index |
HIGH |
200 + body has "_links" JSON |
OWASP A05:2021 |
Generic /admin returning 200 (not 401/403) |
HIGH |
200 + HTML body with admin-shaped UI |
CWE-285 |
Elasticsearch _cat exposed |
HIGH |
200 + body matches health\s+status\s+index |
CWE-200 |
| GraphQL Playground on prod |
MEDIUM |
200 + body contains "GraphQLPlayground" |
CWE-200 |
Probe a target for directories that return auto-generated index listings instead of denying or serving a specific file — exposes the full file tree under any reachable directory, including files the application never linked to.
ReadBash(python3:*)Bash(curl:*)
Detecting Directory Listing
Overview
Web servers can be configured to auto-generate an HTML index page when a request hits a directory without a matching default file (no index.html / index.php / etc.). The auto-generated page lists every file in the directory. This is by design for static- file servers and FTP-style file-sharing setups; it's a misconfiguration on application servers where the file tree was never meant to be public.
The risk compounds in two ways:
- File enumeration without prior knowledge. Attackers find files you didn't link to (backup files, log files, old uploads).
- Chaining with exposed-files (#6). A
/.git/ request that returns an autoindex listing reveals every object file under .git/objects/ and confirms the entire repo is reachable for GitDumper-style reconstruction.
This skill probes commonly-vulnerable directory paths and grades each based on the framework-specific autoindex fingerprint.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Application-root directory listing |
HIGH |
/ returns autoindex page |
OWASP A05:2021 |
| Backup / upload / log directory listing |
HIGH |
/backup/, /uploads/, /logs/, /dump/ autoindex |
CWE-548 |
Asset directory listing (/assets/, /static/) |
MEDIUM |
autoindex on asset dirs (enables file enumeration) |
CWE-548 |
| Config directory listing |
CRITICAL |
/config/, /conf/, /.config/ autoindex |
NIST 800-53 SC-28 |
| VCS subdir listing (chains with skill #6) |
CRITICAL |
/.git/, /.svn/, /.hg/ autoindex |
NIST 800-53 SC-28 |
| Generic root reachable via autoindex |
MEDIUM |
/ or arbitrary path autoindex on app server |
OWASP A05:2021 |
Prerequisites
- Python 3.9+ with
requests
- Authorization for non-local targets
Instructions
Step 1 — Confirm Authorization
"Do you have authorization to perform directory-listing discovery on
this target? I need confirmation before proceeding."
Step 2 — Run the scanner
python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-directory-listing/scripts/probe_directory_listing.py \
https://target.example.com \
--authorized
Scan a source tree for dynamic-code-execution APIs that an attacker can hijack: Python eval / exec / compile, JavaScript eval / Function() / setTimeout(string), Ruby eval / instance_eval / class_eval, Java ScriptEngine, PHP eval / assert($str), .
ReadBash(python3:*)GlobGrep
Detecting eval / exec Usage
Overview
Dynamic-code-execution APIs (CWE-95 Eval Injection) let an application interpret a string as code at runtime. If the string contains anything user-controllable, the application has handed the attacker arbitrary code execution.
The defensive posture: don't use these APIs. The exceptions are narrow: rule engines, formula evaluators (spreadsheet = formulas), plugin systems with explicit sandboxing. For everything else, there's almost always a safer alternative.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
Python eval(...) with non-literal |
CRITICAL |
argument contains var ref |
CWE-95 |
Python exec(...) with non-literal |
CRITICAL |
argument contains var ref |
CWE-95 |
Python compile(...) with non-literal |
HIGH |
source string contains var |
CWE-95 |
Python __import__(var) |
HIGH |
dynamic module loading |
CWE-95 |
JS eval(...) |
CRITICAL |
any |
CWE-95 |
JS new Function(str) |
CRITICAL |
any non-literal |
CWE-95 |
JS setTimeout/setInterval(string) |
HIGH |
string instead of function |
CWE-95 |
Ruby eval(...)/instance_eval(...)/class_eval(...) |
CRITICAL |
non-literal |
CWE-95 |
PHP eval(...) |
CRITICAL |
always |
CWE-95 |
PHP assert($str) |
CRITICAL |
(legacy code-eval form) |
CWE-95 |
PHP create_function |
CRITICAL |
deprecated, eval-equivalent |
CWE-95 |
Java ScriptEngineManager + eval |
HIGH |
dynamic script execution |
CWE-95 |
C# Activator.CreateInstance(Type.GetType(str)) |
HIGH |
type loading from string |
CWE-95 |
Prerequisites
- Python 3.9+
- Source tree on local filesystem
Instructions
Run
python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-eval-exec-usage/scripts/scan_eval.py /path/to/repo
Options: --output FILE, --format json|jsonl|markdown, --min-severity, --include-tests
Probe a target for accidentally-served secret-bearing files in the web root — .
ReadBash(python3:*)Bash(curl:*)
Detecting Exposed Secrets Files
Overview
The single highest-value pentest probe per HTTP request. A .git/config disclosure leaks repo URL + credentials embedded in remote URLs. A .env disclosure leaks every API key the app has. A backup.sql disclosure leaks the entire database. These are not "weak crypto" findings that need a chained exploit. They are direct, immediate compromise.
The probe set is the canonical 40+ paths web servers commonly expose by accident: VCS directories (.git/, .svn/, .hg/), dotenv files, OS metadata (.DS_Store), database dumps, archive files, IDE configs, CI configs, and key files. Each is fingerprinted to distinguish a true positive (server returns the file's expected content) from a 200 OK that's actually the application's SPA index page catching the route.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
.git/HEAD reachable + valid content |
CRITICAL |
200 + body matches ref: or 40-char SHA |
NIST 800-53 SC-28 |
.git/config reachable + repo URL leaked |
CRITICAL |
200 + body matches [remote |
NIST 800-53 SC-28 |
.env reachable + dotenv format |
CRITICAL |
200 + body matches KEY=VALUE lines |
OWASP A05:2021 |
*.sql / *.dump / backup.* reachable |
CRITICAL |
200 + body looks like SQL or binary dump |
CWE-538 |
.aws/credentials reachable |
CRITICAL |
200 + body matches [default]\naws_access_key_id |
CWE-200 |
id_rsa / *.pem / *.key reachable |
CRITICAL |
200 + body matches BEGIN PRIVATE KEY or BEGIN RSA |
CWE-321 |
.svn/entries / .hg/store/ reachable |
HIGH |
200 + body matches VCS format |
NIST 800-53 SC-28 |
.DS_Store reachable |
MEDIUM |
200 + binary blob with Bud1 magic |
CWE-538 |
IDE configs (.idea/, .vscode/) reachable |
LOW |
200 + JSON/XML |
CWE-200 |
composer.json / package.json reachable on prod |
LOW |
200 + valid JSON in non-API root |
CWE-200 |
Prerequisites
Scan a source tree for unsafe-by-default deserialization APIs: Python pickle.
ReadBash(python3:*)GlobGrep
Detecting Insecure Deserialization
Overview
Insecure deserialization (CWE-502, OWASP A08:2021) is the highest- severity injection class in many language stacks because it directly maps to RCE. Pickle, Java serialization, PHP unserialize, and BinaryFormatter all execute object-construction code during deserialization. If that code includes __reduce__ / readObject / __wakeup / OnDeserialization callbacks that the attacker controls, the deserialization step IS code execution.
Most legitimate use cases have safer alternatives (JSON for data, YAML with safe-load, Protocol Buffers, Avro). The remaining cases need explicit type allow-lists and HMAC-signed payloads.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
Python pickle.loads(...) |
CRITICAL |
always (untrusted input) |
CWE-502 |
Python pickle.load(file) |
CRITICAL |
always |
CWE-502 |
Python dill.loads |
CRITICAL |
always |
CWE-502 |
Python yaml.load(...) without Loader= |
CRITICAL |
unsafe legacy default |
CWE-502 |
Python yaml.unsafe_load(...) |
CRITICAL |
explicit unsafe |
CWE-502 |
Python shelve.open(...) |
HIGH |
pickle-backed; user-controllable filename |
CWE-502 |
Java ObjectInputStream.readObject() |
CRITICAL |
always |
CWE-502 |
PHP unserialize($input) |
CRITICAL |
non-literal input |
CWE-502 |
.NET BinaryFormatter.Deserialize(...) |
CRITICAL |
deprecated unsafe API |
CWE-502 |
.NET NetDataContractSerializer |
CRITICAL |
also unsafe |
CWE-502 |
.NET LosFormatter.Deserialize |
CRITICAL |
ViewState path |
CWE-502 |
Ruby Marshal.load(...) |
CRITICAL |
non-literal |
CWE-502 |
Ruby YAML.load(...) (pre-3.1 Psych) |
CRITICAL |
safe in Psych 4.0+; needs version check |
CWE-502 |
Node.js node-serialize.unserialize |
CRITICAL |
known-vulnerable lib |
CWE-502 |
Node.js serialize-javascript reviver |
HIGH |
Scan a source tree for SQL-injection vulnerable patterns: string concatenation into queries, f-string interpolation in SQL, string-format substitution into raw queries, deprecated cursor methods (cursor.
ReadBash(python3:*)GlobGrep
Detecting SQL Injection Patterns
Overview
SQL injection (CWE-89, OWASP A03:2021) remains one of the highest- impact and most-easily-introduced vulnerability classes. The fix is near-universal: use parameterized queries. The cause when introduced: an engineer concatenates user input into a SQL string because the ORM's parameterization mechanism wasn't obvious, or because they "just need to add a quick condition."
The scanner reads source files and grades each apparent SQL-string construction against the threshold table.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| f-string with SQL keywords + user input |
CRITICAL |
f"SELECT * FROM users WHERE id = {user_id}" |
CWE-89 |
| String concat into SQL keyword string |
CRITICAL |
"SELECT ... " + var + " ..." |
CWE-89 |
| %-format SQL string |
HIGH |
"SELECT * FROM %s" % table_name |
CWE-89 |
.format() into SQL string |
HIGH |
"SELECT {} FROM users".format(col) |
CWE-89 |
cursor.execute(f"...") |
CRITICAL |
f-string passed directly to cursor.execute |
CWE-89 |
sequelize.query with template literal |
HIGH |
sequelize.query(\`SELECT * FROM ${table}\`) |
CWE-89 |
| Knex / sequelize raw() with interpolation |
HIGH |
knex.raw('SELECT * FROM ' + table) |
CWE-89 |
Django .extra() with raw SQL |
MEDIUM |
Model.objects.extra(where=['col = ' + val]) |
CWE-89 |
cursor.executemany with string-built query |
CRITICAL |
Same risk as execute |
CWE-89 |
JDBC Statement.execute with concat |
HIGH |
Java pattern: not PreparedStatement |
CWE-89 |
Rails where() with string interpolation |
HIGH |
User.where("name = '#{name}'") |
CWE-89 |
Go db.Query with fmt.Sprintf |
HIGH |
db.Query(fmt.Sprintf("...", arg)) |
CWE-89 |
Prerequisites
- Python 3.9+
- Target source tree on local filesystem
Instructions
Step 1 — Run the s
Audit a target's TLS certificate beyond protocol/expiry — chain ordering, OCSP stapling, revocation status, Certificate Transparency presence, key-usage flags, and over-broad wildcards.
ReadBash(python3:*)Bash(openssl:*)
Detecting SSL Certificate Issues
Overview
This skill is the second-level cert audit, run after analyzing-tls-config clears the protocol+cipher+expiry+hostname basics. It surfaces issues that don't break the handshake today but make the cert fragile or open to soft-bypass attacks: missing OCSP stapling forces clients to phone home to the CA (privacy + latency hit), missing Certificate Transparency SCTs are rejected by Chrome since 2018, an out-of-order chain confuses older clients, and over-broad wildcards expand the blast radius of any future key compromise.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Revoked certificate presented |
CRITICAL |
OCSP responder says "revoked" |
RFC 6960 |
| Missing or invalid OCSP staple |
HIGH |
No status_request response on production |
RFC 6066, CA/B BR |
| Fewer than 2 SCTs embedded |
HIGH |
CT-policy violation (Chrome enforces) |
RFC 6962, CA/B Baseline Reqs |
| Intermediate served out of RFC 5246 order |
MEDIUM |
Server sends root before leaf |
RFC 5246 §7.4.2 |
| AIA extension missing |
MEDIUM |
No CA Issuers / OCSP URL in cert |
RFC 5280 §4.2.2.1 |
| Over-broad wildcard |
HIGH |
Scope of 2-level or wider (e.g., *.com) |
CA/B Baseline Reqs §3.2.2 |
| Wildcard at apex SAN |
LOW |
*.example.com without example.com |
RFC 6125 §6.4.3 |
| Key Usage missing digitalSignature |
MEDIUM |
KU bit absent for TLS server cert |
RFC 5280 §4.2.1.3 |
| Cert chain longer than 4 |
LOW |
Performance + trust expansion |
CA/B Baseline Reqs |
Prerequisites
- Python 3.9+ with
cryptography library
openssl CLI 1.1.1+ (for OCSP query + chain enumeration)
- Authorization for non-local targets (see
references/AUTHORIZATION.md in skill #1 analyzing-tls-config for the canonical pattern)
Instructions
Step 1 — Confirm Authorization
Active scan; ask the user verbatim:
> "Do you have authorization to perform TLS testing on this target? > I need confirmation before proceeding."
Step 2 — Run the scanner
python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-ssl-cert-issues/scripts/check_cert_chain.py \
https://target.example.com \
--authorized
Options:
Usage: check_cert_cha
Scan a source tree for weak cryptographic primitives: MD5 / SHA-1 used for security purposes, DES / 3DES / RC4 ciphers, ECB block mode, custom-built crypto (XOR loops, hand-rolled HMAC), hardcoded IVs, predictable random (Math.
ReadBash(python3:*)GlobGrep
Detecting Weak Cryptography
Overview
Weak cryptography (CWE-327 Use of a Broken or Risky Cryptographic Algorithm, CWE-330 Use of Insufficiently Random Values) shows up when engineers use the convenient API instead of the cryptographic one. hashlib.md5(password) is faster to type than the correct bcrypt/argon2 invocation; Math.random() returns a number quickly without needing to know about crypto.randomBytes().
The fix is universal: use the modern primitive. SHA-256 for general hashing, bcrypt/argon2/scrypt for passwords, AES-GCM for encryption, HMAC-SHA256 for signing, secrets / crypto.randomBytes / SecureRandom for randomness.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| MD5 used in security context |
HIGH |
hashlib.md5, MessageDigest.MD5, CryptoJS.MD5 |
CWE-327 |
| SHA-1 used in security context |
HIGH |
hashlib.sha1, etc. |
CWE-327 |
| DES / 3DES cipher |
CRITICAL |
DESCrypto, "DES/CBC", "DESede" |
CWE-327 |
| RC4 cipher |
CRITICAL |
"ARC4", "RC4" |
CWE-327 |
| AES ECB mode |
CRITICAL |
"AES/ECB" or MODE_ECB |
CWE-327 |
| Hardcoded IV (initialization vector) |
CRITICAL |
IV literal in source |
CWE-329 |
| Custom XOR-based "encryption" |
CRITICAL |
XOR loop over bytes |
CWE-327 |
| Predictable random for crypto seed |
CRITICAL |
Math.random / java.util.Random / random.random for keys |
CWE-330 |
| TLS cert verification disabled |
CRITICAL |
verify=False, rejectUnauthorized:false, ServerCertificateValidationCallback returning true |
CWE-295 |
| Hardcoded HMAC secret |
HIGH |
Long literal in HMAC constructor |
CWE-321 |
| Insecure password hashing (no salt, no KDF) |
CRITICAL |
hashlib.sha256(password) without bcrypt/argon2 |
CWE-916 |
Prerequisites
- Python 3.9+
- Source tree on local filesystem
Instructions
Run
python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-weak-cryptography/scripts/scan_weak_crypto.py /path/to/repo
Options: --output, --format, --min-severity, --include-tests, --languages,
Identify the server software, framework, and component versions a target is running from its HTTP response signatures — Server header, X-Powered-By, Via, X-AspNet-Version, X-Runtime, X-Drupal-Cache, X-Generator, Set-Cookie name patterns, error-page artwork, HTTP method behavior signatures.
ReadBash(python3:*)Bash(curl:*)
Fingerprinting Server Software
Overview
Version disclosure is the cheapest recon an attacker buys. A single GET request returns the Server: header, the X-Powered-By: header, and any framework-default cookies. From those three signals the attacker derives: web server family + exact version, app-framework + version, language runtime + version. That maps directly to a CVE catalog query: every published CVE affecting any of those components, filtered to ones an unauthenticated attacker can trigger.
The fix is operationally trivial (one line in nginx, one line in Apache, one line in IIS) but the discipline isn't universal. This skill enumerates the signals + reports each disclosure with the severity matching how much it enables follow-on attack.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Server header discloses version |
MEDIUM |
Server: nginx/1.18.0 or similar with explicit version |
CWE-200 |
| Server header discloses minor version |
LOW |
Server: nginx (no version) |
CWE-200 |
| X-Powered-By discloses framework version |
MEDIUM |
X-Powered-By: PHP/7.4.21, Express, ASP.NET |
CWE-200 |
| X-AspNet-Version present |
HIGH |
Specific dotnet runtime version |
CWE-200 |
| X-Runtime / X-Rails / X-Django headers present |
LOW |
Framework identification, no version |
CWE-200 |
| X-Generator: drupal/wordpress + version |
MEDIUM |
CMS family + version disclosure |
CWE-200 |
| Via header discloses proxy chain |
LOW |
Reveals upstream architecture (Varnish, Squid, CloudFront) |
CWE-200 |
| Framework-default Set-Cookie pattern |
LOW |
PHPSESSID, JSESSIONID, connect.sid, etc. |
CWE-200 |
| Error page reveals stack trace |
HIGH |
5xx response body contains source file paths or framework banner |
CWE-209 |
| HTTP/2 server-push fingerprint |
LOW |
HTTP/2 :server pseudo-header with version |
CWE-200 |
| ETag format identifies cluster member |
LOW |
Apache-style hex ETags reveal node |
CWE-200 |
Prerequisites
- Python 3.9+ with
requests
- Authorization for non-local targets
Instructions
Step 1 — Confirm Authorization<
Compose an exec-readable summary from a unified findings JSONL plus the OWASP coverage report.
ReadWriteBash(python3:*)Glob
Generating Executive Summary
Overview
The vulnerability report is comprehensive — every finding, full detail, every reference. The C-level reader doesn't open it. They ask their security lead "what should I tell the board?" The security lead needs a one-page answer.
That one-page answer is the executive summary. It states the engagement's bottom line:
- A single risk score (0-100)
- Headline counts by severity
- Top-3 remediation priorities, each with rough effort + impact
- OWASP Top 10 coverage (where the work landed)
- Engagement scope and authorization summary (what was tested, under what authority, in what window)
- Next steps the customer's organization should take
The summary doesn't omit anything important; it just compresses. The vulnerability report remains the deep artifact for anyone who needs the technical detail.
This skill consumes the enriched findings JSONL (after OWASP mapping) + the OWASP coverage report + the ROE, computes the risk score, picks the top remediation priorities deterministically, and renders the document.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Input findings file missing |
CRITICAL |
Source JSONL doesn't exist |
(operational) |
| OWASP coverage report missing |
HIGH |
Coverage referenced but not present |
(operational) |
| ROE missing |
MEDIUM |
Can still generate summary but lacks scope/authz context |
(operational) |
| Exec summary written cleanly |
INFO |
Confirmation |
(informational) |
| Risk score >75 (high engagement risk) |
HIGH |
Computed risk score elevated |
(advisory) |
| Risk score >90 (critical engagement risk) |
CRITICAL |
Engagement exposed material risk; needs urgent action |
(advisory) |
Risk score (0-100) composition
The single risk score is the headline number on the exec summary. The composition is deterministic and documented:
risk = clamp(0, 100,
20 * count(CRITICAL)
+ 10 * count(HIGH)
+ 3 * count(MEDIUM)
+ 1 * count(LOW)
+ 0 * count(INFO)
+ 5 * (count(distinct OWASP categories touched) - 5 if >5 else 0)
- 10 * 1 if engagement was authorized cleanly and in-scope (governance bonus)
)
The first five terms weight by severity. The OWASP-coverage term adds 5 points per category beyond 5 (a broader-finding engagement implies broader risk surface). The governance bonus is a -10 adjustment when ROE was clean — explicit recognition that findin
Annotate every pentest finding with its OWASP Top 10 (2021) category by applying a deterministic rule table keyed on source skill, finding category, detail keywords, and CWE identifier when present.
ReadWriteBash(python3:*)Glob
Mapping Findings to OWASP Top 10
Overview
OWASP Top 10 is the canonical taxonomy of web-application risk categories. Every customer-facing pentest report has an "OWASP coverage" section because customers, auditors, and insurers expect to see findings mapped against the Top 10. Without the mapping, a long list of CVEs and misconfigurations reads as noise; with the mapping, it reads as a structured assessment.
This skill applies a deterministic rule table to annotate each finding with its OWASP category. Rules are keyed on:
- Source skill ID (a finding from
auditing-npm-dependencies is almost always A06 — Vulnerable and Outdated Components).
- Finding category (the per-skill category tag, e.g.
dependency-vulnerability, engagement-scope).
- CWE identifier if present (CWE → OWASP is a well-trodden mapping; OWASP themselves publish the cross-walk).
- Detail keywords as a fallback when the above don't determine a category.
Output is an enriched JSONL (each finding gets an owasp_category field) plus a coverage report showing how the engagement's findings distribute across A01-A10. UNMAPPED findings are surfaced for human review — extend the rule table or accept the finding as cross-cutting (some findings genuinely don't fit a single A0X bucket).
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Finding unmapped after rule application |
INFO |
No rule matched the finding |
(operational) |
| Source JSONL unparseable |
HIGH |
Standard JSONL parse error |
(operational) |
| Annotation written back successfully |
INFO |
Confirmation per source file |
(informational) |
| Coverage report generated |
INFO |
Coverage report path emitted |
(informational) |
| Engagement covers all 10 categories |
INFO |
At least one finding in each A01-A10 bucket |
(positive observation) |
| Engagement covers <5 of 10 categories |
MEDIUM |
Suggests scope may have been narrow |
(informational) |
OWASP Top 10 (2021) reference
| Category |
Description |
| A01:2021 |
Broken Access Control |
| A02:2021 |
Cryptographic Failures |
| A03:2021 |
Injection |
| A04:2021 |
Insecure Design |
| A05:2021 |
Security Misconfiguration |
| A06:2021 |
<
Orchestrate a penetration test by routing user intent to one or more of the 25 narrow skills in this pack.
ReadWriteEditGlobGrepBash(python3:*)Bash(pip:*)Bash(npm:*)Bash(bandit:*)Bash(pip-audit:*)Bash(pipdeptree:*)Bash(gpg:*)Bash(tar:*)
Performing Penetration Testing
Overview
v3.0.0 of penetration-tester is a 25-skill pack. Each skill is narrow and heavy-hitter compliant (≥250 LOC scripts, ≥2 reference docs, 8-field SKILL.md frontmatter). This orchestrator routes user intent to the right combination of narrow skills.
The 25 skills group into 7 clusters:
- Cluster 0 — this orchestrator
- Cluster 1 (5 skills) — Network / transport
- Cluster 2 (4 skills) — Information disclosure
- Cluster 3 (6 skills) — Source-code static analysis
- Cluster 4 (4 skills) — Dependency analysis
- Cluster 5 (3 skills) — Engagement governance
- Cluster 6 (3 skills) — Reporting
Cluster 5 + 6 are the v3 additions versus v2. Cluster 5 runs BEFORE any scan and refuses to proceed if authorization is missing or scope is malformed. Cluster 6 runs AFTER scans and produces the deliverable artifacts (vulnerability report, OWASP coverage report, executive summary, chain-of-custody archive).
Instructions
The orchestrator's job is intent routing — given a user utterance, decide which of the 25 narrow skills to invoke and in what order. Four steps:
Step 1 — Parse the user intent
Match the utterance against the intent-routing table below. The leftmost matching row determines the routing. If no exact match, default to the cluster-1-4 governance-first sequence and pare back based on context.
Step 2 — Run authorization-first
Before any cluster 1-4 scan invocation, run confirming-pentest-authorization. If it emits any CRITICAL finding, HALT — do not invoke any scan skill. The user must resolve the authorization issue before proceeding.
Step 3 — Run the matched skills in order
Invoke each skill from the routing-table row, in the listed order. Each skill emits its findings as JSON/JSONL/markdown via lib/report.py. Persist per-skill output into engagement/findings/<skill>-<date>.jsonl so the cluster 6 skills can consume them.
Step 4 — Compose deliverables
After scan skills complete, run cluster 6 in sequence: mapping-findings-to-owasp-top10, then composing-vulnerability-report, then generating-executive-summary, then recording-pentest-engagement for the chain-of-custody archive.
Intent routing (the table)
| User intent / trigger phrase |
Skills to invoke (in order) |
| "pentest", "full security scan" |
confirming-pentest-authorization → defining-pentest-scope → cluster 1-4 (all) → mapping-findings-to-owasp-top10 → composing-vulnerability-report → generating-executive-summary → recording-pentest-engagement |
Probe a target for HTTP methods that should not be enabled in production — TRACE (XST attack), unrestricted PUT/DELETE, DEBUG/CONNECT, WebDAV (PROPFIND/MKCOL/COPY/MOVE), and Allow header enumeration.
ReadBash(python3:*)Bash(curl:*)
Probing Dangerous HTTP Methods
Overview
Most HTTP methods beyond GET/POST/HEAD are vestigial — leftover from WebDAV authoring stacks of the early 2000s, debugging features in legacy servers, or default-enabled methods nobody disabled at install time. Each enabled method that the application doesn't use is an attack surface: TRACE enables Cross-Site Tracing (XST), PUT enables arbitrary file upload to unprotected paths, CONNECT enables proxy abuse against internal services, WebDAV enables directory manipulation.
This skill probes the canonical method set and grades each based on its presence and response.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| TRACE method enabled |
HIGH |
TRACE returns 200 with request echo |
OWASP A05:2021, CWE-693 |
| PUT method enabled outside API path |
HIGH |
PUT returns 200/201/204 on non-API path |
CWE-434 |
| DELETE method enabled outside API path |
HIGH |
DELETE returns 200/204 on non-API path |
CWE-285 |
| CONNECT method enabled |
CRITICAL |
CONNECT returns 200 (proxy abuse open) |
CWE-441 |
| DEBUG method enabled |
HIGH |
DEBUG returns response (legacy IIS / dev servers) |
CWE-489 |
| WebDAV methods enabled (PROPFIND/MKCOL/COPY/MOVE) |
HIGH |
Any return 207 or 201 |
CWE-538 |
| Allow header discloses unused methods |
LOW |
OPTIONS Allow includes methods app doesn't use |
CWE-200 |
| OPTIONS returns full method enumeration |
LOW |
Allow:* or broad list |
CWE-200 |
Prerequisites
- Python 3.9+
- Authorization for non-local targets
Instructions
Step 1 — Confirm authorization
"Do you have authorization to perform HTTP method probing on this
target? I need confirmation before proceeding."
Step 2 — Run the scanner
python3 ${CLAUDE_PLUGIN_ROOT}/skills/probing-dangerous-http-methods/scripts/probe_methods.py \
https://example.com \
--authorized
Options:
Usage: probe_methods.py URL [OPTIONS]
Options:
--authorized Attest authorization (required)
--output FILE
--format FMT json | jsonl | markdown (default: markdown)
--min-severity SEV (default: info)
--timeout SECS
--is-api Treat URL as API endpoint (relaxes PUT/DELETE checks)
By default the scanner treats the URL as a non-API endpoint where PUT/DELET
Package an engagement's findings, scan outputs, evidence, and signed ROE into a timestamped archive with a SHA-256 manifest covering every file.
ReadBash(python3:*)Bash(tar:*)Bash(gpg:*)Glob
Recording Pentest Engagement
Overview
A penetration test produces a lot of artifacts: scan outputs in multiple formats, screenshots showing the state of vulnerable pages, raw tool logs (nmap, Burp, custom scripts), the ROE and any amendments, exec-summary docs, and the findings themselves. Six months after the engagement closes, a question arises — sometimes benign ("can you remind me what we found?"), sometimes adversarial ("the customer claims we accessed an out-of-scope system; show us the logs"). The answer needs to be: "yes, here's the archive, and here's cryptographic proof it hasn't been touched since the engagement closed."
This skill packages the engagement directory into a single timestamped archive with a SHA-256 manifest covering every file. Optionally signs the manifest with GPG. The result is a portable, self-verifying record of what the engagement produced.
The skill also surfaces inconsistencies that would weaken the chain-of-custody claim: out-of-tree paths referenced in findings (meaning the finding refers to a file not actually in the archive), files in the directory not listed in the manifest, manifest entries whose hashes don't match. These are HIGH findings — they mean the archive is incomplete or has been modified after the fact, neither of which is acceptable for evidence purposes.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| File in tree not in manifest |
HIGH |
Found during walk; manifest entry missing |
(evidence integrity) |
| Manifest entry hash mismatch |
CRITICAL |
Computed SHA-256 differs from manifest |
(evidence integrity) |
| Manifest entry without file |
HIGH |
Manifest lists a path that doesn't exist |
(evidence integrity) |
| Findings reference out-of-tree path |
MEDIUM |
A finding's evidence field points to a file not in the archive |
(evidence completeness) |
| Symlink in tree |
MEDIUM |
Symlinks break archive portability and integrity |
(evidence integrity) |
| Empty file in tree |
INFO |
0-byte file; possibly an export error |
(operational) |
| Archive package complete |
INFO |
All checks pass |
(positive confirmation) |
| Manifest signed |
INFO |
GPG signature present and valid form |
(positive confirmation) |
Prerequisites
- Python 3.9+
- An engagement directory laid out as recommended (see structure below)
- Opt
Scan a source-code tree for hardcoded credentials embedded in source files: AWS access keys, GitHub tokens, Stripe keys, Slack tokens, Anthropic API keys, OpenAI keys, JWT signing secrets, generic base64-encoded passwords, RSA / SSH private keys, and high-entropy string literals that pattern-match common credential shapes.
ReadBash(python3:*)GlobGrep
Scanning for Hardcoded Secrets
Overview
The single most common cause of credential breach in 2026 remains hardcoded secrets in source code. Engineers paste an API key into a config file "just for testing," forget to remove it, commit the file. The credential is now in the repository's history forever (git rebase doesn't help if anyone cloned in between) and extractable by anyone who reaches the repo: contractors, ex-employees, attackers via .git/ directory exposure (see skill six), GitHub bot scrapers crawling public repos.
The cost of detection-after-commit is near-zero (free tools exist: gitleaks, trufflehog, this skill). The cost of detection-before-commit is also near-zero (pre-commit hooks). The cost of remediation after the fact is rotating every credential exposed + auditing for exploitation + potentially notifying customers of breach. The asymmetry is severe, the discipline is the only constraint.
This skill scans a filesystem tree, matching against a canonical regex library covering the credential shapes attackers and bots search for first.
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| AWS access key (AKIA-prefix) |
CRITICAL |
Literal AKIA[0-9A-Z]{16} in any file |
CWE-798 |
| AWS secret access key |
CRITICAL |
40-char base64 in aws_secret_access_key field context |
CWE-798 |
| GitHub personal access token |
CRITICAL |
ghp_[A-Za-z0-9]{36} or gho_, ghu_, ghs_, ghr_ |
CWE-798 |
| GitHub app installation token |
CRITICAL |
ghs_[A-Za-z0-9]{36} |
CWE-798 |
| Stripe live key |
CRITICAL |
sk_live_[A-Za-z0-9]{24,} |
CWE-798 |
| Stripe test key |
MEDIUM |
sk_test_[A-Za-z0-9]{24,} |
CWE-798 |
| Anthropic API key |
CRITICAL |
sk-ant-api03-[A-Za-z0-9_-]{93} or similar |
CWE-798 |
| OpenAI API key |
CRITICAL |
sk-(proj-)?[A-Za-z0-9_-]{40,} |
CWE-798 |
| Slack bot token |
CRITICAL |
xoxb-[A-Za-z0-9-]+ |
CWE-798 |
| Slack user token |
CRITICAL |
xoxp-[A-Za-z0-9-]+ |
CWE-798 |
| Google API key |
HIGH |
AIza[A-Za-z0-9_-]{35} |
CWE-798 |
| RSA / OpenSSH private key |
Build a dependency-tree map of a project (npm or Python) and trace the path from each known-vulnerable transitive package back to one or more direct dependencies.
ReadBash(npm:*)Bash(pip:*)Bash(pip-audit:*)Bash(python3:*)Bash(pipdeptree:*)Glob
Tracing Transitive Vulnerabilities
Overview
The auditing-npm-dependencies and auditing-python-dependencies skills each surface CVEs, but they don't answer the question that actually decides remediation order: which of these findings can I clear by bumping ONE direct dep, and which require deeper intervention?
That question is the core of supply-chain triage. A high-CVSS CVE in lodash@4.17.4 is alarming on first read. If it's pulled in by five different direct deps, the right fix may not be to bump any of them — it may be a single root-level overrides entry pinning lodash@^4.17.21. The triage discussion goes very differently when you can quote: "this CVE is reachable via 5 paths, all of which flow through webpack, which has a fixed version available." That shifts a project-wide panic to a one-line PR.
This skill walks the project's dependency graph (via npm ls, pipdeptree, or equivalent), intersects it with the CVE findings already produced by the per-language audit skills, and emits a trace report that includes:
- For each CVE: the full path(s) from direct dep → ... → vulnerable package
- For each direct dep: the count of CVEs reachable through it
- "Highest-leverage upgrade" recommendation — the single direct-dep bump that clears the most findings at once
- "Unreachable" findings — CVEs whose vulnerable version range is forced by EVERY parent in the path, requiring overrides or vendor-patch
- "Deep transitive" findings (≥3 levels from a direct dep) — these are highest-risk for hidden surprises because the relationship to your code is most opaque
When the skill produces findings
| Finding |
Severity |
Threshold |
Affected control |
| Critical CVE at deep transitive depth (≥3) |
HIGH |
Depth ≥3 + severity CRITICAL — blast radius unclear |
CWE-1395 |
| High CVE reachable only via overrides |
HIGH |
No direct-dep version clears the finding |
CWE-1395 |
| Multi-CVE direct-dep hotspot |
MEDIUM |
Single direct dep is ancestor for ≥3 separate CVEs |
(informational) |
| Direct-dep bump clears N findings |
INFO |
Reports the recommended bump |
(operational) |
| Unreachable CVE (no fix in any reachable version) |
HIGH |
Finding has no fix-available across the whole graph |
CWE-1395 |
| Circular dep with CVE |
MEDIUM |
Cycle in the dep graph involves a vulnerable package |
(operational) |
Prerequisites
How It Works
Check security headers on a URL:
> Check the security headers on https://example.com
Audit project dependencies:
> Audit the dependencies in this project for vulnerabilities
Scan code for security issues:
> Scan this codebase for hardcoded secrets and security issues
Full security audit:
> Run a full security audit on this project
Ready to use penetration-tester?
|
|
|