|
9617 | 9617 | }
|
9618 | 9618 | /**
|
9619 | 9619 | * Detect the captcha provider based on the element
|
| 9620 | + * @param {Document | HTMLElement} root |
9620 | 9621 | * @param {HTMLElement} element - The element to check
|
9621 | 9622 | * @returns {import('./providers/provider.interface').CaptchaProvider|null}
|
9622 | 9623 | */
|
9623 |
| - detectProvider(element) { |
9624 |
| - return this._getAllProviders().find((provider) => provider.isSupportedForElement(element)) || null; |
| 9624 | + detectProvider(root, element) { |
| 9625 | + return this._getAllProviders().find((provider) => provider.isSupportedForElement(root, element)) || null; |
9625 | 9626 | }
|
9626 | 9627 | /**
|
9627 | 9628 | * Detect the captcha provider based on the root document
|
|
9782 | 9783 | return __privateGet(this, _config).type;
|
9783 | 9784 | }
|
9784 | 9785 | /**
|
| 9786 | + * @param {Document | HTMLElement} _root |
9785 | 9787 | * @param {HTMLElement} captchaContainerElement
|
9786 | 9788 | */
|
9787 |
| - isSupportedForElement(captchaContainerElement) { |
| 9789 | + isSupportedForElement(_root, captchaContainerElement) { |
9788 | 9790 | return !!this._getCaptchaElement(captchaContainerElement);
|
9789 | 9791 | }
|
9790 | 9792 | /**
|
|
9885 | 9887 | return "image";
|
9886 | 9888 | }
|
9887 | 9889 | /**
|
| 9890 | + * @param {Document | HTMLElement} _root |
9888 | 9891 | * @param {HTMLElement} captchaImageElement - The captcha image element
|
9889 | 9892 | */
|
9890 |
| - isSupportedForElement(captchaImageElement) { |
| 9893 | + isSupportedForElement(_root, captchaImageElement) { |
9891 | 9894 | if (!captchaImageElement) {
|
9892 | 9895 | return false;
|
9893 | 9896 | }
|
|
9943 | 9946 | return isElementType(element, "img");
|
9944 | 9947 | }
|
9945 | 9948 |
|
| 9949 | + // src/features/broker-protection/captcha-services/providers/cloudflare-turnstile.js |
| 9950 | + init_define_import_meta_trackerLookup(); |
| 9951 | + |
| 9952 | + // src/features/broker-protection/captcha-services/utils/attribute.js |
| 9953 | + init_define_import_meta_trackerLookup(); |
| 9954 | + function getAttributeValue({ element, attrName }) { |
| 9955 | + if (!element) { |
| 9956 | + throw Error("[getAttributeValue] element parameter is required"); |
| 9957 | + } |
| 9958 | + const attributeValue = element.getAttribute(attrName); |
| 9959 | + if (!attributeValue) { |
| 9960 | + throw Error(`[getAttributeValue] ${attrName} is not defined or has no value`); |
| 9961 | + } |
| 9962 | + return attributeValue; |
| 9963 | + } |
| 9964 | + |
| 9965 | + // src/features/broker-protection/captcha-services/providers/cloudflare-turnstile.js |
| 9966 | + var _config2; |
| 9967 | + var CloudFlareTurnstileProvider = class { |
| 9968 | + constructor() { |
| 9969 | + /** |
| 9970 | + * @type {CloudFlareTurnstileProviderConfig} |
| 9971 | + */ |
| 9972 | + __privateAdd(this, _config2); |
| 9973 | + __privateSet(this, _config2, { |
| 9974 | + providerUrl: "https://challenges.cloudflare.com/turnstile/v0", |
| 9975 | + responseElementName: "cf-turnstile-response" |
| 9976 | + }); |
| 9977 | + } |
| 9978 | + getType() { |
| 9979 | + return "cloudFlareTurnstile"; |
| 9980 | + } |
| 9981 | + /** |
| 9982 | + * @param {Document | HTMLElement} root |
| 9983 | + * @param {HTMLElement} _captchaContainerElement |
| 9984 | + * @returns {boolean} Whether the captcha is supported for the element |
| 9985 | + */ |
| 9986 | + isSupportedForElement(root, _captchaContainerElement) { |
| 9987 | + return !!this._getCaptchaScript(root); |
| 9988 | + } |
| 9989 | + /** |
| 9990 | + * @param {HTMLElement} captchaContainerElement - The element containing the captcha |
| 9991 | + */ |
| 9992 | + getCaptchaIdentifier(captchaContainerElement) { |
| 9993 | + const sitekeyAttribute = "data-sitekey"; |
| 9994 | + return Promise.resolve( |
| 9995 | + safeCallWithError(() => getAttributeValue({ element: captchaContainerElement, attrName: sitekeyAttribute }), { |
| 9996 | + errorMessage: `[CloudFlareTurnstileProvider.getCaptchaIdentifier] could not extract site key from attribute: ${sitekeyAttribute}` |
| 9997 | + }) |
| 9998 | + ); |
| 9999 | + } |
| 10000 | + getSupportingCodeToInject() { |
| 10001 | + return null; |
| 10002 | + } |
| 10003 | + /** |
| 10004 | + * @param {HTMLElement} captchaContainerElement - The element containing the captcha |
| 10005 | + * @returns {boolean} Whether the captcha can be solved |
| 10006 | + */ |
| 10007 | + canSolve(captchaContainerElement) { |
| 10008 | + const callbackAttribute = "data-callback"; |
| 10009 | + const hasCallback = safeCallWithError(() => getAttributeValue({ element: captchaContainerElement, attrName: callbackAttribute }), { |
| 10010 | + errorMessage: `[CloudFlareTurnstileProvider.canSolve] could not extract callback function name from attribute: ${callbackAttribute}` |
| 10011 | + }); |
| 10012 | + if (PirError.isError(hasCallback)) { |
| 10013 | + return false; |
| 10014 | + } |
| 10015 | + const hasResponseElement = safeCallWithError(() => getElementByTagName(captchaContainerElement, __privateGet(this, _config2).responseElementName), { |
| 10016 | + errorMessage: `[CloudFlareTurnstileProvider.canSolve] could not find response element: ${__privateGet(this, _config2).responseElementName}` |
| 10017 | + }); |
| 10018 | + if (PirError.isError(hasResponseElement)) { |
| 10019 | + return false; |
| 10020 | + } |
| 10021 | + return true; |
| 10022 | + } |
| 10023 | + /** |
| 10024 | + * @param {HTMLElement} captchaContainerElement - The element containing the captcha |
| 10025 | + * @param {string} token - The solved captcha token |
| 10026 | + */ |
| 10027 | + injectToken(captchaContainerElement, token) { |
| 10028 | + return injectTokenIntoElement({ captchaContainerElement, elementName: __privateGet(this, _config2).responseElementName, token }); |
| 10029 | + } |
| 10030 | + /** |
| 10031 | + * @param {HTMLElement} captchaContainerElement - The element containing the captcha |
| 10032 | + * @param {string} token - The solved captcha token |
| 10033 | + */ |
| 10034 | + getSolveCallback(captchaContainerElement, token) { |
| 10035 | + const callbackAttribute = "data-callback"; |
| 10036 | + const callbackFunctionName = safeCallWithError( |
| 10037 | + () => getAttributeValue({ element: captchaContainerElement, attrName: callbackAttribute }), |
| 10038 | + { |
| 10039 | + errorMessage: `[CloudFlareTurnstileProvider.getSolveCallback] could not extract callback function name from attribute: ${callbackAttribute}` |
| 10040 | + } |
| 10041 | + ); |
| 10042 | + if (PirError.isError(callbackFunctionName)) { |
| 10043 | + return callbackFunctionName; |
| 10044 | + } |
| 10045 | + return stringifyFunction({ |
| 10046 | + /** |
| 10047 | + * @param {Object} args - The arguments passed to the function |
| 10048 | + * @param {string} args.callbackFunctionName - The callback function name |
| 10049 | + * @param {string} args.token - The solved captcha token |
| 10050 | + */ |
| 10051 | + functionBody: function cloudflareCaptchaCallback(args) { |
| 10052 | + window[args.callbackFunctionName](args.token); |
| 10053 | + }, |
| 10054 | + functionName: "cloudflareCaptchaCallback", |
| 10055 | + args: { callbackFunctionName, token } |
| 10056 | + }); |
| 10057 | + } |
| 10058 | + /** |
| 10059 | + * @private |
| 10060 | + * @param {Document | HTMLElement} root - The root element to search in |
| 10061 | + */ |
| 10062 | + _getCaptchaScript(root) { |
| 10063 | + return getElementWithSrcStart(root, __privateGet(this, _config2).providerUrl); |
| 10064 | + } |
| 10065 | + }; |
| 10066 | + _config2 = new WeakMap(); |
| 10067 | + |
9946 | 10068 | // src/features/broker-protection/captcha-services/providers/registry.js
|
9947 | 10069 | var captchaFactory = new CaptchaFactory();
|
9948 | 10070 | captchaFactory.registerProvider(
|
|
9959 | 10081 | responseElementName: "g-recaptcha-response"
|
9960 | 10082 | })
|
9961 | 10083 | );
|
| 10084 | + captchaFactory.registerProvider(new CloudFlareTurnstileProvider()); |
9962 | 10085 | captchaFactory.registerProvider(new ImageProvider());
|
9963 | 10086 |
|
9964 | 10087 | // src/features/broker-protection/captcha-services/get-captcha-provider.js
|
9965 |
| - function getCaptchaProvider(captchaContainer, captchaType) { |
| 10088 | + function getCaptchaProvider(root, captchaContainer, captchaType) { |
9966 | 10089 | const captchaProvider = captchaFactory.getProviderByType(captchaType);
|
9967 | 10090 | if (!captchaProvider) {
|
9968 | 10091 | return PirError.create(`[getCaptchaProvider] could not find captcha provider with type ${captchaType}`);
|
9969 | 10092 | }
|
9970 |
| - if (captchaProvider.isSupportedForElement(captchaContainer)) { |
| 10093 | + if (captchaProvider.isSupportedForElement(root, captchaContainer)) { |
9971 | 10094 | return captchaProvider;
|
9972 | 10095 | }
|
9973 |
| - const detectedProvider = captchaFactory.detectProvider(captchaContainer); |
| 10096 | + const detectedProvider = captchaFactory.detectProvider(root, captchaContainer); |
9974 | 10097 | if (!detectedProvider) {
|
9975 | 10098 | return PirError.create(
|
9976 | 10099 | `[getCaptchaProvider] could not detect captcha provider for ${captchaType} captcha and element ${captchaContainer}`
|
@@ -10118,14 +10241,17 @@
|
10118 | 10241 | if (PirError.isError(captchaContainer)) {
|
10119 | 10242 | return createError(captchaContainer.error.message);
|
10120 | 10243 | }
|
10121 |
| - const captchaProvider = getCaptchaProvider(captchaContainer, captchaType); |
| 10244 | + const captchaProvider = getCaptchaProvider(root, captchaContainer, captchaType); |
10122 | 10245 | if (PirError.isError(captchaProvider)) {
|
10123 | 10246 | return createError(captchaProvider.error.message);
|
10124 | 10247 | }
|
10125 | 10248 | const captchaIdentifier = await captchaProvider.getCaptchaIdentifier(captchaContainer);
|
10126 | 10249 | if (!captchaIdentifier) {
|
10127 | 10250 | return createError(`could not extract captcha identifier from the container with selector ${selector}`);
|
10128 | 10251 | }
|
| 10252 | + if (PirError.isError(captchaIdentifier)) { |
| 10253 | + return createError(captchaIdentifier.error.message); |
| 10254 | + } |
10129 | 10255 | const response = {
|
10130 | 10256 | url: removeUrlQueryParams(window.location.href),
|
10131 | 10257 | // query params (which may include PII)
|
|
0 commit comments