security
When to use
Use when implementing authentication, authorization, or any security-sensitive functionality.
Do NOT use when:
- Validation logic only — route to the project's validation carve-out (
laravel-validation for Laravel; otherwise the framework-native primitive — Zod / class-validator, Pydantic, struct-tag validators).
- Full security audit — route to
security-audit.
- You need a pre-implementation threat model — route to
threat-modeling.
- You need end-to-end authorization analysis — route to
authz-review.
Stack-specific carve-outs
The procedure below is stack-agnostic. For framework-specific primitives (Laravel Policies / Gates / FormRequests, Symfony voters, NestJS guards, Next.js middleware), defer to:
Procedure: Implement security for a feature (stack-neutral)
Step 0: Inspect
- Read the project's auth doc (
agents/authentication.md, docs/auth.md, or framework docs).
- Read the project's authorization doc (gates / policies / voters / guards).
- Locate existing authorization rules in the project's idiomatic location (Laravel
app/Policies/, Symfony src/Security/Voter/, NestJS *.guard.ts).
Step 1: Authentication
- Identify the auth mechanism in use (session, JWT, OAuth, API token) — read the framework's auth config (
config/auth.php, next-auth.config.ts, Symfony security.yaml, FastAPI dependency).
- Check guard / strategy / provider configuration.
- Multi-tenant identification happens after authentication — see
multi-tenancy.
Step 2: Authorization
- Create / locate the authz rule in the framework's idiomatic primitive (Policy, voter, guard, middleware, route dependency).
- Apply it at the request boundary (FormRequest
authorize(), controller / route-handler dependency, middleware chain).
- Cover non-model gates (cross-aggregate rules) — keep them centralised, not scattered across handlers.
Step 3: Review for adversarial
For security-sensitive changes, run adversarial-review.
Focus on: attack surface, trusting user input, authorization gaps.
Conventions
→ For PHP / Laravel specifics (auth helpers, mass assignment, Blade escaping, CSRF middleware): see guideline docs/guidelines/php/security.md.
→ For other stacks, follow the framework's hardening guide and the carve-outs above.
Crypto, password storage, certificates — route, do not guess
This skill carries no cryptographic parameter, key size, work factor, cipher
suite, or TLS version floor: such a value is authoritative-looking long after it stops
being true. Take it from https://cheatsheetseries.owasp.org/ at the moment you
need it — never from memory, never from this file. Rationale and reopening
condition: ADR-238.
Validate
- Verify all user input is validated at the boundary via the framework's primitive — never trust raw request data.
- Confirm an authorization check exists for every state-changing action.
- Check that no raw user input reaches SQL, HTML output, shell commands, or template renderers without escaping.
- Run the project's type-checker — must pass (catches type-safety issues that enable injection).
Output format
- Security-hardened code with auth, input validation at the boundary, and output encoding.
- Authorization rule (Policy / voter / guard / middleware) co-located with the route.
Gotcha
- Validation ensures format, not intent — don't trust input after validation alone.
- "Throw" vs "boolean" authz APIs behave differently (
Gate::authorize() throws vs Gate::allows() returns bool in Laravel; CanActivate in NestJS throws; FastAPI dependencies throw HTTPException). Pick based on how the framework expects failure to surface.
- Rate-limit ALL public endpoints, not just login.
- Never log passwords, tokens, or API keys.
Do NOT
- Do NOT bypass the framework's request-validation primitive inside handlers.
- Do NOT bulk-bind raw request payloads to ORM entities without an explicit allow-list (
$fillable / $guarded, DTO mapping, Pydantic model).
- Do NOT store plaintext passwords or secrets in the database.
- Do NOT expose internal error details in production API responses.
Auto-trigger keywords
- security
- authentication
- authorization
- CSRF
- XSS
- policy
1---2name: security3description: Use when applying security best practices — authentication, authorization, CSRF protection, input sanitization, rate limiting, or secure coding — stack-agnostic.4---56# security78## When to use910Use when implementing authentication, authorization, or any security-sensitive functionality.1112Do NOT use when:1314* Validation logic only — route to the project's validation carve-out ([`laravel-validation`](../laravel-validation/SKILL.md) for Laravel; otherwise the framework-native primitive — Zod / class-validator, Pydantic, struct-tag validators).15* Full security audit — route to [`security-audit`](../security-audit/SKILL.md).16* You need a pre-implementation threat model — route to [`threat-modeling`](../threat-modeling/SKILL.md).17* You need end-to-end authorization analysis — route to [`authz-review`](../authz-review/SKILL.md).1819## Stack-specific carve-outs2021The procedure below is stack-agnostic. For framework-specific primitives (Laravel Policies / Gates / FormRequests, Symfony voters, NestJS guards, Next.js middleware), defer to:2223| Stack | Carve-out |24|---|---|25| Laravel | [`laravel`](../laravel/SKILL.md), [`laravel-validation`](../laravel-validation/SKILL.md), [`laravel-middleware`](../laravel-middleware/SKILL.md) |26| Symfony | [`symfony-workflow`](../symfony-workflow/SKILL.md) |27| Next.js / TS | [`nextjs-patterns`](../nextjs-patterns/SKILL.md) |2829## Procedure: Implement security for a feature (stack-neutral)3031### Step 0: Inspect32331. Read the project's auth doc (`agents/authentication.md`, `docs/auth.md`, or framework docs).342. Read the project's authorization doc (gates / policies / voters / guards).353. Locate existing authorization rules in the project's idiomatic location (Laravel `app/Policies/`, Symfony `src/Security/Voter/`, NestJS `*.guard.ts`).3637### Step 1: Authentication3839- Identify the auth mechanism in use (session, JWT, OAuth, API token) — read the framework's auth config (`config/auth.php`, `next-auth.config.ts`, Symfony `security.yaml`, FastAPI dependency).40- Check guard / strategy / provider configuration.41- Multi-tenant identification happens **after** authentication — see [`multi-tenancy`](../multi-tenancy/SKILL.md).4243### Step 2: Authorization44451. Create / locate the authz rule in the framework's idiomatic primitive (Policy, voter, guard, middleware, route dependency).462. Apply it at the request boundary (FormRequest `authorize()`, controller / route-handler dependency, middleware chain).473. Cover non-model gates (cross-aggregate rules) — keep them centralised, not scattered across handlers.4849### Step 3: Review for adversarial5051For security-sensitive changes, run [`adversarial-review`](../adversarial-review/SKILL.md).52Focus on: attack surface, trusting user input, authorization gaps.5354## Conventions5556→ For PHP / Laravel specifics (auth helpers, mass assignment, Blade escaping, CSRF middleware): see guideline `docs/guidelines/php/security.md`.57→ For other stacks, follow the framework's hardening guide and the carve-outs above.5859### Crypto, password storage, certificates — route, do not guess6061This skill carries no cryptographic parameter, key size, work factor, cipher62suite, or TLS version floor: such a value is authoritative-looking long after it stops63being true. Take it from <https://cheatsheetseries.owasp.org/> at the moment you64need it — never from memory, never from this file. Rationale and reopening65condition: [ADR-238](../../../docs/decisions/ADR-238-security-content-routes-to-external-authority.md).6667### Validate6869- Verify all user input is validated at the boundary via the framework's primitive — never trust raw request data.70- Confirm an authorization check exists for every state-changing action.71- Check that no raw user input reaches SQL, HTML output, shell commands, or template renderers without escaping.72- Run the project's type-checker — must pass (catches type-safety issues that enable injection).7374## Output format75761. Security-hardened code with auth, input validation at the boundary, and output encoding.772. Authorization rule (Policy / voter / guard / middleware) co-located with the route.7879## Gotcha8081- Validation ensures format, not intent — don't trust input after validation alone.82- "Throw" vs "boolean" authz APIs behave differently (`Gate::authorize()` throws vs `Gate::allows()` returns bool in Laravel; `CanActivate` in NestJS throws; FastAPI dependencies throw `HTTPException`). Pick based on how the framework expects failure to surface.83- Rate-limit ALL public endpoints, not just login.84- Never log passwords, tokens, or API keys.8586## Do NOT8788- Do NOT bypass the framework's request-validation primitive inside handlers.89- Do NOT bulk-bind raw request payloads to ORM entities without an explicit allow-list (`$fillable` / `$guarded`, DTO mapping, Pydantic model).90- Do NOT store plaintext passwords or secrets in the database.91- Do NOT expose internal error details in production API responses.9293## Auto-trigger keywords9495- security96- authentication97- authorization98- CSRF99- XSS100- policy