@astrasyncai/verification-gateway 5.21.1 → 5.22.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/CHANGELOG.md +16 -0
- package/README.md +19 -7
- package/dist/adapters/express.js +75 -9
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +75 -9
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.js +34 -3
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +34 -3
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.js +75 -9
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +75 -9
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.js +1 -1
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +1 -1
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +1 -1
- package/dist/bin/astrasync-codex-hook.js +1 -1
- package/dist/bin/astrasync-guard.js +1 -1
- package/dist/bin/astrasync.js +1 -1
- package/dist/browser/background.js +1 -1
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +1 -1
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +1 -1
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +1 -1
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +1 -1
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +1 -1
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.js +1 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +1 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +4 -1
- package/dist/edge-core/index.d.ts +4 -1
- package/dist/edge-core/index.js +60 -13
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +60 -13
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.js +1 -1
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +1 -1
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/index.d.mts +27 -6
- package/dist/index.d.ts +27 -6
- package/dist/index.js +75 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +75 -9
- package/dist/index.mjs.map +1 -1
- package/dist/metadata-capture.d.mts +5 -3
- package/dist/metadata-capture.d.ts +5 -3
- package/dist/metadata-capture.js +33 -2
- package/dist/metadata-capture.js.map +1 -1
- package/dist/metadata-capture.mjs +33 -2
- package/dist/metadata-capture.mjs.map +1 -1
- package/dist/platform-signatures.d.mts +20 -1
- package/dist/platform-signatures.d.ts +20 -1
- package/dist/platform-signatures.js +41 -6
- package/dist/platform-signatures.js.map +1 -1
- package/dist/platform-signatures.mjs +41 -6
- package/dist/platform-signatures.mjs.map +1 -1
- package/dist/registration/index.js +1 -1
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +1 -1
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.js +1 -1
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +1 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/verify.js +1 -1
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +1 -1
- package/dist/verify.mjs.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,22 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
8
8
|
Full architectural context for each release lives in the platform spec at
|
|
9
9
|
`docs/research/adapter-architecture-technical-requirements.md`.
|
|
10
10
|
|
|
11
|
+
## [5.22.0] — 2026-10-26 — edge verdicts on beacons, WAF bot labels, Akamai and Imperva headers
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Edge verdict on beacons.** Edge beacons now carry `edgeDecision` (`allow` | `deny`) and `edgeMode`, decided before the beacon is sent. An enforce-mode deny at the edge is recorded as a deny in Visit Intelligence instead of looking like a pass.
|
|
16
|
+
- **`wafLabelPatterns`** on `PlatformSignatureDef` and **`wafLabels`** on `PlatformDetectionInput`: attribute a request by the bot name a customer's WAF assigned (AWS Bot Control, Akamai Bot Manager, Cloudflare verified-bot category, Imperva client app). Seed patterns are added for Claude, ChatGPT, Gemini, Perplexity, Grok and Meta AI. They only ever match rows read from WAF security logs.
|
|
17
|
+
- **`signature-agent`** is now a platform-signal header, and ChatGPT's Web Bot Auth directory host attributes a signed agent whose user agent is stock Chrome.
|
|
18
|
+
- `deriveConnectionFromHeaders`: `Incap-Client-IP` (Imperva) in the client-IP precedence, and Akamai's `X-Akamai-Edgescape` filling country, region, city, lat/long, time zone, ASN and continent where no direct header supplied them.
|
|
19
|
+
|
|
20
|
+
## [5.21.2] — 2026-10-25 — xAI Grok and Meta AI crawler signatures
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- Platform signatures for xAI Grok (`GrokBot/1.0`, `xAI-Grok/1.0`, `Grok-DeepSearch/1.0`) and Meta AI (`Meta-WebIndexer`, `Meta-ExternalFetcher`), so these crawlers attribute as named platforms (`grok`, `meta-ai`) instead of unknown. Anchored, versioned tokens only: a stock browser UA never matches.
|
|
25
|
+
- A note in the registry: Meta's Muse and OpenAI's Dots publish no user agent and browse through real Chromium sessions, so they can't be detected by UA. Verified identity is the only reliable signal for them.
|
|
26
|
+
|
|
11
27
|
## [5.21.1] — 2026-10-24 — pricingContextFrom exported
|
|
12
28
|
|
|
13
29
|
### Added
|
package/README.md
CHANGED
|
@@ -1083,15 +1083,27 @@ Both consume the same dashboard-managed policy (`./edge-config`), the same platf
|
|
|
1083
1083
|
|
|
1084
1084
|
Header-level capture (`ObservedMetadata.headers` etc.) is identical everywhere: **automatic on every verify** for the edge adapters and the `express` / `nextjs` / `mcp` SDK adapters (no config, no opt-out). What differs is the **connection layer** — and the deciding variable is _who operates the CDN in front of your origin_:
|
|
1085
1085
|
|
|
1086
|
-
| Integration | Connection layer
|
|
1087
|
-
| -------------------------------------------------------------------------------------------------- |
|
|
1088
|
-
| **Edge adapter** (`adapter-lambda`, `adapter-edge`) | **Full** — native platform signals (IP, ASN, geo, TLS, HTTP version, device class, header structure)
|
|
1089
|
-
| **SDK behind a CDN you control** (your own Cloudflare / Fastly / Vercel / CloudFront distribution) | **Derived** — geo/ASN/TLS promoted from the CDN's injected headers (`deriveConnectionFromHeaders`)
|
|
1090
|
-
| **SDK behind
|
|
1091
|
-
| **SDK
|
|
1086
|
+
| Integration | Connection layer | TLS fingerprint (JA3/JA4) |
|
|
1087
|
+
| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1088
|
+
| **Edge adapter** (`adapter-lambda`, `adapter-edge`) | **Full** — native platform signals (IP, ASN, geo, TLS, HTTP version, device class, header structure) | CloudFront: **JA3 + JA4** (2.2.0+, via the template's origin request policy). Cloudflare Workers: Bot Management zones only. Vercel: JA4 with BotID. Fastly: none |
|
|
1089
|
+
| **SDK behind a CDN you control** (your own Cloudflare / Fastly / Vercel / CloudFront distribution) | **Derived** — geo/ASN/TLS promoted from the CDN's injected headers (`deriveConnectionFromHeaders`) | Cloudflare: JA3 + JA4 (`cf-ja3-hash` / `cf-ja4`, plan-dependent). CloudFront: JA3 + JA4 — add the `CloudFront-Viewer-JA3/JA4-Fingerprint` headers to your origin request policy |
|
|
1090
|
+
| **SDK behind Akamai or Imperva** | **Derived** — IP from `True-Client-IP` / `Incap-Client-IP`; geo/ASN from Akamai's `X-Akamai-Edgescape` when the property adds it | None |
|
|
1091
|
+
| **SDK behind a platform-managed CDN** (Railway, Render, Fly, Heroku, …) | **IP only** — platform CDNs forward `x-forwarded-for` but NOT their provider-specific geo/TLS headers to customer origins | None — no fingerprint header reaches your origin |
|
|
1092
|
+
| **SDK, no CDN** | Absent (IP from `x-forwarded-for` when a proxy sets it) | None |
|
|
1092
1093
|
|
|
1093
1094
|
> "Behind Fastly" ≠ "receives Fastly's headers": platform-managed CDNs are operated by the platform, and they do not forward `Fastly-Client-IP`-style headers to your origin. If connection-layer signals or fingerprints matter to you on a managed platform, deploy the edge adapter in front (bucket 1) — that is the whole point of the TAG.
|
|
1094
1095
|
|
|
1096
|
+
> **Blocked at the edge.** Every row above sees only traffic the CDN's own
|
|
1097
|
+
> firewall and bot rules let through: those rules run first. Requests they
|
|
1098
|
+
> block reach no AstraSync code. Connect the CDN or WAF's security logs (an
|
|
1099
|
+
> edge-log connector on the endpoint page) to see them in Visit Intelligence
|
|
1100
|
+
> as "Blocked at your edge", classified from the logs and never
|
|
1101
|
+
> cryptographically verified.
|
|
1102
|
+
>
|
|
1103
|
+
> Edge beacons also carry the gateway's own verdict (`edgeDecision`,
|
|
1104
|
+
> `edgeMode`; 5.22.0), so an enforce-mode deny at the edge is recorded as a
|
|
1105
|
+
> deny.
|
|
1106
|
+
|
|
1095
1107
|
> **Note:** `runCommercePipeline` (on the `./transport` subpath) is the **server-side** orchestrator of commerce-protocol verification — it runs inside the AstraSync verify-access service. The edge adapters forward raw commerce artifacts to verify-access and never bundle or call the pipeline themselves.
|
|
1096
1108
|
|
|
1097
1109
|
### Which `EdgeConfig` fields each integration consumes
|
|
@@ -1117,7 +1129,7 @@ The ONE sanitiser deciding what is safe to store from an inbound agent's request
|
|
|
1117
1129
|
- `sanitizeHeaders(raw)` → `{ headers, apiKeyFormat?, platformHeaders? }` — drops genuine secrets (cookies, signatures, AstraSync credential headers), reduces credential headers to a safe **format prefix** (e.g. `sk-ant-api03` — a platform signal, never the key), keeps everything else verbatim, and enforces size caps (`MAX_HEADERS`, `MAX_HEADER_VALUE_BYTES`, `MAX_TOTAL_BYTES`).
|
|
1118
1130
|
- `extractApiKeyFormat(value)` — the safe key-format prefix of a credential header value, or `undefined` for unrecognised/opaque tokens.
|
|
1119
1131
|
- `extractPlatformHeaders(sanitized)` — the platform-signal subset (`openai-organization`, `anthropic-version`, `x-goog-user-project`, …) of an already-sanitised map.
|
|
1120
|
-
- `deriveConnectionFromHeaders(headers)` — promotes CDN-injected headers (Cloudflare / Fastly / Vercel / CloudFront geo, client-IP, TLS-fingerprint, and header-structure headers — incl. `cf-ja3-hash` / `cf-ja4` and `cloudfront-viewer-ja3-fingerprint` / `-ja4-fingerprint` from 4.3.0) into the `connection` block, so SDK-only deployments behind a CDN **you control** still get connection-layer attribution (see the capture matrix for the platform-managed-CDN caveat). Values are truncated to `MAX_CONNECTION_VALUE_CHARS` (256 — the platform bound). Native edge signals remain authoritative; this is the SDK-level fallback.
|
|
1132
|
+
- `deriveConnectionFromHeaders(headers)` — promotes CDN-injected headers (Cloudflare / Fastly / Vercel / CloudFront / Akamai EdgeScape geo, client-IP incl. Imperva `incap-client-ip` (5.22.0), TLS-fingerprint, and header-structure headers — incl. `cf-ja3-hash` / `cf-ja4` and `cloudfront-viewer-ja3-fingerprint` / `-ja4-fingerprint` from 4.3.0) into the `connection` block, so SDK-only deployments behind a CDN **you control** still get connection-layer attribution (see the capture matrix for the platform-managed-CDN caveat). Values are truncated to `MAX_CONNECTION_VALUE_CHARS` (256 — the platform bound). Native edge signals remain authoritative; this is the SDK-level fallback.
|
|
1121
1133
|
- `buildSdkObservedMetadata(rawHeaders)` — the one builder the express / nextjs / mcp adapters share: sanitised headers + `apiKeyFormat` + `platformHeaders` + derived `connection` + `schemaVersion`.
|
|
1122
1134
|
- `CAPTURE_SCHEMA_VERSION` — the capture-semantics version stamped on every `ObservedMetadata` (currently `1`). Capture semantics are **frozen within a major version**: `schemaVersion` bumps only on a semantic change to what is kept/dropped/derived, never for additive signals. The verify body's `sdkVersion` disambiguates builds.
|
|
1123
1135
|
- Types: `ObservedMetadata` (`{ headers, apiKeyFormat?, platformHeaders?, connection?, schemaVersion? }` — `connection` is populated natively by edge adapters and via CDN-header derivation by SDK adapters), `SanitizeHeadersResult`.
|
package/dist/adapters/express.js
CHANGED
|
@@ -60,7 +60,11 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
|
60
60
|
"x-goog-api-client",
|
|
61
61
|
"user-agent",
|
|
62
62
|
"x-agent-card-url",
|
|
63
|
-
"x-agent-provider"
|
|
63
|
+
"x-agent-provider",
|
|
64
|
+
// Round 31 — Web Bot Auth's Signature-Agent names the signing agent's
|
|
65
|
+
// directory host (a public URL, never a secret). It attributes a signed
|
|
66
|
+
// agent even when its UA is stock Chrome.
|
|
67
|
+
"signature-agent"
|
|
64
68
|
]);
|
|
65
69
|
var PLATFORM_HEADER_PREFIXES = [
|
|
66
70
|
"x-goog-",
|
|
@@ -177,7 +181,12 @@ function sanitizeHeaders(raw) {
|
|
|
177
181
|
return result;
|
|
178
182
|
}
|
|
179
183
|
var CONNECTION_HEADER_SOURCES = [
|
|
180
|
-
|
|
184
|
+
// Round 31: `incap-client-ip` = Imperva Cloud WAF; `true-client-ip` is
|
|
185
|
+
// also Akamai's client-IP header when the property enables it.
|
|
186
|
+
[
|
|
187
|
+
"ip",
|
|
188
|
+
["cf-connecting-ip", "fastly-client-ip", "incap-client-ip", "true-client-ip", "x-real-ip"]
|
|
189
|
+
],
|
|
181
190
|
[
|
|
182
191
|
"country",
|
|
183
192
|
["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
|
|
@@ -228,6 +237,28 @@ function deriveConnectionFromHeaders(headers) {
|
|
|
228
237
|
if (portMatch) connection.sourcePort = portMatch[1];
|
|
229
238
|
if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
|
|
230
239
|
}
|
|
240
|
+
{
|
|
241
|
+
const edgescape = (lower["x-akamai-edgescape"] ?? lower["akamai-edgescape"])?.trim();
|
|
242
|
+
if (edgescape) {
|
|
243
|
+
const fields = /* @__PURE__ */ new Map();
|
|
244
|
+
for (const pair of edgescape.split(",")) {
|
|
245
|
+
const i = pair.indexOf("=");
|
|
246
|
+
if (i > 0) fields.set(pair.slice(0, i).trim().toLowerCase(), pair.slice(i + 1).trim());
|
|
247
|
+
}
|
|
248
|
+
const fill = (key, field) => {
|
|
249
|
+
const v = fields.get(field);
|
|
250
|
+
if (v && !connection[key]) connection[key] = v.slice(0, MAX_CONNECTION_VALUE_CHARS);
|
|
251
|
+
};
|
|
252
|
+
fill("country", "country_code");
|
|
253
|
+
fill("region", "region_code");
|
|
254
|
+
fill("city", "city");
|
|
255
|
+
fill("latitude", "lat");
|
|
256
|
+
fill("longitude", "long");
|
|
257
|
+
fill("timeZone", "timezone");
|
|
258
|
+
fill("asn", "asnum");
|
|
259
|
+
fill("continent", "continent");
|
|
260
|
+
}
|
|
261
|
+
}
|
|
231
262
|
if (!connection.ip) {
|
|
232
263
|
const xff = lower["x-forwarded-for"];
|
|
233
264
|
const client = xff?.split(",")[0]?.trim();
|
|
@@ -479,7 +510,7 @@ function routePolicyOf(route) {
|
|
|
479
510
|
}
|
|
480
511
|
|
|
481
512
|
// src/version.ts
|
|
482
|
-
var SDK_VERSION = "5.
|
|
513
|
+
var SDK_VERSION = "5.22.0";
|
|
483
514
|
|
|
484
515
|
// src/http.ts
|
|
485
516
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -1900,7 +1931,8 @@ var PLATFORM_AGENT_SIGNATURES = [
|
|
|
1900
1931
|
uaPatterns: [/claude/i, /anthropic/i],
|
|
1901
1932
|
agentCardPatterns: [/anthropic\.com/i, /claude\.ai/i],
|
|
1902
1933
|
apiKeyFormatPatterns: [/^sk-ant/i],
|
|
1903
|
-
headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i]
|
|
1934
|
+
headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i],
|
|
1935
|
+
wafLabelPatterns: [/\bclaude/i, /\banthropic/i]
|
|
1904
1936
|
},
|
|
1905
1937
|
{
|
|
1906
1938
|
vendor: "chatgpt",
|
|
@@ -1911,7 +1943,14 @@ var PLATFORM_AGENT_SIGNATURES = [
|
|
|
1911
1943
|
uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i, /\bGPTBot\b/i, /\bOAI-SearchBot\b/i],
|
|
1912
1944
|
agentCardPatterns: [/openai\.com/i, /chatgpt\.com/i],
|
|
1913
1945
|
apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],
|
|
1914
|
-
headerPatterns: [
|
|
1946
|
+
headerPatterns: [
|
|
1947
|
+
/^openai-organization/i,
|
|
1948
|
+
/^openai-project/i,
|
|
1949
|
+
/^openai-version/i,
|
|
1950
|
+
// ChatGPT agent signs with Web Bot Auth under this directory host.
|
|
1951
|
+
/^signature-agent: "?https:\/\/chatgpt\.com/i
|
|
1952
|
+
],
|
|
1953
|
+
wafLabelPatterns: [/\bgptbot\b/i, /\bchatgpt/i, /\boai-searchbot\b/i, /\bopenai/i]
|
|
1915
1954
|
},
|
|
1916
1955
|
{
|
|
1917
1956
|
vendor: "gemini",
|
|
@@ -1932,7 +1971,8 @@ var PLATFORM_AGENT_SIGNATURES = [
|
|
|
1932
1971
|
],
|
|
1933
1972
|
agentCardPatterns: [/gemini\.google\.com/i, /ai\.google\.dev/i],
|
|
1934
1973
|
apiKeyFormatPatterns: [/^aiza/i],
|
|
1935
|
-
headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i]
|
|
1974
|
+
headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i],
|
|
1975
|
+
wafLabelPatterns: [/\bgoogle-extended\b/i, /\bgoogleother\b/i, /\bgemini/i]
|
|
1936
1976
|
},
|
|
1937
1977
|
{
|
|
1938
1978
|
vendor: "cursor",
|
|
@@ -1951,7 +1991,31 @@ var PLATFORM_AGENT_SIGNATURES = [
|
|
|
1951
1991
|
displayName: "Perplexity",
|
|
1952
1992
|
uaPatterns: [/perplexity/i],
|
|
1953
1993
|
agentCardPatterns: [/perplexity\.ai/i],
|
|
1954
|
-
apiKeyFormatPatterns: [/^pplx-/i]
|
|
1994
|
+
apiKeyFormatPatterns: [/^pplx-/i],
|
|
1995
|
+
wafLabelPatterns: [/\bperplexity/i]
|
|
1996
|
+
},
|
|
1997
|
+
// Round 30.1: xAI's documented crawlers (GrokBot/1.0, xAI-Grok/1.0,
|
|
1998
|
+
// Grok-DeepSearch/1.0). Anchored, versioned tokens — never in a stock UA.
|
|
1999
|
+
{
|
|
2000
|
+
vendor: "grok",
|
|
2001
|
+
displayName: "xAI Grok",
|
|
2002
|
+
uaPatterns: [/\bGrokBot\//i, /\bxAI-Grok\//i, /\bGrok-DeepSearch\//i],
|
|
2003
|
+
agentCardPatterns: [/\bx\.ai\b/i, /grok\.com/i],
|
|
2004
|
+
apiKeyFormatPatterns: [/^xai-/i],
|
|
2005
|
+
wafLabelPatterns: [/\bgrok/i, /\bxai\b/i]
|
|
2006
|
+
},
|
|
2007
|
+
// Round 30.1: Meta AI's index and fetch crawlers. facebookexternalhit
|
|
2008
|
+
// (link previews) is deliberately NOT here — it stays a generic crawler.
|
|
2009
|
+
{
|
|
2010
|
+
vendor: "meta-ai",
|
|
2011
|
+
displayName: "Meta AI",
|
|
2012
|
+
uaPatterns: [/\bMeta-WebIndexer\b/i, /\bMeta-ExternalFetcher\b/i],
|
|
2013
|
+
agentCardPatterns: [/meta\.ai/i],
|
|
2014
|
+
wafLabelPatterns: [
|
|
2015
|
+
/\bmeta-webindexer\b/i,
|
|
2016
|
+
/\bmeta-externalfetcher\b/i,
|
|
2017
|
+
/\bmeta-externalagent\b/i
|
|
2018
|
+
]
|
|
1955
2019
|
},
|
|
1956
2020
|
// Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
|
|
1957
2021
|
// never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
|
|
@@ -1999,6 +2063,7 @@ function matchPlatformSignature(signatures, input) {
|
|
|
1999
2063
|
const apiKeyFormat = input.apiKeyFormat ?? "";
|
|
2000
2064
|
const ja4 = input.ja4 ?? "";
|
|
2001
2065
|
const asn = input.asn ?? "";
|
|
2066
|
+
const wafLabels = input.wafLabels ?? [];
|
|
2002
2067
|
const headerLines = [];
|
|
2003
2068
|
if (input.platformHeaders) {
|
|
2004
2069
|
for (const [name, value] of Object.entries(input.platformHeaders)) {
|
|
@@ -2012,7 +2077,8 @@ function matchPlatformSignature(signatures, input) {
|
|
|
2012
2077
|
const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
|
|
2013
2078
|
const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
|
|
2014
2079
|
const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
|
|
2015
|
-
|
|
2080
|
+
const wafMatch = wafLabels.length > 0 && sig.wafLabelPatterns?.some((p) => wafLabels.some((label) => p.test(label)));
|
|
2081
|
+
if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch || wafMatch) {
|
|
2016
2082
|
const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
|
|
2017
2083
|
const cardHost = (() => {
|
|
2018
2084
|
try {
|
|
@@ -2021,7 +2087,7 @@ function matchPlatformSignature(signatures, input) {
|
|
|
2021
2087
|
return "";
|
|
2022
2088
|
}
|
|
2023
2089
|
})();
|
|
2024
|
-
const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
|
|
2090
|
+
const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || wafMatch && `waf:${wafLabels[0]?.toLowerCase()}` || "unknown";
|
|
2025
2091
|
return {
|
|
2026
2092
|
vendor: sig.vendor,
|
|
2027
2093
|
fingerprintKey: `${sig.vendor}:${keyBasis}`,
|