@prosopo/user-access-policy 3.12.33 → 3.14.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 (64) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +11 -10
  2. package/.turbo/turbo-build$colon$tsc.log +14 -14
  3. package/.turbo/turbo-build.log +12 -11
  4. package/CHANGELOG.md +53 -0
  5. package/dist/.export.d.ts +1 -0
  6. package/dist/.export.d.ts.map +1 -1
  7. package/dist/.export.js +2 -1
  8. package/dist/.export.js.map +1 -1
  9. package/dist/cjs/.export.cjs +8 -0
  10. package/dist/cjs/headerMatch.cjs +122 -0
  11. package/dist/cjs/mongoose/mongooseRuleSchema.cjs +20 -0
  12. package/dist/cjs/redis/reader/redisRulesReader.cjs +5 -0
  13. package/dist/cjs/redis/redisRuleIndex.cjs +20 -0
  14. package/dist/cjs/rule.cjs +1 -0
  15. package/dist/cjs/ruleInput/userScopeInput.cjs +13 -1
  16. package/dist/cjs/ruleRecord.cjs +6 -1
  17. package/dist/headerMatch.d.ts +13 -0
  18. package/dist/headerMatch.d.ts.map +1 -0
  19. package/dist/headerMatch.js +116 -0
  20. package/dist/headerMatch.js.map +1 -0
  21. package/dist/mongoose/mongooseRuleSchema.d.ts.map +1 -1
  22. package/dist/mongoose/mongooseRuleSchema.js +20 -0
  23. package/dist/mongoose/mongooseRuleSchema.js.map +1 -1
  24. package/dist/redis/reader/redisRulesReader.d.ts.map +1 -1
  25. package/dist/redis/reader/redisRulesReader.js +5 -0
  26. package/dist/redis/reader/redisRulesReader.js.map +1 -1
  27. package/dist/redis/redisRuleIndex.d.ts.map +1 -1
  28. package/dist/redis/redisRuleIndex.js +20 -0
  29. package/dist/redis/redisRuleIndex.js.map +1 -1
  30. package/dist/rule.d.ts +7 -1
  31. package/dist/rule.d.ts.map +1 -1
  32. package/dist/rule.js +1 -0
  33. package/dist/rule.js.map +1 -1
  34. package/dist/ruleInput/ruleInput.d.ts +30 -0
  35. package/dist/ruleInput/ruleInput.d.ts.map +1 -1
  36. package/dist/ruleInput/userScopeInput.d.ts +40 -0
  37. package/dist/ruleInput/userScopeInput.d.ts.map +1 -1
  38. package/dist/ruleInput/userScopeInput.js +13 -1
  39. package/dist/ruleInput/userScopeInput.js.map +1 -1
  40. package/dist/ruleRecord.d.ts +2 -2
  41. package/dist/ruleRecord.d.ts.map +1 -1
  42. package/dist/ruleRecord.js +6 -1
  43. package/dist/ruleRecord.js.map +1 -1
  44. package/dist/tests/headerMatch.unit.test.d.ts +2 -0
  45. package/dist/tests/headerMatch.unit.test.d.ts.map +1 -0
  46. package/dist/tests/headerMatch.unit.test.js +202 -0
  47. package/dist/tests/headerMatch.unit.test.js.map +1 -0
  48. package/dist/tests/redis/reader/redisRulesQuery.unit.test.js +3 -3
  49. package/dist/tests/redis/reader/redisRulesQuery.unit.test.js.map +1 -1
  50. package/dist/tests/transformRule.unit.test.js +5 -0
  51. package/dist/tests/transformRule.unit.test.js.map +1 -1
  52. package/package.json +11 -11
  53. package/src/.export.ts +11 -0
  54. package/src/headerMatch.ts +180 -0
  55. package/src/mongoose/mongooseRuleSchema.ts +5 -0
  56. package/src/redis/reader/redisRulesReader.ts +7 -0
  57. package/src/redis/redisRuleIndex.ts +14 -0
  58. package/src/rule.ts +29 -0
  59. package/src/ruleInput/userScopeInput.ts +28 -0
  60. package/src/ruleRecord.ts +5 -0
  61. package/src/tests/headerMatch.unit.test.ts +350 -0
  62. package/src/tests/redis/reader/redisRulesQuery.unit.test.ts +3 -3
  63. package/src/tests/transformRule.unit.test.ts +5 -0
  64. package/tsconfig.tsbuildinfo +1 -1
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/user-access-policy@3.12.33 build:cjs
2
+ > @prosopo/user-access-policy@3.14.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: .
@@ -33,7 +33,7 @@ ViteCommonJSConfig: .
33
33
  vite v8.1.5 building ssr environment for production...
34
34
  Bundle build started
35
35
  
36
- ✓ 39 modules transformed.
36
+ ✓ 40 modules transformed.
37
37
  rendering chunks...
38
38
  computing gzip size...
39
39
  dist/cjs/api/accessRulesApiClient.cjs 0.00 kB │ gzip: 0.02 kB
@@ -43,11 +43,11 @@ dist/cjs/api/write/.export.cjs 0.00 kB │ gzip: 0.02 kB
43
43
  dist/cjs/mongoose/.export.cjs 0.25 kB │ gzip: 0.16 kB
44
44
  dist/cjs/rulesStorage.cjs 0.34 kB │ gzip: 0.22 kB
45
45
  dist/cjs/redis/.export.cjs 0.40 kB │ gzip: 0.18 kB
