@prosopo/user-access-policy 3.8.1 → 3.9.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 -6
- package/.turbo/turbo-build$colon$tsc.log +14 -14
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +28 -0
- package/dist/api/read/fetchRules.d.ts +1 -30
- package/dist/api/read/fetchRules.d.ts.map +1 -1
- package/dist/api/read/fetchRules.js.map +1 -1
- package/dist/api/write/insertRules.d.ts +2 -2
- package/dist/api/write/insertRules.d.ts.map +1 -1
- package/dist/api/write/insertRules.js.map +1 -1
- package/dist/cjs/mongoose/mongooseRuleSchema.cjs +2 -1
- package/dist/cjs/ruleInput/policyInput.cjs +5 -1
- package/dist/cjs/ruleInput/userScopeInput.cjs +1 -1
- package/dist/cjs/transformRule.cjs +2 -1
- package/dist/mongoose/mongooseRuleSchema.d.ts.map +1 -1
- package/dist/mongoose/mongooseRuleSchema.js +2 -1
- package/dist/mongoose/mongooseRuleSchema.js.map +1 -1
- package/dist/redis/redisClient.d.ts +2 -2
- package/dist/redis/redisClient.d.ts.map +1 -1
- package/dist/redis/redisClient.js.map +1 -1
- package/dist/rule.d.ts +1 -0
- package/dist/rule.d.ts.map +1 -1
- package/dist/ruleInput/policyInput.d.ts +3 -0
- package/dist/ruleInput/policyInput.d.ts.map +1 -1
- package/dist/ruleInput/policyInput.js +5 -1
- package/dist/ruleInput/policyInput.js.map +1 -1
- package/dist/ruleInput/ruleInput.d.ts +3 -16
- package/dist/ruleInput/ruleInput.d.ts.map +1 -1
- package/dist/ruleInput/ruleInput.js.map +1 -1
- package/dist/ruleInput/userScopeInput.js +2 -2
- package/dist/ruleInput/userScopeInput.js.map +1 -1
- package/dist/tests/transformRule.unit.test.js +45 -1
- package/dist/tests/transformRule.unit.test.js.map +1 -1
- package/dist/transformRule.d.ts.map +1 -1
- package/dist/transformRule.js +3 -2
- package/dist/transformRule.js.map +1 -1
- package/package.json +3 -3
- package/src/api/read/fetchRules.ts +10 -2
- package/src/api/write/insertRules.ts +4 -2
- package/src/mongoose/mongooseRuleSchema.ts +1 -0
- package/src/redis/redisClient.ts +7 -2
- package/src/rule.ts +12 -0
- package/src/ruleInput/policyInput.ts +12 -1
- package/src/ruleInput/ruleInput.ts +11 -6
- package/src/ruleInput/userScopeInput.ts +7 -7
- package/src/tests/transformRule.unit.test.ts +68 -1
- package/src/transformRule.ts +7 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CaptchaType } from "@prosopo/types";
|
|
2
|
-
import { Address4 } from "ip-address";
|
|
2
|
+
import { Address4, Address6 } from "ip-address";
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
import { AccessPolicyType } from "#policy/rule.js";
|
|
5
5
|
import { getCidrFromNumericIpRange, makeAccessRuleHash, transformAccessRuleIntoRecord, transformAccessRuleRecordIntoRule, } from "#policy/transformRule.js";
|
|
@@ -67,6 +67,7 @@ describe("transformRule", () => {
|
|
|
67
67
|
powDifficulty: 1,
|
|
68
68
|
unsolvedImagesCount: 1,
|
|
69
69
|
frictionlessScore: 1,
|
|
70
|
+
deferToVerify: false,
|
|
70
71
|
headersHash: "headersHash",
|
|
71
72
|
ja4Hash: "js4Hash",
|
|
72
73
|
clientId: "client",
|
|
@@ -130,6 +131,25 @@ describe("transformRule", () => {
|
|
|
130
131
|
userAgent: "test",
|
|
131
132
|
})).toThrow();
|
|
132
133
|
});
|
|
134
|
+
it("should round-trip an IPv6 address and CIDR mask", () => {
|
|
135
|
+
const ipv6 = "2001:db8::1";
|
|
136
|
+
const ipv6Cidr = "2001:db8::/32";
|
|
137
|
+
const expectedNumericIp = new Address6(ipv6).bigInt();
|
|
138
|
+
const expectedMaskMin = new Address6(ipv6Cidr).startAddress().bigInt();
|
|
139
|
+
const expectedMaskMax = new Address6(ipv6Cidr).endAddress().bigInt();
|
|
140
|
+
const ruleRecord = {
|
|
141
|
+
type: AccessPolicyType.Restrict,
|
|
142
|
+
ip: ipv6,
|
|
143
|
+
ipMask: ipv6Cidr,
|
|
144
|
+
};
|
|
145
|
+
const accessRule = transformAccessRuleRecordIntoRule(ruleRecord);
|
|
146
|
+
expect(accessRule.numericIp).toEqual(expectedNumericIp);
|
|
147
|
+
expect(accessRule.numericIpMaskMin).toEqual(expectedMaskMin);
|
|
148
|
+
expect(accessRule.numericIpMaskMax).toEqual(expectedMaskMax);
|
|
149
|
+
const reconstructed = transformAccessRuleIntoRecord(accessRule);
|
|
150
|
+
expect(reconstructed.ip).toEqual(new Address6(ipv6).correctForm());
|
|
151
|
+
expect(reconstructed.ipMask).toEqual(ipv6Cidr);
|
|
152
|
+
});
|
|
133
153
|
});
|
|
134
154
|
describe("getCidrFromNumericIpRange", () => {
|
|
135
155
|
const cirdsSet = [
|
|
@@ -198,5 +218,29 @@ describe("getCidrFromNumericIpRange", () => {
|
|
|
198
218
|
const cird = getCidrFromNumericIpRange(new Address4(cirdExample.startIp).bigInt(), new Address4(cirdExample.endIp).bigInt());
|
|
199
219
|
expect(cird).toEqual(cirdExample.cidr);
|
|
200
220
|
});
|
|
221
|
+
const ipv6CirdsSet = [
|
|
222
|
+
{
|
|
223
|
+
cidr: "2001:db8::/32",
|
|
224
|
+
startIp: "2001:db8::",
|
|
225
|
+
endIp: "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff",
|
|
226
|
+
description: "/32 IPv6 network",
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
cidr: "2001:db8::/64",
|
|
230
|
+
startIp: "2001:db8::",
|
|
231
|
+
endIp: "2001:db8::ffff:ffff:ffff:ffff",
|
|
232
|
+
description: "/64 IPv6 subnet",
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
cidr: "fe80::/10",
|
|
236
|
+
startIp: "fe80::",
|
|
237
|
+
endIp: "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
|
238
|
+
description: "/10 IPv6 link-local",
|
|
239
|
+
},
|
|
240
|
+
];
|
|
241
|
+
it.each(ipv6CirdsSet)("should convert $description to $cidr", (cirdExample) => {
|
|
242
|
+
const cidr = getCidrFromNumericIpRange(new Address6(cirdExample.startIp).bigInt(), new Address6(cirdExample.endIp).bigInt());
|
|
243
|
+
expect(cidr).toEqual(cirdExample.cidr);
|
|
244
|
+
});
|
|
201
245
|
});
|
|
202
246
|
//# sourceMappingURL=transformRule.unit.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transformRule.unit.test.js","sourceRoot":"","sources":["../../src/tests/transformRule.unit.test.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"transformRule.unit.test.js","sourceRoot":"","sources":["../../src/tests/transformRule.unit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transformRule.d.ts","sourceRoot":"","sources":["../src/transformRule.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAO5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"transformRule.d.ts","sourceRoot":"","sources":["../src/transformRule.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAO5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAOxD,eAAO,MAAM,kBAAkB,SAAU,UAAU,KAAG,MAYrD,CAAC;AAEF,eAAO,MAAM,iCAAiC,eACjC,gBAAgB,KAC1B,UAE+B,CAAC;AAEnC,eAAO,MAAM,6BAA6B,SACnC,UAAU,KACd,gBAAwD,CAAC;AAkE5D,eAAO,MAAM,yBAAyB,YAC5B,MAAM,SACR,MAAM,KACX,MAAM,GAAG,SASX,CAAC"}
|
package/dist/transformRule.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import { IpRange, IpAddress } from "cidr-calc";
|
|
3
|
-
import { Address4 } from "ip-address";
|
|
3
|
+
import { Address6, Address4 } from "ip-address";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { policyScopeInput, accessPolicyInput } from "./ruleInput/policyInput.js";
|
|
6
6
|
import { accessRuleInput } from "./ruleInput/ruleInput.js";
|
|
7
7
|
import { userScopeSchema } from "./ruleInput/userScopeInput.js";
|
|
8
8
|
const RULE_HASH_ALGORITHM = "md5";
|
|
9
|
+
const MAX_IPV4_NUMERIC = (1n << 32n) - 1n;
|
|
9
10
|
const makeAccessRuleHash = (rule) => {
|
|
10
11
|
const valueProperties = Object.entries(rule).filter(
|
|
11
12
|
([key, value]) => "undefined" !== typeof value
|
|
@@ -60,7 +61,7 @@ const hashObject = (object, algorithm) => crypto.createHash(algorithm).update(
|
|
|
60
61
|
)
|
|
61
62
|
)
|
|
62
63
|
).digest("hex");
|
|
63
|
-
const getStringIpFromNumeric = (numericIp) => Address4.fromInteger(Number(numericIp)).address;
|
|
64
|
+
const getStringIpFromNumeric = (numericIp) => numericIp > MAX_IPV4_NUMERIC ? Address6.fromBigInt(numericIp).correctForm() : Address4.fromInteger(Number(numericIp)).address;
|
|
64
65
|
const getCidrFromNumericIpRange = (startIp, endIp) => {
|
|
65
66
|
const ipRange = new IpRange(
|
|
66
67
|
IpAddress.of(getStringIpFromNumeric(startIp)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transformRule.js","sourceRoot":"","sources":["../src/transformRule.ts"],"names":[],"mappings":"AAcA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"transformRule.js","sourceRoot":"","sources":["../src/transformRule.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/user-access-policy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "^24",
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"test": "npm run test:unit && npm run test:integration"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@prosopo/api": "3.4.
|
|
46
|
+
"@prosopo/api": "3.4.11",
|
|
47
47
|
"@prosopo/api-route": "2.6.46",
|
|
48
48
|
"@prosopo/common": "3.1.38",
|
|
49
49
|
"@prosopo/logger": "1.0.2",
|
|
50
50
|
"@prosopo/redis-client": "1.0.23",
|
|
51
|
-
"@prosopo/types": "4.4.
|
|
51
|
+
"@prosopo/types": "4.4.1",
|
|
52
52
|
"@prosopo/util": "3.2.15",
|
|
53
53
|
"@redis/search": "5.0.0",
|
|
54
54
|
"cidr-calc": "1.0.4",
|
|
@@ -36,9 +36,17 @@ export type FetchRulesResponse = {
|
|
|
36
36
|
ruleEntries: AccessRuleEntry[];
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
// Explicit annotation with `unknown` input position rather than the
|
|
40
|
+
// strict identity form because `ruleEntryInput.rule` transitively uses
|
|
41
|
+
// `z.preprocess` on `deferToVerify`. Required for portable declaration
|
|
42
|
+
// emit; the `AllKeys<...>` constraint still catches missing fields.
|
|
43
|
+
export const fetchRulesResponse: ZodType<
|
|
44
|
+
FetchRulesResponse,
|
|
45
|
+
z.ZodTypeDef,
|
|
46
|
+
unknown
|
|
47
|
+
> = z.object({
|
|
40
48
|
ruleEntries: ruleEntryInput.array(),
|
|
41
|
-
} satisfies AllKeys<FetchRulesResponse>)
|
|
49
|
+
} satisfies AllKeys<FetchRulesResponse>);
|
|
42
50
|
|
|
43
51
|
export type FetchRulesEndpointResponse = ApiEndpointResponse & {
|
|
44
52
|
data?: FetchRulesResponse;
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from "@prosopo/api-route";
|
|
20
20
|
import type { AllKeys } from "@prosopo/common";
|
|
21
21
|
import { LogLevel, type Logger } from "@prosopo/logger";
|
|
22
|
-
import { type ZodType, z } from "zod";
|
|
22
|
+
import { type ZodType, type ZodTypeDef, z } from "zod";
|
|
23
23
|
import type {
|
|
24
24
|
AccessPolicy,
|
|
25
25
|
AccessRule,
|
|
@@ -56,7 +56,9 @@ type ParsedInsertRulesGroup = InsertRulesGroup & {
|
|
|
56
56
|
|
|
57
57
|
type ParsedInsertRuleGroups = ParsedInsertRulesGroup[];
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
// Input position widened to `unknown` because `accessPolicyInput` uses
|
|
60
|
+
// `z.preprocess` on `deferToVerify` for Redis string round-tripping.
|
|
61
|
+
type InsertRulesSchema = ZodType<InsertRulesGroup[], ZodTypeDef, unknown>;
|
|
60
62
|
|
|
61
63
|
export class InsertRulesEndpoint implements ApiEndpoint<InsertRulesSchema> {
|
|
62
64
|
public constructor(
|
|
@@ -56,6 +56,7 @@ const accessPolicySchema: SchemaDefinition<AccessPolicy> = {
|
|
|
56
56
|
powDifficulty: { type: Number, required: false },
|
|
57
57
|
unsolvedImagesCount: { type: Number, required: false },
|
|
58
58
|
frictionlessScore: { type: Number, required: false },
|
|
59
|
+
deferToVerify: { type: Boolean, required: false },
|
|
59
60
|
} satisfies AllKeys<AccessPolicy>;
|
|
60
61
|
|
|
61
62
|
export const accessRuleMongooseSchema: SchemaDefinition<AccessRuleRecord> = {
|
package/src/redis/redisClient.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import type { Logger } from "@prosopo/logger";
|
|
16
16
|
import type { RedisClientType } from "redis";
|
|
17
|
-
import { type ZodType, z } from "zod";
|
|
17
|
+
import { type ZodType, type ZodTypeDef, z } from "zod";
|
|
18
18
|
|
|
19
19
|
export const REDIS_BATCH_SIZE = 1_000;
|
|
20
20
|
|
|
@@ -67,9 +67,14 @@ export const fetchRedisHashRecords = async (
|
|
|
67
67
|
};
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
// `recordSchema` is intentionally typed with `unknown` as the input
|
|
71
|
+
// position. Some schemas use `z.preprocess` (e.g. accessPolicyInput's
|
|
72
|
+
// deferToVerify boolean coercion from Redis strings), which widens the
|
|
73
|
+
// zod input type. The strict `ZodType<T, ZodTypeDef, T>` form rejects
|
|
74
|
+
// those at the call site even though they parse correctly at runtime.
|
|
70
75
|
export const parseRedisRecords = <T>(
|
|
71
76
|
records: unknown[],
|
|
72
|
-
recordSchema: ZodType<T>,
|
|
77
|
+
recordSchema: ZodType<T, ZodTypeDef, unknown>,
|
|
73
78
|
logger: Logger,
|
|
74
79
|
): T[] =>
|
|
75
80
|
records.flatMap((record) => {
|
package/src/rule.ts
CHANGED
|
@@ -27,6 +27,18 @@ export type AccessPolicy = {
|
|
|
27
27
|
powDifficulty?: number;
|
|
28
28
|
unsolvedImagesCount?: number;
|
|
29
29
|
frictionlessScore?: number;
|
|
30
|
+
// When true, a Block policy does NOT fire at the request-time
|
|
31
|
+
// blockMiddleware (so the user does not see a 401 on the captcha
|
|
32
|
+
// challenge endpoint) — it fires at the verify step instead, marking
|
|
33
|
+
// the commitment ACCESS_POLICY_BLOCK / disapproved. The verify
|
|
34
|
+
// response returns `{verified:false}` to the dApp's server while the
|
|
35
|
+
// user-facing widget completes normally. Mirrors the existing
|
|
36
|
+
// coords-rule deferral pattern: the middleware blanks coords out of
|
|
37
|
+
// the userScope, so coords rules can only ever be matched in the
|
|
38
|
+
// verify path; `deferToVerify` is the explicit form for non-coords
|
|
39
|
+
// signals (ja4, headersHash, etc.) when the operator wants the
|
|
40
|
+
// attacker to pay the captcha-solving cost before being rejected.
|
|
41
|
+
deferToVerify?: boolean;
|
|
30
42
|
};
|
|
31
43
|
|
|
32
44
|
export type PolicyScope = {
|
|
@@ -21,6 +21,11 @@ import {
|
|
|
21
21
|
type PolicyScope,
|
|
22
22
|
} from "#policy/rule.js";
|
|
23
23
|
|
|
24
|
+
// `satisfies ZodType<AccessPolicy>` is intentionally omitted: the
|
|
25
|
+
// `deferToVerify` preprocess widens the schema's input type to `unknown`
|
|
26
|
+
// (preprocess accepts anything), which fails the
|
|
27
|
+
// `ZodType<T, ZodTypeDef, T>` identity check. The `AllKeys<AccessPolicy>`
|
|
28
|
+
// constraint still catches missing-field regressions.
|
|
24
29
|
export const accessPolicyInput = z.object({
|
|
25
30
|
type: z.nativeEnum(AccessPolicyType),
|
|
26
31
|
captchaType: CaptchaTypeSchema.optional(),
|
|
@@ -35,7 +40,13 @@ export const accessPolicyInput = z.object({
|
|
|
35
40
|
unsolvedImagesCount: z.coerce.number().optional(),
|
|
36
41
|
// used to increase the user's score
|
|
37
42
|
frictionlessScore: z.coerce.number().optional(),
|
|
38
|
-
|
|
43
|
+
// Skip the request-time block middleware and only fire at verify.
|
|
44
|
+
// Redis stores booleans as strings — preprocess so "true"/"false"
|
|
45
|
+
// round-trip to the JS boolean the matcher expects.
|
|
46
|
+
deferToVerify: z
|
|
47
|
+
.preprocess((v) => (typeof v === "string" ? v === "true" : v), z.boolean())
|
|
48
|
+
.optional(),
|
|
49
|
+
} satisfies AllKeys<AccessPolicy>);
|
|
39
50
|
|
|
40
51
|
// Sanitize block policies by removing captchaType and solvedImagesCount
|
|
41
52
|
export const sanitizeAccessPolicy = (policy: AccessPolicy): AccessPolicy => {
|
|
@@ -48,20 +48,25 @@ const ruleGroupInput = z
|
|
|
48
48
|
return ruleGroup;
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
// Explicit `ZodType<…, ZodTypeDef, unknown>` annotation rather than the
|
|
52
|
+
// strict-identity form because `accessPolicyInput.shape.deferToVerify`
|
|
53
|
+
// uses `z.preprocess` which widens the input position to `unknown`. The
|
|
54
|
+
// relaxed annotation is portable for declaration emit; the `transform`
|
|
55
|
+
// pins the OUTPUT to AccessRule.
|
|
56
|
+
export const accessRuleInput: ZodType<AccessRule, z.ZodTypeDef, unknown> = z
|
|
52
57
|
.object({
|
|
53
58
|
...accessPolicyInput.shape,
|
|
54
59
|
...policyScopeInput.shape,
|
|
55
60
|
})
|
|
56
61
|
.and(userScopeInput)
|
|
57
62
|
.and(ruleGroupInput)
|
|
58
|
-
// transform is used for type safety only - plain "satisfies ZodType<x>" doesn't work after ".and()"
|
|
59
63
|
.transform((ruleInput: AccessRuleInput): AccessRule => ruleInput);
|
|
60
64
|
|
|
61
|
-
export const ruleEntryInput
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
export const ruleEntryInput: ZodType<AccessRuleEntry, z.ZodTypeDef, unknown> =
|
|
66
|
+
z.object({
|
|
67
|
+
rule: accessRuleInput,
|
|
68
|
+
expiresUnixTimestamp: z.coerce.number().optional(),
|
|
69
|
+
} satisfies AllKeys<AccessRuleEntry>);
|
|
65
70
|
|
|
66
71
|
export type AccessRulesFilterInput = AccessRulesFilter & {
|
|
67
72
|
userScope?: UserScopeInput;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import crypto from "node:crypto";
|
|
16
16
|
import type { AllKeys } from "@prosopo/common";
|
|
17
17
|
import { getIPAddress } from "@prosopo/util";
|
|
18
|
-
import { Address4 } from "ip-address";
|
|
18
|
+
import { Address4, Address6 } from "ip-address";
|
|
19
19
|
import { type ZodType, z } from "zod";
|
|
20
20
|
import type { UserAttributes, UserIp, UserScope } from "#policy/rule.js";
|
|
21
21
|
import type { UserAttributesRecord, UserIpRecord } from "#policy/ruleRecord.js";
|
|
@@ -77,14 +77,14 @@ const userIpInput = z
|
|
|
77
77
|
|
|
78
78
|
// Assuming ipMask is already validated to be a string in CIDR format
|
|
79
79
|
if ("string" === typeof ipMask) {
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
80
|
+
// Try IPv4 CIDR first (e.g., 192.168.1.0/24); fall back to IPv6
|
|
81
|
+
// CIDR (e.g., 2001:db8::/32). Both Address4 and Address6 understand
|
|
82
|
+
// CIDR notation and expose start/end addresses for the network.
|
|
83
|
+
const ipObject = Address4.isValid(ipMask)
|
|
84
|
+
? new Address4(ipMask)
|
|
85
|
+
: new Address6(ipMask);
|
|
83
86
|
|
|
84
|
-
// The minimum IP in the CIDR range is the start address of the network.
|
|
85
87
|
numericUserIp.numericIpMaskMin = ipObject.startAddress().bigInt();
|
|
86
|
-
|
|
87
|
-
// The maximum IP in the CIDR range is the end address of the network.
|
|
88
88
|
numericUserIp.numericIpMaskMax = ipObject.endAddress().bigInt();
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
|
|
15
15
|
import { CaptchaType } from "@prosopo/types";
|
|
16
|
-
import { Address4 } from "ip-address";
|
|
16
|
+
import { Address4, Address6 } from "ip-address";
|
|
17
17
|
import { describe, expect, it } from "vitest";
|
|
18
18
|
import { AccessPolicyType, type AccessRule } from "#policy/rule.js";
|
|
19
19
|
import type { AccessRuleRecord } from "#policy/ruleRecord.js";
|
|
@@ -106,6 +106,7 @@ describe("transformRule", () => {
|
|
|
106
106
|
powDifficulty: 1,
|
|
107
107
|
unsolvedImagesCount: 1,
|
|
108
108
|
frictionlessScore: 1,
|
|
109
|
+
deferToVerify: false,
|
|
109
110
|
headersHash: "headersHash",
|
|
110
111
|
ja4Hash: "js4Hash",
|
|
111
112
|
clientId: "client",
|
|
@@ -186,6 +187,32 @@ describe("transformRule", () => {
|
|
|
186
187
|
} as unknown as AccessRule),
|
|
187
188
|
).toThrow();
|
|
188
189
|
});
|
|
190
|
+
|
|
191
|
+
it("should round-trip an IPv6 address and CIDR mask", () => {
|
|
192
|
+
const ipv6 = "2001:db8::1";
|
|
193
|
+
const ipv6Cidr = "2001:db8::/32";
|
|
194
|
+
|
|
195
|
+
const expectedNumericIp = new Address6(ipv6).bigInt();
|
|
196
|
+
const expectedMaskMin = new Address6(ipv6Cidr).startAddress().bigInt();
|
|
197
|
+
const expectedMaskMax = new Address6(ipv6Cidr).endAddress().bigInt();
|
|
198
|
+
|
|
199
|
+
const ruleRecord: AccessRuleRecord = {
|
|
200
|
+
type: AccessPolicyType.Restrict,
|
|
201
|
+
ip: ipv6,
|
|
202
|
+
ipMask: ipv6Cidr,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const accessRule = transformAccessRuleRecordIntoRule(ruleRecord);
|
|
206
|
+
|
|
207
|
+
expect(accessRule.numericIp).toEqual(expectedNumericIp);
|
|
208
|
+
expect(accessRule.numericIpMaskMin).toEqual(expectedMaskMin);
|
|
209
|
+
expect(accessRule.numericIpMaskMax).toEqual(expectedMaskMax);
|
|
210
|
+
|
|
211
|
+
const reconstructed = transformAccessRuleIntoRecord(accessRule);
|
|
212
|
+
|
|
213
|
+
expect(reconstructed.ip).toEqual(new Address6(ipv6).correctForm());
|
|
214
|
+
expect(reconstructed.ipMask).toEqual(ipv6Cidr);
|
|
215
|
+
});
|
|
189
216
|
});
|
|
190
217
|
|
|
191
218
|
describe("getCidrFromNumericIpRange", () => {
|
|
@@ -267,4 +294,44 @@ describe("getCidrFromNumericIpRange", () => {
|
|
|
267
294
|
|
|
268
295
|
expect(cird).toEqual(cirdExample.cidr);
|
|
269
296
|
});
|
|
297
|
+
|
|
298
|
+
type Ipv6CidrExample = {
|
|
299
|
+
cidr: string;
|
|
300
|
+
startIp: string;
|
|
301
|
+
endIp: string;
|
|
302
|
+
description: string;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const ipv6CirdsSet: Ipv6CidrExample[] = [
|
|
306
|
+
{
|
|
307
|
+
cidr: "2001:db8::/32",
|
|
308
|
+
startIp: "2001:db8::",
|
|
309
|
+
endIp: "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff",
|
|
310
|
+
description: "/32 IPv6 network",
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
cidr: "2001:db8::/64",
|
|
314
|
+
startIp: "2001:db8::",
|
|
315
|
+
endIp: "2001:db8::ffff:ffff:ffff:ffff",
|
|
316
|
+
description: "/64 IPv6 subnet",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
cidr: "fe80::/10",
|
|
320
|
+
startIp: "fe80::",
|
|
321
|
+
endIp: "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
|
322
|
+
description: "/10 IPv6 link-local",
|
|
323
|
+
},
|
|
324
|
+
];
|
|
325
|
+
|
|
326
|
+
it.each(ipv6CirdsSet)(
|
|
327
|
+
"should convert $description to $cidr",
|
|
328
|
+
(cirdExample) => {
|
|
329
|
+
const cidr = getCidrFromNumericIpRange(
|
|
330
|
+
new Address6(cirdExample.startIp).bigInt(),
|
|
331
|
+
new Address6(cirdExample.endIp).bigInt(),
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
expect(cidr).toEqual(cirdExample.cidr);
|
|
335
|
+
},
|
|
336
|
+
);
|
|
270
337
|
});
|
package/src/transformRule.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import crypto from "node:crypto";
|
|
16
16
|
import { IpAddress, IpRange } from "cidr-calc";
|
|
17
|
-
import { Address4 } from "ip-address";
|
|
17
|
+
import { Address4, Address6 } from "ip-address";
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
import type { AccessRule } from "./rule.js";
|
|
20
20
|
import {
|
|
@@ -27,6 +27,9 @@ import type { AccessRuleRecord } from "./ruleRecord.js";
|
|
|
27
27
|
|
|
28
28
|
const RULE_HASH_ALGORITHM = "md5";
|
|
29
29
|
|
|
30
|
+
// IPv4 numeric values occupy 0..2^32-1. Anything above is treated as IPv6.
|
|
31
|
+
const MAX_IPV4_NUMERIC = (1n << 32n) - 1n;
|
|
32
|
+
|
|
30
33
|
export const makeAccessRuleHash = (rule: AccessRule): string => {
|
|
31
34
|
// 1. exclude "undefined" values to ensure hash consistency
|
|
32
35
|
const valueProperties = Object.entries(rule).filter(
|
|
@@ -111,7 +114,9 @@ const hashObject = (
|
|
|
111
114
|
.digest("hex");
|
|
112
115
|
|
|
113
116
|
const getStringIpFromNumeric = (numericIp: bigint): string =>
|
|
114
|
-
|
|
117
|
+
numericIp > MAX_IPV4_NUMERIC
|
|
118
|
+
? Address6.fromBigInt(numericIp).correctForm()
|
|
119
|
+
: Address4.fromInteger(Number(numericIp)).address;
|
|
115
120
|
|
|
116
121
|
export const getCidrFromNumericIpRange = (
|
|
117
122
|
startIp: bigint,
|