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