46
- dist/cjs/rule.cjs 0.45 kB │ gzip: 0.26 kB
46
+ dist/cjs/rule.cjs 0.48 kB │ gzip: 0.27 kB
47
47
  dist/cjs/api/.export.cjs 0.53 kB │ gzip: 0.21 kB
48
48
  dist/cjs/ruleInput/.export.cjs 0.57 kB │ gzip: 0.20 kB
49
49
  dist/cjs/api/delete/deleteAllRules.cjs 0.79 kB │ gzip: 0.41 kB
50
- dist/cjs/ruleRecord.cjs 0.84 kB │ gzip: 0.36 kB
50
+ dist/cjs/ruleRecord.cjs 0.92 kB │ gzip: 0.39 kB
51
51
  dist/cjs/_virtual/_rolldown/runtime.cjs 1.00 kB │ gzip: 0.50 kB
52
52
  dist/cjs/api/read/getMissingIds.cjs 1.12 kB │ gzip: 0.51 kB
53
53
  dist/cjs/api/read/fetchRules.cjs 1.22 kB │ gzip: 0.54 kB
@@ -56,24 +56,25 @@ dist/cjs/api/delete/deleteRuleGroups.cjs 1.43 kB │ gzip: 0.65 kB
56
56
  dist/cjs/classifyBrowser.cjs 1.45 kB │ gzip: 0.73 kB
57
57
  dist/cjs/api/write/rehashRules.cjs 1.48 kB │ gzip: 0.56 kB
58
58
  dist/cjs/api/delete/deleteRules.cjs 1.52 kB │ gzip: 0.65 kB
59
- dist/cjs/mongoose/mongooseRuleSchema.cjs 1.57 kB │ gzip: 0.43 kB
60
59
  dist/cjs/api/read/findRuleIds.cjs 1.58 kB │ gzip: 0.66 kB
61
- dist/cjs/.export.cjs 1.88 kB │ gzip: 0.45 kB
60
+ dist/cjs/mongoose/mongooseRuleSchema.cjs 1.85 kB │ gzip: 0.46 kB
62
61
  dist/cjs/redis/redisClient.cjs 1.92 kB │ gzip: 0.72 kB
63
62
  dist/cjs/classifyOs.cjs 2.04 kB │ gzip: 1.05 kB
64
63
  dist/cjs/ruleInput/policyInput.cjs 2.09 kB │ gzip: 0.79 kB
65
64
  dist/cjs/ruleInput/ruleInput.cjs 2.28 kB │ gzip: 0.69 kB
66
65
  dist/cjs/matchedRule.cjs 2.29 kB │ gzip: 1.03 kB
67
- dist/cjs/ruleInput/userScopeInput.cjs 2.48 kB │ gzip: 0.78 kB
66
+ dist/cjs/.export.cjs 2.44 kB │ gzip: 0.57 kB
68
67
  dist/cjs/api/rulesApiClient.cjs 2.53 kB │ gzip: 0.68 kB
69
68
  dist/cjs/redis/reader/redisAggregate.cjs 2.57 kB │ gzip: 1.04 kB
70
69
  dist/cjs/transformRule.cjs 2.90 kB │ gzip: 1.03 kB
70
+ dist/cjs/ruleInput/userScopeInput.cjs 3.02 kB │ gzip: 0.95 kB
71
71
  dist/cjs/api/write/insertRules.cjs 3.12 kB │ gzip: 1.09 kB
72
- dist/cjs/redis/redisRuleIndex.cjs 3.20 kB │ gzip: 0.83 kB
73
72
  dist/cjs/redis/redisRulesWriter.cjs 3.50 kB │ gzip: 1.15 kB
73
+ dist/cjs/redis/redisRuleIndex.cjs 3.64 kB │ gzip: 0.87 kB
74
74
  dist/cjs/api/ruleApiRoutes.cjs 4.14 kB │ gzip: 1.06 kB
75
75
  dist/cjs/redis/reader/redisRulesQuery.cjs 4.76 kB │ gzip: 1.58 kB
76
76
  dist/cjs/redis/reader/redisRulesSplitQuery.cjs 5.17 kB │ gzip: 2.17 kB
77
- dist/cjs/redis/reader/redisRulesReader.cjs 11.76 kB │ gzip: 2.85 kB
77
+ dist/cjs/headerMatch.cjs 5.74 kB │ gzip: 2.25 kB
78
+ dist/cjs/redis/reader/redisRulesReader.cjs 11.85 kB │ gzip: 2.88 kB
78
79
 
79
- ✓ built in 147ms
80
+ ✓ built in 99ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/user-access-policy@3.12.33 build:tsc
2
+ > @prosopo/user-access-policy@3.14.0 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 10:39:27 PM - Projects in this build:
5
+ 11:40:48 AM - Projects in this build:
6
6
  * ../../dev/config/tsconfig.json
7
7
  * ../locale/tsconfig.json
8
8
  * ../util/tsconfig.json
@@ -15,27 +15,27 @@
15
15
  * ../api/tsconfig.json
16
16
  * tsconfig.json
17
17
 
18
- 10:39:27 PM - 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'
18
+ 11:40:48 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/vite/vite.backend.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
19
19
 
20
- 10:39:27 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
20
+ 11:40:48 AM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
21
21
 
22
- 10:39:27 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
22
+ 11:40:48 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
23
23
 
24
- 10:39:27 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/logger.ts' is older than output '../logger/tsconfig.tsbuildinfo'
24
+ 11:40:48 AM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
25
25
 
