Skip to main content
AC
Quality & Security6.2 KBMIT licensed

security-and-hardening

Rewritten from patterns in addyosmani/agent-skills (MIT)

Threat-model and harden web application code. Use when handling user input, authentication, sessions, file uploads, webhooks, secrets, third-party integrations, or LLM features, and when reviewing code for injection, XSS, SSRF, or access control gaps. Not for launch readiness and rollout: use shipping-and-launch. Not for auditing third-party skill packages: use skill-security-auditor.

  • security
  • and
  • hardening

SKILL.md

Security and Hardening

Treat every external input as hostile, every secret as compromised the moment it leaks, and every authorization check as mandatory. Security is a constraint on each line that touches user data, not a phase.

Threat Model First (5 minutes, always)

Controls without a threat model are guesses. Before hardening:

  1. Map trust boundaries. Where does untrusted data enter? HTTP requests, forms, uploads, webhooks, third-party APIs, queues, and LLM output. Each boundary is attack surface.
  2. Name the assets. Credentials, PII, payment data, admin actions, money movement.
  3. Run STRIDE per boundary. Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege. One question each, one mitigation each.
  4. Write abuse cases next to use cases. "How would I misuse this?" becomes your first test.

If you cannot name the trust boundaries, you are not ready to secure the feature.

The Three Tiers

Always, no exceptions: validate all input at the boundary, parameterize every query, encode output (keep framework auto-escaping on), HTTPS everywhere, hash passwords with bcrypt/scrypt/argon2, set security headers (CSP, HSTS, X-Frame-Options), httpOnly + secure + sameSite session cookies, run npm audit (or equivalent) before release.

Ask first (human approval): new auth flows, new categories of sensitive data, new external integrations, CORS changes, file upload handlers, rate limit changes, elevated roles.

Never: secrets in version control, sensitive data in logs, client-side validation as a security boundary, eval() or innerHTML with user data, auth tokens in localStorage, stack traces exposed to users.

Read references/security-checklist.md for code patterns per vulnerability class (injection, XSS, access control, SSRF, uploads, rate limiting, headers) and the full pre-commit review checklist.

LLM Features Are Attack Surface

If the app calls a model:

  • Treat model output as untrusted input. Never pass it to eval, SQL, a shell, innerHTML, or a file path without validation and encoding.
  • The system prompt is not a security boundary. Enforce permissions in code. Any untrusted text in the context can carry injected instructions.
  • Keep secrets and cross-tenant data out of prompts; anything in context can be echoed back.
  • Scope tool permissions to minimum, confirm destructive actions, validate every tool argument.
  • Cap tokens, request rate, and recursion depth.
  • In RAG, partition embeddings per tenant and validate documents before indexing.

Dependency Triage

Critical or high finding that is reachable in production: fix now. Fix unavailable: workaround, replace, or allowlist with a review date, in that order. Moderate and reachable: next release. Dev-only or low: backlog with a date. Document every deferral.

npm audit misses malicious packages. Also: commit the lockfile, install with npm ci in CI, review new dependencies before adding (maintenance, downloads, postinstall scripts), watch for typosquats.

If a secret is ever committed, rotate it. Deleting the line is not enough. Revoke and reissue first, purge history second.

Verification

Run npm audit --audit-level=high. Expect exit code 0. If not, triage per the rules above before shipping.

Run git diff --cached | grep -iE "password|secret|api_key|token" before every commit touching config. Expect no matches on real values. If a real value matches, unstage, move it to the environment, and rotate it.

Hit a protected endpoint without a token and with another user's token. Expect 401 and 403 respectively. Any 200 is a broken access control bug, fix before anything else.

Good vs Bad

Bad: "It's an internal tool, I'll skip authorization checks and validate on the client." Internal tools get compromised and attackers pivot through the weakest link. Client validation is UX, not security.

Good: Same internal tool ships with parameterized queries, per-resource ownership checks, and secrets in the environment. Cost: an hour. Retrofit cost after a breach: weeks plus disclosure.

Footguns

  • Authenticated but not authorized. The endpoint checks login, not ownership. Fix: compare resource.ownerId to req.user.id on every mutating route.
  • SSRF via user-supplied URLs. Webhooks and "import from URL" aimed at cloud metadata or localhost. Fix: allowlist scheme and host, resolve DNS and reject non-unicast IPs, forbid redirects. Pattern in references/security-checklist.md.
  • Wildcard CORS with credentials. origin: '*' plus cookies leaks sessions. Fix: explicit origin list from the environment.
  • Secrets "removed" by rewriting history. The key already reached a remote or a fork. Fix: rotate first, always.

Red Flags: Stop and Re-check

  • "This is internal, security doesn't matter."
  • "We'll add security later."
  • "No one would try to exploit this."
  • "The framework handles it."
  • "It's just a prototype."
  • "It's just LLM output, it's only text."

Each phrase is a rationalization, not a risk assessment. There is no configuration of these excuses that makes skipping a boundary check acceptable. If a control genuinely does not apply, write down why, in the PR, before skipping it.

Completion Checklist

  • [ ] Trust boundaries and assets named for the feature
  • [ ] All input validated at the boundary, all queries parameterized
  • [ ] Authz (ownership/role) checked on every protected route, verified with a foreign-token request
  • [ ] No secrets in code, logs, or prompts; audit clean or deferrals documented
  • [ ] Security headers, cookie flags, and CORS origins verified in a real response
  • [ ] LLM output validated and encoded before use (if applicable)

Any box unchecked: not done. Fix or say so.

Reference files

More in Quality & Security

All skills