@prosopo/provider 5.12.2 → 5.13.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.
Files changed (69) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +13 -10
  2. package/.turbo/turbo-build$colon$tsc.log +27 -27
  3. package/.turbo/turbo-build.log +13 -10
  4. package/CHANGELOG.md +69 -0
  5. package/dist/api/captcha/assetSeal.d.ts +15 -0
  6. package/dist/api/captcha/assetSeal.d.ts.map +1 -0
  7. package/dist/api/captcha/assetSeal.js +22 -0
  8. package/dist/api/captcha/assetSeal.js.map +1 -0
  9. package/dist/api/captcha/assetSealFormat.d.ts +23 -0
  10. package/dist/api/captcha/assetSealFormat.d.ts.map +1 -0
  11. package/dist/api/captcha/assetSealFormat.js +87 -0
  12. package/dist/api/captcha/assetSealFormat.js.map +1 -0
  13. package/dist/api/captcha/getImageCaptchaChallenge.d.ts.map +1 -1
  14. package/dist/api/captcha/getImageCaptchaChallenge.js +2 -1
  15. package/dist/api/captcha/getImageCaptchaChallenge.js.map +1 -1
  16. package/dist/api/captcha/sealedAssetsResolver.d.ts +22 -0
  17. package/dist/api/captcha/sealedAssetsResolver.d.ts.map +1 -0
  18. package/dist/api/captcha/sealedAssetsResolver.js +69 -0
  19. package/dist/api/captcha/sealedAssetsResolver.js.map +1 -0
  20. package/dist/api/captcha/signedAssetsResolver.d.ts +2 -1
  21. package/dist/api/captcha/signedAssetsResolver.d.ts.map +1 -1
  22. package/dist/api/captcha/signedAssetsResolver.js +6 -5
  23. package/dist/api/captcha/signedAssetsResolver.js.map +1 -1
  24. package/dist/cjs/api/captcha/assetSeal.cjs +22 -0
  25. package/dist/cjs/api/captcha/assetSealFormat.cjs +95 -0
  26. package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +2 -1
  27. package/dist/cjs/api/captcha/sealedAssetsResolver.cjs +70 -0
  28. package/dist/cjs/api/captcha/signedAssetsResolver.cjs +6 -4
  29. package/dist/cjs/compositeIpAddress.cjs +44 -0
  30. package/dist/cjs/index.cjs +1 -0
  31. package/dist/cjs/tasks/captchaManager.cjs +15 -1
  32. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +12 -2
  33. package/dist/compositeIpAddress.d.ts +1 -0
  34. package/dist/compositeIpAddress.d.ts.map +1 -1
  35. package/dist/compositeIpAddress.js +44 -1
  36. package/dist/compositeIpAddress.js.map +1 -1
  37. package/dist/edge/assetSealHandler.d.ts +9 -0
  38. package/dist/edge/assetSealHandler.d.ts.map +1 -0
  39. package/dist/edge/assetSealHandler.js +74 -0
  40. package/dist/edge/assetSealHandler.js.map +1 -0
  41. package/dist/index.js +2 -2
  42. package/dist/tasks/captchaManager.d.ts +1 -1
  43. package/dist/tasks/captchaManager.d.ts.map +1 -1
  44. package/dist/tasks/captchaManager.js +15 -1
  45. package/dist/tasks/captchaManager.js.map +1 -1
  46. package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
  47. package/dist/tasks/powCaptcha/powTasks.js +13 -3
  48. package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
  49. package/dist/tests/unit/api/captcha/assetSeal.unit.test.d.ts +2 -0
  50. package/dist/tests/unit/api/captcha/assetSeal.unit.test.d.ts.map +1 -0
  51. package/dist/tests/unit/api/captcha/assetSeal.unit.test.js +132 -0
  52. package/dist/tests/unit/api/captcha/assetSeal.unit.test.js.map +1 -0
  53. package/dist/tests/unit/api/captcha/sealedAssetsResolver.unit.test.d.ts +2 -0
  54. package/dist/tests/unit/api/captcha/sealedAssetsResolver.unit.test.d.ts.map +1 -0
  55. package/dist/tests/unit/api/captcha/sealedAssetsResolver.unit.test.js +151 -0
  56. package/dist/tests/unit/api/captcha/sealedAssetsResolver.unit.test.js.map +1 -0
  57. package/dist/tests/unit/compositeIpAddress.unit.test.js +37 -1
  58. package/dist/tests/unit/compositeIpAddress.unit.test.js.map +1 -1
  59. package/dist/tests/unit/edge/assetSealHandler.unit.test.d.ts +2 -0
  60. package/dist/tests/unit/edge/assetSealHandler.unit.test.d.ts.map +1 -0
  61. package/dist/tests/unit/edge/assetSealHandler.unit.test.js +145 -0
  62. package/dist/tests/unit/edge/assetSealHandler.unit.test.js.map +1 -0
  63. package/dist/tests/unit/tasks/captchaManager.unit.test.js +41 -0
  64. package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
  65. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +100 -1
  66. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +1 -1
  67. package/edge/entry.ts +62 -0
  68. package/package.json +16 -14
  69. package/scripts/buildEdgeScript.mjs +31 -0
