[vendor]
name = "hcaptcha"
display_name = "hCaptcha"
captcha_type = "hcaptcha"
api_url = "https://js.hcaptcha.com/1/api.js"
public_test_sitekeys = [
{ kind = "autopass", key = "10000000-ffff-ffff-ffff-000000000001" },
{ kind = "block", key = "20000000-ffff-ffff-ffff-000000000002" },
]
[positives]
sample_tokens = [
"P0_eyJhbGciOiJIUzUxMiJ9.eyJ0eXAiOiJDIiwiZXhwIjo5OTk5OTk5OTk5fQ.aB3xY7zQ9mK2wL5jH8nR4pT6vC1dF0gN-8eXqM7lJ4kS",
"P1_eyJhbGciOiJSUzI1NiJ9.zM7nQ4kL1jH8pR5tV2wY9xC6dF3gN0eM7lJ4kS1aZbY8cV5uI2oP9rQ.6tW3nB0mE7sD4xL1kJ8hG5fR2qV9yU6cP3aB0eX",
]
[negatives]
decoy_tokens = [
"",
"DUMMY",
"ok",
"X",
"TEST_FAKE",
]
[evasions]
inputs = [
{ token = "P0_", classify = "decoy" },
{ token = "P1_<script>", classify = "decoy" },
{ token = "P0_eyJhbGciOiJIUzUxMiJ9.eyJ0eXAiOiJDIiwiZXhwIjo5OTk5OTk5OTk5fQ.aB3xY7zQ9mK2wL5jH8nR4pT6vC1dF0gN-8eXqM7lJ4kS", classify = "plausible" },
]
[cross_file]
scenario = "token_capture_and_replay"
[cve_replay]
cases = [
{ id = "HCAPTCHA-EMPTY-PASS-2024", description = "Empty token claimed as success", token = "", expect = "decoy" },
{ id = "HCAPTCHA-NOPREFIX-2024", description = "JWT-shaped but missing P0_/P1_ prefix", token = "eyJhbGciOiJIUzUxMiJ9.eyJ0eXAiOiJDIn0.aaaaaaaaaaaa", expect = "decoy" },
]
[property]
min_length_for_plausible = 30
runs = 10000
[differential]
agreement_target = 0.95
[[differential.consensus]]
token = "P0_eyJhbGciOiJIUzUxMiJ9.aB3xY7zQ9mK2wL5jH8nR4pT6vC1dF0gN-8eXqM7lJ4kS.zM7nQ4kL1jH8pR5tV2wY9xC6dF"
verdict = "plausible"
[[differential.consensus]]
token = "ok"
verdict = "decoy"
[perf]
budget_us_per_classify = 10.0
budget_us_per_solve_path = 500000.0
[scale]
batch_size = 100000
budget_seconds = 5.0
[e2e_cli]
detector_class = "HCaptcha"
expected_solve_methods = ["BehavioralBypass", "AutoPass", "WaitForToken", "AudioBypass"]
forbidden_methods = []