@prosopo/procaptcha-frictionless 2.12.8 → 2.12.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/procaptcha-frictionless@2.12.8 build:cjs
2
+ > @prosopo/procaptcha-frictionless@2.12.11 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -61,6 +61,6 @@ rendering chunks...
61
61
  dist/cjs/index.cjs 0.24 kB
62
62
  dist/cjs/sessionInvalidatedRecovery.cjs 0.85 kB
63
63
  dist/cjs/detectorLoader.cjs 1.37 kB
64
- dist/cjs/customDetectBot.cjs 3.37 kB
64
+ dist/cjs/customDetectBot.cjs 4.26 kB
65
65
  dist/cjs/ProcaptchaFrictionless.cjs 9.11 kB
66
- ✓ built in 126ms
66
+ ✓ built in 86ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/procaptcha-frictionless@2.12.8 build:tsc
2
+ > @prosopo/procaptcha-frictionless@2.12.11 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 11:42:28 AM - Projects in this build:
5
+ 10:35:14 AM - Projects in this build:
6
6
  * ../../dev/config/tsconfig.json
7
7
  * ../locale/tsconfig.json
8
8
  * ../util/tsconfig.json
@@ -26,49 +26,49 @@
26
26
  * ../procaptcha-react/tsconfig.json
27
27
  * tsconfig.json
28
28
 
29
- 11:42:28 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/vite/NodejsPolarsNativeFilePlugin.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
29
+ 10:35:14 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
30
30
 
31
- 11:42:28 AM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/useTranslation.ts' is older than output '../locale/tsconfig.tsbuildinfo'
31
+ 10:35:14 AM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
32
32
 
33
- 11:42:28 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
33
+ 10:35:14 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
34
34
 
35
- 11:42:28 AM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
35
+ 10:35:14 AM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
36
36
 
37
- 11:42:28 AM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
37
+ 10:35:14 AM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
38
38
 
39
- 11:42:28 AM - Project '../detector/tsconfig.json' is up to date because newest input '../detector/src/index.js' is older than output '../detector/tsconfig.tsbuildinfo'
39
+ 10:35:14 AM - Project '../detector/tsconfig.json' is up to date because newest input '../detector/src/index.js' is older than output '../detector/tsconfig.tsbuildinfo'
40
40
 
41
- 11:42:28 AM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
41
+ 10:35:14 AM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
42
42
 
43
- 11:42:28 AM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
43
+ 10:35:14 AM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
44
44
 
45
- 11:42:28 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
45
+ 10:35:14 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/error.ts' is older than output '../common/tsconfig.tsbuildinfo'
46
46
 
47
- 11:42:28 AM - Project '../fingerprintjs/tsconfig.json' is up to date because newest input '../fingerprintjs/src/utils/browser.ts' is older than output '../fingerprintjs/tsconfig.tsbuildinfo'
47
+ 10:35:14 AM - Project '../fingerprintjs/tsconfig.json' is up to date because newest input '../fingerprintjs/src/utils/browser.ts' is older than output '../fingerprintjs/tsconfig.tsbuildinfo'
48
48
 
49
- 11:42:28 AM - Project '../fingerprint/tsconfig.json' is up to date because newest input '../fingerprint/src/index.ts' is older than output '../fingerprint/tsconfig.tsbuildinfo'
49
+ 10:35:14 AM - Project '../fingerprint/tsconfig.json' is up to date because newest input '../fingerprint/src/index.ts' is older than output '../fingerprint/tsconfig.tsbuildinfo'
50
50
 
51
- 11:42:28 AM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/balancer.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
51
+ 10:35:14 AM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/balancer.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
52
52
 
53
- 11:42:28 AM - Project '../widget-skeleton/tsconfig.json' is up to date because newest input '../widget-skeleton/src/constants.ts' is older than output '../widget-skeleton/tsconfig.tsbuildinfo'
53
+ 10:35:15 AM - Project '../widget-skeleton/tsconfig.json' is up to date because newest input '../widget-skeleton/src/constants.ts' is older than output '../widget-skeleton/tsconfig.tsbuildinfo'
54
54
 
