Sanitize and escape all user-provided input before rendering in HTML, executing in SQL, or passing to system commands. Unsanitized input is the entry point for XSS, SQL injection, and command injection attacks — the three most exploited vulnerability classes in web applications.
Unsanitized user input is the root cause of XSS, SQL injection, and command injection — the three most exploited vulnerability classes according to OWASP. A single unsanitized form field can allow an attacker to steal user sessions, exfiltrate database contents, or execute arbitrary commands on your server.
BeforeMerge scans your pull requests against this rule and 5+ others. Get actionable feedback before code ships.
Every piece of data from the user is potentially malicious. Form fields, URL parameters, HTTP headers, file uploads, and WebSocket messages can all contain payloads designed to exploit your application. The three most dangerous attack classes all stem from unsanitized input:
Cross-Site Scripting (XSS): Injecting <script> tags or event handlers into HTML. The attacker's JavaScript runs in the victim's browser with full access to cookies, session tokens, and the DOM.
SQL Injection: Injecting SQL syntax into queries. The attacker can read, modify, or delete any data in your database, and in some configurations, execute system commands on the database server.
Command Injection: Injecting shell metacharacters into system commands. The attacker can execute arbitrary commands with the full privileges of the Node.js process.
Never concatenate user input directly into HTML, SQL queries, or shell commands. Use parameterized queries for SQL, context-aware escaping for HTML, and argument arrays (not string interpolation) for shell commands.
// SQL Injection — user input concatenated into query
const result = await db.query(
`SELECT * FROM users WHERE email = '${req.body.email}'`
);
// XSS — user input rendered as raw HTML
app.get("/search", (req, res) => {
res.send(`<h1>Results for: ${req.query.q}</h1>`);
});
// Command Injection — user input in shell command
const { exec } = require("child_process");
exec(`convert ${req.body.filename} output.png`);// Parameterized query — input is never part of SQL syntax
const result = await db.query(
"SELECT * FROM users WHERE email = $1",
[req.body.email]
);
// Template with auto-escaping (React handles this by default)
function SearchResults({ query }: { query: string }) {
return <h1>Results for: {query}</h1>; // React escapes by default
}
// Argument array — input is never parsed by the shell
const { execFile } = require("child_process");
execFile("convert", [req.body.filename, "output.png"]);Search for string interpolation in SQL queries and shell commands:
grep -rn '\$\{.*\}.*SELECT\|\$\{.*\}.*INSERT\|exec(.*\$\{' --include="*.ts"Use a SAST tool like Semgrep to detect injection patterns automatically.
exec() with execFile() for shell commands and pass arguments as arrays