26
- 10:39:27 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'
26
+ 11:40:48 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'
27
27
 
28
- 10:39:27 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'
28
+ 11:40:48 AM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/types.test-d.ts' is older than output '../types/tsconfig.tsbuildinfo'
29
29
 
30
- 10:39:27 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/tests/utils/batches.unit.test.ts' is older than output '../common/tsconfig.tsbuildinfo'
30
+ 11:40:48 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/error.ts' is older than output '../common/tsconfig.tsbuildinfo'
31
31
 
32
- 10:39:27 PM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/index.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
32
+ 11:40:48 AM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
33
33
 
34
- 10:39:27 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
34
+ 11:40:48 AM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
35
35
 
36
- 10:39:27 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/tests/apiHarness.ts' is older than output '../api/tsconfig.tsbuildinfo'
36
+ 11:40:48 AM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
37
37
 
38
- 10:39:27 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
38
+ 11:40:48 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
39
39
 
40
- 10:39:27 PM - Building project '/home/runner/work/captcha/captcha/packages/user-access-policy/tsconfig.json'...
40
+ 11:40:48 AM - Building project '/home/runner/work/captcha/captcha/packages/user-access-policy/tsconfig.json'...
41
41
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/user-access-policy@3.12.33 build
2
+ > @prosopo/user-access-policy@3.14.0 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/user-access-policy@3.12.33 build:cross-env
6
+ > @prosopo/user-access-policy@3.14.0 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -37,7 +37,7 @@ ViteEsmConfig: .
37
37
  vite v8.1.5 building ssr environment for production...
38
38
  Bundle build started
39
39
  
40
- ✓ 39 modules transformed.
40
+ ✓ 40 modules transformed.
41
41
  rendering chunks...
42
42
  computing gzip size...
43
43
  dist/_virtual/_rolldown/runtime.js 0.04 kB │ gzip: 0.06 kB
@@ -50,34 +50,35 @@ dist/redis/.export.js 0.24 kB │ gzip: 0.14 kB
50
50
  dist/rulesStorage.js 0.29 kB │ gzip: 0.19 kB
51
51
  dist/ruleInput/.export.js 0.30 kB │ gzip: 0.14 kB
52
52
  dist/api/.export.js 0.31 kB │ gzip: 0.16 kB
53
- dist/rule.js 0.36 kB │ gzip: 0.23 kB
54
- dist/ruleRecord.js 0.67 kB │ gzip: 0.33 kB
53
+ dist/rule.js 0.40 kB │ gzip: 0.24 kB
55
54
  dist/api/delete/deleteAllRules.js 0.73 kB │ gzip: 0.37 kB
55
+ dist/ruleRecord.js 0.76 kB │ gzip: 0.36 kB
56
56
  dist/api/read/getMissingIds.js 1.02 kB │ gzip: 0.48 kB
57
57
  dist/api/read/fetchRules.js 1.07 kB │ gzip: 0.49 kB
58
- dist/.export.js 1.09 kB │ gzip: 0.33 kB
59
58
  dist/redis/redisRulesStorage.js 1.22 kB │ gzip: 0.45 kB
60
59
  dist/api/delete/deleteRuleGroups.js 1.32 kB │ gzip: 0.61 kB
61
60
  dist/api/delete/deleteRules.js 1.36 kB │ gzip: 0.58 kB
62
61
  dist/classifyBrowser.js 1.38 kB │ gzip: 0.71 kB
63
62
  dist/api/read/findRuleIds.js 1.39 kB │ gzip: 0.60 kB
64
63
  dist/api/write/rehashRules.js 1.42 kB │ gzip: 0.53 kB
65
- dist/mongoose/mongooseRuleSchema.js 1.52 kB │ gzip: 0.41 kB
64
+ dist/.export.js 1.43 kB │ gzip: 0.42 kB
66
65
  dist/redis/redisClient.js 1.78 kB │ gzip: 0.68 kB
66
+ dist/mongoose/mongooseRuleSchema.js 1.79 kB │ gzip: 0.44 kB
67
67
  dist/ruleInput/ruleInput.js 1.80 kB │ gzip: 0.63 kB
68
68
  dist/ruleInput/policyInput.js 1.84 kB │ gzip: 0.73 kB
69
69
  dist/classifyOs.js 1.98 kB │ gzip: 1.03 kB
70
70
  dist/api/rulesApiClient.js 2.12 kB │ gzip: 0.62 kB
71
- dist/ruleInput/userScopeInput.js 2.13 kB │ gzip: 0.68 kB
72
71
  dist/matchedRule.js 2.22 kB │ gzip: 1.00 kB
73
72
  dist/redis/reader/redisAggregate.js 2.35 kB │ gzip: 0.99 kB
74
73
  dist/transformRule.js 2.40 kB │ gzip: 0.94 kB
75
- dist/redis/redisRuleIndex.js 2.58 kB │ gzip: 0.75 kB
74
+ dist/ruleInput/userScopeInput.js 2.65 kB │ gzip: 0.85 kB
76
75
  dist/api/write/insertRules.js 2.87 kB │ gzip: 1.03 kB
76
+ dist/redis/redisRuleIndex.js 2.95 kB │ gzip: 0.79 kB
77
77
  dist/redis/redisRulesWriter.js 3.03 kB │ gzip: 1.07 kB
78
78
  dist/api/ruleApiRoutes.js 3.68 kB │ gzip: 0.99 kB
