check-type-juggling

Detects PHP type juggling vulnerabilities. Identifies loose comparison with user input, in_array without strict mode, switch statement type coercion, and hash comparison bypasses.

59 stars

Best use case

check-type-juggling is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Detects PHP type juggling vulnerabilities. Identifies loose comparison with user input, in_array without strict mode, switch statement type coercion, and hash comparison bypasses.

Teams using check-type-juggling should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/check-type-juggling/SKILL.md --create-dirs "https://raw.githubusercontent.com/dykyi-roman/awesome-claude-code/main/skills/check-type-juggling/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/check-type-juggling/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How check-type-juggling Compares

Feature / Agentcheck-type-jugglingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Detects PHP type juggling vulnerabilities. Identifies loose comparison with user input, in_array without strict mode, switch statement type coercion, and hash comparison bypasses.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Type Juggling Security Check (A03:2021)

Analyze PHP code for type juggling vulnerabilities exploiting PHP's loose comparison behavior.

## Detection Patterns

### 1. Loose Comparison with User Input

```php
// CRITICAL: Loose == comparison with user input
if ($request->get('role') == 'admin') { } // '0' == 'admin' is false, but 0 == 'admin' is true!
if ($token == $expectedToken) { }          // Type juggling bypass possible

// CRITICAL: Password comparison
if ($password == $storedHash) { }          // NEVER use == for security checks

// CORRECT: Strict comparison
if ($request->get('role') === 'admin') { }
if (hash_equals($expectedToken, $token)) { } // Timing-safe comparison
```

### 2. in_array Without Strict Mode

```php
// CRITICAL: in_array defaults to loose comparison
$allowedRoles = ['admin', 'editor', 'viewer'];
if (in_array($request->get('role'), $allowedRoles)) { }
// in_array(0, ['admin', 'editor']) === true! (0 == 'admin' is true)
// in_array(true, ['admin']) === true!

// VULNERABLE: Checking allowed values
$allowedStatuses = ['active', 'inactive'];
if (in_array($input, $allowedStatuses)) { }
// true matches any string!

// CORRECT: Always use strict mode
if (in_array($request->get('role'), $allowedRoles, true)) { }
```

### 3. Switch Statement Type Coercion

```php
// VULNERABLE: Switch uses loose comparison
switch ($request->get('action')) {
    case 0:     // Matches any non-numeric string!
        $this->deleteAll();
        break;
    case 'view':
        $this->view();
        break;
}
// Input 'view' matches case 0 first! (if 0 is before 'view')

// CORRECT: Use match (strict comparison)
$result = match ($request->get('action')) {
    'view' => $this->view(),
    'edit' => $this->edit(),
    default => throw new InvalidActionException(),
};
```

### 4. Hash Comparison Bypass

```php
// CRITICAL: strcmp() returns 0 for array input
if (strcmp($input, $expected) == 0) { }
// strcmp([], 'password') returns NULL, and NULL == 0 is true!

// CRITICAL: md5/sha1 magic hashes
if (md5($input) == '0') { }
// md5('240610708') = '0e462097431906509019562988736854'
// '0e...' == '0' is true (scientific notation: 0 * 10^... = 0)

// CRITICAL: Loose comparison of hashes
if (md5($a) == md5($b)) { }
// Two different inputs can have 0e... hashes → both equal 0

// CORRECT: hash_equals for hash comparison
if (hash_equals($expectedHash, md5($input))) { }
```

### 5. Null Coalescing with Loose Types

```php
// VULNERABLE: isset + loose comparison
if (isset($data['admin']) && $data['admin'] == true) {
    $this->grantAdminAccess(); // 'yes', '1', 1, true all pass
}

// VULNERABLE: Empty check
if (!empty($request->get('verified'))) {
    // '0' is empty, but 'false' is not — inconsistent
}

// CORRECT: Explicit type check
if (($data['admin'] ?? false) === true) {
    $this->grantAdminAccess();
}
```

### 6. Array Key Type Juggling

```php
// VULNERABLE: Numeric string keys become integers
$permissions = ['0' => 'none', '1' => 'read', '2' => 'write'];
$level = $request->get('level'); // String from request
$permission = $permissions[$level]; // '01' !== 1, but both exist in different contexts

// VULNERABLE: Boolean key
$config = [true => 'enabled', false => 'disabled'];
// true becomes 1, false becomes 0 as array keys
```

### 7. JSON Decode Type Juggling

```php
// VULNERABLE: JSON sends integer where string expected
$data = json_decode($request->getContent(), true);
if ($data['token'] == $validToken) { }
// JSON: {"token": 0} → 0 == "any-string" is true!

// CORRECT: Validate type after decode
$data = json_decode($request->getContent(), true);
if (!is_string($data['token'] ?? null)) {
    throw new InvalidInputException('Token must be a string');
}
if (hash_equals($validToken, $data['token'])) { }
```

