From marcoshack
Scan the codebase for leaked secrets, API security vulnerabilities, input validation issues, authentication bypass risks, sensitive data exposure, supply-chain and CI/CD weaknesses, container/infrastructure misconfiguration, and frontend token-handling issues. Use when user asks to check for secrets, scan for security issues before commit, audit sensitive data, review API security, or run a full security audit.
How this skill is triggered — by the user, by Claude, or both
Slash command
/marcoshack:security-scanThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Perform a security audit aligned with: OWASP API Security Top 10 (2023), OWASP Top 10 (2021), OWASP ASVS 4.0, OWASP Secure Headers, CWE, NIST SSDF (SP 800-218), SLSA, and CIS Docker Benchmark.
Perform a security audit aligned with: OWASP API Security Top 10 (2023), OWASP Top 10 (2021), OWASP ASVS 4.0, OWASP Secure Headers, CWE, NIST SSDF (SP 800-218), SLSA, and CIS Docker Benchmark.
Full scan (default for /security-scan) — run all applicable parts against the whole repository.
Staged/diff scan (--staged, pre-commit, or when invoked by the commit skill) — run Part 1 (secrets) against the diff, plus any other part whose subject files are touched by the diff (e.g. a change to a route file triggers Part 2 checks on that file; a Dockerfile change triggers Part 9 checks on it).
Diff-scan limitation — state this in the report. A diff scan checks "is this change safe", not "is the system safe". It cannot detect missing controls (absent authorization, absent rate limiting, absent dependency scanning, missing security headers) unless the diff touches the relevant file. When running a diff scan, end the report with: the last time a full scan should be/was run, and a recommendation to run
/security-scan(full) periodically — at minimum before releases and after auth/infra changes.
Do NOT limit the scan to API handler/service files. The full scan covers:
| Surface | Typical files |
|---|---|
| API backend | handlers, middleware, services, repositories, route registration |
| Frontend SPA | API clients, auth contexts/hooks, token storage, HTML-injection sites |
| Background workers / queue consumers | worker mains, dispatchers, task handlers (any language) |
| Real-time | SSE/WebSocket hubs, subscription handlers |
| Database | migrations, schema files (for auth-hot-path indexes, see 6.4) |
| Containers | Dockerfile*, docker-compose*.yml, .dockerignore |
| CI/CD | .github/workflows/*, GitLab CI, pipeline definitions |
| Edge / proxy | nginx/Caddy/Traefik configs, CDN config |
| Host / deploy | systemd units, install/deploy scripts, Makefiles, Terraform/K8s manifests |
Before scanning, detect the stack (languages, frameworks, infrastructure) from file extensions, manifests (go.mod, package.json, pyproject.toml, Cargo.toml, etc.), and config files. Then:
These four principles matter more than any individual grep pattern:
USER in a Dockerfile, no Dependabot config, no token revocation, no panic recovery in a worker pool. For each surface, ask "what control SHOULD exist here?" and verify it does. Grep finds presence; only deliberate inventory finds absence.add_header is NOT inherited into location blocks that have their own add_header). Apply each tool's actual semantics.(See checklist.md in this skill directory for the full pattern checklist.)
git ls-files # tracked files
git ls-files --others --exclude-standard # untracked, not ignored
git status --porcelain
Verify sensitive files are excluded: .env files, credential files (.pem, .key, .p12, .pfx, .jks), private keys, build artifacts, database dumps.
API keys and tokens: provider-named vars (ANTHROPIC_API_KEY, AWS_SECRET_ACCESS_KEY, STRIPE_SECRET_KEY, …), generic api[_-]?key.*=.*[a-zA-Z0-9]{20,}, Bearer tokens, long base64 strings assigned to secret/key/token variables.
Credentials: DB connection strings with embedded passwords, OAuth client secrets, JWT/HMAC signing secrets, basic auth.
Cryptographic material: private keys (-----BEGIN .* PRIVATE KEY-----), TLS certs, URLs with embedded credentials (scheme://user:pass@host).
All sensitive config from env vars (Go os.Getenv/viper, JS process.env, Python os.environ); Docker Compose uses ${VAR} substitution; secrets are mandatory with fail-fast validation, not insecure defaults.
git diff --cached
git ls-files | xargs grep -iE "(api[_-]?key|password|secret|token|webhook).*[=:].*['\"]?[a-zA-Z0-9]{20,}"
echo generated passwords or tokens to stdout (lands in scrollback, CI logs, screen shares) — print "written to .env" insteadARGs (persist in image history)Check for:
userID == resource.OwnerID, namespace membership, role checks)event:{id}, user:{id}), any authenticated user can subscribe to other tenants' streams. Verify the hub/subscription handler consults the caller's auth info per topic, not just per connection.Referrer-Policy: no-referrer on share responses.Check for:
exp, alg: none, missing algorithm allow-list (algorithm-confusion guard), missing audience/issuer validationIsActive per request but JWTs don't, flag the asymmetry. Look for tokens_valid_after / jti denylist; check token TTL (24h+ access tokens without refresh = finding); check whether a refresh endpoint exists but is unused by the client.state must be a per-session random nonce stored client-side (Secure; HttpOnly; SameSite cookie) and exact-matched at callback. An HMAC-of-timestamp state that any callback can replay within a window defeats CSRF protection → login CSRF / code injection.X-Forwarded-For / X-Real-IP, verify those headers are only honored from configured trusted proxy IPs. Attacker-rotated headers = fresh bucket per request = full bypass of login/reset throttling.Check for:
json.Decode into structs with role/is_admin; JS {...req.body} into updates)password_hash, internal_id, storage_key); missing DTOs; missing json:"-" on sensitive Go fieldsCheck for:
Check for:
Check for:
Check for:
http.Get()/fetch()/axios() without domain allow-listing127.0.0.1, 169.254.169.254, RFC1918, ::1, fd00::/8); redirect following on user URLs; image/file fetching from user-provided URLsCheck for:
* in production; missing security headers (verify they are actually served, see 9.3)InsecureSkipVerify: true / NODE_TLS_REJECT_UNAUTHORIZED=0Content-Type instead of a fixed type (e.g. avatar served with content-type from object-store metadata — hard-code it)0.0.0.0 instead of the container/internal network; endpoints answering on any path/request lineCheck for:
/debug/pprof, /_internal/); deprecated API versions without deprecation headers; undocumented routes; auth differences between versionsCheck for:
fmt.Sprintf in queries with user input (note: Sprintf building only $N placeholder indices is safe — read before flagging)ORDER BY/WHERE from user input without allow-listing; ORM raw-query methods with unescaped inputfmt.Sprintf("SELECT|INSERT|UPDATE|DELETE, "WHERE " +, .Raw(/.Exec( with concatenationexec.Command()/child_process.exec()/subprocess with user-supplied arguments; shell string interpolationdangerouslySetInnerHTML without sanitization (note mitigations like mermaid securityLevel: 'strict' but flag fragility)href without a scheme allow-list (javascript: URIs bypass react-markdown defaults when components are overridden); missing rel="noopener noreferrer"Content-Type on API responsesfilepath.Clean()/containment; file-serving endpoints without root confinement"Subject: " + subject + "\r\n") where the subject/recipient/from-name includes user- or admin-controlled values (event names, display names). Newlines inject Bcc: headers or body content. Verify the input validation layer rejects control characters, or the mail layer strips \r\n.Location)X-Powered-By/Server disclosureEventSource/SSE since it can't set headers, and with download/share links): they land in proxy/access logs, browser history, and Referer headersmath/rand (Go) / Math.random() (JS) / random (Python) for security values instead of CSPRNGnone, short-secret HS256); missing HTTPS enforcementCrash- and resource-safety of async surfaces is a security property: one poison message or one panic should never take down the service.
-race tests is a finding in itself.recover()/try-except — one bad task crashes the whole worker (compare: does the HTTP path have recovery middleware while the worker path has none?)AckWait, ack_wait, visibility timeout) shorter than worst-case task runtime → redelivery and duplicate execution; long tasks should heartbeat (in-progress acks)asyncio.to_thread)WHERE key_hash = $1 with no index = sequential scan per requestUPDATE last_used_at = now()) — suggest throttling/batchingApplicable when the repo contains a browser client (React/Vue/Svelte/plain JS).
localStorage/sessionStorage = exfiltratable by any XSS. Prefer httpOnly; Secure; SameSite cookies + CSRF token. If localStorage is kept, verify short TTL + refresh-token rotation is actually wired up (an unused refresh() helper is a finding).?token=, &access_token=, EventSource URLsdangerouslySetInnerHTML, href schemes); also window.open without noopener, postMessage handlers without origin checks/ and not //)eval, server-validated OAuth state, JSX auto-escaping relied on (no string HTML assembly)These are absence checks — grep proves nothing exists; verify each control:
.github/dependabot.yml or renovate.json) covering ALL ecosystems present (gomod, npm — each workspace, pip/uv, docker, github-actions)go.sum, package-lock.json, uv.lock/poetry.lock) and CI installs with frozen/ci mode (npm ci, uv sync --frozen, go mod download from committed sum)govulncheck, npm audit gate, pip-audit) — grep workflows for trivy|grype|codeql|govulncheck|snyk; zero hits = findinganchore/sbom-action, docker buildx --sbom)uses: foo/bar@v1 can be repointed to malicious code running with GITHUB_TOKEN). Prioritize low-reputation actions with high-privilege tokens (packages: write, contents: write).permissions: blocks set to least privilege; no pull_request_target with checkout of untrusted codeUSER directive — final stage must run as non-root (static Go binaries never need root; use nginxinc/nginx-unprivileged for nginx)image@sha256:...), no :latest anywhere (Dockerfiles AND compose files).dockerignore exists for every build context — check what the context actually is in compose/build commands; a repo-root context without .dockerignore ships .env, .git/, and data dirs to the daemon, one COPY mistake from a leaked layerHEALTHCHECK in images (and compose healthchecks for every service, including workers)ENV/ARG/layerscap_drop: [ALL], security_opt: [no-new-privileges:true], read_only: true where feasibleX-Content-Type-Options, X-Frame-Options/frame-ancestors, Referrer-Policy, server_tokens offadd_header directives are NOT inherited into location blocks that declare their own add_header — headers defined only at server level are silently dropped on proxied/static responses. Verify headers apply to every location (use an included snippet). Check with the proxy's real semantics, not by header presence in the file.limit_req_zone/limit_conn) on API locations, with burst allowance for SSE/streamingUser=, NoNewPrivileges=, ProtectSystem=, PrivateTmp=; no placeholder paths left unfilled## Security Scan Complete - [SAFE/UNSAFE]
### Scan Scope
- Mode: full / staged-diff (if diff: note limitation + recommend periodic full scan)
- Stack detected: <e.g. Go API, React SPA, Python worker, Docker/nginx, GitHub Actions>
- Files scanned: N
- Not applicable (skipped): <section — reason>
---
### Critical Findings
(Must fix before commit/deploy)
| # | Category | File | Finding | Severity | CWE/OWASP/Framework |
|---|----------|------|---------|----------|---------------------|
| 1 | Auth Bypass | [handler/foo.go:42](handler/foo.go#L42) | Endpoint missing auth middleware (verified) | Critical | API2:2023 |
### High Findings
(Should fix soon)
### Medium / Low Findings
(Address in next iteration)
---
### Secrets Audit
- .env ignored: YES/NO
- Hardcoded secrets found: YES/NO
- Environment variables used correctly: YES/NO
- Scripts/CI leak secrets to output: YES/NO
### Control Inventory
- [ ] Object/topic-level authorization incl. real-time (API1)
- [ ] Authentication, OAuth state, revocation, trusted proxies (API2)
- [ ] Property-level authorization (API3)
- [ ] Resource limits: API + workers + connections (API4)
- [ ] Function-level authorization (API5)
- [ ] Business flow abuse (API6)
- [ ] SSRF protections (API7)
- [ ] Security configuration + headers actually served (API8)
- [ ] API inventory (API9)
- [ ] Third-party API safety (API10)
- [ ] CRLF/header injection (email, HTTP, logs)
- [ ] No tokens in URLs
- [ ] Worker crash-safety, ack deadlines, poison messages
- [ ] Frontend token storage + XSS surface
- [ ] Dependency updates + CVE/secret scanning + SBOM in CI
- [ ] CI actions SHA-pinned, least-privilege tokens
- [ ] Containers non-root, images digest-pinned, .dockerignore
- [ ] Proxy: CSP/HSTS served on all locations, proxy rate limits
---
**Status**: [SAFE/UNSAFE] — N critical, N high, N medium, N low findings
| Severity | Criteria |
|---|---|
| Critical | Direct exploitation possible: auth bypass, SQL injection, hardcoded secrets, RCE |
| High | Significant risk with some conditions: IDOR/topic BOLA, mass assignment, SSRF, rate-limit bypass via spoofed headers, OAuth state replay, weak crypto for auth, mutable-tag actions with write tokens, worker crash on poison input |
| Medium | Moderate risk or defense-in-depth gap: missing rate limits, no revocation, verbose errors, missing headers, root containers, no dependency scanning, tokens in URLs |
| Low | Best practice violation, minimal direct risk: info disclosure in headers, missing CSP on low-risk app, missing healthchecks, image tag pinning |
/security-scan # full scan, all applicable parts
/security-scan src/ # specific directory
/security-scan --staged # staged files (pre-commit; states diff limitation)
/security-scan --api # Parts 2-5 only
/security-scan --infra # Parts 8-9 only (supply chain + containers/proxy)
[file.go:42](file.go#L42)npx claudepluginhub marcoshack/agent-skills --plugin marcoshackProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.