@@ -0,0 +1,151 @@
1
+ import { afterEach, describe, expect, test } from "vitest";
2
+ import { unsealAssetPath } from "../../../../api/captcha/assetSeal.js";
3
+ import { SEAL_KEY_BYTES, toBase64Url, } from "../../../../api/captcha/assetSealFormat.js";
4
+ import { DEFAULT_ASSET_SEAL_TTL_SECONDS, SealedAssetsResolver, getSealedAssetsResolver, } from "../../../../api/captcha/sealedAssetsResolver.js";
5
+ const KEY = new Uint8Array(SEAL_KEY_BYTES).fill(3);
6
+ const KEY_ID = 4;
7
+ const NOW_MS = 1_700_000_000_000;
8
+ const ORIGIN = "https://prosopoimages.b-cdn.net";
9
+ const PATH = "/v5_dataset_flat/images/abc.webp";
10
+ const IMAGE = `${ORIGIN}${PATH}`;
11
+ const resolver = (options = {}) => new SealedAssetsResolver({
12
+ key: KEY,
13
+ keyId: KEY_ID,
14
+ now: () => NOW_MS,
15
+ ...options,
16
+ });
17
+ const opened = (sealedUrl, nowSeconds = NOW_MS / 1000) => {
18
+ const segments = new URL(sealedUrl).pathname.split("/");
19
+ const blob = segments[segments.length - 1]?.replace(/\.[a-z0-9]+$/, "") ?? "";
20
+ const path = unsealAssetPath({
21
+ blob,
22
+ keys: new Map([[KEY_ID, KEY]]),
23
+ nowSeconds,
24
+ });
25
+ if (!path)
26
+ throw new Error("expected the sealed URL to open");
27
+ return path;
28
+ };
29
+ const withEnv = (values, run) => {
30
+ const saved = new Map();
31
+ for (const [name, value] of Object.entries(values)) {
32
+ saved.set(name, process.env[name]);
33
+ if (undefined === value) {
34
+ Reflect.deleteProperty(process.env, name);
35
+ }
36
+ else {
37
+ process.env[name] = value;
38
+ }
39
+ }
40
+ try {
41
+ run();
42
+ }
43
+ finally {
44
+ for (const [name, value] of saved) {
45
+ if (undefined === value) {
46
+ Reflect.deleteProperty(process.env, name);
47
+ }
48
+ else {
49
+ process.env[name] = value;
50
+ }
51
+ }
52
+ }
53
+ };
54
+ describe("SealedAssetsResolver", () => {
55
+ test("keeps the image on the same zone", () => {
56
+ const url = new URL(resolver().resolveAsset(IMAGE).getURL());
57
+ expect(url.origin).toBe(ORIGIN);
58
+ });
59
+ test("replaces the path with an opaque blob under the prefix", () => {
60
+ const url = new URL(resolver().resolveAsset(IMAGE).getURL());
61
+ expect(url.pathname).toMatch(/^\/s\/[A-Za-z0-9_-]+\.webp$/);
62
+ expect(url.pathname).not.toContain("abc");
63
+ expect(url.pathname).not.toContain("v5_dataset_flat");
64
+ });
65
+ test("the blob opens to the original path", () => {
66
+ expect(opened(resolver().resolveAsset(IMAGE).getURL())).toBe(PATH);
67
+ });
68
+ test("gives the same image a different URL every time", () => {
69
+ const instance = resolver();
70
+ const urls = new Set(Array.from({ length: 20 }, () => instance.resolveAsset(IMAGE).getURL()));
71
+ expect(urls.size).toBe(20);
72
+ });
73
+ test("expires the URL after the ttl", () => {
74
+ const url = resolver({ ttlSeconds: 120 }).resolveAsset(IMAGE).getURL();
75
+ expect(opened(url, NOW_MS / 1000 + 119)).toBe(PATH);
76
+ expect(() => opened(url, NOW_MS / 1000 + 120)).toThrow();
77
+ });
78
+ test("defaults to the same window as a signed URL", () => {
79
+ const url = resolver().resolveAsset(IMAGE).getURL();
80
+ expect(opened(url, NOW_MS / 1000 + DEFAULT_ASSET_SEAL_TTL_SECONDS - 1)).toBe(PATH);
81
+ expect(() => opened(url, NOW_MS / 1000 + DEFAULT_ASSET_SEAL_TTL_SECONDS)).toThrow();
82
+ });
83
+ test("honours a configured url prefix", () => {
84
+ const url = new URL(resolver({ urlPrefix: "img" }).resolveAsset(IMAGE).getURL());
85
+ expect(url.pathname.startsWith("/img/")).toBe(true);
86
+ });
87
+ test("carries the file extension over", () => {
88
+ for (const [path, expected] of [
89
+ ["/a/b.webp", ".webp"],
90
+ ["/a/b.jpeg", ".jpeg"],
91
+ ["/a/b", ""],
92
+ ["/a/b.", ""],
93
+ ]) {
94
+ const url = resolver().resolveAsset(`${ORIGIN}${path}`).getURL();
95
+ expect(url.endsWith(`${expected}`)).toBe(true);
96
+ }
97
+ });
98
+ test("preserves the original URI on the asset", () => {
99
+ expect(resolver().resolveAsset(IMAGE).URI).toBe(IMAGE);
100
+ });
101
+ test("passes through anything that is not an http url", () => {
102
+ for (const uri of ["/local/fixture.webp", "not a url", "file:///a.webp"]) {
103
+ expect(resolver().resolveAsset(uri).getURL()).toBe(uri);
104
+ }
105
+ });
106
+ test("refuses a url that already has a query string", () => {
107
+ expect(() => resolver().resolveAsset(`${IMAGE}?v=2`)).toThrow(/query string/);
108
+ });
109
+ });
110
+ describe("getSealedAssetsResolver", () => {
111
+ afterEach(() => {
112
+ Reflect.deleteProperty(process.env, "PROSOPO_ASSET_SEAL_KEYS");
113
+ Reflect.deleteProperty(process.env, "PROSOPO_ASSET_SEAL_ACTIVE_KEY_ID");
114
+ });
115
+ test("is off until keys are configured", () => {
116
+ withEnv({ PROSOPO_ASSET_SEAL_KEYS: undefined }, () => {
117
+ expect(getSealedAssetsResolver()).toBeUndefined();
118
+ });
119
+ });
120
+ test("seals with the highest key id by default", () => {
121
+ withEnv({
122
+ PROSOPO_ASSET_SEAL_KEYS: `1:${toBase64Url(new Uint8Array(SEAL_KEY_BYTES).fill(1))},${KEY_ID}:${toBase64Url(KEY)}`,
123
+ PROSOPO_ASSET_SEAL_ACTIVE_KEY_ID: undefined,
124
+ }, () => {
125
+ const url = getSealedAssetsResolver()?.resolveAsset(IMAGE).getURL();
126
+ if (!url)
127
+ throw new Error("expected a resolver");
128
+ expect(opened(url, Math.floor(Date.now() / 1000))).toBe(PATH);
129
+ });
130
+ });
131
+ test("seals with the key id it is told to", () => {
132
+ withEnv({
133
+ PROSOPO_ASSET_SEAL_KEYS: `${KEY_ID}:${toBase64Url(KEY)},9:${toBase64Url(new Uint8Array(SEAL_KEY_BYTES).fill(9))}`,
134
+ PROSOPO_ASSET_SEAL_ACTIVE_KEY_ID: String(KEY_ID),
135
+ }, () => {
136
+ const url = getSealedAssetsResolver()?.resolveAsset(IMAGE).getURL();
137
+ if (!url)
138
+ throw new Error("expected a resolver");
139
+ expect(opened(url, Math.floor(Date.now() / 1000))).toBe(PATH);
140
+ });
141
+ });
142
+ test("refuses an active key id it does not hold", () => {
143
+ withEnv({
144
+ PROSOPO_ASSET_SEAL_KEYS: `${KEY_ID}:${toBase64Url(KEY)}`,
145
+ PROSOPO_ASSET_SEAL_ACTIVE_KEY_ID: "77",
146
+ }, () => {
147
+ expect(() => getSealedAssetsResolver()).toThrow(/77/);
148
+ });
149
+ });
150
+ });
151
+ //# sourceMappingURL=sealedAssetsResolver.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealedAssetsResolver.unit.test.js","sourceRoot":"","sources":["../../../../../src/tests/unit/api/captcha/sealedAssetsResolver.unit.test.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EACN,cAAc,EACd,WAAW,GACX,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACN,8BAA8B,EAC9B,oBAAoB,EACpB,uBAAuB,GACvB,MAAM,iDAAiD,CAAC;AAEzD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACnD,MAAM,MAAM,GAAG,CAAC,CAAC;AACjB,MAAM,MAAM,GAAG,iBAAiB,CAAC;AACjC,MAAM,MAAM,GAAG,iCAAiC,CAAC;AACjD,MAAM,IAAI,GAAG,kCAAkC,CAAC;AAChD,MAAM,KAAK,GAAG,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC;AAEjC,MAAM,QAAQ,GAAG,CAChB,UAAuD,EAAE,EAClC,EAAE,CACzB,IAAI,oBAAoB,CAAC;IACxB,GAAG,EAAE,GAAG;IACR,KAAK,EAAE,MAAM;IACb,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM;IACjB,GAAG,OAAO;CACV,CAAC,CAAC;AAEJ,MAAM,MAAM,GAAG,CAAC,SAAiB,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,EAAU,EAAE;IACxE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IAC9E,MAAM,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI;QACJ,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;QAC9B,UAAU;KACV,CAAC,CAAC;IACH,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CACf,MAA0C,EAC1C,GAAe,EACR,EAAE;IACT,MAAM,KAAK,GAAG,IAAI,GAAG,EAA8B,CAAC;IACpD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC;IACF,CAAC;IACD,IAAI,CAAC;QACJ,GAAG,EAAE,CAAC;IACP,CAAC;YAAS,CAAC;QACV,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;YACnC,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;gBACzB,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACP,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;YAC3B,CAAC;QACF,CAAC;IACF,CAAC;AACF,CAAC,CAAC;AAEF,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACrC,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wDAAwD,EAAE,GAAG,EAAE;QACnE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;QAC5D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE;QAC5D,MAAM,QAAQ,GAAG,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,GAAG,CACnB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CACvE,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+BAA+B,EAAE,GAAG,EAAE;QAC1C,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QACvE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACxD,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QACpD,MAAM,CACL,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,8BAA8B,GAAG,CAAC,CAAC,CAC/D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,CAAC,GAAG,EAAE,CACX,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,8BAA8B,CAAC,CAC3D,CAAC,OAAO,EAAE,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAClB,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAC3D,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI;YAC9B,CAAC,WAAW,EAAE,OAAO,CAAC;YACtB,CAAC,WAAW,EAAE,OAAO,CAAC;YACtB,CAAC,MAAM,EAAE,EAAE,CAAC;YACZ,CAAC,OAAO,EAAE,EAAE,CAAC;SACb,EAAE,CAAC;YACH,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC,YAAY,CAAC,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;YACjE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE;QAC5D,KAAK,MAAM,GAAG,IAAI,CAAC,qBAAqB,EAAE,WAAW,EAAE,gBAAgB,CAAC,EAAE,CAAC;YAC1E,MAAM,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzD,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAC5D,cAAc,CACd,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACxC,SAAS,CAAC,GAAG,EAAE;QACd,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;QAC/D,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,kCAAkC,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC7C,OAAO,CAAC,EAAE,uBAAuB,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE;YACpD,MAAM,CAAC,uBAAuB,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;QACnD,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACrD,OAAO,CACN;YACC,uBAAuB,EAAE,KAAK,WAAW,CAAC,IAAI,UAAU,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE;YACjH,gCAAgC,EAAE,SAAS;SAC3C,EACD,GAAG,EAAE;YACJ,MAAM,GAAG,GAAG,uBAAuB,EAAE,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;YACpE,IAAI,CAAC,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACjD,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,CAAC,CACD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAChD,OAAO,CACN;YACC,uBAAuB,EAAE,GAAG,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,IAAI,UAAU,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;YACjH,gCAAgC,EAAE,MAAM,CAAC,MAAM,CAAC;SAChD,EACD,GAAG,EAAE;YACJ,MAAM,GAAG,GAAG,uBAAuB,EAAE,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;YACpE,IAAI,CAAC,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACjD,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,CAAC,CACD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACtD,OAAO,CACN;YACC,uBAAuB,EAAE,GAAG,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE;YACxD,gCAAgC,EAAE,IAAI;SACtC,EACD,GAAG,EAAE;YACJ,MAAM,CAAC,GAAG,EAAE,CAAC,uBAAuB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvD,CAAC,CACD,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { IpAddressType } from "@prosopo/types";
2
2
  import { describe, expect, it } from "vitest";