79
79
  dist/redis/reader/redisRulesQuery.js 4.53 kB │ gzip: 1.54 kB
80
80
  dist/redis/reader/redisRulesSplitQuery.js 4.98 kB │ gzip: 2.14 kB
81
- dist/redis/reader/redisRulesReader.js 10.34 kB │ gzip: 2.71 kB
81
+ dist/headerMatch.js 5.51 kB │ gzip: 2.22 kB
82
+ dist/redis/reader/redisRulesReader.js 10.44 kB │ gzip: 2.74 kB
82
83
 
83
- ✓ built in 274ms
84
+ ✓ built in 239ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # @prosopo/user-access-policy
2
2
 
3
+ ## 3.14.0
4
+ ### Minor Changes
5
+
6
+ - af267c2: Web Bot Auth verifier and an authenticated frictionless flow for pre-verified agents.
7
+
8
+ **`@prosopo/web-bot-auth`** — a new package: an RFC 9421 HTTP Message Signatures verifier built on `@noble/curves/ed25519`, with no Cloudflare dependency. It parses `Signature-Agent` in both its bare-string and dictionary forms, resolves the signer's JWKS at `/.well-known/http-message-signatures-directory` honouring the response's cache-control TTL, and verifies the Ed25519 signature over the RFC 9421 signature base.
9
+
10
+ **Provider fast path.** `/captcha/frictionless` returns `captchaType: authenticated` when a non-`deferToVerify` `AccessPolicyType.Allow` rule matches the request's user scope, and writes a session with `serverChecked: false`, `agent: true` and the issuing IP frozen for verify-time binding. Decrypt, bot score and the decision machine are all skipped. A verified `Signature-Agent` is one way to qualify — the userScope gains a `webBotAuthAgent` field, set only when signature verification succeeded so a rule scoped to a signer can never be matched by a spoofed header — but an IP CIDR, JA4, user agent, ASN or country rule qualifies the same way. A `Block` or `Restrict` on the same match set always wins, because severity outranks Allow.
11
+
12
+ **`/client/authenticated/verify`.** A separate router with mandatory IP binding — the operator must forward the client IP (`API.AUTHENTICATED_IP_REQUIRED`) and it must match the one the session was issued to (`API.AUTHENTICATED_IP_MISMATCH`), so a leaked token cannot be replayed from elsewhere. Single use is enforced through `serverChecked`, and `captchaType` is checked so an ordinary captcha token cannot be redeemed on this route. `clientSessionId` correlation goes through the same `isClientSessionMismatch` helper as pow / image / puzzle, so the authenticated path cannot drift from the others.
13
+
14
+ **Surface.** `AccessPolicyType.Allow` and `CaptchaType.authenticated`; `webBotAuthAgent` on the user scope (indexed, normalised at parse time to a lowercase scheme+host with no trailing slash); `Session.agent` / `Session.webBotAuthAgent` for the Traffic view's "pre-verified pass" filter; `submitAuthenticatedCaptchaVerify` on `ProviderApi` and the matching branch in `@prosopo/server.verifyProvider`; `AuthenticatedBadge` and a dispatch branch in `procaptcha-frictionless`.
15
+
16
+ Three fixes the new end-to-end coverage turned up, each of which broke the flow outright:
17
+
18
+ - `ipMatchesSession` compared the operator's parsed IP against the session's composite halves with `===`. A session read back from Mongo carries BSON (`Decimal128`, or `Long` on pre-migration records), not the `bigint` the type claims, so the comparison was false for every session that had been through the database — every legitimate redemption was rejected as `API.AUTHENTICATED_IP_MISMATCH`. Both halves are now normalised before comparison, and an unparseable half fails closed rather than defaulting to `0n`, so garbage still cannot match garbage.
19
+ - `serverChecked` was never written onto the authenticated session, so "never set" and "consumed" were distinguishable only by an absence. It is now written as `false` at issuance.
20
+ - `serverChecked` was missing from `SESSION_PROJECTION`. Left out, the single-use check reads `undefined` and an authenticated token verifies an unlimited number of times.
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies [929d99b]
25
+ - Updated dependencies [934fa5d]
26
+ - Updated dependencies [27f525e]
27
+ - Updated dependencies [af267c2]
28
+ - @prosopo/types@5.8.0
29
+ - @prosopo/api@4.2.0
30
+
31
+ ## 3.13.0
32
+ ### Minor Changes
33
+
34
+ - e22d5fb: Add an arbitrary-header match dimension to user access policies.
35
+
36
+ A `Block` or `Restrict` rule can now target a named request header with an `equals`, `contains`, `notEquals`, `notContains`, `notEqualsAny` or `notContainsAny` operator. The negated operators back the portal's allow-list mode — block unless the header matches; the `*Any` pair carries a list of accepted values in `headerValue` so an allow-list over several values of the same header fires only when the header matches none of them (separate single-value rules would each fire on the other's value and block everything). Because substring `contains` and per-rule operators can't be expressed as a Redis TAG query — and an allow-list rule must still fire on a request that omits the header — the header condition is carried on the rule as `headerName`/`headerValue`/`headerOperator` and evaluated in code against the raw request headers, while an indexed `headerMatch` sentinel makes every header rule a matching candidate for every request. Header rules contribute one point to rule specificity ranking, mirroring the other scalar dimensions.
37
+
38
+ The raw request headers are a **required** argument of `getPrioritisedAccessRule` / `CaptchaManager.getPrioritisedAccessPolicies`, with no default. A negated header operator treats a missing header as "does not match", so a lookup that quietly ran with an empty header map would fire every allow-list rule on every request. Every lookup now passes them: the request-time block middleware, the verify-path hard-block check, the `/frictionless` policy and dedup lookups, and the image / PoW / puzzle challenge endpoints.
39
+
40
+ `getPrioritisedAccessRule` now caches only the candidate fetch and ranks per request, because the header verdict depends on data that is not part of the cache key. This also closes a latent gap where `os` was never part of `hardBlockCacheKey`, so a cached ranked list could serve one operating system's verdict to a request from another.
41
+
42
+ ### Patch Changes
43
+
44
+ - 6f57ee9: chore(deps): bump yargs from 17.7.2 to 18.1.0
45
+ - 6f57ee9: chore(deps): bump the npm-minor-and-patch group across 1 directory with 3 updates
46
+ - Updated dependencies [6f57ee9]
47
+ - Updated dependencies [d288371]
48
+ - @prosopo/types@5.7.0
49
+ - @prosopo/util@3.3.9
50
+ - @prosopo/api@4.1.6
51
+ - @prosopo/api-route@2.6.58
52
+ - @prosopo/common@3.1.54
53
+ - @prosopo/logger@2.0.9
54
+ - @prosopo/redis-client@1.0.35
55
+
3
56
  ## 3.12.33
