custom-sanitizer-audit · git:20260529.17de87f · 2026-05-29 · sha256 751852288695d534
custom-sanitizer-audit git:20260529.17de87fB
Immutable. This exact content is served forever at /api/v1/blob/751852288695d534.
---
name: custom-sanitizer-audit
description: "Audit custom sanitization functions for bypass vulnerabilities using the Five-Point Checklist and ordering analysis. Use when encountering homegrown sanitize/filter/clean/escape functions, reviewing input validation, or testing custom security wrappers."
---
# Custom Sanitizer Audit
Every homegrown security function is a bypass waiting to be found. Framework-provided sanitizers have years of adversarial testing; custom ones have the developer's imagination as their ceiling.
## The Five-Point Checklist
Every custom sanitizer MUST be evaluated against all five points. A failure on ANY single point is a bypass.
### 1. Case-Insensitive?
```php
// BYPASSABLE: strpos() not stripos()
if (strpos($input, 'SELECT') !== false) { block(); }
// Bypass: select, SeLeCt
// SECURE: stripos()
if (stripos($input, 'SELECT') !== false) { block(); }
```
### 2. Global Replacement?
```javascript
// BYPASSABLE: first match only
input.replace("../", "")
// Input: "....//etc/passwd" → "../etc/passwd"
// SECURE: global flag
input.replace(/\.\.\//g, "")
```
### 3. Recursive?
After one pass of removal, does the remaining string reconstitute the blocked pattern?
```
Input: <scrip<script>t>alert(1)</scrip</script>t>
Pass 1: inner <script> removed → <script>alert(1)</script> ← XSS
```
**Test:** Nest the blocked string inside itself. If the sanitizer runs once, the outer halves collapse into the blocked string.
### 4. Complete?
Does the blocklist cover all dangerous variants?
```javascript
// BYPASSABLE: blocks <script> but not event handlers
input.replace(/<script[^>]*>.*?<\/script>/gi, "");
// Bypass: <img onerror=alert(1) src=x>, <svg onload=alert(1)>
```
### 5. Consistent Across All Routes?
Is the sanitizer applied uniformly to every entry point?
```bash
# Find the sanitizer definition, search all call sites,
# find all routes handling the same input type,
# diff the two lists — missing routes are bypasses
```
## Ordering Bugs: Validate-Then-Transform
The sanitizer may be correct in isolation but applied in the wrong order relative to data transformations. See [references/ordering-bugs.md](references/ordering-bugs.md) for patterns.
**General Rule:** decode/normalize/transform FIRST, sanitize LAST, use IMMEDIATELY. Any operation between sanitization and use is a potential bypass.
## The Sixth Check: Security Gate with No Bailout
```php
// VULNERABLE: check exists but doesn't stop execution
if (!is_valid_input($data)) {
$error = true; // flag set, never checked before dangerous op
}
execute_query($data); // runs regardless
```
For every security check, verify the NEXT line is `return`, `exit`, `throw`, `die()`, or `abort()`.
## Detection Workflow
### Step 1: Find All Custom Security Functions
```bash
grep -rn "function.*sanitiz\|function.*filter\|function.*clean\|function.*escap\|function.*valid" \
--include="*.php" --include="*.js" --include="*.py" --include="*.rb" --include="*.java" --include="*.go"
grep -rn "blocked\|blacklist\|blocklist\|forbidden\|banned\|disallowed" \
--include="*.php" --include="*.js" --include="*.py" --include="*.rb"
```
### Step 2: Audit Each Function
```
Function: sanitize_input() at src/utils/security.php:42
| Check | Result | Evidence |
|--------------------|--------|-----------------------------------|
| Case-insensitive? | FAIL | Uses strpos() not stripos() |
| Global replacement?| PASS | Uses str_replace() (global in PHP)|
| Recursive? | FAIL | Single pass, nesting bypasses |
| Complete? | FAIL | Missing UNION, HAVING keywords |
| Consistent? | FAIL | Not called in /api/v2/export |
```
### Step 3: Check Ordering
Trace data flow backward AND forward from each sanitizer call site. See [references/ordering-bugs.md](references/ordering-bugs.md).
### Step 4: Test Bypasses
For every FAIL, craft a specific bypass payload and test.
## Anti-Patterns by Language
See [references/anti-patterns.md](references/anti-patterns.md) for the full table of common anti-patterns across PHP, JS, Python, Ruby, Java, and Go.
## Related Skills
- **parser-differential-bypass** -- sanitizer and consumer parse input differently
- **unicode-normalization-bypass** -- Unicode NFKC/NFKD undoes sanitization
- **dompurify-mxss-bypass** -- DOMPurify sanitizer bypass
- **insecure-defaults** -- sanitizer has fail-open behavior on error