@astrasyncai/verification-gateway 4.3.0 → 4.5.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/dist/adapters/express.js +104 -28
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +104 -28
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.js +63 -21
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +63 -21
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.js +103 -27
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +103 -27
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.js +33 -12
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +33 -12
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.js +24 -3
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +24 -3
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +31 -10
- package/dist/bin/astrasync-codex-hook.js +31 -10
- package/dist/bin/astrasync-guard.js +35 -14
- package/dist/bin/astrasync.js +75 -29
- package/dist/browser/background.js +31 -10
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +31 -10
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +23 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +23 -2
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +31 -10
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +31 -10
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +31 -10
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +31 -10
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +11 -4
- package/dist/edge-config.d.ts +11 -4
- package/dist/edge-config.js +24 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +22 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +21 -7
- package/dist/edge-core/index.d.ts +21 -7
- package/dist/edge-core/index.js +170 -25
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +170 -25
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.js +31 -10
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +31 -10
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/index.d.mts +51 -2
- package/dist/index.d.ts +51 -2
- package/dist/index.js +169 -40
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +165 -40
- package/dist/index.mjs.map +1 -1
- package/dist/metadata-capture.d.mts +17 -0
- package/dist/metadata-capture.d.ts +17 -0
- package/dist/metadata-capture.js +25 -4
- package/dist/metadata-capture.js.map +1 -1
- package/dist/metadata-capture.mjs +25 -4
- package/dist/metadata-capture.mjs.map +1 -1
- package/dist/platform-signatures.d.mts +18 -1
- package/dist/platform-signatures.d.ts +18 -1
- package/dist/platform-signatures.js +35 -2
- package/dist/platform-signatures.js.map +1 -1
- package/dist/platform-signatures.mjs +35 -2
- package/dist/platform-signatures.mjs.map +1 -1
- package/dist/registration/index.js +25 -4
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +25 -4
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.js +22 -1
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +22 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/verify.js +38 -17
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +38 -17
- package/dist/verify.mjs.map +1 -1
- package/package.json +2 -2
package/dist/edge-core/index.mjs
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
// src/version.ts
|
|
2
|
+
var SDK_VERSION = "4.5.0";
|
|
3
|
+
|
|
4
|
+
// src/http.ts
|
|
5
|
+
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
6
|
+
function withUserAgent(initHeaders) {
|
|
7
|
+
if (!initHeaders) return { "user-agent": SDK_USER_AGENT };
|
|
8
|
+
if (initHeaders instanceof Headers) {
|
|
9
|
+
const copy = new Headers(initHeaders);
|
|
10
|
+
if (!copy.has("user-agent")) copy.set("user-agent", SDK_USER_AGENT);
|
|
11
|
+
return copy;
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(initHeaders)) {
|
|
14
|
+
const has2 = initHeaders.some(([name]) => name.toLowerCase() === "user-agent");
|
|
15
|
+
return has2 ? initHeaders : [...initHeaders, ["user-agent", SDK_USER_AGENT]];
|
|
16
|
+
}
|
|
17
|
+
const has = Object.keys(initHeaders).some((name) => name.toLowerCase() === "user-agent");
|
|
18
|
+
return has ? initHeaders : { "user-agent": SDK_USER_AGENT, ...initHeaders };
|
|
19
|
+
}
|
|
20
|
+
var sdkFetch = (input, init) => fetch(input, { ...init, headers: withUserAgent(init?.headers) });
|
|
21
|
+
|
|
1
22
|
// src/edge-config.ts
|
|
2
23
|
var DEFAULT_EDGE_CONFIG = Object.freeze({
|
|
3
24
|
version: 1,
|
|
@@ -57,7 +78,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
|
57
78
|
"x-agent-card-url",
|
|
58
79
|
"x-agent-provider"
|
|
59
80
|
]);
|
|
60
|
-
var PLATFORM_HEADER_PREFIXES = [
|
|
81
|
+
var PLATFORM_HEADER_PREFIXES = [
|
|
82
|
+
"x-goog-",
|
|
83
|
+
"x-stainless-",
|
|
84
|
+
"x-anthropic-",
|
|
85
|
+
"x-openai-",
|
|
86
|
+
"sec-ch-"
|
|
87
|
+
];
|
|
61
88
|
var KEY_FORMAT_PREFIXES = [
|
|
62
89
|
"sk-ant-api03",
|
|
63
90
|
"sk-ant-api",
|
|
@@ -129,6 +156,7 @@ function sanitizeHeaders(raw) {
|
|
|
129
156
|
const headers = {};
|
|
130
157
|
const platformHeaders = {};
|
|
131
158
|
let apiKeyFormat;
|
|
159
|
+
let cookieCount;
|
|
132
160
|
let totalBytes = 0;
|
|
133
161
|
let count = 0;
|
|
134
162
|
if (!raw) return { headers };
|
|
@@ -143,6 +171,9 @@ function sanitizeHeaders(raw) {
|
|
|
143
171
|
if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
|
|
144
172
|
continue;
|
|
145
173
|
}
|
|
174
|
+
if (name === "cookie") {
|
|
175
|
+
cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
|
|
176
|
+
}
|
|
146
177
|
if (isSecretHeader(name)) continue;
|
|
147
178
|
let value = joined;
|
|
148
179
|
if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
|
|
@@ -158,6 +189,7 @@ function sanitizeHeaders(raw) {
|
|
|
158
189
|
const result = { headers };
|
|
159
190
|
if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
|
|
160
191
|
if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
|
|
192
|
+
if (cookieCount !== void 0) result.cookieCount = cookieCount;
|
|
161
193
|
return result;
|
|
162
194
|
}
|
|
163
195
|
|
|
@@ -205,12 +237,43 @@ var PLATFORM_AGENT_SIGNATURES = [
|
|
|
205
237
|
uaPatterns: [/perplexity/i],
|
|
206
238
|
agentCardPatterns: [/perplexity\.ai/i],
|
|
207
239
|
apiKeyFormatPatterns: [/^pplx-/i]
|
|
240
|
+
},
|
|
241
|
+
// Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
|
|
242
|
+
// never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
|
|
243
|
+
// browser product is a different surface than the assistant service, and
|
|
244
|
+
// the DB registry mirrors one row per vendor. These sit after the primary
|
|
245
|
+
// vendor entries so a UA carrying an explicit vendor token keeps its
|
|
246
|
+
// primary attribution; a browser-shaped UA with the agentic token is
|
|
247
|
+
// caught here BEFORE classify's human tier, which is what makes these
|
|
248
|
+
// visits observable at all. Token drift is real (some builds mimic Chrome
|
|
249
|
+
// outright) — re-verify against captured events when a new browser ships,
|
|
250
|
+
// and check `sec-ch-ua` platformHeaders for the real brand.
|
|
251
|
+
{
|
|
252
|
+
vendor: "chatgpt-atlas",
|
|
253
|
+
displayName: "ChatGPT Atlas browser",
|
|
254
|
+
uaPatterns: [/\batlas\/\d/i],
|
|
255
|
+
headerPatterns: [/^sec-ch-ua: .*atlas/i]
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
vendor: "perplexity-comet",
|
|
259
|
+
displayName: "Perplexity Comet browser",
|
|
260
|
+
uaPatterns: [/\bcomet\/\d/i],
|
|
261
|
+
headerPatterns: [/^sec-ch-ua: .*comet/i]
|
|
262
|
+
},
|
|
263
|
+
// Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
|
|
264
|
+
// traffic must never pollute the unknown/anonymous pool it exists to audit.
|
|
265
|
+
{
|
|
266
|
+
vendor: "astrasync-sdk",
|
|
267
|
+
displayName: "AstraSync SDK",
|
|
268
|
+
uaPatterns: [/^astrasync-sdk\//i]
|
|
208
269
|
}
|
|
209
270
|
];
|
|
210
271
|
function matchPlatformSignature(signatures, input) {
|
|
211
272
|
const ua = input.userAgent ?? "";
|
|
212
273
|
const cardUrl = input.agentCardUrl ?? "";
|
|
213
274
|
const apiKeyFormat = input.apiKeyFormat ?? "";
|
|
275
|
+
const ja4 = input.ja4 ?? "";
|
|
276
|
+
const asn = input.asn ?? "";
|
|
214
277
|
const headerLines = [];
|
|
215
278
|
if (input.platformHeaders) {
|
|
216
279
|
for (const [name, value] of Object.entries(input.platformHeaders)) {
|
|
@@ -222,7 +285,9 @@ function matchPlatformSignature(signatures, input) {
|
|
|
222
285
|
const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
|
|
223
286
|
const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
|
|
224
287
|
const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
|
|
225
|
-
|
|
288
|
+
const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
|
|
289
|
+
const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
|
|
290
|
+
if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
|
|
226
291
|
const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
|
|
227
292
|
const cardHost = (() => {
|
|
228
293
|
try {
|
|
@@ -231,7 +296,7 @@ function matchPlatformSignature(signatures, input) {
|
|
|
231
296
|
return "";
|
|
232
297
|
}
|
|
233
298
|
})();
|
|
234
|
-
const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
|
|
299
|
+
const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
|
|
235
300
|
return {
|
|
236
301
|
vendor: sig.vendor,
|
|
237
302
|
fingerprintKey: `${sig.vendor}:${keyBasis}`,
|
|
@@ -253,9 +318,6 @@ function getTrustLevel(score) {
|
|
|
253
318
|
return "BRONZE";
|
|
254
319
|
}
|
|
255
320
|
|
|
256
|
-
// src/version.ts
|
|
257
|
-
var SDK_VERSION = "4.1.0";
|
|
258
|
-
|
|
259
321
|
// src/well-known.ts
|
|
260
322
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
261
323
|
var cache = /* @__PURE__ */ new Map();
|
|
@@ -284,7 +346,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
|
|
|
284
346
|
initCheckPerformed = true;
|
|
285
347
|
try {
|
|
286
348
|
const probeUrl = `${apiBaseUrl}/agents/verify-access`;
|
|
287
|
-
const response = await
|
|
349
|
+
const response = await sdkFetch(probeUrl, { method: "HEAD" });
|
|
288
350
|
const contentType = response.headers.get("content-type") ?? "";
|
|
289
351
|
if (contentType.startsWith("text/html")) {
|
|
290
352
|
const message = `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). This usually means apiBaseUrl is pointing at a marketing site instead of the API. Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging).`;
|
|
@@ -298,7 +360,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
|
|
|
298
360
|
);
|
|
299
361
|
}
|
|
300
362
|
if (apiKey) {
|
|
301
|
-
const selfCheck = await
|
|
363
|
+
const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
|
|
302
364
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
303
365
|
});
|
|
304
366
|
if (selfCheck.status === 401) {
|
|
@@ -545,7 +607,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
545
607
|
headers["X-API-Key"] = config.apiKey;
|
|
546
608
|
}
|
|
547
609
|
try {
|
|
548
|
-
const response = await
|
|
610
|
+
const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {
|
|
549
611
|
method: "POST",
|
|
550
612
|
headers,
|
|
551
613
|
body: JSON.stringify(body),
|
|
@@ -727,12 +789,15 @@ async function verify(config, request, options) {
|
|
|
727
789
|
const verificationContext = apiResponse.verificationContext;
|
|
728
790
|
const accessLevel = apiResponse.access?.accessLevel ?? "standard";
|
|
729
791
|
const result = {
|
|
730
|
-
//
|
|
731
|
-
//
|
|
732
|
-
//
|
|
733
|
-
//
|
|
734
|
-
//
|
|
735
|
-
|
|
792
|
+
// 4.5.0 (probe F1): read idVerified from verificationContext with the
|
|
793
|
+
// SAME absent⇒false default as the deny branch. The previous `!== false`
|
|
794
|
+
// default manufactured `identityVerified: true` for anonymous
|
|
795
|
+
// allow_full/allow_partial/audit responses (which allow access but omit
|
|
796
|
+
// verificationContext entirely) — the "anonymous caller labelled
|
|
797
|
+
// verified" defect. Every authenticated success response has carried
|
|
798
|
+
// `verificationContext.idVerified: true` since v2.2.4, so a truth claim
|
|
799
|
+
// this load-bearing must never be defaulted on.
|
|
800
|
+
identityVerified: apiResponse.verificationContext?.idVerified === true,
|
|
736
801
|
policyAllowed: true,
|
|
737
802
|
accessLevel,
|
|
738
803
|
agent,
|
|
@@ -790,7 +855,7 @@ async function reportUnregisteredAttempt(config, data) {
|
|
|
790
855
|
...counterpartyUrl && { counterpartyUrl },
|
|
791
856
|
...config.counterpartyId && { counterpartyId: config.counterpartyId }
|
|
792
857
|
};
|
|
793
|
-
await
|
|
858
|
+
await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
|
|
794
859
|
method: "POST",
|
|
795
860
|
headers: { "Content-Type": "application/json" },
|
|
796
861
|
body: JSON.stringify(body)
|
|
@@ -807,16 +872,39 @@ function requestUrl(req, host) {
|
|
|
807
872
|
return `https://${host}${req.uri}${qs}`;
|
|
808
873
|
}
|
|
809
874
|
function buildObservedMetadata(req) {
|
|
810
|
-
const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(req.headers);
|
|
875
|
+
const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(req.headers);
|
|
811
876
|
const connection = req.connection ?? {};
|
|
877
|
+
const queryKeys = extractQueryKeys(req.querystring);
|
|
812
878
|
return {
|
|
813
879
|
headers,
|
|
814
880
|
...apiKeyFormat && { apiKeyFormat },
|
|
815
881
|
...platformHeaders && { platformHeaders },
|
|
816
882
|
...Object.keys(connection).length > 0 && { connection },
|
|
883
|
+
...cookieCount !== void 0 && { cookieCount },
|
|
884
|
+
...queryKeys && { queryKeys },
|
|
817
885
|
schemaVersion: CAPTURE_SCHEMA_VERSION
|
|
818
886
|
};
|
|
819
887
|
}
|
|
888
|
+
var MAX_QUERY_KEYS = 32;
|
|
889
|
+
var MAX_QUERY_KEY_CHARS = 64;
|
|
890
|
+
function extractQueryKeys(querystring) {
|
|
891
|
+
if (!querystring) return void 0;
|
|
892
|
+
let params;
|
|
893
|
+
try {
|
|
894
|
+
params = new URLSearchParams(querystring);
|
|
895
|
+
} catch {
|
|
896
|
+
return void 0;
|
|
897
|
+
}
|
|
898
|
+
const keys = [];
|
|
899
|
+
for (const key of params.keys()) {
|
|
900
|
+
const normalized = key.toLowerCase().slice(0, MAX_QUERY_KEY_CHARS);
|
|
901
|
+
if (normalized && !keys.includes(normalized)) {
|
|
902
|
+
keys.push(normalized);
|
|
903
|
+
if (keys.length >= MAX_QUERY_KEYS) break;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return keys.length > 0 ? keys : void 0;
|
|
907
|
+
}
|
|
820
908
|
|
|
821
909
|
// src/edge-core/detect.ts
|
|
822
910
|
var SD_JWT_PATTERN = /^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;
|
|
@@ -960,6 +1048,7 @@ function detectCommerce(req, host) {
|
|
|
960
1048
|
|
|
961
1049
|
// src/edge-core/classify.ts
|
|
962
1050
|
var STATIC_EXTENSIONS = /\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i;
|
|
1051
|
+
var SELF_UA_PATTERN = /^astrasync-sdk\//i;
|
|
963
1052
|
var BOT_UA_PATTERN = /curl|wget|python-requests|python-urllib|node-fetch|axios|got|okhttp|go-http-client|libwww|httpie|java\/|ruby|php|scrapy|headless|phantomjs|selenium|playwright|puppeteer|bot|crawler|spider|slurp/i;
|
|
964
1053
|
var BROWSER_UA_PATTERN = /mozilla\/\d/i;
|
|
965
1054
|
function resolvePolicy(config, path) {
|
|
@@ -976,6 +1065,9 @@ function resolvePolicy(config, path) {
|
|
|
976
1065
|
}
|
|
977
1066
|
function classify(req, host, depth) {
|
|
978
1067
|
const userAgent = headerValue(req, "user-agent") ?? "";
|
|
1068
|
+
if (SELF_UA_PATTERN.test(userAgent)) {
|
|
1069
|
+
return { tier: "self" };
|
|
1070
|
+
}
|
|
979
1071
|
const credentials = extractCredentials(req.headers, {});
|
|
980
1072
|
if (credentials.astraId || credentials.apiKey || credentials.jwt) {
|
|
981
1073
|
return { tier: "verified", protocol: "x-astra" };
|
|
@@ -1006,7 +1098,7 @@ function classify(req, host, depth) {
|
|
|
1006
1098
|
}
|
|
1007
1099
|
return { tier: "human" };
|
|
1008
1100
|
}
|
|
1009
|
-
var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
|
|
1101
|
+
var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN, SELF_UA_PATTERN };
|
|
1010
1102
|
|
|
1011
1103
|
// src/edge-core/messaging.ts
|
|
1012
1104
|
var PROTOCOL_MESSAGES = {
|
|
@@ -1046,6 +1138,20 @@ function buildDenyBody(outcome, apiBaseUrl) {
|
|
|
1046
1138
|
}
|
|
1047
1139
|
};
|
|
1048
1140
|
}
|
|
1141
|
+
function buildUnavailableBody(outcome) {
|
|
1142
|
+
return {
|
|
1143
|
+
success: false,
|
|
1144
|
+
error: {
|
|
1145
|
+
code: "VERIFICATION_UNAVAILABLE",
|
|
1146
|
+
message: "Agent verification is temporarily unavailable and this path enforces verification. Your identity was not evaluated and no denial was recorded \u2014 retry with exponential backoff.",
|
|
1147
|
+
failures: outcome.reasons,
|
|
1148
|
+
...outcome.correlationId && { correlationId: outcome.correlationId },
|
|
1149
|
+
guidance: {
|
|
1150
|
+
message: "Retry the request with exponential backoff; the condition is transient."
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1049
1155
|
function buildManualReviewBody(outcome) {
|
|
1050
1156
|
return {
|
|
1051
1157
|
success: false,
|
|
@@ -1088,6 +1194,14 @@ function applyOutcome(options, outcome, policy) {
|
|
|
1088
1194
|
}
|
|
1089
1195
|
switch (outcome.decision) {
|
|
1090
1196
|
case "deny":
|
|
1197
|
+
if (outcome.unavailable) {
|
|
1198
|
+
return {
|
|
1199
|
+
action: "respond",
|
|
1200
|
+
status: 503,
|
|
1201
|
+
headers: { "Retry-After": "30" },
|
|
1202
|
+
bodyJson: buildUnavailableBody(outcome)
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1091
1205
|
return {
|
|
1092
1206
|
action: "respond",
|
|
1093
1207
|
status: 403,
|
|
@@ -1326,6 +1440,17 @@ function outcomeFromVerifyResult(classification, result) {
|
|
|
1326
1440
|
};
|
|
1327
1441
|
return outcome;
|
|
1328
1442
|
}
|
|
1443
|
+
function unavailableOutcome(classification, policy, reason, correlationId) {
|
|
1444
|
+
const outcome = baseOutcome(classification);
|
|
1445
|
+
outcome.unavailable = true;
|
|
1446
|
+
outcome.reasons = [reason];
|
|
1447
|
+
if (correlationId) outcome.correlationId = correlationId;
|
|
1448
|
+
if (policy.mode === "enforce") outcome.decision = "deny";
|
|
1449
|
+
return outcome;
|
|
1450
|
+
}
|
|
1451
|
+
function isApiErrorResult(result) {
|
|
1452
|
+
return (result.failures ?? []).some((f) => f.dimension === "verify_access.api_error") === true && !result.misconfigured;
|
|
1453
|
+
}
|
|
1329
1454
|
async function runTier(options, req, classification, policy, apiKey, sampling) {
|
|
1330
1455
|
const budgets = options.budgets ?? {};
|
|
1331
1456
|
const beaconMs = budgets.beaconMs ?? 300;
|
|
@@ -1352,9 +1477,19 @@ async function runTier(options, req, classification, policy, apiKey, sampling) {
|
|
|
1352
1477
|
verifyMs
|
|
1353
1478
|
);
|
|
1354
1479
|
if (!result) {
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1480
|
+
return unavailableOutcome(
|
|
1481
|
+
classification,
|
|
1482
|
+
policy,
|
|
1483
|
+
"verify-access unreachable within budget"
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
1486
|
+
if (isApiErrorResult(result)) {
|
|
1487
|
+
return unavailableOutcome(
|
|
1488
|
+
classification,
|
|
1489
|
+
policy,
|
|
1490
|
+
result.denialReasons?.[0] ?? "verify-access api error",
|
|
1491
|
+
result.correlationId
|
|
1492
|
+
);
|
|
1358
1493
|
}
|
|
1359
1494
|
return outcomeFromVerifyResult(classification, result);
|
|
1360
1495
|
}
|
|
@@ -1365,9 +1500,19 @@ async function runTier(options, req, classification, policy, apiKey, sampling) {
|
|
|
1365
1500
|
verifyMs
|
|
1366
1501
|
);
|
|
1367
1502
|
if (!result) {
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1503
|
+
return unavailableOutcome(
|
|
1504
|
+
classification,
|
|
1505
|
+
policy,
|
|
1506
|
+
"verify-access unreachable within budget"
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
if (isApiErrorResult(result)) {
|
|
1510
|
+
return unavailableOutcome(
|
|
1511
|
+
classification,
|
|
1512
|
+
policy,
|
|
1513
|
+
result.denialReasons?.[0] ?? "verify-access api error",
|
|
1514
|
+
result.correlationId
|
|
1515
|
+
);
|
|
1371
1516
|
}
|
|
1372
1517
|
return outcomeFromVerifyResult(classification, result);
|
|
1373
1518
|
}
|
|
@@ -1409,7 +1554,7 @@ async function evaluateEdgeRequest(options, req, config, apiKey) {
|
|
|
1409
1554
|
if (policy.skip) return { policy, application: passUntouched() };
|
|
1410
1555
|
const host = headerValue(req, "x-forwarded-host") ?? headerValue(req, "host") ?? new URL(options.counterpartyUrl).host;
|
|
1411
1556
|
const classification = classify(req, host, policy.depth);
|
|
1412
|
-
if (classification.tier === "human") {
|
|
1557
|
+
if (classification.tier === "human" || classification.tier === "self") {
|
|
1413
1558
|
return { policy, classification, application: passUntouched() };
|
|
1414
1559
|
}
|
|
1415
1560
|
const outcome = await runTier(options, req, classification, policy, apiKey, config.sampling);
|