3
- import { getCompositeIpAddress, getIpAddressFromComposite, } from "../../compositeIpAddress.js";
3
+ import { getCompositeIpAddress, getIpAddressFromComposite, isSameIpOrigin, } from "../../compositeIpAddress.js";
4
4
  describe("compositeIpAddress", () => {
5
5
  describe("getCompositeIpAddress", () => {
6
6
  it("should convert IPv4 string to composite format", () => {
@@ -154,5 +154,41 @@ describe("compositeIpAddress", () => {
154
154
  });
155
155
  });
156
156
  });
157
+ describe("isSameIpOrigin", () => {
158
+ it("matches an unchanged IPv4 address", () => {
159
+ expect(isSameIpOrigin(getCompositeIpAddress("203.0.113.7"), getCompositeIpAddress("203.0.113.7"))).toBe(true);
160
+ });
161
+ it("separates two IPv4 addresses, however close", () => {
162
+ expect(isSameIpOrigin(getCompositeIpAddress("203.0.113.7"), getCompositeIpAddress("203.0.113.8"))).toBe(false);
163
+ });
164
+ it("treats a rotated IPv6 interface identifier as the same network", () => {
165
+ expect(isSameIpOrigin(getCompositeIpAddress("2001:db8:abcd:1234::1"), getCompositeIpAddress("2001:db8:abcd:1234:9f3a:2b01:cc:77"))).toBe(true);
166
+ });
167
+ it("separates two different IPv6 /64s", () => {
168
+ expect(isSameIpOrigin(getCompositeIpAddress("2001:db8:abcd:1234::1"), getCompositeIpAddress("2001:db8:abcd:5678::1"))).toBe(false);
169
+ });
170
+ it("separates a v4 address from a v6 one", () => {
171
+ expect(isSameIpOrigin(getCompositeIpAddress("203.0.113.7"), getCompositeIpAddress("2001:db8::1"))).toBe(false);
172
+ });
173
+ it("calls an unreadable value the same, rather than escalating on it", () => {
174
+ const unreadable = {
175
+ lower: { toString: () => "not a number" },
176
+ type: IpAddressType.v4,
177
+ };
178
+ expect(isSameIpOrigin(unreadable, getCompositeIpAddress("203.0.113.7"))).toBe(true);
179
+ });
180
+ it("calls an unread address the same, rather than escalating on it", () => {
181
+ expect(isSameIpOrigin(getCompositeIpAddress("not an address"), getCompositeIpAddress("203.0.113.7"))).toBe(true);
182
+ expect(isSameIpOrigin(getCompositeIpAddress("203.0.113.7"), getCompositeIpAddress("not an address"))).toBe(true);
183
+ });
184
+ it("reads a Decimal128-style value rather than throwing on it", () => {
185
+ const asDecimal128 = {
186
+ lower: { toString: () => "3405803783" },
187
+ type: IpAddressType.v4,
188
+ };
189
+ expect(isSameIpOrigin(asDecimal128, getCompositeIpAddress("203.0.113.7"))).toBe(true);
190
+ expect(isSameIpOrigin(asDecimal128, getCompositeIpAddress("203.0.113.8"))).toBe(false);
191
+ });
192
+ });
157
193
  });
