From skillry-security
Use when you need to review secrets, environment variables, auth, CORS, token handling, logging redaction, and unsafe changes.
How this skill is triggered — by the user, by Claude, or both
Slash command
/skillry-security:47-security-and-secrets-reviewThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Review secrets, environment variables, auth, CORS, token handling, logging redaction, and unsafe change surfaces. Every finding gets a severity (Critical to Low), a concrete fix, and — for any live exposed secret — a rotation flag, because a finding without a next action is not a finding. The review is evidence-driven: every claim points at a `file:line`, and no raw secret value ever appears in...
Review secrets, environment variables, auth, CORS, token handling, logging redaction, and unsafe change surfaces. Every finding gets a severity (Critical to Low), a concrete fix, and — for any live exposed secret — a rotation flag, because a finding without a next action is not a finding. The review is evidence-driven: every claim points at a file:line, and no raw secret value ever appears in the output.
.env / config files.49-authz-permission-review for permission logic, 50-dependency-supply-chain-review for CVEs, 51-env-config-hardening for config schema..env*, config files, and CI secret references, then confirm .gitignore excludes every env/secret file. Treat anything tracked by git as already public — being in .gitignore today does not undo a past commit.file:line; never paste the raw value — redact to first/last 4 characters.localStorage), how JWTs are signed and verified, the algorithm, expiry, and whether any dev fallback secret exists.HttpOnly, Secure, SameSite), and HTTPS assumptions.eval, dynamic child_process, and SQL/HTML built by string concatenation with user input..gitignore now excludes it..env, .env.local, *.pem, id_rsa, service-account JSON tracked by git.process.env.JWT_SECRET || 'dev', SECRET_KEY = "changeme", apiKey = "sk-..." literal.algorithms: ['none'], jwt.decode() used in place of jwt.verify(), missing expiresIn, symmetric secret shared across services.localStorage/sessionStorage instead of HttpOnly; Secure; SameSite cookies (XSS-readable).Access-Control-Allow-Origin: * together with Allow-Credentials: true; app.use(cors()) with no origin allowlist; reflected origin echoed without validation.HttpOnly, Secure, or SameSite; session cookie with no expiry.console.log(req.headers), logger.info(req.body), logging Authorization/Set-Cookie/password fields.eval(, exec(, child_process with template strings, SELECT ... ${userInput}, dangerouslySetInnerHTML with unsanitized input.AKIA[0-9A-Z]{16}, Google AIza[0-9A-Za-z_-]{35}, Stripe sk_live_[0-9A-Za-z]{24,}, GitHub ghp_[0-9A-Za-z]{36}, Slack xox[baprs]-, OpenAI sk-[A-Za-z0-9]{20,}, private-key header -----BEGIN ... PRIVATE KEY-----.http:// hardcoded for an API call that carries credentials; rejectUnauthorized: false or verify=False.NEXT_PUBLIC_-prefixed or VITE_-prefixed var holding a private key — that prefix makes it public).# Env/secret files that must never be tracked
git ls-files | grep -E '(^|/)\.env($|\.)|\.pem$|id_rsa|service-account.*\.json' || echo "clean"
# Confirm .gitignore actually excludes env files
git check-ignore -v .env .env.local 2>/dev/null || echo "WARNING: .env not ignored"
# High-signal provider-key scan (working tree; rg = ripgrep)
rg -nP '(AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|sk_live_[0-9A-Za-z]{24,}|ghp_[0-9A-Za-z]{36}|xox[baprs]-|sk-[A-Za-z0-9]{20,}|-----BEGIN [A-Z ]*PRIVATE KEY-----)' -g '!*.lock' -g '!node_modules' .
# Generic credential assignments
rg -niP '(password|passwd|secret|api[_-]?key|token)\s*[:=]\s*["'\''][^"'\'']{6,}' -g '!*.lock' .
# CORS / cookie / logging / sink smells
rg -n 'Access-Control-Allow-Origin.*\*|cors\(\)|localStorage\.(set|get)Item.*(token|jwt)|console\.log\(.*(req\.|token|secret)|dangerouslySetInnerHTML|verify=False|rejectUnauthorized:\s*false' .
# JWT handling
rg -nP "algorithms:\s*\[\s*['\"]none|jwt\.decode\(|jsonwebtoken" .
If rg is unavailable use grep -REn. For a known leaked string (with approval only) trace history via git log -p -S'<string>'. Findings table format:
| Severity | file:line | Issue | Evidence (redacted) | Fix |
|----------|------------------------|--------------------------------|---------------------|--------------------------------------|
| Critical | config/prod.env:12 | Stripe live key committed | sk_live…a1b2 | Remove, rotate key, move to vault |
| High | api/cors.ts:8 | `*` origin + credentials:true | — | Set explicit origin allowlist |
Assign severity consistently so the report is actionable, not just a list:
eval/SQL/shell. Blocks merge and triggers rotation.* or reflected origin, a token stored in localStorage, JWT verified with decode instead of verify, TLS validation disabled on a credentialed request. Fix before release.HttpOnly/Secure/SameSite, request body logged at info level, a non-production secret committed (test/sandbox key). Fix soon; not a release blocker on its own.A finding's severity is set by its real exploitability in this codebase, not by the abstract category. A committed sandbox Stripe key is Medium; a committed live key is Critical.
A grep hit at services/payments.ts:14:
const stripe = new Stripe(process.env.STRIPE_KEY || "sk_live_4eC39Hq...a1b2");
Report it as: severity Critical; evidence redacted to sk_live_4eC3…a1b2; issue "live Stripe secret hardcoded as a fallback default — present in git history". Fix: remove the literal, require STRIPE_KEY to be set (fail fast if unset), rotate the key in the Stripe dashboard, and confirm .env is gitignored going forward. The rotation is non-negotiable because the literal is already in every clone and in history.
.gitignore as remediation. Adding a secret to .gitignore after it was committed leaves it in history and on every clone. It must be rotated.abcd…wxyz.jwt.decode confused with jwt.verify. decode reads the payload without checking the signature — anyone can forge a token. Always verify.req.headers.origin back into Access-Control-Allow-Origin with credentials is equivalent to * plus credentials.logger.info(req) or console.log(req.headers) writes Authorization and cookies to log storage, which is rarely access-controlled.process.env.JWT_SECRET || 'dev-secret' silently signs production tokens with a known string if the env var is unset.verify=False / rejectUnauthorized: false. Disables TLS validation, enabling man-in-the-middle interception of credentials.token = line floods the report; scope by file type and exclude lockfiles and node_modules.If a secret is found in git history (not just the working tree), the working-tree fix is insufficient. The order of operations matters:
.env/config files are gitignored so it is not re-committed.git filter-repo, BFG) is disruptive — it rewrites commit SHAs and forces every collaborator to re-clone. Recommend it only with explicit approval, and only after rotation, because rewriting history without rotating leaves the live secret valid on existing clones.The report should state: the secret is rotated (or rotation is the blocking next action), the working tree is clean, and history rewrite is a separate, approval-gated step — never present a history rewrite as the primary fix.
Return a findings table — severity | file:line | issue | evidence (redacted) | fix. Severity order: Critical (live secret committed / auth bypass) > High > Medium > Low. End with three lists: secrets requiring rotation, env var names that must move out of code, and the next safe verification command. No unredacted secret value may appear anywhere in the output.
A clean scan is not proof of zero secrets — it is proof the patterns you ran did not match. State the limits in the report:
.env, a sealed secret) will not match plaintext patterns. Note them as "not covered" rather than "clean".Report the exact commands run and what they cover, so "clean" is honest and scoped, not an absolute claim.
abcd…wxyz; never print, echo, or commit a full secret value..gitignore / env changes unless the user approves.Done means the secret surface is mapped, the scan ran (or its absence is justified), every finding has file:line + severity + a concrete fix, exposed secrets are flagged for rotation with env-var names listed, and no raw secret value appears in the output.
Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub fluxonlab/skillry --plugin skillry-security