· 5 min read ·

Security Context Files: The Missing Manual for AI Coding Assistants

Source: martinfowler

The Thoughtworks team building marketing applications discovered something that most developers working with AI assistants have noticed but few have systematized: AI agents confidently suggest insecure configurations, and the faster you build, the faster you accumulate security debt.

The pattern is consistent. Ask Claude or GitHub Copilot to spin up a database connection, and you’ll get hardcoded credentials. Request an API endpoint, and you’ll receive one without rate limiting. The AI optimizes for working code, not secure code. This creates a specific problem: the velocity gains from AI-assisted development get consumed by security remediation cycles.

Security context files offer a solution. Rather than treating AI code generation as a black box that occasionally produces vulnerabilities, you can provide explicit security requirements upfront. The implementation is straightforward, but the design choices matter.

What Goes in a Security Context File

A security context file lives in your repository, typically as .claude/security.md or SECURITY.md. The AI assistant reads it before generating code. The content needs to be specific enough to prevent common vulnerabilities while remaining general enough to apply across your codebase.

Here’s what belongs:

Authentication requirements. Specify your auth stack explicitly. If you use JWT tokens, document the signing algorithm, token expiry, and refresh token policy. If you use OAuth2, list the flows you support and the ones you explicitly reject. Include the libraries you’ve already vetted.

## Authentication
- Use `jsonwebtoken` v9.x for JWT signing (ES256 only, not HS256)
- Access tokens expire in 15 minutes
- Refresh tokens expire in 7 days
- Never store tokens in localStorage, use httpOnly cookies
- All auth endpoints require HTTPS in production

Database access patterns. Document your ORM, connection pooling strategy, and query parameterization requirements. Specify which database users have write access and which operations require transaction isolation.

## Database
- Use Prisma Client for all database queries
- Never construct raw SQL with string interpolation
- Read replicas for analytics queries (user: analytics_ro)
- Write operations use connection pool max 10
- Sensitive queries (payments, PII) require serializable isolation

Secrets management. This section prevents the most common AI-generated vulnerability: hardcoded credentials. List your secret management system and the exact pattern for accessing secrets.

## Secrets
- All secrets come from environment variables loaded via dotenv
- Never commit .env files (use .env.example as template)
- API keys use prefix convention: EXTERNAL_SERVICE_API_KEY
- Database URLs follow format: postgresql://user:pass@host:port/db
- Rotate credentials if accidentally committed

API security. Rate limiting, input validation, and CORS policies need explicit documentation. AI assistants will skip these unless you specify them.

## API Endpoints
- Rate limit: 100 req/min per IP (use express-rate-limit)
- Validate all inputs with Zod schemas
- CORS allows only whitelisted origins from ALLOWED_ORIGINS env var
- Log all authentication failures
- Return generic error messages (no stack traces in production)

The Permission Request Problem

AI assistants in autonomous modes will request file system access, network permissions, and shell command execution. Each request creates a decision point: does this assistant need this permission to complete the task, or is it overreaching?

The Thoughtworks team recommends caution. I’ve found a more specific heuristic works better: grant read permissions liberally, write permissions conservatively, and network permissions only with explicit review.

When Claude Code asks to run npm install, that’s a write to node_modules and a network call to the npm registry. Both are reasonable for dependency management. When it asks to curl an external API to fetch documentation, that’s a network call that might leak information about your codebase structure. Deny it and paste the documentation yourself.

The permission model in most AI coding assistants lacks granularity. You can’t easily say “yes to npm registry, no to arbitrary HTTP requests.” Until that changes, maintain a mental model of what each permission grants.

Daily Security Intelligence

Static security context files handle predictable vulnerabilities. New CVEs require active monitoring. The Thoughtworks approach suggests a daily security intelligence feed, which sounds like overhead until you’ve shipped a vulnerability that was announced two days prior.

The implementation can be lightweight. Subscribe to security advisories for your critical dependencies. For JavaScript projects, that means monitoring the npm Security Advisories feed. For Python, the PyPI advisory database. For Go, the Go vulnerability database.

Automate the check:

# Run daily in CI
npm audit --audit-level=high
# Or for more control
npm audit --json | jq '.vulnerabilities | to_entries[] | select(.value.severity == "high" or .value.severity == "critical")'

The output goes to your team chat. High and critical vulnerabilities get immediate attention. Moderate vulnerabilities get triaged weekly. Low vulnerabilities get reviewed quarterly.

More important than the tooling is the response process. When a vulnerability appears in a dependency you use, you need to answer three questions quickly: Are we using the vulnerable code path? Is there a patched version? Can we upgrade without breaking changes?

Document your answers. The next time a similar vulnerability appears, you’ll have a template for triage.

Secure-by-Default Harnesses

The most effective security intervention happens before the first line of code. Secure-by-default project templates eliminate entire classes of vulnerabilities by making the secure choice the default choice.

A secure harness for a Node.js API might include:

  • Express with Helmet.js pre-configured
  • Rate limiting middleware installed and enabled
  • CORS restricted to environment-specified origins
  • Request logging to structured JSON
  • Error handling that never leaks stack traces
  • Health check endpoints with no authentication (for load balancers)
  • Authentication middleware that validates JWT signatures
  • Input validation middleware using a schema library

The harness doesn’t prevent developers from making mistakes, but it raises the floor. The insecure configuration requires actively removing security features, which is more visible in code review than adding security features to insecure scaffolding.

For Discord bots specifically, a secure harness includes:

  • Token validation on startup
  • Intent declarations (request only the intents you need)
  • Command permission checks
  • Rate limiting per user and per guild
  • Audit logging for administrative commands
  • Graceful error handling that doesn’t expose internal state

When you ask an AI assistant to “add a new command,” it works within the harness constraints. The command inherits the permission system and rate limiting without explicit prompting.

What This Doesn’t Solve

Security context files guide AI assistants toward secure defaults. They don’t replace security review, penetration testing, or threat modeling. They handle the routine cases, the configurations that should always be secure, the credentials that should never be hardcoded.

Complex security requirements still need human judgment. Multi-tenancy isolation, cryptographic protocol selection, and access control policy design exceed what you can specify in a markdown file that an AI reads.

The value proposition is narrower: eliminate the most common AI-generated vulnerabilities so your security review can focus on the problems that actually require expertise. Stop spending time in code review pointing out hardcoded API keys and start spending time on authorization logic.

The Thoughtworks team found this approach effective for their marketing applications. I’ve found it effective for Discord bots and internal tools. The pattern generalizes to any project where you’re using AI assistance for implementation velocity.

Write the security context file once, update it when your security requirements change, and let the AI assistant handle the repetitive work of implementing those requirements correctly. The alternative is reviewing every AI-generated line for security issues, which defeats the purpose of using AI assistance in the first place.

Was this interesting?