Universal security scanning skill for AI agents - finds hardcoded secrets, API keys, and vulnerabilities in any codebase. 44 patterns validated against GitLeaks, OWASP Top 10 mapping, Markdown/SARIF/JSON reports. Works across Claude Code, Cursor, Windsurf, and any agentic platfor
SaferSkills independently audited security-audit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Scan a codebase for hardcoded secrets, credentials, API keys, and common security vulnerabilities. Produce a severity-classified report with remediation guidance.
Throughout this skill, ${CLAUDE_PLUGIN_ROOT} refers to the directory where this plugin is installed. Claude Code sets this automatically when loading a plugin. If the variable is not available in your environment, resolve it as the parent directory of the skills/ folder containing this file (i.e., the repository root of the security-audit plugin).
This skill MUST NOT exfiltrate discovered secrets. All findings are for the user's eyes only — to help them remediate issues in their own codebase.
Rules that apply to ALL phases:
.tfstate, .pem, .key, .p12, .pfx, .jks, credentials.json, service-account*.json). For other files, scan line by line and discard each line's content from your response after checking it — never quote raw secret values in your output.Execute the following four phases in order. Adapt based on environment capabilities detected in Phase 1.
Run a single bash command to detect all environment capabilities at once:
echo "=== ENV ===" && echo "shell-ok" && (git --version 2>/dev/null || echo "git-missing") && (python3 --version 2>/dev/null || python --version 2>/dev/null || echo "python-missing") && echo "=== CONFIG ===" && (cat .security-audit.yml 2>/dev/null || echo "no-config")Parse the output to determine: shell access, git availability, Python command (python3 or python), and config. If .security-audit.yml is absent, use defaults:
fullnode_modules, venv, .git, dist, build, __pycache__, .next, vendor, target*.min.js, *.lock, *.mapDetermine scan mode:
git diff --name-only <base_branch>...HEAD to get changed filesIf shell access is available (primary path):
Run the Python scanner (preferred — faster, includes entropy detection). Combine scanner + tech stack detection into a single bash call to minimize tool call overhead:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/security-audit/scripts/scan-secrets.py" --target "<target_dir>" --patterns "${CLAUDE_PLUGIN_ROOT}/skills/security-audit/scripts/patterns.dat" --output "<output_file>" && echo "=== TECHSTACK ===" && ls -1 package.json requirements.txt pyproject.toml go.mod pom.xml build.gradle Gemfile Dockerfile docker-compose.yml 2>/dev/null; find . -maxdepth 2 -name '*.tf' -print -quit 2>/dev/nullUse python3 or python depending on what is available. For incremental mode, add --base-branch <branch>.
If Python is not available, fall back to the bash scanner:
bash "${CLAUDE_PLUGIN_ROOT}/skills/security-audit/scripts/scan-secrets.sh" "<target_dir>" "${CLAUDE_PLUGIN_ROOT}/skills/security-audit/scripts/patterns.dat" "<output_file>" && echo "=== TECHSTACK ===" && ls -1 package.json requirements.txt pyproject.toml go.mod pom.xml build.gradle Gemfile Dockerfile docker-compose.yml 2>/dev/null; find . -maxdepth 2 -name '*.tf' -print -quit 2>/dev/nullFor incremental mode, add --base-branch <branch>.
If no shell access (fallback path):
Read files directly and apply pattern knowledge from references/secret-patterns.md and references/vulnerability-patterns.md. Important: follow Secret Data Safety rules — skip files matching dangerous file patterns entirely (.tfstate, .pem, .key, .p12, .pfx, .jks, credentials.json, service-account*.json) and flag them as file-level findings without reading their contents. For other files, scan for:
When a secret is found, redact it immediately — never include the raw value in your output. Output findings in the same format: file, line number, redacted match, pattern name, base severity.
This phase runs automatically as part of the scanner scripts (both bash and Python). It detects files that should never be committed to git, regardless of their contents.
How it works: Uses git ls-files --cached to check only tracked files. Gitignored files are NOT flagged. Results are appended to the same output file as Phase 2 findings, using line: 0 to indicate file-level findings.
Detected file types: Terraform state (.tfstate), private keys (.pem, .key, .p12, .pfx), Java keystores (.jks), cloud credentials (credentials.json, service-account*.json), Terraform cache (.terraform/), database files (.sqlite, .db), Terraform output files, and files with "secret" in the name.
See references/secret-patterns.md § Dangerous File Types for the complete list with pattern IDs and descriptions.
If no shell access (fallback path):
Use git ls-files --cached knowledge to ask the user about tracked files matching dangerous patterns. Flag any matches with line: 0 and match: "(entire file)" in findings.
Performance guidelines for this phase:
references/secret-patterns.md, references/vulnerability-patterns.md, or references/severity-guide.md unless you encounter a finding you cannot classify from the information in this file. The key rules are summarized below.Read the redacted findings JSONL output file once (single Read tool call). Perform all analysis below from that single read — do not re-read the file. Follow Secret Data Safety rules — work from scanner output only, not original source files, for secret findings.
#### 3a. Tech Stack Detection
Tech stack detection was already run as part of the Phase 2 scanner command (after === TECHSTACK === in the output). Parse those results — do NOT run a separate command. Map results to remediation context:
package.json → Node.js (process.env, AWS Secrets Manager, GitHub Secrets)requirements.txt / pyproject.toml → Python (os.environ, python-dotenv, Vault)go.mod → Go (os.Getenv, Vault, cloud-native secrets)pom.xml / build.gradle → Java (Spring Vault, AWS Secrets Manager)Gemfile → Ruby (ENV[], dotenv, Rails credentials)*.tf → Terraform (remote state, variable files, Vault provider)Dockerfile / docker-compose.yml → containerized (Docker secrets, not build args)Use this context to tailor all remediation advice to the actual stack.
#### 3b. Cross-Finding Correlation
Group related findings before classifying:
#### 3c. Finding Triage
Process ALL findings in a single reasoning pass (not one tool call per finding). For each finding (or correlated group):
test/, tests/, example/, examples/, docs/, fixtures/ paths with placeholder valuesline: 0), check if file is in example/docs/test paths for potential downgrade.filter() is safe (parameterized); React JSX {expr} auto-escapes; Rails where(key: val) is safe; subprocess.run([list]) without shell=True is safe.env.development/.env.local/.env.testdeploy/, infra/, .github/workflows/), vendor-specific prefix match, production URL contextAKIA, ghp_, sk_live_, or unambiguous file type), Medium (generic pattern needing context), Low (entropy/heuristic match)#### 3d. Executive Summary
Before the detailed findings, produce a prioritized summary:
#### 3e. Augmented Findings
After triage, produce an augmented findings list by adding fields to each finding:
"confidence": "High", "Medium", or "Low""context": one sentence explaining the severity rating and confidence — e.g., "Live Stripe key in a production deploy script; no placeholder markers present.""commit" (optional, skip for speed unless user requests it): git commit hash and authorCommit attribution is expensive and optional. Only run it if the user explicitly requests blame/commit info. When enabled, batch all lookups into a single command instead of one per finding:
# Batch: extract blame for all finding locations at once
while IFS=: read -r file line; do
git log -1 --format="%h %ae %as" -L "${line},${line}:${file}" 2>/dev/null | head -1
done <<'LOCATIONS'
path/to/file1.py:42
path/to/file2.js:17
LOCATIONSThis augmented list is what gets passed to report generation.
If shell access is available (Python required):
python3 "${CLAUDE_PLUGIN_ROOT}/skills/security-audit/scripts/generate-report.py" "<findings_file>" "<format>" "<output_file>"Supported formats: markdown, sarif, json. Use - as output file for stdout.
If no shell access:
Format the report directly following this structure:
# Security Audit Report
**Scan date:** <date>
**Mode:** <full|incremental>
**Files scanned:** <count>
**Findings:** <N> Critical, <N> High, <N> Medium, <N> Low
---
## Critical (<count>)
### [C-001] <Pattern Name>
- **File:** `<path>:<line>`
- **Commit:** `<hash>` (<date>, <author>) [if git available]
- **Pattern:** <pattern name>
- **Match:** <redacted>
- **Confidence:** <High|Medium|Low>
- **Context:** <LLM assessment>
- **Remediation:** <actionable steps>Group findings by severity (Critical, High, Medium, Low). Assign sequential IDs per severity tier (C-001, H-001, M-001, L-001). Never echo full secret values — always use the redacted form from scanner output. For file-level findings (line: 0), display as <path>:0 (entire file) and include the file-type-specific remediation from references/secret-patterns.md.
End with scan metadata: excluded directories, pattern counts, config source.
The skill reads .security-audit.yml from the repo root. See examples/security-audit.yml for all supported options including:
scan.mode — full, incremental, or pathsexclude.directories / exclude.files — skip patternsseverity.minimum — filter output by minimum severityoutput.format — markdown, sarif, or jsoncustom_patterns.secrets — add custom regex patternscustom_patterns.allowlist — suppress known false positivesApplying configuration to script invocations:
exclude.directories → pass as --exclude-dirs (comma-separated) to both scanner scriptsscan.mode: incremental + scan.base_branch → pass as --base-branch to scanner scriptsoutput.format → pass as the <format> argument to generate-report.pyseverity.minimum — filter applied by the LLM during Phase 3; not a script flagexclude.patterns (glob patterns) — not supported by the scanner scripts; apply filtering manually before invoking the scripts, or skip files that match during Phase 3 analysiscustom_patterns.secrets — not passed to scanner scripts at runtime; to use custom patterns with the scripts, add them to scripts/patterns.dat in the format: <SEVERITY>\t<id>\t<name>\t<regex>custom_patterns.allowlist — applied by the LLM during Phase 3; check each finding's file path and matched value against the allowlist and suppress matching findingsConsult these for detailed patterns and classification rules:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.