4
57
  ### Patch Changes
5
58
 
package/dist/.export.d.ts CHANGED
@@ -4,6 +4,7 @@ export { type AccessRule, type AccessPolicy, type PolicyScope, type UserScope, A
4
4
  export { describeMatchedRule } from "./matchedRule.js";
5
5
  export { classifyOs, OS_NAMES, type OsName } from "./classifyOs.js";
6
6
  export { classifyBrowser, BROWSER_NAMES, type BrowserName, } from "./classifyBrowser.js";
7
+ export { HEADER_OPERATORS, type HeaderOperator, HEADER_RULE_MARKER, isHeaderOperator, evaluateHeaderCondition, accessRuleHeaderMatches, encodeHeaderValueList, decodeHeaderValueList, } from "./headerMatch.js";
7
8
  export { type AccessRulesFilter, type AccessRulesStorage, type AccessRuleEntry, FilterScopeMatch, } from "./rulesStorage.js";
8
9
  export { type AccessRuleRecord, type UserScopeRecord, type UserScopeRecordField, userScopeRecordFields, getUserScopeRecordFromAccessRuleRecord, } from "./ruleRecord.js";
9
10
  //# sourceMappingURL=.export.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":".export.d.ts","sourceRoot":"","sources":["../src/.export.ts"],"names":[],"mappings":"AAcA,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,kBAAkB,EAClB,iCAAiC,EACjC,6BAA6B,GAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EACN,eAAe,EACf,aAAa,EACb,KAAK,WAAW,GAChB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,gBAAgB,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,qBAAqB,EACrB,sCAAsC,GACtC,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":".export.d.ts","sourceRoot":"","sources":["../src/.export.ts"],"names":[],"mappings":"AAcA,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,kBAAkB,EAClB,iCAAiC,EACjC,6BAA6B,GAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EACN,eAAe,EACf,aAAa,EACb,KAAK,WAAW,GAChB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,gBAAgB,EAChB,KAAK,cAAc,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,gBAAgB,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,qBAAqB,EACrB,sCAAsC,GACtC,MAAM,iBAAiB,CAAC"}
package/dist/.export.js CHANGED
@@ -9,4 +9,5 @@ import { getUserScopeRecordFromAccessRuleRecord, userScopeRecordFields } from ".
9
9
  import { describeMatchedRule } from "./matchedRule.js";
10
10
  import { OS_NAMES, classifyOs } from "./classifyOs.js";
11
11
  import { BROWSER_NAMES, classifyBrowser } from "./classifyBrowser.js";
12
- export { AccessPolicyType, BROWSER_NAMES, FilterScopeMatch, OS_NAMES, accessPolicyInput, accessRuleInput, classifyBrowser, classifyOs, describeMatchedRule, getUserScopeRecordFromAccessRuleRecord, makeAccessRuleHash, policyScopeInput, transformAccessRuleIntoRecord, transformAccessRuleRecordIntoRule, userScopeInput, userScopeRecordFields };
12
+ import { HEADER_OPERATORS, HEADER_RULE_MARKER, accessRuleHeaderMatches, decodeHeaderValueList, encodeHeaderValueList, evaluateHeaderCondition, isHeaderOperator } from "./headerMatch.js";
13
+ export { AccessPolicyType, BROWSER_NAMES, FilterScopeMatch, HEADER_OPERATORS, HEADER_RULE_MARKER, OS_NAMES, accessPolicyInput, accessRuleHeaderMatches, accessRuleInput, classifyBrowser, classifyOs, decodeHeaderValueList, describeMatchedRule, encodeHeaderValueList, evaluateHeaderCondition, getUserScopeRecordFromAccessRuleRecord, isHeaderOperator, makeAccessRuleHash, policyScopeInput, transformAccessRuleIntoRecord, transformAccessRuleRecordIntoRule, userScopeInput, userScopeRecordFields };
@@ -1 +1 @@
1
- {"version":3,"file":".export.js","sourceRoot":"","sources":["../src/.export.ts"],"names":[],"mappings":"AAcA,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,kBAAkB,EAClB,iCAAiC,EACjC,6BAA6B,GAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAKN,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAe,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EACN,eAAe,EACf,aAAa,GAEb,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAIN,gBAAgB,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAIN,qBAAqB,EACrB,sCAAsC,GACtC,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":".export.js","sourceRoot":"","sources":["../src/.export.ts"],"names":[],"mappings":"AAcA,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,kBAAkB,EAClB,iCAAiC,EACjC,6BAA6B,GAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAKN,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAe,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EACN,eAAe,EACf,aAAa,GAEb,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,gBAAgB,EAEhB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAIN,gBAAgB,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAIN,qBAAqB,EACrB,sCAAsC,GACtC,MAAM,iBAAiB,CAAC"}
@@ -9,16 +9,24 @@ const require_ruleRecord = require("./ruleRecord.cjs");
9
9
  const require_matchedRule = require("./matchedRule.cjs");
10
10
  const require_classifyOs = require("./classifyOs.cjs");
11
11
  const require_classifyBrowser = require("./classifyBrowser.cjs");
12
+ const require_headerMatch = require("./headerMatch.cjs");
12
13
  exports.AccessPolicyType = require_rule.AccessPolicyType;
13
14
  exports.BROWSER_NAMES = require_classifyBrowser.BROWSER_NAMES;
14
15
  exports.FilterScopeMatch = require_rulesStorage.FilterScopeMatch;
16
+ exports.HEADER_OPERATORS = require_headerMatch.HEADER_OPERATORS;
17
+ exports.HEADER_RULE_MARKER = require_headerMatch.HEADER_RULE_MARKER;
15
18
  exports.OS_NAMES = require_classifyOs.OS_NAMES;
16
19
  exports.accessPolicyInput = require_ruleInput_policyInput.accessPolicyInput;
20
+ exports.accessRuleHeaderMatches = require_headerMatch.accessRuleHeaderMatches;
17
21
  exports.accessRuleInput = require_ruleInput_ruleInput.accessRuleInput;
18
22
  exports.classifyBrowser = require_classifyBrowser.classifyBrowser;
19
23
  exports.classifyOs = require_classifyOs.classifyOs;
24
+ exports.decodeHeaderValueList = require_headerMatch.decodeHeaderValueList;
20
25
  exports.describeMatchedRule = require_matchedRule.describeMatchedRule;
26
+ exports.encodeHeaderValueList = require_headerMatch.encodeHeaderValueList;
27
+ exports.evaluateHeaderCondition = require_headerMatch.evaluateHeaderCondition;
21
28
  exports.getUserScopeRecordFromAccessRuleRecord = require_ruleRecord.getUserScopeRecordFromAccessRuleRecord;
29
+ exports.isHeaderOperator = require_headerMatch.isHeaderOperator;
22
30
  exports.makeAccessRuleHash = require_transformRule.makeAccessRuleHash;
23
31
  exports.policyScopeInput = require_ruleInput_policyInput.policyScopeInput;
24
32
  exports.transformAccessRuleIntoRecord = require_transformRule.transformAccessRuleIntoRecord;
@@ -0,0 +1,122 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/headerMatch.ts
3
+ /**
4
+ * Operators for an access rule's arbitrary-header condition.
5
+ *
6
+ * `equals` / `contains` are the deny-list operators (block when the request's
7
+ * header matches). `notEquals` / `notContains` are their allow-list negations
8
+ * (block when the request's header does NOT match — including when the header
9
+ * is absent), so an allow-list card that says "only let requests with header X
10
+ * through" desugars to `notEquals` / `notContains` Block rules, exactly the way
11
+ * the OS allow-list desugars to Block rules on the complement.
12
+ *
13
+ * `notEqualsAny` / `notContainsAny` are the multi-value form of those two: the
14
+ * rule's `headerValue` holds a list (see `encodeHeaderValueList`) and the rule
15
+ * blocks unless the request's header matches *one of* the listed values. They
16
+ * exist because an allow-list over several values of the SAME header cannot be
17
+ * expressed as several single-value rules — each rule fires independently, so
18
+ * `notEquals ios` + `notEquals android` blocks both an iOS and an Android
19
+ * request. One rule holding both values is the only way to get the "any of"
20
+ * semantics the allow-list needs.
21
+ */
22
+ var HEADER_OPERATORS = [
23
+ "equals",
24
+ "contains",
25
+ "notEquals",
26
+ "notContains",
27
+ "notEqualsAny",
28
+ "notContainsAny"
29
+ ];
30
+ var isHeaderOperator = (value) => value !== void 0 && HEADER_OPERATORS.includes(value);
31
+ /**
32
+ * Sentinel value carried both on every request's user scope (see
33
+ * `getRequestUserScope`) and on every header-restriction rule. Header rules
34
+ * match on data (`headerName` / `headerValue` / `headerOperator`) that Redis
35
+ * cannot evaluate — substring `contains` and per-rule operators aren't
36
+ * expressible as a TAG query, and an allow-list rule must still fire on a
37
+ * request that OMITS the target header, so candidate selection can't key off
38
+ * header presence. Redis returns header rules via the reader's
39
+ * `no-user-scope` fall-through probe (they constrain none of the indexed
40
+ * scope dimensions); this marker is what keeps them candidates through the
41
+ * JS-side `ruleApplies` scalar check, which would otherwise drop a rule
42
+ * carrying a scope field the request lacks. The concrete condition is then
43
+ * checked by `accessRuleHeaderMatches`.
44
+ */
45
+ var HEADER_RULE_MARKER = "1";
46
+ /**
47
+ * Encode the value list carried by a multi-value operator (`notEqualsAny` /
48
+ * `notContainsAny`) into the single `headerValue` string an access rule can
49
+ * store. JSON rather than a delimiter because header values are arbitrary
50
+ * text — any separator we picked could legitimately occur inside a value.
51
+ */
52
+ var encodeHeaderValueList = (values) => JSON.stringify(values);
53
+ /**
54
+ * Inverse of {@link encodeHeaderValueList}, tolerant of rules written by hand
55
+ * (e.g. via the access-policy CLI) that carry a single literal value instead of
56
+ * an encoded list.
57
+ *
58
+ * Returns `undefined` for a value that looks list-encoded but isn't usable — an
59
+ * empty list, a non-array, non-string members, or malformed JSON. Callers treat
60
+ * that as a malformed rule and decline to fire it, so a garbled list can't
61
+ * silently block all traffic.
62
+ */
63
+ var decodeHeaderValueList = (value) => {
64
+ if (!value.trimStart().startsWith("[")) return [value];
65
+ let parsed;
66
+ try {
67
+ parsed = JSON.parse(value);
68
+ } catch {
69
+ return;
70
+ }
71
+ if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every((entry) => typeof entry === "string")) return;
72
+ return parsed;
73
+ };
74
+ /**
75
+ * Evaluate a single header condition against a request's headers. Returns
76
+ * `true` when the condition is satisfied — i.e. when a Block rule carrying it
77
+ * should fire for this request.
78
+ *
79
+ * `headers` is expected to be keyed by lower-cased header name (HTTP header
80
+ * names are case-insensitive); `headerName` is lower-cased here defensively.
81
+ * The negated operators treat an absent header as "not matching the value",
82
+ * which is what makes an allow-list block a request that drops the header.
83
+ */
84
+ var evaluateHeaderCondition = (headerName, operator, value, headers) => {
85
+ const actual = headers[headerName.toLowerCase()];
86
+ switch (operator) {
87
+ case "equals": return actual !== void 0 && actual === value;
88
+ case "contains": return actual?.includes(value) ?? false;
89
+ case "notEquals": return actual === void 0 || actual !== value;
90
+ case "notContains": return actual === void 0 || !actual.includes(value);
91
+ case "notEqualsAny": {
92
+ const values = decodeHeaderValueList(value);
93
+ if (values === void 0) return false;
94
+ return actual === void 0 || !values.includes(actual);
95
+ }
96
+ case "notContainsAny": {
97
+ const values = decodeHeaderValueList(value);
98
+ if (values === void 0) return false;
99
+ return actual === void 0 || !values.some((candidate) => actual.includes(candidate));
100
+ }
101
+ }
102
+ };
103
+ /**
104
+ * Whether a rule's header condition (if any) is satisfied by the request's
105
+ * headers. A rule with no `headerName` carries no header constraint, so it is
106
+ * treated as satisfied and the rule's other dimensions decide the match. A
107
+ * rule with a `headerName` but an unrecognised operator is malformed and never
108
+ * matches (fail-safe: a garbled rule must not silently block traffic).
109
+ */
110
+ var accessRuleHeaderMatches = (rule, headers) => {
111
+ if (rule.headerName === void 0) return true;
112
+ if (!isHeaderOperator(rule.headerOperator)) return false;
113
+ return evaluateHeaderCondition(rule.headerName, rule.headerOperator, rule.headerValue ?? "", headers);
114
+ };
115
+ //#endregion
116
+ exports.HEADER_OPERATORS = HEADER_OPERATORS;
117
+ exports.HEADER_RULE_MARKER = HEADER_RULE_MARKER;
118
+ exports.accessRuleHeaderMatches = accessRuleHeaderMatches;
119
+ exports.decodeHeaderValueList = decodeHeaderValueList;
120
+ exports.encodeHeaderValueList = encodeHeaderValueList;
121
+ exports.evaluateHeaderCondition = evaluateHeaderCondition;
122
+ exports.isHeaderOperator = isHeaderOperator;
@@ -40,6 +40,26 @@ var userAttributesSchema = {
40
40
  browser: {
41
41
  type: String,
42
42
  required: false
43
+ },
44
+ headerMatch: {
45
+ type: String,
46
+ required: false
47
+ },
48
+ headerName: {
49
+ type: String,
50
+ required: false
51
+ },
52
+ headerValue: {
53
+ type: String,
54
+ required: false
55
+ },
56
+ headerOperator: {
57
+ type: String,
58
+ required: false
59
+ },
60
+ webBotAuthAgent: {
61
+ type: String,
62
+ required: false
43
63
  }
44
64
  };
