captchaforge 0.2.20

Automatic CAPTCHA detection and multi-strategy solving for chromiumoxide-driven headless browsers (Cloudflare Turnstile, reCAPTCHA v2/v3, hCaptcha, image grids, audio, sliders).
Documentation
# Community-contributed captcha provider rules.
#
# Each [[provider]] entry adds a TOML-driven detector to the registry.
# See `captchaforge::detect::rules` for the full schema. Detection is
# OR across triggers; first match wins.
#
# Priority guidance — interleave with built-ins:
#     5    challenge_page (CF interstitial — built-in)
#    10    turnstile (built-in)
#    11–19 reserved for vendor-specific overrides
#    20    recaptcha (built-in)
#    30    hcaptcha (built-in)
#    40    image_captcha (built-in)
#    50    audio_captcha (built-in)
#    60    pow_captcha (built-in)
#    65    canvas_captcha (built-in)
#    75    slider_captcha (built-in)
#    85    multi_step (built-in)
#    90    shadow_dom (built-in)
#    100+  catch-alls / community vendors
#
# When you ship a new provider, give it a priority that runs BEFORE the
# generic detector that would otherwise catch it (e.g. Geetest v4 must
# precede the generic slider_captcha at 75).

[[provider]]
name = "datadome"
priority = 11

[provider.triggers]
selectors = ["[data-datadome]", ".datadome-captcha", "iframe[src*='captcha-delivery.com']"]
window_globals = ["DataDomeOptions", "ddjskey"]
script_src_contains = ["js.datadome.co", "captcha-delivery.com"]


[[provider]]
name = "akamai_bot_manager"
priority = 12

[provider.triggers]
selectors = ["[data-akamai-bot]", "iframe[src*='akamaihd.net/captcha']"]
window_globals = ["bmak", "_abck"]
script_src_contains = ["akamaihd.net/captcha", "akam.net/captcha", "akamaiedge.net"]


[[provider]]
name = "perimeterx_human"
priority = 13

# PerimeterX rebranded to HUMAN Security; both old and new signatures
# are kept here so the rule covers fleet that hasn't migrated.
[provider.triggers]
selectors = ["#px-captcha", ".px-captcha-error-container", "[data-px-captcha]"]
window_globals = ["_pxAppId", "PX", "_humanSecurity"]
script_src_contains = ["client.px-cdn.net", "captcha.px-cdn.net", "human-security.com/captcha"]


[[provider]]
name = "arkose_funcaptcha"
priority = 14

# Arkose Labs (formerly FunCaptcha) — gamified rotation/orientation
# challenges. Detection signatures cover both branding eras.
[provider.triggers]
selectors = ["[data-pkey]", "#funcaptcha", ".arkose-iframe", "iframe[src*='arkoselabs.com']"]
window_globals = ["ArkoseEnforcement", "FunCaptcha"]
script_src_contains = ["client-api.arkoselabs.com", "funcaptcha.com"]


[[provider]]
name = "geetest_v4"
priority = 15

# Geetest v4 — slider/click puzzle. Must run BEFORE the generic
# slider_captcha (priority 75), otherwise the generic detector wins.
[provider.triggers]
selectors = [".geetest_box", ".geetest_panel", "[data-gt]"]
window_globals = ["initGeetest4", "Geetest"]
script_src_contains = ["static.geetest.com/v4", "static.geetest.com/static/js/gt4"]


[[provider]]
name = "geetest_v3"
priority = 16

# Geetest v3 (legacy slider). Distinct selectors from v4 so they
# don't false-match each other.
[provider.triggers]
selectors = [".geetest_holder", ".geetest_radar_btn", ".geetest_slider_button"]
window_globals = ["initGeetest"]
script_src_contains = ["static.geetest.com/static/tools/api.js", "geetest.com/v3"]


[[provider]]
name = "aws_waf_captcha"
priority = 17

# AWS WAF Captcha — visible visual challenge issued by AWS WAF
# protections. Distinct from the AWS Bot Control silent challenge,
# which has no captcha widget to solve.
[provider.triggers]
selectors = ["#captcha-container", "[data-aws-waf-captcha]", "iframe[src*='captcha.awswaf.com']"]
window_globals = ["AwsWafIntegration", "AwsWafCaptcha"]
script_src_contains = ["captcha.awswaf.com", "captcha-prod.awswaf.com"]


[[provider]]
name = "friendly_captcha"
priority = 18

# Friendly Captcha — privacy-preserving PoW. Distinct from generic
# pow_captcha (priority 60) so per-vendor routing is possible.
[provider.triggers]
selectors = [".frc-captcha", "[data-sitekey][data-puzzle-endpoint]"]
script_src_contains = ["cdn.jsdelivr.net/npm/friendly-challenge", "friendlycaptcha.com"]