158
194
  //# sourceMappingURL=compositeIpAddress.unit.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compositeIpAddress.unit.test.js","sourceRoot":"","sources":["../../../src/tests/unit/compositeIpAddress.unit.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compositeIpAddress.unit.test.js","sourceRoot":"","sources":["../../../src/tests/unit/compositeIpAddress.unit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=assetSealHandler.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assetSealHandler.unit.test.d.ts","sourceRoot":"","sources":["../../../../src/tests/unit/edge/assetSealHandler.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,145 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { sealAssetPath } from "../../../api/captcha/assetSeal.js";
3
+ import { SEAL_KEY_BYTES } from "../../../api/captcha/assetSealFormat.js";
4
+ import { SealedAssetsResolver, getSealedAssetsResolver, } from "../../../api/captcha/sealedAssetsResolver.js";
5
+ import { createAssetSealHandler, } from "../../../edge/assetSealHandler.js";
6
+ const KEY = new Uint8Array(SEAL_KEY_BYTES).fill(5);
7
+ const OTHER_KEY = new Uint8Array(SEAL_KEY_BYTES).fill(6);
8
+ const KEY_ID = 2;
9
+ const NOW_MS = 1_700_000_000_000;
10
+ const NOW_SECONDS = NOW_MS / 1000;
11
+ const ZONE = "https://prosopoimages.b-cdn.net";
12
+ const PATH = "/v5_dataset_flat/images/abc.webp";
13
+ const handler = (options = {}) => createAssetSealHandler({
14
+ keys: options.keys ?? new Map([[KEY_ID, KEY]]),
15
+ urlPrefix: options.urlPrefix ?? "s",
16
+ now: () => options.nowMs ?? NOW_MS,
17
+ ...(options.requiredPathPrefix !== undefined
18
+ ? { requiredPathPrefix: options.requiredPathPrefix }
19
+ : {}),
20
+ });
21
+ const sealedUrl = (options = {}) => `${ZONE}/s/${sealAssetPath({
22
+ path: options.path ?? PATH,
23
+ key: options.key ?? KEY,
24
+ keyId: options.keyId ?? KEY_ID,
25
+ expiresAtSeconds: NOW_SECONDS + (options.ttl ?? 300),
26
+ })}.webp`;
27
+ const rewritten = async (url, handle = handler()) => handle(new Request(url));
28
+ describe("a sealed request", () => {
29
+ test("is rewritten onto the canonical path", async () => {
30
+ const result = await rewritten(sealedUrl());
31
+ expect(result).toBeInstanceOf(Request);
32
+ expect(new URL(result.url).pathname).toBe(PATH);
33
+ });
34
+ test("keeps the zone it arrived on", async () => {
35
+ const result = await rewritten(sealedUrl());
36
+ expect(new URL(result.url).origin).toBe(ZONE);
37
+ });
38
+ test("carries the method and headers over", async () => {
39
+ const result = await handler()(new Request(sealedUrl(), {
40
+ method: "GET",
41
+ headers: { accept: "image/webp", "x-forwarded-for": "203.0.113.7" },
42
+ }));
43
+ expect(result.method).toBe("GET");
44
+ expect(result.headers.get("accept")).toBe("image/webp");
45
+ expect(result.headers.get("x-forwarded-for")).toBe("203.0.113.7");
46
+ });
47
+ test("drops a query string someone else added", async () => {
48
+ const result = await rewritten(`${sealedUrl()}?width=99`);
49
+ expect(new URL(result.url).search).toBe("");
50
+ });
51
+ test("rewrites two different seals of one image onto the same path", async () => {
52
+ const handle = handler();
53
+ const first = await rewritten(sealedUrl(), handle);
54
+ const second = await rewritten(sealedUrl(), handle);
55
+ expect(new URL(first.url).pathname).toBe(new URL(second.url).pathname);
56
+ });
57
+ });
58
+ describe("what the edge refuses", () => {
59
+ const refused = async (url, handle) => {
60
+ const result = await rewritten(url, handle);
61
+ expect(result).toBeInstanceOf(Response);
62
+ return result;
63
+ };
64
+ test("an expired seal", async () => {
65
+ const url = sealedUrl({ ttl: 60 });
66
+ const response = await refused(url, handler({ nowMs: NOW_MS + 60_000 }));
67
+ expect(response.status).toBe(404);
68
+ });
69
+ test("a seal made with a key the edge does not hold", async () => {
70
+ const response = await refused(sealedUrl({ key: OTHER_KEY, keyId: 7 }));
71
+ expect(response.status).toBe(404);
72
+ });
73
+ test("a tampered blob", async () => {
74
+ const url = sealedUrl();
75
+ const flipped = url.replace(/(.)(\.webp)$/, (_, last, ext) => `${"A" === last ? "B" : "A"}${ext}`);
76
+ const response = await refused(flipped);
77
+ expect(response.status).toBe(404);
78
+ });
79
+ test("a path outside the required prefix", async () => {
80
+ const response = await refused(sealedUrl({ path: "/elsewhere/a.webp" }), handler({ requiredPathPrefix: "/v5_dataset_flat/" }));
81
+ expect(response.status).toBe(404);
82
+ });
83
+ test("noise under the prefix", async () => {
84
+ for (const blob of ["", "x", "not-a-seal", "%20"]) {
85
+ expect((await refused(`${ZONE}/s/${blob}`)).status).toBe(404);
86
+ }
87
+ });
88
+ test("and never lets the refusal be cached", async () => {
89
+ const response = await refused(sealedUrl({ key: OTHER_KEY, keyId: 7 }));
90
+ expect(response.headers.get("cache-control")).toBe("no-store");
91
+ });
92
+ });
93
+ describe("what the edge leaves alone", () => {
94
+ test("a request that is not under the prefix", async () => {
95
+ const result = await rewritten(`${ZONE}/health`);
96
+ expect(result).toBeInstanceOf(Request);
97
+ expect(new URL(result.url).pathname).toBe("/health");
98
+ });
99
+ test("an already-rewritten request, so running twice is safe", async () => {
100
+ const handle = handler();
101
+ const once = await rewritten(sealedUrl(), handle);
102
+ const twice = await handle(once);
103
+ expect(twice).toBeInstanceOf(Request);
104
+ expect(new URL(twice.url).pathname).toBe(PATH);
105
+ });
106
+ test("a request under a different prefix from the one configured", async () => {
107
+ const result = await rewritten(sealedUrl(), handler({ urlPrefix: "img" }));
108
+ expect(result).toBeInstanceOf(Request);
109
+ });
110
+ });
111
+ describe("the provider and the edge agree", () => {
112
+ test("what node seals, webcrypto opens", async () => {
113
+ const url = new SealedAssetsResolver({
114
+ key: KEY,
115
+ keyId: KEY_ID,
116
+ now: () => NOW_MS,
117
+ })
118
+ .resolveAsset(`${ZONE}${PATH}`)
119
+ .getURL();
120
+ const result = await rewritten(url);
121
+ expect(new URL(result.url).pathname).toBe(PATH);
122
+ });
123
+ test("end to end, from the env the provider reads", async () => {
124
+ const saved = process.env.PROSOPO_ASSET_SEAL_KEYS;
125
+ process.env.PROSOPO_ASSET_SEAL_KEYS = `${KEY_ID}:${Buffer.from(KEY).toString("base64")}`;
126
+ try {
127
+ const url = getSealedAssetsResolver()
128
+ ?.resolveAsset(`${ZONE}${PATH}`)
129
+ .getURL();
130
+ if (!url)
131
+ throw new Error("expected a resolver");
132
+ const result = await rewritten(url, handler({ nowMs: Date.now() }));
133
+ expect(new URL(result.url).pathname).toBe(PATH);
134
+ }
135
+ finally {
136
+ if (undefined === saved) {
137
+ Reflect.deleteProperty(process.env, "PROSOPO_ASSET_SEAL_KEYS");
138
+ }
139
+ else {
140
+ process.env.PROSOPO_ASSET_SEAL_KEYS = saved;
141
+ }
142
+ }
143
+ });
144
+ });
145
+ //# sourceMappingURL=assetSealHandler.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assetSealHandler.unit.test.js","sourceRoot":"","sources":["../../../../src/tests/unit/edge/assetSealHandler.unit.test.ts"],"names":[],"mappings":"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"}
@@ -954,6 +954,31 @@ describe("CaptchaManager", () => {
954
954
  sessionId: "session-abc",
955
955
  });
