/* AutoFirmer — captcha render hook. * * Runs in the page's MAIN world at document_start, before any captcha script * loads. It wraps `turnstile.render` / `grecaptcha.render` / `hcaptcha.render` * and records what the page passed to them on `window.__autofirmerCaptcha`. * * Why bother, when the sitekey is usually a `data-sitekey` attribute: a widget * rendered programmatically puts NOTHING in the DOM — no sitekey, and more * importantly no callback. The callback is the whole point. Filling the hidden * response field with a solved token does not by itself tell the page a token * arrived; the page only reacts when its own callback runs with it. Turnstile in * particular hands that callback straight to render() and never exposes it * anywhere else, so a hook installed before the widget renders is the only way * to hold on to it. * * Reads nothing and sends nothing. It only remembers arguments, in the page's * own world, for background.js to use when a token comes back from 2captcha. */ (() => { const registry = (window.__autofirmerCaptcha = window.__autofirmerCaptcha || { widgets: [] }); /** Wrap `render` on a captcha API object, once. */ function wrap(kind, api) { if (!api || typeof api !== 'object' || api.__autofirmerWrapped) return api; if (typeof api.render === 'function') { const original = api.render.bind(api); api.render = function (container, params) { const widgetId = original(container, params); // `params` holds the sitekey, the callback, and for Turnstile the // action / cData / chlPageData that a solve has to be asked for. registry.widgets.push({ kind, params: params || {}, widgetId }); return widgetId; }; try { Object.defineProperty(api, '__autofirmerWrapped', { value: true }); } catch { /* frozen API object — wrapping still took */ } } // reCAPTCHA Enterprise is a second render() hanging off the same object. if (api.enterprise) wrap(kind, api.enterprise); return api; } /** Wrap whatever the page eventually assigns to `window[name]`. * * The script that defines it loads after this one, so there is nothing to * wrap yet — the accessor waits for the assignment instead. It also catches * the reCAPTCHA pattern of replacing a `{ready}` placeholder later on. */ function intercept(name, kind) { let current = wrap(kind, window[name]); try { Object.defineProperty(window, name, { configurable: true, enumerable: true, get: () => current, set: (value) => { current = wrap(kind, value); }, }); } catch { // The page made the property non-configurable. Detection falls back // to the DOM, which covers every declaratively rendered widget. } } intercept('turnstile', 'turnstile'); intercept('grecaptcha', 'recaptcha'); intercept('hcaptcha', 'hcaptcha'); })();