Awesome-claude-code check-input-validation
Analyzes PHP code for input validation issues. Detects missing validation, weak regex, type coercion attacks, length/format gaps, whitelist violations.
install
source · Clone the upstream repo
git clone https://github.com/dykyi-roman/awesome-claude-code
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/dykyi-roman/awesome-claude-code "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/check-input-validation" ~/.claude/skills/dykyi-roman-awesome-claude-code-check-input-validation && rm -rf "$T"
manifest:
skills/check-input-validation/SKILL.mdsource content
Input Validation Security Check
Analyze PHP code for input validation vulnerabilities.
Detection Patterns
1. Missing Input Validation
// CRITICAL: Direct use of request data $id = $_GET['id']; $this->delete($id); // No validation // CRITICAL: Controller without validation public function update(Request $request): Response { $data = $request->all(); $this->service->update($data); // Unvalidated } // CRITICAL: Command handler trusts input public function handle(UpdateUserCommand $command): void { $user->setEmail($command->email); // Not validated }
2. Weak Regex Patterns
// VULNERABLE: Anchors missing if (preg_match('/[a-z]+/', $input)) { } // Matches substring // VULNERABLE: Case sensitivity if (preg_match('/^[a-z]+$/', $email)) { } // Misses uppercase // VULNERABLE: Dot matches newline if (preg_match('/^.+$/', $input)) { } // Dot doesn't match newline // VULNERABLE: Overly permissive if (preg_match('/.*/', $input)) { } // Matches everything
3. Type Coercion Attacks
// VULNERABLE: Loose comparison if ($request->get('admin') == true) { } // '1', 'true', 1 all pass // VULNERABLE: Array injection $where['id'] = $_GET['id']; // Could be array: ?id[]=1&id[]=2 // VULNERABLE: Type juggling if ($_POST['password'] == 0) { } // 'password123' == 0 is true!
4. Length/Format Validation Gaps
// VULNERABLE: No length limit $description = $request->get('description'); $this->save($description); // Could be megabytes // VULNERABLE: Missing format check $phone = $request->get('phone'); // Could contain scripts // VULNERABLE: No max items $ids = $request->get('ids'); foreach ($ids as $id) { } // Unbounded array
5. Whitelist Violations
// VULNERABLE: Blacklist instead of whitelist $forbidden = ['<script>', 'javascript:']; if (!in_array($input, $forbidden)) { } // Easy to bypass // VULNERABLE: Dynamic field access $field = $_GET['field']; $value = $entity->$field; // Can access any property // CORRECT: Whitelist approach $allowed = ['name', 'email', 'phone']; if (!in_array($field, $allowed, true)) { throw new InvalidArgumentException(); }
6. File Upload Validation
// VULNERABLE: Only checking extension if (pathinfo($file['name'], PATHINFO_EXTENSION) === 'jpg') { } // VULNERABLE: MIME type can be spoofed if ($file['type'] === 'image/jpeg') { } // CORRECT: Check actual file content $finfo = new finfo(FILEINFO_MIME_TYPE); $mime = $finfo->file($file['tmp_name']); $allowed = ['image/jpeg', 'image/png', 'image/gif']; if (!in_array($mime, $allowed, true)) { }
7. Numeric Input Validation
// VULNERABLE: No range check $page = (int) $_GET['page']; // Could be negative or huge // VULNERABLE: Float precision $amount = (float) $_POST['amount']; // 0.1 + 0.2 != 0.3 // CORRECT: Full validation $page = filter_var($_GET['page'], FILTER_VALIDATE_INT, [ 'options' => ['min_range' => 1, 'max_range' => 1000] ]);
Grep Patterns
# Direct $_GET/$_POST usage Grep: "\$_(GET|POST|REQUEST)\[['\"][^'\"]+['\"]\]" --glob "**/*.php" # Missing filter_var Grep: "\$request->get\([^)]+\)\s*;" --glob "**/*.php" # Weak regex (no anchors) Grep: "preg_match\(['\"]\/[^$^]" --glob "**/*.php" # Dynamic property access Grep: "\$\w+->\$" --glob "**/*.php"
Severity Classification
| Pattern | Severity |
|---|---|
| No input validation at all | 🔴 Critical |
| Type juggling vulnerability | 🔴 Critical |
| Missing file content check | 🟠 Major |
| Weak regex | 🟠 Major |
| Missing length limits | 🟡 Minor |
Best Practices
Use Validation Libraries
// Symfony Validation $constraints = new Assert\Collection([ 'email' => [new Assert\NotBlank(), new Assert\Email()], 'age' => [new Assert\Range(['min' => 18, 'max' => 120])], ]); // Laravel Validation $validated = $request->validate([ 'email' => 'required|email|max:255', 'age' => 'required|integer|min:18|max:120', ]);
Always Use Strict Types
declare(strict_types=1); function process(int $id, string $email): void { }
Output Format
### Input Validation: [Description] **Severity:** 🔴/🟠/🟡 **Location:** `file.php:line` **CWE:** CWE-20 (Improper Input Validation) **Issue:** [Description of missing/weak validation] **Attack Vector:** [How attacker exploits this] **Code:** ```php // Vulnerable code
Fix:
// With proper validation
## When This Is Acceptable - **Framework FormRequest** — Laravel/Symfony form requests already validate input; additional validation is redundant - **Internal service calls** — Methods called only by already-validated application layer don't need re-validation - **Value Objects** — VOs validate in constructor; callers don't need additional checks ### False Positive Indicators - Input passes through a FormRequest or Validator before reaching the method - Method is private/protected and called only from validated entry points - Parameter is a Value Object that self-validates