0

Security Checklist

agent-skillsreferences

Quick reference for web application security. Use alongside the security-and-hardening skill.

Sign in to save downloads to your library and vote.

Preview

Security Checklist

Quick reference for web application security. Use alongside the security-and-hardening skill.

Table of Contents

Threat Modeling (Start Here)

Before reaching for controls, spend five minutes thinking like an attacker:

  • Trust boundaries mapped (requests, uploads, webhooks, third-party APIs, LLM output, and local values written by processes you don't control)
  • Assets named (credentials, PII, payment data, admin actions, money movement)
  • STRIDE run per boundary (Spoofing, Tampering, Repudiation, Info disclosure, DoS, Elevation)
  • Abuse cases written next to use cases ("how would I misuse this?")

Pre-Commit Checks

  • No secrets in code (git diff --cached | grep -i "password\|secret\|api_key\|token")
  • .gitignore covers: .env, .env.local, *.pem, *.key
  • .env.example uses placeholder values (not real secrets)

Authentication

  • Passwords hashed with bcrypt (≥12 rounds), scrypt, or argon2
  • Session cookies: httpOnly, secure, sameSite: 'lax'
  • Session expiration configured (reasonable max-age)
  • Rate limiting on login endpoint (≤10 attempts per 15 minutes)
  • Password reset tokens: time-limited (≤1 hour), single-use
  • Account lockout after repeated failures (optional, with notification)
  • MFA supported for sensitive operations (optional but recommended)

Authorization

  • Every protected endpoint checks authentication
  • Every resource access checks ownership/role (prevents IDOR)
  • Admin endpoints require admin role verification
  • API keys scoped to minimum necessary permissions
  • JWT tokens validated (signature, expiration, issuer)

Input Validation

  • All user input validated at system boundaries (API routes, form handlers)
  • Validation uses allowlists (not denylists)
  • String lengths constrained (min/max)
  • Numeric ranges validated
  • Email, URL, and date formats validated with proper libraries
  • File uploads: type restricted, size limited, content verified
  • SQL queries parameterized (no string concatenation)
  • HTML output encoded (use framework auto-escaping)
  • URLs validated before redirect (prevent open redirect)
  • Server-side URL fetches allowlisted; private/reserved IPs blocked (prevent SSRF)
  • Destructive path operations (delete/move/overwrite): symlinks resolved, allowlisted root, minimum depth, ownership evidence read before the call

Destructive Path Operations

Containment for a target named by data. Resolve first, then decide — and treat the result as a candidate, not as authorization:

import { realpath, readFile } from 'node:fs/promises';
import { resolve, relative, isAbsolute, join, sep } from 'node:path';

const ALLOWED_ROOTS = ['/var/lib/myapp/sessions']; // an allowlist, not a pattern
const MIN_DEPTH = 1;                               // so a root is never the target

async function resolveDeletable(candidate: string, expectedOwner: string) {
  const target = await realpath(resolve(candidate)); // symlinks resolved BEFORE the check
  const inRoot = ALLOWED_ROOTS.some((root) => {
    const rel = relative(root, target);
    // `rel === '..'` / `'../'` only — a plain `startsWith('..')` would also
    // reject a legitimate child named `..cache`.
    if (rel === '' || rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) return false;
    return rel.split(sep).length >= MIN_DEPTH;
  });
  if (!inRoot) throw new Error(`refusing: outside allowed roots (${target})`);

  const owner = await readFile(join(target, '.owner'), 'utf8').catch(() => null);
  if (owner?.trim() !== expectedOwner) throw new Error(`refusing: unproven owner (${target})`);
  return target;
}

What this does not do, and must be said where the snippet is copied from:

  • The marker is self-attestation. Anything that can write inside the root can write .owner. expectedOwner has to come from authenticated state, and the marker needs integrity protection (restrictive ownership, or a MAC) before it is authorization rather than a consistency check against a misderived target.
  • Returning a path leaves a check/use race. Where an untrusted process can swap an ancestor between the check and the call, operate on a descriptor with no-follow, beneath-the-root semantics, or guarantee the hierarchy is immutable for the duration.

Security Headers

Content-Security-Policy: default-src 'self'; script-src 'self'
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0  (disabled, rely on CSP)
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

CORS Configuration

// Restrictive (recommended)
cors({
  origin: ['https://yourdomain.com', 'https://app.yourdomain.com'],
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
})

// NEVER use in production:
cors({ origin: '*' })  // Allows any origin

Data Protection

  • Sensitive fields excluded from API responses (passwordHash, resetToken, etc.)
  • Sensitive data not logged (passwords, tokens, full CC numbers)
  • PII encrypted at rest (if required by regulation)
  • HTTPS for all external communication
  • Database backups encrypted
  • Personal data is classified, collected against a stated purpose, and minimized
  • Personal data has a retention limit and a working deletion path (incl. backups, caches, indexes)
  • Export/delete (data-subject) requests are supported where required; third-party sharing has consent and a data-processing agreement

Dependency Security

First locate the installation boundary. If the package is matched by a parent workspaces declaration, use that workspace root; otherwise use the nearest project root that owns both its manifest and dependency graph. At that boundary, corroborate packageManager (when present), the lockfile, and CI commands. Stop if they disagree or competing manager lockfiles exist there. A nested project is independent only when it is outside the parent workspace; independent subprojects may legitimately use different managers.

Manager/version signalFrozen/immutable CI installKnown-advisory audit
npm (package-lock.json or npm-shrinkwrap.json)npm cinpm audit
pnpmpnpm install --frozen-lockfilepnpm audit
Yarn 2+yarn install --immutableyarn npm audit -A -R
Yarn 1yarn install --frozen-lockfileyarn audit

For an unlisted manager or version, consult its official documentation; do not substitute another manager's commands or newer defaults.

Install-Script Gate

Never discover dependency lifecycle scripts by first executing an ordinary install on a client whose defaults have not been verified.

  1. Bootstrap with dependency scripts disabled, or with a documented default-deny policy plus fail-closed enforcement.
  2. Inspect the exact script source and package version before approval.
  3. Record the narrowest native allow/deny policy at the installation boundary and commit it.
  4. Run a clean frozen/immutable install with that policy and verify the required packages still build.

Point-in-time snapshot: Package-manager defaults and command names change quickly. Verify this matrix against the pinned client's current official documentation before relying on it.

Manager versionNative policy
npm without verified granular approvalsBootstrap with npm ci --ignore-scripts, or persist ignore-scripts=true when project-wide blocking is intended. Keep scripts disabled or deliberately upgrade before allowing any reviewed dependency script.
npm 11.18.x (verified on 11.18.0)Unreviewed dependency scripts run with a warning by default. Enforce strict-allow-scripts=true before a normal install, then use the workspace-unaware npm install-scripts ls from the installation boundary; keep approvals version-pinned and denials name-wide.
npm 12.x (verified on 12.0.1)Unreviewed dependency scripts are skipped by default; strict-allow-scripts=true makes their presence fail the install before execution. Use the same npm install-scripts review and approval flow.
pnpm 11+Use pnpm approve-builds and commit allowBuilds decisions; strictDepBuilds defaults to true, so unreviewed builds fail.
pnpm 10.26–10.xConfigure allowBuilds explicitly, or use pnpm approve-builds with the legacy onlyBuiltDependencies / ignoredBuiltDependencies lists. Set strictDepBuilds: true; its v10 default is false.
pnpm 10.1–10.25pnpm approve-builds records the legacy lists; enable strictDepBuilds where supported (10.3+).
Older or unknown pnpmBootstrap with pnpm install --frozen-lockfile --ignore-scripts. Keep scripts disabled unless the pinned version documents an enforceable policy.
Yarn 4.14+Dependency postinstalls are disabled by default. Grant only required exceptions with top-level dependenciesMeta.<package>.built: true.
Yarn 2–4.13Set enableScripts: false in .yarnrc.yml, then grant only required exceptions with top-level dependenciesMeta.<package>.built: true; do not enable scripts globally.
Yarn 1Bootstrap with yarn install --ignore-scripts; keep scripts disabled unless each required exception is reviewed under the pinned client's documented workflow.

Authoritative checks: npm install-scripts, install policy, and CLI releases; pnpm approve-builds and build settings; Yarn security and manifest.

Supply-chain hygiene (advisory audits do not catch newly malicious packages):

  • Exactly one authoritative lockfile per project/workspace root is committed and CI never rewrites it
  • Critical/high findings are triaged for reachability; deferrals have a reason and review date
  • Forced audit remediation (npm audit fix --force or equivalent) is never automatic; remediation diffs and changelogs are reviewed
  • Registry signatures/provenance are verified where the manager supports it
  • Dependency lifecycle scripts are blocked before first execution and approved only through the pinned manager's native policy
  • New dependencies are reviewed for ownership, maintenance, release age, provenance, transitive graph, and typosquatting

AI / LLM Security

For any feature that calls an LLM (chatbots, summarizers, agents, RAG):

  • Model output treated as untrusted — never into eval/SQL/shell/innerHTML/file paths
  • Prompt injection assumed; permissions enforced in code, not in the system prompt
  • Secrets, cross-tenant data, and full system prompts kept out of the context window
  • Tool/agent permissions scoped; destructive or irreversible actions require confirmation
  • Token, rate, and recursion/loop limits set (bound consumption)

Error Handling

// Production: generic error, no internals
res.status(500).json({
  error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' }
});

// NEVER in production:
res.status(500).json({
  error: err.message,
  stack: err.stack,         // Exposes internals
  query: err.sql,           // Exposes database details
});

OWASP Top 10 Quick Reference

#VulnerabilityPrevention
1Broken Access ControlAuth checks on every endpoint, ownership verification
2Cryptographic FailuresHTTPS, strong hashing, no secrets in code
3InjectionParameterized queries, input validation
4Insecure DesignThreat modeling, spec-driven development
5Security MisconfigurationSecurity headers, minimal permissions, audit deps
6Vulnerable ComponentsThe ecosystem's dependency audit (npm audit, pip-audit, ...), keep deps updated, minimal deps
7Auth FailuresStrong passwords, rate limiting, session management
8Data Integrity FailuresVerify updates/dependencies, signed artifacts
9Logging FailuresLog security events, don't log secrets
10SSRFValidate/allowlist URLs, restrict outbound requests

OWASP Top 10 for LLMs Quick Reference

For apps with LLM features. See the OWASP GenAI Security Project.

IDRiskPrevention
LLM01Prompt InjectionDon't trust the system prompt as a boundary; enforce permissions in code
LLM02Sensitive Information DisclosureKeep secrets/PII out of prompts; filter outputs
LLM03Supply ChainVet models, datasets, and plugins like any dependency
LLM04Data and Model PoisoningUse trusted model sources, verify integrity; vet fine-tuning and RAG data
LLM05Improper Output HandlingTreat model output as untrusted; validate, parameterize, encode
LLM06Excessive AgencyScope tool permissions; confirm destructive actions
LLM07System Prompt LeakageAssume the system prompt can leak; put no secrets in it
LLM08Vector and Embedding WeaknessesPartition RAG embeddings per tenant; validate documents before indexing
LLM09MisinformationGround answers with citations; validate critical claims; keep a human in the loop
LLM10Unbounded ConsumptionCap tokens, request rate, and loop/recursion depth

Ingestion metadata

Source catalog
agent-skills
Repository
addyosmani/agent-skills · main
File path
references/security-checklist.md
Last refreshed
10/24/2026, 3:00:12 AM (5h ago)
Refresh schedule
Daily · 03:00 UTC
Dedupe status
Unique · deduped by (source, url)