UNPKG

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