55
- 11:42:28 AM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
55
+ 10:35:15 AM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
56
56
 
57
- 11:42:28 AM - Project '../account/tsconfig.json' is up to date because newest input '../account/src/custom.d.ts' is older than output '../account/tsconfig.tsbuildinfo'
57
+ 10:35:15 AM - Project '../account/tsconfig.json' is up to date because newest input '../account/src/workers/cryptoWorker.ts' is older than output '../account/tsconfig.tsbuildinfo'
58
58
 
59
- 11:42:28 AM - Project '../procaptcha-common/tsconfig.json' is up to date because newest input '../procaptcha-common/src/reactComponents/Honeypot.tsx' is older than output '../procaptcha-common/tsconfig.tsbuildinfo'
59
+ 10:35:15 AM - Project '../procaptcha-common/tsconfig.json' is up to date because newest input '../procaptcha-common/src/tests/defaultEvents.test.ts' is older than output '../procaptcha-common/tsconfig.tsbuildinfo'
60
60
 
61
- 11:42:28 AM - Project '../procaptcha-pow/tsconfig.json' is up to date because newest input '../procaptcha-pow/src/index.ts' is older than output '../procaptcha-pow/tsconfig.tsbuildinfo'
61
+ 10:35:15 AM - Project '../procaptcha-pow/tsconfig.json' is up to date because newest input '../procaptcha-pow/src/index.ts' is older than output '../procaptcha-pow/tsconfig.tsbuildinfo'
62
62
 
63
- 11:42:29 AM - Project '../procaptcha-puzzle/tsconfig.json' is up to date because newest input '../procaptcha-puzzle/src/index.ts' is older than output '../procaptcha-puzzle/tsconfig.tsbuildinfo'
63
+ 10:35:15 AM - Project '../procaptcha-puzzle/tsconfig.json' is up to date because newest input '../procaptcha-puzzle/src/index.ts' is older than output '../procaptcha-puzzle/tsconfig.tsbuildinfo'
64
64
 
65
- 11:42:29 AM - Project '../datasets/tsconfig.json' is up to date because newest input '../datasets/src/index.ts' is older than output '../datasets/tsconfig.tsbuildinfo'
65
+ 10:35:15 AM - Project '../datasets/tsconfig.json' is up to date because newest input '../datasets/src/index.ts' is older than output '../datasets/tsconfig.tsbuildinfo'
66
66
 
67
- 11:42:29 AM - Project '../procaptcha/tsconfig.json' is up to date because newest input '../procaptcha/src/index.ts' is older than output '../procaptcha/tsconfig.tsbuildinfo'
67
+ 10:35:15 AM - Project '../procaptcha/tsconfig.json' is up to date because newest input '../procaptcha/src/index.ts' is older than output '../procaptcha/tsconfig.tsbuildinfo'
68
68
 
69
- 11:42:29 AM - Project '../procaptcha-react/tsconfig.json' is up to date because newest input '../procaptcha-react/src/index.ts' is older than output '../procaptcha-react/tsconfig.tsbuildinfo'
69
+ 10:35:15 AM - Project '../procaptcha-react/tsconfig.json' is up to date because newest input '../procaptcha-react/src/index.ts' is older than output '../procaptcha-react/tsconfig.tsbuildinfo'
70
70
 
71
- 11:42:29 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
71
+ 10:35:15 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
72
72
 
73
- 11:42:29 AM - Building project '/home/runner/work/captcha/captcha/packages/procaptcha-frictionless/tsconfig.json'...
73
+ 10:35:15 AM - Building project '/home/runner/work/captcha/captcha/packages/procaptcha-frictionless/tsconfig.json'...
74
74
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/procaptcha-frictionless@2.12.8 build
2
+ > @prosopo/procaptcha-frictionless@2.12.11 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/procaptcha-frictionless@2.12.8 build:cross-env
6
+ > @prosopo/procaptcha-frictionless@2.12.11 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -64,6 +64,6 @@ rendering chunks...
64
64
  dist/index.js 0.11 kB