[[provider]]
name = "mtcaptcha"
priority = 19

# MTCaptcha — accessibility-focused visual + audio captcha.
[provider.triggers]
selectors = [".mtcaptcha-container", "#mtcaptcha", ".mtcaptcha-widget"]
window_globals = ["mtcaptcha", "mtcaptchaConfig"]
script_src_contains = ["service.mtcaptcha.com", "service2.mtcaptcha.com"]


[[provider]]
name = "wp_math_captcha"
priority = 100

# Generic WordPress math captcha — common on contact forms.
# Runs LATE so per-vendor detectors win when both match.
[provider.triggers]
selectors = [
    "input[name='captcha-result']",
    "input[name='math_captcha']",
    "input[id*='math_captcha']",
    ".math-captcha-question",
    ".cptch_input",   # Captcha by BestWebSoft plugin
]


[[provider]]
name = "imperva_incapsula"
priority = 21
solver_names = ["CloudflareInterstitialSolver", "ThirdPartyCaptchaSolver"]

# Imperva (formerly Incapsula) bot challenge interstitial. Sets the
# `incap_ses_*` and `visid_incap_*` cookies after a JS challenge.
# Fronts many enterprise sites; routes through CloudflareInterstitialSolver
# pattern (passive wait + cookie capture).
[provider.triggers]
selectors = ["iframe[src*='_Incapsula_Resource']", "[id*='incapsula']"]
window_globals = ["_Incapsula_Resource", "incap_ses"]
script_src_contains = ["incapsula.com", "_Incapsula_Resource"]


[[provider]]
name = "kasada"
priority = 22
solver_names = ["WaitForTokenSolver", "CloudflareInterstitialSolver", "ThirdPartyCaptchaSolver"]

# Kasada bot defense — issues x-kpsdk-ct token via challenge JS.
# Production sites: Twitter (X), Canada Goose, Hotels.com, others.
# Passive challenge; with stealth + warmup the JS challenge passes
# and we harvest the resulting cookies.
[provider.triggers]
selectors = ["script[src*='/ips.js']", "[id*='kpsdk']"]
window_globals = ["KPSDK"]
script_src_contains = ["client.px-cloud.net", "/ips.js"]


[[provider]]
name = "f5_distributed_cloud"
priority = 23
solver_names = ["CloudflareInterstitialSolver", "ThirdPartyCaptchaSolver"]

# F5 Distributed Cloud Bot Defense (formerly Shape Security).
# Heavy JS challenge with Akamai-style cookies + token POST.
# Fronts financial sites; passive challenge with stealth.
[provider.triggers]
selectors = ["script[src*='shape']", "[id*='shape']"]
window_globals = ["ShapeBotDefense", "_shape"]
script_src_contains = ["shapesecurity.com", "f5cloud.com", "/shape/"]


[[provider]]
name = "yandex_smartcaptcha"
priority = 24
solver_names = ["SliderCaptchaSolver", "VlmCaptchaSolver", "ThirdPartyCaptchaSolver"]

# Yandex SmartCaptcha — image-grid + slider variants. Common on
# Russian / CIS sites and globally on Yandex services.
[provider.triggers]
selectors = [".smart-captcha", "[data-captcha-id]"]
window_globals = ["smartCaptcha"]
script_src_contains = ["captcha-api.yandex.ru", "smartcaptcha.yandexcloud.net"]


[[provider]]
name = "tencent_captcha"
priority = 25
solver_names = ["SliderCaptchaSolver", "VlmCaptchaSolver", "ThirdPartyCaptchaSolver"]

# Tencent (TCaptcha) — slider + click-image variants. Heavy
# usage across China and APAC. Routes through SliderCaptchaSolver
# for the slider variant; image-grid via VLM.
[provider.triggers]
selectors = ["#tcaptcha_iframe", ".tcaptcha-container", "[data-aid*='tcaptcha']"]
window_globals = ["TencentCaptcha", "TCaptcha"]
script_src_contains = ["t.captcha.qq.com", "ssl.captcha.qq.com"]


[[provider]]
name = "keycaptcha"
priority = 26
solver_names = ["SliderCaptchaSolver", "VlmCaptchaSolver", "ThirdPartyCaptchaSolver"]

# KeyCaptcha — drag-piece-into-slot puzzle. Routes through
# SliderCaptchaSolver with KeyCaptcha-specific selectors.
[provider.triggers]
selectors = ["#capcode", "#keycaptcha", ".keycaptcha"]
window_globals = ["s_s_c_user_id", "k_c_t_v"]
script_src_contains = ["www.keycaptcha.com", "backs.keycaptcha.com"]