skills-ws
Version:
87 agent skills for AI coding assistants — marketing, growth, web3, dev, design & operations. Built for OpenClaw, Claude Code, Cursor, and Codex.
104 lines (90 loc) • 6.14 kB
Markdown
## 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.
---