65
65
  dist/detectorLoader.js 0.11 kB
66
66
  dist/sessionInvalidatedRecovery.js 0.71 kB
67
- dist/customDetectBot.js 3.19 kB
67
+ dist/customDetectBot.js 4.08 kB
68
68
  dist/ProcaptchaFrictionless.js 7.67 kB
69
- ✓ built in 188ms
69
+ ✓ built in 146ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # @prosopo/procaptcha-frictionless
2
2
 
3
+ ## 2.12.11
4
+ ### Patch Changes
5
+
6
+ - 85e8857: Record both the top-frame URL and the widget's own iframe URL on frictionless sessions.
7
+
8
+ Previously the client only sent one field (`currentUrl`), which for embedded widgets resolved to the top-frame URL — so we lost visibility into which iframe endpoint the session was actually loaded through. Now the client sends both:
9
+
10
+ - `currentUrl`: the top-frame URL (same resolution rules as before — same-origin iframes read `window.top.location.href` directly; cross-origin iframes fall back to `document.referrer`).
11
+ - `iframeUrl`: the widget's own frame URL when embedded. Undefined when the widget IS the top frame (nothing to distinguish).
12
+
13
+ Both fields are sanitised client- and server-side (origin + path only; query string, fragment and any embedded credentials stripped). The provider persists both on the `Session` record and re-uses them on post-PoW escalation sessions. Only `currentUrl` is gated in the frictionless decision machine (unchanged — missing `currentUrl` still forces an image captcha); `iframeUrl` is recorded for analytics.
14
+
15
+ Both fields are also surfaced to the decision machines as raw signals: `RoutingMachineRawSignals` gains an optional `iframeUrl` populated from the freshly decrypted frictionless payload on the `route` phase, from the persisted Session record on the `postPow` phase, and from the cached Session in the dedup replay path — matching how `currentUrl` is already threaded through.
16
+
17
+ Additionally, sessions carry a new computed boolean `isProtect`, set at session-creation time when the widget iframe was served from `protect.<tenant>` and embedded in a page on the same tenant (subdomain-of matching, dot-boundary safe — see `isProtectDeployment` in `@prosopo/util`). Persisted only when true (same pattern as `isEscalation`) and backed by a sparse `{isProtect, createdAt}` index so analytics can cheaply retrieve Protect sessions without re-parsing URLs. Post-PoW escalation sessions inherit the flag from the origin session.
18
+ - Updated dependencies [85e8857]
19
+ - @prosopo/api@3.5.15
20
+ - @prosopo/types@4.9.8
21
+ - @prosopo/procaptcha-pow@2.10.15
22
+ - @prosopo/procaptcha-puzzle@2.10.30
23
+ - @prosopo/common@3.1.44
24
+ - @prosopo/detector@3.5.9
25
+ - @prosopo/procaptcha-common@2.11.9
26
+ - @prosopo/procaptcha-react@2.9.88
27
+
28
+ ## 2.12.10
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies [8bde5df]
32
+ - @prosopo/types@4.9.7
33
+ - @prosopo/api@3.5.14
34
+ - @prosopo/detector@3.5.8
35
+ - @prosopo/procaptcha-common@2.11.8
36
+ - @prosopo/procaptcha-pow@2.10.14
37
+ - @prosopo/procaptcha-puzzle@2.10.29
38
+ - @prosopo/procaptcha-react@2.9.87
39
+
40
+ ## 2.12.9
41
+ ### Patch Changes
42
+
43
+ - 35d2784: Report the top-frame URL as `currentUrl` when the widget runs inside an iframe.
44
+
45
+ Previously the frictionless client always sent `window.location.origin + pathname`, which is the iframe's own URL — so every session loaded through Protect's site-wide iframe (`protect.<tenant>.live/...`) reported the same widget endpoint regardless of which page the user was actually on. Downstream detectors (HEAD_HASH_OUTLIER's proxy-pool signal in particular) then saw diverse-geography traffic on one URL+UA fingerprint and treated our own iframe as a bot cluster.
46
+
47
+ Resolution order for `currentUrl`:
48
+ 1. Top window → local `location.href` (widget is the top frame).
49
+ 2. Same-origin iframe → `window.top.location.href`.
50
+ 3. Cross-origin iframe → `document.referrer` (browser fills it subject to Referrer-Policy).
51
+ 4. Fallback → the iframe's own `location.href` so the field is never empty.
52
+
53
+ Origin+path sanitisation is preserved across all paths — the query string, fragment and any embedded credentials are still stripped before the URL leaves the browser.
54
+
3
55
  ## 2.12.8
4
56
  ### Patch Changes
5
57
 
@@ -4,15 +4,38 @@ const api = require("@prosopo/api");
4
4
  const common = require("@prosopo/common");
5
5
  const procaptchaCommon = require("@prosopo/procaptcha-common");
6
6
  const detectorLoader = require("./detectorLoader.cjs");
7
- const getCurrentPageUrl = () => {
8
- if (typeof window === "undefined" || !window.location) {
7
+ const sanitiseHref = (href) => {
8
+ try {
9
+ const u = new URL(href);
10
+ if (!u.origin || u.origin === "null") return void 0;
11
+ return `${u.origin}${u.pathname || ""}`;
12
+ } catch {
9
13
  return void 0;
10
14
  }
11
- const { origin, pathname } = window.location;
12
- if (!origin || origin === "null") {
13
- return void 0;
15
+ };
16
+ const getCurrentPageUrls = () => {
17
+ if (typeof window === "undefined" || !window.location) {
18
+ return { currentUrl: void 0, iframeUrl: void 0 };
19
+ }
20
+ const local = (() => {
21
+ const { origin, pathname } = window.location;
22
+ if (!origin || origin === "null") return void 0;
23
+ return `${origin}${pathname || ""}`;
24
+ })();
25
+ if (window === window.top) {
26
+ return { currentUrl: local, iframeUrl: void 0 };
27
+ }
28
+ try {
29
+ const topHref = window.top?.location?.href;
30
+ const sanitised = topHref ? sanitiseHref(topHref) : void 0;
31
+ if (sanitised) return { currentUrl: sanitised, iframeUrl: local };
32
+ } catch {
33
+ }
34
+ if (typeof document !== "undefined" && document.referrer) {
35
+ const fromReferrer = sanitiseHref(document.referrer);
36
+ if (fromReferrer) return { currentUrl: fromReferrer, iframeUrl: local };
14
37
  }
15
- return `${origin}${pathname || ""}`;
38
+ return { currentUrl: local, iframeUrl: local };
16
39
  };
17
40
  const withTimeout = async (promise, ms) => {
18
41
  let timeoutId;
@@ -60,6 +83,7 @@ const customDetectBot = async (config, container, restartFn, retryContext) => {
60
83
  provider.provider.url,
61
84
  config.account.address
62
85
  );
86
+ const { currentUrl, iframeUrl } = getCurrentPageUrls();
63
87
  const captchaPromise = providerApi.getFrictionlessCaptcha(
64
88
  detectionResult.token,
65
89
  detectionResult.encryptHeadHash,
@@ -67,7 +91,8 @@ const customDetectBot = async (config, container, restartFn, retryContext) => {
67
91
  userAccount.account.address,
68
92
  config.mode,
69
93
  void 0,
70
- getCurrentPageUrl()
94
+ currentUrl,
95
+ iframeUrl
71
96
  );
72
97
  if (detectionResult.getSimdReadings) {
73
98
  void detectionResult.getSimdReadings(6e4).catch(() => void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"customDetectBot.d.ts","sourceRoot":"","sources":["../src/customDetectBot.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACX,oBAAoB,EAGpB,MAAM,gBAAgB,CAAC;AAsBxB,eAAO,MAAM,WAAW,GAAU,CAAC,WACzB,OAAO,CAAC,CAAC,CAAC,MACf,MAAM,KACR,OAAO,CAAC,CAAC,CAoBX,CAAC;AAEF,QAAA,MAAM,eAAe,EAAE,oBAwGtB,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"customDetectBot.d.ts","sourceRoot":"","sources":["../src/customDetectBot.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACX,oBAAoB,EAGpB,MAAM,gBAAgB,CAAC;AA2ExB,eAAO,MAAM,WAAW,GAAU,CAAC,WACzB,OAAO,CAAC,CAAC,CAAC,MACf,MAAM,KACR,OAAO,CAAC,CAAC,CAoBX,CAAC;AAEF,QAAA,MAAM,eAAe,EAAE,oBA0GtB,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -2,15 +2,38 @@ import { ProviderApi } from "@prosopo/api";
2
2
  import { ProsopoEnvError } from "@prosopo/common";
3
3
  import { ExtensionLoader, getProcaptchaRandomActiveProvider, pickIpMode } from "@prosopo/procaptcha-common";
4
4
  import { DetectorLoader } from "./detectorLoader.js";
5
- const getCurrentPageUrl = () => {
6
- if (typeof window === "undefined" || !window.location) {
5
+ const sanitiseHref = (href) => {
6
+ try {
7
+ const u = new URL(href);
8
+ if (!u.origin || u.origin === "null") return void 0;
9
+ return `${u.origin}${u.pathname || ""}`;
10
+ } catch {
7
11
  return void 0;
8
12
  }
9
- const { origin, pathname } = window.location;
10
- if (!origin || origin === "null") {
11
- return void 0;
13
+ };
14
+ const getCurrentPageUrls = () => {
15
+ if (typeof window === "undefined" || !window.location) {
16
+ return { currentUrl: void 0, iframeUrl: void 0 };
17
+ }
18
+ const local = (() => {
19
+ const { origin, pathname } = window.location;
20
+ if (!origin || origin === "null") return void 0;
21
+ return `${origin}${pathname || ""}`;
22
+ })();
23
+ if (window === window.top) {
24
+ return { currentUrl: local, iframeUrl: void 0 };
25
+ }
26
+ try {
27
+ const topHref = window.top?.location?.href;
28
+ const sanitised = topHref ? sanitiseHref(topHref) : void 0;
29
+ if (sanitised) return { currentUrl: sanitised, iframeUrl: local };
30
+ } catch {
31
+ }
32
+ if (typeof document !== "undefined" && document.referrer) {
33
+ const fromReferrer = sanitiseHref(document.referrer);
34
+ if (fromReferrer) return { currentUrl: fromReferrer, iframeUrl: local };
12
35
  }
13
- return `${origin}${pathname || ""}`;
36
+ return { currentUrl: local, iframeUrl: local };
14
37
  };
15
38
  const withTimeout = async (promise, ms) => {
16
39
  let timeoutId;
@@ -58,6 +81,7 @@ const customDetectBot = async (config, container, restartFn, retryContext) => {
58
81
  provider.provider.url,
59
82
  config.account.address
60
83
  );
84
+ const { currentUrl, iframeUrl } = getCurrentPageUrls();
61
85
  const captchaPromise = providerApi.getFrictionlessCaptcha(
62
86
  detectionResult.token,
63
87
  detectionResult.encryptHeadHash,
@@ -65,7 +89,8 @@ const customDetectBot = async (config, container, restartFn, retryContext) => {
65
89
  userAccount.account.address,
66
90
  config.mode,
67
91
  void 0,
68
- getCurrentPageUrl()
92
+ currentUrl,
93
+ iframeUrl
69
94
  );
70
95
  if (detectionResult.getSimdReadings) {
71
96
  void detectionResult.getSimdReadings(6e4).catch(() => void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"customDetectBot.js","sourceRoot":"","sources":["../src/customDetectBot.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACN,eAAe,EACf,iCAAiC,EACjC,UAAU,GACV,MAAM,4BAA4B,CAAC;AAOpC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AASrD,MAAM,iBAAiB,GAAG,GAAuB,EAAE;IAClD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACvD,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC7C,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,MAAM,GAAG,QAAQ,IAAI,EAAE,EAAE,CAAC;AACrC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAC/B,OAAmB,EACnB,EAAU,EACG,EAAE;IACf,IAAI,SAAqC,CAAC;IAC1C,MAAM,cAAc,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QACvD,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC3B,MAAM,CAAC,IAAI,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC;QAC5C,CAAC,EAAE,EAAE,CAAC,CAAC;IACR,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;QAC7D,IAAI,SAAS,EAAE,CAAC;YACf,YAAY,CAAC,SAAS,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,SAAS,EAAE,CAAC;YACf,YAAY,CAAC,SAAS,CAAC,CAAC;QACzB,CAAC;QACD,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,eAAe,GAAyB,KAAK,EAClD,MAAoC,EACpC,SAAkC,EAClC,SAAqB,EACrB,YAAyC,EACH,EAAE;IACxC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC5C,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC;QAC5B,cAAc,EAAE;KAChB,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAE,CAAC;IAO3B,MAAM,eAAe,GAAG,MAAM,MAAM,CACnC,MAAM,CAAC,kBAAkB,EACzB,KAAK,IAAI,EAAE,CAAC,iCAAiC,CAAC,MAAM,CAAC,kBAAkB,CAAC,EACxE,SAAS,EACT,SAAS,EACT,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAC5B,CAAC;IAEF,MAAM,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC;IAEhD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,eAAe,CAAC,0BAA0B,CAAC,CAAC;IACvD,CAAC;IASD,MAAM,QAAQ,GAAG,MAAM,iCAAiC,CACvD,MAAM,CAAC,kBAAkB,EACzB,UAAU,CAAC,MAAM,CAAC,EAClB,YAAY,CACZ,CAAC;IAEF,MAAM,WAAW,GAAG,IAAI,WAAW,CAClC,QAAQ,CAAC,QAAQ,CAAC,GAAG,EACrB,MAAM,CAAC,OAAO,CAAC,OAAO,CACtB,CAAC;IAQF,MAAM,cAAc,GAAG,WAAW,CAAC,sBAAsB,CACxD,eAAe,CAAC,KAAK,EACrB,eAAe,CAAC,eAAe,EAC/B,MAAM,CAAC,OAAO,CAAC,OAAO,EACtB,WAAW,CAAC,OAAO,CAAC,OAAO,EAC3B,MAAM,CAAC,IAAI,EACX,SAAS,EACT,iBAAiB,EAAE,CACnB,CAAC;IACF,IAAI,eAAe,CAAC,eAAe,EAAE,CAAC;QAGrC,KAAK,eAAe,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IAIzD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,IAAI,CAAC;YACJ,KAAK,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE;gBAC3B,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,SAAS;gBACf,WAAW,EAAE,MAAM;gBACnB,SAAS,EAAE,IAAI;gBACf,KAAK,EAAE,UAAU;aACjB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;QAET,CAAC;IACF,CAAC;IAED,OAAO;QACN,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,WAAW,EAAE,WAAW;QACxB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,EAAE,EAAE,OAAO,CAAC,EAAE;QAEd,kBAAkB,EAAE,eAAe,CAAC,YAAY;QAChD,kBAAkB,EAAE,eAAe,CAAC,YAAY;QAChD,kBAAkB,EAAE,eAAe,CAAC,YAAY;QAChD,gBAAgB,EAAE,eAAe,CAAC,eAAe;QACjD,qBAAqB,EAAE,eAAe,CAAC,qBAAqB;QAC5D,kBAAkB,EAAE,eAAe,CAAC,kBAAkB;QACtD,eAAe,EAAE,eAAe,CAAC,eAAe;KAChD,CAAC;AACH,CAAC,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"customDetectBot.js","sourceRoot":"","sources":["../src/customDetectBot.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/procaptcha-frictionless",
3
- "version": "2.12.8",
3
+ "version": "2.12.11",
4
4
  "author": "PROSOPO LIMITED <info@prosopo.io>",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -30,15 +30,15 @@
30
30
  },
31
31
  "browserslist": ["> 0.5%, last 2 versions, not dead"],
32
32
  "dependencies": {
33
- "@prosopo/api": "3.5.13",
34
- "@prosopo/common": "3.1.43",
35
- "@prosopo/detector": "3.5.7",
33
+ "@prosopo/api": "3.5.15",
34
+ "@prosopo/common": "3.1.44",
35
+ "@prosopo/detector": "3.5.9",
36
36
  "@prosopo/locale": "3.2.6",
37
- "@prosopo/procaptcha-common": "2.11.7",
38
- "@prosopo/procaptcha-pow": "2.10.13",
39
- "@prosopo/procaptcha-puzzle": "2.10.28",
40
- "@prosopo/procaptcha-react": "2.9.86",
41
- "@prosopo/types": "4.9.6",
37
+ "@prosopo/procaptcha-common": "2.11.9",
38
+ "@prosopo/procaptcha-pow": "2.10.15",
39
+ "@prosopo/procaptcha-puzzle": "2.10.30",
40
+ "@prosopo/procaptcha-react": "2.9.88",
41
+ "@prosopo/types": "4.9.8",
42
42
  "@prosopo/widget-skeleton": "2.8.4",
43
43
  "dotenv": "16.4.5",
44
44
  "react": "18.3.1"
@@ -27,22 +27,75 @@ import type {
27
27
  import type { BotDetectionFunctionResult } from "@prosopo/types";
28
28
  import { DetectorLoader } from "./detectorLoader.js";
29
29
 
30
- // The page the widget is rendered on, reduced to origin + path. Deliberately
31
- // built from `origin` + `pathname` (never `href`) so the query string,
32
- // fragment, and any embedded `user:pass@` credentials never leave the browser
33
- // — sites routinely carry tokens / session ids / reset codes in those parts.
34
- // Returns undefined in non-browser contexts (SSR / tests) or for opaque
35
- // origins; the provider then treats the session as having no page URL and
36
- // forces an image captcha.
37
- const getCurrentPageUrl = (): string | undefined => {
38
- if (typeof window === "undefined" || !window.location) {
30
+ // The page(s) the widget is rendered on, reduced to origin + path.
31
+ // Deliberately built from `origin` + `pathname` (never `href`) so the query
32
+ // string, fragment, and any embedded `user:pass@` credentials never leave the
33
+ // browser — sites routinely carry tokens / session ids / reset codes in those
34
+ // parts.
35
+ //
36
+ // Returns two fields:
37
+ // - `currentUrl`: the top-frame URL as best we can determine it. Same-origin
38
+ // iframes read it directly; cross-origin iframes fall back to
39
+ // `document.referrer`, which the browser fills with the embedding page URL
40
+ // subject to Referrer-Policy. When neither is available the field falls
41
+ // back to the local (iframe) URL so the provider still sees a value.
42
+ // - `iframeUrl`: the widget's own frame URL when we're embedded. Undefined
43
+ // when the widget IS the top frame — nothing to distinguish. Emitted so
44
+ // downstream analytics can separate "Protect's site-wide iframe endpoint"
45
+ // from "the page the user was actually on".
46
+ //
47
+ // Both fields are undefined in non-browser contexts (SSR / tests) or for
48
+ // opaque origins.
49
+ const sanitiseHref = (href: string): string | undefined => {
50
+ try {
51
+ const u = new URL(href);
52
+ if (!u.origin || u.origin === "null") return undefined;
53
+ return `${u.origin}${u.pathname || ""}`;
54
+ } catch {
39
55
  return undefined;
40
56
  }
41
- const { origin, pathname } = window.location;
42
- if (!origin || origin === "null") {
43
- return undefined;
57
+ };
58
+
59
+ type PageUrls = {
60
+ currentUrl: string | undefined;
61
+ iframeUrl: string | undefined;
62
+ };
63
+
64
+ const getCurrentPageUrls = (): PageUrls => {
65
+ if (typeof window === "undefined" || !window.location) {
66
+ return { currentUrl: undefined, iframeUrl: undefined };
67
+ }
68
+ const local = (() => {
69
+ const { origin, pathname } = window.location;
70
+ if (!origin || origin === "null") return undefined;
71
+ return `${origin}${pathname || ""}`;
72
+ })();
73
+
74
+ // Top window — no iframe distinction, report the local URL only.
75
+ if (window === window.top) {
76
+ return { currentUrl: local, iframeUrl: undefined };
44
77
  }
45
- return `${origin}${pathname || ""}`;
78
+
79
+ // Same-origin iframe — read the top URL directly.
80
+ try {
81
+ const topHref = window.top?.location?.href;
82
+ const sanitised = topHref ? sanitiseHref(topHref) : undefined;
83
+ if (sanitised) return { currentUrl: sanitised, iframeUrl: local };
84
+ } catch {
85
+ /* cross-origin — fall through */
86
+ }
87
+
88
+ // Cross-origin iframe — the embedding page URL comes via referrer,
89
+ // subject to the parent's Referrer-Policy header.
90
+ if (typeof document !== "undefined" && document.referrer) {
91
+ const fromReferrer = sanitiseHref(document.referrer);
92
+ if (fromReferrer) return { currentUrl: fromReferrer, iframeUrl: local };
93
+ }
94
+
95
+ // Referrer unavailable — fall back to the iframe URL for `currentUrl` so
96
+ // the provider still sees a value, and echo it as `iframeUrl` so
97
+ // downstream can tell the top frame was not observed.
98
+ return { currentUrl: local, iframeUrl: local };
46
99
  };
47
100
 
48
101
  export const withTimeout = async <T>(
@@ -125,6 +178,7 @@ const customDetectBot: BotDetectionFunction = async (
125
178
  // lets it complete in the worker thread while the network round-trip
126
179
  // burns. Readings still attach on the challenge GET and on solution
127
180
  // submit (first-hop-wins server-side).
181
+ const { currentUrl, iframeUrl } = getCurrentPageUrls();
128
182
  const captchaPromise = providerApi.getFrictionlessCaptcha(
129
183
  detectionResult.token,
130
184
  detectionResult.encryptHeadHash,
@@ -132,7 +186,8 @@ const customDetectBot: BotDetectionFunction = async (
132
186
  userAccount.account.address,
133
187
  config.mode,
134
188
  undefined,
135
- getCurrentPageUrl(),
189
+ currentUrl,
190
+ iframeUrl,
136
191
  );
137
192
  if (detectionResult.getSimdReadings) {
138
193
  // Fire-and-forget: triggers the memoised prefetch inside the catcher
@@ -93,7 +93,7 @@ describe("customDetectBot SIMD deferral", () => {
93
93
 
94
94
  expect(mocks.getFrictionlessCaptcha).toHaveBeenCalledTimes(1);
95
95
  const args = mocks.getFrictionlessCaptcha.mock.calls[0];
96
- // args: token, headHash, dappAccount, userAccount, mode, simdReadings, currentUrl
96
+ // args: token, headHash, dappAccount, userAccount, mode, simdReadings, currentUrl, iframeUrl
97
97
  expect(args?.[5]).toBeUndefined();
98
98
  });
99
99