## Grep Patterns

```bash
# Loose comparison with variables
Grep: "\\\$.*==\s*['\"]|['\"].*==\s*\\\$" --glob "**/*.php"
Grep: "==\s*true|==\s*false|==\s*null|==\s*0\b" --glob "**/*.php"

# in_array without strict
Grep: "in_array\([^)]+\)(?!.*true)" --glob "**/*.php"

# switch instead of match
Grep: "switch\s*\(\\\$.*request\|switch\s*\(\\\$.*input\|switch\s*\(\\\$.*data" --glob "**/*.php"

# strcmp with loose comparison
Grep: "strcmp\(.*==\s*0|strcmp\(.*!=\s*0" --glob "**/*.php"

# Hash comparison with ==
Grep: "md5\(.*==|sha1\(.*==|hash\(.*==" --glob "**/*.php"

# array_search without strict
Grep: "array_search\([^)]+\)(?!.*true)" --glob "**/*.php"
```

## Severity Classification

| Pattern | Severity |
|---------|----------|
| Token/hash comparison with == | 🔴 Critical |
| Authentication check with == | 🔴 Critical |
| in_array without strict on security check | 🔴 Critical |
| JSON decode + loose comparison | 🟠 Major |
| switch on user input (instead of match) | 🟠 Major |
| in_array without strict (non-security) | 🟡 Minor |
| General loose == usage | 🟡 Minor |

## PHP Type Juggling Reference

| Comparison | Result | Why |
|-----------|--------|-----|
| `0 == 'admin'` | `true` | String cast to int = 0 |
| `0 == null` | `true` | Both falsy |
| `'' == null` | `true` | Both falsy |
| `'0e1' == '0e2'` | `true` | Both = 0 (scientific notation) |
| `true == 'anything'` | `true` | Non-empty string is truthy |
| `[] == false` | `true` | Empty array is falsy |

## Output Format

```markdown
### Type Juggling: [Description]

**Severity:** 🔴/🟠/🟡
**Location:** `file.php:line`
**CWE:** CWE-1025 (Comparison Using Wrong Factors)
**OWASP:** A03:2021 — Injection

**Issue:**
[Description of the type juggling vulnerability]

**Exploit:**
Input `0` (integer) matches any non-numeric string via loose comparison.

**Code:**
```php
// Vulnerable code with ==
```

**Fix:**
```php
// Fixed with === or hash_equals()
```
```

Related Skills

find-type-issues

59
from dykyi-roman/awesome-claude-code

Detects type issues in PHP code. Finds implicit type coercion, mixed types in comparisons, unsafe casting, type mismatches in returns.

create-prototype

59
from dykyi-roman/awesome-claude-code

Generates Prototype pattern implementations for PHP 8.4. Creates deep/shallow copy, clone customization, prototype registries, and immutable object duplication.

create-health-check

59
from dykyi-roman/awesome-claude-code

Generates Health Check pattern for PHP 8.4. Creates application-level health endpoints with component checkers (Database, Redis, RabbitMQ), status aggregation, and RFC-compliant JSON response. Includes unit tests.

create-docker-healthcheck

59
from dykyi-roman/awesome-claude-code

Generates Docker health check scripts for PHP services. Creates PHP-FPM, Nginx, and custom endpoint health checks.

check-xxe

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for XML External Entity vulnerabilities. Detects unsafe XML parsers, missing entity protection, LIBXML flags issues, XSLT attacks.

check-version-consistency

59
from dykyi-roman/awesome-claude-code

Audits version consistency across project files. Checks composer.json, README, CHANGELOG, docs, and configuration files for version number synchronization.

check-timeout-strategy

59
from dykyi-roman/awesome-claude-code

Audits timeout configuration across HTTP clients, database connections, queue consumers, cache operations, and external service calls. Detects missing or misconfigured timeouts.

check-test-quality

59
from dykyi-roman/awesome-claude-code

Analyzes PHP test code quality. Checks test structure, assertion quality, test isolation, naming conventions, AAA pattern adherence.

check-ssrf

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for SSRF vulnerabilities. Detects unvalidated URLs, internal network access, DNS rebinding, cloud metadata access, URL parsing bypass attempts.

check-sql-injection

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for SQL injection vulnerabilities. Detects query concatenation, ORM misuse, raw queries, dynamic identifiers, prepared statement bypasses.

check-serialization

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for serialization overhead. Detects inefficient JSON encoding, large object hydration, missing JsonSerializable, circular reference issues.

check-sensitive-data

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for sensitive data exposure. Detects plaintext secrets, exposed credentials, PII in logs, insecure storage, hardcoded keys.