Injection
Prevent SQL injection, XSS, command injection, and other cases where untrusted input is interpreted as code.
Results for: ${req.query.q}
`); // attacker: }); // Command injection — user input passed to a shell const { exec } = require('child_process'); app.post('/api/convert', (req, res) => { exec(`convert ${req.body.filename} output.png`, callback); // attacker: "; rm -rf / #" }); ``` ## Secure Pattern ```javascript // Parameterized query — input is bound as data, never interpreted as SQL app.get('/api/users', async (req, res) => { const results = await db('users').where({ email: req.query.email }); // query builder parameterizes automatically res.json(results); }); // Context-aware output encoding — modern templating engines auto-escape by default app.get('/search', (req, res) => { res.render('search-results', { query: req.query.q }); // template engine escapes automatically }); // Avoid shell interpolation entirely — use an argument array, not a concatenated string const { execFile } = require('child_process'); app.post('/api/convert', (req, res) => { const filename = validateFilename(req.body.filename); // allow-list validated first execFile('convert', [filename, 'output.png'], callback); // arguments passed directly, no shell parsing }); ``` ## Copy-Paste Rules for Cursor, Claude Code, and Codex ```markdown --- description: Injection prevention — SQL, XSS, command injection, and interpreter safety alwaysApply: true --- # Injection Prevention Rules - Never build a SQL query (or any query language) using string concatenation or template literals with untrusted input. Always use parameterized queries, prepared statements, or a query builder/ORM that parameterizes automatically. - Never insert untrusted input directly into HTML output without context-aware escaping. Rely on the templating engine's automatic escaping rather than manually constructing HTML strings with interpolated input, and never disable auto-escaping for user-controllable content. - Never pass untrusted input to a shell interpreter (`exec`, `system`, backticks) via string concatenation. Prefer APIs that accept arguments as an array (`execFile`, `spawn`) so input is never parsed by a shell. - Validate untrusted input against an explicit allow-list of expected formats (e.g., filenames, identifiers) before using it in any sensitive context, in addition to parameterization — defense in depth, not either/or. - For NoSQL databases, apply the same discipline: never construct query objects by directly merging unvalidated user input into operators like `$where` or `$gt` without validation. - When generating any code that constructs a query, command, or markup string, default to the safe/parameterized API for that context — treat string concatenation into an interpreter as disallowed, not merely discouraged. ``` ## Verification Checklist - [ ] All database queries use parameterization, prepared statements, or an ORM/query builder — no raw string concatenation - [ ] Output rendered into HTML relies on automatic, context-aware escaping; auto-escaping is never disabled for user-controllable content - [ ] Any shell command execution uses argument-array APIs, never string-interpolated shell commands - [ ] User input used in file paths, filenames, or system commands is validated against an explicit allow-list - [ ] NoSQL query construction validates input types before merging into query objects - [ ] Automated and manual testing includes injection payloads across all input surfaces — see [Trusteed's vulnerability scanner](https://trusteed.io/vulnerability-scanner) and [WAAP](https://trusteed.io/waap) for continuous injection testingFrequently asked questions
Is XSS still relevant if a framework like React auto-escapes output by default?
Yes — modern frameworks significantly reduce risk by escaping by default, but explicit "danger" APIs (`dangerouslySetInnerHTML` in React, `v-html` in Vue) bypass that protection intentionally, and injection risk returns wherever those APIs are used with untrusted input.
What's the difference between input validation and parameterization — do I need both?
Yes, both — parameterization prevents the input from being interpreted as command syntax, which is the core defense. Input validation (allow-listing expected formats) is a complementary layer that catches unexpected input early and reduces the attack surface, but doesn't replace parameterization on its own.
Can a WAF fully prevent injection attacks?
A WAF can block many known injection patterns at the network edge, providing valuable defense in depth, but it operates on signature matching and can be bypassed by novel encoding or obfuscation — secure coding at the application layer (parameterization, escaping) remains the primary defense, not the WAF alone.