45
65
  var userIpSchema = {
@@ -26,6 +26,7 @@ var SPECIFICITY_EXPR = [
26
26
  "exists(@asn)",
27
27
  "exists(@os)",
28
28
  "exists(@browser)",
29
+ "exists(@headerMatch)",
29
30
  "exists(@numericIp)",
30
31
  "exists(@numericIpMaskMin)"
31
32
  ].join(" + ");
@@ -54,6 +55,10 @@ var RULE_LOAD_FIELDS = [
54
55
  "@asn",
55
56
  "@os",
56
57
  "@browser",
58
+ "@headerMatch",
59
+ "@headerName",
60
+ "@headerValue",
61
+ "@headerOperator",
57
62
  "@numericIp",
58
63
  "@numericIpMaskMin",
59
64
  "@numericIpMaskMax"
@@ -58,6 +58,26 @@ var userAttributesRedisSchema = {
58
58
  browser: {
59
59
  type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
60
60
  INDEXMISSING: true
61
+ },
62
+ headerMatch: {
63
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
64
+ INDEXMISSING: true
65
+ },
66
+ headerName: {
67
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
68
+ INDEXMISSING: true
69
+ },
70
+ headerValue: {
71
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
72
+ INDEXMISSING: true
73
+ },
74
+ headerOperator: {
75
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
76
+ INDEXMISSING: true
77
+ },
78
+ webBotAuthAgent: {
79
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
80
+ INDEXMISSING: true
61
81
  }
62
82
  };
63
83
  var userScopeRedisSchema = {
package/dist/cjs/rule.cjs CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  var AccessPolicyType = /* @__PURE__ */ function(AccessPolicyType) {
4
4
  AccessPolicyType["Block"] = "block";
5
5
  AccessPolicyType["Restrict"] = "restrict";
6
+ AccessPolicyType["Allow"] = "allow";
6
7
  return AccessPolicyType;
7
8
  }({});
8
9
  var GLOBAL_CLIENT_SCOPE_SENTINEL = "global";
@@ -6,6 +6,13 @@ node_crypto = require_runtime.__toESM(node_crypto, 1);
6
6
  let _prosopo_util = require("@prosopo/util");
7
7
  let ip_address = require("ip-address");
8
8
  //#region src/ruleInput/userScopeInput.ts
9
+ function normaliseSignatureAgentUrl(raw) {
10
+ const url = new URL(raw);
11
+ url.hostname = url.hostname.toLowerCase();
12
+ url.protocol = url.protocol.toLowerCase();
13
+ if (url.pathname === "/") url.pathname = "";
14
+ return url.toString().replace(/\/$/, "");
15
+ }
9
16
  var userAttributesSchema = zod.z.object({
10
17
  userId: zod.z.coerce.string().optional(),
11
18
  ja4Hash: zod.z.coerce.string().optional(),
@@ -16,7 +23,12 @@ var userAttributesSchema = zod.z.object({
16
23
  countryCode: zod.z.coerce.string().optional(),
17
24
  asn: zod.z.coerce.number().int().nonnegative().optional(),
18
25
  os: zod.z.coerce.string().optional(),
19
- browser: zod.z.coerce.string().optional()
26
+ browser: zod.z.coerce.string().optional(),
27
+ headerMatch: zod.z.coerce.string().optional(),
28
+ headerName: zod.z.coerce.string().optional(),
29
+ headerValue: zod.z.coerce.string().optional(),
30
+ headerOperator: zod.z.coerce.string().optional(),
31
+ webBotAuthAgent: zod.z.coerce.string().url().transform(normaliseSignatureAgentUrl).optional()
20
32
  });
21
33
  var userAttributesInput = zod.z.object({
22
34
  ...userAttributesSchema.shape,
@@ -10,7 +10,12 @@ var userAttributesRecordFields = [
10
10
  "countryCode",
11
11
  "asn",
12
12
  "os",
13
- "browser"
13
+ "browser",
14
+ "headerMatch",
15
+ "headerName",
16
+ "headerValue",
17
+ "headerOperator",
18
+ "webBotAuthAgent"
14
19
  ];
15
20
  var userIpRecordFields = ["ip", "ipMask"];
16
21
  var userScopeRecordFields = [...userAttributesRecordFields, ...userIpRecordFields];
@@ -0,0 +1,13 @@
1
+ export declare const HEADER_OPERATORS: readonly ["equals", "contains", "notEquals", "notContains", "notEqualsAny", "notContainsAny"];
2
+ export type HeaderOperator = (typeof HEADER_OPERATORS)[number];
3
+ export declare const isHeaderOperator: (value: string | undefined) => value is HeaderOperator;
4
+ export declare const HEADER_RULE_MARKER = "1";
5
+ export declare const encodeHeaderValueList: (values: ReadonlyArray<string>) => string;
6
+ export declare const decodeHeaderValueList: (value: string) => string[] | undefined;
7
+ export declare const evaluateHeaderCondition: (headerName: string, operator: HeaderOperator, value: string, headers: Record<string, string>) => boolean;
8
+ export declare const accessRuleHeaderMatches: (rule: {
9
+ headerName?: string;
10
+ headerValue?: string;
11
+ headerOperator?: string;
12
+ }, headers: Record<string, string>) => boolean;
13
+ //# sourceMappingURL=headerMatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headerMatch.d.ts","sourceRoot":"","sources":["../src/headerMatch.ts"],"names":[],"mappings":"AAiCA,eAAO,MAAM,gBAAgB,+FAOnB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,eAAO,MAAM,gBAAgB,UACrB,MAAM,GAAG,SAAS,KACvB,KAAK,IAAI,cAEgD,CAAC;AAgB7D,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAQtC,eAAO,MAAM,qBAAqB,WAAY,aAAa,CAAC,MAAM,CAAC,KAAG,MAC/C,CAAC;AAYxB,eAAO,MAAM,qBAAqB,UAAW,MAAM,KAAG,MAAM,EAAE,GAAG,SAmBhE,CAAC;AAYF,eAAO,MAAM,uBAAuB,eACvB,MAAM,YACR,cAAc,SACjB,MAAM,WACJ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC7B,OA6BF,CAAC;AASF,eAAO,MAAM,uBAAuB,SAC7B;IACL,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,WACQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC7B,OAaF,CAAC"}