956
956
  });
957
+ it("returns a score of zero rather than dropping the field", () => {
958
+ const result = captchaManager.getVerificationResponse(true, {
959
+ account: "account",
960
+ tier: Tier.Professional,
961
+ }, () => "translated", 0);
962
+ expect(result).toEqual({
963
+ status: "translated",
964
+ verified: true,
965
+ score: 0,
966
+ });
967
+ });
968
+ it("omits the score when there is no score to report", () => {
969
+ const result = captchaManager.getVerificationResponse(true, {
970
+ account: "account",
971
+ tier: Tier.Professional,
972
+ }, () => "translated", undefined);
973
+ expect(result).not.toHaveProperty("score");
974
+ });
975
+ it("still hides a zero score from the free tier", () => {
976
+ const result = captchaManager.getVerificationResponse(true, {
977
+ account: "account",
978
+ tier: Tier.Free,
979
+ }, () => "translated", 0);
980
+ expect(result).not.toHaveProperty("score");
981
+ });
957
982
  it("should omit the sessionId when there isn't one", () => {
958
983
  const result = captchaManager.getVerificationResponse(true, {
959
984
  account: "account",
@@ -962,6 +987,22 @@ describe("CaptchaManager", () => {
962
987
  expect(result).not.toHaveProperty("sessionId");
963
988
  });
964
989
  });
990
+ describe("canClientSeeScore", () => {
991
+ it("answers with a boolean, not the score or the tier", () => {
992
+ expect(typeof CaptchaManager.canClientSeeScore(Tier.Professional, 0)).toBe("boolean");
993
+ expect(typeof CaptchaManager.canClientSeeScore(Tier.Professional, undefined)).toBe("boolean");
994
+ });
995
+ it("shows a zero score to a paying tier", () => {
996
+ expect(CaptchaManager.canClientSeeScore(Tier.Professional, 0)).toBe(true);
997
+ });
998
+ it("withholds the score from the free tier at any value", () => {
999
+ expect(CaptchaManager.canClientSeeScore(Tier.Free, 0)).toBe(false);
1000
+ expect(CaptchaManager.canClientSeeScore(Tier.Free, 0.5)).toBe(false);
1001
+ });
1002
+ it("has nothing to show when no score was computed", () => {
1003
+ expect(CaptchaManager.canClientSeeScore(Tier.Professional, undefined)).toBe(false);
1004
+ });
1005
+ });
965
1006
  describe("decodeSubmissionPayloads", () => {
966
1007
  const bundle = { key: "pk", innerConfig: "cfg" };
967
1008
  beforeEach(() => {