@prosopo/procaptcha-common 2.14.1 → 2.15.0
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.
- package/.turbo/turbo-build$colon$cjs.log +6 -5
- package/.turbo/turbo-build$colon$tsc.log +18 -18
- package/.turbo/turbo-build.log +7 -6
- package/CHANGELOG.md +27 -0
- package/dist/cjs/clientMetaData.cjs +9 -1
- package/dist/cjs/index.cjs +3 -0
- package/dist/cjs/protectSession.cjs +64 -0
- package/dist/clientMetaData.d.ts.map +1 -1
- package/dist/clientMetaData.js +9 -1
- package/dist/clientMetaData.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/protectSession.d.ts +3 -0
- package/dist/protectSession.d.ts.map +1 -0
- package/dist/protectSession.js +63 -0
- package/dist/protectSession.js.map +1 -0
- package/dist/tests/clientMetaData.test.js +20 -1
- package/dist/tests/clientMetaData.test.js.map +1 -1
- package/dist/tests/protectSession.test.d.ts +2 -0
- package/dist/tests/protectSession.test.d.ts.map +1 -0
- package/dist/tests/protectSession.test.js +95 -0
- package/dist/tests/protectSession.test.js.map +1 -0
- package/package.json +4 -4
- package/src/clientMetaData.ts +9 -1
- package/src/index.ts +1 -0
- package/src/protectSession.ts +110 -0
- package/src/tests/clientMetaData.test.ts +27 -1
- package/src/tests/protectSession.test.ts +148 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/procaptcha-common@2.
|
|
2
|
+
> @prosopo/procaptcha-common@2.15.0 build:cjs
|
|
3
3
|
> NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
|
|
4
4
|
|
|
5
5
|
ViteCommonJSConfig: .
|
|
@@ -39,25 +39,26 @@ ViteCommonJSConfig: .
|
|
|
39
39
|
[36mvite v8.1.5 [32mbuilding ssr environment for production...[36m[39m
|
|
40
40
|
Bundle build started
|
|
41
41
|
[2K
|
|
42
|
-
✓
|
|
42
|
+
✓ 18 modules transformed.
|
|
43
43
|
rendering chunks...
|
|
44
44
|
computing gzip size...
|
|
45
45
|
dist/cjs/extensionLoader.cjs 0.27 kB │ gzip: 0.16 kB
|
|
46
46
|
dist/cjs/callbacks/defaultEvents.cjs 0.28 kB │ gzip: 0.16 kB
|
|
47
47
|
dist/cjs/elements/form.cjs 0.62 kB │ gzip: 0.31 kB
|
|
48
48
|
dist/cjs/simdReadings.cjs 0.79 kB │ gzip: 0.36 kB
|
|
49
|
-
dist/cjs/clientMetaData.cjs 0.83 kB │ gzip: 0.47 kB
|
|
50
49
|
dist/cjs/events/trust.cjs 0.91 kB │ gzip: 0.53 kB
|
|
51
50
|
dist/cjs/_virtual/_rolldown/runtime.cjs 1.00 kB │ gzip: 0.50 kB
|
|
52
51
|
dist/cjs/elements/window.cjs 1.12 kB │ gzip: 0.60 kB
|
|
52
|
+
dist/cjs/clientMetaData.cjs 1.31 kB │ gzip: 0.67 kB
|
|
53
53
|
dist/cjs/reactComponents/TestModeBanner.cjs 1.36 kB │ gzip: 0.75 kB
|
|
54
54
|
dist/cjs/providers.cjs 1.54 kB │ gzip: 0.64 kB
|
|
55
55
|
dist/cjs/reactComponents/Honeypot.cjs 2.03 kB │ gzip: 0.91 kB
|
|
56
|
-
dist/cjs/index.cjs 2.
|
|
56
|
+
dist/cjs/index.cjs 2.66 kB │ gzip: 0.67 kB
|
|
57
|
+
dist/cjs/protectSession.cjs 2.80 kB │ gzip: 1.32 kB
|
|
57
58
|
dist/cjs/reactComponents/Reload.cjs 2.94 kB │ gzip: 1.30 kB
|
|
58
59
|
dist/cjs/state/builder.cjs 3.13 kB │ gzip: 0.84 kB
|
|
59
60
|
dist/cjs/callbacks/defaultCallbacks.cjs 3.78 kB │ gzip: 1.01 kB
|
|
60
61
|
dist/cjs/reactComponents/Checkbox.cjs 6.21 kB │ gzip: 2.42 kB
|
|
61
62
|
dist/cjs/reactComponents/ChallengeSurface.cjs 6.72 kB │ gzip: 2.29 kB
|
|
62
63
|
|
|
63
|
-
[32m✓ built in
|
|
64
|
+
[32m✓ built in 89ms[39m
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/procaptcha-common@2.
|
|
2
|
+
> @prosopo/procaptcha-common@2.15.0 build:tsc
|
|
3
3
|
> tsc --build --verbose
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
2:42:45 PM - Projects in this build:
|
|
6
6
|
* ../../dev/config/tsconfig.json
|
|
7
7
|
* ../locale/tsconfig.json
|
|
8
8
|
* ../util/tsconfig.json
|
|
@@ -18,35 +18,35 @@
|
|
|
18
18
|
* ../account/tsconfig.json
|
|
19
19
|
* tsconfig.json
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
2:42:45 PM - 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'
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
2:42:45 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
2:42:45 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
2:42:45 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
2:42:45 PM - 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'
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
2:42:45 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/types.test-d.ts' is older than output '../types/tsconfig.tsbuildinfo'
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
2:42:45 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/utils/batches.ts' is older than output '../common/tsconfig.tsbuildinfo'
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
2:42:45 PM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/tests/productionProviderList.unit.test.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
2:42:45 PM - Project '../widget-skeleton/tsconfig.json' is up to date because newest input '../widget-skeleton/src/webComponent/createWebComponent.ts' is older than output '../widget-skeleton/tsconfig.tsbuildinfo'
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
2:42:45 PM - Project '../fingerprintjs/tsconfig.json' is up to date because newest input '../fingerprintjs/src/utils/browser.ts' is older than output '../fingerprintjs/tsconfig.tsbuildinfo'
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
2:42:45 PM - Project '../fingerprint/tsconfig.json' is up to date because newest input '../fingerprint/src/index.ts' is older than output '../fingerprint/tsconfig.tsbuildinfo'
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
2:42:45 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/pair/decode.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
2:42:45 PM - Project '../account/tsconfig.json' is out of date because output file '../account/tsconfig.tsbuildinfo' does not exist
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
2:42:45 PM - Building project '/home/runner/work/captcha/captcha/packages/account/tsconfig.json'...
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
2:42:48 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
2:42:48 PM - Building project '/home/runner/work/captcha/captcha/packages/procaptcha-common/tsconfig.json'...
|
|
52
52
|
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/procaptcha-common@2.
|
|
2
|
+
> @prosopo/procaptcha-common@2.15.0 build
|
|
3
3
|
> npm run build:cross-env -- --mode ${NODE_ENV:-development}
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @prosopo/procaptcha-common@2.
|
|
6
|
+
> @prosopo/procaptcha-common@2.15.0 build:cross-env
|
|
7
7
|
> vite build --config vite.esm.config.ts --mode production
|
|
8
8
|
|
|
9
9
|
ViteEsmConfig: .
|
|
@@ -43,7 +43,7 @@ ViteEsmConfig: .
|
|
|
43
43
|
[36mvite v8.1.5 [32mbuilding ssr environment for production...[36m[39m
|
|
44
44
|
Bundle build started
|
|
45
45
|
[2K
|
|
46
|
-
✓
|
|
46
|
+
✓ 18 modules transformed.
|
|
47
47
|
rendering chunks...
|
|
48
48
|
computing gzip size...
|
|
49
49
|
dist/_virtual/_rolldown/runtime.js 0.04 kB │ gzip: 0.06 kB
|
|
@@ -51,17 +51,18 @@ dist/callbacks/defaultEvents.js 0.23 kB │ gzip: 0.15 kB
|
|
|
51
51
|
dist/extensionLoader.js 0.25 kB │ gzip: 0.16 kB
|
|
52
52
|
dist/elements/form.js 0.56 kB │ gzip: 0.30 kB
|
|
53
53
|
dist/simdReadings.js 0.72 kB │ gzip: 0.35 kB
|
|
54
|
-
dist/clientMetaData.js 0.81 kB │ gzip: 0.47 kB
|
|
55
54
|
dist/events/trust.js 0.89 kB │ gzip: 0.52 kB
|
|
56
55
|
dist/elements/window.js 1.07 kB │ gzip: 0.60 kB
|
|
57
56
|
dist/reactComponents/TestModeBanner.js 1.23 kB │ gzip: 0.71 kB
|
|
57
|
+
dist/clientMetaData.js 1.27 kB │ gzip: 0.66 kB
|
|
58
58
|
dist/providers.js 1.41 kB │ gzip: 0.62 kB
|
|
59
|
-
dist/index.js 1.
|
|
59
|
+
dist/index.js 1.60 kB │ gzip: 0.53 kB
|
|
60
60
|
dist/reactComponents/Honeypot.js 1.86 kB │ gzip: 0.89 kB
|
|
61
61
|
dist/reactComponents/Reload.js 2.69 kB │ gzip: 1.26 kB
|
|
62
|
+
dist/protectSession.js 2.74 kB │ gzip: 1.31 kB
|
|
62
63
|
dist/state/builder.js 3.09 kB │ gzip: 0.84 kB
|
|
63
64
|
dist/callbacks/defaultCallbacks.js 3.58 kB │ gzip: 0.98 kB
|
|
64
65
|
dist/reactComponents/Checkbox.js 5.66 kB │ gzip: 2.34 kB
|
|
65
66
|
dist/reactComponents/ChallengeSurface.js 6.39 kB │ gzip: 2.21 kB
|
|
66
67
|
|
|
67
|
-
[32m✓ built in
|
|
68
|
+
[32m✓ built in 98ms[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @prosopo/procaptcha-common
|
|
2
2
|
|
|
3
|
+
## 2.15.0
|
|
4
|
+
### Minor Changes
|
|
5
|
+
|
|
6
|
+
- 0f23010: Correlate captcha sessions with Prosopo Protect sessions on sites that run both.
|
|
7
|
+
|
|
8
|
+
Protect's challenge page already renders the widget with `data-sessionid=<its session id>`, so captchas served from the interstitial can be matched back to the Protect session. A widget the site embeds itself — on its own pages — had no way to know that id, so those sessions could not be matched to anything.
|
|
9
|
+
|
|
10
|
+
The widget now falls back to reading Protect's session id from the page (`window.prosopo_protect.jti`, or the `prosopo_session` cookie Protect sets on the site's domain) when the site has not supplied a session id of its own. A session id the site does supply always wins, so nothing changes for sites that use the field themselves, and sites without Protect are unaffected. Only the id is read — the session token that shares the cookie never leaves the page.
|
|
11
|
+
|
|
12
|
+
Two gaps in the existing field are closed alongside it: the widget now sends the session id when it first asks for a captcha rather than only when submitting a solution, and the provider records it on the session at that point. Previously a session that was allowed without a challenge, or abandoned before the user solved one, carried no session id at all. An escalated session now inherits the id from the session it escalated from.
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies [a606f54]
|
|
17
|
+
- Updated dependencies [0f23010]
|
|
18
|
+
- @prosopo/types@5.9.0
|
|
19
|
+
- @prosopo/account@2.8.95
|
|
20
|
+
- @prosopo/load-balancer@2.10.47
|
|
21
|
+
|
|
22
|
+
## 2.14.2
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [be25974]
|
|
26
|
+
- @prosopo/types@5.8.5
|
|
27
|
+
- @prosopo/account@2.8.94
|
|
28
|
+
- @prosopo/load-balancer@2.10.46
|
|
29
|
+
|
|
3
30
|
## 2.14.1
|
|
4
31
|
### Patch Changes
|
|
5
32
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const require_protectSession = require("./protectSession.cjs");
|
|
1
2
|
//#region src/clientMetaData.ts
|
|
2
3
|
/**
|
|
3
4
|
* Assembles the widget-controlled metadata attached to a captcha solution.
|
|
@@ -7,14 +8,21 @@
|
|
|
7
8
|
* Returns `undefined` when nothing is set, so the submission body omits the
|
|
8
9
|
* key entirely rather than carrying an empty object.
|
|
9
10
|
*
|
|
11
|
+
* A site that renders no session id of its own but runs Prosopo Protect falls
|
|
12
|
+
* back to Protect's session id, which is the same value Protect's own
|
|
13
|
+
* challenge page renders the widget with. Read here rather than at render time
|
|
14
|
+
* because Protect initialises asynchronously and may not have a session yet
|
|
15
|
+
* when the widget mounts.
|
|
16
|
+
*
|
|
10
17
|
* @param hp - live honeypot input value, if the honeypot was filled in
|
|
11
18
|
* @param clientSessionId - the site's session id, if the widget was rendered
|
|
12
19
|
* with `data-sessionid` / `renderOptions.sessionId`
|
|
13
20
|
*/
|
|
14
21
|
var buildClientMetaData = (hp, clientSessionId) => {
|
|
22
|
+
const sessionId = require_protectSession.resolveClientSessionId(clientSessionId);
|
|
15
23
|
const clientMetaData = {
|
|
16
24
|
...hp && { hp },
|
|
17
|
-
...
|
|
25
|
+
...sessionId && { clientSessionId: sessionId }
|
|
18
26
|
};
|
|
19
27
|
return Object.keys(clientMetaData).length > 0 ? clientMetaData : void 0;
|
|
20
28
|
};
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_providers = require("./providers.cjs");
|
|
3
|
+
const require_protectSession = require("./protectSession.cjs");
|
|
3
4
|
const require_clientMetaData = require("./clientMetaData.cjs");
|
|
4
5
|
const require_trust = require("./events/trust.cjs");
|
|
5
6
|
const require_builder = require("./state/builder.cjs");
|
|
@@ -26,6 +27,7 @@ exports.computeFloatPosition = require_ChallengeSurface.computeFloatPosition;
|
|
|
26
27
|
exports.getDefaultCallbacks = require_defaultCallbacks.getDefaultCallbacks;
|
|
27
28
|
exports.getDefaultEvents = require_defaultEvents.getDefaultEvents;
|
|
28
29
|
exports.getProcaptchaRandomActiveProvider = require_providers.getProcaptchaRandomActiveProvider;
|
|
30
|
+
exports.getProtectJti = require_protectSession.getProtectJti;
|
|
29
31
|
exports.getRetryDelayMs = require_providers.getRetryDelayMs;
|
|
30
32
|
exports.getSimdReadingsForSubmit = require_simdReadings.getSimdReadingsForSubmit;
|
|
31
33
|
exports.getWindowCallback = require_window.getWindowCallback;
|
|
@@ -33,5 +35,6 @@ exports.isEventTrusted = require_trust.isEventTrusted;
|
|
|
33
35
|
exports.isSecureBrowserContext = require_window.isSecureBrowserContext;
|
|
34
36
|
exports.pickIpMode = require_providers.pickIpMode;
|
|
35
37
|
exports.providerRetry = require_providers.providerRetry;
|
|
38
|
+
exports.resolveClientSessionId = require_protectSession.resolveClientSessionId;
|
|
36
39
|
exports.setUserCallbacks = require_defaultCallbacks.setUserCallbacks;
|
|
37
40
|
exports.useProcaptcha = require_builder.useProcaptcha;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
let _prosopo_types = require("@prosopo/types");
|
|
2
|
+
//#region src/protectSession.ts
|
|
3
|
+
var PROTECT_GLOBAL = "prosopo_protect";
|
|
4
|
+
var PROTECT_COOKIE = "prosopo_session";
|
|
5
|
+
var isProtectGlobal = (value) => typeof value === "object" && value !== null;
|
|
6
|
+
/**
|
|
7
|
+
* `bumblebee-<uuid>` today, but the prefix names the edge that issued the
|
|
8
|
+
* session, so this only rejects what could not be an id at all.
|
|
9
|
+
*
|
|
10
|
+
* The length bound is load-bearing rather than defensive: the provider caps
|
|
11
|
+
* `clientSessionId` at `INPUT_LIMITS.ID` and rejects the whole solution body
|
|
12
|
+
* when it is longer, so an oversized value here would fail the solve rather
|
|
13
|
+
* than just losing the correlation.
|
|
14
|
+
*/
|
|
15
|
+
var isUsableJti = (value) => value.length > 0 && value.length <= _prosopo_types.INPUT_LIMITS.ID && /^[A-Za-z0-9._-]+$/.test(value);
|
|
16
|
+
var readGlobalJti = () => {
|
|
17
|
+
const global = Reflect.get(window, PROTECT_GLOBAL);
|
|
18
|
+
if (!isProtectGlobal(global) || typeof global.jti !== "string") return;
|
|
19
|
+
const jti = global.jti.trim();
|
|
20
|
+
return isUsableJti(jti) ? jti : void 0;
|
|
21
|
+
};
|
|
22
|
+
var readCookieJti = () => {
|
|
23
|
+
if (typeof document === "undefined") return;
|
|
24
|
+
for (const pair of document.cookie.split(";")) {
|
|
25
|
+
const separator = pair.indexOf("=");
|
|
26
|
+
if (separator === -1 || pair.slice(0, separator).trim() !== PROTECT_COOKIE) continue;
|
|
27
|
+
const jti = pair.slice(separator + 1).trim().split("|")[0];
|
|
28
|
+
return jti !== void 0 && isUsableJti(jti) ? jti : void 0;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The Protect session id (JTI) for this page, when the site runs Prosopo
|
|
33
|
+
* Protect alongside the captcha.
|
|
34
|
+
*
|
|
35
|
+
* Protect publishes it two ways, both read here: `window.prosopo_protect.jti`,
|
|
36
|
+
* and the `prosopo_session` cookie it sets on the registrable domain (so a
|
|
37
|
+
* session minted at `protect.example.com` is visible on `www.example.com`).
|
|
38
|
+
* The global is preferred — it is the value Protect itself is using, whereas
|
|
39
|
+
* the cookie may have been rewritten by anything on the page.
|
|
40
|
+
*
|
|
41
|
+
* Undefined whenever the site does not run Protect, the widget is embedded
|
|
42
|
+
* cross-origin, or Protect has not finished initialising. Every caller treats
|
|
43
|
+
* that as "no correlation available", never as a fault.
|
|
44
|
+
*/
|
|
45
|
+
var getProtectJti = () => {
|
|
46
|
+
if (typeof window === "undefined") return;
|
|
47
|
+
try {
|
|
48
|
+
return readGlobalJti() ?? readCookieJti();
|
|
49
|
+
} catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The session id this widget reports to the provider: whatever the site asked
|
|
55
|
+
* for, and Protect's session id only when the site asked for nothing.
|
|
56
|
+
*
|
|
57
|
+
* One function so the precedence is stated once — the frictionless request and
|
|
58
|
+
* the solution submit have to agree on it, or the provider's own
|
|
59
|
+
* mismatch check would fire on a session it built both halves of.
|
|
60
|
+
*/
|
|
61
|
+
var resolveClientSessionId = (clientSessionId) => clientSessionId || getProtectJti();
|
|
62
|
+
//#endregion
|
|
63
|
+
exports.getProtectJti = getProtectJti;
|
|
64
|
+
exports.resolveClientSessionId = resolveClientSessionId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientMetaData.d.ts","sourceRoot":"","sources":["../src/clientMetaData.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"clientMetaData.d.ts","sourceRoot":"","sources":["../src/clientMetaData.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAqBrD,eAAO,MAAM,mBAAmB,QAC1B,MAAM,oBACO,MAAM,KACtB,cAAc,GAAG,SAQnB,CAAC"}
|
package/dist/clientMetaData.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveClientSessionId } from "./protectSession.js";
|
|
1
2
|
//#region src/clientMetaData.ts
|
|
2
3
|
/**
|
|
3
4
|
* Assembles the widget-controlled metadata attached to a captcha solution.
|
|
@@ -7,14 +8,21 @@
|
|
|
7
8
|
* Returns `undefined` when nothing is set, so the submission body omits the
|
|
8
9
|
* key entirely rather than carrying an empty object.
|
|
9
10
|
*
|
|
11
|
+
* A site that renders no session id of its own but runs Prosopo Protect falls
|
|
12
|
+
* back to Protect's session id, which is the same value Protect's own
|
|
13
|
+
* challenge page renders the widget with. Read here rather than at render time
|
|
14
|
+
* because Protect initialises asynchronously and may not have a session yet
|
|
15
|
+
* when the widget mounts.
|
|
16
|
+
*
|
|
10
17
|
* @param hp - live honeypot input value, if the honeypot was filled in
|
|
11
18
|
* @param clientSessionId - the site's session id, if the widget was rendered
|
|
12
19
|
* with `data-sessionid` / `renderOptions.sessionId`
|
|
13
20
|
*/
|
|
14
21
|
var buildClientMetaData = (hp, clientSessionId) => {
|
|
22
|
+
const sessionId = resolveClientSessionId(clientSessionId);
|
|
15
23
|
const clientMetaData = {
|
|
16
24
|
...hp && { hp },
|
|
17
|
-
...
|
|
25
|
+
...sessionId && { clientSessionId: sessionId }
|
|
18
26
|
};
|
|
19
27
|
return Object.keys(clientMetaData).length > 0 ? clientMetaData : void 0;
|
|
20
28
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientMetaData.js","sourceRoot":"","sources":["../src/clientMetaData.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"clientMetaData.js","sourceRoot":"","sources":["../src/clientMetaData.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAoB7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAClC,EAAW,EACX,eAAwB,EACK,EAAE;IAC/B,MAAM,SAAS,GAAG,sBAAsB,CAAC,eAAe,CAAC,CAAC;IAC1D,MAAM,cAAc,GAAmB;QACtC,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACjB,GAAG,CAAC,SAAS,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;KAChD,CAAC;IAEF,OAAO,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5E,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "./_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { getProcaptchaRandomActiveProvider, getRetryDelayMs, pickIpMode, providerRetry } from "./providers.js";
|
|
3
|
+
import { getProtectJti, resolveClientSessionId } from "./protectSession.js";
|
|
3
4
|
import { buildClientMetaData } from "./clientMetaData.js";
|
|
4
5
|
import { isEventTrusted } from "./events/trust.js";
|
|
5
6
|
import { buildUpdateState, useProcaptcha } from "./state/builder.js";
|
|
@@ -13,4 +14,4 @@ import { Checkbox } from "./reactComponents/Checkbox.js";
|
|
|
13
14
|
import { ChallengeSurface, computeFloatPosition } from "./reactComponents/ChallengeSurface.js";
|
|
14
15
|
import { Honeypot } from "./reactComponents/Honeypot.js";
|
|
15
16
|
import { TestModeBanner } from "./reactComponents/TestModeBanner.js";
|
|
16
|
-
export { ChallengeSurface, Checkbox, ExtensionLoader, Honeypot, ReloadButton, SIMD_READINGS_SUBMIT_TIMEOUT_MS, TestModeBanner, buildClientMetaData, buildUpdateState, computeFloatPosition, getDefaultCallbacks, getDefaultEvents, getProcaptchaRandomActiveProvider, getRetryDelayMs, getSimdReadingsForSubmit, getWindowCallback, isEventTrusted, isSecureBrowserContext, pickIpMode, providerRetry, setUserCallbacks, useProcaptcha };
|
|
17
|
+
export { ChallengeSurface, Checkbox, ExtensionLoader, Honeypot, ReloadButton, SIMD_READINGS_SUBMIT_TIMEOUT_MS, TestModeBanner, buildClientMetaData, buildUpdateState, computeFloatPosition, getDefaultCallbacks, getDefaultEvents, getProcaptchaRandomActiveProvider, getProtectJti, getRetryDelayMs, getSimdReadingsForSubmit, getWindowCallback, isEventTrusted, isSecureBrowserContext, pickIpMode, providerRetry, resolveClientSessionId, setUserCallbacks, useProcaptcha };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protectSession.d.ts","sourceRoot":"","sources":["../src/protectSession.ts"],"names":[],"mappings":"AAsFA,eAAO,MAAM,aAAa,QAAO,MAAM,GAAG,SAWzC,CAAC;AAUF,eAAO,MAAM,sBAAsB,qBAChB,MAAM,KACtB,MAAM,GAAG,SAA+C,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { INPUT_LIMITS } from "@prosopo/types";
|
|
2
|
+
//#region src/protectSession.ts
|
|
3
|
+
var PROTECT_GLOBAL = "prosopo_protect";
|
|
4
|
+
var PROTECT_COOKIE = "prosopo_session";
|
|
5
|
+
var isProtectGlobal = (value) => typeof value === "object" && value !== null;
|
|
6
|
+
/**
|
|
7
|
+
* `bumblebee-<uuid>` today, but the prefix names the edge that issued the
|
|
8
|
+
* session, so this only rejects what could not be an id at all.
|
|
9
|
+
*
|
|
10
|
+
* The length bound is load-bearing rather than defensive: the provider caps
|
|
11
|
+
* `clientSessionId` at `INPUT_LIMITS.ID` and rejects the whole solution body
|
|
12
|
+
* when it is longer, so an oversized value here would fail the solve rather
|
|
13
|
+
* than just losing the correlation.
|
|
14
|
+
*/
|
|
15
|
+
var isUsableJti = (value) => value.length > 0 && value.length <= INPUT_LIMITS.ID && /^[A-Za-z0-9._-]+$/.test(value);
|
|
16
|
+
var readGlobalJti = () => {
|
|
17
|
+
const global = Reflect.get(window, PROTECT_GLOBAL);
|
|
18
|
+
if (!isProtectGlobal(global) || typeof global.jti !== "string") return;
|
|
19
|
+
const jti = global.jti.trim();
|
|
20
|
+
return isUsableJti(jti) ? jti : void 0;
|
|
21
|
+
};
|
|
22
|
+
var readCookieJti = () => {
|
|
23
|
+
if (typeof document === "undefined") return;
|
|
24
|
+
for (const pair of document.cookie.split(";")) {
|
|
25
|
+
const separator = pair.indexOf("=");
|
|
26
|
+
if (separator === -1 || pair.slice(0, separator).trim() !== PROTECT_COOKIE) continue;
|
|
27
|
+
const jti = pair.slice(separator + 1).trim().split("|")[0];
|
|
28
|
+
return jti !== void 0 && isUsableJti(jti) ? jti : void 0;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The Protect session id (JTI) for this page, when the site runs Prosopo
|
|
33
|
+
* Protect alongside the captcha.
|
|
34
|
+
*
|
|
35
|
+
* Protect publishes it two ways, both read here: `window.prosopo_protect.jti`,
|
|
36
|
+
* and the `prosopo_session` cookie it sets on the registrable domain (so a
|
|
37
|
+
* session minted at `protect.example.com` is visible on `www.example.com`).
|
|
38
|
+
* The global is preferred — it is the value Protect itself is using, whereas
|
|
39
|
+
* the cookie may have been rewritten by anything on the page.
|
|
40
|
+
*
|
|
41
|
+
* Undefined whenever the site does not run Protect, the widget is embedded
|
|
42
|
+
* cross-origin, or Protect has not finished initialising. Every caller treats
|
|
43
|
+
* that as "no correlation available", never as a fault.
|
|
44
|
+
*/
|
|
45
|
+
var getProtectJti = () => {
|
|
46
|
+
if (typeof window === "undefined") return;
|
|
47
|
+
try {
|
|
48
|
+
return readGlobalJti() ?? readCookieJti();
|
|
49
|
+
} catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The session id this widget reports to the provider: whatever the site asked
|
|
55
|
+
* for, and Protect's session id only when the site asked for nothing.
|
|
56
|
+
*
|
|
57
|
+
* One function so the precedence is stated once — the frictionless request and
|
|
58
|
+
* the solution submit have to agree on it, or the provider's own
|
|
59
|
+
* mismatch check would fire on a session it built both halves of.
|
|
60
|
+
*/
|
|
61
|
+
var resolveClientSessionId = (clientSessionId) => clientSessionId || getProtectJti();
|
|
62
|
+
//#endregion
|
|
63
|
+
export { getProtectJti, resolveClientSessionId };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protectSession.js","sourceRoot":"","sources":["../src/protectSession.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,cAAc,GAAG,iBAAiB,CAAC;AACzC,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAMzC,MAAM,eAAe,GAAG,CAAC,KAAc,EAA0B,EAAE,CAClE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AAW7C,MAAM,WAAW,GAAG,CAAC,KAAa,EAAW,EAAE,CAC9C,KAAK,CAAC,MAAM,GAAG,CAAC;IAChB,KAAK,CAAC,MAAM,IAAI,YAAY,CAAC,EAAE;IAC/B,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAEjC,MAAM,aAAa,GAAG,GAAuB,EAAE;IAC9C,MAAM,MAAM,GAAY,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC5D,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QAChE,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3C,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,GAAuB,EAAE;IAC9C,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACrC,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,IACC,SAAS,KAAK,CAAC,CAAC;YAChB,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,EAAE,KAAK,cAAc,EACjD,CAAC;YACF,SAAS;QACV,CAAC;QAGD,MAAM,GAAG,GAAG,IAAI;aACd,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;aACpB,IAAI,EAAE;aACN,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAChB,OAAO,GAAG,KAAK,SAAS,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC,CAAC;AAgBF,MAAM,CAAC,MAAM,aAAa,GAAG,GAAuB,EAAE;IACrD,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QACnC,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,IAAI,CAAC;QACJ,OAAO,aAAa,EAAE,IAAI,aAAa,EAAE,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QAGR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC,CAAC;AAUF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACrC,eAAwB,EACH,EAAE,CAAC,eAAe,IAAI,aAAa,EAAE,CAAC"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
2
|
import { buildClientMetaData } from "../clientMetaData.js";
|
|
3
|
+
const PROTECT_JTI = "bumblebee-0f1e2d3c-4b5a-6978-8796-a5b4c3d2e1f0";
|
|
4
|
+
const runProtect = () => {
|
|
5
|
+
Reflect.set(window, "prosopo_protect", { jti: PROTECT_JTI, ready: true });
|
|
6
|
+
};
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
Reflect.deleteProperty(window, "prosopo_protect");
|
|
9
|
+
});
|
|
3
10
|
describe("buildClientMetaData", () => {
|
|
4
11
|
it("returns undefined when the widget has nothing to report", () => {
|
|
5
12
|
expect(buildClientMetaData(undefined, undefined)).toBeUndefined();
|
|
@@ -23,5 +30,17 @@ describe("buildClientMetaData", () => {
|
|
|
23
30
|
clientSessionId: "jti-1",
|
|
24
31
|
});
|
|
25
32
|
});
|
|
33
|
+
it("falls back to Protect's session id when the site rendered none", () => {
|
|
34
|
+
runProtect();
|
|
35
|
+
expect(buildClientMetaData(undefined, undefined)).toEqual({
|
|
36
|
+
clientSessionId: PROTECT_JTI,
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
it("leaves the site's own session id alone when Protect is also present", () => {
|
|
40
|
+
runProtect();
|
|
41
|
+
expect(buildClientMetaData(undefined, "site-session-1")).toEqual({
|
|
42
|
+
clientSessionId: "site-session-1",
|
|
43
|
+
});
|
|
44
|
+
});
|
|
26
45
|
});
|
|
27
46
|
//# sourceMappingURL=clientMetaData.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientMetaData.test.js","sourceRoot":"","sources":["../../src/tests/clientMetaData.test.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"clientMetaData.test.js","sourceRoot":"","sources":["../../src/tests/clientMetaData.test.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,GAAG,gDAAgD,CAAC;AAErE,MAAM,UAAU,GAAG,GAAS,EAAE;IAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC,CAAC;AAEF,SAAS,CAAC,GAAG,EAAE;IACd,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,mBAAmB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,mBAAmB,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;YAChE,EAAE,EAAE,gBAAgB;SACpB,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YACvD,eAAe,EAAE,OAAO;SACxB,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YACpD,EAAE,EAAE,MAAM;YACV,eAAe,EAAE,OAAO;SACxB,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACzE,UAAU,EAAE,CAAC;QAEb,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;YACzD,eAAe,EAAE,WAAW;SAC5B,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC9E,UAAU,EAAE,CAAC;QAEb,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC;YAChE,eAAe,EAAE,gBAAgB;SACjC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protectSession.test.d.ts","sourceRoot":"","sources":["../../src/tests/protectSession.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { INPUT_LIMITS } from "@prosopo/types";
|
|
2
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { getProtectJti, resolveClientSessionId } from "../protectSession.js";
|
|
4
|
+
const JTI = "bumblebee-0f1e2d3c-4b5a-6978-8796-a5b4c3d2e1f0";
|
|
5
|
+
const JWT = "eyJhbGciOiJzcjI1NTE5In0.eyJqdGkiOiJ4In0.c2lnbmF0dXJl";
|
|
6
|
+
const setCookie = (value) => {
|
|
7
|
+
document.cookie = `prosopo_session=${value}; path=/`;
|
|
8
|
+
};
|
|
9
|
+
const clearCookie = () => {
|
|
10
|
+
document.cookie = "prosopo_session=; path=/; max-age=0";
|
|
11
|
+
};
|
|
12
|
+
const setGlobal = (value) => {
|
|
13
|
+
Reflect.set(window, "prosopo_protect", value);
|
|
14
|
+
};
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
Reflect.deleteProperty(window, "prosopo_protect");
|
|
17
|
+
clearCookie();
|
|
18
|
+
});
|
|
19
|
+
describe("getProtectJti", () => {
|
|
20
|
+
it("returns undefined when the site does not run Protect", () => {
|
|
21
|
+
expect(getProtectJti()).toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
it("reads the jti from the Protect global", () => {
|
|
24
|
+
setGlobal({ version: "0.1.0", jti: JTI, ready: true });
|
|
25
|
+
expect(getProtectJti()).toBe(JTI);
|
|
26
|
+
});
|
|
27
|
+
it("falls back to the session cookie when the global has no jti yet", () => {
|
|
28
|
+
setGlobal({ version: "0.1.0", ready: false });
|
|
29
|
+
setCookie(`${JTI}|${JWT}`);
|
|
30
|
+
expect(getProtectJti()).toBe(JTI);
|
|
31
|
+
});
|
|
32
|
+
it("never returns the jwt half of the cookie", () => {
|
|
33
|
+
setCookie(`${JTI}|${JWT}`);
|
|
34
|
+
const jti = getProtectJti();
|
|
35
|
+
expect(jti).toBe(JTI);
|
|
36
|
+
expect(jti).not.toContain(JWT);
|
|
37
|
+
});
|
|
38
|
+
it("reads a cookie that carries the jti alone", () => {
|
|
39
|
+
setCookie(JTI);
|
|
40
|
+
expect(getProtectJti()).toBe(JTI);
|
|
41
|
+
});
|
|
42
|
+
it("ignores an empty jti in the cookie", () => {
|
|
43
|
+
setCookie(`|${JWT}`);
|
|
44
|
+
expect(getProtectJti()).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
it("ignores other cookies whose name merely ends in the same suffix", () => {
|
|
47
|
+
document.cookie = `not_prosopo_session=${JTI}; path=/`;
|
|
48
|
+
expect(getProtectJti()).toBeUndefined();
|
|
49
|
+
document.cookie = "not_prosopo_session=; path=/; max-age=0";
|
|
50
|
+
});
|
|
51
|
+
it("ignores a jti longer than the provider accepts", () => {
|
|
52
|
+
setGlobal({ jti: "a".repeat(INPUT_LIMITS.ID + 1) });
|
|
53
|
+
expect(getProtectJti()).toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
it("accepts a jti exactly at the limit", () => {
|
|
56
|
+
const atLimit = "a".repeat(INPUT_LIMITS.ID);
|
|
57
|
+
setGlobal({ jti: atLimit });
|
|
58
|
+
expect(getProtectJti()).toBe(atLimit);
|
|
59
|
+
});
|
|
60
|
+
it("ignores a jti containing characters an id cannot have", () => {
|
|
61
|
+
setGlobal({ jti: "bumblebee-<script>" });
|
|
62
|
+
expect(getProtectJti()).toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
it("ignores a non-string jti", () => {
|
|
65
|
+
setGlobal({ jti: 42 });
|
|
66
|
+
expect(getProtectJti()).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
it("survives a page that defines the global as a throwing getter", () => {
|
|
69
|
+
Object.defineProperty(window, "prosopo_protect", {
|
|
70
|
+
configurable: true,
|
|
71
|
+
get: () => {
|
|
72
|
+
throw new Error("nope");
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
expect(getProtectJti()).toBeUndefined();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe("resolveClientSessionId", () => {
|
|
79
|
+
it("prefers the session id the site rendered the widget with", () => {
|
|
80
|
+
setGlobal({ jti: JTI });
|
|
81
|
+
expect(resolveClientSessionId("site-session-1")).toBe("site-session-1");
|
|
82
|
+
});
|
|
83
|
+
it("falls back to Protect when the site rendered no session id", () => {
|
|
84
|
+
setGlobal({ jti: JTI });
|
|
85
|
+
expect(resolveClientSessionId(undefined)).toBe(JTI);
|
|
86
|
+
});
|
|
87
|
+
it("treats an empty site session id as none", () => {
|
|
88
|
+
setGlobal({ jti: JTI });
|
|
89
|
+
expect(resolveClientSessionId("")).toBe(JTI);
|
|
90
|
+
});
|
|
91
|
+
it("returns undefined when neither is available", () => {
|
|
92
|
+
expect(resolveClientSessionId(undefined)).toBeUndefined();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=protectSession.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protectSession.test.js","sourceRoot":"","sources":["../../src/tests/protectSession.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/procaptcha-common",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"author": "PROSOPO LIMITED <info@prosopo.io>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@emotion/react": "11.14.0",
|
|
36
36
|
"@emotion/styled": "11.14.1",
|
|
37
|
-
"@prosopo/account": "2.8.
|
|
38
|
-
"@prosopo/load-balancer": "2.10.
|
|
39
|
-
"@prosopo/types": "5.
|
|
37
|
+
"@prosopo/account": "2.8.95",
|
|
38
|
+
"@prosopo/load-balancer": "2.10.47",
|
|
39
|
+
"@prosopo/types": "5.9.0",
|
|
40
40
|
"@prosopo/widget-skeleton": "2.8.7",
|
|
41
41
|
"react": "19.3.0",
|
|
42
42
|
"react-dom": "19.3.0"
|