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.
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