## Contents

- 4a. Remediation Playbooks
- SQL / NoSQL Injection
- Cross-Site Scripting (XSS)
- SSRF (Server-Side Request Forgery)
- Command Injection
- Broken Authorization (IDOR / privilege escalation)
- Broken Authentication (sessions, JWT, brute force)
- Insecure Deserialization
- HTTP Request Smuggling
- Security Misconfiguration / headers

## 4a. Remediation Playbooks

A finding is only closed when the secure pattern is in place *and* a regression test proves the PoC no longer works. Below are framework-specific fixes for each class Shannon covers. Pair each fix with a test (see §6 — Regression Testing).

### SQL / NoSQL Injection
- **Root cause:** untrusted input concatenated into a query.
- **Fix:** always parameterize; never build query strings. Prefer a query builder / ORM with bound parameters.
  ```js
  // ❌ const r = await db.query(`SELECT * FROM users WHERE name LIKE '%${q}%'`);
  // ✅ Node + pg
  const r = await db.query('SELECT * FROM users WHERE name LIKE $1', [`%${q}%`]);
  // ✅ Mongo: never pass req.body/req.query straight into a filter — cast & whitelist:
  await User.find({ name: String(q) });   // reject objects so {$ne:null} can't slip in
  ```
- Also: least-privilege DB user (no DDL), reject `$`/`.` keys in JSON filters, validate types at the edge (zod/Joi).

### Cross-Site Scripting (XSS)
- **Root cause:** untrusted data rendered into HTML/JS/attribute/URL context without context-correct encoding.
- **Fix:** rely on the framework's auto-escaping; never bypass it with raw-HTML sinks on untrusted data.
  ```jsx
  // ✅ React/Vue/Svelte auto-escape {value}. The danger is the escape hatch:
  // ❌ <div dangerouslySetInnerHTML={{ __html: userInput }} />
  // ✅ If you MUST render HTML, sanitize first:
  import DOMPurify from 'dompurify';
  <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
  ```
- Defense in depth: a strict `Content-Security-Policy` (no `unsafe-inline`; use nonces/hashes), `HttpOnly`+`Secure`+`SameSite` cookies, and URL-scheme allowlists (`https:` only) to block `javascript:` sinks.

### SSRF (Server-Side Request Forgery)
- **Root cause:** server fetches a user-supplied URL.
- **Fix:** allowlist destinations; resolve the host and reject private/link-local ranges; disable redirects to new hosts.
  ```js
  import dns from 'node:dns/promises';
  import ipaddr from 'ipaddr.js';
  async function assertPublicUrl(raw) {
    const u = new URL(raw);
    if (!['http:', 'https:'].includes(u.protocol)) throw new Error('scheme');
    const { address } = (await dns.lookup(u.hostname));
    const r = ipaddr.parse(address).range();           // 'private' | 'loopback' | 'linkLocal' | ...
    if (['private','loopback','linkLocal','uniqueLocal','reserved'].includes(r)) throw new Error('blocked');
    return u;                                            // fetch with redirect: 'manual', re-check each hop
  }
  ```
- Cloud: enforce **IMDSv2** so a basic SSRF can't read instance credentials; egress-firewall the service.

### Command Injection
- **Root cause:** user input reaches a shell.
- **Fix:** never invoke a shell with interpolated input; pass an argv array to `execFile`/`spawn` with `shell:false`.
  ```js
  // ❌ exec(`convert ${file} out.png`);            // shell metacharacters → RCE
  import { execFile } from 'node:child_process';
  execFile('convert', [file, 'out.png'], { shell: false }, cb);  // ✅ args never parsed by a shell
  ```
- Allowlist the binary and validate args (e.g. filename matches `^[\w.-]+$`).

### Broken Authorization (IDOR / privilege escalation)
- **Root cause:** the handler trusts a client-supplied id/role without checking the *current* user owns or may access it.
- **Fix:** enforce object-level authz on every read/write, server-side, from the session — not from a request field.
  ```js
  // ❌ const doc = await Doc.findById(req.params.id);          // any id → anyone's doc
  // ✅ scope the query to the authenticated principal
  const doc = await Doc.findOne({ _id: req.params.id, ownerId: req.user.id });
  if (!doc) return res.sendStatus(404);                         // 404, not 403 (don't confirm existence)
  // role checks come from the verified session/JWT claims, never from req.body.role
  ```
- Use centralized policy (e.g. CASL/OPA), deny-by-default, and avoid sequential/guessable ids (use UUIDs).

### Broken Authentication (sessions, JWT, brute force)
- **Fix:** verify JWTs with a pinned algorithm (`algorithms:['RS256']`) and reject `alg:none`; rotate the session id on login (kills fixation); short-lived access tokens + rotating refresh tokens; bcrypt/argon2 for passwords; rate-limit + lockout/backoff on login.
  ```js
  jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] }); // never accept attacker-chosen alg
  req.session.regenerate(() => {/* set new session after successful auth */});
  ```

### Insecure Deserialization
- **Root cause:** untrusted bytes turned into objects that can execute code on construct.
- **Fix:** don't deserialize untrusted data into rich objects. Use a data-only format (JSON) and validate against a schema; never `node-serialize`/Java native `readObject`/Python `pickle.loads`/PHP `unserialize` on user input.
  ```js
  const data = JSON.parse(body);          // data only, no behavior
  const safe = MySchema.parse(data);      // zod: reject unexpected shape/types
  ```

### HTTP Request Smuggling
- **Root cause:** front-end and back-end disagree on request boundaries (`Content-Length` vs `Transfer-Encoding`).
- **Fix:** mostly an infra fix — use HTTP/2 end-to-end or a proxy that normalizes/rejects ambiguous framing; reject requests containing both `Content-Length` and `Transfer-Encoding`; keep proxy and origin on the same HTTP version and patched. Validate with `smuggler`/`h2csmuggler` (see §7).

### Security Misconfiguration / headers
- **Fix:** ship secure defaults — `helmet()` (Express) or framework equivalents — setting HSTS, `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY` (or CSP `frame-ancestors`), a strict CSP, and disabling stack traces / `X-Powered-By` in production.

---
