Engine 4 of 7

Taint Analysis Engine

Track untrusted data as it flows through your application from sources to sinks. The definitive defense against injection attacks.

Overview

Taint Analysis tracks the flow of potentially malicious data through your application. When untrusted data (from user input, files, network) reaches a security-sensitive operation (database query, command execution, HTML output) without proper sanitization, Bloodhound flags it as a vulnerability.

🔴
Source
Untrusted input
🟡
Propagation
Data transforms
💥
Sink
Dangerous operation

Sources and Sinks

Taint Sources

HTTP Request
req.bodyreq.paramsreq.queryreq.headers
File System
fs.readFilefs.readFileSyncfile.read()
Database
db.query()Model.find()cursor.fetchall()
Environment
process.envos.environSystem.getenv()
User Input
readline()input()Scanner.next()

Security Sinks

SQL QuerySQL Injection
db.query()cursor.execute()
Command ExecCommand Injection
exec()system()spawn()
HTML OutputXSS
innerHTMLdocument.write()res.send()
File PathPath Traversal
fs.readFile()open()fopen()
URL RedirectOpen Redirect
res.redirect()location.href

Taint Propagation

Taint propagates through operations that transform data. Bloodhound tracks taint through string concatenation, method calls, object properties, and more.

JavaScript
1// Taint propagation example
2
3// Source: User input is tainted
4const userId = req.query.id; // 🔴 TAINTED
5
6// Propagation: Taint spreads through operations
7const query = "SELECT * FROM users WHERE id = " + userId; // 🔴 TAINTED
8const upperQuery = query.toUpperCase(); // 🔴 TAINTED (string methods preserve taint)
9const data = { sql: query }; // 🔴 TAINTED (object property)
10
11// Sink: Tainted data reaches dangerous operation
12db.query(query); // 💥 SQL INJECTION DETECTED
13
14// Taint trace:
15// req.query.id → userId → query → db.query()
16// SOURCE → PROP → PROP → SINK

Sanitizers

Sanitizers are functions that clean tainted data, making it safe to use. Bloodhound recognizes common sanitization patterns.

JavaScript
1// Sanitizers remove taint
2
3const userId = req.query.id; // 🔴 TAINTED
4
5// ✅ Parameterized query (sanitizer)
6db.query("SELECT * FROM users WHERE id = ?", [userId]); // Safe!
7
8// ✅ Validation sanitizer
9const validId = parseInt(userId, 10); // 🟢 Type coercion sanitizes
10if (isNaN(validId)) throw new Error("Invalid ID");
11
12// ✅ Encoding sanitizer
13const safeHtml = escapeHtml(userInput); // 🟢 Encoding sanitizes
14element.innerHTML = safeHtml; // Safe for XSS
15
16// ✅ Allowlist sanitizer
17const validSort = ['name', 'date', 'id'].includes(sortField)
18 ? sortField // 🟢 Allowlist sanitizes
19 : 'id';
20
21// Custom sanitizer annotation
22/** @sanitizes sql */
23function sanitizeForSQL(input) {
24 return input.replace(/['"\\]/g, '');
25}

Custom Sanitizers

Annotate your own sanitizer functions with @sanitizes JSDoc tags to teach Bloodhound about your security controls.

Interprocedural Analysis

Taint tracking works across function calls, even spanning multiple files. Bloodhound builds a complete call graph to trace data flow.

TypeScript
1// file: routes/users.ts
2import { getUserById } from '../services/user';
3import { renderProfile } from '../views/profile';
4
5app.get('/user/:id', async (req, res) => {
6 const user = await getUserById(req.params.id); // Taint enters
7 res.send(renderProfile(user)); // Taint flows to output
8});
9
10// file: services/user.ts
11export async function getUserById(id: string) {
12 // 💥 SQL Injection - tainted 'id' reaches query
13 return db.query(`SELECT * FROM users WHERE id = '${id}'`);
14}
15
16// file: views/profile.ts
17export function renderProfile(user: User) {
18 // 💥 XSS - tainted 'user.bio' reaches HTML output
19 return `<div class="bio">${user.bio}</div>`;
20}
21
22// Bloodhound traces:
23// Route 1: req.params.id → getUserById() → db.query() [SQL Injection]
24// Route 2: req.params.id → getUserById() → user → renderProfile() → HTML [XSS]

Real-World Examples

Command Injection via Filename

JavaScript
1// User uploads file with malicious name
2const filename = req.file.originalname; // "file; rm -rf /"
3
4// Taint flows to command execution
5exec(`convert ${filename} output.png`); // 💥 RCE
6
7// Fix: Use execFile with argument array
8execFile('convert', [filename, 'output.png']);

Properly Sanitized Input

JavaScript
1// User input
2const searchTerm = req.query.search; // 🔴 Tainted
3
4// Proper sanitization chain
5const sanitized = searchTerm
6 .slice(0, 100) // Length limit
7 .replace(/[<>&"']/g, '') // Remove dangerous chars
8 .trim(); // 🟢 Now safe
9
10// Safe to use in various contexts
11db.query('SELECT * FROM products WHERE name LIKE ?', [`%${sanitized}%`]);
12res.send(`<h1>Results for: ${escapeHtml(sanitized)}</h1>`);

Next Engine

Taint paths that involve complex conditions are verified by the Symbolic Execution Engine to eliminate false positives.