Security — Web Security for Modern Frontends
1. Philosophy
- Defense in depth — Multiple layers: headers, CSP, validation, framework protections.
- Secure by default — Frameworks protect you; don't disable protections.
- Minimal attack surface — Least privilege, minimal dependencies, no secrets in code.
- Detect and respond — Logging, monitoring, incident response plan.
- Supply chain integrity — Pin dependencies, verify provenance, audit regularly.
2. Threat Model (OWASP 2025 Essential)
| Risk |
Mitigation |
| A01: Broken Access Control |
Server-side auth checks, RBAC, deny by default |
| A02: Cryptographic Failures |
TLS 1.3, Argon2id, RS256, key rotation |
| A03: Injection |
Parameterized queries, input validation, ORM |
| A04: Insecure Design |
Threat modeling, secure patterns, code review |
| A05: Security Misconfiguration |
Hardened headers, CSP, no defaults |
| A06: Vulnerable Components |
Dependency scanning, pinning, updates |
| A07: Auth Failures |
MFA, rotation, reuse detection, rate limits |
| A08: Software Integrity |
SRI, signed commits, CI verification |
| A09: Logging Failures |
Structured logs, alerting, no secrets |
| A10: SSRF |
Allowlist URLs, no user-controlled fetch |
3. HTTPS & HSTS
# TLS 1.3 only
ssl_protocols TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# HSTS (1 year, include subdomains, preload)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Rules
- TLS 1.3 only — disable 1.2 and below
- HSTS with preload — submit to hstspreload.org
- Certificates — 90-day rotation (Let's Encrypt), monitor expiry
- No mixed content — all resources HTTPS
4. Security Headers
| Header |
Value |
Purpose |
Content-Security-Policy |
See §5 |
XSS, injection prevention |
X-Frame-Options |
DENY |
Clickjacking |
X-Content-Type-Options |
nosniff |
MIME sniffing |
Referrer-Policy |
strict-origin-when-cross-origin |
Referrer leakage |
Permissions-Policy |
camera=(), microphone=(), geolocation=() |
Feature control |
Cross-Origin-Opener-Policy |
same-origin |
COOP |
Cross-Origin-Resource-Policy |
same-origin |
CORP |
Cross-Origin-Embedder-Policy |
require-corp |
COEP |
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
5. Content Security Policy (CSP)
Essential policy
Content-Security-Policy:
default-src 'self';
script-src 'self' 'wasm-unsafe-eval'; # wasm for some libs
style-src 'self' 'unsafe-inline'; # Tailwind/inline styles
img-src 'self' data: https:; # images + data URIs
font-src 'self' data:; # fonts
connect-src 'self' https://api.example.com; # API + websockets
frame-src 'none'; # no iframes
object-src 'none'; # no plugins
base-uri 'self'; # base tag
form-action 'self'; # form targets
frame-ancestors 'none'; # embedding
block-all-mixed-content; # HTTPS only
upgrade-insecure-requests; # upgrade HTTP
Rules CSP
- Report-only first —
Content-Security-Policy-Report-Only to test
- Nonce for inline scripts —
script-src 'nonce-<random>'
- No
unsafe-eval — except WASM if needed
- Report URI —
report-uri /csp-report for monitoring
6. XSS Prevention
Server-side
// Escape for HTML context
import { escapeHtml } from "escape-html";
const safe = escapeHtml(userInput);
// Sanitize HTML (if user HTML allowed)
import DOMPurify from "isomorphic-dompurify";
const clean = DOMPurify.sanitize(dirtyHtml, { ALLOWED_TAGS: ["b", "i", "em"] });
Client-side
// React: auto-escapes by default
<div>{userContent}</div> // ✅ Safe
// Dangerous - avoid
<div dangerouslySetInnerHTML={{ __html: userContent }} /> // ❌ Unless sanitized
Rules XSS Prevention
- Framework auto-escaping — React, Vue, Svelte escape by default
- Never
dangerouslySetInnerHTML without DOMPurify
- CSP as backup — blocks injected scripts
Content-Type: application/json — prevents sniffing
7. CSRF Prevention
Auth integration: see auth skill.
SameSite cookies (primary)
Set-Cookie: session=...; SameSite=Lax; Secure; HttpOnly
Set-Cookie: csrf_token=...; SameSite=Strict; Secure; HttpOnly
Double-submit (fallback)
// Generate
const csrfToken = crypto.randomBytes(32).toString("hex");
res.cookie("csrf_token", csrfToken, {
sameSite: "strict",
secure: true,
httpOnly: true,
});
// Validate (non-GET)
if (req.method !== "GET") {
const headerToken = req.headers["x-csrf-token"];
const cookieToken = req.cookies.csrf_token;
if (!headerToken || headerToken !== cookieToken) {
throw new Error("CSRF token mismatch");
}
}
Rules CSRF
SameSite=Lax for session — allows navigation
SameSite=Strict for CSRF token — no cross-site send
- Custom header —
X-CSRF-Token for SPA mutations
- Exempt GET/HEAD — safe methods
8. Clickjacking
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none';
Rules Clickjacking
X-Frame-Options: DENY — no embedding
- CSP
frame-ancestors 'none' — modern replacement
- Allow specific only if business requires:
frame-ancestors https://partner.example.com
9. CORS
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Idempotency-Key, X-CSRF-Token
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
Rules CORS
- Exact origin — no
* with credentials
- Preflight cache —
Max-Age: 86400
- Allow only needed headers —
Authorization, Content-Type, Idempotency-Key, X-CSRF-Token
10. Input Validation
import { z } from "zod";
const schema = z.object({
email: z.string().email(),
age: z.number().int().min(13).max(120),
tags: z.array(z.string().max(50)).max(10),
metadata: z.record(z.string()).optional(),
});
// Validate at boundary
const result = schema.safeParse(input);
if (!result.success) {
throw new ValidationError(result.error.flatten());
}
Rules Input Validation
- Validate at boundaries — API entry, form submit
- Schema-based — Zod, Valibot, ArkType
- Reject unknown —
strict() mode
- Sanitize, don't just validate — HTML, SQL, paths
11. Injection Prevention
SQL (parameterized)
// ✅ Safe
const users = await db.query("SELECT * FROM users WHERE email = $1", [email]);
// ❌ Unsafe
const users = await db.query(`SELECT * FROM users WHERE email = '${email}'`);
NoSQL (whitelist operators)
// ✅ Safe
const users = await db.users.find({ email: { $eq: email } });
// ❌ Unsafe
const users = await db.users.find({ $where: `this.email == '${email}'` });
Path traversal
// ✅ Safe
const safePath = path.resolve(baseDir, userInput);
if (!safePath.startsWith(baseDir)) throw new Error("Invalid path");
// ❌ Unsafe
fs.readFile(`/var/www/${userInput}`);
Rules Injections Prevention
- Always parameterized — never string concat
- ORM preferred — Prisma, Drizzle, TypeORM
- Whitelist operators — reject
$where, $expr
- Path resolution —
path.resolve + prefix check
12. Auth & Secrets Security
Auth patterns: see auth skill.
Secrets management
# Never in code
# .env.local (gitignored)
JWT_SECRET=...
DATABASE_URL=...
# Production: secret manager
# AWS Secrets Manager, GCP Secret Manager, Vault
Rules Auth & Secrets Security
- No secrets in repo —
.env* in .gitignore
- Secret manager in prod — rotation, audit, access control
- Different secrets per env — dev/staging/prod
- Rotate on leak — immediate, automated
13. Rate Limiting
Rate limiting patterns: see api-design skill.
Layers
| Layer |
Limit |
Scope |
| Edge (CDN) |
1000/min/IP |
DDoS |
| API Gateway |
100/min/user |
Abuse |
| Auth endpoints |
5/min/IP |
Brute force |
| Auth mutations |
10/min/user |
Credential stuffing |
14. Supply Chain Security
// package.json
{
"dependencies": {
"lodash": "4.17.21" // pinned, not ^
}
}
# Audit
pnpm audit --prod
pnpm audit --audit-level high
# Provenance
pnpm install --frozen-lockfile
Rules Supply Chain Security
- Pin exact versions — no
^/~ in production
pnpm audit in CI — fail on high/critical
- Dependency review —
pnpm why <pkg>, check maintenance
- Signed commits —
git commit -S
- Provenance verification —
npm pkg get integrity
15. Subresource Integrity (SRI)
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-abc123..."
crossorigin="anonymous"
></script>
# Generate
openssl dgst -sha384 -binary lib.js | openssl base64 -A
Rules SRI
- All third-party CDN scripts — SRI required
- Generate at build — Vite/Rollup plugin
- Fail on mismatch — browser blocks
16. Prototype Pollution
// Prevention
Object.freeze(Object.prototype);
Object.freeze(Array.prototype);
// Safe merge
import { merge } from "lodash-es";
const result = merge({}, userInput, { safe: true }); // lodash 4.17.21+
// Or use structuredClone
const safe = structuredClone(userInput);
Rules Prototype Pollution
- Freeze prototypes — early in app bootstrap
- Use safe libraries — lodash 4.17.21+, no
_.merge without checks
- Validate object keys — reject
__proto__, constructor, prototype
17. Security Testing
CI Gates
# .github/workflows/security.yml
- name: Dependency audit
run: pnpm audit --prod --audit-level high
- name: SAST (Semgrep)
uses: returntocorp/semgrep-action@v1
with:
config: p/security-audit
- name: Container scan
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
severity: HIGH,CRITICAL
Rules Security Testing
pnpm audit in CI — fail on high/critical
- SAST — Semgrep, CodeQL for code patterns
- Container scan — Trivy for Docker images
- Penetration test — annual, after major changes
18. Methodology
Before using ANY security pattern not documented in
this skill:
- MCP Context7 (priority):
context7_resolve-library-id +
context7_query-docs for OWASP, CSP, crypto libs.
- Official docs: OWASP Cheatsheets, MDN Security, RFC specs.
- Project config:
package.json, CSP headers, middleware
— verify against actual setup.
- HARD RULE: If not in this skill AND cannot be verified against
2 authoritative sources → DO NOT USE IT. Document as assumption or risk in
report to orchestrator.
19. Prohibitions
- ❌ Do not disable CSP — even in dev
- ❌ Do not use
eval/Function constructor
- ❌ Do not skip input validation — ever
- ❌ Do not use string concat for SQL/NoSQL
- ❌ Do not store secrets in repo — ever
- ❌ Do not use
dangerouslySetInnerHTML without DOMPurify
- ❌ Do not skip rate limiting on auth endpoints
- ❌ Do not use
SameSite=None without Secure
- ❌ Do not skip SRI on CDN scripts
- ❌ Do not ignore
pnpm audit failures in CI
20. References
Note: For JavaScript conventions (crypto, fetch),
see JavaScript
Note: For Auth patterns, see Auth
Note: For API Design (rate limiting, CORS),
see API Design
Note: For Next.js patterns, see Next.js
Note: For React patterns, see React
Note: For Astro patterns, see Astro
Note: For Vite config, see Vite
Last updated: 2026-08
1---2name: security3description: Web security rules for modern frontend development - OWASP Top 10 (2025), HTTPS and HSTS, security headers, Content Security Policy (CSP), XSS prevention, CSRF prevention, clickjacking, CORS configuration, input validation and sanitization, SQL/NoSQL injection prevention with parameterized queries, authentication security and secrets, password hashing (bcrypt/argon2), rate limiting and anti-abuse, supply chain and dependency auditing, subresource integrity, prototype pollution, security testing, framework-specific security (React/Next/Astro/Vite)4---56# Security — Web Security for Modern Frontends78---910## 1. Philosophy11121. **Defense in depth** — Multiple layers: headers, CSP, validation, framework protections.132. **Secure by default** — Frameworks protect you; don't disable protections.143. **Minimal attack surface** — Least privilege, minimal dependencies, no secrets in code.154. **Detect and respond** — Logging, monitoring, incident response plan.165. **Supply chain integrity** — Pin dependencies, verify provenance, audit regularly.1718---1920## 2. Threat Model (OWASP 2025 Essential)2122| Risk | Mitigation |23| ---------------------------------- | ---------------------------------------------- |24| **A01: Broken Access Control** | Server-side auth checks, RBAC, deny by default |25| **A02: Cryptographic Failures** | TLS 1.3, Argon2id, RS256, key rotation |26| **A03: Injection** | Parameterized queries, input validation, ORM |27| **A04: Insecure Design** | Threat modeling, secure patterns, code review |28| **A05: Security Misconfiguration** | Hardened headers, CSP, no defaults |29| **A06: Vulnerable Components** | Dependency scanning, pinning, updates |30| **A07: Auth Failures** | MFA, rotation, reuse detection, rate limits |31| **A08: Software Integrity** | SRI, signed commits, CI verification |32| **A09: Logging Failures** | Structured logs, alerting, no secrets |33| **A10: SSRF** | Allowlist URLs, no user-controlled fetch |3435---3637## 3. HTTPS & HSTS3839```nginx40# TLS 1.3 only41ssl_protocols TLSv1.3;42ssl_ciphers HIGH:!aNULL:!MD5;4344# HSTS (1 year, include subdomains, preload)45add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;46```4748### Rules4950- **TLS 1.3 only** — disable 1.2 and below51- **HSTS with preload** — submit to hstspreload.org52- **Certificates** — 90-day rotation (Let's Encrypt), monitor expiry53- **No mixed content** — all resources HTTPS5455---5657## 4. Security Headers5859| Header | Value | Purpose |60| ------------------------------ | ------------------------------------------ | ------------------------- |61| `Content-Security-Policy` | See §5 | XSS, injection prevention |62| `X-Frame-Options` | `DENY` | Clickjacking |63| `X-Content-Type-Options` | `nosniff` | MIME sniffing |64| `Referrer-Policy` | `strict-origin-when-cross-origin` | Referrer leakage |65| `Permissions-Policy` | `camera=(), microphone=(), geolocation=()` | Feature control |66| `Cross-Origin-Opener-Policy` | `same-origin` | COOP |67| `Cross-Origin-Resource-Policy` | `same-origin` | CORP |68| `Cross-Origin-Embedder-Policy` | `require-corp` | COEP |6970```nginx71add_header X-Frame-Options "DENY" always;72add_header X-Content-Type-Options "nosniff" always;73add_header Referrer-Policy "strict-origin-when-cross-origin" always;74add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;75add_header Cross-Origin-Opener-Policy "same-origin" always;76add_header Cross-Origin-Resource-Policy "same-origin" always;77```7879---8081## 5. Content Security Policy (CSP)8283### Essential policy8485```http86Content-Security-Policy:87 default-src 'self';88 script-src 'self' 'wasm-unsafe-eval'; # wasm for some libs89 style-src 'self' 'unsafe-inline'; # Tailwind/inline styles90 img-src 'self' data: https:; # images + data URIs91 font-src 'self' data:; # fonts92 connect-src 'self' https://api.example.com; # API + websockets93 frame-src 'none'; # no iframes94 object-src 'none'; # no plugins95 base-uri 'self'; # base tag96 form-action 'self'; # form targets97 frame-ancestors 'none'; # embedding98 block-all-mixed-content; # HTTPS only99 upgrade-insecure-requests; # upgrade HTTP100```101102### Rules CSP103104- **Report-only first** — `Content-Security-Policy-Report-Only` to test105- **Nonce for inline scripts** — `script-src 'nonce-<random>'`106- **No `unsafe-eval`** — except WASM if needed107- **Report URI** — `report-uri /csp-report` for monitoring108109---110111## 6. XSS Prevention112113### Server-side114115```ts116// Escape for HTML context117import { escapeHtml } from "escape-html";118const safe = escapeHtml(userInput);119120// Sanitize HTML (if user HTML allowed)121import DOMPurify from "isomorphic-dompurify";122const clean = DOMPurify.sanitize(dirtyHtml, { ALLOWED_TAGS: ["b", "i", "em"] });123```124125### Client-side126127```tsx128// React: auto-escapes by default129<div>{userContent}</div> // ✅ Safe130131// Dangerous - avoid132<div dangerouslySetInnerHTML={{ __html: userContent }} /> // ❌ Unless sanitized133```134135### Rules XSS Prevention136137- **Framework auto-escaping** — React, Vue, Svelte escape by default138- **Never `dangerouslySetInnerHTML`** without DOMPurify139- **CSP as backup** — blocks injected scripts140- **`Content-Type: application/json`** — prevents sniffing141142---143144## 7. CSRF Prevention145146> **Auth integration**: see `auth` skill.147148### SameSite cookies (primary)149150```http151Set-Cookie: session=...; SameSite=Lax; Secure; HttpOnly152Set-Cookie: csrf_token=...; SameSite=Strict; Secure; HttpOnly153```154155### Double-submit (fallback)156157```ts158// Generate159const csrfToken = crypto.randomBytes(32).toString("hex");160res.cookie("csrf_token", csrfToken, {161 sameSite: "strict",162 secure: true,163 httpOnly: true,164});165166// Validate (non-GET)167if (req.method !== "GET") {168 const headerToken = req.headers["x-csrf-token"];169 const cookieToken = req.cookies.csrf_token;170 if (!headerToken || headerToken !== cookieToken) {171 throw new Error("CSRF token mismatch");172 }173}174```175176### Rules CSRF177178- **`SameSite=Lax`** for session — allows navigation179- **`SameSite=Strict`** for CSRF token — no cross-site send180- **Custom header** — `X-CSRF-Token` for SPA mutations181- **Exempt GET/HEAD** — safe methods182183---184185## 8. Clickjacking186187```http188X-Frame-Options: DENY189Content-Security-Policy: frame-ancestors 'none';190```191192### Rules Clickjacking193194- **`X-Frame-Options: DENY`** — no embedding195- **CSP `frame-ancestors 'none'`** — modern replacement196- **Allow specific** only if business requires:197 `frame-ancestors https://partner.example.com`198199---200201## 9. CORS202203```http204Access-Control-Allow-Origin: https://app.example.com205Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS206Access-Control-Allow-Headers: Content-Type, Authorization, Idempotency-Key, X-CSRF-Token207Access-Control-Allow-Credentials: true208Access-Control-Max-Age: 86400209```210211### Rules CORS212213- **Exact origin** — no `*` with credentials214- **Preflight cache** — `Max-Age: 86400`215- **Allow only needed headers** — `Authorization`, `Content-Type`, `Idempotency-Key`, `X-CSRF-Token`216217---218219## 10. Input Validation220221```ts222import { z } from "zod";223224const schema = z.object({225 email: z.string().email(),226 age: z.number().int().min(13).max(120),227 tags: z.array(z.string().max(50)).max(10),228 metadata: z.record(z.string()).optional(),229});230231// Validate at boundary232const result = schema.safeParse(input);233if (!result.success) {234 throw new ValidationError(result.error.flatten());235}236```237238### Rules Input Validation239240- **Validate at boundaries** — API entry, form submit241- **Schema-based** — Zod, Valibot, ArkType242- **Reject unknown** — `strict()` mode243- **Sanitize, don't just validate** — HTML, SQL, paths244245---246247## 11. Injection Prevention248249### SQL (parameterized)250251```ts252// ✅ Safe253const users = await db.query("SELECT * FROM users WHERE email = $1", [email]);254255// ❌ Unsafe256const users = await db.query(`SELECT * FROM users WHERE email = '${email}'`);257```258259### NoSQL (whitelist operators)260261```ts262// ✅ Safe263const users = await db.users.find({ email: { $eq: email } });264265// ❌ Unsafe266const users = await db.users.find({ $where: `this.email == '${email}'` });267```268269### Path traversal270271```ts272// ✅ Safe273const safePath = path.resolve(baseDir, userInput);274if (!safePath.startsWith(baseDir)) throw new Error("Invalid path");275276// ❌ Unsafe277fs.readFile(`/var/www/${userInput}`);278```279280### Rules Injections Prevention281282- **Always parameterized** — never string concat283- **ORM preferred** — Prisma, Drizzle, TypeORM284- **Whitelist operators** — reject `$where`, `$expr`285- **Path resolution** — `path.resolve` + prefix check286287---288289## 12. Auth & Secrets Security290291> **Auth patterns**: see `auth` skill.292293### Secrets management294295```bash296# Never in code297# .env.local (gitignored)298JWT_SECRET=...299DATABASE_URL=...300301# Production: secret manager302# AWS Secrets Manager, GCP Secret Manager, Vault303```304305### Rules Auth & Secrets Security306307- **No secrets in repo** — `.env*` in `.gitignore`308- **Secret manager in prod** — rotation, audit, access control309- **Different secrets per env** — dev/staging/prod310- **Rotate on leak** — immediate, automated311312---313314## 13. Rate Limiting315316> **Rate limiting patterns**: see `api-design` skill.317318### Layers319320| Layer | Limit | Scope |321| ------------------ | ------------ | ------------------- |322| **Edge (CDN)** | 1000/min/IP | DDoS |323| **API Gateway** | 100/min/user | Abuse |324| **Auth endpoints** | 5/min/IP | Brute force |325| **Auth mutations** | 10/min/user | Credential stuffing |326327---328329## 14. Supply Chain Security330331```json332// package.json333{334 "dependencies": {335 "lodash": "4.17.21" // pinned, not ^336 }337}338```339340```bash341# Audit342pnpm audit --prod343pnpm audit --audit-level high344345# Provenance346pnpm install --frozen-lockfile347```348349### Rules Supply Chain Security350351- **Pin exact versions** — no `^`/`~` in production352- **`pnpm audit` in CI** — fail on high/critical353- **Dependency review** — `pnpm why <pkg>`, check maintenance354- **Signed commits** — `git commit -S`355- **Provenance verification** — `npm pkg get integrity`356357---358359## 15. Subresource Integrity (SRI)360361```html362<script363 src="https://cdn.example.com/lib.js"364 integrity="sha384-abc123..."365 crossorigin="anonymous"366></script>367```368369```bash370# Generate371openssl dgst -sha384 -binary lib.js | openssl base64 -A372```373374### Rules SRI375376- **All third-party CDN scripts** — SRI required377- **Generate at build** — Vite/Rollup plugin378- **Fail on mismatch** — browser blocks379380---381382## 16. Prototype Pollution383384```ts385// Prevention386Object.freeze(Object.prototype);387Object.freeze(Array.prototype);388389// Safe merge390import { merge } from "lodash-es";391const result = merge({}, userInput, { safe: true }); // lodash 4.17.21+392393// Or use structuredClone394const safe = structuredClone(userInput);395```396397### Rules Prototype Pollution398399- **Freeze prototypes** — early in app bootstrap400- **Use safe libraries** — lodash 4.17.21+, no `_.merge` without checks401- **Validate object keys** — reject `__proto__`, `constructor`, `prototype`402403---404405## 17. Security Testing406407### CI Gates408409```yaml410# .github/workflows/security.yml411- name: Dependency audit412 run: pnpm audit --prod --audit-level high413414- name: SAST (Semgrep)415 uses: returntocorp/semgrep-action@v1416 with:417 config: p/security-audit418419- name: Container scan420 uses: aquasecurity/trivy-action@master421 with:422 scan-type: fs423 severity: HIGH,CRITICAL424```425426### Rules Security Testing427428- **`pnpm audit` in CI** — fail on high/critical429- **SAST** — Semgrep, CodeQL for code patterns430- **Container scan** — Trivy for Docker images431- **Penetration test** — annual, after major changes432433---434435## 18. Methodology436437Before using ANY security pattern not documented in438this skill:4394401. **MCP Context7** (priority): `context7_resolve-library-id` +441 `context7_query-docs` for OWASP, CSP, crypto libs.4422. **Official docs**: OWASP Cheatsheets, MDN Security, RFC specs.4433. **Project config**: `package.json`, CSP headers, middleware444 — verify against actual setup.4454. **HARD RULE**: If not in this skill AND cannot be verified against446 2 authoritative sources → DO NOT USE IT. Document as assumption or risk in447 report to orchestrator.448449---450451## 19. Prohibitions452453- ❌ Do not disable CSP — even in dev454- ❌ Do not use `eval`/`Function` constructor455- ❌ Do not skip input validation — ever456- ❌ Do not use string concat for SQL/NoSQL457- ❌ Do not store secrets in repo — ever458- ❌ Do not use `dangerouslySetInnerHTML` without DOMPurify459- ❌ Do not skip rate limiting on auth endpoints460- ❌ Do not use `SameSite=None` without `Secure`461- ❌ Do not skip SRI on CDN scripts462- ❌ Do not ignore `pnpm audit` failures in CI463464---465466## 20. References467468> **Note:** For JavaScript conventions (crypto, fetch),469> see [JavaScript](../javascript/SKILL.md)470> **Note:** For Auth patterns, see [Auth](../auth/SKILL.md)471> **Note:** For API Design (rate limiting, CORS),472> see [API Design](../api-design/SKILL.md)473> **Note:** For Next.js patterns, see [Next.js](../nextjs/SKILL.md)474> **Note:** For React patterns, see [React](../reactjs/SKILL.md)475> **Note:** For Astro patterns, see [Astro](../astro/SKILL.md)476> **Note:** For Vite config, see [Vite](../vite/SKILL.md)477478---479480Last updated: 2026-08