@prosopo/user-access-policy 3.9.0 → 3.10.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 +35 -35
- package/.turbo/turbo-build$colon$tsc.log +14 -14
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +21 -0
- package/dist/cjs/redis/reader/redisAggregate.cjs +22 -4
- package/dist/cjs/redis/reader/redisRulesReader.cjs +133 -35
- package/dist/redis/reader/redisAggregate.d.ts +1 -1
- package/dist/redis/reader/redisAggregate.d.ts.map +1 -1
- package/dist/redis/reader/redisAggregate.js +22 -4
- package/dist/redis/reader/redisAggregate.js.map +1 -1
- package/dist/redis/reader/redisRulesReader.d.ts +4 -1
- package/dist/redis/reader/redisRulesReader.d.ts.map +1 -1
- package/dist/redis/reader/redisRulesReader.js +132 -34
- package/dist/redis/reader/redisRulesReader.js.map +1 -1
- package/dist/tests/redis/redisRulesReaderRank.benchmark.integration.test.d.ts +2 -0
- package/dist/tests/redis/redisRulesReaderRank.benchmark.integration.test.d.ts.map +1 -0
- package/dist/tests/redis/redisRulesReaderRank.benchmark.integration.test.js +189 -0
- package/dist/tests/redis/redisRulesReaderRank.benchmark.integration.test.js.map +1 -0
- package/dist/tests/redis/redisRulesStorage.integration.test.js +31 -0
- package/dist/tests/redis/redisRulesStorage.integration.test.js.map +1 -1
- package/package.json +3 -3
- package/src/redis/reader/redisAggregate.ts +27 -1
- package/src/redis/reader/redisRulesReader.ts +197 -37
- package/src/tests/redis/redisRulesReaderRank.benchmark.integration.test.ts +338 -0
- package/src/tests/redis/redisRulesStorage.integration.test.ts +52 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vite.test.config.ts +12 -1
|
@@ -1115,6 +1115,58 @@ describe("redisAccessRulesStorage", () => {
|
|
|
1115
1115
|
expect(foundAccessRules).toEqual([]);
|
|
1116
1116
|
});
|
|
1117
1117
|
|
|
1118
|
+
test("returns all matches when the candidate set exceeds the FT.SEARCH page size", async () => {
|
|
1119
|
+
// Regression: under the production traffic profile of a high-volume
|
|
1120
|
+
// bot attack, the greedy `@field:{X} | @field:{Y}` query returns
|
|
1121
|
+
// thousands of candidate rules sharing the dominant ja4 fingerprint.
|
|
1122
|
+
// FT.SEARCH's LIMIT (1000) silently truncated the candidate set,
|
|
1123
|
+
// dropping less-frequent block rules — they never reached the
|
|
1124
|
+
// JS-side specificity sort, so verify let the bot through.
|
|
1125
|
+
// FT.AGGREGATE WITHCURSOR paginates the result and returns all of
|
|
1126
|
+
// them. This test inserts > 1000 rules so the old code would
|
|
1127
|
+
// truncate; the target block rule must still come back.
|
|
1128
|
+
const clientId = getUniqueString();
|
|
1129
|
+
const popularJa4 = `t13d1516h2_${getUniqueString()}`;
|
|
1130
|
+
|
|
1131
|
+
const targetBlockRule: AccessRule = {
|
|
1132
|
+
type: AccessPolicyType.Block,
|
|
1133
|
+
clientId: clientId,
|
|
1134
|
+
ja4Hash: popularJa4,
|
|
1135
|
+
coords: "[[[867,60]]]",
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
// 1500 noise rules sharing the popular ja4 but with distinct coords.
|
|
1139
|
+
// 1500 > REDIS_BATCH_SIZE (1000) — guarantees the targetBlockRule's
|
|
1140
|
+
// FT index position has at least a 1/3 chance of sitting past the
|
|
1141
|
+
// truncation boundary on any given run. With pagination, it must
|
|
1142
|
+
// always come back regardless of indexed order.
|
|
1143
|
+
const noiseRules: AccessRule[] = Array.from({ length: 1500 }, (_, i) => ({
|
|
1144
|
+
type: AccessPolicyType.Restrict,
|
|
1145
|
+
clientId: clientId,
|
|
1146
|
+
ja4Hash: popularJa4,
|
|
1147
|
+
coords: `[[[${i % 1024},${60 + (i % 32)}]]]`,
|
|
1148
|
+
description: `noise-${i}`,
|
|
1149
|
+
}));
|
|
1150
|
+
|
|
1151
|
+
await insertRules([targetBlockRule, ...noiseRules]);
|
|
1152
|
+
|
|
1153
|
+
const indexRecordsCount = await getIndexRecordsCount(indexName);
|
|
1154
|
+
expect(indexRecordsCount).toBe(1501);
|
|
1155
|
+
|
|
1156
|
+
const found = await accessRulesReader.findRules({
|
|
1157
|
+
policyScope: { clientId: clientId },
|
|
1158
|
+
policyScopeMatch: FilterScopeMatch.Greedy,
|
|
1159
|
+
userScope: {
|
|
1160
|
+
ja4Hash: popularJa4,
|
|
1161
|
+
coords: "[[[867,60]]]",
|
|
1162
|
+
},
|
|
1163
|
+
userScopeMatch: FilterScopeMatch.Greedy,
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
expect(found.length).toBe(1501);
|
|
1167
|
+
expect(found).toContainEqual(targetBlockRule);
|
|
1168
|
+
}, 60_000);
|
|
1169
|
+
|
|
1118
1170
|
test("finds rules with matchingFieldsOnly when only userId is set and all IP fields are missing", async () => {
|
|
1119
1171
|
// This is the exact scenario from the production error where the query
|
|
1120
1172
|
// contained duplicate ismissing(@numericIpMaskMin) ismissing(@numericIpMaskMax)
|