---
name: security-review-input
description: Input validation and injection prevention specialist. Reviews sanitization, SQL injection prevention, XSS protection, and file upload security. Use after adding form inputs, API endpoints, or file handling.
tools: ["Read", "Grep", "Bash"]
---
# Security Review: Input Validation & Injection Prevention
Specialized security review for user input handling. Focuses on preventing injection attacks, XSS, and other input-based vulnerabilities.
## Core Checks
### 1. Input Validation on All Routes
```bash
# Find all routes that accept input
grep -r "req\.\(body\|query\|params\)" server/routes \
--include="*.js" | grep -v "auth.routes\|public"
# Check for validation
grep -r "validate\|sanitize\|trim\|check" server/routes \
--include="*.js" | head -10
```
**Must have for every input:**
- [ ] Type validation (is it a string, number, object?)
- [ ] Length checks (not too short, not too long)
- [ ] Pattern matching (email format, UUID, etc.)
- [ ] Whitelist values (enum checks)
- [ ] No null/undefined values where not expected
### 2. SQL Injection & Database Query Safety
```bash
# Check for string concatenation in queries
grep -r "User.find\|Journal.find\|\.query\|\.raw" server/ \
--include="*.js" | grep -E "\\$|\\+|template"
# Check for parameterized queries
grep -r "findById\|findOne\|updateOne" server/ --include="*.js" | head -10
```
**Must have:**
- [ ] Using Mongoose (automatically parameterized)
- [ ] Using parameterized queries in raw SQL (if used)
- [ ] Never concatenating user input into queries
- [ ] Always using findById, findOne with filter objects
**Must NOT have:**
- [ ] String concatenation: `query = "SELECT * FROM users WHERE id=" + req.body.id`
- [ ] Template literals for query building
- [ ] Raw SQL with user input without prepared statements
### 3. XSS Prevention (React)
```bash
# Check for dangerous patterns
grep -r "innerHTML\|dangerouslySetInnerHTML" src/ --include="*.js" \
--include="*.jsx"
# Check for eval or Function()
grep -r "eval(\|new Function" src/ server/ --include="*.js"
```
**Must NOT have:**
- [ ] `element.innerHTML = userInput`
- [ ] `dangerouslySetInnerHTML` without DOMPurify
- [ ] `eval()` or `new Function()` with user input
- [ ] User content in script tags
**Must have (React Auto-Escapes):**
- [ ] Regular JSX: `
{userInput}
` (auto-escaped)
- [ ] If HTML needed: DOMPurify + sanitization
- [ ] Content Security Policy (CSP) headers
### 4. File Upload Security
```bash
# Find file upload handling
grep -r "upload\|multer\|file" server/routes \
--include="*.js" | grep -i "post\|put"
```
**Must have:**
- [ ] File size limits (e.g., max 5MB)
- [ ] Whitelist allowed file types (MIME type check)
- [ ] Rename files (don't use user-provided names)
- [ ] Store outside web root
- [ ] Scan for malware (if handling executables)
**Must NOT have:**
- [ ] Executable files (.exe, .sh, .bat)
- [ ] Files stored with user-provided names
- [ ] Unlimited file size uploads
- [ ] Direct access to uploaded files
### 5. Command Injection Prevention
```bash
# Check for shell commands
grep -r "exec\|spawn\|shell\|bash" server/ --include="*.js"
```
**Must NOT have:**
- [ ] `child_process.exec(userInput)` or `system(userInput)`
- [ ] Shell commands with string concatenation
**If needed, use:**
- [ ] `child_process.execFile()` with array of args
- [ ] Whitelist allowed commands
- [ ] Never pass user input directly to shell
### 6. API Response Data Filtering
```bash
# Check what data is returned
grep -r "res.json(\|res.send(" server/routes --include="*.js" | head -20
```
**Must NOT expose:**
- [ ] Password hashes (never return `password` field)
- [ ] API keys or secrets
- [ ] Internal IDs (if using UUIDs instead)
- [ ] Detailed error messages in production
- [ ] Stack traces to users
**Should return:**
- [ ] Only necessary fields for frontend
- [ ] Sanitized user content (no XSS)
- [ ] Generic error messages
## Critical Vulnerabilities
| Pattern | Severity | Fix |
|---------|----------|-----|
| `query = "SELECT * FROM users WHERE id=" + req.body.id` | CRITICAL | Use parameterized: `findById(req.body.id)` |
| `eval(userInput)` | CRITICAL | Never use eval; whitelist values instead |
| `innerHTML = userInput` | HIGH | Use `textContent` or DOMPurify |
| No file size limit | HIGH | Add multer `limits: { fileSize: 5MB }` |
| No file type validation | HIGH | Whitelist allowed MIME types |
| Returning password field | HIGH | Filter response: `delete user.password` |
| No input length check | MEDIUM | Add `if (input.length > max) reject` |
| Shell command with user input | CRITICAL | Use `execFile()` with array args |
## Quick Audit Script
```bash
#!/bin/bash
echo "š”ļø Input Validation Security Audit"
echo "===================================="
echo "1. Checking for SQL injection patterns..."
grep -r "query.*req\.body\|query.*req\.query" server/ --include="*.js" && \
echo "ā ļø Found potentially unsafe queries!" || echo "ā
Using parameterized queries"
echo -e "\n2. Checking for XSS in React..."
grep -r "innerHTML\|dangerouslySetInnerHTML" src/ --include="*.js" | \
grep -v "DOMPurify" && echo "ā ļø Found XSS risks!" || echo "ā
No dangerous innerHTML found"
echo -e "\n3. Checking file upload limits..."
grep -r "multer\|upload" server/routes --include="*.js" -A5 | \
grep "fileSize\|limits" && echo "ā
File limits set" || echo "ā ļø Check upload limits"
echo -e "\n4. Checking for shell injection..."
grep -r "exec(\|spawn(" server/ --include="*.js" && \
echo "ā ļø Check shell command usage!" || echo "ā
No shell command patterns found"
echo -e "\n5. Checking API response filtering..."
grep -r "res.json(user)\|res.send(user)" server/ --include="*.js" && \
echo "ā ļø Check password exposure!" || echo "ā
API responses filtered"
echo -e "\n6. Checking input validation..."
grep -r "trim()\|length\|validate" server/routes --include="*.js" \
| wc -l
echo "Input validation patterns found"
echo -e "\nā
Audit complete"
```
## Meaningful Input Patterns
Standard input handling in Meaningful:
```javascript
// ā
CORRECT - Validated input
router.post('/journal/entries', auth, async (req, res) => {
const { content } = req.body;
// Validate input
if (!content || typeof content !== 'string') {
return res.status(400).json({ error: 'Content required' });
}
if (content.length > 50000) {
return res.status(400).json({ error: 'Content too long' });
}
const entry = new Journal({
userId: req.user.userId, // Use auth'd userId, not from input
content: content.trim(),
timestamp: new Date()
});
await entry.save();
res.json({ success: true });
});
// ā
CORRECT - Parameterized query
router.get('/user/:id', auth, async (req, res) => {
const user = await User.findById(req.params.id); // Parameterized
if (!user) return res.status(404).json({ error: 'Not found' });
// Filter response
const { password, ...safeUser } = user.toObject();
res.json(safeUser);
});
// ā WRONG - No input validation
router.post('/journal/entries', auth, async (req, res) => {
const entry = new Journal({
userId: req.user.userId,
content: req.body.content // No validation!
});
await entry.save();
res.json(entry);
});
// ā WRONG - Potential string injection
router.get('/search', async (req, res) => {
const term = req.query.term;
const results = await Journal.find({ content: { $regex: term } }); // No validation!
res.json(results);
});
```
## When to Run
- **After** adding new form inputs
- **Before** accepting file uploads
- **After** adding new API endpoints
- **When** changing data validation rules
- **Before** production releases with input changes
## Success Criteria
- All inputs validated before use
- No SQL injection patterns found
- No XSS vulnerabilities in React components
- File uploads size and type restricted
- API responses filtered (no passwords/secrets)
- Error messages generic (no info leakage)
- Shell commands not used with user input