@patchstack/connect 0.3.26 → 0.3.27

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pulse-token.ts","../src/types.ts","../src/client.ts","../src/config.ts","../src/parsers/consistency.ts","../src/parsers/npm.ts","../src/parsers/node_modules.ts","../src/parsers/pnpm.ts","../src/parsers/yarn.ts","../src/parsers/index.ts","../src/normalize.ts","../src/protect/refresh-manifest.ts","../src/protect/runtime.js","../src/protect/safe-origin.js","../src/protect/engine/client.js","../src/protect/engine/request.js","../src/protect/engine/normalizer.js","../src/protect/engine/engine.js","../src/protect/engine/pulse-client.js","../src/protect/engine/fetch.js","../src/protect/engine/node.js","../src/protect/egress.js","../src/protect/defaults.js","../src/protect/block-page.js","../src/protect/rules/store.js","../src/protect/rules/validate.js","../src/protect/rules/source.js","../src/protect/rules/refresh.js","../src/protect/firewall-log.js","../src/protect/supabase-guard.js"],"sourcesContent":["import type { Config } from './types.js';\n\n/**\n * Bearer tokens for the authenticated Pulse endpoints (ADR-0018).\n *\n * Deliberately separate from the block-log token flow in\n * `src/protect/firewall-log.js`: that path talks to the auth/ Lambda's\n * /oauth/token and must keep working exactly as it does today.\n */\n\nconst TOKEN_SKEW_MS = 60_000;\n\n/** Build the Pulse token URL corresponding to a manifest endpoint override. */\nexport function buildTokenUrl(manifestEndpoint: string): string {\n const url = new URL(manifestEndpoint);\n const path = url.pathname.replace(/\\/$/, '');\n url.pathname = path.endsWith('/manifest')\n ? `${path.slice(0, -'/manifest'.length)}/token`\n : '/monitor/pulse/token';\n url.search = '';\n url.hash = '';\n return url.toString();\n}\n\n/** Split the WP-format `{secret}-{oauth.id}` credential on its last hyphen. */\nexport function parsePulseAuth(credential: string): { clientId: string; clientSecret: string } | null {\n const index = credential.lastIndexOf('-');\n if (index <= 0 || index === credential.length - 1) return null;\n\n const clientId = credential.slice(index + 1);\n if (!/^\\d+$/.test(clientId)) return null;\n\n return { clientId, clientSecret: credential.slice(0, index) };\n}\n\nlet cached: { token: string; expiresAt: number } | null = null;\nlet inflight: Promise<string | null> | null = null;\n\n/** Drops the cached token. Exported for tests and for 401 handling. */\nexport function clearPulseToken(): void {\n cached = null;\n}\n\n/**\n * Resolve a bearer token for `config.pulseAuth`, exchanging one if needed.\n *\n * Returns null whenever a token cannot be obtained — no credential, a rejected\n * exchange, a network failure. Callers then send the request unauthenticated,\n * which the server still accepts while it runs dual-accept.\n */\nexport async function getPulseToken(\n config: Config,\n fetchImpl: typeof fetch = fetch,\n): Promise<string | null> {\n // Not `=== null`: Config is public, so callers can hand us an object that\n // predates this field, and an unusable credential must never throw here.\n if (typeof config.pulseAuth !== 'string' || config.pulseAuth.length === 0) return null;\n\n if (cached !== null && Date.now() < cached.expiresAt - TOKEN_SKEW_MS) {\n return cached.token;\n }\n if (inflight !== null) return inflight;\n\n const credentials = parsePulseAuth(config.pulseAuth);\n if (credentials === null) return null;\n\n inflight = (async () => {\n try {\n const response = await fetchImpl(buildTokenUrl(config.endpoint), {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify({\n grant_type: 'client_credentials',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n }),\n signal: AbortSignal.timeout(config.timeoutMs),\n });\n\n if (!response.ok) return null;\n\n const body = (await response.json()) as { access_token?: unknown; expires_in?: unknown };\n if (typeof body.access_token !== 'string' || body.access_token.length === 0) return null;\n\n const expiresIn = Number(body.expires_in);\n const ttlMs = Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn * 1000 : 3600_000;\n cached = { token: body.access_token, expiresAt: Date.now() + ttlMs };\n\n return body.access_token;\n } catch {\n return null;\n } finally {\n inflight = null;\n }\n })();\n\n return inflight;\n}\n\n/**\n * `Authorization` header for a Pulse request, or `{}` when unauthenticated.\n * Spread into an existing header object so call sites stay one line.\n */\nexport async function pulseAuthHeader(\n config: Config,\n fetchImpl: typeof fetch = fetch,\n): Promise<Record<string, string>> {\n const token = await getPulseToken(config, fetchImpl);\n return token === null ? {} : { Authorization: `Bearer ${token}` };\n}\n\n/**\n * Send a Pulse request, attaching the bearer token and retrying once if the\n * server rejects it.\n *\n * A cached token can stop being valid before it expires — the credential may\n * have been rotated or revoked meanwhile — so the server's 401 is authoritative\n * over our own clock. Without this a long-running process would keep presenting\n * a dead token until its local expiry.\n *\n * Only 401 retries: a 403 is a scope or site mismatch, which a fresh token\n * would not fix.\n */\nexport async function pulseFetch(\n config: Config,\n url: string,\n init: RequestInit,\n fetchImpl: typeof fetch = fetch,\n): Promise<Response> {\n const send = async () => {\n const auth = await pulseAuthHeader(config, fetchImpl);\n const response = await fetchImpl(url, {\n ...init,\n headers: { ...(init.headers as Record<string, string> | undefined), ...auth },\n });\n\n return { response, authenticated: auth.Authorization !== undefined };\n };\n\n const first = await send();\n\n // Retrying an unauthenticated request would just repeat it: the 401 was\n // about something other than our token.\n if (first.response.status === 401 && first.authenticated) {\n clearPulseToken();\n\n return (await send()).response;\n }\n\n return first.response;\n}\n","export type Ecosystem = 'npm' | 'composer';\n\n/**\n * Which environment a manifest was captured in. The connector reports from real\n * builds (prebuild scan / build hooks), so it defaults to 'production'. Override\n * with PATCHSTACK_ENVIRONMENT=sandbox (or \"environment\" in .patchstackrc.json)\n * for test manifests.\n */\nexport type Environment = 'production' | 'sandbox';\n\nexport interface PackageEntry {\n name: string;\n version: string;\n path?: string;\n direct?: boolean;\n}\n\nexport interface Manifest {\n ecosystem: Ecosystem;\n packages: PackageEntry[];\n /**\n * Human-readable scan diagnostics — set when the preferred lockfile looked\n * stale (missing dependencies declared in package.json) and another source\n * was used, or when no fully-consistent source existed. Never fatal.\n */\n warnings?: string[];\n}\n\nexport interface Config {\n /**\n * The site UUID. `null` means we don't have one yet — `postManifest` will then\n * post to the bare endpoint, the server will provision a fresh site, and the\n * UUID it returns should be persisted via `persistSiteUuid()`.\n */\n siteUuid: string | null;\n /**\n * WP-format site API key (`{oauth.secret}-{oauth.id}`) for authenticated\n * block-log reporting via connector `/api/logs/log`. Issued once as `api_key`\n * on first provision. Prefer `PATCHSTACK_API_KEY` in production deploys.\n */\n apiKey: string | null;\n /**\n * Credential for the authenticated Pulse endpoints (ADR-0018). Exchanged for\n * a short-lived bearer token at `monitor/pulse/token`; never sent directly.\n * Falls back to `apiKey` when unset. Prefer `PATCHSTACK_PULSE_AUTH`.\n */\n pulseAuth: string | null;\n endpoint: string;\n timeoutMs: number;\n /** Environment to report the manifest under. Defaults to 'production'. */\n environment: Environment;\n /**\n * Whether the connector manages the disclosure-widget tag (source shell on\n * `scan`, built HTML on `mark-build`). Defaults to true; persist\n * `\"widget\": false` in .patchstackrc.json for dependency-scanning only.\n */\n widget: boolean;\n}\n\nexport interface StoreManifestResponse {\n /** The UUID of the site the manifest was stored against. Always returned. */\n uuid?: string;\n /**\n * WP-format API key for connector log ingest. Present only when oauth\n * credentials are created in this request (first provision / backfill).\n */\n api_key?: string;\n stored: boolean;\n manifest_id?: number;\n checksum?: string;\n reason?: string;\n message?: string;\n error?: string;\n}\n\nexport class PatchstackError extends Error {\n constructor(\n message: string,\n public readonly code:\n | 'CONFIG_MISSING'\n | 'CONFIG_INVALID'\n | 'LOCKFILE_NOT_FOUND'\n | 'LOCKFILE_UNSUPPORTED'\n | 'LOCKFILE_PARSE_ERROR'\n | 'NETWORK_ERROR'\n | 'NETWORK_TIMEOUT'\n | 'SITE_NOT_FOUND'\n | 'VALIDATION_ERROR'\n | 'SERVER_ERROR',\n public readonly cause?: unknown,\n ) {\n super(message);\n this.name = 'PatchstackError';\n }\n}\n","import { PatchstackError, type Config, type StoreManifestResponse } from './types.js';\nimport type { WirePayload } from './normalize.js';\nimport { pulseFetch } from './pulse-token.js';\n\nexport const DEFAULT_ENDPOINT = 'https://api.patchstack.com/monitor/pulse/manifest';\nexport const DEFAULT_TIMEOUT_MS = 30_000;\n\nexport function buildEndpointUrl(base: string, siteUuid?: string | null): string {\n const trimmed = base.replace(/\\/$/, '');\n return siteUuid !== undefined && siteUuid !== null && siteUuid.length > 0\n ? `${trimmed}/${encodeURIComponent(siteUuid)}`\n : trimmed;\n}\n\n/** Build the live Pulse rules URL corresponding to a manifest endpoint override. */\nexport function buildRulesUrl(manifestEndpoint: string, siteUuid: string): string {\n const url = new URL(manifestEndpoint);\n const path = url.pathname.replace(/\\/$/, '');\n url.pathname = path.endsWith('/manifest')\n ? `${path.slice(0, -'/manifest'.length)}/rules/${encodeURIComponent(siteUuid)}`\n : `/monitor/pulse/rules/${encodeURIComponent(siteUuid)}`;\n url.search = '';\n url.hash = '';\n return url.toString();\n}\n\n/**\n * Build the claim URL for a site. The claim page lives on the same origin as\n * the API endpoint, at `/monitor/claim?site=<uuid>`. Using the API endpoint's\n * origin (rather than a hard-coded https://api.patchstack.com) means staging,\n * ngrok tunnels and local dev environments all produce a claim URL on the same\n * host the connector is already talking to.\n */\nexport function buildClaimUrl(endpoint: string, siteUuid: string): string {\n const origin = new URL(endpoint).origin;\n return `${origin}/monitor/claim?site=${encodeURIComponent(siteUuid)}`;\n}\n\n/**\n * Build the public widget-settings URL for a site. Like the claim URL, it\n * lives on the API endpoint's origin, at `/monitor/widget/settings/<uuid>`.\n */\nexport function buildSettingsUrl(endpoint: string, siteUuid: string): string {\n const origin = new URL(endpoint).origin;\n return `${origin}/monitor/widget/settings/${encodeURIComponent(siteUuid)}`;\n}\n\n/** Build the package-removed signal URL corresponding to a manifest endpoint override. */\nexport function buildPackageRemovedUrl(manifestEndpoint: string, siteUuid: string): string {\n const url = new URL(manifestEndpoint);\n const path = url.pathname.replace(/\\/$/, '');\n url.pathname = path.endsWith('/manifest')\n ? `${path.slice(0, -'/manifest'.length)}/package-removed/${encodeURIComponent(siteUuid)}`\n : `/monitor/pulse/package-removed/${encodeURIComponent(siteUuid)}`;\n url.search = '';\n url.hash = '';\n return url.toString();\n}\n\n/** Build the input-map ingest URL corresponding to a manifest endpoint override. */\nexport function buildInputMapUrl(manifestEndpoint: string, siteUuid: string): string {\n const url = new URL(manifestEndpoint);\n const path = url.pathname.replace(/\\/$/, '');\n url.pathname = path.endsWith('/manifest')\n ? `${path.slice(0, -'/manifest'.length)}/input-map/${encodeURIComponent(siteUuid)}`\n : `/monitor/pulse/input-map/${encodeURIComponent(siteUuid)}`;\n url.search = '';\n url.hash = '';\n return url.toString();\n}\n\n/**\n * Outcome of uploading an attack-surface map. `unchanged` is a first-class result, not a failure: most\n * builds do not change the surface, and the server keeps one revision per distinct surface rather than\n * one per deploy. `skipped` covers the cases where there is nothing to send or nowhere to send it.\n */\nexport type InputMapUploadOutcome =\n | { result: 'stored'; revision: number }\n | { result: 'unchanged'; revision: number }\n | { result: 'skipped'; message: string }\n | { result: 'failed'; message: string };\n\n/**\n * Upload an attack-surface map for this site.\n *\n * Fail-open by construction: every failure path returns a result rather than throwing, because this runs\n * during someone's build and a Patchstack outage must never break it. The caller decides what to print.\n */\nexport async function postInputMap(\n config: Config,\n map: { version: number; endpoints: unknown[] },\n): Promise<InputMapUploadOutcome> {\n if (config.siteUuid === null) {\n return { result: 'skipped', message: 'No site UUID configured — run `patchstack-connect scan` first.' };\n }\n\n const url = buildInputMapUrl(config.endpoint, config.siteUuid);\n try {\n const response = await pulseFetch(config, url, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify(map),\n signal: AbortSignal.timeout(config.timeoutMs),\n });\n if (response.status === 404) {\n return { result: 'failed', message: 'Site not found — the configured site UUID is unknown to Patchstack.' };\n }\n if (response.status === 422) {\n // The server implements a different map schema than this client emits. Say so plainly: it means\n // one side is out of date, and guessing at compatibility is how a consumer misreads a document.\n return { result: 'failed', message: `Patchstack does not accept this map schema (version ${map.version}). Update @patchstack/connect.` };\n }\n if (!response.ok) {\n return { result: 'failed', message: `Patchstack returned ${response.status}.` };\n }\n const body = (await response.json()) as { result?: string; revision?: number };\n // The revision is part of the contract, not decoration: \"stored, revision 0\" is not a state the server\n // can be in, so accepting it would report a successful upload that cannot be pointed at afterwards.\n const revision = Number(body.revision);\n if ((body.result === 'stored' || body.result === 'unchanged') && Number.isInteger(revision) && revision > 0) {\n return { result: body.result, revision };\n }\n return { result: 'failed', message: 'Patchstack returned an unexpected response.' };\n } catch {\n return { result: 'failed', message: `Could not reach Patchstack at ${url}.` };\n }\n}\n\n/**\n * Outcome of the package-removed signal. 'deleted' — the site was unclaimed\n * and its record was removed; 'flagged' — the site is claimed, so it was only\n * marked for its owner to confirm removal in the dashboard; 'gone' — the\n * record no longer existed; 'failed' — Patchstack could not be reached or\n * returned an unexpected response.\n */\nexport interface PackageRemovedOutcome {\n result: 'deleted' | 'flagged' | 'gone' | 'failed';\n message: string | null;\n}\n\n/**\n * Tell Patchstack the @patchstack/connect package is being uninstalled from\n * this project. The site UUID is the only credential, so the server deletes\n * only unclaimed (anonymous) sites; claimed sites are merely flagged for\n * their owner. Never throws — an unreachable server must not block the local\n * uninstall.\n */\nexport async function postPackageRemoved(config: Config): Promise<PackageRemovedOutcome> {\n if (config.siteUuid === null) {\n return { result: 'failed', message: 'No site UUID configured.' };\n }\n\n const url = buildPackageRemovedUrl(config.endpoint, config.siteUuid);\n try {\n const response = await pulseFetch(config, url, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n signal: AbortSignal.timeout(config.timeoutMs),\n });\n if (response.status === 404) {\n return { result: 'gone', message: null };\n }\n if (!response.ok) {\n return { result: 'failed', message: `Patchstack returned ${response.status}.` };\n }\n const body = (await response.json()) as { status?: string; message?: string };\n if (body.status === 'deleted' || body.status === 'flagged') {\n return { result: body.status, message: body.message ?? null };\n }\n return { result: 'failed', message: 'Patchstack returned an unexpected response.' };\n } catch {\n return { result: 'failed', message: `Could not reach Patchstack at ${url}.` };\n }\n}\n\n/**\n * Whether the site record still exists on Patchstack's side. Removing a site\n * (dashboard delete or the widget's uninstall flow) only deletes the remote\n * record — the local integration files stay in the project — so this is the\n * signal that distinguishes \"removed from Patchstack\" from \"still active\".\n */\nexport type SiteStatus = 'active' | 'removed' | 'unknown';\n\n/**\n * Check the remote site status via the public widget-settings endpoint:\n * 200 means the site record exists, 404 means it was removed. Any other\n * response or a network failure is 'unknown' — never throws. The endpoint's\n * 200 responses are cacheable for an hour, so a cache-busting query param\n * keeps intermediaries from reporting a just-removed site as active.\n */\nexport async function fetchSiteStatus(config: Config): Promise<SiteStatus> {\n if (config.siteUuid === null) return 'unknown';\n\n const url = new URL(buildSettingsUrl(config.endpoint, config.siteUuid));\n url.searchParams.set('t', Date.now().toString());\n\n try {\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'Cache-Control': 'no-cache',\n 'User-Agent': '@patchstack/connect',\n },\n signal: AbortSignal.timeout(config.timeoutMs),\n });\n if (response.status === 404) return 'removed';\n if (response.ok) return 'active';\n return 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nexport async function postManifest(\n config: Config,\n payload: WirePayload,\n): Promise<StoreManifestResponse> {\n const url = buildEndpointUrl(config.endpoint, config.siteUuid);\n const timeoutMs = config.timeoutMs;\n\n let response: Response;\n try {\n // Unauthenticated on the bootstrap POST: there is no credential until this\n // request issues one.\n response = await pulseFetch(config, url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify({ ...payload, environment: config.environment }),\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (cause) {\n if (isTimeoutError(cause)) {\n throw new PatchstackError(\n `Patchstack request to ${url} timed out after ${timeoutMs}ms. Override with PATCHSTACK_TIMEOUT_MS.`,\n 'NETWORK_TIMEOUT',\n cause,\n );\n }\n throw new PatchstackError(\n `Could not reach Patchstack at ${url}. Check your network connection.`,\n 'NETWORK_ERROR',\n cause,\n );\n }\n\n const text = await response.text();\n let body: StoreManifestResponse | null = null;\n try {\n body = text.length > 0 ? (JSON.parse(text) as StoreManifestResponse) : null;\n } catch {\n body = null;\n }\n\n if (response.status === 404) {\n throw new PatchstackError(\n body?.error ?? 'Site not found. Check that your site UUID is correct and that the app is registered as a Pulse app in your Patchstack dashboard.',\n 'SITE_NOT_FOUND',\n );\n }\n\n if (response.status === 422) {\n throw new PatchstackError(\n body?.message ?? 'Patchstack rejected the manifest payload (validation failed).',\n 'VALIDATION_ERROR',\n );\n }\n\n if (response.status < 200 || response.status >= 300) {\n throw new PatchstackError(\n `Patchstack returned ${response.status}: ${text.slice(0, 200)}`,\n 'SERVER_ERROR',\n );\n }\n\n if (body === null) {\n throw new PatchstackError('Patchstack returned an empty response.', 'SERVER_ERROR');\n }\n\n return body;\n}\n\nfunction isTimeoutError(cause: unknown): boolean {\n if (cause instanceof Error) {\n return cause.name === 'TimeoutError' || cause.name === 'AbortError';\n }\n return false;\n}\n","import { readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type Config, type Environment } from './types.js';\nimport { DEFAULT_ENDPOINT, DEFAULT_TIMEOUT_MS } from './client.js';\n\nconst CONFIG_FILENAME = '.patchstackrc.json';\n\nexport const DEFAULT_ENVIRONMENT: Environment = 'production';\n\ninterface ConfigFile {\n siteUuid?: string;\n /** WP-format `{secret}-{oauth.id}` for connector /api/logs/log. Server-only. */\n apiKey?: string;\n /**\n * Credential for the authenticated Pulse endpoints (ADR-0018). Same format as\n * `apiKey` and today the same value, but kept as its own field so the Pulse\n * and block-log paths can diverge without disturbing each other. Server-only.\n */\n pulseAuth?: string;\n endpoint?: string;\n timeoutMs?: number;\n environment?: string;\n widget?: boolean;\n}\n\nexport interface ResolveConfigOptions {\n cwd: string;\n cliSiteUuid?: string;\n cliEndpoint?: string;\n /**\n * When true, resolveConfig throws CONFIG_MISSING if no site UUID is configured.\n * Defaults to false: callers that can run without a UUID (the first `scan` after\n * `npm install`) just get `siteUuid: null` back and learn the UUID from the\n * server response.\n */\n requireSiteUuid?: boolean;\n}\n\nexport async function resolveConfig(options: ResolveConfigOptions): Promise<Config> {\n const fromFile = await readConfigFile(options.cwd);\n const fromEnv = readEnv();\n\n const siteUuid =\n options.cliSiteUuid ??\n fromEnv.siteUuid ??\n fromFile.siteUuid ??\n null;\n\n const endpoint =\n options.cliEndpoint ??\n fromEnv.endpoint ??\n fromFile.endpoint ??\n DEFAULT_ENDPOINT;\n\n const timeoutMs = fromEnv.timeoutMs ?? fromFile.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const environmentRaw = fromEnv.environment ?? fromFile.environment;\n if (environmentRaw !== undefined && !isEnvironment(environmentRaw)) {\n throw new PatchstackError(\n `Environment must be \"production\" or \"sandbox\"; got \"${environmentRaw}\".`,\n 'CONFIG_INVALID',\n );\n }\n const environment: Environment = environmentRaw ?? DEFAULT_ENVIRONMENT;\n\n if (siteUuid !== null && siteUuid.length > 0 && !isUuid(siteUuid)) {\n throw new PatchstackError(\n `Site UUID \"${siteUuid}\" does not look like a valid UUID.`,\n 'CONFIG_INVALID',\n );\n }\n\n if (options.requireSiteUuid && (siteUuid === null || siteUuid.length === 0)) {\n throw new PatchstackError(\n 'No site UUID configured. Run `patchstack-connect scan` to provision one, or set PATCHSTACK_SITE_UUID.',\n 'CONFIG_MISSING',\n );\n }\n\n const apiKeyRaw = fromEnv.apiKey ?? fromFile.apiKey ?? null;\n // Falls back to apiKey so sites provisioned before ADR-0018 authenticate\n // without re-provisioning: today both hold the same credential.\n const pulseAuthRaw = fromEnv.pulseAuth ?? fromFile.pulseAuth ?? apiKeyRaw;\n\n return {\n siteUuid: siteUuid === null || siteUuid.length === 0 ? null : siteUuid,\n apiKey: apiKeyRaw === null || apiKeyRaw.length === 0 ? null : apiKeyRaw,\n pulseAuth: pulseAuthRaw === null || pulseAuthRaw.length === 0 ? null : pulseAuthRaw,\n endpoint,\n timeoutMs,\n environment,\n widget: fromFile.widget !== false,\n };\n}\n\nexport async function writeConfigFile(cwd: string, config: ConfigFile): Promise<string> {\n const target = path.join(cwd, CONFIG_FILENAME);\n const content = JSON.stringify(config, null, 2) + '\\n';\n await writeFile(target, content, 'utf8');\n return target;\n}\n\n/**\n * Merge a new siteUuid into the existing `.patchstackrc.json` (or create it).\n * Preserves any `endpoint` / `timeoutMs` / `apiKey` the user already wrote.\n */\nexport async function persistSiteUuid(cwd: string, siteUuid: string): Promise<string> {\n const existing = await readConfigFile(cwd);\n return writeConfigFile(cwd, { ...existing, siteUuid });\n}\n\n/**\n * Persist the WP-format api_key issued at provision. Authenticates both the\n * Pulse endpoints and connector log reporting. Never embed it in the public\n * disclosure widget.\n *\n * Drops any `pulseAuth` written by an earlier version. That field resolves\n * ahead of `apiKey`, so leaving a copy behind after the credential changes\n * would leave Pulse authenticating with the value the server just replaced.\n */\nexport async function persistApiKey(cwd: string, apiKey: string): Promise<string> {\n const { pulseAuth: _dropped, ...existing } = await readConfigFile(cwd);\n return writeConfigFile(cwd, { ...existing, apiKey });\n}\n\n/**\n * Persist a Pulse-specific credential.\n *\n * Only needed when Pulse ingest and block-log reporting must use *different*\n * credentials; they share one today, so `persistApiKey` covers both. Retained\n * for callers that separate them.\n */\nexport async function persistPulseAuth(cwd: string, pulseAuth: string): Promise<string> {\n const existing = await readConfigFile(cwd);\n return writeConfigFile(cwd, { ...existing, pulseAuth });\n}\n\nasync function readConfigFile(cwd: string): Promise<ConfigFile> {\n const target = path.join(cwd, CONFIG_FILENAME);\n let raw: string;\n try {\n raw = await readFile(target, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return {};\n }\n throw new PatchstackError(\n `Could not read ${target}: ${(err as Error).message}`,\n 'CONFIG_INVALID',\n err,\n );\n }\n\n try {\n return JSON.parse(raw) as ConfigFile;\n } catch (err) {\n throw new PatchstackError(\n `Config file ${target} contains invalid JSON.`,\n 'CONFIG_INVALID',\n err,\n );\n }\n}\n\nfunction readEnv(): ConfigFile {\n const timeoutRaw = process.env.PATCHSTACK_TIMEOUT_MS;\n let timeoutMs: number | undefined;\n if (timeoutRaw !== undefined && timeoutRaw.length > 0) {\n const parsed = Number(timeoutRaw);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new PatchstackError(\n `PATCHSTACK_TIMEOUT_MS must be a positive number; got \"${timeoutRaw}\".`,\n 'CONFIG_INVALID',\n );\n }\n timeoutMs = parsed;\n }\n const environmentRaw = process.env.PATCHSTACK_ENVIRONMENT;\n return {\n siteUuid: process.env.PATCHSTACK_SITE_UUID ?? undefined,\n apiKey: process.env.PATCHSTACK_API_KEY ?? undefined,\n pulseAuth: process.env.PATCHSTACK_PULSE_AUTH ?? undefined,\n endpoint: process.env.PATCHSTACK_ENDPOINT ?? undefined,\n timeoutMs,\n environment:\n environmentRaw !== undefined && environmentRaw.length > 0 ? environmentRaw : undefined,\n };\n}\n\nfunction isUuid(value: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);\n}\n\nfunction isEnvironment(value: string): value is Environment {\n return value === 'production' || value === 'sandbox';\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { PackageEntry } from '../types.js';\n\n/**\n * Registry-installable dependency names declared in package.json\n * (dependencies + devDependencies). Non-registry specifiers (file:, link:,\n * workspace:, portal:) are excluded — lockfile parsers skip those too, so\n * their absence from a parsed set says nothing about staleness. Returns []\n * when package.json is missing or unreadable (nothing to validate against).\n */\nexport async function readDeclaredDependencyNames(cwd: string): Promise<string[]> {\n let raw: string;\n try {\n raw = await readFile(path.join(cwd, 'package.json'), 'utf8');\n } catch {\n return [];\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return [];\n }\n if (typeof parsed !== 'object' || parsed === null) {\n return [];\n }\n\n const names = new Set<string>();\n for (const group of ['dependencies', 'devDependencies'] as const) {\n const deps = (parsed as Record<string, unknown>)[group];\n if (typeof deps !== 'object' || deps === null) {\n continue;\n }\n for (const [name, spec] of Object.entries(deps as Record<string, unknown>)) {\n if (typeof spec === 'string' && /^(file|link|workspace|portal):/.test(spec)) {\n continue;\n }\n names.add(name);\n }\n }\n\n return [...names];\n}\n\n/**\n * Declared names absent from a parsed package set. A non-empty result means\n * the source predates package.json — the fossil-lockfile failure mode where\n * e.g. `npm install` planted a package-lock.json once and the platform's\n * native package manager (bun on Lovable) never updates it again.\n */\nexport function missingDependencies(declared: string[], packages: PackageEntry[]): string[] {\n const present = new Set(packages.map((entry) => entry.name));\n return declared.filter((name) => !present.has(name));\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\ninterface LockfileV2Package {\n name?: string;\n version?: string;\n link?: boolean;\n resolved?: string;\n}\n\ninterface LockfileV2 {\n lockfileVersion: number;\n packages?: Record<string, LockfileV2Package>;\n dependencies?: Record<string, LockfileV1Dependency>;\n}\n\ninterface LockfileV1Dependency {\n version: string;\n dependencies?: Record<string, LockfileV1Dependency>;\n}\n\nexport async function parseNpmLockfile(lockfilePath: string): Promise<PackageEntry[]> {\n let raw: string;\n try {\n raw = await readFile(lockfilePath, 'utf8');\n } catch (cause) {\n throw new PatchstackError(`Could not read lockfile at ${lockfilePath}`, 'LOCKFILE_NOT_FOUND', cause);\n }\n\n let parsed: LockfileV2;\n try {\n parsed = JSON.parse(raw) as LockfileV2;\n } catch (cause) {\n throw new PatchstackError(`Lockfile at ${lockfilePath} is not valid JSON`, 'LOCKFILE_PARSE_ERROR', cause);\n }\n\n if (parsed.packages) {\n return extractFromV2(parsed.packages);\n }\n\n if (parsed.dependencies) {\n return extractFromV1(parsed.dependencies);\n }\n\n throw new PatchstackError(\n `Lockfile at ${lockfilePath} has no \"packages\" or \"dependencies\" key`,\n 'LOCKFILE_PARSE_ERROR',\n );\n}\n\nfunction extractFromV2(packages: Record<string, LockfileV2Package>): PackageEntry[] {\n const entries: PackageEntry[] = [];\n\n for (const [pkgPath, pkg] of Object.entries(packages)) {\n if (pkgPath === '') {\n continue;\n }\n if (pkg.link === true) {\n continue;\n }\n if (typeof pkg.version !== 'string' || pkg.version.length === 0) {\n continue;\n }\n\n const name = pkg.name ?? extractNameFromPath(pkgPath);\n if (name === null) {\n continue;\n }\n\n entries.push({\n name,\n version: pkg.version,\n path: pkgPath,\n direct: isDirectV2(pkgPath),\n });\n }\n\n return entries;\n}\n\nfunction extractFromV1(\n deps: Record<string, LockfileV1Dependency>,\n acc: PackageEntry[] = [],\n depth = 0,\n): PackageEntry[] {\n for (const [name, dep] of Object.entries(deps)) {\n if (typeof dep.version === 'string' && dep.version.length > 0) {\n acc.push({ name, version: dep.version, direct: depth === 0 });\n }\n if (dep.dependencies) {\n extractFromV1(dep.dependencies, acc, depth + 1);\n }\n }\n return acc;\n}\n\nfunction extractNameFromPath(pkgPath: string): string | null {\n const segments = pkgPath.split('node_modules' + path.sep === pkgPath ? path.sep : '/');\n const parts = pkgPath.split('/');\n const nmIndex = parts.lastIndexOf('node_modules');\n if (nmIndex === -1 || nmIndex >= parts.length - 1) {\n return segments[segments.length - 1] ?? null;\n }\n const tail = parts.slice(nmIndex + 1);\n if (tail.length === 0) {\n return null;\n }\n const first = tail[0];\n if (first !== undefined && first.startsWith('@') && tail.length >= 2) {\n return `${first}/${tail[1]}`;\n }\n return first ?? null;\n}\n\nfunction isDirectV2(pkgPath: string): boolean {\n const parts = pkgPath.split('/');\n const nmCount = parts.filter((p) => p === 'node_modules').length;\n return nmCount === 1;\n}\n","import { lstat, readFile, readdir, stat } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\nexport async function walkNodeModules(cwd: string): Promise<PackageEntry[]> {\n const root = path.join(cwd, 'node_modules');\n\n try {\n const info = await stat(root);\n if (!info.isDirectory()) {\n throw new PatchstackError(\n `${root} exists but is not a directory.`,\n 'LOCKFILE_NOT_FOUND',\n );\n }\n } catch (cause) {\n if (cause instanceof PatchstackError) {\n throw cause;\n }\n throw new PatchstackError(\n `node_modules/ not found at ${cwd}. Install dependencies first (e.g. \\`bun install\\` or \\`npm install\\`).`,\n 'LOCKFILE_NOT_FOUND',\n cause,\n );\n }\n\n const entries: PackageEntry[] = [];\n await walk(root, entries, 0);\n return entries;\n}\n\nasync function walk(dir: string, acc: PackageEntry[], depth: number): Promise<void> {\n let names: string[];\n try {\n names = await readdir(dir);\n } catch {\n return;\n }\n\n for (const name of names) {\n if (name.startsWith('.')) {\n continue;\n }\n\n const fullPath = path.join(dir, name);\n if (!(await isPlainDirectory(fullPath))) {\n continue;\n }\n\n if (name.startsWith('@')) {\n let subNames: string[];\n try {\n subNames = await readdir(fullPath);\n } catch {\n continue;\n }\n for (const sub of subNames) {\n if (sub.startsWith('.')) {\n continue;\n }\n const scopedDir = path.join(fullPath, sub);\n if (!(await isPlainDirectory(scopedDir))) {\n continue;\n }\n await readPackage(scopedDir, depth, acc);\n await walkNested(scopedDir, acc, depth);\n }\n continue;\n }\n\n await readPackage(fullPath, depth, acc);\n await walkNested(fullPath, acc, depth);\n }\n}\n\nasync function readPackage(\n pkgDir: string,\n depth: number,\n acc: PackageEntry[],\n): Promise<void> {\n let raw: string;\n try {\n raw = await readFile(path.join(pkgDir, 'package.json'), 'utf8');\n } catch {\n return;\n }\n\n let parsed: { name?: unknown; version?: unknown };\n try {\n parsed = JSON.parse(raw) as { name?: unknown; version?: unknown };\n } catch {\n return;\n }\n\n if (typeof parsed.name !== 'string' || parsed.name.length === 0) {\n return;\n }\n if (typeof parsed.version !== 'string' || parsed.version.length === 0) {\n return;\n }\n\n acc.push({\n name: parsed.name,\n version: parsed.version,\n direct: depth === 0,\n });\n}\n\nasync function walkNested(\n pkgDir: string,\n acc: PackageEntry[],\n depth: number,\n): Promise<void> {\n const nested = path.join(pkgDir, 'node_modules');\n if (!(await isPlainDirectory(nested))) {\n return;\n }\n await walk(nested, acc, depth + 1);\n}\n\nasync function isPlainDirectory(dir: string): Promise<boolean> {\n try {\n const info = await lstat(dir);\n return info.isDirectory() && !info.isSymbolicLink();\n } catch {\n return false;\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\n/**\n * Parses pnpm-lock.yaml without pulling in a YAML library. We don't need full\n * YAML semantics — only the `packages:` block (the canonical list of every\n * installed package) and, for direct-dep marking, the dependency sections under\n * `importers:` (v9+) or at the top level (v6-v8 single-project).\n *\n * Supported pnpm-lock key formats:\n * v5: /pkg/1.0.0 /@scope/pkg/1.0.0\n * v6-v8: /pkg@1.0.0 /@scope/pkg@1.0.0 (optional `(peer@x)` suffix)\n * v9: pkg@1.0.0 @scope/pkg@1.0.0 (optional `(peer@x)` suffix, may be quoted)\n */\nexport async function parsePnpmLockfile(lockfilePath: string): Promise<PackageEntry[]> {\n let raw: string;\n try {\n raw = await readFile(lockfilePath, 'utf8');\n } catch (cause) {\n throw new PatchstackError(\n `Could not read lockfile at ${lockfilePath}`,\n 'LOCKFILE_NOT_FOUND',\n cause,\n );\n }\n\n const lines = raw.split(/\\r?\\n/);\n const directNames = collectDirectDepNames(lines);\n const packageKeys = collectPackagesBlockKeys(lines);\n\n if (packageKeys.length === 0) {\n throw new PatchstackError(\n `Lockfile at ${lockfilePath} has no \"packages\" entries`,\n 'LOCKFILE_PARSE_ERROR',\n );\n }\n\n const entries: PackageEntry[] = [];\n for (const key of packageKeys) {\n const parsed = parsePackageKey(key);\n if (parsed === null) {\n continue;\n }\n entries.push({\n name: parsed.name,\n version: parsed.version,\n direct: directNames.has(parsed.name),\n });\n }\n\n return entries;\n}\n\ninterface ParsedKey {\n name: string;\n version: string;\n}\n\nexport function parsePackageKey(rawKey: string): ParsedKey | null {\n let k = rawKey.trim();\n if (k.length === 0) {\n return null;\n }\n\n if (\n (k.startsWith(\"'\") && k.endsWith(\"'\")) ||\n (k.startsWith('\"') && k.endsWith('\"'))\n ) {\n k = k.slice(1, -1);\n }\n\n if (k.startsWith('/')) {\n k = k.slice(1);\n }\n\n // Strip peer-dependency suffix used by v6+: `pkg@1.0.0(peer@1.0.0)(other@2)`.\n const parenIdx = k.indexOf('(');\n if (parenIdx >= 0) {\n k = k.slice(0, parenIdx);\n }\n\n // Split off the optional `@scope/` prefix so the remaining \"body\" contains\n // exactly one separator between the bare name and the version. npm forbids\n // both `@` and `/` inside the bare-name portion, so the first occurrence of\n // either character in the body is unambiguously the name/version separator.\n let scopePrefix = '';\n let body = k;\n if (k.startsWith('@')) {\n const firstSlash = k.indexOf('/');\n if (firstSlash <= 0) {\n return null;\n }\n scopePrefix = k.slice(0, firstSlash + 1);\n body = k.slice(firstSlash + 1);\n }\n\n const slashIdx = body.indexOf('/');\n const atIdx = body.indexOf('@');\n\n let sepIdx: number;\n if (slashIdx < 0 && atIdx < 0) {\n return null;\n } else if (slashIdx < 0) {\n sepIdx = atIdx;\n } else if (atIdx < 0) {\n sepIdx = slashIdx;\n } else {\n sepIdx = Math.min(slashIdx, atIdx);\n }\n\n const name = scopePrefix + body.slice(0, sepIdx);\n let version = body.slice(sepIdx + 1);\n\n // v5 peer suffix uses `_`: `1.0.0_react@18.2.0`.\n const underscoreIdx = version.indexOf('_');\n if (underscoreIdx >= 0) {\n version = version.slice(0, underscoreIdx);\n }\n\n if (name.length === 0 || version.length === 0) {\n return null;\n }\n\n return { name, version };\n}\n\nfunction indentOf(line: string): number {\n let i = 0;\n while (i < line.length && line[i] === ' ') {\n i++;\n }\n return i;\n}\n\nfunction isBlankOrComment(line: string): boolean {\n const trimmed = line.trim();\n return trimmed.length === 0 || trimmed.startsWith('#');\n}\n\n/**\n * Collects keys directly nested under the top-level `packages:` block. Stops\n * when another top-level key (e.g. `snapshots:`) appears.\n */\nfunction collectPackagesBlockKeys(lines: string[]): string[] {\n const keys: string[] = [];\n let inBlock = false;\n let childIndent: number | null = null;\n\n for (const line of lines) {\n if (isBlankOrComment(line)) {\n continue;\n }\n\n const indent = indentOf(line);\n\n if (!inBlock) {\n if (indent === 0 && line.trim() === 'packages:') {\n inBlock = true;\n }\n continue;\n }\n\n if (indent === 0) {\n break;\n }\n\n if (childIndent === null) {\n childIndent = indent;\n }\n\n if (indent !== childIndent) {\n continue;\n }\n\n const content = line.slice(indent);\n if (!content.endsWith(':')) {\n continue;\n }\n\n keys.push(content.slice(0, -1));\n }\n\n return keys;\n}\n\n/**\n * Collects direct dependency names. Looks in two places, since the format\n * differs across lockfile versions and workspaces:\n *\n * 1. `importers:` > `<importer>:` > `{dependencies,devDependencies,optionalDependencies}:`\n * (v9 always, v6-v8 in workspaces)\n * 2. Top-level `{dependencies,devDependencies,optionalDependencies}:`\n * (v6-v8 single-project lockfiles)\n *\n * Direct-dep marking is best-effort: a name absent from both is simply left as\n * not-direct, matching the npm parser's behaviour for transitive packages.\n */\nfunction collectDirectDepNames(lines: string[]): Set<string> {\n const names = new Set<string>();\n collectFromImporters(lines, names);\n for (const section of ['dependencies', 'devDependencies', 'optionalDependencies']) {\n collectFromTopLevelSection(lines, section, names);\n }\n return names;\n}\n\nconst DEP_SECTIONS = new Set([\n 'dependencies',\n 'devDependencies',\n 'optionalDependencies',\n]);\n\nfunction collectFromImporters(lines: string[], out: Set<string>): void {\n let inImporters = false;\n let importerIndent: number | null = null;\n let inDepSection = false;\n let depSectionIndent: number | null = null;\n let leafIndent: number | null = null;\n\n for (const line of lines) {\n if (isBlankOrComment(line)) {\n continue;\n }\n const indent = indentOf(line);\n const trimmed = line.trim();\n\n if (!inImporters) {\n if (indent === 0 && trimmed === 'importers:') {\n inImporters = true;\n }\n continue;\n }\n\n if (indent === 0) {\n break;\n }\n\n // Track the indent at which importer entries (e.g. `.:`, `packages/foo:`) sit.\n if (importerIndent === null) {\n importerIndent = indent;\n }\n\n if (indent === importerIndent) {\n inDepSection = false;\n depSectionIndent = null;\n leafIndent = null;\n continue;\n }\n\n if (!inDepSection) {\n const key = stripTrailingColon(trimmed);\n if (key !== null && DEP_SECTIONS.has(key)) {\n inDepSection = true;\n depSectionIndent = indent;\n }\n continue;\n }\n\n if (depSectionIndent !== null && indent <= depSectionIndent) {\n // Reset — we've exited the dep section. Reconsider this line.\n inDepSection = false;\n depSectionIndent = null;\n leafIndent = null;\n const key = stripTrailingColon(trimmed);\n if (key !== null && DEP_SECTIONS.has(key)) {\n inDepSection = true;\n depSectionIndent = indent;\n }\n continue;\n }\n\n if (leafIndent === null) {\n leafIndent = indent;\n }\n\n if (indent !== leafIndent) {\n continue;\n }\n\n const name = extractLeafName(trimmed);\n if (name !== null) {\n out.add(name);\n }\n }\n}\n\nfunction collectFromTopLevelSection(\n lines: string[],\n section: string,\n out: Set<string>,\n): void {\n let inSection = false;\n let leafIndent: number | null = null;\n\n for (const line of lines) {\n if (isBlankOrComment(line)) {\n continue;\n }\n const indent = indentOf(line);\n const trimmed = line.trim();\n\n if (!inSection) {\n if (indent === 0 && trimmed === `${section}:`) {\n inSection = true;\n }\n continue;\n }\n\n if (indent === 0) {\n break;\n }\n\n if (leafIndent === null) {\n leafIndent = indent;\n }\n\n if (indent !== leafIndent) {\n continue;\n }\n\n const name = extractLeafName(trimmed);\n if (name !== null) {\n out.add(name);\n }\n }\n}\n\nfunction stripTrailingColon(s: string): string | null {\n if (!s.endsWith(':')) {\n return null;\n }\n return s.slice(0, -1).trim();\n}\n\n/**\n * Extracts the package name from a dependency-section leaf, handling both the\n * short form (`pkg: 1.0.0`) and the v9 expanded form (`pkg:` followed by\n * `specifier:` / `version:` children). Quoted scoped names like `'@scope/pkg':`\n * are unquoted.\n */\nfunction extractLeafName(trimmed: string): string | null {\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx < 0) {\n return null;\n }\n let name = trimmed.slice(0, colonIdx).trim();\n if (name.length === 0) {\n return null;\n }\n if (\n (name.startsWith(\"'\") && name.endsWith(\"'\")) ||\n (name.startsWith('\"') && name.endsWith('\"'))\n ) {\n name = name.slice(1, -1);\n }\n return name.length > 0 ? name : null;\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\n/**\n * Parses yarn.lock (yarn classic v1 and yarn berry v2+) without a YAML\n * dependency. Both generations share the same block structure — a top-level\n * mapping of comma-separated descriptor lists to a block containing a\n * `version` field — so we walk them with the same scanner and only branch on\n * the `version` syntax (`version \"x\"` for v1, `version: x` for berry).\n *\n * Direct vs transitive can't be derived from yarn.lock alone (yarn does not\n * record an importer manifest the way pnpm v9 does), so we cross-reference\n * the sibling `package.json` when present.\n */\nexport async function parseYarnLockfile(lockfilePath: string): Promise<PackageEntry[]> {\n let raw: string;\n try {\n raw = await readFile(lockfilePath, 'utf8');\n } catch (cause) {\n throw new PatchstackError(\n `Could not read lockfile at ${lockfilePath}`,\n 'LOCKFILE_NOT_FOUND',\n cause,\n );\n }\n\n const blocks = parseBlocks(raw);\n if (blocks.length === 0) {\n throw new PatchstackError(\n `Lockfile at ${lockfilePath} contains no package entries`,\n 'LOCKFILE_PARSE_ERROR',\n );\n }\n\n const directNames = await readDirectDepNames(path.dirname(lockfilePath));\n\n const entries: PackageEntry[] = [];\n const seen = new Set<string>();\n for (const block of blocks) {\n if (block.version.length === 0 || block.names.size === 0) {\n continue;\n }\n for (const name of block.names) {\n const dedupKey = `${name}@${block.version}`;\n if (seen.has(dedupKey)) {\n continue;\n }\n seen.add(dedupKey);\n entries.push({\n name,\n version: block.version,\n direct: directNames.has(name),\n });\n }\n }\n\n return entries;\n}\n\ninterface Block {\n names: Set<string>;\n version: string;\n}\n\nfunction parseBlocks(raw: string): Block[] {\n const lines = raw.split(/\\r?\\n/);\n const blocks: Block[] = [];\n let current: Block | null = null;\n\n const finalize = () => {\n if (current !== null && current.version.length > 0 && current.names.size > 0) {\n blocks.push(current);\n }\n current = null;\n };\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.length === 0 || trimmed.startsWith('#')) {\n continue;\n }\n\n const indent = countLeadingSpaces(line);\n\n if (indent === 0) {\n finalize();\n if (!trimmed.endsWith(':')) {\n continue;\n }\n // `__metadata:` (yarn berry header) has no `@` in any descriptor and\n // produces an empty names set, so it's naturally skipped on finalize.\n const keyLine = trimmed.slice(0, -1);\n const names = new Set<string>();\n for (const spec of splitDescriptors(keyLine)) {\n const name = extractName(spec);\n if (name !== null) {\n names.add(name);\n }\n }\n current = { names, version: '' };\n continue;\n }\n\n if (current === null) {\n continue;\n }\n\n const version = parseVersionField(trimmed);\n if (version !== null) {\n current.version = version;\n }\n }\n\n finalize();\n return blocks;\n}\n\nfunction countLeadingSpaces(line: string): number {\n let i = 0;\n while (i < line.length && line[i] === ' ') {\n i++;\n }\n return i;\n}\n\n/**\n * Splits a yarn descriptor key list on top-level commas. yarn quotes any\n * descriptor that contains characters needing escaping, so we respect quotes\n * while splitting to avoid breaking on commas inside (rare in practice but\n * cheap to handle).\n */\nexport function splitDescriptors(keyLine: string): string[] {\n const parts: string[] = [];\n let current = '';\n let quote: '\"' | \"'\" | null = null;\n\n for (let i = 0; i < keyLine.length; i++) {\n const c = keyLine[i];\n if (quote !== null) {\n current += c;\n if (c === quote) {\n quote = null;\n }\n continue;\n }\n if (c === '\"' || c === \"'\") {\n quote = c;\n current += c;\n continue;\n }\n if (c === ',') {\n const piece = current.trim();\n if (piece.length > 0) {\n parts.push(piece);\n }\n current = '';\n continue;\n }\n current += c;\n }\n const tail = current.trim();\n if (tail.length > 0) {\n parts.push(tail);\n }\n return parts;\n}\n\n/**\n * Extracts the package name from a yarn descriptor like `axios@^1.6.0`,\n * `\"@scope/pkg@^2.1.0\"`, or `\"@scope/pkg@npm:2.1.0\"`. The descriptor's\n * range portion is discarded — we only need the name, since the resolved\n * version comes from the `version` field of the block.\n */\nexport function extractName(rawSpec: string): string | null {\n let s = rawSpec.trim();\n if (s.length === 0) {\n return null;\n }\n if (\n (s.startsWith('\"') && s.endsWith('\"')) ||\n (s.startsWith(\"'\") && s.endsWith(\"'\"))\n ) {\n s = s.slice(1, -1);\n }\n // Position-0 `@` belongs to a scope, so we want the last `@` after it.\n const atIdx = s.lastIndexOf('@');\n if (atIdx <= 0) {\n return null;\n }\n const name = s.slice(0, atIdx);\n return name.length > 0 ? name : null;\n}\n\nfunction parseVersionField(content: string): string | null {\n if (!content.startsWith('version')) {\n return null;\n }\n const after = content.slice('version'.length);\n // yarn v1: `version \"1.2.3\"` (whitespace then quoted)\n // yarn berry: `version: 1.2.3` or `version: \"1.2.3\"`\n const firstChar = after.charAt(0);\n if (firstChar !== ' ' && firstChar !== '\\t' && firstChar !== ':') {\n return null;\n }\n let rest = firstChar === ':' ? after.slice(1) : after;\n rest = rest.trim();\n if (rest.length === 0) {\n return null;\n }\n if (\n (rest.startsWith('\"') && rest.endsWith('\"')) ||\n (rest.startsWith(\"'\") && rest.endsWith(\"'\"))\n ) {\n rest = rest.slice(1, -1);\n }\n return rest.length > 0 ? rest : null;\n}\n\nasync function readDirectDepNames(cwd: string): Promise<Set<string>> {\n const names = new Set<string>();\n let raw: string;\n try {\n raw = await readFile(path.join(cwd, 'package.json'), 'utf8');\n } catch {\n return names;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return names;\n }\n\n if (typeof parsed !== 'object' || parsed === null) {\n return names;\n }\n const obj = parsed as Record<string, unknown>;\n\n for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {\n const section = obj[field];\n if (typeof section !== 'object' || section === null) {\n continue;\n }\n for (const name of Object.keys(section)) {\n names.add(name);\n }\n }\n\n return names;\n}\n","import { access } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type Manifest, type PackageEntry } from '../types.js';\nimport { missingDependencies, readDeclaredDependencyNames } from './consistency.js';\nimport { parseNpmLockfile } from './npm.js';\nimport { walkNodeModules } from './node_modules.js';\nimport { parsePnpmLockfile } from './pnpm.js';\nimport { parseYarnLockfile } from './yarn.js';\n\ntype LockfileFilename =\n | 'package-lock.json'\n | 'bun.lock'\n | 'bun.lockb'\n | 'yarn.lock'\n | 'pnpm-lock.yaml';\n\ntype DetectionStrategy =\n | 'npm-lockfile'\n | 'node-modules-walk'\n | 'pnpm-lockfile'\n | 'yarn-lockfile';\n\ninterface DetectedLockfile {\n ecosystem: 'npm';\n filePath: string;\n filename: LockfileFilename;\n strategy: DetectionStrategy;\n}\n\ninterface LockfileCandidate {\n filename: LockfileFilename;\n strategy: DetectionStrategy;\n}\n\n// Checked in priority order: the first candidate present in `cwd` wins.\nconst LOCKFILE_CANDIDATES: readonly LockfileCandidate[] = [\n { filename: 'package-lock.json', strategy: 'npm-lockfile' },\n { filename: 'bun.lock', strategy: 'node-modules-walk' },\n { filename: 'bun.lockb', strategy: 'node-modules-walk' },\n { filename: 'pnpm-lock.yaml', strategy: 'pnpm-lockfile' },\n { filename: 'yarn.lock', strategy: 'yarn-lockfile' },\n];\n\nexport async function detectLockfile(cwd: string): Promise<DetectedLockfile> {\n // Probe every candidate concurrently, then resolve by declaration order so\n // the result is identical to a sequential first-match scan — but bounded by a\n // single I/O round-trip instead of up to one `stat` per candidate.\n const probed = await Promise.all(\n LOCKFILE_CANDIDATES.map(async (candidate) => {\n const filePath = path.join(cwd, candidate.filename);\n return { ...candidate, filePath, present: await exists(filePath) };\n }),\n );\n\n const match = probed.find((candidate) => candidate.present);\n if (match) {\n return {\n ecosystem: 'npm',\n filePath: match.filePath,\n filename: match.filename,\n strategy: match.strategy,\n };\n }\n\n const expected = LOCKFILE_CANDIDATES.map((candidate) => candidate.filename).join(', ');\n throw new PatchstackError(\n `No lockfile found in ${cwd}. Expected one of: ${expected}.`,\n 'LOCKFILE_NOT_FOUND',\n );\n}\n\nexport async function scanLockfile(cwd: string): Promise<Manifest> {\n const candidates = await presentLockfiles(cwd);\n if (candidates.length === 0) {\n // Preserve the exact historical error for the no-lockfile case.\n await detectLockfile(cwd);\n }\n\n // Validate each source against package.json before trusting it. A lockfile\n // missing declared dependencies is a fossil — e.g. `npm install` planted a\n // package-lock.json once on a bun-managed platform (Lovable), and the native\n // dependency flow never updates it again. Trusting it would freeze the\n // manifest and the build fingerprint while the real dependency set drifts.\n const declared = await readDeclaredDependencyNames(cwd);\n const warnings: string[] = [];\n let firstParsed: { packages: PackageEntry[]; filename: string } | null = null;\n let walkTried = false;\n\n for (const candidate of candidates) {\n let packages: PackageEntry[];\n try {\n packages = await runStrategy(candidate, cwd);\n } catch {\n continue;\n }\n walkTried ||= candidate.strategy === 'node-modules-walk';\n firstParsed ??= { packages, filename: candidate.filename };\n\n const missing = missingDependencies(declared, packages);\n if (missing.length === 0) {\n return manifestWith(packages, warnings);\n }\n warnings.push(staleWarning(candidate.filename, missing));\n }\n\n // Last resort: the installed truth. node_modules reflects what the build\n // actually compiles against, whichever package manager wrote it.\n if (!walkTried) {\n try {\n const packages = await walkNodeModules(cwd);\n const missing = missingDependencies(declared, packages);\n if (missing.length > 0) {\n warnings.push(staleWarning('node_modules/', missing));\n }\n warnings.push('Scanned node_modules/ instead. Delete the stale lockfile to silence this warning.');\n return manifestWith(packages, warnings);\n } catch {\n // fall through to the best lockfile we managed to parse\n }\n }\n\n if (firstParsed === null) {\n const expected = LOCKFILE_CANDIDATES.map((candidate) => candidate.filename).join(', ');\n throw new PatchstackError(\n `No readable lockfile found in ${cwd}. Expected one of: ${expected}.`,\n 'LOCKFILE_NOT_FOUND',\n );\n }\n\n warnings.push(\n `No fully-consistent source found; reporting ${firstParsed.filename}. The manifest may understate the real dependency set.`,\n );\n\n return manifestWith(firstParsed.packages, warnings);\n}\n\nfunction manifestWith(packages: PackageEntry[], warnings: string[]): Manifest {\n return warnings.length > 0\n ? { ecosystem: 'npm', packages, warnings }\n : { ecosystem: 'npm', packages };\n}\n\nfunction staleWarning(source: string, missing: string[]): string {\n const sample = missing.slice(0, 3).join(', ');\n const suffix = missing.length > 3 ? `, +${missing.length - 3} more` : '';\n return `${source} looks stale: package.json declares ${missing.length} dependenc${missing.length === 1 ? 'y' : 'ies'} it does not contain (${sample}${suffix}).`;\n}\n\nasync function presentLockfiles(cwd: string): Promise<DetectedLockfile[]> {\n const probed = await Promise.all(\n LOCKFILE_CANDIDATES.map(async (candidate) => {\n const filePath = path.join(cwd, candidate.filename);\n return { ...candidate, filePath, present: await exists(filePath) };\n }),\n );\n\n return probed\n .filter((candidate) => candidate.present)\n .map(({ filename, strategy, filePath }) => ({ ecosystem: 'npm' as const, filename, strategy, filePath }));\n}\n\nasync function runStrategy(\n detected: DetectedLockfile,\n cwd: string,\n): Promise<PackageEntry[]> {\n switch (detected.strategy) {\n case 'npm-lockfile':\n return parseNpmLockfile(detected.filePath);\n case 'pnpm-lockfile':\n return parsePnpmLockfile(detected.filePath);\n case 'yarn-lockfile':\n return parseYarnLockfile(detected.filePath);\n case 'node-modules-walk':\n return walkNodeModules(cwd);\n }\n}\n\nasync function exists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n","import type { Manifest, PackageEntry } from './types.js';\n\nexport interface WirePackage {\n name: string;\n version: string;\n}\n\nexport interface WirePayload {\n ecosystem: Manifest['ecosystem'];\n packages: WirePackage[];\n}\n\nexport interface NormalizeStats {\n uniqueNames: number;\n duplicateNames: string[];\n totalEntries: number;\n}\n\nexport interface NormalizeResult {\n payload: WirePayload;\n stats: NormalizeStats;\n}\n\nexport function buildWirePayload(manifest: Manifest): NormalizeResult {\n const seen = new Map<string, Set<string>>();\n const wirePackages: WirePackage[] = [];\n\n for (const entry of manifest.packages) {\n const versions = seen.get(entry.name);\n if (versions) {\n if (versions.has(entry.version)) {\n continue;\n }\n versions.add(entry.version);\n } else {\n seen.set(entry.name, new Set([entry.version]));\n }\n wirePackages.push({ name: entry.name, version: entry.version });\n }\n\n wirePackages.sort((a, b) => {\n if (a.name === b.name) {\n return compareVersions(a.version, b.version);\n }\n return a.name < b.name ? -1 : 1;\n });\n\n const duplicateNames: string[] = [];\n for (const [name, versions] of seen) {\n if (versions.size > 1) {\n duplicateNames.push(name);\n }\n }\n\n return {\n payload: { ecosystem: manifest.ecosystem, packages: wirePackages },\n stats: {\n uniqueNames: seen.size,\n duplicateNames,\n totalEntries: manifest.packages.length,\n },\n };\n}\n\nexport function compareVersions(a: string, b: string): number {\n if (a === b) {\n return 0;\n }\n\n const [aBase, aPre] = splitPrerelease(a);\n const [bBase, bPre] = splitPrerelease(b);\n\n const baseCmp = compareSegments(aBase.split('.'), bBase.split('.'));\n if (baseCmp !== 0) {\n return baseCmp;\n }\n\n if (aPre === null && bPre === null) {\n return 0;\n }\n if (aPre === null) {\n return 1;\n }\n if (bPre === null) {\n return -1;\n }\n return compareSegments(aPre.split('.'), bPre.split('.'));\n}\n\nfunction splitPrerelease(version: string): [string, string | null] {\n const cleaned = version.replace(/^[v=]+/, '').split('+')[0]!;\n const dashIndex = cleaned.indexOf('-');\n if (dashIndex === -1) {\n return [cleaned, null];\n }\n return [cleaned.slice(0, dashIndex), cleaned.slice(dashIndex + 1)];\n}\n\nfunction compareSegments(a: string[], b: string[]): number {\n const max = Math.max(a.length, b.length);\n for (let i = 0; i < max; i++) {\n const aPart = a[i];\n const bPart = b[i];\n if (aPart === undefined) {\n return -1;\n }\n if (bPart === undefined) {\n return 1;\n }\n const aNum = /^\\d+$/.test(aPart);\n const bNum = /^\\d+$/.test(bPart);\n if (aNum && bNum) {\n const diff = Number(aPart) - Number(bPart);\n if (diff !== 0) {\n return diff < 0 ? -1 : 1;\n }\n continue;\n }\n if (aNum) {\n return -1;\n }\n if (bNum) {\n return 1;\n }\n if (aPart < bPart) {\n return -1;\n }\n if (aPart > bPart) {\n return 1;\n }\n }\n return 0;\n}\n\nexport function findPackageInManifest(\n manifest: Manifest,\n name: string,\n): PackageEntry[] {\n return manifest.packages.filter((p) => p.name === name);\n}\n","// Re-post the project's dependency manifest to Pulse — the runtime counterpart to the CLI's\n// `scan`. The sandbox refresh loop (see runtime.js) calls this each tick so a dependency added\n// after boot is reported without a restart: a targeted `npm install <pkg>` fires no npm lifecycle\n// hook, so nothing else re-scans. Reuses the CLI scan pipeline; best-effort (the caller catches).\nimport { resolveConfig } from '../config.js';\nimport { scanLockfile } from '../parsers/index.js';\nimport { buildWirePayload } from '../normalize.js';\nimport { postManifest } from '../client.js';\n\n/** Scan the lockfile under `cwd` and post the manifest to Pulse. No-op without a provisioned site. */\nexport async function reportManifest(cwd: string): Promise<void> {\n const config = await resolveConfig({ cwd });\n if (config.siteUuid === null || config.siteUuid.length === 0) {\n return;\n }\n const manifest = await scanLockfile(cwd);\n const { payload } = buildWirePayload(manifest);\n await postManifest(config, payload);\n}\n","// @patchstack/protect — \"Protect = respond\".\n//\n// One entry point that composes the node-waf engine + adapters with:\n// - a rule source: an explicit bundle, or fetched from the Patchstack API (token),\n// with a disk cache so the engine keeps working on last-known-good if the API is down\n// - execution modes: 'dry-run' (detect + log, never block — the safe onramp) and 'block' (enforce).\n// This API's default is 'dry-run'. NOTE the scaffolded guard (`patchstack-connect protect`)\n// deliberately passes mode: 'block' and only drops to dry-run when PATCHSTACK_MODE=dry-run — so an\n// installed guard ENFORCES by default even though this constructor's default doesn't. Precedence:\n// PATCHSTACK_MODE env > API `enforcement` > options.mode > dry-run.\n// - fail-open everywhere: a rule/engine error never blocks (or crashes) a request. Where the guard\n// fails open *without* inspecting (body caps, live streams, binary bodies, resolver failures) it\n// is counted and reported — see `protection.coverage()` / the `onSkip` option.\n//\n// Runtime guards: .express(), .node(), .fetch(handler) / .fetchGuard() — same policy,\n// every runtime an AI builder deploys to.\n// Vendored node-waf engine (this package is self-contained — no @patchstack/node-waf dep).\nimport { RuleEngine } from './engine/index.js';\nimport { matchValue, walkLeaves, safeRegExp } from './engine/engine.js';\nimport { PulseRuleClient } from './engine/pulse-client.js';\nimport { fromFetchRequest } from './engine/fetch.js';\nimport { fromNodeRequest } from './engine/node.js';\nimport { installEgressGuard } from './egress.js';\nimport { DEFAULT_RESPONSE_RULES, DEFAULT_EGRESS_RULES } from './defaults.js';\nimport { renderBlockPage } from './block-page.js';\n// Rule lifecycle (source / tiered store / refresh) lives in ./rules/ — this file stays focused on\n// composing the engine + guards and running the three screening phases.\nimport { makeStore } from './rules/store.js';\nimport { resolveRules } from './rules/source.js';\nimport { startRefresh, makeRefreshHandler } from './rules/refresh.js';\nimport { createFirewallLogReporter, resolveApiBase, telemetryEnabled } from './firewall-log.js';\n\n// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\nexport { createSupabaseGuard, GUARD_PATH } from './supabase-guard.js';\n\n// Per-site live rule client (Pulse). Re-exported for callers/tests that want to use it directly.\nexport { PulseRuleClient };\n\n// Server-function guard. Modern Lovable apps mutate data through TanStack server functions\n// (browser → server fn → server-side Supabase client), which bypass the browser-side tunnel the\n// Supabase guard relies on. This inspects the decoded server-fn call args against the SAME policy\n// by feeding them through fetchGuard (the args become the request body, so the engine resolves\n// `post.<field>` exactly as it does for a tunneled Supabase insert). Returns a block receipt\n// { rule?, message } to throw on, or null to allow (also null in dry-run — the detection is still\n// recorded by fetchGuard). Fail-open on any error.\nexport function createServerFnGuard({ protection }) {\n const guard = protection.fetchGuard();\n return async (data) => {\n let res;\n try {\n const req = new Request('https://patchstack.local/_serverfn', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(data ?? {}),\n });\n res = await guard(req);\n } catch {\n return null; // fail open\n }\n if (!res) return null; // allowed (or dry-run)\n let body = {};\n try {\n body = await res.clone().json();\n } catch {\n /* non-JSON block response */\n }\n return { rule: body.rule, message: body.message || 'Blocked by Patchstack' };\n };\n}\n\nexport async function createProtection(options = {}) {\n const onError = options.onError;\n const userOnDetect = options.onDetect ?? defaultOnDetect;\n\n // Report enforced blocks via existing connector POST /api/logs/log (WP path).\n // Needs api_key from provision / PATCHSTACK_API_KEY / .patchstackrc.json.\n // Opt out: PATCHSTACK_TELEMETRY=off. Never embed api_key in the public widget.\n const apiKey = await resolveApiKey(options);\n const firewallLog =\n apiKey && telemetryEnabled() && options.reportFirewallLog !== false\n ? createFirewallLogReporter({\n apiKey,\n apiBase: resolveApiBase(options.pulseRulesUrl ?? options.baseUrl),\n sourceHost: options.sourceHost,\n fetchImpl: options.fetchImpl,\n })\n : null;\n\n const onDetect = (detection) => {\n userOnDetect(detection);\n if (firewallLog && detection?.mode === 'block') {\n firewallLog.record({\n rule: detection.rule,\n method: detection.method,\n path: detection.path,\n ip: detection.ip,\n userAgent: detection.userAgent,\n });\n }\n };\n\n // One tiered store (memory → filesystem/pluggable) shared by the initial load and every refresh.\n const store = makeStore(options);\n // Startup must not hang on the network: hosted platforms (Replit et al.) fail a deploy whose health\n // check is slow, and the guard can always boot from last-known-good / the bundled fallback. Refreshes\n // keep the full budget. Override with { bootTimeoutMs }.\n const bootTimeoutMs = Number(options.bootTimeoutMs) > 0 ? Number(options.bootTimeoutMs) : 5_000;\n // Resolved once and threaded through ctx: reading it is a filesystem hit on\n // runtimes that have one, and refreshes should not repeat it.\n const pulseAuth = await resolvePulseAuth(options);\n const bundle = await resolveRules(options, store, { timeoutMs: bootTimeoutMs, pulseAuth });\n // Mode is mutable so a Pulse refresh can flip dry-run ↔ block when SaaS enables production.\n // Precedence: PATCHSTACK_MODE env (local override) > API enforcement > options.mode > dry-run.\n let mode = resolveMode(options, bundle);\n // Rule-derived runtime state. Held in `let` bindings the guard methods below close over, so a\n // refresh (see the loop near the end) can hot-swap the engines by reassigning them — the\n // egress interception and the protection object itself stay in place, no re-install.\n let requestRules;\n let responseRules;\n let egressRules;\n let screenCap; // max body we'll buffer/screen (rules can raise it)\n let engine;\n let responseRuleSet;\n let egressEngine;\n\n // Split the delivered ruleset by phase (default \"request\"), merging phase defaults +\n // per-call overrides. Detection is fully rule-driven — nothing hardcoded.\n const applyBundle = (delivered) => {\n const incoming = delivered.firewall ?? [];\n requestRules = byPhase(incoming, 'request');\n responseRules = [...(options.responseRules ?? DEFAULT_RESPONSE_RULES), ...byPhase(incoming, 'response')];\n screenCap = responseScreenCap(responseRules);\n egressRules = [...(options.egressRules ?? DEFAULT_EGRESS_RULES), ...byPhase(incoming, 'egress')];\n engine = new RuleEngine({\n firewall: requestRules,\n whitelists: delivered.whitelists,\n whitelist_keys: delivered.whitelist_keys,\n onError,\n });\n // One engine per response rule so we can find ALL matches (to redact each). `action:\n // \"redact\"` masks the offending span(s); anything else withholds the whole response.\n responseRuleSet = responseRules.map((rule) => ({\n rule,\n engine: new RuleEngine({ firewall: [rule], onError }),\n redactors: rule.action === 'redact' || rule.action === 'encode' ? extractRedactors(rule) : null,\n // A redact/encode condition that carries body-transforming mutations (base64_decode, urldecode,\n // json_decode, …) detects on the DECODED body but the span redactors run on the RAW body — so\n // they mask nothing and the secret is served while the log says \"redacted\". Flag it so screenText\n // fails such a rule CLOSED (block) instead of serving a no-op redaction.\n mutatedSpan: (rule.action === 'redact' || rule.action === 'encode') && hasSpanMutations(rule),\n // Optional cheap pre-filter: literal anchor(s) that MUST appear for the (expensive) regex to\n // have any chance of matching. Lets screenText skip the full scan on bodies with no candidate —\n // the common case — cutting CPU/latency and shrinking the regex/ReDoS surface. Case-insensitive.\n prefilter: Array.isArray(rule.prefilter) && rule.prefilter.length\n ? rule.prefilter.map((s) => String(s).toLowerCase())\n : null,\n }));\n egressEngine = new RuleEngine({ firewall: egressRules, onError });\n };\n\n applyBundle(bundle);\n\n // Fail-open COVERAGE. The guard deliberately passes traffic through rather than risk breaking the\n // app: an oversized request body, a response past the screening cap, a live stream, a binary body, a\n // parse failure, a DNS resolver failure. Each of those is a real hole in enforcement, and until now\n // it was SILENT — \"always-on\" read as \"always inspected\". Every such bypass is now counted and\n // reported to `onSkip`, so a host can alert on it and `protection.coverage()` can be surfaced.\n // `onSkip` is a TRUSTED SERVER callback: `detail` carries operational context (sizes, statuses,\n // outbound hostnames) for logging/alerting. Do not forward it to a client response.\n const skipCounts = Object.create(null);\n const onSkip = typeof options.onSkip === 'function' ? options.onSkip : null;\n const recordSkip = (phase, reason, detail) => {\n const key = `${phase}:${reason}`;\n skipCounts[key] = (skipCounts[key] ?? 0) + 1;\n if (onSkip) {\n try {\n onSkip({ phase, reason, detail, count: skipCounts[key] });\n } catch {\n /* a reporting callback must never affect request handling */\n }\n }\n };\n\n const maskFn =\n typeof options.maskWith === 'function'\n ? options.maskWith\n : () => (typeof options.maskWith === 'string' ? options.maskWith : '[REDACTED]');\n\n // Given a request/egress result, enforce (block mode) or just record (dry-run).\n //\n // A rule may carry its own `enforcement: 'dry-run'`, which wins over block mode for that rule alone.\n // Auto-generated rules arrive that way: their coordinate comes from best-effort static analysis, so they\n // are served to detect until a probe or a human has justified them, WITHOUT holding back the\n // hand-authored rules on the same site. A rule with no `enforcement` follows the bundle exactly as\n // before, so an older server that never sends the field behaves identically.\n const ruleMode = (rule) => (rule?.enforcement === 'dry-run' ? 'dry-run' : mode);\n\n const decide = (phase, result, block, allow, ctx = {}) => {\n if (!result || !result.blocked) return allow();\n const effectiveMode = ruleMode(result.rule);\n onDetect({\n phase,\n // The mode this detection was actually handled under, not the site's: a consumer counting blocks\n // would otherwise over-report, and the whole point of a dry-run rule is that it did not block.\n mode: effectiveMode,\n category: result.rule?.category,\n rule: result.rule,\n message: result.message,\n method: ctx.method,\n path: ctx.path,\n ip: ctx.ip,\n userAgent: ctx.userAgent,\n });\n return effectiveMode === 'block' ? block() : allow();\n };\n\n // Response phase core: screen a text body → { verdict: 'pass'|'block'|'redact', body? }.\n // redact masks matched spans; block withholds; block wins over redact. Enforcement only in\n // block mode (dry-run records via onDetect but returns 'pass').\n const screenText = (text, meta, reqCtx) => {\n let blockRule = null;\n const redactions = [];\n const headerMutations = [];\n let lowerText = null; // lazily lowercased body, only if a rule uses a prefilter\n for (const { rule, engine: re, redactors, prefilter, mutatedSpan } of responseRuleSet) {\n // Cheap pre-filter: if none of the rule's literal anchors is in the body, its regex can't\n // match — skip the full scan (the common no-secret case) before touching the engine.\n if (prefilter) {\n if (lowerText === null) lowerText = text.toLowerCase();\n if (!prefilter.some((p) => lowerText.includes(p))) continue;\n }\n let result;\n try {\n // Spread the originating request (method / originalUrl / headers) alongside the response,\n // so a response rule's `when` route/method scope resolves against the REAL request and so\n // request Host/Origin are visible to response rules — rather than the phantom empty request\n // the response phase used to build (which made `when` on a response rule inert).\n result = re.evaluate({ ...(reqCtx || {}), _response: { ...meta, body: text } });\n } catch (err) {\n onError?.(err);\n continue;\n }\n if (!result.blocked) continue;\n // Per-rule enforcement applies to every phase, not just the request. A generated response rule in\n // dry-run must not redact or withhold a body either: \"detect until justified\" is meaningless if the\n // rule still rewrites what the user sees.\n const responseMode = ruleMode(rule);\n onDetect({ phase: 'response', mode: responseMode, category: rule.category, rule, message: result.message });\n if (responseMode !== 'block') continue; // dry-run: observe only\n if (redactors && redactors.length) {\n // Span redactors on a mutation-decoded rule can't map back to the raw body → fail closed.\n const spanRedactors = redactors.filter((r) => !r.jsonPath);\n if (mutatedSpan && spanRedactors.length) {\n if (!blockRule) blockRule = rule;\n } else {\n redactions.push({ rule, redactors });\n }\n } else if (isHeaderMutation(rule.action)) headerMutations.push(rule);\n else if (!blockRule) blockRule = rule;\n }\n if (mode !== 'block' || (!blockRule && !redactions.length && !headerMutations.length)) return { verdict: 'pass' };\n if (blockRule) return { verdict: 'block' };\n let body = text;\n // Redact the offending spans in the body AND in every (string) header value — so a secret\n // that leaks in a header (Set-Cookie, an echoed X-Api-Key, …) is masked too, and a rule that\n // targets `response.header.*` actually strips the header rather than just detecting it.\n const headers = { ...(meta.headers || {}) };\n for (const { rule, redactors } of redactions) {\n const mask = maskFn(rule.category);\n // action `encode` HTML-escapes the matched value in place (neutralize stored XSS at output);\n // `redact` masks it. jsonPath redactors act on a structural JSON location, span redactors on\n // text spans in the body AND header values. Apply structural first (on clean JSON), then spans.\n const transform = rule.action === 'encode' ? htmlEscape : null;\n const pathRedactors = redactors.filter((r) => r.jsonPath);\n const spanRedactors = redactors.filter((r) => !r.jsonPath);\n if (pathRedactors.length) body = applyPathRedactors(body, pathRedactors, mask, screenCap, transform);\n if (!spanRedactors.length) continue;\n body = applyRedactors(body, spanRedactors, mask, transform);\n if (transform) continue; // encoding is a body/output concern — headers aren't HTML\n for (const name of Object.keys(headers)) {\n const value = headers[name];\n if (typeof value === 'string') {\n headers[name] = applyRedactors(value, spanRedactors, mask);\n } else if (Array.isArray(value)) {\n // Multi-valued headers (Set-Cookie) — redact each entry.\n headers[name] = value.map((item) => (typeof item === 'string' ? applyRedactors(item, spanRedactors, mask) : item));\n }\n }\n }\n for (const rule of headerMutations) applyHeaderMutation(headers, rule);\n return { verdict: 'redact', body, headers };\n };\n\n // Minimal request context for the response phase: what a response rule's `when` scope and any\n // request-header reference (Host/Origin) need — method, path, and request headers. No body.\n const reqContextFromFetch = (request) => {\n try {\n const u = new URL(request.url);\n const headers = headerObject(request.headers);\n // A fetch Request doesn't expose the Host header (it's set at send time), so derive it from the\n // URL — response rules that compare origins (open-redirect / CORS) need the request Host.\n if (!headers.host) headers.host = u.host;\n return { method: request.method, originalUrl: u.pathname + u.search, headers };\n } catch {\n return undefined;\n }\n };\n const reqContextFromNode = (req) => (req ? { method: req.method, originalUrl: req.url, headers: req.headers || {} } : undefined);\n\n // Screen a fetch Response (used by .fetch() and — via protection.screenResponse — the Supabase guard).\n const screenResp = async (response, reqCtx) => {\n const read = await readTextResponse(response, screenCap);\n if (read.skip) {\n // Nothing was screened — a leak/PII rule cannot have applied. Record it (a live stream and a\n // binary body are by design; a body-cap or read failure is a coverage hole worth alerting on).\n if (read.skip !== 'not-a-response') recordSkip('response', read.skip, { status: response?.status });\n return response;\n }\n const text = read.text;\n const r = screenText(text, { status: response.status, headers: headerObject(response.headers) }, reqCtx);\n if (r.verdict === 'block') return leakResponse();\n if (r.verdict === 'redact') return rebuildResponse(response, r.body, r.headers);\n return response;\n };\n\n // Wrap a Node ServerResponse so its (buffered, text) body is screened before it's sent.\n // Opt-in (buffering can delay a streamed response); over 512 KiB it stops buffering and\n // passes through unscanned.\n const wrapNodeResponse = (res, reqCtx) => {\n const origWrite = res.write.bind(res);\n const origEnd = res.end.bind(res);\n const chunks = [];\n let size = 0;\n let overflow = false;\n const MAX = screenCap;\n const collect = (chunk, enc) => {\n if (chunk == null) return;\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, typeof enc === 'string' ? enc : 'utf8');\n size += buf.length;\n if (size > MAX) {\n // Too big to screen — abandon buffering, but FLUSH what we already captured (the head) plus\n // this chunk before switching to pass-through, so the client gets a complete body (not a\n // truncated one missing everything before the cap was hit).\n for (const c of chunks) origWrite(c);\n chunks.length = 0;\n origWrite(buf);\n overflow = true;\n recordSkip('response', 'body-cap', { bytes: size });\n return;\n }\n chunks.push(buf);\n };\n res.write = function (chunk, enc, cb) {\n if (overflow) return origWrite(chunk, enc, cb);\n collect(chunk, enc);\n if (typeof enc === 'function') enc();\n else if (typeof cb === 'function') cb();\n return true;\n };\n res.end = function (chunk, enc, cb) {\n if (typeof chunk === 'function') { cb = chunk; chunk = undefined; enc = undefined; }\n else if (typeof enc === 'function') { cb = enc; enc = undefined; }\n if (overflow) { if (chunk != null) origWrite(chunk, enc); return origEnd(cb); }\n collect(chunk, enc);\n if (overflow) return origEnd(cb); // collect just flushed head + final chunk on overflow\n const buffer = Buffer.concat(chunks);\n let ct = res.getHeader ? res.getHeader('content-type') : undefined;\n if (Array.isArray(ct)) ct = ct[0];\n const kind = screenableContentType(ct);\n // Skip live streams / binary bodies (incl. an octet-stream that sniffs as binary) — untouched.\n if (kind === 'skip' || (kind === 'sniff' && looksBinary(buffer))) {\n recordSkip('response', kind === 'skip' ? (baseContentType(ct) === 'text/event-stream' ? 'live-stream' : 'non-text-content-type') : 'binary-body');\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n }\n const text = buffer.toString('utf8');\n let r;\n try {\n r = screenText(text, { status: res.statusCode, headers: res.getHeaders ? res.getHeaders() : {} }, reqCtx);\n } catch (err) {\n onError?.(err);\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n }\n if (r.verdict === 'block') {\n res.statusCode = 500;\n try { res.setHeader('content-type', 'application/json'); } catch { /* headers sent */ }\n return origEnd(JSON.stringify({ error: 'Response withheld by Patchstack (sensitive data detected)' }), cb);\n }\n if (r.verdict === 'redact') {\n try { res.removeHeader && res.removeHeader('content-length'); } catch { /* ignore */ }\n if (r.headers && res.setHeader) {\n const current = res.getHeaders ? res.getHeaders() : {};\n for (const [name, value] of Object.entries(r.headers)) {\n // content-length was just removed (the redacted body has a new length); never re-set a\n // stale one here or the response truncates/hangs.\n if (name.toLowerCase() === 'content-length') continue;\n if (value === null || value === undefined) {\n try { res.removeHeader && res.removeHeader(name); } catch { /* ignore */ } // header-mutation removal\n } else if (Array.isArray(value)) {\n try { res.setHeader(name, value); } catch { /* ignore invalid header */ } // Set-Cookie array\n } else if (typeof value === 'string' && current[name] !== value) {\n try { res.setHeader(name, value); } catch { /* ignore invalid header */ }\n }\n }\n }\n return origEnd(r.body, cb);\n }\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n };\n };\n\n // Egress phase: is this outbound call blocked? (records detection either way)\n const allow = new Set((options.allowHosts ?? []).map((h) => String(h).toLowerCase()));\n const egressShouldBlock = (url, host, method) => {\n if (host && allow.has(host.toLowerCase())) return false;\n let result;\n try {\n result = egressEngine.evaluate({ _egress: { url, host, method } });\n } catch (err) {\n onError?.(err);\n return false;\n }\n if (!result.blocked) return false;\n // Same for egress: a dry-run rule records the outbound attempt without preventing it. Blocking a\n // request the app makes is at least as disruptive as blocking one it receives.\n const egressMode = ruleMode(result.rule);\n onDetect({ phase: 'egress', mode: egressMode, category: result.rule?.category, rule: result.rule, message: result.message });\n return egressMode === 'block';\n };\n\n const protection = {\n get mode() {\n return mode;\n },\n get rules() {\n return { request: requestRules, response: responseRules, egress: egressRules };\n },\n\n /**\n * Enforcement coverage: how often the guard FAILED OPEN rather than inspecting, keyed\n * `<phase>:<reason>` (e.g. `response:body-cap`, `request:body-cap`, `response:live-stream`,\n * `egress:resolver-failed`). \"Always-on\" is not \"always inspected\" — surface this (or pass\n * `onSkip`) so an unscreened path is visible and alertable rather than silent.\n */\n coverage() {\n return { skipped: { ...skipCounts } };\n },\n\n // Screen a fetch Response through the response-phase rules (redact/block). Used by\n // .fetch(), and by the Supabase guard on its forwarded upstream response.\n screenResponse: (response, request) => screenResp(response, request ? reqContextFromFetch(request) : undefined),\n\n // (request) => Response | null (null = allow, caller proceeds). Request phase only.\n fetchGuard() {\n return async (request) => {\n let result;\n try {\n result = engine.evaluate(await fromFetchRequest(request));\n } catch (err) {\n onError?.(err);\n return null; // fail open\n }\n return decide('request', result, () => blockResponse(result, request), () => null, fetchRequestMeta(request));\n };\n },\n\n // Wrap a fetch handler: screens the request, then the response (redact/block).\n fetch(handler) {\n const guard = protection.fetchGuard();\n return async (request, ...rest) => {\n const blocked = await guard(request);\n if (blocked) return blocked;\n const response = await handler(request, ...rest);\n return screenResp(response, reqContextFromFetch(request));\n };\n },\n\n // Express middleware (request phase; expects express-parsed req.query/req.body).\n // Pass { screenResponses: true } to also screen the outgoing response (buffers it).\n express(exprOptions = {}) {\n return (req, res, next) => {\n let result;\n try {\n result = engine.evaluate(req);\n } catch (err) {\n onError?.(err);\n if (exprOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n return next();\n }\n decide(\n 'request',\n result,\n () => {\n if (isDocumentNavigation((n) => req.headers?.[n])) {\n res.status(403).type('html').send(renderBlockPage({ url: req.originalUrl || req.url || '/', code: result?.rule?.id }));\n } else {\n res.status(403).json(blockBody(result));\n }\n },\n () => {\n if (exprOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n next();\n },\n nodeRequestMeta(req),\n );\n };\n },\n\n // Node / Connect middleware — buffers the body itself (request phase).\n // Pass { screenResponses: true } to also screen the outgoing response (buffers it).\n node(nodeOptions = {}) {\n const maxBytes = nodeOptions.maxBodyBytes ?? 1024 * 1024;\n return (req, res, next) => {\n const chunks = [];\n let size = 0;\n let overflow = false;\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n req.on('error', (err) => {\n onError?.(err);\n next();\n });\n req.on('end', () => {\n if (overflow) recordSkip('request', 'body-cap', { bytes: size, limit: maxBytes });\n const rawBody = overflow ? '' : Buffer.concat(chunks).toString('utf8');\n let shaped;\n let result;\n try {\n shaped = fromNodeRequest(req, rawBody);\n result = engine.evaluate(shaped);\n } catch (err) {\n onError?.(err);\n return next();\n }\n decide(\n 'request',\n result,\n () => {\n res.statusCode = 403;\n if (isDocumentNavigation((n) => req.headers?.[n])) {\n res.setHeader('content-type', 'text/html; charset=utf-8');\n res.end(renderBlockPage({ url: req.url || '/', code: result?.rule?.id }));\n } else {\n res.setHeader('content-type', 'application/json');\n res.end(JSON.stringify(blockBody(result)));\n }\n },\n () => {\n // This guard consumed the request stream to screen it; re-expose the parsed\n // body so a downstream handler (without its own body-parser) can read it.\n if (req.body === undefined) req.body = shaped.body;\n if (nodeOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n next();\n },\n nodeRequestMeta(req),\n );\n });\n };\n },\n };\n\n // Egress interception is opt-in (it wraps the global fetch, and node:http/https on Node).\n if (options.egress) {\n protection.uninstallEgress = await installEgressGuard({\n shouldBlock: egressShouldBlock,\n onBlock: options.onEgressBlock,\n // Route egress coverage gaps (a DNS resolver failure / no resolver on this runtime) into the\n // same skip accounting as the request/response phases.\n onSkip: ({ reason, detail }) => recordSkip('egress', reason, detail),\n dnsScreen: options.screenDns !== false,\n allowHosts: options.allowHosts,\n });\n }\n\n // Live rule refresh. The guard otherwise reads its rules once, at process start — so a rule that\n // only becomes relevant after boot (a dependency added mid-session and flagged by Pulse, a\n // zero-day published) never applies until the process restarts. A refresh re-fetches and\n // hot-swaps the engines in place; the same tick can be driven by a poll loop (`refreshMs`), a\n // manual `protection.refresh()`, or an authenticated push (`protection.refreshHandler()`).\n const cwd = options.cwd ?? (typeof process !== 'undefined' ? process.cwd() : undefined);\n const live = Boolean(options.siteUuid || options.token);\n const refreshSecret = options.refreshSecret ?? (typeof process !== 'undefined' ? process.env.PATCHSTACK_REFRESH_SECRET : undefined);\n const refreshable = live && (options.refreshMs > 0 || Boolean(refreshSecret));\n\n // On the Pulse (siteUuid) path, re-post the dependency manifest before re-fetching. A targeted\n // `npm install <pkg>` fires no npm lifecycle hook, so nothing else re-scans; reporting here lets\n // the server flag a newly-added vulnerable dependency and the SAME tick's rule fetch pick up its\n // rule. Loaded once, up front, only when a refresh path is enabled — a refresh-off production\n // guard never pulls in the scan pipeline; a load/report failure never blocks the rule refresh.\n let reporter = null;\n if (refreshable && options.siteUuid && options.reportManifest !== false && cwd) {\n try {\n ({ reportManifest: reporter } = await import('./refresh-manifest.js'));\n } catch (err) {\n onError?.(err); // scan pipeline unavailable (e.g. an edge runtime) — rules still refresh\n }\n }\n\n const runRefreshTick = async () => {\n if (reporter) {\n try {\n await reporter(cwd);\n } catch (err) {\n onError?.(err); // a failed report must not stop the rule refresh\n }\n }\n const next = await resolveRules(options, store, { timeoutMs: options.refreshTimeoutMs, pulseAuth });\n mode = resolveMode(options, next);\n applyBundle(next);\n };\n\n if (live) {\n // Manual one-shot refresh (also the primitive the loop + push endpoint run).\n protection.refresh = () => runRefreshTick();\n // Authenticated push endpoint — the platform/SaaS hits it for an immediate refresh. No secret\n // configured → the handler 404s (never an open refresh trigger).\n protection.refreshHandler = () => makeRefreshHandler(runRefreshTick, refreshSecret);\n }\n\n if (options.refreshMs > 0 && live) {\n const loop = startRefresh(runRefreshTick, { refreshMs: options.refreshMs, onError });\n protection.stopRefresh = () => {\n loop.stop();\n firewallLog?.stop();\n };\n } else if (firewallLog) {\n protection.stopRefresh = () => firewallLog.stop();\n }\n\n return protection;\n}\n\n/**\n * Resolve runtime enforcement mode.\n * Precedence: PATCHSTACK_MODE env > Pulse `enforcement` on the rules bundle > options.mode > dry-run.\n */\nfunction resolveMode(options, bundle) {\n const env = typeof process !== 'undefined' ? process.env?.PATCHSTACK_MODE : undefined;\n if (env === 'block' || env === 'dry-run') return env;\n if (bundle?.enforcement === 'block' || bundle?.enforcement === 'dry-run') return bundle.enforcement;\n if (options?.mode === 'block') return 'block';\n if (options?.mode === 'dry-run') return 'dry-run';\n return 'dry-run';\n}\n\n/**\n * WP-format api_key for connector /api/logs/log. Never use the public site UUID.\n * The `.patchstackrc.json` fallback reads the filesystem, so fs/path are imported LAZILY — this\n * module must stay loadable on edge runtimes (Next edge middleware, Workers, Deno, Supabase\n * Functions), where a static `node:fs` import fails to resolve and would take the guard down.\n */\nasync function resolveApiKey(options) {\n if (typeof options?.apiKey === 'string' && options.apiKey.length > 0) return options.apiKey;\n if (typeof process !== 'undefined') {\n const fromEnv = process.env?.PATCHSTACK_API_KEY;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) return fromEnv;\n }\n try {\n if (typeof process === 'undefined' || typeof process.cwd !== 'function') return undefined;\n const [{ readFileSync }, { join }] = await Promise.all([import('node:fs'), import('node:path')]);\n const cwd = options?.cwd ?? process.cwd();\n const raw = readFileSync(join(cwd, '.patchstackrc.json'), 'utf8');\n const key = JSON.parse(raw)?.apiKey;\n if (typeof key === 'string' && key.length > 0) return key;\n } catch {\n /* missing, or no filesystem on this runtime — reporting stays off */\n }\n return undefined;\n}\n\n/**\n * Credential for the authenticated rules lookup (ADR-0018). Same resolution\n * order and the same edge-runtime caution as resolveApiKey, and falls back to\n * it so guards installed before pulseAuth existed keep authenticating.\n *\n * Returning undefined is fine: the rules fetch then goes out unauthenticated,\n * which the server still accepts.\n */\nasync function resolvePulseAuth(options) {\n if (typeof options?.pulseAuth === 'string' && options.pulseAuth.length > 0) return options.pulseAuth;\n if (typeof process !== 'undefined') {\n const fromEnv = process.env?.PATCHSTACK_PULSE_AUTH;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) return fromEnv;\n }\n try {\n if (typeof process === 'undefined' || typeof process.cwd !== 'function') return resolveApiKey(options);\n const [{ readFileSync }, { join }] = await Promise.all([import('node:fs'), import('node:path')]);\n const cwd = options?.cwd ?? process.cwd();\n const raw = readFileSync(join(cwd, '.patchstackrc.json'), 'utf8');\n const key = JSON.parse(raw)?.pulseAuth;\n if (typeof key === 'string' && key.length > 0) return key;\n } catch {\n /* missing, or no filesystem here — fall through to the apiKey path */\n }\n\n return resolveApiKey(options);\n}\n\n// --- phase / response helpers -------------------------------------------\n\nfunction byPhase(rules, phase) {\n return (rules ?? []).filter((r) => (r.phase ?? 'request') === phase);\n}\n\n// Classify a content-type for response screening: 'text' = screen; 'sniff' = screen only if the\n// bytes aren't binary (octet-stream is often a misdeclared JSON export/config); 'skip' = pass\n// through unscreened (live streams, known binary families). SSE is matched on the EXACT base type,\n// not a loose substring — `application/json; profile=\"event-stream\"` is not a stream.\nfunction baseContentType(ct) {\n return String(ct || '').toLowerCase().split(';')[0].trim();\n}\nfunction screenableContentType(ct) {\n const base = baseContentType(ct);\n if (base === 'text/event-stream') return 'skip'; // live token/SSE stream — never buffer\n if (base === '') return 'text';\n if (/(json|text|xml|html|javascript|csv|yaml|x-www-form-urlencoded)/.test(base)) return 'text';\n if (base === 'application/octet-stream') return 'sniff'; // maybe a text/JSON export mislabeled\n return 'skip'; // image/video/audio/font/pdf/zip/wasm/… — don't buffer binary\n}\n// Cheap binary sniff over a byte prefix: a NUL byte, or many control chars, means \"don't treat as text\".\nfunction looksBinary(bytes) {\n const n = Math.min(bytes.length, 512);\n let ctrl = 0;\n for (let i = 0; i < n; i++) {\n const b = bytes[i];\n if (b === 0) return true;\n if (b < 9 || (b > 13 && b < 32)) ctrl++;\n }\n return n > 0 && ctrl / n > 0.1;\n}\n\n// Returns { text } when the body was fully buffered for screening, or { skip: <reason> } when it was\n// NOT screened — the reason is surfaced to `onSkip`/coverage so a fail-open bypass is observable\n// instead of silent (an unscreened response is a real hole in enforcement).\nasync function readTextResponse(response, cap = DEFAULT_SCREEN_CAP) {\n if (!response || typeof response.clone !== 'function') return { skip: 'not-a-response' };\n const ct = response.headers?.get?.('content-type') || '';\n const kind = screenableContentType(ct);\n if (kind === 'skip') return { skip: baseContentType(ct) === 'text/event-stream' ? 'live-stream' : 'non-text-content-type' };\n const sniff = kind === 'sniff';\n const len = Number(response.headers?.get?.('content-length') || 0);\n if (len && len > cap) return { skip: 'body-cap' };\n let clone;\n try {\n clone = response.clone();\n } catch {\n return { skip: 'clone-failed' };\n }\n\n // Stream the read so a body WITHOUT a Content-Length can't buffer past the cap. Over the cap the\n // response is left UNSCREENED — but we keep draining the clone so the original stays intact.\n const body = clone.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks = [];\n let size = 0;\n let over = false;\n let sniffed = !sniff;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n if (!sniffed) {\n sniffed = true;\n if (looksBinary(value)) return { skip: 'binary-body' };\n }\n size += value.byteLength;\n if (over) continue; // keep draining, stop buffering\n if (size > cap) { over = true; continue; }\n chunks.push(value);\n }\n } catch {\n return { skip: 'read-failed' };\n }\n if (over) return { skip: 'body-cap' };\n try {\n return { text: new TextDecoder().decode(concatBytes(chunks, size)) };\n } catch {\n return { skip: 'decode-failed' };\n }\n }\n\n try {\n const text = await clone.text();\n if (text.length > cap) return { skip: 'body-cap' };\n if (sniff && looksBinary(new TextEncoder().encode(text.slice(0, 512)))) return { skip: 'binary-body' };\n return { text };\n } catch {\n return { skip: 'read-failed' };\n }\n}\n\nfunction concatBytes(chunks, total) {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n\nfunction headerObject(headers) {\n const out = {};\n headers?.forEach?.((v, k) => { out[k.toLowerCase()] = v; });\n // Set-Cookie is multi-valued; forEach collapses it. Recover the individual cookies so each can\n // be screened (and re-emitted) separately.\n const setCookies = headers?.getSetCookie?.();\n if (setCookies && setCookies.length) out['set-cookie'] = setCookies;\n return out;\n}\n\n// True if any of the rule's conditions carries a body-transforming mutation on a SPAN match\n// (regex/contains/stripos) — those decode the body before matching, so a span redactor derived from\n// the literal/regex can't be located in the raw body. (array_key_value structural redaction decodes\n// the JSON itself, so json_decode there is fine and doesn't count.)\nfunction hasSpanMutations(rule) {\n let found = false;\n const walk = (conds) => {\n for (const c of conds ?? []) {\n if (found) return;\n if (Array.isArray(c.rules)) walk(c.rules);\n const isSpan = c.match && (c.match.type === 'regex' || c.match.type === 'contains' || c.match.type === 'stripos');\n if (isSpan && Array.isArray(c.mutations) && c.mutations.length) found = true;\n }\n };\n walk(rule.rule_v2);\n return found;\n}\n\n// Derive redaction targets from a rule's own conditions: regex → mask every match;\n// contains/stripos → mask the literal. (Other match types can't identify a span → the\n// rule falls back to block.)\nfunction extractRedactors(rule) {\n const out = [];\n const walk = (conds) => {\n for (const c of conds ?? []) {\n if (Array.isArray(c.rules)) walk(c.rules);\n const m = c.match;\n if (!m) continue;\n if (m.type === 'regex' && typeof m.value === 'string') {\n // Route through the SAME ReDoS guard detection uses — a catastrophic redactor pattern must\n // not hang the response path (safeRegExp returns null for dangerous/invalid patterns → skip).\n const safe = safeRegExp(m.value);\n if (safe) {\n const flags = safe.flags.includes('g') ? safe.flags : safe.flags + 'g';\n try {\n out.push({ re: new RegExp(safe.source, flags) });\n } catch {\n /* skip invalid */\n }\n }\n } else if ((m.type === 'contains' || m.type === 'stripos') && m.value != null) {\n out.push({ literal: String(m.value) });\n } else if (m.type === 'array_key_value' && m.match && isBodyParam(c.parameter)) {\n // Structural redaction: mask the value at a JSON path (fanning out over arrays) rather than\n // a text span — e.g. key \"orders.customers.email\" masks that field in every array element.\n const keys = Array.isArray(m.key) ? m.key : [m.key];\n for (const key of keys) {\n out.push({ jsonPath: String(key).split('.'), condition: m.match });\n }\n }\n }\n };\n walk(rule.rule_v2);\n return out;\n}\n\n// HTML-entity escape, for the `encode` action (neutralize markup rather than mask it). NOTE: this is\n// sound only for HTML text / attribute-VALUE contexts. It does NOT neutralize a `javascript:` / `data:`\n// URI or an event-handler name (those carry no HTML metacharacters) — use `block` for a rule that\n// targets a URL/scheme context. See the rule-authoring guidance in the triage-vpatch-npm skill.\nfunction htmlEscape(str) {\n return String(str).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c]);\n}\n\n// `transform` (optional): map a matched span to its replacement (the `encode` action passes\n// htmlEscape). Without it, matches are replaced by the `mask` string (the `redact` action).\nfunction applyRedactors(body, redactors, mask, transform) {\n let out = body;\n for (const r of redactors) {\n if (r.re) out = out.replace(r.re, transform ? (m) => transform(m) : mask);\n else if (r.literal) {\n // Detection (matchValue for contains/stripos) is case-insensitive, so mask case-insensitively\n // too — otherwise a `contains: \"SECRET\"` redactor detects `secret` but masks nothing, serving\n // the leak while reporting a redaction. Escape the literal so it matches literally, not as regex.\n const re = new RegExp(r.literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'gi');\n out = out.replace(re, (m) => (transform ? transform(m) : mask));\n }\n }\n return out;\n}\n\n// A response-body redaction target (array_key_value masks the JSON body). A bare condition with no\n// parameter also defaults to the body.\nfunction isBodyParam(parameter) {\n return parameter == null || parameter === 'response.body' || parameter === 'raw' || parameter === 'response.raw';\n}\n\n// Build a predicate from the array_key_value nested match, so a path can be masked conditionally\n// (only leaves that match) or — with `isset` — unconditionally. Fail-closed (don't mask) on error.\nfunction conditionPredicate(condition) {\n if (!condition || !condition.type) return () => true;\n return (value) => {\n try {\n return matchValue(condition.type, value, condition.value, condition);\n } catch {\n return false;\n }\n };\n}\n\nconst DEFAULT_SCREEN_CAP = 512 * 1024;\n\n// Effective response-screening size cap for a rule set. Bodies larger than this are passed through\n// UNSCREENED (so a redact rule can't mask them — the leak/PII would slip out). A rule can raise the\n// ceiling for the whole response phase: `bypass_limit: true` removes the cap entirely (accepts the\n// memory cost on a hostile large body), or `max_bytes: <n>` raises it to n. The cap is shared (the\n// body is buffered once), so the effective cap is the MAX across all active response rules.\nfunction responseScreenCap(rules) {\n let cap = DEFAULT_SCREEN_CAP;\n for (const r of rules ?? []) {\n if (r && r.bypass_limit === true) return Infinity;\n const n = Number(r && r.max_bytes);\n if (Number.isFinite(n) && n > cap) cap = n;\n }\n return cap;\n}\n\n// Apply jsonPath redactors structurally: parse the JSON body, mask each targeted leaf (fanning out\n// over arrays at every path segment), re-serialize. Fail-open — a non-JSON / oversized / unparseable\n// body is returned unchanged, and any per-leaf error is swallowed.\nfunction applyPathRedactors(text, pathRedactors, mask, cap, transform) {\n if (!pathRedactors.length || typeof text !== 'string' || text.length > cap) return text;\n const head = text.trimStart()[0];\n if (head !== '{' && head !== '[') return text; // not a JSON object/array\n // Preserve out-of-safe-range integers across the parse→stringify round-trip: JSON.parse would\n // round e.g. a 20-digit id. We quote such number tokens to a sentinel string before parsing and\n // unquote them after stringifying, so untouched big ints survive losslessly.\n const preserved = preserveBigInts(text);\n let obj;\n try {\n obj = JSON.parse(preserved);\n } catch {\n return text;\n }\n let changed = false;\n for (const r of pathRedactors) {\n const pred = conditionPredicate(r.condition);\n walkLeaves(obj, r.jsonPath, (loc) => {\n try {\n if (pred(loc.value)) {\n // `encode`: escape the leaf's own value in place; `redact`: replace it with the mask.\n loc.parent[loc.key] = transform ? transform(String(loc.value)) : mask;\n changed = true;\n }\n } catch {\n /* skip this leaf */\n }\n });\n }\n return changed ? restoreBigInts(JSON.stringify(obj)) : text;\n}\n\nconst BIGINT_OPEN = '__PSBIGINT_9c2f__';\nconst BIGINT_CLOSE = '__DNEGIB__';\n\n// Quote every out-of-safe-range integer *value* (a bare number token outside a string) into a\n// sentinel string, so JSON.parse keeps it verbatim. String-aware scan (respects \\ escapes) so a\n// number inside a string value is never touched. Plain-ASCII sentinel → survives JSON.stringify.\nfunction preserveBigInts(text) {\n let out = '';\n let inStr = false;\n for (let i = 0; i < text.length; ) {\n const ch = text[i];\n if (inStr) {\n out += ch;\n if (ch === '\\\\') { out += text[i + 1] ?? ''; i += 2; continue; }\n if (ch === '\"') inStr = false;\n i++;\n continue;\n }\n if (ch === '\"') { inStr = true; out += ch; i++; continue; }\n if (ch === '-' || (ch >= '0' && ch <= '9')) {\n let j = ch === '-' ? i + 1 : i;\n let digits = 0;\n while (j < text.length && text[j] >= '0' && text[j] <= '9') { digits++; j++; }\n const next = text[j];\n const isIntToken = digits > 0 && next !== '.' && next !== 'e' && next !== 'E';\n if (isIntToken && digits >= 16) {\n out += `\"${BIGINT_OPEN}${text.slice(i, j)}${BIGINT_CLOSE}\"`;\n } else {\n out += text.slice(i, j || i + 1);\n }\n i = j > i ? j : i + 1;\n continue;\n }\n out += ch;\n i++;\n }\n return out;\n}\n\nfunction restoreBigInts(text) {\n if (!text.includes(BIGINT_OPEN)) return text;\n return text.replace(new RegExp(`\"${BIGINT_OPEN}(-?\\\\d+)${BIGINT_CLOSE}\"`, 'g'), '$1');\n}\n\n// Response-hardening actions. Mutate the (lowercase-keyed) headers object in place; a `null` value\n// signals removal to rebuildResponse / the node path. `set-header` sets/overwrites (or `ensure`s only\n// when absent); `remove-header` strips; `harden-cookie` adds missing HttpOnly/Secure/SameSite flags.\nfunction isHeaderMutation(action) {\n return action === 'set-header' || action === 'remove-header' || action === 'harden-cookie';\n}\n\nfunction applyHeaderMutation(headers, rule) {\n if (rule.action === 'remove-header') {\n for (const name of rule.remove_headers ?? []) headers[String(name).toLowerCase()] = null;\n return;\n }\n if (rule.action === 'set-header') {\n const ensure = rule.ensure === true; // set only when the header is absent (don't clobber)\n for (const [name, value] of Object.entries(rule.set_headers ?? {})) {\n const key = String(name).toLowerCase();\n const present = headers[key] != null && headers[key] !== '';\n if (ensure && present) continue;\n headers[key] = String(value);\n }\n return;\n }\n if (rule.action === 'harden-cookie') {\n const cookie = headers['set-cookie'];\n const flags = rule.cookie_flags ?? {};\n if (Array.isArray(cookie)) {\n headers['set-cookie'] = cookie.map((c) => (typeof c === 'string' ? hardenCookie(c, flags) : c));\n } else if (typeof cookie === 'string') {\n headers['set-cookie'] = hardenCookie(cookie, flags);\n }\n }\n}\n\nfunction hardenCookie(cookie, { httpOnly = true, secure = true, sameSite = 'Lax' } = {}) {\n let out = String(cookie);\n if (httpOnly && !/;\\s*httponly/i.test(out)) out += '; HttpOnly';\n if (secure && !/;\\s*secure/i.test(out)) out += '; Secure';\n if (sameSite && !/;\\s*samesite\\s*=/i.test(out)) out += `; SameSite=${sameSite}`;\n return out;\n}\n\nfunction rebuildResponse(response, body, redactedHeaders) {\n const headers = new Headers(response.headers);\n headers.delete('content-length'); // body length changed after redaction\n if (redactedHeaders) {\n for (const [name, value] of Object.entries(redactedHeaders)) {\n if (value === null || value === undefined) {\n try { headers.delete(name); } catch { /* skip */ } // header-mutation removal\n } else if (typeof value === 'string') {\n if (headers.get(name) !== value) {\n try { headers.set(name, value); } catch { /* invalid header name — skip */ }\n }\n } else if (Array.isArray(value)) {\n // Re-emit each (possibly redacted) Set-Cookie separately (Headers collapses them otherwise).\n try {\n headers.delete(name);\n for (const item of value) headers.append(name, String(item));\n } catch { /* skip */ }\n }\n }\n }\n // Null-body statuses (204/205/304/101) must not carry a body, or the Response constructor throws.\n const nullBody = response.status === 101 || response.status === 204 || response.status === 205 || response.status === 304;\n return new Response(nullBody ? null : body, { status: response.status, statusText: response.statusText, headers });\n}\n\nfunction leakResponse() {\n return new Response(JSON.stringify({ error: 'Response withheld by Patchstack (sensitive data detected)' }), {\n status: 500,\n headers: { 'content-type': 'application/json' }\n });\n}\n\n// (rule source / tiered store moved to ./rules/source.js + ./rules/store.js)\n\n// --- responses ----------------------------------------------------------\n\n// Client-facing block text — deliberately generic: no WAF narrative, no rule title. The reason\n// detail stays in the server-side log via onDetect.\nconst BLOCK_MESSAGE = 'This request has been blocked by Patchstack.';\n\nfunction blockBody(result) {\n // Human text is masked (no WAF narrative, no rule title). The opaque rule id stays for machine\n // consumers (server-fn receipts, support reference); full rule detail lives in the server log.\n return { error: BLOCK_MESSAGE, message: BLOCK_MESSAGE, rule: result?.rule?.id };\n}\n\n// A top-level document navigation (vs an XHR/fetch)? Browsers set Sec-Fetch-Dest on navigations;\n// fall back to the Accept header. Governs whether a block returns the HTML page or JSON.\nfunction isDocumentNavigation(getHeader) {\n const dest = getHeader('sec-fetch-dest');\n if (dest) return dest === 'document';\n return (getHeader('accept') || '').includes('text/html');\n}\n\n// Request-phase block. Serves the branded HTML \"Access Denied\" page to a browser navigation, and\n// masked JSON to XHR/fetch/programmatic clients.\nfunction blockResponse(result, request) {\n if (request && isDocumentNavigation((n) => request.headers.get(n))) {\n return new Response(renderBlockPage({ url: request.url, code: result?.rule?.id }), {\n status: 403,\n headers: { 'content-type': 'text/html; charset=utf-8' },\n });\n }\n return new Response(JSON.stringify(blockBody(result)), {\n status: 403,\n headers: { 'content-type': 'application/json' },\n });\n}\n\nfunction defaultOnDetect({ phase, mode, category, rule, message }) {\n const tag = mode === 'block' ? 'BLOCK' : 'DETECT (dry-run)';\n console.warn(`[patchstack] ${tag} phase=${phase ?? 'request'} category=${category ?? '?'} rule=${rule?.id ?? '?'} ${message ?? ''}`.trim());\n}\n\n/** @param {Request} request */\nfunction fetchRequestMeta(request) {\n if (!request) return {};\n let path = null;\n try {\n path = new URL(request.url).pathname;\n } catch {\n path = typeof request.url === 'string' ? request.url : null;\n }\n return {\n method: request.method ?? null,\n path,\n ip: request.headers?.get?.('x-forwarded-for') ?? null,\n userAgent: request.headers?.get?.('user-agent') ?? null,\n };\n}\n\n/** @param {import('http').IncomingMessage & { ip?: string, originalUrl?: string }} req */\nfunction nodeRequestMeta(req) {\n if (!req) return {};\n const headers = req.headers ?? {};\n const ua = headers['user-agent'] ?? headers['User-Agent'];\n const fwd = headers['x-forwarded-for'] ?? headers['X-Forwarded-For'];\n return {\n method: req.method ?? null,\n path: req.originalUrl || req.url || null,\n ip: req.ip ?? (typeof fwd === 'string' ? fwd : null),\n userAgent: typeof ua === 'string' ? ua : Array.isArray(ua) ? ua[0] : null,\n };\n}\n","// Which origins this guard is willing to talk to. Both of its remote conversations are security\n// sensitive: the RULE endpoint delivers policy the engine then executes on every request (an\n// attacker-controlled endpoint could remove protection wholesale or serve a CPU-expensive ruleset), and\n// the telemetry endpoint receives the site api_key. Env/CI injection is the realistic threat, so a\n// non-default override must be https — with localhost permitted so local development and tests work.\nexport function isSafeOrigin(value) {\n try {\n const u = new URL(value);\n if (u.protocol === 'https:') return true;\n return u.protocol === 'http:' && (u.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '[::1]' || u.hostname === '::1');\n } catch {\n return false;\n }\n}\n\n/**\n * Accept `candidate` only if it is a safe origin; otherwise warn once and fall back to `fallback`.\n * @param {string|undefined} candidate @param {string} fallback @param {string} label\n */\nexport function safeBaseUrl(candidate, fallback, label) {\n if (typeof candidate !== 'string' || candidate === '') return fallback;\n if (isSafeOrigin(candidate)) return candidate;\n warnOnce(label, `[patchstack] ignoring unsafe ${label} override (${candidate}): must be https (or localhost). Using the default.`);\n return fallback;\n}\n\nconst warned = new Set();\nfunction warnOnce(key, message) {\n if (warned.has(key)) return;\n warned.add(key);\n // eslint-disable-next-line no-console\n console.warn(message);\n}\n","import { safeBaseUrl } from '../safe-origin.js';\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com';\nconst DEFAULT_CACHE_TTL = 300_000;\n// Randomly shorten the effective TTL by up to this fraction so many long-lived clients don't all\n// revalidate on the same tick (spreads load against the rules API).\nconst JITTER_FRACTION = 0.1;\n\n// Token-authenticated rules client. Conditional fetch (If-None-Match / 304) and the persisted\n// `etag` mirror PulseRuleClient; both are a no-op until the API emits an ETag and honors the\n// conditional request — with no ETag in the response the client behaves as before (full fetch).\nexport class PatchstackRuleClient {\n #token;\n #baseUrl;\n #cacheTtl;\n #cache = null;\n #cacheTime = null;\n #ttlEffective = 0;\n #etag;\n #timeoutMs;\n\n constructor({ token, baseUrl, cacheTtl, etag, timeoutMs } = {}) {\n // Bounded so app STARTUP can't hang on a slow API (see the boot budget in rules/source.js).\n this.#timeoutMs = Number(timeoutMs) > 0 ? Number(timeoutMs) : 30_000;\n this.#token = token ?? process.env.PATCHSTACK_WAF_TOKEN;\n this.#baseUrl = safeBaseUrl(baseUrl ?? process.env.PATCHSTACK_WAF_API_URL, DEFAULT_BASE_URL, 'rule endpoint');\n this.#cacheTtl = Number.isFinite(cacheTtl) && cacheTtl > 0 ? cacheTtl : DEFAULT_CACHE_TTL;\n this.#etag = etag ?? null;\n\n if (!this.#token) {\n throw new Error('Patchstack WAF token is required. Pass { token } or set PATCHSTACK_WAF_TOKEN env var.');\n }\n }\n\n async getRules() {\n const now = Date.now();\n\n if (this.#cache && this.#cacheTime !== null && (now - this.#cacheTime) < this.#ttlEffective) {\n return this.#cache;\n }\n\n const url = `${this.#baseUrl}/api/get-rules/3`;\n\n try {\n const headers = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.#token}`,\n 'LicenseID': '1' // Hard-coded, is never actually used but needed by the API\n };\n if (this.#etag) headers['If-None-Match'] = this.#etag;\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n signal: AbortSignal.timeout(this.#timeoutMs)\n });\n\n if (response.status === 304) {\n this.#touch(now); // revalidated — reset the clock (fresh jitter)\n return this.#cache ?? { success: true, notModified: true, etag: this.#etag, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n\n if (!response.ok) {\n return {\n success: false,\n error: `API returned ${response.status}: ${response.statusText}`,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n\n const data = await response.json();\n\n // A 200 that isn't a genuine rule envelope must not be treated as \"no rules\". Report failure\n // so the caller falls back to the cache / bundled rules rather than caching an empty bundle.\n if (!data || !Array.isArray(data.firewall)) {\n return { success: false, error: 'unexpected response shape (no firewall array)', firewall: [], whitelists: [], whitelist_keys: {} };\n }\n\n const result = {\n success: true,\n notModified: false,\n etag: response.headers?.get?.('etag') ?? null,\n firewall: data.firewall,\n whitelists: Array.isArray(data.whitelists) ? data.whitelists : [],\n whitelist_keys: data.whitelist_keys ?? {}\n };\n\n this.#cache = result;\n this.#etag = result.etag;\n this.#touch(now);\n\n return result;\n } catch (err) {\n return {\n success: false,\n error: err.name === 'TimeoutError'\n ? 'Request timed out'\n : err.message,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n }\n\n #touch(now) {\n this.#cacheTime = now;\n this.#ttlEffective = this.#cacheTtl - Math.floor(Math.random() * this.#cacheTtl * JITTER_FRACTION);\n }\n\n clearCache() {\n this.#cache = null;\n this.#cacheTime = null;\n this.#etag = null;\n }\n}\n","// Resolvable DATA attributes of an uploaded file part (files.<name>.<attr>). The engine only exposes\n// the raw data — WHAT counts as a malicious upload (signatures, type-vs-content mismatch) is expressed\n// in rules (see the triage-vpatch-npm skill), not hardcoded here.\nconst FILE_ATTRS = new Set(['content', 'filename', 'type']);\n\n// A captured file part is { filename, type, content }; tolerate the legacy bare-filename string.\nconst fileFilename = (f) => (f && typeof f === 'object' ? f.filename : f);\nconst fileAttribute = (f, attr) => (f && typeof f === 'object' ? f[attr] : attr === 'filename' ? f : undefined);\n\n// WinterCG-safe base64 decode: use Buffer on Node, fall back to atob/TextDecoder on\n// edge runtimes (Cloudflare Workers, Deno, Bun) where Buffer may be absent. Keeps the\n// engine hot path free of Node-only APIs (per the ADR engine-language decision).\nfunction base64DecodeUtf8(value) {\n const str = String(value);\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n const binary = atob(str);\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n}\n\nexport class RequestResolver {\n #req;\n #cookies;\n\n constructor(req) {\n this.#req = req;\n this.#cookies = null;\n }\n\n resolve(parameter) {\n if (!parameter || parameter === 'false') {\n return [null];\n }\n\n if (parameter === 'rules') {\n return [null];\n }\n\n if (parameter === 'raw') {\n return this.#resolveRaw();\n }\n\n if (parameter === 'all') {\n return this.#resolveAll();\n }\n\n const dotIndex = parameter.indexOf('.');\n if (dotIndex === -1) {\n return [];\n }\n\n const source = parameter.substring(0, dotIndex);\n const key = parameter.substring(dotIndex + 1);\n\n switch (source) {\n case 'get':\n return this.#resolveGet(key);\n case 'post':\n return this.#resolvePost(key);\n case 'request':\n return this.#resolveRequest(key);\n case 'cookie':\n return this.#resolveCookie(key);\n case 'server':\n return this.#resolveServer(key);\n case 'files':\n return this.#resolveFiles(key);\n case 'response':\n return this.#resolveResponse(key);\n case 'egress':\n return this.#resolveEgress(key);\n default:\n return [];\n }\n }\n\n // Response-phase sources (req._response = { status, headers, body }). Lets rules inspect\n // what the app is about to SEND — e.g. a leaked secret in the body — regardless of route.\n #resolveResponse(key) {\n const resp = this.#req._response;\n if (!resp) {\n return [];\n }\n if (key === 'status') {\n return resp.status !== undefined ? [String(resp.status)] : [];\n }\n if (key === 'body') {\n return resp.body != null && resp.body !== '' ? [resp.body] : [];\n }\n if (key === 'headers') {\n const headers = resp.headers ?? {};\n return [Object.entries(headers).map(([k, v]) => `${k}: ${v}`).join('\\n')];\n }\n if (key.startsWith('header.')) {\n const name = key.slice('header.'.length).toLowerCase();\n const value = (resp.headers ?? {})[name];\n return value !== undefined ? [value] : [];\n }\n return [];\n }\n\n // Egress-phase sources (req._egress = { url, host, method }). Lets rules inspect an\n // OUTBOUND request the app is about to make — SSRF at the egress boundary.\n #resolveEgress(key) {\n const eg = this.#req._egress;\n if (!eg) {\n return [];\n }\n if (key === 'url') return eg.url ? [eg.url] : [];\n if (key === 'host') return eg.host ? [eg.host] : [];\n if (key === 'method') return eg.method ? [eg.method] : [];\n return [];\n }\n\n applyMutations(mutations, value) {\n if (!mutations || !Array.isArray(mutations)) {\n return value;\n }\n\n let result = value;\n\n for (const mutation of mutations) {\n result = this.#applyMutation(mutation, result);\n }\n\n return result;\n }\n\n #applyMutation(mutation, value) {\n if (value === null || value === undefined) {\n return value;\n }\n\n switch (mutation) {\n case 'base64_decode':\n try {\n return base64DecodeUtf8(value);\n } catch {\n return value;\n }\n\n case 'json_decode':\n try {\n return JSON.parse(String(value));\n } catch {\n return value;\n }\n\n case 'json_encode':\n try {\n return JSON.stringify(value);\n } catch {\n return value;\n }\n\n case 'urldecode':\n try {\n return decodeURIComponent(String(value));\n } catch {\n return value;\n }\n\n case 'intval':\n return parseInt(String(value), 10) || 0;\n\n case 'getArrayValues':\n if (typeof value === 'object' && value !== null) {\n return Object.values(value);\n }\n return value;\n\n default:\n return value;\n }\n }\n\n #resolveGet(key) {\n const query = this.#req.query ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(query, key);\n }\n\n const value = this.#getNestedValue(query, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolvePost(key) {\n const body = this.#req.body ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(body, key);\n }\n\n const value = this.#getNestedValue(body, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolveRequest(key) {\n const query = this.#req.query ?? {};\n const body = this.#req.body ?? {};\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return [\n ...this.#resolveWildcard(query, key),\n ...this.#resolveWildcard(body, key),\n ...this.#resolveWildcard(cookies, key)\n ];\n }\n\n const value = this.#getNestedValue(query, key)\n ?? this.#getNestedValue(body, key)\n ?? cookies[key];\n\n return value !== undefined ? [value] : [];\n }\n\n #resolveCookie(key) {\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(cookies, key);\n }\n\n const value = cookies[key];\n return value !== undefined ? [value] : [];\n }\n\n #resolveServer(key) {\n const req = this.#req;\n\n switch (key) {\n case 'REQUEST_URI':\n return [req.originalUrl ?? req.url ?? '/'];\n case 'REQUEST_METHOD':\n return [req.method ?? 'GET'];\n case 'HTTP_USER_AGENT':\n return req.headers?.['user-agent'] ? [req.headers['user-agent']] : [];\n case 'HTTP_REFERER':\n return req.headers?.referer ? [req.headers.referer] : [];\n case 'HTTP_HOST':\n return req.headers?.host ? [req.headers.host] : [];\n case 'REMOTE_ADDR':\n case 'ip':\n return [req.ip ?? req.socket?.remoteAddress ?? ''];\n case 'CONTENT_TYPE':\n return req.headers?.['content-type'] ? [req.headers['content-type']] : [];\n case 'CONTENT_LENGTH':\n return req.headers?.['content-length'] ? [req.headers['content-length']] : [];\n default: {\n if (key.startsWith('HTTP_')) {\n const headerName = key.substring(5).toLowerCase().replace(/_/g, '-');\n const headers = req.headers;\n // Presence, not truthiness. A header sent with an empty value IS present, and an `isset`\n // rule authored against it must see it — some bypasses are carried by the header existing\n // at all, so treating `Header:` as absent would make the rule quietly miss the shape it\n // was written for. (The named cases above keep value semantics: for host/origin/referer an\n // empty string and an absent header mean the same thing to the matchers that read them.)\n if (headers === null || typeof headers !== 'object') return [];\n return Object.prototype.hasOwnProperty.call(headers, headerName) ? [headers[headerName]] : [];\n }\n return [];\n }\n }\n }\n\n #resolveFiles(key) {\n const files = this.#req.files;\n if (!files || typeof files !== 'object') {\n return [];\n }\n\n // files.<name>.<attr> — content | filename | type. Fans out over multiple files uploaded under\n // the same field name.\n const dot = key.lastIndexOf('.');\n if (dot !== -1 && FILE_ATTRS.has(key.slice(dot + 1)) && Object.prototype.hasOwnProperty.call(files, key.slice(0, dot))) {\n const attr = key.slice(dot + 1);\n const entry = files[key.slice(0, dot)];\n const list = Array.isArray(entry) ? entry : [entry];\n const out = [];\n for (const f of list) {\n const v = fileAttribute(f, attr);\n if (v !== undefined && v !== '') out.push(v);\n }\n return out;\n }\n\n // Bare files.<name> (or wildcard) → the filename(s), preserving the legacy behavior that\n // filename-scoped rules rely on (the parser now stores a { filename, type, content } object).\n const filenamesOf = (entry) => (Array.isArray(entry) ? entry.map(fileFilename) : [fileFilename(entry)]);\n if (key.endsWith('*')) {\n const prefix = key.slice(0, -1);\n const out = [];\n for (const [k, entry] of Object.entries(files)) {\n if (k.startsWith(prefix)) out.push(...filenamesOf(entry));\n }\n return out.filter((v) => v !== undefined);\n }\n if (!Object.prototype.hasOwnProperty.call(files, key)) return [];\n return filenamesOf(files[key]).filter((v) => v !== undefined);\n }\n\n #resolveRaw() {\n // Use pre-captured raw body if available (set by normalizeRequest).\n // For string bodies, the original text is preserved verbatim.\n // For pre-parsed objects, serializeForRawDetection uses Object.getOwnPropertyNames()\n // to include __proto__ own-property keys that JSON.stringify() would silently drop.\n if (typeof this.#req._rawBody === 'string') {\n return this.#req._rawBody ? [this.#req._rawBody] : [];\n }\n\n const body = this.#req.body;\n\n if (body === undefined || body === null) {\n return [];\n }\n\n if (typeof body === 'string') {\n return [body];\n }\n\n try {\n return [JSON.stringify(body)];\n } catch {\n return [String(body)];\n }\n }\n\n #resolveAll() {\n const parts = [];\n\n const uri = this.#req.originalUrl ?? this.#req.url ?? '/';\n parts.push(uri);\n\n const queryString = uri.includes('?') ? uri.split('?')[1] : '';\n if (queryString) {\n parts.push(queryString);\n }\n\n const body = this.#req.body;\n if (body) {\n parts.push(typeof body === 'string' ? body : JSON.stringify(body));\n }\n // Also fold in the verbatim body: a body the adapter couldn't structurally parse (an unusual\n // content-type, a non-JSON payload) leaves `body` empty, but the raw text must still be matchable\n // by an `all` rule — otherwise it's only visible via `raw`.\n if (typeof this.#req._rawBody === 'string' && this.#req._rawBody) {\n parts.push(this.#req._rawBody);\n }\n\n const headers = this.#req.headers ?? {};\n const excludedHeaders = new Set([\n 'host', 'connection', 'cache-control', 'accept', 'accept-encoding',\n 'accept-language', 'priority', 'sec-ch-ua', 'sec-ch-ua-mobile',\n 'sec-ch-ua-platform', 'sec-fetch-dest', 'sec-fetch-mode',\n 'sec-fetch-site', 'sec-fetch-user', 'upgrade-insecure-requests'\n ]);\n\n for (const [name, value] of Object.entries(headers)) {\n if (!excludedHeaders.has(name)) {\n parts.push(`${name}: ${value}`);\n }\n }\n\n const cookies = this.#parseCookies();\n const cookieStr = Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ');\n if (cookieStr) {\n parts.push(cookieStr);\n }\n\n return [parts.join(' ')];\n }\n\n #resolveWildcard(obj, pattern) {\n if (typeof obj !== 'object' || obj === null) {\n return [];\n }\n\n const prefix = pattern.slice(0, -1);\n const values = [];\n\n for (const [key, value] of Object.entries(obj)) {\n if (key.startsWith(prefix)) {\n values.push(value);\n }\n }\n\n return values;\n }\n\n #getNestedValue(obj, key) {\n if (typeof obj !== 'object' || obj === null) {\n return undefined;\n }\n\n // Own-property only: `key in obj` would resolve `__proto__`/`constructor`/`toString` to the\n // prototype chain, so a rule like `post.__proto__` (detecting a literal `__proto__` field) would\n // read Object.prototype instead of request data and never match. Use hasOwnProperty.\n const own = (o, k) => o !== null && typeof o === 'object' && Object.prototype.hasOwnProperty.call(o, k);\n\n if (own(obj, key)) {\n return obj[key];\n }\n\n const parts = key.split('.');\n let current = obj;\n\n for (const part of parts) {\n if (!own(current, part)) {\n return undefined;\n }\n current = current[part];\n }\n\n return current;\n }\n\n #parseCookies() {\n if (this.#cookies !== null) {\n return this.#cookies;\n }\n\n if (this.#req.cookies) {\n this.#cookies = this.#req.cookies;\n return this.#cookies;\n }\n\n const header = this.#req.headers?.cookie;\n if (!header) {\n this.#cookies = {};\n return this.#cookies;\n }\n\n const cookies = {};\n\n for (const pair of header.split(';')) {\n const eqIndex = pair.indexOf('=');\n if (eqIndex === -1) {\n continue;\n }\n const name = pair.substring(0, eqIndex).trim();\n const value = pair.substring(eqIndex + 1).trim();\n cookies[name] = value;\n }\n\n this.#cookies = cookies;\n return this.#cookies;\n }\n}\n","/**\n * Request Normalization Pipeline\n *\n * Normalizes request data to prevent encoding-based bypass attacks.\n * This module handles URL encoding, HTML entities, SQL comments, and other\n * obfuscation techniques that attackers use to evade pattern matching.\n */\n\nconst HTML_ENTITIES = {\n '&amp;': '&',\n '&lt;': '<',\n '&gt;': '>',\n '&quot;': '\"',\n '&#39;': \"'\",\n '&apos;': \"'\",\n '&nbsp;': ' ',\n '&#x27;': \"'\",\n '&#x22;': '\"',\n '&#x3C;': '<',\n '&#x3E;': '>',\n '&#x26;': '&',\n '&#34;': '\"',\n '&#60;': '<',\n '&#62;': '>',\n '&#38;': '&'\n};\n\nconst MAX_DECODE_ITERATIONS = 5;\n\nexport function normalize(value, options = {}) {\n if (typeof value !== 'string') {\n return value;\n }\n\n const opts = {\n urlDecode: true,\n htmlDecode: true,\n sqlComments: true,\n nullBytes: true,\n whitespace: true,\n ...options\n };\n\n let result = value;\n\n if (opts.nullBytes) {\n result = removeNullBytes(result);\n }\n\n if (opts.urlDecode) {\n result = urlDecode(result);\n }\n\n if (opts.htmlDecode) {\n result = htmlEntityDecode(result);\n }\n\n if (opts.sqlComments) {\n result = removeSqlComments(result);\n }\n\n if (opts.whitespace) {\n result = normalizeWhitespace(result);\n }\n\n return result;\n}\n\nexport function urlDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n let previous = '';\n let iterations = 0;\n\n while (result !== previous && iterations < MAX_DECODE_ITERATIONS) {\n previous = result;\n iterations++;\n\n try {\n result = decodeURIComponent(result);\n } catch {\n result = safeUrlDecode(result);\n break;\n }\n }\n\n return result;\n}\n\nfunction safeUrlDecode(value) {\n return value.replace(/%([0-9A-Fa-f]{2})/g, (match, hex) => {\n try {\n return String.fromCharCode(parseInt(hex, 16));\n } catch {\n return match;\n }\n });\n}\n\nexport function htmlEntityDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n\n for (const [entity, char] of Object.entries(HTML_ENTITIES)) {\n result = result.split(entity).join(char);\n }\n\n result = result.replace(/&#(\\d+);/g, (match, code) => {\n const num = parseInt(code, 10);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n result = result.replace(/&#x([0-9A-Fa-f]+);/g, (match, hex) => {\n const num = parseInt(hex, 16);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n return result;\n}\n\nexport function removeSqlComments(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n // Collapse inline block comments to a space (the anti-obfuscation goal). We must NOT strip the\n // line-comment forms (`--…`, `#…`) to end-of-line: on the WAF inspection path that DELETES\n // attacker-controlled spans from the value the engine sees while the app still processes the\n // original — e.g. `#<script>…` becomes empty and evades an XSS rule, though the browser still\n // runs it. Keeping the content only ever ADDS matches (more of the value is inspected), never\n // hides one. SQLi keyword detection is unaffected — the keywords remain visible.\n let result = value;\n\n result = result.replace(/\\/\\*[\\s\\S]*?\\*\\//g, ' ');\n result = result.replace(/\\/\\*![\\s\\S]*?\\*\\//g, ' ');\n\n return result;\n}\n\nexport function removeNullBytes(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/\\x00/g, '');\n\n result = result.replace(/[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F]/g, '');\n\n return result;\n}\n\nexport function normalizeWhitespace(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/[\\t\\r\\n\\f\\v]+/g, ' ');\n\n result = result.replace(/ {2,}/g, ' ');\n\n return result;\n}\n\nfunction serializeForRawDetection(body, visited = new Set(), isRoot = true) {\n if (body === null || body === undefined) {\n return isRoot ? '' : 'null';\n }\n\n if (typeof body === 'string') {\n return isRoot ? body : JSON.stringify(body);\n }\n\n if (typeof body !== 'object') {\n return String(body);\n }\n\n if (visited.has(body)) {\n return '[Circular]';\n }\n visited.add(body);\n\n if (Array.isArray(body)) {\n const items = body.map(item => serializeForRawDetection(item, visited, false));\n return '[' + items.join(',') + ']';\n }\n\n // Object.getOwnPropertyNames includes non-enumerable own properties, so a __proto__ key\n // set via Object.defineProperty (as modern JSON.parse may do) is included in the output.\n const keys = Object.getOwnPropertyNames(body);\n const parts = keys.map(key => {\n const val = serializeForRawDetection(body[key], visited, false);\n return JSON.stringify(key) + ':' + val;\n });\n\n return '{' + parts.join(',') + '}';\n}\n\nexport function normalizeRequest(req, options = {}) {\n // Prefer a caller-provided verbatim body string (set by the fetch/node adapters):\n // it preserves literal keys like `__proto__` that JSON.stringify drops, which is\n // what makes prototype-pollution rules on `raw` robust. Fall back to a\n // reconstruction from the parsed body (the Express path, which has no raw text).\n const rawBody = typeof req._rawBody === 'string'\n ? req._rawBody\n : serializeForRawDetection(req.body ?? null);\n\n return {\n query: normalizeObject(req.query || {}, options),\n body: normalizeObject(req.body || {}, options),\n headers: normalizeObject(req.headers || {}, options),\n url: normalize(req.url || '', options),\n originalUrl: normalize(req.originalUrl || req.url || '', options),\n _rawBody: rawBody\n };\n}\n\n// Depth bound for the recursive walk: a pathologically deep object would otherwise overflow the\n// stack, and the engine's per-rule catch would swallow that into a fail-open. Beyond the bound the\n// sub-value is left un-normalized (still matched, just in its raw form) rather than crashing.\nconst MAX_NORMALIZE_DEPTH = 200;\n\nexport function normalizeObject(value, options = {}, depth = 0) {\n if (typeof value === 'string') {\n return normalize(value, options);\n }\n\n if (depth >= MAX_NORMALIZE_DEPTH) {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map(item => normalizeObject(item, options, depth + 1));\n }\n\n if (typeof value === 'object' && value !== null) {\n const result = {};\n\n for (const [key, val] of Object.entries(value)) {\n result[key] = normalizeObject(val, options, depth + 1);\n }\n\n return result;\n }\n\n return value;\n}\n\nexport function createMatchVariants(value) {\n if (typeof value !== 'string') {\n return [value];\n }\n\n const variants = new Set();\n\n variants.add(value);\n\n const urlDecoded = urlDecode(value);\n variants.add(urlDecoded);\n\n const htmlDecoded = htmlEntityDecode(value);\n variants.add(htmlDecoded);\n\n const bothDecoded = htmlEntityDecode(urlDecoded);\n variants.add(bothDecoded);\n\n const normalized = normalize(value);\n variants.add(normalized);\n\n variants.add(value.toLowerCase());\n variants.add(normalized.toLowerCase());\n\n return Array.from(variants);\n}\n\nexport const _testExports = {\n safeUrlDecode,\n serializeForRawDetection,\n HTML_ENTITIES,\n MAX_DECODE_ITERATIONS\n};\n","import { RequestResolver } from './request.js';\nimport { normalizeRequest } from './normalizer.js';\n\n// Catastrophic-backtracking shapes. Broad on purpose: a group whose inner content is quantified\n// (+, *, or {n,}) and is itself quantified — (a+)+, (\\w+)+, (.*)*, ([a-z]+)*, (ab+)+ — or an\n// alternation under an outer quantifier — (a|a)*, (x|y)+. A rule matching one of these is skipped\n// (safer than hanging the event loop). The `NEST` variants allow ONE level of nested parentheses in\n// the outer group so a quantified SUBGROUP is caught too (`((ab)+)+`, `((a|b)+)*`) — the earlier\n// `[^)]*` forms stopped at the first inner `)` and missed those. `.test()` scans every start offset,\n// so deeper nestings match at an inner window too.\nconst GRP = '(?:[^()]|\\\\([^()]*\\\\))*'; // group body allowing one level of nested parens\nconst REDOS_PATTERNS = [\n /\\([^)]*[+*}][^)]*\\)\\s*[+*]/,\n /\\([^)]*\\|[^)]*\\)\\s*[+*]/,\n new RegExp('\\\\(' + GRP + '[+*}]' + GRP + '\\\\)\\\\s*[+*]'), // nested quantified subgroup\n new RegExp('\\\\(' + GRP + '\\\\|' + GRP + '\\\\)\\\\s*[+*]') // nested alternation under a quantifier\n];\n\n// Report once when a rule's regex is rejected (ReDoS-shaped or unparseable). Unlike an unknown match\n// type, a rejected regex used to fail silently — so a delivered rule protected nothing and nobody knew.\nconst warnedRejectedPatterns = new Set();\nfunction warnRejectedPatternOnce(pattern) {\n const key = String(pattern);\n if (warnedRejectedPatterns.has(key)) return;\n warnedRejectedPatterns.add(key);\n console.warn(\n `[patchstack] rule_v2 regex pattern rejected (unsafe or invalid) — condition treated as no-match. ` +\n `The rule relying on it is NOT enforced: ${key}`\n );\n}\n\n// An absurdly long pattern is either a mistake or an attack on our own matcher; compiling and running\n// it on every request is unbounded work. The rule-bundle validator rejects these upstream — this is the\n// backstop for a caller-supplied bundle that never went through it.\nconst MAX_PATTERN_LENGTH = 1000;\n\nexport function safeRegExp(pattern) {\n if (!pattern) {\n return null;\n }\n if (typeof pattern !== 'string' || pattern.length > MAX_PATTERN_LENGTH) {\n return null;\n }\n\n for (const dangerous of REDOS_PATTERNS) {\n if (dangerous.test(pattern)) {\n return null;\n }\n }\n\n const match = pattern.match(/^\\/(.+?)\\/([gimsuy]*)$/s);\n if (!match) {\n return null;\n }\n\n try {\n return new RegExp(match[1], match[2]);\n } catch {\n return null;\n }\n}\n\n// ctype_*/is_numeric compare their character-class result to the rule's expected\n// `value`. Rules are almost always written `{ \"type\": \"ctype_digit\", \"value\": false }`\n// meaning \"flag when NOT of this class\" — so the result must be compared to matchVal,\n// not returned raw. Empty/absent values never match (mirrors engine-php's `$value != ''`\n// guard), otherwise every missing parameter false-positives a `value:false` rule.\n// When `value` is not a boolean (null/omitted) the legacy default of `true` is used, so\n// existing rules that relied on the raw class check are unaffected.\nfunction ctypeResult(strValue, isClass, matchVal) {\n if (strValue === '') {\n return false;\n }\n const expected = typeof matchVal === 'boolean' ? matchVal : true;\n return isClass === expected;\n}\n\n// Walk a dot-path into a decoded object and invoke `cb({ parent, key, value })` for every leaf it\n// reaches, fanning out over arrays at EVERY segment (not just the last) — so `orders.customers.email`\n// visits the email of each customer of each order, for arbitrary-length / arbitrarily-nested arrays.\n// `cb` gets the container + key so callers can either read (match) or set (redact) the leaf.\nexport function walkLeaves(node, segments, cb) {\n if (node === null || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n for (const el of node) walkLeaves(el, segments, cb);\n return;\n }\n const [head, ...rest] = segments;\n if (head === undefined || !Object.prototype.hasOwnProperty.call(node, head)) return;\n if (rest.length === 0) {\n const leaf = node[head];\n if (Array.isArray(leaf)) {\n for (let i = 0; i < leaf.length; i++) cb({ parent: leaf, key: i, value: leaf[i] });\n } else {\n cb({ parent: node, key: head, value: leaf });\n }\n } else {\n walkLeaves(node[head], rest, cb);\n }\n}\n\n// array_key_value: navigate `match.key` (a dot-path, or an array of paths) inside the decoded value\n// and run the nested `match.match` against every leaf it finds (fanning out over arrays at every\n// segment). Mirrors engine-php's recursive array_key_value handling.\nfunction arrayKeyValue(value, matchObj) {\n if (!matchObj || !matchObj.match || value === null || typeof value !== 'object') {\n return false;\n }\n const keys = Array.isArray(matchObj.key) ? matchObj.key : [matchObj.key];\n const sub = matchObj.match;\n\n for (const key of keys) {\n let matched = false;\n walkLeaves(value, String(key).split('.'), ({ value: leaf }) => {\n if (!matched && matchValue(sub.type, leaf, sub.value, sub)) matched = true;\n });\n if (matched) return true;\n }\n return false;\n}\n\n// Match types that operate on the whole container (not per-leaf): `isset` (presence) and\n// `array_in_array` / `array_key_value` (structural). Everything else is a scalar matcher that must\n// fan out over the leaves of an object/array value.\nconst WHOLE_VALUE_MATCH_TYPES = new Set(['isset', 'array_in_array', 'array_key_value']);\n\n// Iteratively collect every scalar (non-object) leaf of a structured value. Iterative + bounded\n// (depth and node caps) so a pathologically deep/large attacker payload STOPS at the bound rather\n// than throwing a RangeError that the per-rule catch would swallow into a fail-open bypass.\nfunction collectLeafValues(root, nodeCap = 20000, maxDepth = 1000) {\n const out = [];\n const stack = [[root, 0]];\n let visited = 0;\n while (stack.length) {\n const [node, depth] = stack.pop();\n if (node === null || node === undefined) continue;\n if (typeof node !== 'object') {\n out.push(node);\n continue;\n }\n if (depth >= maxDepth || visited >= nodeCap) continue;\n visited++;\n if (Array.isArray(node)) {\n for (let i = node.length - 1; i >= 0; i--) stack.push([node[i], depth + 1]);\n } else {\n for (const k of Object.keys(node)) stack.push([node[k], depth + 1]);\n }\n }\n return out;\n}\n\n// Emit a warning at most once per distinct key (keeps a persistent misconfiguration from spamming).\nconst warnedKeys = new Set();\nfunction warnOnce(key, message) {\n if (warnedKeys.has(key)) return;\n warnedKeys.add(key);\n console.warn(message);\n}\n\n// Report an unknown/removed match type once, so a rule referencing it is not silently\n// unenforced (ADR: \"unknown match type → skipped and logged, never silently passed\").\nconst warnedMatchTypes = new Set();\nfunction warnUnsupportedMatchType(type) {\n if (type == null || warnedMatchTypes.has(type)) {\n return;\n }\n warnedMatchTypes.add(type);\n console.warn(\n `[patchstack] Unsupported rule_v2 match type \"${type}\" — condition treated as no-match. ` +\n `Rules relying on it are not enforced by this engine.`\n );\n}\n\n// Internal / private / loopback / link-local / cloud-metadata host check, used by the\n// `internal_host` match type for SSRF egress rules. It CANONICALIZES the host before classifying —\n// a textual/prefix check is bypassable by alternate encodings (decimal/hex/octal IPv4, expanded or\n// IPv4-mapped IPv6), which is a classic SSRF evasion. Handles localhost / *.local / GCP metadata\n// names, every IPv4 spelling inet_aton accepts, and IPv6 loopback/link-local/unique-local/mapped.\nfunction isInternalHost(hostname) {\n if (!hostname) return false;\n let host = String(hostname).toLowerCase().replace(/^\\[|\\]$/g, '');\n host = host.replace(/%[^\\]]*$/, ''); // strip an IPv6 zone id (fe80::1%eth0)\n host = host.replace(/\\.$/, ''); // strip a single trailing dot (127.0.0.1.)\n\n if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.local')) return true;\n if (host === 'metadata.google.internal') return true;\n\n // IPv6 (contains a colon): expand to 8 groups, then classify on the canonical form.\n if (host.includes(':')) {\n const g = expandIPv6(host);\n if (!g) return false;\n const allZeroHi = g[0] === 0 && g[1] === 0 && g[2] === 0 && g[3] === 0 && g[4] === 0;\n if (allZeroHi && g[5] === 0 && g[6] === 0 && (g[7] === 0 || g[7] === 1)) return true; // ::, ::1 loopback\n if ((g[0] & 0xffc0) === 0xfe80) return true; // link-local fe80::/10\n if ((g[0] & 0xfe00) === 0xfc00) return true; // unique-local fc00::/7\n if (allZeroHi && (g[5] === 0xffff || g[5] === 0)) {\n // IPv4-mapped (::ffff:a.b.c.d) / IPv4-compatible (::a.b.c.d) — classify the embedded v4.\n return isPrivateV4Int((((g[6] << 16) >>> 0) | g[7]) >>> 0);\n }\n return false;\n }\n\n // IPv4 in any inet_aton spelling (dotted quad, shorthand, decimal, hex, octal).\n const v4 = parseIPv4ToInt(host);\n if (v4 !== null) return isPrivateV4Int(v4);\n return false;\n}\n\n// Private / loopback / link-local / this-host / CGNAT test on a 32-bit IPv4 integer.\nfunction isPrivateV4Int(n) {\n const a = (n >>> 24) & 0xff;\n const b = (n >>> 16) & 0xff;\n if (a === 127 || a === 10 || a === 0) return true; // loopback / private / this-host\n if (a === 169 && b === 254) return true; // link-local incl. 169.254.169.254 metadata\n if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12\n if (a === 192 && b === 168) return true; // 192.168.0.0/16\n if (a === 100 && b >= 64 && b <= 127) return true; // 100.64.0.0/10 CGNAT (Alibaba metadata 100.100.100.200)\n return false;\n}\n\n// inet_aton-style parse: 1–4 dot-separated parts, each decimal / 0x-hex / 0-octal; the final part\n// fills the remaining low bytes. Returns a uint32, or null if the host isn't a numeric IPv4 form\n// (so ordinary hostnames — which contain letters outside [0-9a-fx] or other chars — return null).\nfunction parseIPv4ToInt(host) {\n if (!/^[0-9a-fx]+(\\.[0-9a-fx]+)*$/i.test(host)) return null;\n const parts = host.split('.');\n if (parts.length > 4) return null;\n const nums = [];\n for (const p of parts) {\n let n;\n if (/^0x[0-9a-f]+$/i.test(p)) n = parseInt(p, 16);\n else if (/^0[0-7]+$/.test(p)) n = parseInt(p, 8);\n else if (/^[0-9]+$/.test(p)) n = parseInt(p, 10);\n else return null; // e.g. a bare hex like \"7f\" (not inet_aton-valid without 0x)\n if (!Number.isInteger(n) || n < 0) return null;\n nums.push(n);\n }\n const last = nums.length - 1;\n let value = 0;\n for (let i = 0; i < last; i++) {\n if (nums[i] > 0xff) return null;\n value += nums[i] * 2 ** (8 * (3 - i));\n }\n const maxLast = 2 ** (8 * (5 - nums.length)) - 1; // the final part fills the remaining low bytes\n if (nums[last] > maxLast) return null;\n value += nums[last];\n if (value < 0 || value > 0xffffffff) return null;\n return value >>> 0;\n}\n\n// Expand an IPv6 string (any `::` compression, optional embedded IPv4 tail) to 8 numeric groups,\n// or null if it isn't valid IPv6. Lets the classifier compare the canonical form, not a string prefix.\nfunction expandIPv6(host) {\n if (!host.includes(':')) return null;\n let s = host;\n // Embedded IPv4 tail (::ffff:127.0.0.1) → convert the dotted part to two hex groups.\n const lastColon = s.lastIndexOf(':');\n const tail = s.slice(lastColon + 1);\n if (tail.includes('.')) {\n const v4 = parseIPv4ToInt(tail);\n if (v4 === null) return null;\n s = s.slice(0, lastColon + 1) + ((v4 >>> 16) & 0xffff).toString(16) + ':' + (v4 & 0xffff).toString(16);\n }\n const halves = s.split('::');\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(':') : [];\n const back = halves.length === 2 ? (halves[1] ? halves[1].split(':') : []) : null;\n let groups;\n if (back === null) {\n groups = head;\n } else {\n const fill = 8 - head.length - back.length;\n if (fill < 0) return null;\n groups = [...head, ...Array(fill).fill('0'), ...back];\n }\n if (groups.length !== 8) return null;\n const out = [];\n for (const g of groups) {\n if (!/^[0-9a-f]{1,4}$/i.test(g)) return null;\n out.push(parseInt(g, 16));\n }\n return out;\n}\n\n// Route/method scope for a rule's optional `when: { method, path }`. Fail-open: if the scope can't\n// be evaluated, the rule still applies (never silently suppress a rule).\nfunction ruleAppliesTo(when, resolver) {\n try {\n if (when.method) {\n const methods = (Array.isArray(when.method) ? when.method : [when.method]).map((m) => String(m).toUpperCase());\n const actual = String(resolver.resolve('server.REQUEST_METHOD')[0] ?? 'GET').toUpperCase();\n if (!methods.includes(actual)) return false;\n }\n if (when.path) {\n const uri = String(resolver.resolve('server.REQUEST_URI')[0] ?? '/');\n if (!pathMatches(String(when.path), uri.split('?')[0])) return false;\n }\n return true;\n } catch {\n return true;\n }\n}\n\n// `when.path`: an exact path, a glob with `*`, or an explicit `/regex/` (starts & ends with `/`).\nfunction pathMatches(pattern, path) {\n if (pattern.length > 2 && pattern.startsWith('/') && pattern.endsWith('/')) {\n const re = safeRegExp(pattern);\n return re ? re.test(path) : false;\n }\n if (pattern.includes('*')) {\n const re = safeRegExp('/^' + pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '[^?]*') + '$/');\n return re ? re.test(path) : false;\n }\n return path === pattern;\n}\n\n// Drop a default port so `app.com:443` and `app.com` compare equal (a real proxy shape), without\n// dropping a non-default port (so genuine cross-port stays distinguishable).\nfunction normalizeDefaultPort(host) {\n return String(host).toLowerCase().replace(/:(?:80|443)$/, '');\n}\n\n// CSRF primitive: does the request come from a different origin than its own Host? Lenient only when\n// the Origin AND Referer are TRULY ABSENT (a non-browser client). A present-but-opaque `Origin: null`\n// / empty / unparseable value is NOT same-origin — it's the sandboxed-iframe / opaque-origin signal a\n// CSRF attacker supplies, so it is treated as cross-origin.\nfunction isCrossOrigin(resolver) {\n try {\n const host = normalizeDefaultPort(String(resolver.resolve('server.HTTP_HOST')[0] ?? ''));\n if (!host) return false;\n const originRaw = resolver.resolve('server.HTTP_ORIGIN')[0];\n const hasOrigin = originRaw !== undefined && originRaw !== null;\n const src = hasOrigin ? originRaw : resolver.resolve('server.HTTP_REFERER')[0];\n if (src === undefined || src === null) return false; // both absent → lenient\n const s = String(src).trim();\n if (hasOrigin && (s === '' || s.toLowerCase() === 'null')) return true; // present but opaque → cross-origin\n const srcHost = hostFromUrl(s);\n if (srcHost === null) return hasOrigin; // present-but-unparseable Origin → treat as cross-origin\n return normalizeDefaultPort(srcHost) !== host;\n } catch {\n return false;\n }\n}\n\nfunction hostFromUrl(value) {\n try {\n return new URL(value).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n// Open-redirect primitive (response phase): a 3xx whose Location header points to a DIFFERENT origin\n// than the request's own Host. A relative Location (same-origin) never matches. Needs the request\n// Host, which the response phase threads in via reqCtx. Lenient: no Location, no request Host, or a\n// same-origin / relative target → not flagged (so it can't false-positive without the signal).\nfunction isOffOriginRedirect(resolver) {\n try {\n const status = Number(resolver.resolve('response.status')[0] ?? 0);\n if (status < 300 || status >= 400) return false;\n const location = resolver.resolve('response.header.location')[0];\n if (!location) return false;\n const host = String(resolver.resolve('server.HTTP_HOST')[0] ?? '').toLowerCase();\n if (!host) return false;\n // Resolve the Location the way a browser would before comparing hosts: strip TAB/CR/LF and\n // normalize backslashes to forward slashes (browsers do), then resolve against the request origin\n // as a base. A relative `/path` resolves to our own host (not flagged); a protocol-relative\n // `//evil.com` or backslash `/\\evil.com` resolves off-origin (flagged) — the canonical\n // open-redirect payloads that a base-less `new URL()` used to treat as \"relative & safe\".\n const loc = String(location).replace(/[\\t\\r\\n]/g, '').replace(/\\\\/g, '/');\n let target;\n try {\n target = new URL(loc, 'http://' + host).host.toLowerCase();\n } catch {\n return false; // unresolvable even with a base → not a redirect we can judge\n }\n return normalizeDefaultPort(target) !== normalizeDefaultPort(host);\n } catch {\n return false;\n }\n}\n\n// CORS-reflection primitive (response phase): the response allows credentials AND lets any origin\n// read it — either `Access-Control-Allow-Origin: *`, or it reflects the caller's own Origin (so\n// every origin is allowed) — rather than a fixed allowlisted origin. That combination lets any\n// malicious site read the authenticated response. Needs the request Origin (threaded via reqCtx).\n// Lenient: credentials not allowed, no ACAO, or a fixed (non-reflected, non-*) ACAO → not flagged.\nfunction isReflectedCorsWithCredentials(resolver) {\n try {\n const acac = String(resolver.resolve('response.header.access-control-allow-credentials')[0] ?? '').toLowerCase();\n if (acac !== 'true') return false; // only dangerous when credentials are allowed\n const acao = String(resolver.resolve('response.header.access-control-allow-origin')[0] ?? '');\n if (!acao) return false;\n if (acao === '*') return true; // wildcard + credentials\n if (acao.toLowerCase() === 'null') return true; // `null` + credentials is readable from a sandboxed iframe (Origin: null)\n const origin = String(resolver.resolve('server.HTTP_ORIGIN')[0] ?? '');\n if (!origin) return false;\n return acao === origin; // ACAO echoes the caller's Origin → any origin is allowed\n } catch {\n return false;\n }\n}\n\n// `matchObj` is the full match object; needed by types that read sibling fields\n// (array_key_value reads `key`/`match`). Optional so direct callers/tests can keep\n// using the (type, value, matchVal) signature.\nexport function matchValue(type, value, matchVal, matchObj) {\n if (value === null || value === undefined) {\n if (type === 'isset') {\n return false;\n }\n return false;\n }\n\n // Guard the coercion: String() on a pathologically deep array/object can throw RangeError\n // (stack overflow). Catching it here keeps a hostile nested value from failing the rule open.\n let strValue;\n if (typeof value === 'string') {\n strValue = value;\n } else {\n try {\n strValue = String(value);\n } catch {\n strValue = '';\n }\n }\n\n switch (type) {\n case 'equals':\n return strValue == matchVal;\n\n case 'equals_strict':\n // Type-strict by design (`'1' !== 1`). A rule that wants loose matching should use `equals`.\n return strValue === matchVal;\n\n case 'contains':\n // engine-php exposes `stripos` as an alias of `contains`.\n case 'stripos': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return lower.includes(target);\n }\n\n case 'not_contains': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return !lower.includes(target);\n }\n\n case 'regex': {\n const regex = safeRegExp(matchVal);\n if (!regex) {\n warnRejectedPatternOnce(matchVal);\n return false;\n }\n return regex.test(strValue);\n }\n\n case 'more_than':\n return Number(strValue) > Number(matchVal);\n\n case 'less_than':\n return Number(strValue) < Number(matchVal);\n\n case 'ctype_digit':\n return ctypeResult(strValue, /^\\d+$/.test(strValue), matchVal);\n\n case 'ctype_alnum':\n return ctypeResult(strValue, /^[\\w$\\u0080-\\uFFFF]+$/.test(strValue), matchVal);\n\n case 'is_numeric':\n return ctypeResult(strValue, !isNaN(strValue) && strValue.trim() !== '', matchVal);\n\n case 'isset':\n return true;\n\n case 'in_array': {\n const arr = (Array.isArray(matchVal) ? matchVal : [matchVal]).map((x) => String(x));\n return arr.includes(strValue);\n }\n\n case 'not_in_array': {\n const arr = (Array.isArray(matchVal) ? matchVal : [matchVal]).map((x) => String(x));\n return !arr.includes(strValue);\n }\n\n case 'array_in_array': {\n if (!Array.isArray(value) || !Array.isArray(matchVal)) {\n return false;\n }\n const target = matchVal.map((x) => String(x));\n return value.some((v) => target.includes(String(v)));\n }\n\n case 'hostname': {\n try {\n const url = new URL(strValue);\n return url.hostname === matchVal;\n } catch {\n return false;\n }\n }\n\n case 'internal_host':\n // SSRF egress: private / loopback / link-local / cloud-metadata destinations.\n return isInternalHost(strValue);\n\n case 'quotes':\n // engine-php exposes `inline_js_xss` as an alias of `quotes`.\n case 'inline_js_xss':\n return /['\"]/.test(strValue);\n\n case 'inline_xss':\n // Attribute-breakout heuristic: a quote AND a `>` or `=` (matches engine-php).\n return /['\"]/.test(strValue) && /[>=]/.test(strValue);\n\n case 'ctype_special': {\n // engine-php strips space, `_`, `-`, `,` then applies the alnum/unicode check,\n // and compares the result to `value` (usually false = \"flag when NOT clean\").\n const stripped = strValue.replace(/[ _\\-,]/g, '');\n return ctypeResult(strValue, /^[\\w$€-￿]*$/.test(stripped), matchVal);\n }\n\n case 'array_key_value':\n // Navigate `match.key` (dot-path, or array of paths) inside the decoded value and\n // run the nested `match.match` against it — e.g. a json_decode'd body where a\n // specific key must hold a specific value.\n return arrayKeyValue(value, matchObj);\n\n // file_contains (uploaded-file content scanning) is NOT WordPress-specific, but is\n // not yet implemented in the JS engine (needs multipart body access). Kept as an\n // explicit, documented no-match rather than silently hitting `default`.\n case 'file_contains':\n return false;\n\n // WordPress-only match types (current_user_cannot, general_xss, getShortcodeAtts,\n // getBlockAtts) have been removed: they depend on WP capabilities, wp_kses_post, and\n // the shortcode/block parsers, and have no meaning in a JS app. They now fall through\n // here and are reported once instead of pretending to be a no-match.\n default:\n warnUnsupportedMatchType(type);\n return false;\n }\n}\n\nexport class RuleEngine {\n #rules;\n #whitelists;\n #whitelistKeys;\n #onError;\n #reportedErrors = new Set();\n\n constructor({ firewall = [], whitelists = [], whitelist_keys = {}, onError } = {}) {\n this.#rules = firewall;\n this.#whitelists = whitelists;\n this.#whitelistKeys = whitelist_keys;\n this.#onError = onError;\n // A whitelist with no `rule_id` suppresses EVERY rule when its (attacker-reachable) condition\n // trips — almost never intended. And `whitelist_keys` is accepted but not implemented. Warn once\n // for each so a misconfiguration that silently weakens the firewall is visible to the operator.\n if (Array.isArray(whitelists) && whitelists.some((w) => w && Array.isArray(w.rule_v2) && !w.rule_id)) {\n warnOnce(\n 'whitelist-no-rule-id',\n '[patchstack] a whitelist has no `rule_id` — it suppresses ALL rules when it matches. ' +\n 'Scope each whitelist to a specific rule_id, and key it only on values an attacker cannot set.'\n );\n }\n if (whitelist_keys && typeof whitelist_keys === 'object' && Object.keys(whitelist_keys).length > 0) {\n warnOnce('whitelist-keys-unimplemented', '[patchstack] `whitelist_keys` is not implemented and has no effect.');\n }\n }\n\n // A mitigation engine must never take down the app it protects: any error while\n // evaluating a rule is reported and the request is allowed through (fail open). A\n // malformed rule is skipped without aborting the rest of the ruleset.\n #reportError(err) {\n if (this.#onError) {\n this.#onError(err);\n return;\n }\n // Default: log once per distinct message so a persistently-broken rule doesn't\n // spam per request (never silently swallow — hosts can pass `onError` for structured logging).\n const key = err && err.message ? err.message : String(err);\n if (this.#reportedErrors.has(key)) {\n return;\n }\n this.#reportedErrors.add(key);\n console.error('[patchstack] rule evaluation error (rule skipped, request allowed):', err);\n }\n\n evaluate(req) {\n let normalizedReq;\n let resolver;\n try {\n normalizedReq = { ...req, ...normalizeRequest(req) };\n resolver = new RequestResolver(normalizedReq);\n } catch (err) {\n this.#reportError(err);\n return { blocked: false, rule: null, message: null }; // fail open\n }\n\n for (const rule of this.#rules) {\n try {\n // Route/method scope: a rule may declare `when: { method, path }` to apply only on matching\n // requests (so an auto-generated per-endpoint rule doesn't fire elsewhere).\n if (rule.when && !ruleAppliesTo(rule.when, resolver)) {\n continue;\n }\n\n const conditions = rule.rule_v2;\n\n if (!Array.isArray(conditions) || conditions.length === 0) {\n continue;\n }\n\n if (this.#evaluateRule(conditions, resolver)) {\n if (this.#isWhitelisted(normalizedReq, rule)) {\n continue;\n }\n\n return {\n blocked: true,\n rule,\n message: rule.message ?? `Blocked by Patchstack WAF rule: ${rule.title ?? rule.id}`\n };\n }\n } catch (err) {\n // Skip this rule and keep evaluating the rest — one bad rule never blocks a\n // request nor shadows the rules after it.\n this.#reportError(err);\n }\n }\n\n return { blocked: false, rule: null, message: null };\n }\n\n #evaluateRule(conditions, resolver) {\n const inclusiveConditions = conditions.filter(c => c.inclusive);\n const nonInclusiveConditions = conditions.filter(c => !c.inclusive);\n\n if (inclusiveConditions.length > 0) {\n let inclusiveHits = 0;\n\n for (const condition of inclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n inclusiveHits++;\n }\n }\n\n if (inclusiveHits === inclusiveConditions.length) {\n return true;\n }\n }\n\n for (const condition of nonInclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n\n #evaluateCondition(condition, resolver) {\n const { parameter, match, mutations } = condition;\n\n if (parameter === 'rules' && Array.isArray(condition.rules)) {\n return this.#evaluateRule(condition.rules, resolver);\n }\n\n // `cross_origin` needs the request as a whole (Origin/Referer vs Host), not a single resolved\n // parameter — it's the CSRF primitive: true (→ block) when the request comes from another origin.\n if (match && match.type === 'cross_origin') {\n return isCrossOrigin(resolver);\n }\n\n // `off_origin` (response phase): true (→ block) when a 3xx redirects to a different origin than\n // the request Host — the open-redirect primitive. Like cross_origin, it needs the whole resolver.\n if (match && match.type === 'off_origin') {\n return isOffOriginRedirect(resolver);\n }\n\n // `cors_reflected` (response phase): true (→ block) when the response allows credentials and\n // reflects the caller's Origin (or uses `*`) — the CORS-misconfiguration primitive. Needs the\n // whole resolver (request Origin vs response ACAO/ACAC).\n if (match && match.type === 'cors_reflected') {\n return isReflectedCorsWithCredentials(resolver);\n }\n\n // `parameter` may be an array (e.g. [\"get.action\",\"post.action\"]) — the rule_v2 format\n // uses these pervasively to mean \"any of these sources\". Resolve each and OR the\n // candidate values together. (A bare string resolves as a single source.)\n const params = Array.isArray(parameter) ? parameter : [parameter];\n const values = params.flatMap((p) => resolver.resolve(p));\n\n if (values.length === 0) {\n return false;\n }\n\n for (let value of values) {\n if (mutations) {\n value = resolver.applyMutations(mutations, value);\n }\n\n // array_key_value inspects the object as a whole (it navigates a key path),\n // so it must not go through the per-value object iteration below.\n if (match.type === 'array_key_value') {\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n continue;\n }\n\n // A structured (object / array-of-object) value must be inspected at every leaf: a payload\n // nested deeper than a scalar rule expects would otherwise stringify to \"[object Object]\" and\n // evade the match, while the app still reads the live value. Whole-value match types\n // (isset / array_in_array) see the container; scalar matchers fan out over all leaves.\n if (typeof value === 'object' && value !== null) {\n if (WHOLE_VALUE_MATCH_TYPES.has(match.type)) {\n if (matchValue(match.type, value, match.value, match)) return true;\n } else {\n for (const leaf of collectLeafValues(value)) {\n if (matchValue(match.type, leaf, match.value, match)) return true;\n }\n }\n continue;\n }\n\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n }\n\n return false;\n }\n\n #isWhitelisted(req, rule) {\n if (!this.#whitelists || this.#whitelists.length === 0) {\n return false;\n }\n\n for (const whitelist of this.#whitelists) {\n if (!Array.isArray(whitelist.rule_v2)) {\n continue;\n }\n\n if (whitelist.rule_id && whitelist.rule_id !== rule.id) {\n continue;\n }\n\n const resolver = new RequestResolver(req);\n if (this.#evaluateRule(whitelist.rule_v2, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n}\n\nexport const _testExports = { matchValue, safeRegExp, isInternalHost };\n","import { safeBaseUrl } from '../safe-origin.js';\nimport { pulseAuthHeader } from '../../pulse-token.js';\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com/monitor/pulse';\nconst DEFAULT_CACHE_TTL = 300_000;\n// Randomly shorten the effective TTL by up to this fraction so many long-lived clients don't all\n// revalidate on the same tick (spreads load / avoids a thundering herd against the rules API).\nconst JITTER_FRACTION = 0.1;\n\n// Per-site rules client for Pulse (npm/JS) apps. Public endpoint — the site UUID is the only\n// credential, passed in the path. Fail-open: any error returns success:false + empty rules so\n// createProtection falls back to the disk cache or the bundled rules.\n//\n// Conditional fetch: pass a prior `etag` (persisted with the last cached bundle) and the client\n// sends `If-None-Match`; a 304 returns `{ notModified: true }` so the caller reuses its cache\n// without re-downloading, and a 200 returns the new bundle plus its `etag` to persist. This is a\n// no-op until the rules API emits an ETag and honors If-None-Match — with no ETag in the response\n// the client simply behaves as before (a full fetch every refresh).\nexport class PulseRuleClient {\n #siteUuid;\n #timeoutMs;\n #baseUrl;\n #cacheTtl;\n #cache = null;\n #cacheTime = null;\n #ttlEffective = 0;\n #etag;\n #pulseAuth;\n\n constructor({ siteUuid, baseUrl, cacheTtl, etag, timeoutMs, pulseAuth } = {}) {\n // Bounded so app STARTUP can't hang on a slow API: hosted platforms fail a deploy whose health\n // check is slow, and we always have a cache/bundled fallback to boot from.\n this.#timeoutMs = Number(timeoutMs) > 0 ? Number(timeoutMs) : 30_000;\n this.#siteUuid = siteUuid ?? process.env.PATCHSTACK_SITE_UUID;\n // Rules are executed policy — refuse a plaintext remote override (see safe-origin.js).\n this.#baseUrl = safeBaseUrl(baseUrl ?? process.env.PATCHSTACK_PULSE_RULES_URL, DEFAULT_BASE_URL, 'rule endpoint');\n this.#cacheTtl = Number.isFinite(cacheTtl) && cacheTtl > 0 ? cacheTtl : DEFAULT_CACHE_TTL;\n this.#etag = etag ?? null;\n this.#pulseAuth = pulseAuth ?? null;\n if (!this.#siteUuid) {\n throw new Error('Patchstack site UUID is required. Pass { siteUuid } or set PATCHSTACK_SITE_UUID.');\n }\n }\n\n async getRules() {\n const now = Date.now();\n if (this.#cache && this.#cacheTime !== null && now - this.#cacheTime < this.#ttlEffective) {\n return this.#cache;\n }\n const url = `${this.#baseUrl}/rules/${encodeURIComponent(this.#siteUuid)}`;\n try {\n // Unauthenticated when no credential resolved, or when the exchange\n // fails — the server still accepts the UUID, and protection must never\n // hinge on getting a token.\n const headers = {\n Accept: 'application/json',\n ...(await pulseAuthHeader(\n { pulseAuth: this.#pulseAuth, endpoint: this.#baseUrl, timeoutMs: this.#timeoutMs },\n fetch,\n )),\n };\n if (this.#etag) headers['If-None-Match'] = this.#etag;\n const response = await fetch(url, { method: 'GET', headers, signal: AbortSignal.timeout(this.#timeoutMs) });\n\n if (response.status === 304) {\n this.#touch(now); // revalidated — reset the clock (fresh jitter)\n return this.#cache ?? { success: true, notModified: true, etag: this.#etag, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n if (!response.ok) {\n return { success: false, error: `API returned ${response.status}`, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n const data = await response.json();\n // A 200 that isn't a genuine rule envelope (schema drift, a proxy/interstitial page, an\n // {error} body) must not be treated as \"no rules\". Report failure so the caller falls back to\n // the cache / bundled rules rather than caching an empty bundle.\n if (!data || !Array.isArray(data.firewall)) {\n return { success: false, error: 'unexpected response shape (no firewall array)', firewall: [], whitelists: [], whitelist_keys: {} };\n }\n const result = {\n success: true,\n notModified: false,\n etag: response.headers?.get?.('etag') ?? null,\n firewall: data.firewall,\n whitelists: Array.isArray(data.whitelists) ? data.whitelists : [],\n whitelist_keys: data.whitelist_keys ?? {},\n ...enforcementField(data),\n };\n this.#cache = result;\n this.#etag = result.etag;\n this.#touch(now);\n return result;\n } catch (err) {\n return {\n success: false,\n error: err.name === 'TimeoutError' ? 'Request timed out' : err.message,\n firewall: [], whitelists: [], whitelist_keys: {},\n };\n }\n }\n\n #touch(now) {\n this.#cacheTime = now;\n this.#ttlEffective = this.#cacheTtl - Math.floor(Math.random() * this.#cacheTtl * JITTER_FRACTION);\n }\n\n clearCache() {\n this.#cache = null;\n this.#cacheTime = null;\n this.#etag = null;\n }\n}\n\n/** @param {unknown} data @returns {{ enforcement?: 'block'|'dry-run' }} */\nfunction enforcementField(data) {\n if (!data || typeof data !== 'object') return {};\n const v = data.enforcement ?? data.mode;\n return v === 'block' || v === 'dry-run' ? { enforcement: v } : {};\n}\n","// Web Fetch adapter — runs the node-waf engine on any runtime where a request is a\n// WHATWG `Request` and a handler returns a `Response`: Cloudflare Workers, Bun, Deno,\n// Hono, Next.js edge, and TanStack Start's `server.ts`. This is the surface AI-builder\n// apps actually deploy to, and where require-based instrumentation can't reach.\n//\n// The engine already consumes a runtime-neutral request shape; this adapter builds that\n// shape from a `Request`, so no engine changes are needed beyond keeping the hot path\n// free of Node-only APIs.\nimport { RuleEngine } from './engine.js';\n\n// Cap how much request body we buffer for inspection. A larger body is left UNSCANNED\n// (fail-open) rather than buffered into memory — matches the node adapter's maxBodyBytes.\nconst MAX_BODY_BYTES = 1024 * 1024;\n\n// Build the engine's request shape from a WHATWG Request. The body is read from a\n// CLONE so the downstream handler still receives an intact request.\nexport async function fromFetchRequest(request, options = {}) {\n const maxBodyBytes = options.maxBodyBytes ?? MAX_BODY_BYTES;\n const url = new URL(request.url);\n const method = (request.method || 'GET').toUpperCase();\n\n const headers = {};\n request.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n let rawBody = '';\n if (method !== 'GET' && method !== 'HEAD') {\n rawBody = await readCappedText(request, maxBodyBytes);\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n let files;\n if (rawBody) {\n const parsed = parseBody(rawBody, contentType);\n body = parsed.body;\n files = parsed.files;\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n files,\n headers,\n ip: forwarded.split(',')[0].trim(),\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify\n // drops, so prototype-pollution rules on `raw` are robust.\n _rawBody: rawBody\n };\n}\n\n// Read a request body as text for inspection. Memory is bounded by a hard ceiling (4× the scan\n// cap): a body whose declared Content-Length exceeds it is left UNSCANNED (fail-open). Within the\n// ceiling, an oversize body is TRUNCATED to `max` and its prefix is still scanned — so a\n// front-loaded payload is caught — rather than being discarded outright. Reads a clone so the\n// downstream handler keeps an intact body. (`max` is compared in bytes against Content-Length; the\n// prefix slice is by character, which can only over-scan a multibyte body — the safe direction.)\nasync function readCappedText(request, max) {\n // Do NOT skip scanning based on a declared Content-Length: an attacker can declare a huge length\n // (or none) to dodge inspection while sending a small exploit body. Always stream-scan the prefix\n // up to `max` (buffering is bounded to `max`; the rest is drained but not retained). Anything past\n // the cap is unscanned — the documented prefix-scan tradeoff — but the body is never skipped whole.\n let clone;\n try {\n clone = request.clone();\n } catch {\n return '';\n }\n\n // Stream the read so a body WITHOUT a Content-Length can't buffer unbounded: retain only up to the\n // scan cap (`max`) for inspection, but keep draining to completion so the original request stays\n // intact. A front-loaded payload is still caught; anything past the cap is not scanned (same\n // partial-scan tradeoff as before, now with a hard memory bound regardless of Content-Length).\n const body = clone.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks = [];\n let buffered = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value || buffered >= max) continue; // keep draining, stop buffering past the cap\n const take = Math.min(value.byteLength, max - buffered);\n chunks.push(take === value.byteLength ? value : value.subarray(0, take));\n buffered += take;\n }\n } catch {\n return '';\n }\n try {\n return new TextDecoder().decode(concatChunks(chunks, buffered));\n } catch {\n return '';\n }\n }\n\n // No readable stream — fall back to text() with the post-read guard.\n try {\n const text = await clone.text();\n return text.length > max ? text.slice(0, max) : text;\n } catch {\n return '';\n }\n}\n\nfunction concatChunks(chunks, total) {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n\n// Minimal multipart/form-data parser: enough to expose field names + values (so `post.<field>`\n// and `raw` rules match uploads, e.g. a `__proto__` field name) and file metadata (filename via\n// `files.<field>`). We only need the textual structure, not the binary file contents.\n// Parse a request body into { body, files } for parameter-scoped rules. Content-type detection is\n// deliberately permissive: many AI-built apps `JSON.parse(await req.text())` regardless of the\n// declared type, so a JSON body arriving as `application/vnd.api+json`, `application/ld+json`,\n// `text/plain`, `application/csp-report`, or with NO content-type must still populate post.<field>\n// (otherwise a field-scoped rule silently resolves to nothing). Unrecognized/binary bodies stay `{}`\n// and are still matchable via `raw`.\nexport function parseBody(rawBody, contentType) {\n const ct = String(contentType || '').toLowerCase();\n const isJson = ct.includes('application/json') || /\\+json\\b/.test(ct);\n const isForm = ct.includes('application/x-www-form-urlencoded');\n const isMultipart = ct.includes('multipart/form-data');\n // \"ambiguous\" = a type an app commonly parses as JSON/form even though it isn't declared as such.\n const isAmbiguous = ct === '' || ct.startsWith('text/plain') || ct.includes('csp-report') || ct.includes('/json');\n\n if (isMultipart) {\n const boundary = /boundary=(\"?)([^\";]+)\\1/i.exec(contentType)?.[2];\n if (boundary) return parseMultipart(rawBody, boundary);\n return { body: {}, files: undefined };\n }\n if (isForm) {\n const body = {};\n for (const [k, v] of new URLSearchParams(rawBody)) body[k] = k in body ? [].concat(body[k], v) : v;\n return { body, files: undefined };\n }\n if (isJson || isAmbiguous) {\n try {\n const parsed = JSON.parse(rawBody);\n if (parsed && typeof parsed === 'object') return { body: parsed, files: undefined };\n } catch {\n /* not JSON — leave body empty; `raw`/`all` still see the verbatim text */\n }\n }\n return { body: {}, files: undefined };\n}\n\nexport function parseMultipart(rawBody, boundary) {\n const body = {};\n const files = {};\n for (const part of rawBody.split('--' + boundary)) {\n // Tolerate both CRLF and LF-only line endings (some clients/proxies send bare \\n).\n const sep = /\\r?\\n\\r?\\n/.exec(part);\n if (!sep) continue;\n const rawHeaders = part.slice(0, sep.index);\n const disposition = /content-disposition:[^\\r\\n]*/i.exec(rawHeaders)?.[0];\n if (!disposition) continue;\n const name = /name=\"([^\"]*)\"/i.exec(disposition)?.[1];\n if (name == null) continue;\n const content = part.slice(sep.index + sep[0].length).replace(/\\r?\\n$/, '');\n const filename = /filename=\"([^\"]*)\"/i.exec(disposition)?.[1];\n if (filename !== undefined) {\n // Capture the part's declared content-type and CONTENT (not just the filename), so rules can\n // inspect an upload's bytes (files.<name>.content) and detect a declared-vs-actual type\n // mismatch (files.<name>.mismatch). The content rides inside the already-capped rawBody.\n const partType = /content-type:\\s*([^\\r\\n;]+)/i.exec(rawHeaders)?.[1]?.trim() || '';\n const file = { filename, type: partType, content };\n files[name] = name in files ? [].concat(files[name], file) : file;\n } else {\n body[name] = name in body ? [].concat(body[name], content) : content;\n }\n }\n return { body, files };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlockResponse(result) {\n return new Response(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n }),\n { status: 403, headers: { 'content-type': 'application/json' } }\n );\n}\n\n/**\n * Returns a guard `(request) => Promise<Response|null>`. A `Response` means blocked;\n * `null` means allowed — the caller should proceed to its handler. Accepts either a\n * rules bundle (`{ firewall, whitelists, whitelist_keys }`) or a `RuleEngine` instance.\n * Fails open: a rule that throws never blocks the request.\n */\nexport function createFetchMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function'\n ? rulesData\n : new RuleEngine(rulesData);\n\n const guard = async (request) => {\n let req;\n let result;\n try {\n req = await fromFetchRequest(request); // shaping inside the try — a bad/relative request.url must fail open\n result = engine.evaluate(req);\n } catch (err) {\n if (options.onError) {\n options.onError(err);\n }\n return null; // fail open\n }\n\n if (result.blocked) {\n if (options.onBlock) {\n options.onBlock({\n rule: result.rule,\n message: result.message,\n request: { method: req.method, url: req.url, ip: req.ip }\n });\n }\n return (options.response || defaultBlockResponse)(result);\n }\n\n return null;\n };\n\n guard.engine = engine;\n return guard;\n}\n\n/**\n * Wrap a fetch handler so every request is screened first. One-line hook:\n * export default { fetch: wrapFetchHandler(app.fetch, rulesData) }\n */\nexport function wrapFetchHandler(handler, rulesData, options = {}) {\n const guard = createFetchMiddleware(rulesData, options);\n const wrapped = async (request, ...rest) => {\n const blocked = await guard(request);\n return blocked ?? handler(request, ...rest);\n };\n wrapped.engine = guard.engine;\n return wrapped;\n}\n","// Raw Node.js / Connect adapter — for traditional Node servers where the request is a\n// `http.IncomingMessage` and the body is NOT already parsed (plain `http`, Connect,\n// Express without a body-parser, Fastify via its Node req). It buffers the body once,\n// builds the engine's request shape, and blocks or calls `next()`.\n//\n// This complements the Express `createMiddleware` (which assumes `req.body`/`req.query`\n// are already populated) and the Web-Fetch adapter (Workers/edge). Mount it FIRST, before\n// any body-parser — it consumes the stream and exposes the parsed body as `req.body`.\nimport { RuleEngine } from './engine.js';\nimport { parseBody } from './fetch.js';\n\n// Build the engine's request shape from a Node IncomingMessage + its raw body text.\nexport function fromNodeRequest(req, rawBody = '') {\n const method = (req.method || 'GET').toUpperCase();\n\n const headers = {};\n for (const [key, value] of Object.entries(req.headers || {})) {\n headers[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value;\n }\n\n // An unusual Host header or req.url can make `new URL` throw; shaping must never crash the\n // request (fail-open), so fall back to a safe base.\n const host = headers.host || 'localhost';\n let url;\n try {\n url = new URL(req.url || '/', `http://${host}`);\n } catch {\n try {\n url = new URL(req.url || '/', 'http://localhost');\n } catch {\n url = new URL('http://localhost/');\n }\n }\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n let files;\n if (rawBody) {\n // Same permissive content-type handling as the fetch adapter (+json / text/plain / no-CT bodies\n // still populate post.<field>; multipart exposes field + file metadata) on a raw-Node server too.\n const parsed = parseBody(rawBody, contentType);\n body = parsed.body;\n files = parsed.files;\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n files,\n headers,\n ip: forwarded.split(',')[0].trim() || (req.socket && req.socket.remoteAddress) || '',\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify drops.\n _rawBody: rawBody\n };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlock(res, result) {\n res.statusCode = 403;\n res.setHeader('content-type', 'application/json');\n res.end(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n })\n );\n}\n\n/**\n * Connect/Express-style middleware `(req, res, next)` that buffers the body itself.\n * Accepts a `RuleEngine` instance or a `{ firewall, whitelists, whitelist_keys }` bundle.\n * Fails open: an engine error (or oversized body) never blocks the request.\n * Options: `{ maxBodyBytes = 1MiB, onBlock, onError, response }`.\n */\nexport function createNodeMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function' ? rulesData : new RuleEngine(rulesData);\n const maxBytes = options.maxBodyBytes ?? 1024 * 1024;\n\n return function guard(req, res, next) {\n const chunks = [];\n let size = 0;\n let overflow = false;\n\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n\n req.on('error', (err) => next(err));\n\n req.on('end', () => {\n let result;\n let shaped;\n try {\n const rawBody = overflow ? '' : Buffer.concat(chunks).toString('utf8');\n shaped = fromNodeRequest(req, rawBody); // shaping is inside the try too — never crash\n result = engine.evaluate(shaped);\n } catch (err) {\n if (options.onError) {\n options.onError(err);\n }\n return next(); // fail open\n }\n\n if (result.blocked) {\n if (options.onBlock) {\n options.onBlock({\n rule: result.rule,\n message: result.message,\n request: { method: shaped.method, url: shaped.url, ip: shaped.ip }\n });\n }\n return (options.response || defaultBlock)(res, result);\n }\n\n // Expose the parsed body downstream so a body-parser isn't also required.\n req.body = shaped.body;\n next();\n });\n };\n}\n","// Egress guard MECHANISM only. Wraps the app's outbound calls so they can be screened; the\n// block DECISION is delegated to a caller-supplied `shouldBlock` predicate (which the runtime\n// builds from egress-phase rules — see defaults.js). No policy is hardcoded here.\n// WinterCG-first: always wraps the global `fetch` (Node 18+, Workers, Bun, Deno). On Node it\n// ALSO patches `node:http`/`node:https` so outbound calls made via those modules (axios, got,\n// the raw http client, …) — which never touch `fetch` — are screened too.\n\n/**\n * @param {{ shouldBlock: (url:string, host:string|null, method:string)=>boolean,\n * onBlock?: (info:{url:string,host:string|null,method:string})=>void,\n * dnsScreen?: boolean,\n * lookup?: Function }} opts\n * @returns {Promise<() => void>} uninstall (restores every patched surface)\n */\nexport async function installEgressGuard({ shouldBlock, onBlock, onSkip, dnsScreen = true, lookup, allowHosts } = {}) {\n const restores = [];\n if (typeof shouldBlock !== 'function') return () => {};\n const exempt = new Set((allowHosts ?? []).map((h) => String(h).toLowerCase()));\n\n const block = (url, host, method) => {\n if (!shouldBlock(url, host, method)) return false;\n onBlock?.({ url, host, method });\n return true;\n };\n\n // DNS resolution, shared by the fetch wrapper and the node http path. Instead of trusting the\n // hostname, resolve it and check the resolved address(es). On the node path we also PIN the socket\n // to the vetted IP (no time-of-check/use gap); on fetch we can't pin without a custom undici\n // dispatcher, so we screen the resolution but a re-resolve at connect is a residual window.\n // Needs node:dns + node:net; absent on edge runtimes, where the hostname rules still apply.\n let screen = null;\n if (dnsScreen) {\n try {\n const resolveLookup = lookup ?? (await import('node:dns')).lookup;\n const { isIP } = await import('node:net');\n if (typeof resolveLookup === 'function' && typeof isIP === 'function') {\n screen = { lookup: resolveLookup, isIP, isExempt: (h) => exempt.has(String(h).toLowerCase()) };\n }\n } catch {\n screen = null; // no node:dns/net here — skip, hostname rules still apply\n }\n }\n\n // A resolver failure means the destination was NOT screened by IP — the hostname check alone let it\n // through. That's a real (if rare) coverage hole, so report it via onSkip instead of failing open\n // silently. Still fail-open: a broken resolver must not take the app's outbound traffic down.\n const skip = (reason, detail) => { try { onSkip?.({ phase: 'egress', reason, detail }); } catch { /* never affect traffic */ } };\n\n // True when a hostname resolves to a disallowed address. Fail-open: any resolver error → false.\n const resolvesToDisallowed = (url, host, method) =>\n new Promise((resolve) => {\n if (!screen) {\n // No node:dns/net here (edge runtime) or screening disabled — hostname rules only.\n if (host && dnsScreen) skip('resolver-unavailable', { host });\n return resolve(false);\n }\n if (!host || screen.isIP(host) !== 0 || screen.isExempt(host)) return resolve(false);\n try {\n screen.lookup(host, { all: true }, (err, addresses) => {\n if (err || !Array.isArray(addresses)) { skip('resolver-failed', { host }); return resolve(false); }\n for (const a of addresses) {\n const ip = a && typeof a === 'object' ? a.address : a;\n if (ip && block(url, ip, method)) return resolve(true);\n }\n resolve(false);\n });\n } catch {\n skip('resolver-failed', { host });\n resolve(false);\n }\n });\n\n // 1. global fetch — synchronous install, so it's active the instant this returns (no startup race).\n const originalFetch = globalThis.fetch;\n if (typeof originalFetch === 'function' && !originalFetch.__patchstackGuarded) {\n const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\n const MAX_REDIRECTS = 20;\n\n // Screen one outbound URL: hostname/allowlist/literal-IP check, then a DNS-resolution check for\n // real hostnames. Throws if the destination is disallowed.\n const screenUrl = async (u, method) => {\n let host = null;\n try {\n host = new URL(u).hostname;\n } catch {\n host = null;\n }\n if (block(u, host, method) || (await resolvesToDisallowed(u, host, method))) {\n throw new Error(`Patchstack blocked an outbound request to a disallowed address: ${host ?? u}`);\n }\n };\n\n const guarded = async (input, init) => {\n let cur;\n try {\n cur = new Request(input, { ...(init || {}), redirect: 'manual' });\n } catch {\n return originalFetch(input, init); // odd input we can't normalize — fail open, don't break the caller\n }\n const callerRedirect = (init && init.redirect) || (input && input.redirect) || 'follow';\n\n let url = cur.url;\n let method = cur.method;\n await screenUrl(url, method);\n\n // Caller manages redirects itself (manual/error) → screen once, hand back the raw response.\n if (callerRedirect !== 'follow') return originalFetch(input, init);\n\n // Otherwise follow redirects ourselves so EVERY hop is screened. Native `follow` re-resolves\n // internally and would let a 3xx to an internal address slip past the initial check — SSRF via\n // an open redirect. Buffer the body once (a stream can't be re-read) so 307/308 can replay it.\n const headers = new Headers(cur.headers);\n const signal = cur.signal;\n let body = method === 'GET' || method === 'HEAD' ? undefined : await cur.clone().arrayBuffer();\n\n for (let hop = 0; ; hop++) {\n const resp = await originalFetch(\n hop === 0 ? cur : new Request(url, { method, headers, body, redirect: 'manual', signal }),\n );\n const location = REDIRECT_STATUSES.has(resp.status) ? resp.headers.get('location') : null;\n if (!location) return resp;\n if (hop >= MAX_REDIRECTS) throw new Error('Patchstack blocked an outbound request: too many redirects');\n\n const next = new URL(location, url).href;\n // Fetch redirect semantics: 303, and 301/302 on a POST, become a bodyless GET.\n if (resp.status === 303 || ((resp.status === 301 || resp.status === 302) && method === 'POST')) {\n method = 'GET';\n body = undefined;\n headers.delete('content-type');\n headers.delete('content-length');\n }\n // Drop credentials on a cross-origin hop, mirroring the browser.\n if (new URL(next).origin !== new URL(url).origin) {\n headers.delete('authorization');\n headers.delete('cookie');\n }\n await screenUrl(next, method);\n url = next;\n }\n };\n guarded.__patchstackGuarded = true;\n globalThis.fetch = guarded;\n restores.push(() => {\n if (globalThis.fetch === guarded) globalThis.fetch = originalFetch;\n });\n }\n\n // node:http / node:https — best-effort; absent on Workers/Deno-without-node (import throws).\n for (const moduleName of ['node:http', 'node:https']) {\n try {\n const mod = await import(moduleName);\n const restore = patchHttpModule(mod.default ?? mod, block, screen);\n if (restore) restores.push(restore);\n } catch {\n /* module not available on this runtime — skip */\n }\n }\n\n // WebSocket egress is intentionally NOT screened. The WebSocket constructor is synchronous, so\n // the only check possible inline is a textual hostname match — which can't offer the\n // DNS-resolution guarantee the fetch and node:http/https paths give (a name that resolves to an\n // internal address would pass). A connection-pinning dispatcher could close that, but the\n // server-side, attacker-controlled-WebSocket sink is rare, and a partial hostname-only check\n // over-promises the control. Outbound SSRF screening covers fetch + node:http/https.\n\n return () => {\n for (const restore of restores) {\n try {\n restore();\n } catch {\n /* ignore */\n }\n }\n };\n}\n\n// Wrap http(s).request/get so a blocked destination throws before the socket opens.\nfunction patchHttpModule(http, block, screen) {\n if (!http || typeof http.request !== 'function' || http.__patchstackGuarded) return null;\n const originalRequest = http.request;\n const originalGet = http.get;\n\n const wrap = (original) =>\n function (...args) {\n const target = extractHttpTarget(args);\n if (target && block(target.url, target.host, target.method)) {\n throw new Error(`Patchstack blocked an outbound request to a disallowed address: ${target.host ?? target.url}`);\n }\n // DNS screen: only for real hostnames (a literal IP was already covered by the check above),\n // and skip an explicitly allowlisted host (the operator trusts it — don't second-guess its DNS).\n if (target && screen && target.host && screen.isIP(target.host) === 0 && !screen.isExempt(target.host)) {\n try {\n args = withScreeningLookup(args, target, block, screen.lookup);\n } catch {\n /* injection failed — proceed unscreened (fail-open) */\n }\n }\n return original.apply(this, args);\n };\n\n const guardedRequest = wrap(originalRequest);\n http.request = guardedRequest;\n let guardedGet;\n if (typeof originalGet === 'function') {\n guardedGet = wrap(originalGet);\n http.get = guardedGet;\n }\n http.__patchstackGuarded = true;\n\n return () => {\n // Only restore if our wrapper is still installed — don't clobber a wrapper another library\n // (an APM agent, etc.) layered on top of us after install.\n if (http.request === guardedRequest) http.request = originalRequest;\n if (guardedGet && http.get === guardedGet) http.get = originalGet;\n delete http.__patchstackGuarded;\n };\n}\n\n// http.request accepts (url), (url, options), or (options) — with an optional trailing callback.\nfunction extractHttpTarget(args) {\n const first = args[0];\n try {\n if (typeof first === 'string' || first instanceof URL) {\n const url = new URL(String(first));\n const opts = args.find((a) => a && typeof a === 'object' && !(a instanceof URL));\n return { url: url.href, host: url.hostname, method: (opts && opts.method) || 'GET' };\n }\n if (first && typeof first === 'object') {\n // Node defaults an absent host to localhost — reflect that so the block/screen see a real target.\n const host = normalizeHost(first.hostname || first.host) || 'localhost';\n const protocol = first.protocol || 'http:';\n const port = first.port ? `:${first.port}` : '';\n const path = first.path || '/';\n return { url: `${protocol}//${host}${port}${path}`, host, method: first.method || 'GET' };\n }\n } catch {\n /* fall through */\n }\n return null;\n}\n\n// Extract the bare host from a node http(s) options `host`/`hostname`, WITHOUT mangling IPv6.\n// `[::1]:8080` → `::1`, bare `::1`/`fe80::1` → unchanged, `example.com:443` → `example.com`.\n// (isInternalHost strips brackets and matches ::1 / fe80: / fc / fd, so this must not corrupt them.)\nfunction normalizeHost(raw) {\n const host = String(raw || '').trim();\n const bracketed = /^\\[([^\\]]+)\\]/.exec(host);\n if (bracketed) return bracketed[1];\n if ((host.match(/:/g) || []).length > 1) return host; // bare IPv6 — no host:port to split\n return host.split(':')[0];\n}\n\n// Given the addresses a hostname resolved to, return the first one the policy blocks (else null).\n// Reuses the same `block` predicate as the hostname check, so egress rules + allowlist apply to\n// the resolved IP too. Exported for tests.\nexport function screenResolved(addresses, target, block) {\n for (const a of addresses || []) {\n const ip = a && typeof a === 'object' ? a.address : a;\n if (ip && block(target.url, ip, target.method)) return ip;\n }\n return null;\n}\n\n// Build a DNS `lookup` that screens every resolved address before the socket connects, then hands\n// back the vetted addresses (pinning the connection to what we checked). A blocked address errors\n// the connection; a resolver error or our own failure falls through to normal resolution (fail-open).\nfunction withScreeningLookup(args, target, block, lookup) {\n const screeningLookup = (hostname, options, callback) => {\n let opts = options;\n let cb = callback;\n if (typeof opts === 'function') {\n cb = opts;\n opts = {};\n }\n if (!opts || typeof opts !== 'object') opts = {};\n try {\n lookup(hostname, { ...opts, all: true }, (err, addresses) => {\n if (err) return cb(err);\n const list = Array.isArray(addresses) ? addresses : [];\n const blocked = screenResolved(list, target, block);\n if (blocked) {\n return cb(new Error(`Patchstack blocked an outbound request to a disallowed address: ${target.host} resolved to ${blocked}`));\n }\n if (opts.all) return cb(null, list);\n const first = list[0];\n if (!first) return cb(new Error(`Patchstack: could not resolve ${hostname}`));\n return cb(null, first.address, first.family);\n });\n } catch {\n // Our screening threw — fall back to a plain resolution so we never break a request ourselves.\n try {\n lookup(hostname, opts, cb);\n } catch {\n cb(new Error(`Patchstack: lookup failed for ${hostname}`));\n }\n }\n };\n return injectLookupOption(args, screeningLookup);\n}\n\n// Return a new args array for http(s).request with our `lookup` set on the options object (cloned,\n// never mutating the caller's object), inserting an options object when the call didn't pass one.\nfunction injectLookupOption(args, lookup) {\n const first = args[0];\n if (first && typeof first === 'object' && !(first instanceof URL)) {\n return [{ ...first, lookup }, ...args.slice(1)];\n }\n const rest = args.slice(1);\n const optIdx = rest.findIndex((a) => a && typeof a === 'object' && !(a instanceof URL));\n if (optIdx !== -1) {\n const next = [...rest];\n next[optIdx] = { ...rest[optIdx], lookup };\n return [first, ...next];\n }\n const cbIdx = rest.findIndex((a) => typeof a === 'function');\n if (cbIdx === -1) return [first, { lookup }, ...rest];\n return [first, ...rest.slice(0, cbIdx), { lookup }, ...rest.slice(cbIdx)];\n}\n","// Default response- and egress-phase rule bundles, as JS constants (not JSON files) so\n// tsup bundles them into dist/protect.js without a copy step. These are just rules —\n// override per app via createProtection({ responseRules, egressRules }) or extend by\n// adding phase-tagged rules to the delivered bundle. Patterns are high-precision\n// (low false positive): structural markers a real secret has and normal content doesn't.\n//\n// Each rule carries a `prefilter`: cheap literal anchor(s) that MUST appear in the body for the\n// regex to have any chance of matching. The response screener runs the (expensive) regex only when\n// an anchor is present (case-insensitive), so a body with no candidate — the common case — skips\n// the scan entirely. This cuts CPU/latency and shrinks the regex/ReDoS surface. (Honored by the\n// response phase once the prefilter mechanism lands; a no-op before that.)\n\n// Response phase — secret / info exposure. Default action `redact` masks only the\n// offending span and still serves the page (a legit response that leaks one key gets\n// that key masked, not withheld). Use `action: \"block\"` to withhold the whole response.\nexport const DEFAULT_RESPONSE_RULES = [\n {\n id: 'resp-private-key',\n title: 'Private key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['PRIVATE KEY'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/' } }]\n },\n {\n id: 'resp-aws-access-key',\n title: 'AWS access key id in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['AKIA', 'ASIA'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\\\b/' } }]\n },\n {\n id: 'resp-gcp-api-key',\n title: 'Google API key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['AIza'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\bAIza[0-9A-Za-z_-]{35}\\\\b/' } }]\n },\n {\n id: 'resp-vendor-api-key',\n title: 'Vendor API key / token in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n // High-signal, prefix-anchored provider tokens that never legitimately appear in a response\n // body: Stripe (sk_live_/rk_live_), GitHub (gh[opsu]_ / github_pat_), GitLab (glpat-),\n // Slack (xox[baprs]-), Anthropic (sk-ant-), Google OAuth (ya29.), npm (npm_). Trailing\n // (?![0-9A-Za-z]) instead of \\\\b since some tokens end in - / _ .\n prefilter: ['sk_live_', 'rk_live_', 'ghp_', 'gho_', 'ghs_', 'ghu_', 'github_pat_', 'glpat-', 'xox', 'sk-ant-', 'ya29.', 'npm_'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:sk_live_[0-9A-Za-z]{16,}|rk_live_[0-9A-Za-z]{16,}|gh[opsu]_[0-9A-Za-z]{36}|github_pat_[0-9A-Za-z_]{60,}|glpat-[0-9A-Za-z_-]{20,}|xox[baprs]-[0-9A-Za-z-]{10,}|sk-ant-[0-9A-Za-z_-]{20,}|ya29\\\\.[0-9A-Za-z_-]{20,}|npm_[0-9A-Za-z]{36})(?![0-9A-Za-z])/' } }]\n },\n {\n id: 'resp-jwt',\n title: 'JWT in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['eyJ'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\beyJ[A-Za-z0-9_-]{8,}\\\\.eyJ[A-Za-z0-9_-]{8,}\\\\.[A-Za-z0-9_-]{8,}\\\\b/' } }]\n },\n {\n id: 'resp-db-connection-string',\n title: 'Database connection string with credentials in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['mongodb', 'postgres', 'mysql', 'redis', 'amqp'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqps?):\\\\/\\\\/[^\\\\s:@\\\\/]+:[^\\\\s:@\\\\/]+@/i' } }]\n },\n {\n id: 'resp-stack-trace',\n title: 'Node stack trace leaking in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n // No prefilter: a Node stack frame has no single distinctive literal (` at ` is too common to\n // gate on). The pattern is linearly bounded per line, so it runs on every screened body.\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\n\\\\s+at\\\\s+.+\\\\(.+:\\\\d+:\\\\d+\\\\)/' } }]\n },\n {\n id: 'resp-sql-error',\n title: 'SQL / ORM error disclosure in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n prefilter: ['SQLSTATE', 'Sequelize', 'ER_', 'ORA-', 'PG::', 'SQLITE_ERROR', 'SQL syntax'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/(SQLSTATE\\\\[[0-9A-Z]+\\\\]|SequelizeDatabaseError|ER_[A-Z_]+|ORA-\\\\d{5}|PG::[A-Za-z]+Error|SQLITE_ERROR|You have an error in your SQL syntax)/i' } }]\n },\n {\n id: 'resp-exception-trace',\n title: 'Backend exception / stack trace disclosure in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n // Multi-language exception/traceback signatures a normal API response never carries:\n // Python traceback, Java \"Exception in thread\", .NET System.*Exception, JVM stack frames,\n // Go goroutine dumps. (Node `at fn (file:line:col)` frames are handled by resp-stack-trace.)\n prefilter: ['Traceback', 'Exception in thread', 'System.', 'goroutine ', '.java:', '.kt:', '.scala:', '.rb:', '.py:', '.cs:'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/(Traceback \\\\(most recent call last\\\\)|Exception in thread \"|System\\\\.[A-Za-z.]+Exception|\\\\bat [\\\\w.$]+\\\\([\\\\w]+\\\\.(?:java|kt|scala|rb|py|cs):\\\\d+\\\\)|goroutine \\\\d+ \\\\[)/' } }]\n }\n];\n\n// Egress phase — SSRF: block the app's outbound calls to internal / metadata addresses.\nexport const DEFAULT_EGRESS_RULES = [\n {\n id: 'egress-internal-address',\n title: 'Outbound request to an internal / metadata address (SSRF)',\n phase: 'egress',\n category: 'ssrf',\n rule_v2: [{ parameter: 'egress.host', match: { type: 'internal_host' } }]\n }\n];\n","// Full-page \"Access Denied\" block view, ported from the Patchstack WordPress plugin's\n// sample.php. Served (instead of JSON) when a BLOCKED request is a top-level document navigation\n// rather than an XHR/fetch — so a user who browses into a blocked URL sees a branded page, while\n// programmatic clients still get JSON. Self-contained (inline CSS + base64 SVGs); exposes no WAF\n// narrative and no rule details.\n\nfunction escapeHtml(s) {\n return String(s).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n}\n\n/** @param {{ url?: string, code?: string }} opts @returns {string} a complete HTML document */\nexport function renderBlockPage({ url = '', code = 'patchstack' } = {}) {\n const safeUrl = escapeHtml(url);\n const safeCode = escapeHtml(code || 'patchstack');\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style type=\"text/css\">\n\t\t.access-denied,.site-url-text{letter-spacing:.01em;color:#fff}body{background:#111216}.main-container{display:flex;justify-content:center;flex-direction:column;align-items:center}.container{width:688px;height:456px;background:#1c1e22;border-radius:16px;margin-top:120px}.content{height:330px;margin:64px}.access-denied{font-family:Verdana;font-style:normal;font-weight:600;font-size:24px;line-height:32px;display:flex;align-items:center}.descp-text,.site-url{display:flex;align-items:center}.site-url{margin-top:32px;width:560px;height:64px;background:#26282c;border-radius:4px}.reason-code-text,.site-url-text{font-family:Verdana;font-style:normal;border:none;background:#26282c}.site-url-text{text-overflow: ellipsis;font-weight:400;font-size:16px;line-height:24px;margin-left:16px;width:85%}.copy-icon{width:19px;height:22px;margin-left:20px}.copy-icon:hover{cursor:pointer}.reason-code-text{font-weight:600;font-size:24px;line-height:32px;letter-spacing:.01em;color:#AFE614;margin-left:16px;width:85%}.descp-text,.return-text{font-size:16px;line-height:24px;letter-spacing:.01em;font-weight:400}.return-text:hover{color: #AFE614}.descp-text,.logoText,.return-text,.secured-text{font-family:Verdana;font-style:normal;color:#fff}.descp{margin-top:32px;width:560px}.bottom-second-containers,.bottom-text-containers{align-items:center;display:flex}.bottom-container{margin-top:40px;display:flex;width:688px}.bottom-second-containers{margin-left:auto}.arrow-vector{margin-left:20px}.return-text{text-align:center;margin-left:12.59px;text-decoration:none}.secured-text{font-weight:400;font-size:11.2px;line-height:16px;display:flex;align-items:center;letter-spacing:.02em;margin-right:8px}.logoText{font-weight:600;font-size:16px;line-height:24px;letter-spacing:.01em;flex:none;order:1;flex-grow:0}.patch-logo{width:120px}.mobile-bottom-container{margin-top:40px;width:372px;display:none}.mobile-bottom-second-containers{margin-top:64px;display:flex;margin-left:16.5px}@media only screen and (max-width:900px){.container{background:#111216;width:370px;height:auto;margin-top:0}.content{margin-left:16.5px}.descp,.site-url{width:340px}.site-url-text{font-weight:400;font-size:14.4px;line-height:20px;letter-spacing:.02em;width:80%}.copy-icon{margin-left:20px}.reason-code-text{font-size:16px;line-height:32px;letter-spacing:.02em;margin-left:16px;width:80%}.descp-text,.return-text{font-size:14.4px;line-height:20px}.descp-text{display:flex;align-items:center;letter-spacing:.02em}.bottom-container{display:none}.mobile-bottom-container{display:block}.return-text{text-align:center;letter-spacing:.01em;color:#fff;margin-left:9.73px}}\n </style>\n <title>Access Denied</title>\n</head>\n<body>\n <div class=\"main-container\">\n <div class=\"container\">\n <div class=\"content\">\n <span class=\"access-denied\">Access Denied</span>\n <div class=\"site-url\">\n <input id=\"url\" class=\"site-url-text\" type=\"text\"\n value=\"${safeUrl}\" readonly>\n <img class=\"copy-icon\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=\" alt=\"Copy Text\" onmouseover=\"hover(this);\" onmouseout=\"unhover(this);\" onclick=\"copyText(id='url')\">\n </div>\n <div class=\"site-url\">\n <input id=\"response\" class=\"reason-code-text\" type=\"text\" value=\"${safeCode}\" readonly>\n <img class=\"copy-icon\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=\" alt=\"Copy Text\" onmouseover=\"hover(this);\" onmouseout=\"unhover(this);\" onclick=\"copyText(id='response')\">\n </div>\n <div class=\"descp\">\n <span class=\"descp-text\">This request has been blocked by Patchstack. If this message persists and you are a legitimate user, contact the site administrator with the reference above.</span>\n </div>\n </div>\n </div>\n <div class=\"bottom-container\">\n <div class=\"bottom-text-containers\">\n <img class=\"arrow-vector\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSIxMyIgdmlld0JveD0iMCAwIDggMTMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03LjQxIDEuNDMzNDRMNiAwLjAyMzQzNzVMMCA2LjAyMzQ0TDYgMTIuMDIzNEw3LjQxIDEwLjYxMzRMMi44MyA2LjAyMzQ0TDcuNDEgMS40MzM0NFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==\" alt=\"Arrow Left\">\n <a href=\"/\" class=\"return-text\">Return to homepage</a>\n </div>\n <div class=\"bottom-second-containers\">\n <span class=\"secured-text\">This website is secured by</span>\n <a href=\"https://patchstack.com\" class=\"logoText\">\n <img class=\"patch-logo\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg0IiBoZWlnaHQ9IjIyIiB2aWV3Qm94PSIwIDAgMTg0IDIyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUwLjQwMDEgMTEuNTAwOEM1MC40MDAxIDkuMTAwNzggNDkuMzAwMSA4LjEwMDc4IDQ2LjYwMDEgOC4xMDA3OEM0NC4wMDAxIDguMTAwNzggNDIuNzAwMSA5LjIwMDc4IDQyLjcwMDEgMTEuNTAwOEM0Mi43MDAxIDEzLjgwMDggNDQuMDAwMSAxNC45MDA4IDQ2LjYwMDEgMTQuOTAwOEM0OS4zMDAxIDE0LjkwMDggNTAuNDAwMSAxMy44MDA4IDUwLjQwMDEgMTEuNTAwOFpNNTMuNDAwMSAxMS41MDA4QzUzLjQwMDEgMTUuMzAwOCA1MS4zMDAxIDE3LjYwMDggNDcuNDAwMSAxNy42MDA4QzQ1LjQwMDEgMTcuNjAwOCA0My42MDAxIDE2LjUwMDggNDIuNzAwMSAxNC41MDA4VjIxLjUwMDhIMzkuNjAwMVY1LjYwMDc4SDQyLjcwMDFWOC41MDA3OEM0My42MDAxIDYuNTAwNzggNDUuNDAwMSA1LjMwMDc4IDQ3LjUwMDEgNS4zMDA3OEM1MS40MDAxIDUuMzAwNzggNTMuNDAwMSA3LjcwMDc4IDUzLjQwMDEgMTEuNTAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjYuNDAwMSAxMS40OTg0QzY2LjQwMDEgOS4xOTg0NCA2NS4xMDAxIDguMDk4NDQgNjIuNTAwMSA4LjA5ODQ0QzU5LjgwMDEgOC4wOTg0NCA1OC43MDAxIDkuMDk4NDQgNTguNzAwMSAxMS40OTg0QzU4LjcwMDEgMTMuNzk4NCA1OS44MDAxIDE0Ljg5ODQgNjIuNTAwMSAxNC44OTg0QzY1LjEwMDEgMTQuODk4NCA2Ni40MDAxIDEzLjc5ODQgNjYuNDAwMSAxMS40OTg0Wk02Ni4zMDAxIDUuNTk4NDRINjkuNDAwMVYxNy4zOTg0SDY2LjMwMDFWMTQuNDk4NEM2NS40MDAxIDE2LjQ5ODQgNjMuNzAwMSAxNy42OTg0IDYxLjYwMDEgMTcuNjk4NEM1Ny43MDAxIDE3LjY5ODQgNTUuNjAwMSAxNS4yOTg0IDU1LjYwMDEgMTEuNTk4NEM1NS42MDAxIDcuNzk4NDQgNTcuNjAwMSA1LjM5ODQ0IDYxLjQwMDEgNS4zOTg0NEM2My41MDAxIDUuMzk4NDQgNjUuNDAwMSA2LjU5ODQ0IDY2LjIwMDEgOC41OTg0NFY1LjU5ODQ0SDY2LjMwMDFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTgxLjM5OTkgOC4zSDc3LjM5OTlWMTQuN0g4MS4wOTk5VjE3LjRINzcuOTk5OUM3NS43OTk5IDE3LjQgNzQuMjk5OSAxNS45IDc0LjI5OTkgMTMuN1Y4LjNINzEuMzk5OVY1LjZINzQuMjk5OVYySDc3LjM5OTlWNS41SDgxLjM5OTlWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik05My42OTk5IDEyLjIwMDhMOTYuNjk5OSAxMi45MDA4Qzk1Ljk5OTkgMTYuMDAwOCA5My42OTk5IDE3LjYwMDggODkuOTk5OSAxNy42MDA4Qzg3Ljc5OTkgMTcuNjAwOCA4Ni4wOTk5IDE3LjAwMDggODQuNzk5OSAxNS45MDA4QzgzLjQ5OTkgMTQuODAwOCA4Mi44OTk5IDEzLjMwMDggODIuODk5OSAxMS41MDA4QzgyLjg5OTkgOS42MDA3OCA4My40OTk5IDguMTAwNzggODQuNzk5OSA3LjAwMDc4Qzg2LjA5OTkgNS45MDA3OCA4Ny43OTk5IDUuMzAwNzggODkuOTk5OSA1LjMwMDc4QzkzLjY5OTkgNS4zMDA3OCA5NS45OTk5IDYuOTAwNzggOTYuNjk5OSAxMC4wMDA4TDkzLjY5OTkgMTAuNzAwOEM5My4yOTk5IDguODAwNzggOTIuMjk5OSA4LjEwMDc4IDg5Ljg5OTkgOC4xMDA3OEM4Ni45OTk5IDguMTAwNzggODUuODk5OSA5LjIwMDc4IDg1Ljg5OTkgMTEuNTAwOEM4NS44OTk5IDEzLjgwMDggODYuOTk5OSAxNC45MDA4IDg5Ljg5OTkgMTQuOTAwOEM5Mi4yOTk5IDE0LjkwMDggOTMuMjk5OSAxNC4yMDA4IDkzLjY5OTkgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTExLjggMTAuNlYxNy40SDEwOC43VjExLjJDMTA4LjcgOSAxMDcuOCA4LjEgMTA1LjkgOC4xQzEwMy42IDguMSAxMDIuNCA5LjMgMTAyLjQgMTEuN1YxNy4zSDk5LjI5OThWMC41SDEwMi40VjguM0MxMDMuMSA2LjMgMTA0LjYgNS4zIDEwNyA1LjNDMTEwLjIgNS4zIDExMS44IDcuMSAxMTEuOCAxMC42WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMTQuMSAxNC4wOTg0TDExNy4xIDEzLjI5ODRDMTE3LjQgMTQuNzk4NCAxMTguMyAxNS4yOTg0IDEyMC4yIDE1LjI5ODRDMTIxLjggMTUuMjk4NCAxMjIuNyAxNC44OTg0IDEyMi43IDE0LjA5ODRDMTIyLjcgMTMuMjk4NCAxMjIuMiAxMi45OTg0IDEyMC42IDEyLjc5ODRMMTE4LjIgMTIuNDk4NEMxMTUuOSAxMi4xOTg0IDExNC41IDEwLjg5ODQgMTE0LjUgOC45OTg0NEMxMTQuNSA2Ljc5ODQ0IDExNi41IDUuMzk4NDQgMTE5LjggNS4zOTg0NEMxMjMuMSA1LjM5ODQ0IDEyNSA2LjY5ODQ0IDEyNS41IDguODk4NDRMMTIyLjUgOS41OTg0NEMxMjIuMyA4LjA5ODQ0IDEyMS41IDcuNjk4NDQgMTE5LjkgNy42OTg0NEMxMTguNCA3LjY5ODQ0IDExNy42IDguMDk4NDQgMTE3LjYgOC44OTg0NEMxMTcuNiA5LjU5ODQ0IDExOC4xIDkuOTk4NDQgMTE5LjMgMTAuMDk4NEwxMjIuMiAxMC40OTg0QzEyNC40IDEwLjc5ODQgMTI1LjggMTEuOTk4NCAxMjUuOCAxMy44OTg0QzEyNS44IDE2LjI5ODQgMTIzLjcgMTcuNzk4NCAxMjAuMSAxNy43OTg0QzExNi43IDE3LjU5ODQgMTE0LjUgMTYuMTk4NCAxMTQuMSAxNC4wOTg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzYuNyA4LjNIMTMyLjdWMTQuN0gxMzYuNFYxNy40SDEzMy4zQzEzMS4xIDE3LjQgMTI5LjYgMTUuOSAxMjkuNiAxMy43VjguM0gxMjYuN1Y1LjZIMTI5LjZWMkgxMzIuN1Y1LjVIMTM2LjdWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNDkuMiAxMS40OTg0QzE0OS4yIDkuMTk4NDQgMTQ3LjkgOC4wOTg0NCAxNDUuMyA4LjA5ODQ0QzE0Mi42IDguMDk4NDQgMTQxLjUgOS4wOTg0NCAxNDEuNSAxMS40OTg0QzE0MS41IDEzLjc5ODQgMTQyLjYgMTQuODk4NCAxNDUuMyAxNC44OTg0QzE0Ny45IDE0Ljg5ODQgMTQ5LjIgMTMuNzk4NCAxNDkuMiAxMS40OTg0Wk0xNDkuMSA1LjU5ODQ0SDE1Mi4yVjE3LjM5ODRIMTQ5LjFWMTQuNDk4NEMxNDguMiAxNi40OTg0IDE0Ni41IDE3LjY5ODQgMTQ0LjQgMTcuNjk4NEMxNDAuNSAxNy42OTg0IDEzOC40IDE1LjI5ODQgMTM4LjQgMTEuNTk4NEMxMzguNCA3Ljc5ODQ0IDE0MC40IDUuMzk4NDQgMTQ0LjIgNS4zOTg0NEMxNDYuMyA1LjM5ODQ0IDE0OC4yIDYuNTk4NDQgMTQ5IDguNTk4NDRWNS41OTg0NEgxNDkuMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTY1LjYgMTIuMjAwOEwxNjguNiAxMi45MDA4QzE2Ny45IDE2LjAwMDggMTY1LjYgMTcuNjAwOCAxNjEuOSAxNy42MDA4QzE1OS43IDE3LjYwMDggMTU4IDE3LjAwMDggMTU2LjcgMTUuOTAwOEMxNTUuNCAxNC44MDA4IDE1NC44IDEzLjMwMDggMTU0LjggMTEuNTAwOEMxNTQuOCA5LjYwMDc4IDE1NS40IDguMTAwNzggMTU2LjcgNy4wMDA3OEMxNTggNS45MDA3OCAxNTkuNyA1LjMwMDc4IDE2MS45IDUuMzAwNzhDMTY1LjYgNS4zMDA3OCAxNjcuOSA2LjkwMDc4IDE2OC42IDEwLjAwMDhMMTY1LjYgMTAuNzAwOEMxNjUuMiA4LjgwMDc4IDE2NC4yIDguMTAwNzggMTYxLjggOC4xMDA3OEMxNTguOSA4LjEwMDc4IDE1Ny44IDkuMjAwNzggMTU3LjggMTEuNTAwOEMxNTcuOCAxMy44MDA4IDE1OC45IDE0LjkwMDggMTYxLjggMTQuOTAwOEMxNjQuMSAxNC45MDA4IDE2NS4xIDE0LjIwMDggMTY1LjYgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTgwLjI1NyAxNy40TDE3Ni42NTcgMTIuM0wxNzQuMTU3IDE0LjZWMTcuNEgxNzEuMTU3VjAuNUgxNzQuMTU3VjExLjFMMTc5Ljg1NyA1LjZIMTgzLjc1N0wxNzguNTU3IDEwLjVMMTgzLjk1NyAxNy40SDE4MC4yNTdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgMjEuNTAwOEg3LjNWMTYuMzAwOEgwVjIxLjUwMDhaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNy4yOTk4IDIxLjUwMDhIMTQuNTk5OFYxNi4zMDA4SDcuMjk5OFYyMS41MDA4WiIgZmlsbD0iIzZFQkUwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE0LjYwMDEgMTYuM0gyOS4yMDAxVjAuNUgxNC42MDAxVjE2LjNaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMCAxMUgxNC42VjAuNUgwVjExWiIgZmlsbD0iIzZFQkUwMCIvPgo8L3N2Zz4K\" alt=\"Patchstack Logo\">\n\t\t\t\t</a>\n </div>\n </div>\n \n <div class=\"mobile-bottom-container\">\n <div class=\"mobile-bottom-text-containers\">\n <img class=\"arrow-vector\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSIxMyIgdmlld0JveD0iMCAwIDggMTMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03LjQxIDEuNDMzNDRMNiAwLjAyMzQzNzVMMCA2LjAyMzQ0TDYgMTIuMDIzNEw3LjQxIDEwLjYxMzRMMi44MyA2LjAyMzQ0TDcuNDEgMS40MzM0NFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==\" alt=\"Arrow Left\">\n <a href=\"/\" class=\"return-text\">Return to homepage</a>\n </div>\n <div class=\"mobile-bottom-second-containers\">\n <span class=\"secured-text\">This website is secured by</span>\n <a href=\"https://patchstack.com\" class=\"logoText\">\n <img class=\"patch-logo\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg0IiBoZWlnaHQ9IjIyIiB2aWV3Qm94PSIwIDAgMTg0IDIyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUwLjQwMDEgMTEuNTAwOEM1MC40MDAxIDkuMTAwNzggNDkuMzAwMSA4LjEwMDc4IDQ2LjYwMDEgOC4xMDA3OEM0NC4wMDAxIDguMTAwNzggNDIuNzAwMSA5LjIwMDc4IDQyLjcwMDEgMTEuNTAwOEM0Mi43MDAxIDEzLjgwMDggNDQuMDAwMSAxNC45MDA4IDQ2LjYwMDEgMTQuOTAwOEM0OS4zMDAxIDE0LjkwMDggNTAuNDAwMSAxMy44MDA4IDUwLjQwMDEgMTEuNTAwOFpNNTMuNDAwMSAxMS41MDA4QzUzLjQwMDEgMTUuMzAwOCA1MS4zMDAxIDE3LjYwMDggNDcuNDAwMSAxNy42MDA4QzQ1LjQwMDEgMTcuNjAwOCA0My42MDAxIDE2LjUwMDggNDIuNzAwMSAxNC41MDA4VjIxLjUwMDhIMzkuNjAwMVY1LjYwMDc4SDQyLjcwMDFWOC41MDA3OEM0My42MDAxIDYuNTAwNzggNDUuNDAwMSA1LjMwMDc4IDQ3LjUwMDEgNS4zMDA3OEM1MS40MDAxIDUuMzAwNzggNTMuNDAwMSA3LjcwMDc4IDUzLjQwMDEgMTEuNTAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjYuNDAwMSAxMS40OTg0QzY2LjQwMDEgOS4xOTg0NCA2NS4xMDAxIDguMDk4NDQgNjIuNTAwMSA4LjA5ODQ0QzU5LjgwMDEgOC4wOTg0NCA1OC43MDAxIDkuMDk4NDQgNTguNzAwMSAxMS40OTg0QzU4LjcwMDEgMTMuNzk4NCA1OS44MDAxIDE0Ljg5ODQgNjIuNTAwMSAxNC44OTg0QzY1LjEwMDEgMTQuODk4NCA2Ni40MDAxIDEzLjc5ODQgNjYuNDAwMSAxMS40OTg0Wk02Ni4zMDAxIDUuNTk4NDRINjkuNDAwMVYxNy4zOTg0SDY2LjMwMDFWMTQuNDk4NEM2NS40MDAxIDE2LjQ5ODQgNjMuNzAwMSAxNy42OTg0IDYxLjYwMDEgMTcuNjk4NEM1Ny43MDAxIDE3LjY5ODQgNTUuNjAwMSAxNS4yOTg0IDU1LjYwMDEgMTEuNTk4NEM1NS42MDAxIDcuNzk4NDQgNTcuNjAwMSA1LjM5ODQ0IDYxLjQwMDEgNS4zOTg0NEM2My41MDAxIDUuMzk4NDQgNjUuNDAwMSA2LjU5ODQ0IDY2LjIwMDEgOC41OTg0NFY1LjU5ODQ0SDY2LjMwMDFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTgxLjM5OTkgOC4zSDc3LjM5OTlWMTQuN0g4MS4wOTk5VjE3LjRINzcuOTk5OUM3NS43OTk5IDE3LjQgNzQuMjk5OSAxNS45IDc0LjI5OTkgMTMuN1Y4LjNINzEuMzk5OVY1LjZINzQuMjk5OVYySDc3LjM5OTlWNS41SDgxLjM5OTlWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik05My42OTk5IDEyLjIwMDhMOTYuNjk5OSAxMi45MDA4Qzk1Ljk5OTkgMTYuMDAwOCA5My42OTk5IDE3LjYwMDggODkuOTk5OSAxNy42MDA4Qzg3Ljc5OTkgMTcuNjAwOCA4Ni4wOTk5IDE3LjAwMDggODQuNzk5OSAxNS45MDA4QzgzLjQ5OTkgMTQuODAwOCA4Mi44OTk5IDEzLjMwMDggODIuODk5OSAxMS41MDA4QzgyLjg5OTkgOS42MDA3OCA4My40OTk5IDguMTAwNzggODQuNzk5OSA3LjAwMDc4Qzg2LjA5OTkgNS45MDA3OCA4Ny43OTk5IDUuMzAwNzggODkuOTk5OSA1LjMwMDc4QzkzLjY5OTkgNS4zMDA3OCA5NS45OTk5IDYuOTAwNzggOTYuNjk5OSAxMC4wMDA4TDkzLjY5OTkgMTAuNzAwOEM5My4yOTk5IDguODAwNzggOTIuMjk5OSA4LjEwMDc4IDg5Ljg5OTkgOC4xMDA3OEM4Ni45OTk5IDguMTAwNzggODUuODk5OSA5LjIwMDc4IDg1Ljg5OTkgMTEuNTAwOEM4NS44OTk5IDEzLjgwMDggODYuOTk5OSAxNC45MDA4IDg5Ljg5OTkgMTQuOTAwOEM5Mi4yOTk5IDE0LjkwMDggOTMuMjk5OSAxNC4yMDA4IDkzLjY5OTkgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTExLjggMTAuNlYxNy40SDEwOC43VjExLjJDMTA4LjcgOSAxMDcuOCA4LjEgMTA1LjkgOC4xQzEwMy42IDguMSAxMDIuNCA5LjMgMTAyLjQgMTEuN1YxNy4zSDk5LjI5OThWMC41SDEwMi40VjguM0MxMDMuMSA2LjMgMTA0LjYgNS4zIDEwNyA1LjNDMTEwLjIgNS4zIDExMS44IDcuMSAxMTEuOCAxMC42WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMTQuMSAxNC4wOTg0TDExNy4xIDEzLjI5ODRDMTE3LjQgMTQuNzk4NCAxMTguMyAxNS4yOTg0IDEyMC4yIDE1LjI5ODRDMTIxLjggMTUuMjk4NCAxMjIuNyAxNC44OTg0IDEyMi43IDE0LjA5ODRDMTIyLjcgMTMuMjk4NCAxMjIuMiAxMi45OTg0IDEyMC42IDEyLjc5ODRMMTE4LjIgMTIuNDk4NEMxMTUuOSAxMi4xOTg0IDExNC41IDEwLjg5ODQgMTE0LjUgOC45OTg0NEMxMTQuNSA2Ljc5ODQ0IDExNi41IDUuMzk4NDQgMTE5LjggNS4zOTg0NEMxMjMuMSA1LjM5ODQ0IDEyNSA2LjY5ODQ0IDEyNS41IDguODk4NDRMMTIyLjUgOS41OTg0NEMxMjIuMyA4LjA5ODQ0IDEyMS41IDcuNjk4NDQgMTE5LjkgNy42OTg0NEMxMTguNCA3LjY5ODQ0IDExNy42IDguMDk4NDQgMTE3LjYgOC44OTg0NEMxMTcuNiA5LjU5ODQ0IDExOC4xIDkuOTk4NDQgMTE5LjMgMTAuMDk4NEwxMjIuMiAxMC40OTg0QzEyNC40IDEwLjc5ODQgMTI1LjggMTEuOTk4NCAxMjUuOCAxMy44OTg0QzEyNS44IDE2LjI5ODQgMTIzLjcgMTcuNzk4NCAxMjAuMSAxNy43OTg0QzExNi43IDE3LjU5ODQgMTE0LjUgMTYuMTk4NCAxMTQuMSAxNC4wOTg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzYuNyA4LjNIMTMyLjdWMTQuN0gxMzYuNFYxNy40SDEzMy4zQzEzMS4xIDE3LjQgMTI5LjYgMTUuOSAxMjkuNiAxMy43VjguM0gxMjYuN1Y1LjZIMTI5LjZWMkgxMzIuN1Y1LjVIMTM2LjdWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNDkuMiAxMS40OTg0QzE0OS4yIDkuMTk4NDQgMTQ3LjkgOC4wOTg0NCAxNDUuMyA4LjA5ODQ0QzE0Mi42IDguMDk4NDQgMTQxLjUgOS4wOTg0NCAxNDEuNSAxMS40OTg0QzE0MS41IDEzLjc5ODQgMTQyLjYgMTQuODk4NCAxNDUuMyAxNC44OTg0QzE0Ny45IDE0Ljg5ODQgMTQ5LjIgMTMuNzk4NCAxNDkuMiAxMS40OTg0Wk0xNDkuMSA1LjU5ODQ0SDE1Mi4yVjE3LjM5ODRIMTQ5LjFWMTQuNDk4NEMxNDguMiAxNi40OTg0IDE0Ni41IDE3LjY5ODQgMTQ0LjQgMTcuNjk4NEMxNDAuNSAxNy42OTg0IDEzOC40IDE1LjI5ODQgMTM4LjQgMTEuNTk4NEMxMzguNCA3Ljc5ODQ0IDE0MC40IDUuMzk4NDQgMTQ0LjIgNS4zOTg0NEMxNDYuMyA1LjM5ODQ0IDE0OC4yIDYuNTk4NDQgMTQ5IDguNTk4NDRWNS41OTg0NEgxNDkuMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTY1LjYgMTIuMjAwOEwxNjguNiAxMi45MDA4QzE2Ny45IDE2LjAwMDggMTY1LjYgMTcuNjAwOCAxNjEuOSAxNy42MDA4QzE1OS43IDE3LjYwMDggMTU4IDE3LjAwMDggMTU2LjcgMTUuOTAwOEMxNTUuNCAxNC44MDA4IDE1NC44IDEzLjMwMDggMTU0LjggMTEuNTAwOEMxNTQuOCA5LjYwMDc4IDE1NS40IDguMTAwNzggMTU2LjcgNy4wMDA3OEMxNTggNS45MDA3OCAxNTkuNyA1LjMwMDc4IDE2MS45IDUuMzAwNzhDMTY1LjYgNS4zMDA3OCAxNjcuOSA2LjkwMDc4IDE2OC42IDEwLjAwMDhMMTY1LjYgMTAuNzAwOEMxNjUuMiA4LjgwMDc4IDE2NC4yIDguMTAwNzggMTYxLjggOC4xMDA3OEMxNTguOSA4LjEwMDc4IDE1Ny44IDkuMjAwNzggMTU3LjggMTEuNTAwOEMxNTcuOCAxMy44MDA4IDE1OC45IDE0LjkwMDggMTYxLjggMTQuOTAwOEMxNjQuMSAxNC45MDA4IDE2NS4xIDE0LjIwMDggMTY1LjYgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTgwLjI1NyAxNy40TDE3Ni42NTcgMTIuM0wxNzQuMTU3IDE0LjZWMTcuNEgxNzEuMTU3VjAuNUgxNzQuMTU3VjExLjFMMTc5Ljg1NyA1LjZIMTgzLjc1N0wxNzguNTU3IDEwLjVMMTgzLjk1NyAxNy40SDE4MC4yNTdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgMjEuNTAwOEg3LjNWMTYuMzAwOEgwVjIxLjUwMDhaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNy4yOTk4IDIxLjUwMDhIMTQuNTk5OFYxNi4zMDA4SDcuMjk5OFYyMS41MDA4WiIgZmlsbD0iIzZFQkUwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE0LjYwMDEgMTYuM0gyOS4yMDAxVjAuNUgxNC42MDAxVjE2LjNaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMCAxMUgxNC42VjAuNUgwVjExWiIgZmlsbD0iIzZFQkUwMCIvPgo8L3N2Zz4K\" alt=\"Patchstack Logo\">\n\t\t\t\t</a>\n </div>\n </div>\n </div>\n</body>\n<script>\n function copyText(id) {\n var copyText = document.getElementById(id)\n copyText.select();\n copyText.setSelectionRange(0, 99999);\n navigator.clipboard.writeText(copyText.value);\n }\n\n function hover(element) {\n element.setAttribute('src', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==');\n }\n\n function unhover(element) {\n element.setAttribute('src', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=');\n }\n</script>\n</html>`;\n}\n","// Rule cache as a TIERED store of a `{ bundle, etag }` envelope:\n// 1. memory — always present; last-known-good within the process. Survives refreshes and is\n// the fallback when the disk isn't writable (read-only FS, sandbox).\n// 2. durable — filesystem (default, via `cacheDir`) OR a pluggable adapter (`ruleCache`, e.g.\n// a KV store for filesystem-less runtimes). Survives process restarts.\n// read: memory → durable → null. write: memory + best-effort durable. Everything is fail-open —\n// a read/write error yields \"no cache\" rather than throwing.\n//\n// Node's fs/path are loaded LAZILY (dynamic import), never as a static top-level import: this module\n// is part of the WinterCG/edge-safe graph (Next edge middleware, Workers, Deno, Supabase Functions),\n// where a static `node:fs` import fails to resolve at build/load time and would take the whole guard\n// down. On those runtimes the disk tier simply reports \"no cache\" and the memory tier (or a pluggable\n// `ruleCache` adapter) carries last-known-good.\n\nlet fsMod; // memoized { readFileSync, writeFileSync, mkdirSync, join } | null (unavailable)\nasync function loadFs() {\n if (fsMod !== undefined) return fsMod;\n try {\n const [fs, path] = await Promise.all([import('node:fs'), import('node:path')]);\n fsMod = {\n readFileSync: fs.readFileSync,\n writeFileSync: fs.writeFileSync,\n mkdirSync: fs.mkdirSync,\n join: path.join,\n };\n } catch {\n fsMod = null; // no filesystem here (edge runtime) — memory/adapter tiers still work\n }\n return fsMod;\n}\n\nexport function makeStore(options = {}) {\n let mem = null;\n const durable = durableTier(options);\n return {\n async read() {\n if (mem) return mem;\n const env = await durable.read();\n if (env) mem = env;\n return env;\n },\n async write(env) {\n if (env) mem = env;\n await durable.write(env);\n },\n };\n}\n\n// The durable tier: a caller-supplied adapter wins; otherwise a disk cache under `cacheDir`; if\n// neither is configured, a no-op (memory-only — fine for a long-lived process).\nfunction durableTier(options) {\n const adapter = options.ruleCache;\n if (adapter && typeof adapter.read === 'function' && typeof adapter.write === 'function') {\n return {\n read: async () => {\n try {\n return toEnvelope(await adapter.read());\n } catch {\n return null;\n }\n },\n write: async (env) => {\n try {\n await adapter.write(env);\n } catch {\n /* best-effort */\n }\n },\n };\n }\n const dir = options.cacheDir;\n return {\n read: async () => cacheRead(dir),\n write: async (env) => cacheWrite(dir, env),\n };\n}\n\nasync function cacheWrite(dir, env) {\n if (!dir) return;\n const fs = await loadFs();\n if (!fs) return; // filesystem-less runtime — memory tier only\n try {\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(fs.join(dir, 'patchstack-rules.json'), JSON.stringify(env));\n } catch {\n /* cache is best-effort — the memory tier still holds last-known-good */\n }\n}\n\nasync function cacheRead(dir) {\n if (!dir) return null;\n const fs = await loadFs();\n if (!fs) return null;\n try {\n return toEnvelope(JSON.parse(fs.readFileSync(fs.join(dir, 'patchstack-rules.json'), 'utf8')));\n } catch {\n return null;\n }\n}\n\n// Accept the current { bundle, etag } envelope and a legacy bare bundle (pre-envelope cache files).\nexport function toEnvelope(value) {\n if (!value || typeof value !== 'object') return null;\n if (value.bundle && typeof value.bundle === 'object') {\n return { bundle: value.bundle, etag: value.etag ?? null };\n }\n if (Array.isArray(value.firewall) || Array.isArray(value.whitelists)) {\n return { bundle: value, etag: null }; // legacy bare-bundle cache file\n }\n return null;\n}\n","// Rule-bundle validation. Delivered rules are POLICY fetched over the network, and the engine executes\n// them on every request — so an upstream compromise, a schema drift, or a mistake in the corpus could\n// otherwise hand the app unbounded work (a 50k-rule bundle, a 500-deep condition tree, a pathological\n// regex) or silently unenforceable junk.\n//\n// Two principles:\n// 1. REJECT, don't silently skip. A rule that fails validation is dropped WITH a reported reason, so\n// an unenforceable rule is visible instead of quietly protecting nothing.\n// 2. Bound everything the engine will walk: rule count, conditions per rule, nesting depth, and\n// pattern length. Caps are deliberately far above any real corpus rule.\n//\n// Fail-open in spirit: validation never throws, and a bundle whose rules are all rejected simply means\n// \"no rules\" (the app keeps serving) — never a crash.\n\nexport const LIMITS = {\n maxRules: 5000,\n maxWhitelists: 2000,\n maxConditionsPerRule: 250,\n maxNestingDepth: 12,\n maxRegexLength: 1000,\n maxValueLength: 8192,\n};\n\nconst PHASES = new Set(['request', 'response', 'egress']);\nconst ACTIONS = new Set(['block', 'redact', 'encode', 'set-header', 'remove-header', 'harden-cookie']);\n\n/**\n * Validate a delivered bundle. Returns `{ bundle, rejected }` where `bundle` contains only rules that\n * passed and `rejected` is `[{ id, reason }]` for everything dropped.\n * @param {object} bundle\n * @returns {{ bundle: object, rejected: Array<{id: string, reason: string}> }}\n */\nexport function validateBundle(bundle, opts = {}) {\n const rejected = [];\n const inFirewall = Array.isArray(bundle?.firewall) ? bundle.firewall : [];\n const inWhitelists = Array.isArray(bundle?.whitelists) ? bundle.whitelists : [];\n\n const firewall = [];\n for (const rule of inFirewall) {\n if (firewall.length >= LIMITS.maxRules) {\n rejected.push({ id: idOf(rule), reason: `bundle exceeds maxRules (${LIMITS.maxRules})` });\n continue;\n }\n const reason = ruleProblem(rule);\n if (reason) rejected.push({ id: idOf(rule), reason });\n else firewall.push(rule);\n }\n\n const whitelists = [];\n for (const wl of inWhitelists) {\n if (whitelists.length >= LIMITS.maxWhitelists) {\n rejected.push({ id: idOf(wl), reason: `bundle exceeds maxWhitelists (${LIMITS.maxWhitelists})` });\n continue;\n }\n // A whitelist SUPPRESSES rules, so a malformed one is a protection risk, not a detection risk.\n // One with no `rule_id` applies to EVERY rule — a single tripped condition disables the whole\n // firewall for that request — so it must be opted into explicitly.\n if (!opts.allowGlobalWhitelists && wl && Array.isArray(wl.rule_v2) && !wl.rule_id) {\n rejected.push({ id: idOf(wl), reason: 'whitelist has no rule_id (would suppress every rule); set allowGlobalWhitelists to permit' });\n continue;\n }\n const reason = conditionsProblem(wl?.rule_v2);\n if (reason) rejected.push({ id: idOf(wl), reason: `whitelist: ${reason}` });\n else whitelists.push(wl);\n }\n\n return {\n bundle: { ...bundle, firewall, whitelists },\n rejected,\n };\n}\n\nfunction idOf(rule) {\n const id = rule?.id ?? rule?.rule_id;\n return id === undefined || id === null ? '(unidentified)' : String(id);\n}\n\n/** @returns {string|null} a reason the rule must be dropped, or null when it's acceptable. */\nfunction ruleProblem(rule) {\n if (!rule || typeof rule !== 'object') return 'not an object';\n if (rule.phase !== undefined && !PHASES.has(rule.phase)) return `unknown phase \"${rule.phase}\"`;\n if (rule.action !== undefined && !ACTIONS.has(rule.action)) return `unknown action \"${rule.action}\"`;\n const capOverride = rule.max_bytes;\n if (capOverride !== undefined && !(Number(capOverride) > 0)) return 'max_bytes must be a positive number';\n return conditionsProblem(rule.rule_v2);\n}\n\nfunction conditionsProblem(conditions, depth = 0) {\n if (!Array.isArray(conditions)) return 'rule_v2 must be an array of conditions';\n if (conditions.length === 0) return 'rule_v2 is empty (would never match)';\n if (depth > LIMITS.maxNestingDepth) return `nesting deeper than ${LIMITS.maxNestingDepth}`;\n if (conditions.length > LIMITS.maxConditionsPerRule) {\n return `more than ${LIMITS.maxConditionsPerRule} conditions`;\n }\n for (const c of conditions) {\n if (!c || typeof c !== 'object') return 'condition is not an object';\n if (Array.isArray(c.rules)) {\n const nested = conditionsProblem(c.rules, depth + 1);\n if (nested) return nested;\n continue; // a group carries no match of its own\n }\n const m = c.match;\n if (!m || typeof m !== 'object') return 'condition has no match object';\n if (typeof m.type !== 'string' || m.type === '') return 'match.type must be a non-empty string';\n if (m.type === 'regex') {\n if (typeof m.value !== 'string') return 'regex match.value must be a string';\n if (m.value.length > LIMITS.maxRegexLength) return `regex longer than ${LIMITS.maxRegexLength} chars`;\n } else if (typeof m.value === 'string' && m.value.length > LIMITS.maxValueLength) {\n return `match.value longer than ${LIMITS.maxValueLength} chars`;\n }\n if (m.match) {\n // array_key_value nests a sub-match; count it toward depth so a chain can't be unbounded.\n const nested = conditionsProblem([{ match: m.match }], depth + 1);\n if (nested) return nested;\n }\n }\n return null;\n}\n","// Rule source: pick where the ruleset comes from — an explicit bundle, or the live API by site\n// UUID (Pulse) or token — fetch it (conditional/If-None-Match via the persisted etag), and fall\n// back through last-known-good → bundled → empty. Fail-open: a fetch/parse error never throws.\n// The `store` (see ./store.js) is passed in so a refresh reuses the same tiered cache.\nimport { PatchstackRuleClient } from '../engine/index.js';\nimport { PulseRuleClient } from '../engine/pulse-client.js';\nimport { validateBundle } from './validate.js';\n\n// A LIVE update is accepted ATOMICALLY. Dropping individual invalid rules is fine for a bundle we\n// already trust (a cache entry, a bundled fallback), but for a fresh remote response it would let a\n// broken update REPLACE known-good policy with partial or empty policy — turning \"we validated it\" into\n// a loss of protection, and caching that loss. So: if any rule/whitelist fails validation, reject the\n// whole update, keep last-known-good, report it, and do NOT write the cache. Opt in to the old\n// behaviour with `acceptPartialBundle: true` (metrics still report every drop).\nfunction liveUpdateRejections(res, options) {\n if (options.acceptPartialBundle) return [];\n const { rejected } = validateBundle(\n { firewall: Array.isArray(res.firewall) ? res.firewall : [], whitelists: Array.isArray(res.whitelists) ? res.whitelists : [] },\n { allowGlobalWhitelists: options.allowGlobalWhitelists },\n );\n return rejected;\n}\n\nfunction reportRejections(rejected, options, label) {\n const report = options.onRuleRejected;\n for (const r of rejected) {\n if (typeof report === 'function') {\n try { report({ ...r, accepted: false }); } catch { /* reporting must never break rule loading */ }\n }\n }\n const sample = rejected.slice(0, 3).map((r) => `${r.id} (${r.reason})`).join('; ');\n options.onError?.(new Error(\n `${label}: rejected the entire update because ${rejected.length} rule(s) failed validation — ` +\n `keeping the previous ruleset and NOT caching this response: ${sample}${rejected.length > 3 ? ', …' : ''}`,\n ));\n}\n\nexport async function resolveRules(options, store, ctx = {}) {\n // The INITIAL load is on the app's startup path, so the runtime gives it a short budget (see\n // bootTimeoutMs) and falls back to cache/bundled rather than delaying boot; refreshes get the full\n // budget. A timeout here is not a protection gap by itself — last-known-good still applies.\n const timeoutMs = ctx.timeoutMs;\n if (options.siteUuid) {\n const prior = await store.read(); // { bundle, etag } | null\n const client = new PulseRuleClient({ siteUuid: options.siteUuid, baseUrl: options.pulseRulesUrl, etag: prior?.etag, timeoutMs, pulseAuth: ctx.pulseAuth });\n const res = await client.getRules();\n if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (res.success && !res.notModified) {\n const rejected = liveUpdateRejections(res, options);\n if (rejected.length > 0) {\n reportRejections(rejected, options, 'rule update rejected');\n if (prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (options.rules) return normalizeBundle(options.rules, options);\n return emptyBundle();\n }\n const bundle = normalizeBundle(res, options);\n await store.write({ bundle, etag: res.etag ?? null });\n return bundle;\n }\n if (prior?.bundle) {\n options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); using cached bundle`));\n return normalizeBundle(prior.bundle, options);\n }\n if (options.rules) {\n options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); using bundled fallback`));\n return normalizeBundle(options.rules, options);\n }\n options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); no cache — running with no rules`));\n return emptyBundle();\n }\n\n if (options.token) {\n const prior = await store.read();\n const client = new PatchstackRuleClient({ token: options.token, baseUrl: options.baseUrl, etag: prior?.etag, timeoutMs });\n const res = await client.getRules();\n if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (res.success && !res.notModified) {\n const rejected = liveUpdateRejections(res, options);\n if (rejected.length > 0) {\n reportRejections(rejected, options, 'rule update rejected');\n if (prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (options.rules) return normalizeBundle(options.rules, options);\n return emptyBundle();\n }\n const bundle = normalizeBundle(res, options);\n await store.write({ bundle, etag: res.etag ?? null });\n return bundle;\n }\n if (prior?.bundle) {\n options.onError?.(new Error(`rule fetch failed (${res.error ?? 'no usable response'}); using cached bundle`));\n return normalizeBundle(prior.bundle, options);\n }\n options.onError?.(new Error(`rule fetch failed (${res.error ?? 'no usable response'}); no cache — running with no rules`));\n return emptyBundle();\n }\n\n if (options.rules) {\n return normalizeBundle(options.rules, options);\n }\n\n return emptyBundle();\n}\n\n// Every rule path (live fetch, cache, bundled fallback) funnels through here, so this is where the\n// delivered policy is VALIDATED before the engine ever executes it: bounded rule count / conditions /\n// nesting / pattern length, known phases + actions. A rule that fails is dropped with a reported reason\n// (`onRuleRejected`) rather than silently kept — an unenforceable rule must never look enforced.\nexport function normalizeBundle(b, options = {}) {\n const enforcement = b?.enforcement ?? b?.mode;\n const { bundle: checked, rejected } = validateBundle(\n {\n firewall: Array.isArray(b.firewall) ? b.firewall : [],\n whitelists: Array.isArray(b.whitelists) ? b.whitelists : [],\n },\n { allowGlobalWhitelists: options.allowGlobalWhitelists },\n );\n if (rejected.length > 0) {\n const report = options.onRuleRejected;\n if (typeof report === 'function') {\n for (const r of rejected) {\n try { report(r); } catch { /* reporting must never break rule loading */ }\n }\n } else {\n const sample = rejected.slice(0, 3).map((r) => `${r.id} (${r.reason})`).join('; ');\n // eslint-disable-next-line no-console\n console.warn(\n `[patchstack] ${rejected.length} delivered rule(s) rejected as invalid/oversized and are NOT enforced: ${sample}` +\n (rejected.length > 3 ? ', …' : ''),\n );\n }\n }\n return {\n firewall: checked.firewall,\n whitelists: checked.whitelists,\n whitelist_keys: b.whitelist_keys ?? {},\n ...(enforcement === 'block' || enforcement === 'dry-run' ? { enforcement } : {}),\n };\n}\n\nexport function emptyBundle() {\n return { firewall: [], whitelists: [], whitelist_keys: {} };\n}\n","// Rule refresh scheduling — how a long-lived guard picks up rules that become relevant after boot\n// (a dependency added mid-session, a zero-day published). Two triggers, both driving the same\n// caller-supplied `tick` (which re-fetches + hot-swaps the engines):\n// - startRefresh: a self-scheduling poll LOOP (reschedules after each tick settles, so runs never\n// overlap), with ±jitter (avoid a thundering herd) and exponential backoff on consecutive\n// failures. `unref`'d — it never keeps the process alive.\n// - makeRefreshHandler: a PUSH endpoint — an authenticated fetch handler the platform/SaaS hits\n// for an immediate refresh (zero-day fast lane) instead of waiting for the next poll.\n\nconst JITTER_FRACTION = 0.1;\nconst MAX_BACKOFF_MULTIPLIER = 8; // cap consecutive-failure backoff at 8× the base interval\n\nexport function startRefresh(tick, { refreshMs, onError } = {}) {\n let stopped = false;\n let failures = 0;\n let timer = null;\n\n const schedule = () => {\n if (stopped) return;\n const backoff = Math.min(2 ** failures, MAX_BACKOFF_MULTIPLIER);\n const jitter = 1 - Math.random() * JITTER_FRACTION; // shorten by up to 10% so clients don't align\n const delay = refreshMs * backoff * jitter;\n timer = setTimeout(run, delay);\n timer?.unref?.();\n };\n\n const run = async () => {\n if (stopped) return;\n try {\n await tick();\n failures = 0;\n } catch (err) {\n failures++;\n onError?.(err);\n }\n schedule();\n };\n\n schedule();\n return {\n stop() {\n stopped = true;\n if (timer) clearTimeout(timer);\n },\n };\n}\n\nexport function makeRefreshHandler(tick, secret) {\n return async (request) => {\n // No secret configured → the endpoint doesn't exist (never an open refresh-DoS surface).\n if (!secret) return new Response('not found', { status: 404 });\n let provided = null;\n try {\n provided = request?.headers?.get?.('x-patchstack-refresh') ?? new URL(request.url).searchParams.get('token');\n } catch {\n provided = null;\n }\n if (provided !== secret) return new Response('forbidden', { status: 403 });\n let refreshed = true;\n try {\n await tick();\n } catch {\n refreshed = false; // fail-open: report the outcome, never throw\n }\n return new Response(JSON.stringify({ refreshed }), { status: 200, headers: { 'content-type': 'application/json' } });\n };\n}\n","import { isSafeOrigin } from './safe-origin.js';\n// Fire-and-forget reporter: Connect runtime → existing connector POST /api/logs/log\n// (same path WordPress uses). Auth: WP-style api_key (`{secret}-{oauth.id}`) →\n// POST /oauth/token (client_credentials) → Bearer JWT on /api/logs/log.\n// Opt out: PATCHSTACK_TELEMETRY=off. Never put api_key in the public widget.\n\nconst DEFAULT_API_BASE = 'https://api.patchstack.com';\nconst DEFAULT_FLUSH_MS = 1000;\nconst MAX_BATCH = 50;\nconst TOKEN_SKEW_MS = 60_000;\n\n/**\n * Parse WP plugin api_key (`{secret}-{oauth.id}`) into client credentials.\n * @param {string} apiKey\n * @returns {{ clientId: string, clientSecret: string } | null}\n */\nexport function parseApiKey(apiKey) {\n if (typeof apiKey !== 'string' || apiKey.length === 0) return null;\n const idx = apiKey.lastIndexOf('-');\n if (idx <= 0 || idx === apiKey.length - 1) return null;\n const clientSecret = apiKey.slice(0, idx);\n const clientId = apiKey.slice(idx + 1);\n if (!/^\\d+$/.test(clientId) || clientSecret.length === 0) return null;\n return { clientId, clientSecret };\n}\n\n/**\n * Derive api.patchstack.com origin from a Pulse manifest/rules URL override.\n * @param {string | undefined} pulseOrManifestUrl\n */\nexport function resolveApiBase(pulseOrManifestUrl) {\n const fromEnv = typeof process !== 'undefined' ? process.env?.PATCHSTACK_API_BASE : undefined;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) {\n // The site api_key is exchanged for a token against this origin, so a hostile/injected env value\n // would be a credential-exfiltration path. Require HTTPS (localhost excepted for local testing);\n // anything else falls back to the default origin rather than shipping the key off-platform.\n const candidate = fromEnv.replace(/\\/$/, '');\n if (isSafeOrigin(candidate)) return candidate;\n // eslint-disable-next-line no-console\n console.warn(\n '[patchstack] ignoring PATCHSTACK_API_BASE: block-log reporting requires an https origin ' +\n '(or localhost). Falling back to the default API origin.',\n );\n }\n if (typeof pulseOrManifestUrl === 'string' && pulseOrManifestUrl.length > 0) {\n try {\n return new URL(pulseOrManifestUrl).origin;\n } catch {\n /* fall through */\n }\n }\n const endpoint = typeof process !== 'undefined' ? process.env?.PATCHSTACK_ENDPOINT : undefined;\n if (typeof endpoint === 'string' && endpoint.length > 0) {\n try {\n return new URL(endpoint).origin;\n } catch {\n /* fall through */\n }\n }\n return DEFAULT_API_BASE;\n}\n\n/**\n * @param {{\n * apiKey: string,\n * apiBase?: string,\n * sourceHost?: string,\n * fetchImpl?: typeof fetch,\n * flushMs?: number,\n * }} opts\n */\nexport function createFirewallLogReporter(opts) {\n const creds = parseApiKey(opts.apiKey);\n if (!creds) {\n return { record() {}, flush() {}, stop() {} };\n }\n\n const apiBase = (opts.apiBase ?? DEFAULT_API_BASE).replace(/\\/$/, '');\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n const flushMs = Number.isFinite(opts.flushMs) ? opts.flushMs : DEFAULT_FLUSH_MS;\n const sourceHost = typeof opts.sourceHost === 'string' ? opts.sourceHost : '';\n\n /** @type {Array<Record<string, unknown>>} */\n let queue = [];\n /** @type {ReturnType<typeof setTimeout> | null} */\n let timer = null;\n let stopped = false;\n\n /** @type {{ token: string, expiresAt: number } | null} */\n let cachedToken = null;\n /** @type {Promise<string | null> | null} */\n let tokenInflight = null;\n\n const fetchAccessToken = async () => {\n if (cachedToken && Date.now() < cachedToken.expiresAt - TOKEN_SKEW_MS) {\n return cachedToken.token;\n }\n if (tokenInflight) return tokenInflight;\n\n tokenInflight = (async () => {\n try {\n const res = await fetchImpl(`${apiBase}/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify({\n grant_type: 'client_credentials',\n client_id: creds.clientId,\n client_secret: creds.clientSecret,\n }),\n });\n if (!res || !res.ok) return null;\n const body = await res.json();\n const token = body?.access_token;\n if (typeof token !== 'string' || token.length === 0) return null;\n const expiresIn = Number(body?.expires_in);\n const ttlMs = Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn * 1000 : 3600_000;\n cachedToken = { token, expiresAt: Date.now() + ttlMs };\n return token;\n } catch {\n return null;\n } finally {\n tokenInflight = null;\n }\n })();\n\n return tokenInflight;\n };\n\n const flush = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (queue.length === 0 || typeof fetchImpl !== 'function') return;\n\n const batch = queue.splice(0, MAX_BATCH);\n void (async () => {\n const token = await fetchAccessToken();\n if (!token) return;\n\n const body = new URLSearchParams();\n body.set('type', 'firewall');\n body.set('logs', JSON.stringify(batch));\n\n try {\n const p = fetchImpl(`${apiBase}/api/logs/log`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'User-Agent': '@patchstack/connect',\n ...(sourceHost ? { 'Source-Host': sourceHost } : {}),\n },\n body,\n });\n if (p && typeof p.then === 'function') p.catch(() => {});\n } catch {\n /* ignore */\n }\n })();\n };\n\n return {\n /**\n * @param {{\n * rule?: { id?: string | number },\n * method?: string | null,\n * path?: string | null,\n * ip?: string | null,\n * userAgent?: string | null,\n * }} event\n */\n record(event) {\n if (stopped) return;\n const fid = event?.rule?.id;\n if (fid === undefined || fid === null || fid === '') return;\n\n queue.push({\n fid,\n method: event.method ?? null,\n request_uri: event.path ?? null,\n ip: event.ip ?? null,\n user_agent: event.userAgent ?? null,\n log_date: new Date().toISOString(),\n });\n\n if (queue.length >= MAX_BATCH) {\n flush();\n return;\n }\n if (!timer) timer = setTimeout(flush, flushMs);\n },\n flush,\n stop() {\n stopped = true;\n flush();\n },\n };\n}\n\n/** @returns {boolean} */\nexport function telemetryEnabled() {\n const v = typeof process !== 'undefined' ? process.env?.PATCHSTACK_TELEMETRY : undefined;\n if (v === undefined || v === '') return true;\n return !/^(0|false|off|no)$/i.test(String(v));\n}\n","// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\n//\n// The vibe-coded app's browser talks straight to Supabase, bypassing the app's own server —\n// so a normal in-app WAF never sees the data traffic. The installer patches the generated\n// Supabase client to tunnel every call through this guard (running in the app's own server /\n// Worker). The guard runs the Patchstack protection policy on the tunneled request, then\n// forwards it to Supabase — pinned to the app's own project so it can't be turned into an\n// open proxy (SSRF).\n//\n// The heavy lifting (rule evaluation, dry-run/block, fail-open) is `protection.fetchGuard()`\n// from `createProtection` — this module is just the Supabase-specific tunnel around it.\n\nexport const GUARD_PATH = '/_patchstack/guard';\n\n// Headers we must not copy verbatim when re-emitting the upstream response.\nconst HOP_BY_HOP = new Set(['content-encoding', 'content-length', 'transfer-encoding', 'connection']);\n\n/**\n * @param {object} opts\n * @param {{ fetchGuard: () => (req: Request) => Promise<Response|null> }} opts.protection a createProtection() result\n * @param {string|undefined} opts.supabaseUrl the app's Supabase project URL (server-side env) — the only allowed forward target\n * @param {typeof fetch} [opts.fetchImpl] injectable fetch (tests)\n * @returns {(request: Request) => Promise<Response>}\n */\nexport function createSupabaseGuard({ protection, supabaseUrl, fetchImpl = fetch }) {\n const guard = protection.fetchGuard();\n const allowedOrigin = supabaseUrl ? new URL(supabaseUrl).origin : null;\n\n return async function handleGuardRequest(request) {\n const target = request.headers.get('x-ps-target');\n if (!target) return new Response('patchstack: missing x-ps-target', { status: 400 });\n\n let targetUrl;\n try {\n targetUrl = new URL(target);\n } catch {\n return new Response('patchstack: invalid target', { status: 400 });\n }\n\n // SSRF pin: only ever forward to the app's own Supabase project (origin from server-side\n // env, never the client-supplied header). Anything else — internal hosts, cloud metadata,\n // a different scheme — is rejected before any outbound request.\n if (!allowedOrigin || targetUrl.protocol !== 'https:' || targetUrl.origin !== allowedOrigin) {\n return new Response('patchstack: target not allowed', { status: 403 });\n }\n\n const hasBody = request.method !== 'GET' && request.method !== 'HEAD';\n const bodyText = hasBody ? await request.text() : '';\n\n // Evaluate the tunneled call against the policy. fetchGuard returns a 403 Response when it\n // blocks (block mode + match), or null to allow (allow, or dry-run — it records via onDetect).\n const evalReq = new Request(targetUrl.toString(), {\n method: request.method,\n headers: request.headers,\n body: hasBody ? bodyText : undefined,\n });\n const blocked = await guard(evalReq);\n if (blocked) return blocked;\n\n // Allowed → forward to Supabase, server-side.\n const forwardHeaders = new Headers(request.headers);\n forwardHeaders.delete('x-ps-target');\n forwardHeaders.delete('host');\n const upstream = await fetchImpl(targetUrl.toString(), {\n method: request.method,\n headers: forwardHeaders,\n body: hasBody ? bodyText : undefined,\n redirect: 'manual',\n });\n\n const outHeaders = new Headers();\n upstream.headers.forEach((value, key) => {\n if (!HOP_BY_HOP.has(key.toLowerCase())) outHeaders.set(key, value);\n });\n const buf = await upstream.arrayBuffer();\n const forwarded = new Response(buf, { status: upstream.status, statusText: upstream.statusText, headers: outHeaders });\n\n // Response phase: screen what Supabase returned (query results can leak secrets/PII) —\n // redact the offending spans / withhold, per the response rules. Fail-open if unavailable.\n return protection.screenResponse ? protection.screenResponse(forwarded) : forwarded;\n };\n}\n"],"mappings":"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","names":["path","path","path","import_promises","import_node_path","path","import_promises","import_node_path","path","import_promises","import_node_path","import_promises","path","import_promises","import_node_path","path","import_promises","import_node_path","warnOnce","path","DEFAULT_BASE_URL","DEFAULT_CACHE_TTL","JITTER_FRACTION","parseCookies","path","path","JITTER_FRACTION","TOKEN_SKEW_MS","allow","walk","path"]}
1
+ {"version":3,"sources":["../src/pulse-token.ts","../src/types.ts","../src/client.ts","../src/config.ts","../src/parsers/consistency.ts","../src/parsers/npm.ts","../src/parsers/node_modules.ts","../src/parsers/pnpm.ts","../src/parsers/yarn.ts","../src/parsers/index.ts","../src/normalize.ts","../src/protect/refresh-manifest.ts","../src/protect/runtime.js","../src/protect/safe-origin.js","../src/protect/engine/client.js","../src/protect/engine/request.js","../src/protect/engine/normalizer.js","../src/protect/engine/engine.js","../src/protect/engine/pulse-client.js","../src/protect/engine/fetch.js","../src/protect/engine/node.js","../src/protect/egress.js","../src/protect/defaults.js","../src/protect/block-page.js","../src/protect/rules/store.js","../src/protect/rules/validate.js","../src/protect/rules/source.js","../src/protect/rules/refresh.js","../src/protect/firewall-log.js","../src/protect/supabase-guard.js"],"sourcesContent":["import type { Config } from './types.js';\n\n/**\n * Bearer tokens for the authenticated Pulse endpoints (ADR-0018).\n *\n * Deliberately separate from the block-log token flow in\n * `src/protect/firewall-log.js`: that path talks to the auth/ Lambda's\n * /oauth/token and must keep working exactly as it does today.\n */\n\nconst TOKEN_SKEW_MS = 60_000;\n\n/** Build the Pulse token URL corresponding to a manifest endpoint override. */\nexport function buildTokenUrl(manifestEndpoint: string): string {\n const url = new URL(manifestEndpoint);\n const path = url.pathname.replace(/\\/$/, '');\n url.pathname = path.endsWith('/manifest')\n ? `${path.slice(0, -'/manifest'.length)}/token`\n : '/monitor/pulse/token';\n url.search = '';\n url.hash = '';\n return url.toString();\n}\n\n/** Split the WP-format `{secret}-{oauth.id}` credential on its last hyphen. */\nexport function parsePulseAuth(credential: string): { clientId: string; clientSecret: string } | null {\n const index = credential.lastIndexOf('-');\n if (index <= 0 || index === credential.length - 1) return null;\n\n const clientId = credential.slice(index + 1);\n if (!/^\\d+$/.test(clientId)) return null;\n\n return { clientId, clientSecret: credential.slice(0, index) };\n}\n\nlet cached: { token: string; expiresAt: number } | null = null;\nlet inflight: Promise<string | null> | null = null;\n\n/** Drops the cached token. Exported for tests and for 401 handling. */\nexport function clearPulseToken(): void {\n cached = null;\n}\n\n/**\n * Resolve a bearer token for `config.pulseAuth`, exchanging one if needed.\n *\n * Returns null whenever a token cannot be obtained — no credential, a rejected\n * exchange, a network failure. Callers then send the request unauthenticated,\n * which the server still accepts while it runs dual-accept.\n */\nexport async function getPulseToken(\n config: Config,\n fetchImpl: typeof fetch = fetch,\n): Promise<string | null> {\n // Not `=== null`: Config is public, so callers can hand us an object that\n // predates this field, and an unusable credential must never throw here.\n if (typeof config.pulseAuth !== 'string' || config.pulseAuth.length === 0) return null;\n\n if (cached !== null && Date.now() < cached.expiresAt - TOKEN_SKEW_MS) {\n return cached.token;\n }\n if (inflight !== null) return inflight;\n\n const credentials = parsePulseAuth(config.pulseAuth);\n if (credentials === null) return null;\n\n inflight = (async () => {\n try {\n const response = await fetchImpl(buildTokenUrl(config.endpoint), {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify({\n grant_type: 'client_credentials',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n }),\n signal: AbortSignal.timeout(config.timeoutMs),\n });\n\n if (!response.ok) return null;\n\n const body = (await response.json()) as { access_token?: unknown; expires_in?: unknown };\n if (typeof body.access_token !== 'string' || body.access_token.length === 0) return null;\n\n const expiresIn = Number(body.expires_in);\n const ttlMs = Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn * 1000 : 3600_000;\n cached = { token: body.access_token, expiresAt: Date.now() + ttlMs };\n\n return body.access_token;\n } catch {\n return null;\n } finally {\n inflight = null;\n }\n })();\n\n return inflight;\n}\n\n/**\n * `Authorization` header for a Pulse request, or `{}` when unauthenticated.\n * Spread into an existing header object so call sites stay one line.\n */\nexport async function pulseAuthHeader(\n config: Config,\n fetchImpl: typeof fetch = fetch,\n): Promise<Record<string, string>> {\n const token = await getPulseToken(config, fetchImpl);\n return token === null ? {} : { Authorization: `Bearer ${token}` };\n}\n\n/**\n * Send a Pulse request, attaching the bearer token and retrying once if the\n * server rejects it.\n *\n * A cached token can stop being valid before it expires — the credential may\n * have been rotated or revoked meanwhile — so the server's 401 is authoritative\n * over our own clock. Without this a long-running process would keep presenting\n * a dead token until its local expiry.\n *\n * Only 401 retries: a 403 is a scope or site mismatch, which a fresh token\n * would not fix.\n */\nexport async function pulseFetch(\n config: Config,\n url: string,\n init: RequestInit,\n fetchImpl: typeof fetch = fetch,\n): Promise<Response> {\n const send = async () => {\n const auth = await pulseAuthHeader(config, fetchImpl);\n const response = await fetchImpl(url, {\n ...init,\n headers: { ...(init.headers as Record<string, string> | undefined), ...auth },\n });\n\n return { response, authenticated: auth.Authorization !== undefined };\n };\n\n const first = await send();\n\n // Retrying an unauthenticated request would just repeat it: the 401 was\n // about something other than our token.\n if (first.response.status === 401 && first.authenticated) {\n clearPulseToken();\n\n return (await send()).response;\n }\n\n return first.response;\n}\n","export type Ecosystem = 'npm' | 'composer';\n\n/**\n * Which environment a manifest was captured in. The connector reports from real\n * builds (prebuild scan / build hooks), so it defaults to 'production'. Override\n * with PATCHSTACK_ENVIRONMENT=sandbox (or \"environment\" in .patchstackrc.json)\n * for test manifests.\n */\nexport type Environment = 'production' | 'sandbox';\n\nexport interface PackageEntry {\n name: string;\n version: string;\n path?: string;\n direct?: boolean;\n}\n\nexport interface Manifest {\n ecosystem: Ecosystem;\n packages: PackageEntry[];\n /**\n * Human-readable scan diagnostics — set when the preferred lockfile looked\n * stale (missing dependencies declared in package.json) and another source\n * was used, or when no fully-consistent source existed. Never fatal.\n */\n warnings?: string[];\n}\n\nexport interface Config {\n /**\n * The site UUID. `null` means we don't have one yet — `postManifest` will then\n * post to the bare endpoint, the server will provision a fresh site, and the\n * UUID it returns should be persisted via `persistSiteUuid()`.\n */\n siteUuid: string | null;\n /**\n * WP-format site API key (`{oauth.secret}-{oauth.id}`) for authenticated\n * block-log reporting via connector `/api/logs/log`. Issued once as `api_key`\n * on first provision. Prefer `PATCHSTACK_API_KEY` in production deploys.\n */\n apiKey: string | null;\n /**\n * Credential for the authenticated Pulse endpoints (ADR-0018). Exchanged for\n * a short-lived bearer token at `monitor/pulse/token`; never sent directly.\n * Falls back to `apiKey` when unset. Prefer `PATCHSTACK_PULSE_AUTH`.\n */\n pulseAuth: string | null;\n endpoint: string;\n timeoutMs: number;\n /** Environment to report the manifest under. Defaults to 'production'. */\n environment: Environment;\n /**\n * Whether the connector manages the disclosure-widget tag (source shell on\n * `scan`, built HTML on `mark-build`). Defaults to true; persist\n * `\"widget\": false` in .patchstackrc.json for dependency-scanning only.\n */\n widget: boolean;\n}\n\nexport interface StoreManifestResponse {\n /** The UUID of the site the manifest was stored against. Always returned. */\n uuid?: string;\n /**\n * WP-format API key for connector log ingest. Present only when oauth\n * credentials are created in this request (first provision / backfill).\n */\n api_key?: string;\n stored: boolean;\n manifest_id?: number;\n checksum?: string;\n reason?: string;\n message?: string;\n error?: string;\n}\n\nexport class PatchstackError extends Error {\n constructor(\n message: string,\n public readonly code:\n | 'CONFIG_MISSING'\n | 'CONFIG_INVALID'\n | 'LOCKFILE_NOT_FOUND'\n | 'LOCKFILE_UNSUPPORTED'\n | 'LOCKFILE_PARSE_ERROR'\n | 'NETWORK_ERROR'\n | 'NETWORK_TIMEOUT'\n | 'SITE_NOT_FOUND'\n | 'VALIDATION_ERROR'\n | 'SERVER_ERROR',\n public readonly cause?: unknown,\n ) {\n super(message);\n this.name = 'PatchstackError';\n }\n}\n","import { PatchstackError, type Config, type StoreManifestResponse } from './types.js';\nimport type { WirePayload } from './normalize.js';\nimport { pulseFetch } from './pulse-token.js';\n\nexport const DEFAULT_ENDPOINT = 'https://api.patchstack.com/monitor/pulse/manifest';\nexport const DEFAULT_TIMEOUT_MS = 30_000;\n\nexport function buildEndpointUrl(base: string, siteUuid?: string | null): string {\n const trimmed = base.replace(/\\/$/, '');\n return siteUuid !== undefined && siteUuid !== null && siteUuid.length > 0\n ? `${trimmed}/${encodeURIComponent(siteUuid)}`\n : trimmed;\n}\n\n/** Build the live Pulse rules URL corresponding to a manifest endpoint override. */\nexport function buildRulesUrl(manifestEndpoint: string, siteUuid: string): string {\n const url = new URL(manifestEndpoint);\n const path = url.pathname.replace(/\\/$/, '');\n url.pathname = path.endsWith('/manifest')\n ? `${path.slice(0, -'/manifest'.length)}/rules/${encodeURIComponent(siteUuid)}`\n : `/monitor/pulse/rules/${encodeURIComponent(siteUuid)}`;\n url.search = '';\n url.hash = '';\n return url.toString();\n}\n\n/**\n * Build the claim URL for a site. The claim page lives on the same origin as\n * the API endpoint, at `/monitor/claim?site=<uuid>`. Using the API endpoint's\n * origin (rather than a hard-coded https://api.patchstack.com) means staging,\n * ngrok tunnels and local dev environments all produce a claim URL on the same\n * host the connector is already talking to.\n */\nexport function buildClaimUrl(endpoint: string, siteUuid: string): string {\n const origin = new URL(endpoint).origin;\n return `${origin}/monitor/claim?site=${encodeURIComponent(siteUuid)}`;\n}\n\n/**\n * Build the public widget-settings URL for a site. Like the claim URL, it\n * lives on the API endpoint's origin, at `/monitor/widget/settings/<uuid>`.\n */\nexport function buildSettingsUrl(endpoint: string, siteUuid: string): string {\n const origin = new URL(endpoint).origin;\n return `${origin}/monitor/widget/settings/${encodeURIComponent(siteUuid)}`;\n}\n\n/** Build the package-removed signal URL corresponding to a manifest endpoint override. */\nexport function buildPackageRemovedUrl(manifestEndpoint: string, siteUuid: string): string {\n const url = new URL(manifestEndpoint);\n const path = url.pathname.replace(/\\/$/, '');\n url.pathname = path.endsWith('/manifest')\n ? `${path.slice(0, -'/manifest'.length)}/package-removed/${encodeURIComponent(siteUuid)}`\n : `/monitor/pulse/package-removed/${encodeURIComponent(siteUuid)}`;\n url.search = '';\n url.hash = '';\n return url.toString();\n}\n\n/** Build the input-map ingest URL corresponding to a manifest endpoint override. */\nexport function buildInputMapUrl(manifestEndpoint: string, siteUuid: string): string {\n const url = new URL(manifestEndpoint);\n const path = url.pathname.replace(/\\/$/, '');\n url.pathname = path.endsWith('/manifest')\n ? `${path.slice(0, -'/manifest'.length)}/input-map/${encodeURIComponent(siteUuid)}`\n : `/monitor/pulse/input-map/${encodeURIComponent(siteUuid)}`;\n url.search = '';\n url.hash = '';\n return url.toString();\n}\n\n/**\n * Outcome of uploading an attack-surface map. `unchanged` is a first-class result, not a failure: most\n * builds do not change the surface, and the server keeps one revision per distinct surface rather than\n * one per deploy. `skipped` covers the cases where there is nothing to send or nowhere to send it.\n */\nexport type InputMapUploadOutcome =\n | { result: 'stored'; revision: number }\n | { result: 'unchanged'; revision: number }\n | { result: 'skipped'; message: string }\n | { result: 'failed'; message: string };\n\n/**\n * Upload an attack-surface map for this site.\n *\n * Fail-open by construction: every failure path returns a result rather than throwing, because this runs\n * during someone's build and a Patchstack outage must never break it. The caller decides what to print.\n */\nexport async function postInputMap(\n config: Config,\n map: { version: number; endpoints: unknown[] },\n): Promise<InputMapUploadOutcome> {\n if (config.siteUuid === null) {\n return { result: 'skipped', message: 'No site UUID configured — run `patchstack-connect scan` first.' };\n }\n\n const url = buildInputMapUrl(config.endpoint, config.siteUuid);\n try {\n const response = await pulseFetch(config, url, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify(map),\n signal: AbortSignal.timeout(config.timeoutMs),\n });\n if (response.status === 404) {\n return { result: 'failed', message: 'Site not found — the configured site UUID is unknown to Patchstack.' };\n }\n if (response.status === 422) {\n // The server implements a different map schema than this client emits. Say so plainly: it means\n // one side is out of date, and guessing at compatibility is how a consumer misreads a document.\n return { result: 'failed', message: `Patchstack does not accept this map schema (version ${map.version}). Update @patchstack/connect.` };\n }\n if (!response.ok) {\n return { result: 'failed', message: `Patchstack returned ${response.status}.` };\n }\n const body = (await response.json()) as { result?: string; revision?: number };\n // The revision is part of the contract, not decoration: \"stored, revision 0\" is not a state the server\n // can be in, so accepting it would report a successful upload that cannot be pointed at afterwards.\n const revision = Number(body.revision);\n if ((body.result === 'stored' || body.result === 'unchanged') && Number.isInteger(revision) && revision > 0) {\n return { result: body.result, revision };\n }\n return { result: 'failed', message: 'Patchstack returned an unexpected response.' };\n } catch {\n return { result: 'failed', message: `Could not reach Patchstack at ${url}.` };\n }\n}\n\n/**\n * Outcome of the package-removed signal. 'deleted' — the site was unclaimed\n * and its record was removed; 'flagged' — the site is claimed, so it was only\n * marked for its owner to confirm removal in the dashboard; 'gone' — the\n * record no longer existed; 'failed' — Patchstack could not be reached or\n * returned an unexpected response.\n */\nexport interface PackageRemovedOutcome {\n result: 'deleted' | 'flagged' | 'gone' | 'failed';\n message: string | null;\n}\n\n/**\n * Tell Patchstack the @patchstack/connect package is being uninstalled from\n * this project. The site UUID is the only credential, so the server deletes\n * only unclaimed (anonymous) sites; claimed sites are merely flagged for\n * their owner. Never throws — an unreachable server must not block the local\n * uninstall.\n */\nexport async function postPackageRemoved(config: Config): Promise<PackageRemovedOutcome> {\n if (config.siteUuid === null) {\n return { result: 'failed', message: 'No site UUID configured.' };\n }\n\n const url = buildPackageRemovedUrl(config.endpoint, config.siteUuid);\n try {\n const response = await pulseFetch(config, url, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n signal: AbortSignal.timeout(config.timeoutMs),\n });\n if (response.status === 404) {\n return { result: 'gone', message: null };\n }\n if (!response.ok) {\n return { result: 'failed', message: `Patchstack returned ${response.status}.` };\n }\n const body = (await response.json()) as { status?: string; message?: string };\n if (body.status === 'deleted' || body.status === 'flagged') {\n return { result: body.status, message: body.message ?? null };\n }\n return { result: 'failed', message: 'Patchstack returned an unexpected response.' };\n } catch {\n return { result: 'failed', message: `Could not reach Patchstack at ${url}.` };\n }\n}\n\n/**\n * Whether the site record still exists on Patchstack's side. Removing a site\n * (dashboard delete or the widget's uninstall flow) only deletes the remote\n * record — the local integration files stay in the project — so this is the\n * signal that distinguishes \"removed from Patchstack\" from \"still active\".\n */\nexport type SiteStatus = 'active' | 'removed' | 'unknown';\n\n/**\n * Check the remote site status via the public widget-settings endpoint:\n * 200 means the site record exists, 404 means it was removed. Any other\n * response or a network failure is 'unknown' — never throws. The endpoint's\n * 200 responses are cacheable for an hour, so a cache-busting query param\n * keeps intermediaries from reporting a just-removed site as active.\n */\nexport async function fetchSiteStatus(config: Config): Promise<SiteStatus> {\n if (config.siteUuid === null) return 'unknown';\n\n const url = new URL(buildSettingsUrl(config.endpoint, config.siteUuid));\n url.searchParams.set('t', Date.now().toString());\n\n try {\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'Cache-Control': 'no-cache',\n 'User-Agent': '@patchstack/connect',\n },\n signal: AbortSignal.timeout(config.timeoutMs),\n });\n if (response.status === 404) return 'removed';\n if (response.ok) return 'active';\n return 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nexport async function postManifest(\n config: Config,\n payload: WirePayload,\n): Promise<StoreManifestResponse> {\n const url = buildEndpointUrl(config.endpoint, config.siteUuid);\n const timeoutMs = config.timeoutMs;\n\n let response: Response;\n try {\n // Unauthenticated on the bootstrap POST: there is no credential until this\n // request issues one.\n response = await pulseFetch(config, url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify({ ...payload, environment: config.environment }),\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (cause) {\n if (isTimeoutError(cause)) {\n throw new PatchstackError(\n `Patchstack request to ${url} timed out after ${timeoutMs}ms. Override with PATCHSTACK_TIMEOUT_MS.`,\n 'NETWORK_TIMEOUT',\n cause,\n );\n }\n throw new PatchstackError(\n `Could not reach Patchstack at ${url}. Check your network connection.`,\n 'NETWORK_ERROR',\n cause,\n );\n }\n\n const text = await response.text();\n let body: StoreManifestResponse | null = null;\n try {\n body = text.length > 0 ? (JSON.parse(text) as StoreManifestResponse) : null;\n } catch {\n body = null;\n }\n\n if (response.status === 404) {\n throw new PatchstackError(\n body?.error ?? 'Site not found. Check that your site UUID is correct and that the app is registered as a Pulse app in your Patchstack dashboard.',\n 'SITE_NOT_FOUND',\n );\n }\n\n if (response.status === 422) {\n throw new PatchstackError(\n body?.message ?? 'Patchstack rejected the manifest payload (validation failed).',\n 'VALIDATION_ERROR',\n );\n }\n\n if (response.status < 200 || response.status >= 300) {\n throw new PatchstackError(\n `Patchstack returned ${response.status}: ${text.slice(0, 200)}`,\n 'SERVER_ERROR',\n );\n }\n\n if (body === null) {\n throw new PatchstackError('Patchstack returned an empty response.', 'SERVER_ERROR');\n }\n\n return body;\n}\n\nfunction isTimeoutError(cause: unknown): boolean {\n if (cause instanceof Error) {\n return cause.name === 'TimeoutError' || cause.name === 'AbortError';\n }\n return false;\n}\n","import { readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type Config, type Environment } from './types.js';\nimport { DEFAULT_ENDPOINT, DEFAULT_TIMEOUT_MS } from './client.js';\n\nconst CONFIG_FILENAME = '.patchstackrc.json';\n\nexport const DEFAULT_ENVIRONMENT: Environment = 'production';\n\ninterface ConfigFile {\n siteUuid?: string;\n /** WP-format `{secret}-{oauth.id}` for connector /api/logs/log. Server-only. */\n apiKey?: string;\n /**\n * Credential for the authenticated Pulse endpoints (ADR-0018). Same format as\n * `apiKey` and today the same value, but kept as its own field so the Pulse\n * and block-log paths can diverge without disturbing each other. Server-only.\n */\n pulseAuth?: string;\n endpoint?: string;\n timeoutMs?: number;\n environment?: string;\n widget?: boolean;\n}\n\nexport interface ResolveConfigOptions {\n cwd: string;\n cliSiteUuid?: string;\n cliEndpoint?: string;\n /**\n * When true, resolveConfig throws CONFIG_MISSING if no site UUID is configured.\n * Defaults to false: callers that can run without a UUID (the first `scan` after\n * `npm install`) just get `siteUuid: null` back and learn the UUID from the\n * server response.\n */\n requireSiteUuid?: boolean;\n}\n\nexport async function resolveConfig(options: ResolveConfigOptions): Promise<Config> {\n const fromFile = await readConfigFile(options.cwd);\n const fromEnv = readEnv();\n\n const siteUuid =\n options.cliSiteUuid ??\n fromEnv.siteUuid ??\n fromFile.siteUuid ??\n null;\n\n const endpoint =\n options.cliEndpoint ??\n fromEnv.endpoint ??\n fromFile.endpoint ??\n DEFAULT_ENDPOINT;\n\n const timeoutMs = fromEnv.timeoutMs ?? fromFile.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const environmentRaw = fromEnv.environment ?? fromFile.environment;\n if (environmentRaw !== undefined && !isEnvironment(environmentRaw)) {\n throw new PatchstackError(\n `Environment must be \"production\" or \"sandbox\"; got \"${environmentRaw}\".`,\n 'CONFIG_INVALID',\n );\n }\n const environment: Environment = environmentRaw ?? DEFAULT_ENVIRONMENT;\n\n if (siteUuid !== null && siteUuid.length > 0 && !isUuid(siteUuid)) {\n throw new PatchstackError(\n `Site UUID \"${siteUuid}\" does not look like a valid UUID.`,\n 'CONFIG_INVALID',\n );\n }\n\n if (options.requireSiteUuid && (siteUuid === null || siteUuid.length === 0)) {\n throw new PatchstackError(\n 'No site UUID configured. Run `patchstack-connect scan` to provision one, or set PATCHSTACK_SITE_UUID.',\n 'CONFIG_MISSING',\n );\n }\n\n const apiKeyRaw = fromEnv.apiKey ?? fromFile.apiKey ?? null;\n // Falls back to apiKey so sites provisioned before ADR-0018 authenticate\n // without re-provisioning: today both hold the same credential.\n const pulseAuthRaw = fromEnv.pulseAuth ?? fromFile.pulseAuth ?? apiKeyRaw;\n\n return {\n siteUuid: siteUuid === null || siteUuid.length === 0 ? null : siteUuid,\n apiKey: apiKeyRaw === null || apiKeyRaw.length === 0 ? null : apiKeyRaw,\n pulseAuth: pulseAuthRaw === null || pulseAuthRaw.length === 0 ? null : pulseAuthRaw,\n endpoint,\n timeoutMs,\n environment,\n widget: fromFile.widget !== false,\n };\n}\n\nexport async function writeConfigFile(cwd: string, config: ConfigFile): Promise<string> {\n const target = path.join(cwd, CONFIG_FILENAME);\n const content = JSON.stringify(config, null, 2) + '\\n';\n await writeFile(target, content, 'utf8');\n return target;\n}\n\n/**\n * Merge a new siteUuid into the existing `.patchstackrc.json` (or create it).\n * Preserves any `endpoint` / `timeoutMs` / `apiKey` the user already wrote.\n */\nexport async function persistSiteUuid(cwd: string, siteUuid: string): Promise<string> {\n const existing = await readConfigFile(cwd);\n return writeConfigFile(cwd, { ...existing, siteUuid });\n}\n\n/**\n * Persist the WP-format api_key issued at provision. Authenticates both the\n * Pulse endpoints and connector log reporting. Never embed it in the public\n * disclosure widget.\n *\n * Drops any `pulseAuth` written by an earlier version. That field resolves\n * ahead of `apiKey`, so leaving a copy behind after the credential changes\n * would leave Pulse authenticating with the value the server just replaced.\n */\nexport async function persistApiKey(cwd: string, apiKey: string): Promise<string> {\n const { pulseAuth: _dropped, ...existing } = await readConfigFile(cwd);\n return writeConfigFile(cwd, { ...existing, apiKey });\n}\n\n/**\n * Persist a Pulse-specific credential.\n *\n * Only needed when Pulse ingest and block-log reporting must use *different*\n * credentials; they share one today, so `persistApiKey` covers both. Retained\n * for callers that separate them.\n */\nexport async function persistPulseAuth(cwd: string, pulseAuth: string): Promise<string> {\n const existing = await readConfigFile(cwd);\n return writeConfigFile(cwd, { ...existing, pulseAuth });\n}\n\nasync function readConfigFile(cwd: string): Promise<ConfigFile> {\n const target = path.join(cwd, CONFIG_FILENAME);\n let raw: string;\n try {\n raw = await readFile(target, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return {};\n }\n throw new PatchstackError(\n `Could not read ${target}: ${(err as Error).message}`,\n 'CONFIG_INVALID',\n err,\n );\n }\n\n try {\n return JSON.parse(raw) as ConfigFile;\n } catch (err) {\n throw new PatchstackError(\n `Config file ${target} contains invalid JSON.`,\n 'CONFIG_INVALID',\n err,\n );\n }\n}\n\nfunction readEnv(): ConfigFile {\n const timeoutRaw = process.env.PATCHSTACK_TIMEOUT_MS;\n let timeoutMs: number | undefined;\n if (timeoutRaw !== undefined && timeoutRaw.length > 0) {\n const parsed = Number(timeoutRaw);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new PatchstackError(\n `PATCHSTACK_TIMEOUT_MS must be a positive number; got \"${timeoutRaw}\".`,\n 'CONFIG_INVALID',\n );\n }\n timeoutMs = parsed;\n }\n const environmentRaw = process.env.PATCHSTACK_ENVIRONMENT;\n return {\n siteUuid: process.env.PATCHSTACK_SITE_UUID ?? undefined,\n apiKey: process.env.PATCHSTACK_API_KEY ?? undefined,\n pulseAuth: process.env.PATCHSTACK_PULSE_AUTH ?? undefined,\n endpoint: process.env.PATCHSTACK_ENDPOINT ?? undefined,\n timeoutMs,\n environment:\n environmentRaw !== undefined && environmentRaw.length > 0 ? environmentRaw : undefined,\n };\n}\n\nfunction isUuid(value: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);\n}\n\nfunction isEnvironment(value: string): value is Environment {\n return value === 'production' || value === 'sandbox';\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { PackageEntry } from '../types.js';\n\n/**\n * Registry-installable dependency names declared in package.json\n * (dependencies + devDependencies). Non-registry specifiers (file:, link:,\n * workspace:, portal:) are excluded — lockfile parsers skip those too, so\n * their absence from a parsed set says nothing about staleness. Returns []\n * when package.json is missing or unreadable (nothing to validate against).\n */\nexport async function readDeclaredDependencyNames(cwd: string): Promise<string[]> {\n let raw: string;\n try {\n raw = await readFile(path.join(cwd, 'package.json'), 'utf8');\n } catch {\n return [];\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return [];\n }\n if (typeof parsed !== 'object' || parsed === null) {\n return [];\n }\n\n const names = new Set<string>();\n for (const group of ['dependencies', 'devDependencies'] as const) {\n const deps = (parsed as Record<string, unknown>)[group];\n if (typeof deps !== 'object' || deps === null) {\n continue;\n }\n for (const [name, spec] of Object.entries(deps as Record<string, unknown>)) {\n if (typeof spec === 'string' && /^(file|link|workspace|portal):/.test(spec)) {\n continue;\n }\n names.add(name);\n }\n }\n\n return [...names];\n}\n\n/**\n * Declared names absent from a parsed package set. A non-empty result means\n * the source predates package.json — the fossil-lockfile failure mode where\n * e.g. `npm install` planted a package-lock.json once and the platform's\n * native package manager (bun on Lovable) never updates it again.\n */\nexport function missingDependencies(declared: string[], packages: PackageEntry[]): string[] {\n const present = new Set(packages.map((entry) => entry.name));\n return declared.filter((name) => !present.has(name));\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\ninterface LockfileV2Package {\n name?: string;\n version?: string;\n link?: boolean;\n resolved?: string;\n}\n\ninterface LockfileV2 {\n lockfileVersion: number;\n packages?: Record<string, LockfileV2Package>;\n dependencies?: Record<string, LockfileV1Dependency>;\n}\n\ninterface LockfileV1Dependency {\n version: string;\n dependencies?: Record<string, LockfileV1Dependency>;\n}\n\nexport async function parseNpmLockfile(lockfilePath: string): Promise<PackageEntry[]> {\n let raw: string;\n try {\n raw = await readFile(lockfilePath, 'utf8');\n } catch (cause) {\n throw new PatchstackError(`Could not read lockfile at ${lockfilePath}`, 'LOCKFILE_NOT_FOUND', cause);\n }\n\n let parsed: LockfileV2;\n try {\n parsed = JSON.parse(raw) as LockfileV2;\n } catch (cause) {\n throw new PatchstackError(`Lockfile at ${lockfilePath} is not valid JSON`, 'LOCKFILE_PARSE_ERROR', cause);\n }\n\n if (parsed.packages) {\n return extractFromV2(parsed.packages);\n }\n\n if (parsed.dependencies) {\n return extractFromV1(parsed.dependencies);\n }\n\n throw new PatchstackError(\n `Lockfile at ${lockfilePath} has no \"packages\" or \"dependencies\" key`,\n 'LOCKFILE_PARSE_ERROR',\n );\n}\n\nfunction extractFromV2(packages: Record<string, LockfileV2Package>): PackageEntry[] {\n const entries: PackageEntry[] = [];\n\n for (const [pkgPath, pkg] of Object.entries(packages)) {\n if (pkgPath === '') {\n continue;\n }\n if (pkg.link === true) {\n continue;\n }\n if (typeof pkg.version !== 'string' || pkg.version.length === 0) {\n continue;\n }\n\n const name = pkg.name ?? extractNameFromPath(pkgPath);\n if (name === null) {\n continue;\n }\n\n entries.push({\n name,\n version: pkg.version,\n path: pkgPath,\n direct: isDirectV2(pkgPath),\n });\n }\n\n return entries;\n}\n\nfunction extractFromV1(\n deps: Record<string, LockfileV1Dependency>,\n acc: PackageEntry[] = [],\n depth = 0,\n): PackageEntry[] {\n for (const [name, dep] of Object.entries(deps)) {\n if (typeof dep.version === 'string' && dep.version.length > 0) {\n acc.push({ name, version: dep.version, direct: depth === 0 });\n }\n if (dep.dependencies) {\n extractFromV1(dep.dependencies, acc, depth + 1);\n }\n }\n return acc;\n}\n\nfunction extractNameFromPath(pkgPath: string): string | null {\n const segments = pkgPath.split('node_modules' + path.sep === pkgPath ? path.sep : '/');\n const parts = pkgPath.split('/');\n const nmIndex = parts.lastIndexOf('node_modules');\n if (nmIndex === -1 || nmIndex >= parts.length - 1) {\n return segments[segments.length - 1] ?? null;\n }\n const tail = parts.slice(nmIndex + 1);\n if (tail.length === 0) {\n return null;\n }\n const first = tail[0];\n if (first !== undefined && first.startsWith('@') && tail.length >= 2) {\n return `${first}/${tail[1]}`;\n }\n return first ?? null;\n}\n\nfunction isDirectV2(pkgPath: string): boolean {\n const parts = pkgPath.split('/');\n const nmCount = parts.filter((p) => p === 'node_modules').length;\n return nmCount === 1;\n}\n","import { lstat, readFile, readdir, stat } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\nexport async function walkNodeModules(cwd: string): Promise<PackageEntry[]> {\n const root = path.join(cwd, 'node_modules');\n\n try {\n const info = await stat(root);\n if (!info.isDirectory()) {\n throw new PatchstackError(\n `${root} exists but is not a directory.`,\n 'LOCKFILE_NOT_FOUND',\n );\n }\n } catch (cause) {\n if (cause instanceof PatchstackError) {\n throw cause;\n }\n throw new PatchstackError(\n `node_modules/ not found at ${cwd}. Install dependencies first (e.g. \\`bun install\\` or \\`npm install\\`).`,\n 'LOCKFILE_NOT_FOUND',\n cause,\n );\n }\n\n const entries: PackageEntry[] = [];\n await walk(root, entries, 0);\n return entries;\n}\n\nasync function walk(dir: string, acc: PackageEntry[], depth: number): Promise<void> {\n let names: string[];\n try {\n names = await readdir(dir);\n } catch {\n return;\n }\n\n for (const name of names) {\n if (name.startsWith('.')) {\n continue;\n }\n\n const fullPath = path.join(dir, name);\n if (!(await isPlainDirectory(fullPath))) {\n continue;\n }\n\n if (name.startsWith('@')) {\n let subNames: string[];\n try {\n subNames = await readdir(fullPath);\n } catch {\n continue;\n }\n for (const sub of subNames) {\n if (sub.startsWith('.')) {\n continue;\n }\n const scopedDir = path.join(fullPath, sub);\n if (!(await isPlainDirectory(scopedDir))) {\n continue;\n }\n await readPackage(scopedDir, depth, acc);\n await walkNested(scopedDir, acc, depth);\n }\n continue;\n }\n\n await readPackage(fullPath, depth, acc);\n await walkNested(fullPath, acc, depth);\n }\n}\n\nasync function readPackage(\n pkgDir: string,\n depth: number,\n acc: PackageEntry[],\n): Promise<void> {\n let raw: string;\n try {\n raw = await readFile(path.join(pkgDir, 'package.json'), 'utf8');\n } catch {\n return;\n }\n\n let parsed: { name?: unknown; version?: unknown };\n try {\n parsed = JSON.parse(raw) as { name?: unknown; version?: unknown };\n } catch {\n return;\n }\n\n if (typeof parsed.name !== 'string' || parsed.name.length === 0) {\n return;\n }\n if (typeof parsed.version !== 'string' || parsed.version.length === 0) {\n return;\n }\n\n acc.push({\n name: parsed.name,\n version: parsed.version,\n direct: depth === 0,\n });\n}\n\nasync function walkNested(\n pkgDir: string,\n acc: PackageEntry[],\n depth: number,\n): Promise<void> {\n const nested = path.join(pkgDir, 'node_modules');\n if (!(await isPlainDirectory(nested))) {\n return;\n }\n await walk(nested, acc, depth + 1);\n}\n\nasync function isPlainDirectory(dir: string): Promise<boolean> {\n try {\n const info = await lstat(dir);\n return info.isDirectory() && !info.isSymbolicLink();\n } catch {\n return false;\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\n/**\n * Parses pnpm-lock.yaml without pulling in a YAML library. We don't need full\n * YAML semantics — only the `packages:` block (the canonical list of every\n * installed package) and, for direct-dep marking, the dependency sections under\n * `importers:` (v9+) or at the top level (v6-v8 single-project).\n *\n * Supported pnpm-lock key formats:\n * v5: /pkg/1.0.0 /@scope/pkg/1.0.0\n * v6-v8: /pkg@1.0.0 /@scope/pkg@1.0.0 (optional `(peer@x)` suffix)\n * v9: pkg@1.0.0 @scope/pkg@1.0.0 (optional `(peer@x)` suffix, may be quoted)\n */\nexport async function parsePnpmLockfile(lockfilePath: string): Promise<PackageEntry[]> {\n let raw: string;\n try {\n raw = await readFile(lockfilePath, 'utf8');\n } catch (cause) {\n throw new PatchstackError(\n `Could not read lockfile at ${lockfilePath}`,\n 'LOCKFILE_NOT_FOUND',\n cause,\n );\n }\n\n const lines = raw.split(/\\r?\\n/);\n const directNames = collectDirectDepNames(lines);\n const packageKeys = collectPackagesBlockKeys(lines);\n\n if (packageKeys.length === 0) {\n throw new PatchstackError(\n `Lockfile at ${lockfilePath} has no \"packages\" entries`,\n 'LOCKFILE_PARSE_ERROR',\n );\n }\n\n const entries: PackageEntry[] = [];\n for (const key of packageKeys) {\n const parsed = parsePackageKey(key);\n if (parsed === null) {\n continue;\n }\n entries.push({\n name: parsed.name,\n version: parsed.version,\n direct: directNames.has(parsed.name),\n });\n }\n\n return entries;\n}\n\ninterface ParsedKey {\n name: string;\n version: string;\n}\n\nexport function parsePackageKey(rawKey: string): ParsedKey | null {\n let k = rawKey.trim();\n if (k.length === 0) {\n return null;\n }\n\n if (\n (k.startsWith(\"'\") && k.endsWith(\"'\")) ||\n (k.startsWith('\"') && k.endsWith('\"'))\n ) {\n k = k.slice(1, -1);\n }\n\n if (k.startsWith('/')) {\n k = k.slice(1);\n }\n\n // Strip peer-dependency suffix used by v6+: `pkg@1.0.0(peer@1.0.0)(other@2)`.\n const parenIdx = k.indexOf('(');\n if (parenIdx >= 0) {\n k = k.slice(0, parenIdx);\n }\n\n // Split off the optional `@scope/` prefix so the remaining \"body\" contains\n // exactly one separator between the bare name and the version. npm forbids\n // both `@` and `/` inside the bare-name portion, so the first occurrence of\n // either character in the body is unambiguously the name/version separator.\n let scopePrefix = '';\n let body = k;\n if (k.startsWith('@')) {\n const firstSlash = k.indexOf('/');\n if (firstSlash <= 0) {\n return null;\n }\n scopePrefix = k.slice(0, firstSlash + 1);\n body = k.slice(firstSlash + 1);\n }\n\n const slashIdx = body.indexOf('/');\n const atIdx = body.indexOf('@');\n\n let sepIdx: number;\n if (slashIdx < 0 && atIdx < 0) {\n return null;\n } else if (slashIdx < 0) {\n sepIdx = atIdx;\n } else if (atIdx < 0) {\n sepIdx = slashIdx;\n } else {\n sepIdx = Math.min(slashIdx, atIdx);\n }\n\n const name = scopePrefix + body.slice(0, sepIdx);\n let version = body.slice(sepIdx + 1);\n\n // v5 peer suffix uses `_`: `1.0.0_react@18.2.0`.\n const underscoreIdx = version.indexOf('_');\n if (underscoreIdx >= 0) {\n version = version.slice(0, underscoreIdx);\n }\n\n if (name.length === 0 || version.length === 0) {\n return null;\n }\n\n return { name, version };\n}\n\nfunction indentOf(line: string): number {\n let i = 0;\n while (i < line.length && line[i] === ' ') {\n i++;\n }\n return i;\n}\n\nfunction isBlankOrComment(line: string): boolean {\n const trimmed = line.trim();\n return trimmed.length === 0 || trimmed.startsWith('#');\n}\n\n/**\n * Collects keys directly nested under the top-level `packages:` block. Stops\n * when another top-level key (e.g. `snapshots:`) appears.\n */\nfunction collectPackagesBlockKeys(lines: string[]): string[] {\n const keys: string[] = [];\n let inBlock = false;\n let childIndent: number | null = null;\n\n for (const line of lines) {\n if (isBlankOrComment(line)) {\n continue;\n }\n\n const indent = indentOf(line);\n\n if (!inBlock) {\n if (indent === 0 && line.trim() === 'packages:') {\n inBlock = true;\n }\n continue;\n }\n\n if (indent === 0) {\n break;\n }\n\n if (childIndent === null) {\n childIndent = indent;\n }\n\n if (indent !== childIndent) {\n continue;\n }\n\n const content = line.slice(indent);\n if (!content.endsWith(':')) {\n continue;\n }\n\n keys.push(content.slice(0, -1));\n }\n\n return keys;\n}\n\n/**\n * Collects direct dependency names. Looks in two places, since the format\n * differs across lockfile versions and workspaces:\n *\n * 1. `importers:` > `<importer>:` > `{dependencies,devDependencies,optionalDependencies}:`\n * (v9 always, v6-v8 in workspaces)\n * 2. Top-level `{dependencies,devDependencies,optionalDependencies}:`\n * (v6-v8 single-project lockfiles)\n *\n * Direct-dep marking is best-effort: a name absent from both is simply left as\n * not-direct, matching the npm parser's behaviour for transitive packages.\n */\nfunction collectDirectDepNames(lines: string[]): Set<string> {\n const names = new Set<string>();\n collectFromImporters(lines, names);\n for (const section of ['dependencies', 'devDependencies', 'optionalDependencies']) {\n collectFromTopLevelSection(lines, section, names);\n }\n return names;\n}\n\nconst DEP_SECTIONS = new Set([\n 'dependencies',\n 'devDependencies',\n 'optionalDependencies',\n]);\n\nfunction collectFromImporters(lines: string[], out: Set<string>): void {\n let inImporters = false;\n let importerIndent: number | null = null;\n let inDepSection = false;\n let depSectionIndent: number | null = null;\n let leafIndent: number | null = null;\n\n for (const line of lines) {\n if (isBlankOrComment(line)) {\n continue;\n }\n const indent = indentOf(line);\n const trimmed = line.trim();\n\n if (!inImporters) {\n if (indent === 0 && trimmed === 'importers:') {\n inImporters = true;\n }\n continue;\n }\n\n if (indent === 0) {\n break;\n }\n\n // Track the indent at which importer entries (e.g. `.:`, `packages/foo:`) sit.\n if (importerIndent === null) {\n importerIndent = indent;\n }\n\n if (indent === importerIndent) {\n inDepSection = false;\n depSectionIndent = null;\n leafIndent = null;\n continue;\n }\n\n if (!inDepSection) {\n const key = stripTrailingColon(trimmed);\n if (key !== null && DEP_SECTIONS.has(key)) {\n inDepSection = true;\n depSectionIndent = indent;\n }\n continue;\n }\n\n if (depSectionIndent !== null && indent <= depSectionIndent) {\n // Reset — we've exited the dep section. Reconsider this line.\n inDepSection = false;\n depSectionIndent = null;\n leafIndent = null;\n const key = stripTrailingColon(trimmed);\n if (key !== null && DEP_SECTIONS.has(key)) {\n inDepSection = true;\n depSectionIndent = indent;\n }\n continue;\n }\n\n if (leafIndent === null) {\n leafIndent = indent;\n }\n\n if (indent !== leafIndent) {\n continue;\n }\n\n const name = extractLeafName(trimmed);\n if (name !== null) {\n out.add(name);\n }\n }\n}\n\nfunction collectFromTopLevelSection(\n lines: string[],\n section: string,\n out: Set<string>,\n): void {\n let inSection = false;\n let leafIndent: number | null = null;\n\n for (const line of lines) {\n if (isBlankOrComment(line)) {\n continue;\n }\n const indent = indentOf(line);\n const trimmed = line.trim();\n\n if (!inSection) {\n if (indent === 0 && trimmed === `${section}:`) {\n inSection = true;\n }\n continue;\n }\n\n if (indent === 0) {\n break;\n }\n\n if (leafIndent === null) {\n leafIndent = indent;\n }\n\n if (indent !== leafIndent) {\n continue;\n }\n\n const name = extractLeafName(trimmed);\n if (name !== null) {\n out.add(name);\n }\n }\n}\n\nfunction stripTrailingColon(s: string): string | null {\n if (!s.endsWith(':')) {\n return null;\n }\n return s.slice(0, -1).trim();\n}\n\n/**\n * Extracts the package name from a dependency-section leaf, handling both the\n * short form (`pkg: 1.0.0`) and the v9 expanded form (`pkg:` followed by\n * `specifier:` / `version:` children). Quoted scoped names like `'@scope/pkg':`\n * are unquoted.\n */\nfunction extractLeafName(trimmed: string): string | null {\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx < 0) {\n return null;\n }\n let name = trimmed.slice(0, colonIdx).trim();\n if (name.length === 0) {\n return null;\n }\n if (\n (name.startsWith(\"'\") && name.endsWith(\"'\")) ||\n (name.startsWith('\"') && name.endsWith('\"'))\n ) {\n name = name.slice(1, -1);\n }\n return name.length > 0 ? name : null;\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type PackageEntry } from '../types.js';\n\n/**\n * Parses yarn.lock (yarn classic v1 and yarn berry v2+) without a YAML\n * dependency. Both generations share the same block structure — a top-level\n * mapping of comma-separated descriptor lists to a block containing a\n * `version` field — so we walk them with the same scanner and only branch on\n * the `version` syntax (`version \"x\"` for v1, `version: x` for berry).\n *\n * Direct vs transitive can't be derived from yarn.lock alone (yarn does not\n * record an importer manifest the way pnpm v9 does), so we cross-reference\n * the sibling `package.json` when present.\n */\nexport async function parseYarnLockfile(lockfilePath: string): Promise<PackageEntry[]> {\n let raw: string;\n try {\n raw = await readFile(lockfilePath, 'utf8');\n } catch (cause) {\n throw new PatchstackError(\n `Could not read lockfile at ${lockfilePath}`,\n 'LOCKFILE_NOT_FOUND',\n cause,\n );\n }\n\n const blocks = parseBlocks(raw);\n if (blocks.length === 0) {\n throw new PatchstackError(\n `Lockfile at ${lockfilePath} contains no package entries`,\n 'LOCKFILE_PARSE_ERROR',\n );\n }\n\n const directNames = await readDirectDepNames(path.dirname(lockfilePath));\n\n const entries: PackageEntry[] = [];\n const seen = new Set<string>();\n for (const block of blocks) {\n if (block.version.length === 0 || block.names.size === 0) {\n continue;\n }\n for (const name of block.names) {\n const dedupKey = `${name}@${block.version}`;\n if (seen.has(dedupKey)) {\n continue;\n }\n seen.add(dedupKey);\n entries.push({\n name,\n version: block.version,\n direct: directNames.has(name),\n });\n }\n }\n\n return entries;\n}\n\ninterface Block {\n names: Set<string>;\n version: string;\n}\n\nfunction parseBlocks(raw: string): Block[] {\n const lines = raw.split(/\\r?\\n/);\n const blocks: Block[] = [];\n let current: Block | null = null;\n\n const finalize = () => {\n if (current !== null && current.version.length > 0 && current.names.size > 0) {\n blocks.push(current);\n }\n current = null;\n };\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.length === 0 || trimmed.startsWith('#')) {\n continue;\n }\n\n const indent = countLeadingSpaces(line);\n\n if (indent === 0) {\n finalize();\n if (!trimmed.endsWith(':')) {\n continue;\n }\n // `__metadata:` (yarn berry header) has no `@` in any descriptor and\n // produces an empty names set, so it's naturally skipped on finalize.\n const keyLine = trimmed.slice(0, -1);\n const names = new Set<string>();\n for (const spec of splitDescriptors(keyLine)) {\n const name = extractName(spec);\n if (name !== null) {\n names.add(name);\n }\n }\n current = { names, version: '' };\n continue;\n }\n\n if (current === null) {\n continue;\n }\n\n const version = parseVersionField(trimmed);\n if (version !== null) {\n current.version = version;\n }\n }\n\n finalize();\n return blocks;\n}\n\nfunction countLeadingSpaces(line: string): number {\n let i = 0;\n while (i < line.length && line[i] === ' ') {\n i++;\n }\n return i;\n}\n\n/**\n * Splits a yarn descriptor key list on top-level commas. yarn quotes any\n * descriptor that contains characters needing escaping, so we respect quotes\n * while splitting to avoid breaking on commas inside (rare in practice but\n * cheap to handle).\n */\nexport function splitDescriptors(keyLine: string): string[] {\n const parts: string[] = [];\n let current = '';\n let quote: '\"' | \"'\" | null = null;\n\n for (let i = 0; i < keyLine.length; i++) {\n const c = keyLine[i];\n if (quote !== null) {\n current += c;\n if (c === quote) {\n quote = null;\n }\n continue;\n }\n if (c === '\"' || c === \"'\") {\n quote = c;\n current += c;\n continue;\n }\n if (c === ',') {\n const piece = current.trim();\n if (piece.length > 0) {\n parts.push(piece);\n }\n current = '';\n continue;\n }\n current += c;\n }\n const tail = current.trim();\n if (tail.length > 0) {\n parts.push(tail);\n }\n return parts;\n}\n\n/**\n * Extracts the package name from a yarn descriptor like `axios@^1.6.0`,\n * `\"@scope/pkg@^2.1.0\"`, or `\"@scope/pkg@npm:2.1.0\"`. The descriptor's\n * range portion is discarded — we only need the name, since the resolved\n * version comes from the `version` field of the block.\n */\nexport function extractName(rawSpec: string): string | null {\n let s = rawSpec.trim();\n if (s.length === 0) {\n return null;\n }\n if (\n (s.startsWith('\"') && s.endsWith('\"')) ||\n (s.startsWith(\"'\") && s.endsWith(\"'\"))\n ) {\n s = s.slice(1, -1);\n }\n // Position-0 `@` belongs to a scope, so we want the last `@` after it.\n const atIdx = s.lastIndexOf('@');\n if (atIdx <= 0) {\n return null;\n }\n const name = s.slice(0, atIdx);\n return name.length > 0 ? name : null;\n}\n\nfunction parseVersionField(content: string): string | null {\n if (!content.startsWith('version')) {\n return null;\n }\n const after = content.slice('version'.length);\n // yarn v1: `version \"1.2.3\"` (whitespace then quoted)\n // yarn berry: `version: 1.2.3` or `version: \"1.2.3\"`\n const firstChar = after.charAt(0);\n if (firstChar !== ' ' && firstChar !== '\\t' && firstChar !== ':') {\n return null;\n }\n let rest = firstChar === ':' ? after.slice(1) : after;\n rest = rest.trim();\n if (rest.length === 0) {\n return null;\n }\n if (\n (rest.startsWith('\"') && rest.endsWith('\"')) ||\n (rest.startsWith(\"'\") && rest.endsWith(\"'\"))\n ) {\n rest = rest.slice(1, -1);\n }\n return rest.length > 0 ? rest : null;\n}\n\nasync function readDirectDepNames(cwd: string): Promise<Set<string>> {\n const names = new Set<string>();\n let raw: string;\n try {\n raw = await readFile(path.join(cwd, 'package.json'), 'utf8');\n } catch {\n return names;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return names;\n }\n\n if (typeof parsed !== 'object' || parsed === null) {\n return names;\n }\n const obj = parsed as Record<string, unknown>;\n\n for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {\n const section = obj[field];\n if (typeof section !== 'object' || section === null) {\n continue;\n }\n for (const name of Object.keys(section)) {\n names.add(name);\n }\n }\n\n return names;\n}\n","import { access } from 'node:fs/promises';\nimport path from 'node:path';\nimport { PatchstackError, type Manifest, type PackageEntry } from '../types.js';\nimport { missingDependencies, readDeclaredDependencyNames } from './consistency.js';\nimport { parseNpmLockfile } from './npm.js';\nimport { walkNodeModules } from './node_modules.js';\nimport { parsePnpmLockfile } from './pnpm.js';\nimport { parseYarnLockfile } from './yarn.js';\n\ntype LockfileFilename =\n | 'package-lock.json'\n | 'bun.lock'\n | 'bun.lockb'\n | 'yarn.lock'\n | 'pnpm-lock.yaml';\n\ntype DetectionStrategy =\n | 'npm-lockfile'\n | 'node-modules-walk'\n | 'pnpm-lockfile'\n | 'yarn-lockfile';\n\ninterface DetectedLockfile {\n ecosystem: 'npm';\n filePath: string;\n filename: LockfileFilename;\n strategy: DetectionStrategy;\n}\n\ninterface LockfileCandidate {\n filename: LockfileFilename;\n strategy: DetectionStrategy;\n}\n\n// Checked in priority order: the first candidate present in `cwd` wins.\nconst LOCKFILE_CANDIDATES: readonly LockfileCandidate[] = [\n { filename: 'package-lock.json', strategy: 'npm-lockfile' },\n { filename: 'bun.lock', strategy: 'node-modules-walk' },\n { filename: 'bun.lockb', strategy: 'node-modules-walk' },\n { filename: 'pnpm-lock.yaml', strategy: 'pnpm-lockfile' },\n { filename: 'yarn.lock', strategy: 'yarn-lockfile' },\n];\n\nexport async function detectLockfile(cwd: string): Promise<DetectedLockfile> {\n // Probe every candidate concurrently, then resolve by declaration order so\n // the result is identical to a sequential first-match scan — but bounded by a\n // single I/O round-trip instead of up to one `stat` per candidate.\n const probed = await Promise.all(\n LOCKFILE_CANDIDATES.map(async (candidate) => {\n const filePath = path.join(cwd, candidate.filename);\n return { ...candidate, filePath, present: await exists(filePath) };\n }),\n );\n\n const match = probed.find((candidate) => candidate.present);\n if (match) {\n return {\n ecosystem: 'npm',\n filePath: match.filePath,\n filename: match.filename,\n strategy: match.strategy,\n };\n }\n\n const expected = LOCKFILE_CANDIDATES.map((candidate) => candidate.filename).join(', ');\n throw new PatchstackError(\n `No lockfile found in ${cwd}. Expected one of: ${expected}.`,\n 'LOCKFILE_NOT_FOUND',\n );\n}\n\nexport async function scanLockfile(cwd: string): Promise<Manifest> {\n const candidates = await presentLockfiles(cwd);\n if (candidates.length === 0) {\n // Preserve the exact historical error for the no-lockfile case.\n await detectLockfile(cwd);\n }\n\n // Validate each source against package.json before trusting it. A lockfile\n // missing declared dependencies is a fossil — e.g. `npm install` planted a\n // package-lock.json once on a bun-managed platform (Lovable), and the native\n // dependency flow never updates it again. Trusting it would freeze the\n // manifest and the build fingerprint while the real dependency set drifts.\n const declared = await readDeclaredDependencyNames(cwd);\n const warnings: string[] = [];\n let firstParsed: { packages: PackageEntry[]; filename: string } | null = null;\n let walkTried = false;\n\n for (const candidate of candidates) {\n let packages: PackageEntry[];\n try {\n packages = await runStrategy(candidate, cwd);\n } catch {\n continue;\n }\n walkTried ||= candidate.strategy === 'node-modules-walk';\n firstParsed ??= { packages, filename: candidate.filename };\n\n const missing = missingDependencies(declared, packages);\n if (missing.length === 0) {\n return manifestWith(packages, warnings);\n }\n warnings.push(staleWarning(candidate.filename, missing));\n }\n\n // Last resort: the installed truth. node_modules reflects what the build\n // actually compiles against, whichever package manager wrote it.\n if (!walkTried) {\n try {\n const packages = await walkNodeModules(cwd);\n const missing = missingDependencies(declared, packages);\n if (missing.length > 0) {\n warnings.push(staleWarning('node_modules/', missing));\n }\n warnings.push('Scanned node_modules/ instead. Delete the stale lockfile to silence this warning.');\n return manifestWith(packages, warnings);\n } catch {\n // fall through to the best lockfile we managed to parse\n }\n }\n\n if (firstParsed === null) {\n const expected = LOCKFILE_CANDIDATES.map((candidate) => candidate.filename).join(', ');\n throw new PatchstackError(\n `No readable lockfile found in ${cwd}. Expected one of: ${expected}.`,\n 'LOCKFILE_NOT_FOUND',\n );\n }\n\n warnings.push(\n `No fully-consistent source found; reporting ${firstParsed.filename}. The manifest may understate the real dependency set.`,\n );\n\n return manifestWith(firstParsed.packages, warnings);\n}\n\nfunction manifestWith(packages: PackageEntry[], warnings: string[]): Manifest {\n return warnings.length > 0\n ? { ecosystem: 'npm', packages, warnings }\n : { ecosystem: 'npm', packages };\n}\n\nfunction staleWarning(source: string, missing: string[]): string {\n const sample = missing.slice(0, 3).join(', ');\n const suffix = missing.length > 3 ? `, +${missing.length - 3} more` : '';\n return `${source} looks stale: package.json declares ${missing.length} dependenc${missing.length === 1 ? 'y' : 'ies'} it does not contain (${sample}${suffix}).`;\n}\n\nasync function presentLockfiles(cwd: string): Promise<DetectedLockfile[]> {\n const probed = await Promise.all(\n LOCKFILE_CANDIDATES.map(async (candidate) => {\n const filePath = path.join(cwd, candidate.filename);\n return { ...candidate, filePath, present: await exists(filePath) };\n }),\n );\n\n return probed\n .filter((candidate) => candidate.present)\n .map(({ filename, strategy, filePath }) => ({ ecosystem: 'npm' as const, filename, strategy, filePath }));\n}\n\nasync function runStrategy(\n detected: DetectedLockfile,\n cwd: string,\n): Promise<PackageEntry[]> {\n switch (detected.strategy) {\n case 'npm-lockfile':\n return parseNpmLockfile(detected.filePath);\n case 'pnpm-lockfile':\n return parsePnpmLockfile(detected.filePath);\n case 'yarn-lockfile':\n return parseYarnLockfile(detected.filePath);\n case 'node-modules-walk':\n return walkNodeModules(cwd);\n }\n}\n\nasync function exists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n","import type { Manifest, PackageEntry } from './types.js';\n\nexport interface WirePackage {\n name: string;\n version: string;\n}\n\nexport interface WirePayload {\n ecosystem: Manifest['ecosystem'];\n packages: WirePackage[];\n}\n\nexport interface NormalizeStats {\n uniqueNames: number;\n duplicateNames: string[];\n totalEntries: number;\n}\n\nexport interface NormalizeResult {\n payload: WirePayload;\n stats: NormalizeStats;\n}\n\nexport function buildWirePayload(manifest: Manifest): NormalizeResult {\n const seen = new Map<string, Set<string>>();\n const wirePackages: WirePackage[] = [];\n\n for (const entry of manifest.packages) {\n const versions = seen.get(entry.name);\n if (versions) {\n if (versions.has(entry.version)) {\n continue;\n }\n versions.add(entry.version);\n } else {\n seen.set(entry.name, new Set([entry.version]));\n }\n wirePackages.push({ name: entry.name, version: entry.version });\n }\n\n wirePackages.sort((a, b) => {\n if (a.name === b.name) {\n return compareVersions(a.version, b.version);\n }\n return a.name < b.name ? -1 : 1;\n });\n\n const duplicateNames: string[] = [];\n for (const [name, versions] of seen) {\n if (versions.size > 1) {\n duplicateNames.push(name);\n }\n }\n\n return {\n payload: { ecosystem: manifest.ecosystem, packages: wirePackages },\n stats: {\n uniqueNames: seen.size,\n duplicateNames,\n totalEntries: manifest.packages.length,\n },\n };\n}\n\nexport function compareVersions(a: string, b: string): number {\n if (a === b) {\n return 0;\n }\n\n const [aBase, aPre] = splitPrerelease(a);\n const [bBase, bPre] = splitPrerelease(b);\n\n const baseCmp = compareSegments(aBase.split('.'), bBase.split('.'));\n if (baseCmp !== 0) {\n return baseCmp;\n }\n\n if (aPre === null && bPre === null) {\n return 0;\n }\n if (aPre === null) {\n return 1;\n }\n if (bPre === null) {\n return -1;\n }\n return compareSegments(aPre.split('.'), bPre.split('.'));\n}\n\nfunction splitPrerelease(version: string): [string, string | null] {\n const cleaned = version.replace(/^[v=]+/, '').split('+')[0]!;\n const dashIndex = cleaned.indexOf('-');\n if (dashIndex === -1) {\n return [cleaned, null];\n }\n return [cleaned.slice(0, dashIndex), cleaned.slice(dashIndex + 1)];\n}\n\nfunction compareSegments(a: string[], b: string[]): number {\n const max = Math.max(a.length, b.length);\n for (let i = 0; i < max; i++) {\n const aPart = a[i];\n const bPart = b[i];\n if (aPart === undefined) {\n return -1;\n }\n if (bPart === undefined) {\n return 1;\n }\n const aNum = /^\\d+$/.test(aPart);\n const bNum = /^\\d+$/.test(bPart);\n if (aNum && bNum) {\n const diff = Number(aPart) - Number(bPart);\n if (diff !== 0) {\n return diff < 0 ? -1 : 1;\n }\n continue;\n }\n if (aNum) {\n return -1;\n }\n if (bNum) {\n return 1;\n }\n if (aPart < bPart) {\n return -1;\n }\n if (aPart > bPart) {\n return 1;\n }\n }\n return 0;\n}\n\nexport function findPackageInManifest(\n manifest: Manifest,\n name: string,\n): PackageEntry[] {\n return manifest.packages.filter((p) => p.name === name);\n}\n","// Re-post the project's dependency manifest to Pulse — the runtime counterpart to the CLI's\n// `scan`. The sandbox refresh loop (see runtime.js) calls this each tick so a dependency added\n// after boot is reported without a restart: a targeted `npm install <pkg>` fires no npm lifecycle\n// hook, so nothing else re-scans. Reuses the CLI scan pipeline; best-effort (the caller catches).\nimport { resolveConfig } from '../config.js';\nimport { scanLockfile } from '../parsers/index.js';\nimport { buildWirePayload } from '../normalize.js';\nimport { postManifest } from '../client.js';\n\n/** Scan the lockfile under `cwd` and post the manifest to Pulse. No-op without a provisioned site. */\nexport async function reportManifest(cwd: string): Promise<void> {\n const config = await resolveConfig({ cwd });\n if (config.siteUuid === null || config.siteUuid.length === 0) {\n return;\n }\n const manifest = await scanLockfile(cwd);\n const { payload } = buildWirePayload(manifest);\n await postManifest(config, payload);\n}\n","// @patchstack/protect — \"Protect = respond\".\n//\n// One entry point that composes the node-waf engine + adapters with:\n// - a rule source: an explicit bundle, or fetched from the Patchstack API (token),\n// with a disk cache so the engine keeps working on last-known-good if the API is down\n// - execution modes: 'dry-run' (detect + log, never block — the safe onramp) and 'block' (enforce).\n// This API's default is 'dry-run'. NOTE the scaffolded guard (`patchstack-connect protect`)\n// deliberately passes mode: 'block' and only drops to dry-run when PATCHSTACK_MODE=dry-run — so an\n// installed guard ENFORCES by default even though this constructor's default doesn't. Precedence:\n// PATCHSTACK_MODE env > API `enforcement` > options.mode > dry-run.\n// - fail-open everywhere: a rule/engine error never blocks (or crashes) a request. Where the guard\n// fails open *without* inspecting (body caps, live streams, binary bodies, resolver failures) it\n// is counted and reported — see `protection.coverage()` / the `onSkip` option.\n//\n// Runtime guards: .express(), .node(), .fetch(handler) / .fetchGuard() — same policy,\n// every runtime an AI builder deploys to.\n// Vendored node-waf engine (this package is self-contained — no @patchstack/node-waf dep).\nimport { RuleEngine } from './engine/index.js';\nimport { matchValue, walkLeaves, safeRegExp } from './engine/engine.js';\nimport { PulseRuleClient } from './engine/pulse-client.js';\nimport { fromFetchRequest } from './engine/fetch.js';\nimport { fromNodeRequest } from './engine/node.js';\nimport { installEgressGuard } from './egress.js';\nimport { DEFAULT_RESPONSE_RULES, DEFAULT_EGRESS_RULES } from './defaults.js';\nimport { renderBlockPage } from './block-page.js';\n// Rule lifecycle (source / tiered store / refresh) lives in ./rules/ — this file stays focused on\n// composing the engine + guards and running the three screening phases.\nimport { makeStore } from './rules/store.js';\nimport { resolveRules } from './rules/source.js';\nimport { startRefresh, makeRefreshHandler } from './rules/refresh.js';\nimport { createFirewallLogReporter, resolveApiBase, telemetryEnabled } from './firewall-log.js';\n\n// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\nexport { createSupabaseGuard, GUARD_PATH } from './supabase-guard.js';\n\n// Per-site live rule client (Pulse). Re-exported for callers/tests that want to use it directly.\nexport { PulseRuleClient };\n\n// Server-function guard. Modern Lovable apps mutate data through TanStack server functions\n// (browser → server fn → server-side Supabase client), which bypass the browser-side tunnel the\n// Supabase guard relies on. This inspects the decoded server-fn call args against the SAME policy\n// by feeding them through fetchGuard (the args become the request body, so the engine resolves\n// `post.<field>` exactly as it does for a tunneled Supabase insert). Returns a block receipt\n// { rule?, message } to throw on, or null to allow (also null in dry-run — the detection is still\n// recorded by fetchGuard). Fail-open on any error.\nexport function createServerFnGuard({ protection }) {\n const guard = protection.fetchGuard();\n return async (data) => {\n let res;\n try {\n const req = new Request('https://patchstack.local/_serverfn', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(data ?? {}),\n });\n res = await guard(req);\n } catch {\n return null; // fail open\n }\n if (!res) return null; // allowed (or dry-run)\n let body = {};\n try {\n body = await res.clone().json();\n } catch {\n /* non-JSON block response */\n }\n return { rule: body.rule, message: body.message || 'Blocked by Patchstack' };\n };\n}\n\nexport async function createProtection(options = {}) {\n const onError = options.onError;\n const userOnDetect = options.onDetect ?? defaultOnDetect;\n\n // Report enforced blocks via existing connector POST /api/logs/log (WP path).\n // Needs api_key from provision / PATCHSTACK_API_KEY / .patchstackrc.json.\n // Opt out: PATCHSTACK_TELEMETRY=off. Never embed api_key in the public widget.\n const apiKey = await resolveApiKey(options);\n const firewallLog =\n apiKey && telemetryEnabled() && options.reportFirewallLog !== false\n ? createFirewallLogReporter({\n apiKey,\n apiBase: resolveApiBase(options.pulseRulesUrl ?? options.baseUrl),\n sourceHost: options.sourceHost,\n fetchImpl: options.fetchImpl,\n })\n : null;\n\n const onDetect = (detection) => {\n userOnDetect(detection);\n if (firewallLog && detection?.mode === 'block') {\n firewallLog.record({\n rule: detection.rule,\n method: detection.method,\n path: detection.path,\n ip: detection.ip,\n userAgent: detection.userAgent,\n });\n }\n };\n\n // One tiered store (memory → filesystem/pluggable) shared by the initial load and every refresh.\n const store = makeStore(options);\n // Startup must not hang on the network: hosted platforms (Replit et al.) fail a deploy whose health\n // check is slow, and the guard can always boot from last-known-good / the bundled fallback. Refreshes\n // keep the full budget. Override with { bootTimeoutMs }.\n const bootTimeoutMs = Number(options.bootTimeoutMs) > 0 ? Number(options.bootTimeoutMs) : 5_000;\n // Resolved once and threaded through ctx: reading it is a filesystem hit on\n // runtimes that have one, and refreshes should not repeat it.\n const pulseAuth = await resolvePulseAuth(options);\n const bundle = await resolveRules(options, store, { timeoutMs: bootTimeoutMs, pulseAuth });\n // Mode is mutable so a Pulse refresh can flip dry-run ↔ block when SaaS enables production.\n // Precedence: PATCHSTACK_MODE env (local override) > API enforcement > options.mode > dry-run.\n let mode = resolveMode(options, bundle);\n // Rule-derived runtime state. Held in `let` bindings the guard methods below close over, so a\n // refresh (see the loop near the end) can hot-swap the engines by reassigning them — the\n // egress interception and the protection object itself stay in place, no re-install.\n let requestRules;\n let responseRules;\n let egressRules;\n let screenCap; // max body we'll buffer/screen (rules can raise it)\n let engine;\n let responseRuleSet;\n let egressEngine;\n\n // Split the delivered ruleset by phase (default \"request\"), merging phase defaults +\n // per-call overrides. Detection is fully rule-driven — nothing hardcoded.\n const applyBundle = (delivered) => {\n const incoming = delivered.firewall ?? [];\n requestRules = byPhase(incoming, 'request');\n responseRules = [...(options.responseRules ?? DEFAULT_RESPONSE_RULES), ...byPhase(incoming, 'response')];\n screenCap = responseScreenCap(responseRules);\n egressRules = [...(options.egressRules ?? DEFAULT_EGRESS_RULES), ...byPhase(incoming, 'egress')];\n engine = new RuleEngine({\n firewall: requestRules,\n whitelists: delivered.whitelists,\n whitelist_keys: delivered.whitelist_keys,\n onError,\n });\n // One engine per response rule so we can find ALL matches (to redact each). `action:\n // \"redact\"` masks the offending span(s); anything else withholds the whole response.\n responseRuleSet = responseRules.map((rule) => ({\n rule,\n engine: new RuleEngine({ firewall: [rule], onError }),\n redactors: rule.action === 'redact' || rule.action === 'encode' ? extractRedactors(rule) : null,\n // A redact/encode condition that carries body-transforming mutations (base64_decode, urldecode,\n // json_decode, …) detects on the DECODED body but the span redactors run on the RAW body — so\n // they mask nothing and the secret is served while the log says \"redacted\". Flag it so screenText\n // fails such a rule CLOSED (block) instead of serving a no-op redaction.\n mutatedSpan: (rule.action === 'redact' || rule.action === 'encode') && hasSpanMutations(rule),\n // Optional cheap pre-filter: literal anchor(s) that MUST appear for the (expensive) regex to\n // have any chance of matching. Lets screenText skip the full scan on bodies with no candidate —\n // the common case — cutting CPU/latency and shrinking the regex/ReDoS surface. Case-insensitive.\n prefilter: Array.isArray(rule.prefilter) && rule.prefilter.length\n ? rule.prefilter.map((s) => String(s).toLowerCase())\n : null,\n }));\n egressEngine = new RuleEngine({ firewall: egressRules, onError });\n };\n\n applyBundle(bundle);\n\n // Fail-open COVERAGE. The guard deliberately passes traffic through rather than risk breaking the\n // app: an oversized request body, a response past the screening cap, a live stream, a binary body, a\n // parse failure, a DNS resolver failure. Each of those is a real hole in enforcement, and until now\n // it was SILENT — \"always-on\" read as \"always inspected\". Every such bypass is now counted and\n // reported to `onSkip`, so a host can alert on it and `protection.coverage()` can be surfaced.\n // `onSkip` is a TRUSTED SERVER callback: `detail` carries operational context (sizes, statuses,\n // outbound hostnames) for logging/alerting. Do not forward it to a client response.\n const skipCounts = Object.create(null);\n const onSkip = typeof options.onSkip === 'function' ? options.onSkip : null;\n const recordSkip = (phase, reason, detail) => {\n const key = `${phase}:${reason}`;\n skipCounts[key] = (skipCounts[key] ?? 0) + 1;\n if (onSkip) {\n try {\n onSkip({ phase, reason, detail, count: skipCounts[key] });\n } catch {\n /* a reporting callback must never affect request handling */\n }\n }\n };\n\n const maskFn =\n typeof options.maskWith === 'function'\n ? options.maskWith\n : () => (typeof options.maskWith === 'string' ? options.maskWith : '[REDACTED]');\n\n // Given a request/egress result, enforce (block mode) or just record (dry-run).\n //\n // A rule may carry its own `enforcement: 'dry-run'`, which wins over block mode for that rule alone.\n // Auto-generated rules arrive that way: their coordinate comes from best-effort static analysis, so they\n // are served to detect until a probe or a human has justified them, WITHOUT holding back the\n // hand-authored rules on the same site. A rule with no `enforcement` follows the bundle exactly as\n // before, so an older server that never sends the field behaves identically.\n const ruleMode = (rule) => (rule?.enforcement === 'dry-run' ? 'dry-run' : mode);\n\n const decide = (phase, result, block, allow, ctx = {}) => {\n if (!result || !result.blocked) return allow();\n const effectiveMode = ruleMode(result.rule);\n onDetect({\n phase,\n // The mode this detection was actually handled under, not the site's: a consumer counting blocks\n // would otherwise over-report, and the whole point of a dry-run rule is that it did not block.\n mode: effectiveMode,\n category: result.rule?.category,\n rule: result.rule,\n message: result.message,\n method: ctx.method,\n path: ctx.path,\n ip: ctx.ip,\n userAgent: ctx.userAgent,\n });\n return effectiveMode === 'block' ? block() : allow();\n };\n\n // Response phase core: screen a text body → { verdict: 'pass'|'block'|'redact', body? }.\n // redact masks matched spans; block withholds; block wins over redact. Enforcement only in\n // block mode (dry-run records via onDetect but returns 'pass').\n const screenText = (text, meta, reqCtx) => {\n let blockRule = null;\n const redactions = [];\n const headerMutations = [];\n let lowerText = null; // lazily lowercased body, only if a rule uses a prefilter\n for (const { rule, engine: re, redactors, prefilter, mutatedSpan } of responseRuleSet) {\n // Cheap pre-filter: if none of the rule's literal anchors is in the body, its regex can't\n // match — skip the full scan (the common no-secret case) before touching the engine.\n if (prefilter) {\n if (lowerText === null) lowerText = text.toLowerCase();\n if (!prefilter.some((p) => lowerText.includes(p))) continue;\n }\n let result;\n try {\n // Spread the originating request (method / originalUrl / headers) alongside the response,\n // so a response rule's `when` route/method scope resolves against the REAL request and so\n // request Host/Origin are visible to response rules — rather than the phantom empty request\n // the response phase used to build (which made `when` on a response rule inert).\n result = re.evaluate({ ...(reqCtx || {}), _response: { ...meta, body: text } });\n } catch (err) {\n onError?.(err);\n continue;\n }\n if (!result.blocked) continue;\n // Per-rule enforcement applies to every phase, not just the request. A generated response rule in\n // dry-run must not redact or withhold a body either: \"detect until justified\" is meaningless if the\n // rule still rewrites what the user sees.\n const responseMode = ruleMode(rule);\n onDetect({ phase: 'response', mode: responseMode, category: rule.category, rule, message: result.message });\n if (responseMode !== 'block') continue; // dry-run: observe only\n if (redactors && redactors.length) {\n // Span redactors on a mutation-decoded rule can't map back to the raw body → fail closed.\n const spanRedactors = redactors.filter((r) => !r.jsonPath);\n if (mutatedSpan && spanRedactors.length) {\n if (!blockRule) blockRule = rule;\n } else {\n redactions.push({ rule, redactors });\n }\n } else if (isHeaderMutation(rule.action)) headerMutations.push(rule);\n else if (!blockRule) blockRule = rule;\n }\n if (mode !== 'block' || (!blockRule && !redactions.length && !headerMutations.length)) return { verdict: 'pass' };\n if (blockRule) return { verdict: 'block' };\n let body = text;\n // Redact the offending spans in the body AND in every (string) header value — so a secret\n // that leaks in a header (Set-Cookie, an echoed X-Api-Key, …) is masked too, and a rule that\n // targets `response.header.*` actually strips the header rather than just detecting it.\n const headers = { ...(meta.headers || {}) };\n for (const { rule, redactors } of redactions) {\n const mask = maskFn(rule.category);\n // action `encode` HTML-escapes the matched value in place (neutralize stored XSS at output);\n // `redact` masks it. jsonPath redactors act on a structural JSON location, span redactors on\n // text spans in the body AND header values. Apply structural first (on clean JSON), then spans.\n const transform = rule.action === 'encode' ? htmlEscape : null;\n const pathRedactors = redactors.filter((r) => r.jsonPath);\n const spanRedactors = redactors.filter((r) => !r.jsonPath);\n if (pathRedactors.length) body = applyPathRedactors(body, pathRedactors, mask, screenCap, transform);\n if (!spanRedactors.length) continue;\n body = applyRedactors(body, spanRedactors, mask, transform);\n if (transform) continue; // encoding is a body/output concern — headers aren't HTML\n for (const name of Object.keys(headers)) {\n const value = headers[name];\n if (typeof value === 'string') {\n headers[name] = applyRedactors(value, spanRedactors, mask);\n } else if (Array.isArray(value)) {\n // Multi-valued headers (Set-Cookie) — redact each entry.\n headers[name] = value.map((item) => (typeof item === 'string' ? applyRedactors(item, spanRedactors, mask) : item));\n }\n }\n }\n for (const rule of headerMutations) applyHeaderMutation(headers, rule);\n return { verdict: 'redact', body, headers };\n };\n\n // Minimal request context for the response phase: what a response rule's `when` scope and any\n // request-header reference (Host/Origin) need — method, path, and request headers. No body.\n const reqContextFromFetch = (request) => {\n try {\n const u = new URL(request.url);\n const headers = headerObject(request.headers);\n // A fetch Request doesn't expose the Host header (it's set at send time), so derive it from the\n // URL — response rules that compare origins (open-redirect / CORS) need the request Host.\n if (!headers.host) headers.host = u.host;\n return { method: request.method, originalUrl: u.pathname + u.search, headers };\n } catch {\n return undefined;\n }\n };\n const reqContextFromNode = (req) => (req ? { method: req.method, originalUrl: req.url, headers: req.headers || {} } : undefined);\n\n // Screen a fetch Response (used by .fetch() and — via protection.screenResponse — the Supabase guard).\n const screenResp = async (response, reqCtx) => {\n const read = await readTextResponse(response, screenCap);\n if (read.skip) {\n // Nothing was screened — a leak/PII rule cannot have applied. Record it (a live stream and a\n // binary body are by design; a body-cap or read failure is a coverage hole worth alerting on).\n if (read.skip !== 'not-a-response') recordSkip('response', read.skip, { status: response?.status });\n return response;\n }\n const text = read.text;\n const r = screenText(text, { status: response.status, headers: headerObject(response.headers) }, reqCtx);\n if (r.verdict === 'block') return leakResponse();\n if (r.verdict === 'redact') return rebuildResponse(response, r.body, r.headers);\n return response;\n };\n\n // Wrap a Node ServerResponse so its (buffered, text) body is screened before it's sent.\n // Opt-in (buffering can delay a streamed response); over 512 KiB it stops buffering and\n // passes through unscanned.\n const wrapNodeResponse = (res, reqCtx) => {\n const origWrite = res.write.bind(res);\n const origEnd = res.end.bind(res);\n const chunks = [];\n let size = 0;\n let overflow = false;\n const MAX = screenCap;\n const collect = (chunk, enc) => {\n if (chunk == null) return;\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, typeof enc === 'string' ? enc : 'utf8');\n size += buf.length;\n if (size > MAX) {\n // Too big to screen — abandon buffering, but FLUSH what we already captured (the head) plus\n // this chunk before switching to pass-through, so the client gets a complete body (not a\n // truncated one missing everything before the cap was hit).\n for (const c of chunks) origWrite(c);\n chunks.length = 0;\n origWrite(buf);\n overflow = true;\n recordSkip('response', 'body-cap', { bytes: size });\n return;\n }\n chunks.push(buf);\n };\n res.write = function (chunk, enc, cb) {\n if (overflow) return origWrite(chunk, enc, cb);\n collect(chunk, enc);\n if (typeof enc === 'function') enc();\n else if (typeof cb === 'function') cb();\n return true;\n };\n res.end = function (chunk, enc, cb) {\n if (typeof chunk === 'function') { cb = chunk; chunk = undefined; enc = undefined; }\n else if (typeof enc === 'function') { cb = enc; enc = undefined; }\n if (overflow) { if (chunk != null) origWrite(chunk, enc); return origEnd(cb); }\n collect(chunk, enc);\n if (overflow) return origEnd(cb); // collect just flushed head + final chunk on overflow\n const buffer = Buffer.concat(chunks);\n let ct = res.getHeader ? res.getHeader('content-type') : undefined;\n if (Array.isArray(ct)) ct = ct[0];\n const kind = screenableContentType(ct);\n // Skip live streams / binary bodies (incl. an octet-stream that sniffs as binary) — untouched.\n if (kind === 'skip' || (kind === 'sniff' && looksBinary(buffer))) {\n recordSkip('response', kind === 'skip' ? (baseContentType(ct) === 'text/event-stream' ? 'live-stream' : 'non-text-content-type') : 'binary-body');\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n }\n const text = buffer.toString('utf8');\n let r;\n try {\n r = screenText(text, { status: res.statusCode, headers: res.getHeaders ? res.getHeaders() : {} }, reqCtx);\n } catch (err) {\n onError?.(err);\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n }\n if (r.verdict === 'block') {\n res.statusCode = 500;\n try { res.setHeader('content-type', 'application/json'); } catch { /* headers sent */ }\n return origEnd(JSON.stringify({ error: 'Response withheld by Patchstack (sensitive data detected)' }), cb);\n }\n if (r.verdict === 'redact') {\n try { res.removeHeader && res.removeHeader('content-length'); } catch { /* ignore */ }\n if (r.headers && res.setHeader) {\n const current = res.getHeaders ? res.getHeaders() : {};\n for (const [name, value] of Object.entries(r.headers)) {\n // content-length was just removed (the redacted body has a new length); never re-set a\n // stale one here or the response truncates/hangs.\n if (name.toLowerCase() === 'content-length') continue;\n if (value === null || value === undefined) {\n try { res.removeHeader && res.removeHeader(name); } catch { /* ignore */ } // header-mutation removal\n } else if (Array.isArray(value)) {\n try { res.setHeader(name, value); } catch { /* ignore invalid header */ } // Set-Cookie array\n } else if (typeof value === 'string' && current[name] !== value) {\n try { res.setHeader(name, value); } catch { /* ignore invalid header */ }\n }\n }\n }\n return origEnd(r.body, cb);\n }\n for (const c of chunks) origWrite(c);\n return origEnd(cb);\n };\n };\n\n // Egress phase: is this outbound call blocked? (records detection either way)\n const allow = new Set((options.allowHosts ?? []).map((h) => String(h).toLowerCase()));\n const egressShouldBlock = (url, host, method) => {\n if (host && allow.has(host.toLowerCase())) return false;\n let result;\n try {\n result = egressEngine.evaluate({ _egress: { url, host, method } });\n } catch (err) {\n onError?.(err);\n return false;\n }\n if (!result.blocked) return false;\n // Same for egress: a dry-run rule records the outbound attempt without preventing it. Blocking a\n // request the app makes is at least as disruptive as blocking one it receives.\n const egressMode = ruleMode(result.rule);\n onDetect({ phase: 'egress', mode: egressMode, category: result.rule?.category, rule: result.rule, message: result.message });\n return egressMode === 'block';\n };\n\n const protection = {\n get mode() {\n return mode;\n },\n get rules() {\n return { request: requestRules, response: responseRules, egress: egressRules };\n },\n\n /**\n * Enforcement coverage: how often the guard FAILED OPEN rather than inspecting, keyed\n * `<phase>:<reason>` (e.g. `response:body-cap`, `request:body-cap`, `response:live-stream`,\n * `egress:resolver-failed`). \"Always-on\" is not \"always inspected\" — surface this (or pass\n * `onSkip`) so an unscreened path is visible and alertable rather than silent.\n */\n coverage() {\n return { skipped: { ...skipCounts } };\n },\n\n // Screen a fetch Response through the response-phase rules (redact/block). Used by\n // .fetch(), and by the Supabase guard on its forwarded upstream response.\n screenResponse: (response, request) => screenResp(response, request ? reqContextFromFetch(request) : undefined),\n\n // (request) => Response | null (null = allow, caller proceeds). Request phase only.\n fetchGuard() {\n return async (request) => {\n let result;\n try {\n result = engine.evaluate(await fromFetchRequest(request));\n } catch (err) {\n onError?.(err);\n return null; // fail open\n }\n return decide('request', result, () => blockResponse(result, request), () => null, fetchRequestMeta(request));\n };\n },\n\n // Wrap a fetch handler: screens the request, then the response (redact/block).\n fetch(handler) {\n const guard = protection.fetchGuard();\n return async (request, ...rest) => {\n const blocked = await guard(request);\n if (blocked) return blocked;\n const response = await handler(request, ...rest);\n return screenResp(response, reqContextFromFetch(request));\n };\n },\n\n // Express middleware (request phase; expects express-parsed req.query/req.body).\n // Pass { screenResponses: true } to also screen the outgoing response (buffers it).\n express(exprOptions = {}) {\n return (req, res, next) => {\n let result;\n try {\n result = engine.evaluate(req);\n } catch (err) {\n onError?.(err);\n if (exprOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n return next();\n }\n decide(\n 'request',\n result,\n () => {\n if (isDocumentNavigation((n) => req.headers?.[n])) {\n res.status(403).type('html').send(renderBlockPage({ url: req.originalUrl || req.url || '/', code: result?.rule?.id }));\n } else {\n res.status(403).json(blockBody(result));\n }\n },\n () => {\n if (exprOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n next();\n },\n nodeRequestMeta(req),\n );\n };\n },\n\n // Node / Connect middleware — buffers the body itself (request phase).\n // Pass { screenResponses: true } to also screen the outgoing response (buffers it).\n node(nodeOptions = {}) {\n const maxBytes = nodeOptions.maxBodyBytes ?? 1024 * 1024;\n return (req, res, next) => {\n const chunks = [];\n let size = 0;\n let overflow = false;\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n req.on('error', (err) => {\n onError?.(err);\n next();\n });\n req.on('end', () => {\n if (overflow) recordSkip('request', 'body-cap', { bytes: size, limit: maxBytes });\n const rawBody = overflow ? '' : Buffer.concat(chunks).toString('utf8');\n let shaped;\n let result;\n try {\n shaped = fromNodeRequest(req, rawBody);\n result = engine.evaluate(shaped);\n } catch (err) {\n onError?.(err);\n return next();\n }\n decide(\n 'request',\n result,\n () => {\n res.statusCode = 403;\n if (isDocumentNavigation((n) => req.headers?.[n])) {\n res.setHeader('content-type', 'text/html; charset=utf-8');\n res.end(renderBlockPage({ url: req.url || '/', code: result?.rule?.id }));\n } else {\n res.setHeader('content-type', 'application/json');\n res.end(JSON.stringify(blockBody(result)));\n }\n },\n () => {\n // This guard consumed the request stream to screen it; re-expose the parsed\n // body so a downstream handler (without its own body-parser) can read it.\n if (req.body === undefined) req.body = shaped.body;\n if (nodeOptions.screenResponses) wrapNodeResponse(res, reqContextFromNode(req));\n next();\n },\n nodeRequestMeta(req),\n );\n });\n };\n },\n };\n\n // Egress interception is opt-in (it wraps the global fetch, and node:http/https on Node).\n if (options.egress) {\n protection.uninstallEgress = await installEgressGuard({\n shouldBlock: egressShouldBlock,\n onBlock: options.onEgressBlock,\n // Route egress coverage gaps (a DNS resolver failure / no resolver on this runtime) into the\n // same skip accounting as the request/response phases.\n onSkip: ({ reason, detail }) => recordSkip('egress', reason, detail),\n dnsScreen: options.screenDns !== false,\n allowHosts: options.allowHosts,\n });\n }\n\n // Live rule refresh. The guard otherwise reads its rules once, at process start — so a rule that\n // only becomes relevant after boot (a dependency added mid-session and flagged by Pulse, a\n // zero-day published) never applies until the process restarts. A refresh re-fetches and\n // hot-swaps the engines in place; the same tick can be driven by a poll loop (`refreshMs`), a\n // manual `protection.refresh()`, or an authenticated push (`protection.refreshHandler()`).\n const cwd = options.cwd ?? (typeof process !== 'undefined' ? process.cwd() : undefined);\n const live = Boolean(options.siteUuid || options.token);\n const refreshSecret = options.refreshSecret ?? (typeof process !== 'undefined' ? process.env.PATCHSTACK_REFRESH_SECRET : undefined);\n const refreshable = live && (options.refreshMs > 0 || Boolean(refreshSecret));\n\n // On the Pulse (siteUuid) path, re-post the dependency manifest before re-fetching. A targeted\n // `npm install <pkg>` fires no npm lifecycle hook, so nothing else re-scans; reporting here lets\n // the server flag a newly-added vulnerable dependency and the SAME tick's rule fetch pick up its\n // rule. Loaded once, up front, only when a refresh path is enabled — a refresh-off production\n // guard never pulls in the scan pipeline; a load/report failure never blocks the rule refresh.\n let reporter = null;\n if (refreshable && options.siteUuid && options.reportManifest !== false && cwd) {\n try {\n ({ reportManifest: reporter } = await import('./refresh-manifest.js'));\n } catch (err) {\n onError?.(err); // scan pipeline unavailable (e.g. an edge runtime) — rules still refresh\n }\n }\n\n const runRefreshTick = async () => {\n if (reporter) {\n try {\n await reporter(cwd);\n } catch (err) {\n onError?.(err); // a failed report must not stop the rule refresh\n }\n }\n const next = await resolveRules(options, store, { timeoutMs: options.refreshTimeoutMs, pulseAuth });\n mode = resolveMode(options, next);\n applyBundle(next);\n };\n\n if (live) {\n // Manual one-shot refresh (also the primitive the loop + push endpoint run).\n protection.refresh = () => runRefreshTick();\n // Authenticated push endpoint — the platform/SaaS hits it for an immediate refresh. No secret\n // configured → the handler 404s (never an open refresh trigger).\n protection.refreshHandler = () => makeRefreshHandler(runRefreshTick, refreshSecret);\n }\n\n if (options.refreshMs > 0 && live) {\n const loop = startRefresh(runRefreshTick, { refreshMs: options.refreshMs, onError });\n protection.stopRefresh = () => {\n loop.stop();\n firewallLog?.stop();\n };\n } else if (firewallLog) {\n protection.stopRefresh = () => firewallLog.stop();\n }\n\n return protection;\n}\n\n/**\n * Resolve runtime enforcement mode.\n * Precedence: PATCHSTACK_MODE env > Pulse `enforcement` on the rules bundle > options.mode > dry-run.\n */\nfunction resolveMode(options, bundle) {\n const env = typeof process !== 'undefined' ? process.env?.PATCHSTACK_MODE : undefined;\n if (env === 'block' || env === 'dry-run') return env;\n if (bundle?.enforcement === 'block' || bundle?.enforcement === 'dry-run') return bundle.enforcement;\n if (options?.mode === 'block') return 'block';\n if (options?.mode === 'dry-run') return 'dry-run';\n return 'dry-run';\n}\n\n/**\n * WP-format api_key for connector /api/logs/log. Never use the public site UUID.\n * The `.patchstackrc.json` fallback reads the filesystem, so fs/path are imported LAZILY — this\n * module must stay loadable on edge runtimes (Next edge middleware, Workers, Deno, Supabase\n * Functions), where a static `node:fs` import fails to resolve and would take the guard down.\n */\nasync function resolveApiKey(options) {\n if (typeof options?.apiKey === 'string' && options.apiKey.length > 0) return options.apiKey;\n if (typeof process !== 'undefined') {\n const fromEnv = process.env?.PATCHSTACK_API_KEY;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) return fromEnv;\n }\n try {\n if (typeof process === 'undefined' || typeof process.cwd !== 'function') return undefined;\n const [{ readFileSync }, { join }] = await Promise.all([import('node:fs'), import('node:path')]);\n const cwd = options?.cwd ?? process.cwd();\n const raw = readFileSync(join(cwd, '.patchstackrc.json'), 'utf8');\n const key = JSON.parse(raw)?.apiKey;\n if (typeof key === 'string' && key.length > 0) return key;\n } catch {\n /* missing, or no filesystem on this runtime — reporting stays off */\n }\n return undefined;\n}\n\n/**\n * Credential for the authenticated rules lookup (ADR-0018). Same resolution\n * order and the same edge-runtime caution as resolveApiKey, and falls back to\n * it so guards installed before pulseAuth existed keep authenticating.\n *\n * Returning undefined is fine: the rules fetch then goes out unauthenticated,\n * which the server still accepts.\n */\nasync function resolvePulseAuth(options) {\n if (typeof options?.pulseAuth === 'string' && options.pulseAuth.length > 0) return options.pulseAuth;\n if (typeof process !== 'undefined') {\n const fromEnv = process.env?.PATCHSTACK_PULSE_AUTH;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) return fromEnv;\n }\n try {\n if (typeof process === 'undefined' || typeof process.cwd !== 'function') return resolveApiKey(options);\n const [{ readFileSync }, { join }] = await Promise.all([import('node:fs'), import('node:path')]);\n const cwd = options?.cwd ?? process.cwd();\n const raw = readFileSync(join(cwd, '.patchstackrc.json'), 'utf8');\n const key = JSON.parse(raw)?.pulseAuth;\n if (typeof key === 'string' && key.length > 0) return key;\n } catch {\n /* missing, or no filesystem here — fall through to the apiKey path */\n }\n\n return resolveApiKey(options);\n}\n\n// --- phase / response helpers -------------------------------------------\n\nfunction byPhase(rules, phase) {\n return (rules ?? []).filter((r) => (r.phase ?? 'request') === phase);\n}\n\n// Classify a content-type for response screening: 'text' = screen; 'sniff' = screen only if the\n// bytes aren't binary (octet-stream is often a misdeclared JSON export/config); 'skip' = pass\n// through unscreened (live streams, known binary families). SSE is matched on the EXACT base type,\n// not a loose substring — `application/json; profile=\"event-stream\"` is not a stream.\nfunction baseContentType(ct) {\n return String(ct || '').toLowerCase().split(';')[0].trim();\n}\nfunction screenableContentType(ct) {\n const base = baseContentType(ct);\n if (base === 'text/event-stream') return 'skip'; // live token/SSE stream — never buffer\n if (base === '') return 'text';\n if (/(json|text|xml|html|javascript|csv|yaml|x-www-form-urlencoded)/.test(base)) return 'text';\n if (base === 'application/octet-stream') return 'sniff'; // maybe a text/JSON export mislabeled\n return 'skip'; // image/video/audio/font/pdf/zip/wasm/… — don't buffer binary\n}\n// Cheap binary sniff over a byte prefix: a NUL byte, or many control chars, means \"don't treat as text\".\nfunction looksBinary(bytes) {\n const n = Math.min(bytes.length, 512);\n let ctrl = 0;\n for (let i = 0; i < n; i++) {\n const b = bytes[i];\n if (b === 0) return true;\n if (b < 9 || (b > 13 && b < 32)) ctrl++;\n }\n return n > 0 && ctrl / n > 0.1;\n}\n\n// Returns { text } when the body was fully buffered for screening, or { skip: <reason> } when it was\n// NOT screened — the reason is surfaced to `onSkip`/coverage so a fail-open bypass is observable\n// instead of silent (an unscreened response is a real hole in enforcement).\nasync function readTextResponse(response, cap = DEFAULT_SCREEN_CAP) {\n if (!response || typeof response.clone !== 'function') return { skip: 'not-a-response' };\n const ct = response.headers?.get?.('content-type') || '';\n const kind = screenableContentType(ct);\n if (kind === 'skip') return { skip: baseContentType(ct) === 'text/event-stream' ? 'live-stream' : 'non-text-content-type' };\n const sniff = kind === 'sniff';\n const len = Number(response.headers?.get?.('content-length') || 0);\n if (len && len > cap) return { skip: 'body-cap' };\n let clone;\n try {\n clone = response.clone();\n } catch {\n return { skip: 'clone-failed' };\n }\n\n // Stream the read so a body WITHOUT a Content-Length can't buffer past the cap. Over the cap the\n // response is left UNSCREENED — but we keep draining the clone so the original stays intact.\n const body = clone.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks = [];\n let size = 0;\n let over = false;\n let sniffed = !sniff;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n if (!sniffed) {\n sniffed = true;\n if (looksBinary(value)) return { skip: 'binary-body' };\n }\n size += value.byteLength;\n if (over) continue; // keep draining, stop buffering\n if (size > cap) { over = true; continue; }\n chunks.push(value);\n }\n } catch {\n return { skip: 'read-failed' };\n }\n if (over) return { skip: 'body-cap' };\n try {\n return { text: new TextDecoder().decode(concatBytes(chunks, size)) };\n } catch {\n return { skip: 'decode-failed' };\n }\n }\n\n try {\n const text = await clone.text();\n if (text.length > cap) return { skip: 'body-cap' };\n if (sniff && looksBinary(new TextEncoder().encode(text.slice(0, 512)))) return { skip: 'binary-body' };\n return { text };\n } catch {\n return { skip: 'read-failed' };\n }\n}\n\nfunction concatBytes(chunks, total) {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n\nfunction headerObject(headers) {\n const out = {};\n headers?.forEach?.((v, k) => { out[k.toLowerCase()] = v; });\n // Set-Cookie is multi-valued; forEach collapses it. Recover the individual cookies so each can\n // be screened (and re-emitted) separately.\n const setCookies = headers?.getSetCookie?.();\n if (setCookies && setCookies.length) out['set-cookie'] = setCookies;\n return out;\n}\n\n// True if any of the rule's conditions carries a body-transforming mutation on a SPAN match\n// (regex/contains/stripos) — those decode the body before matching, so a span redactor derived from\n// the literal/regex can't be located in the raw body. (array_key_value structural redaction decodes\n// the JSON itself, so json_decode there is fine and doesn't count.)\nfunction hasSpanMutations(rule) {\n let found = false;\n const walk = (conds) => {\n for (const c of conds ?? []) {\n if (found) return;\n if (Array.isArray(c.rules)) walk(c.rules);\n const isSpan = c.match && (c.match.type === 'regex' || c.match.type === 'contains' || c.match.type === 'stripos');\n if (isSpan && Array.isArray(c.mutations) && c.mutations.length) found = true;\n }\n };\n walk(rule.rule_v2);\n return found;\n}\n\n// Derive redaction targets from a rule's own conditions: regex → mask every match;\n// contains/stripos → mask the literal. (Other match types can't identify a span → the\n// rule falls back to block.)\nfunction extractRedactors(rule) {\n const out = [];\n const walk = (conds) => {\n for (const c of conds ?? []) {\n if (Array.isArray(c.rules)) walk(c.rules);\n const m = c.match;\n if (!m) continue;\n if (m.type === 'regex' && typeof m.value === 'string') {\n // Route through the SAME ReDoS guard detection uses — a catastrophic redactor pattern must\n // not hang the response path (safeRegExp returns null for dangerous/invalid patterns → skip).\n const safe = safeRegExp(m.value);\n if (safe) {\n const flags = safe.flags.includes('g') ? safe.flags : safe.flags + 'g';\n try {\n out.push({ re: new RegExp(safe.source, flags) });\n } catch {\n /* skip invalid */\n }\n }\n } else if ((m.type === 'contains' || m.type === 'stripos') && m.value != null) {\n out.push({ literal: String(m.value) });\n } else if (m.type === 'array_key_value' && m.match && isBodyParam(c.parameter)) {\n // Structural redaction: mask the value at a JSON path (fanning out over arrays) rather than\n // a text span — e.g. key \"orders.customers.email\" masks that field in every array element.\n const keys = Array.isArray(m.key) ? m.key : [m.key];\n for (const key of keys) {\n out.push({ jsonPath: String(key).split('.'), condition: m.match });\n }\n }\n }\n };\n walk(rule.rule_v2);\n return out;\n}\n\n// HTML-entity escape, for the `encode` action (neutralize markup rather than mask it). NOTE: this is\n// sound only for HTML text / attribute-VALUE contexts. It does NOT neutralize a `javascript:` / `data:`\n// URI or an event-handler name (those carry no HTML metacharacters) — use `block` for a rule that\n// targets a URL/scheme context. See the rule-authoring guidance in the triage-vpatch-npm skill.\nfunction htmlEscape(str) {\n return String(str).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c]);\n}\n\n// `transform` (optional): map a matched span to its replacement (the `encode` action passes\n// htmlEscape). Without it, matches are replaced by the `mask` string (the `redact` action).\nfunction applyRedactors(body, redactors, mask, transform) {\n let out = body;\n for (const r of redactors) {\n if (r.re) out = out.replace(r.re, transform ? (m) => transform(m) : mask);\n else if (r.literal) {\n // Detection (matchValue for contains/stripos) is case-insensitive, so mask case-insensitively\n // too — otherwise a `contains: \"SECRET\"` redactor detects `secret` but masks nothing, serving\n // the leak while reporting a redaction. Escape the literal so it matches literally, not as regex.\n const re = new RegExp(r.literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'gi');\n out = out.replace(re, (m) => (transform ? transform(m) : mask));\n }\n }\n return out;\n}\n\n// A response-body redaction target (array_key_value masks the JSON body). A bare condition with no\n// parameter also defaults to the body.\nfunction isBodyParam(parameter) {\n return parameter == null || parameter === 'response.body' || parameter === 'raw' || parameter === 'response.raw';\n}\n\n// Build a predicate from the array_key_value nested match, so a path can be masked conditionally\n// (only leaves that match) or — with `isset` — unconditionally. Fail-closed (don't mask) on error.\nfunction conditionPredicate(condition) {\n if (!condition || !condition.type) return () => true;\n return (value) => {\n try {\n return matchValue(condition.type, value, condition.value, condition);\n } catch {\n return false;\n }\n };\n}\n\nconst DEFAULT_SCREEN_CAP = 512 * 1024;\n\n// Effective response-screening size cap for a rule set. Bodies larger than this are passed through\n// UNSCREENED (so a redact rule can't mask them — the leak/PII would slip out). A rule can raise the\n// ceiling for the whole response phase: `bypass_limit: true` removes the cap entirely (accepts the\n// memory cost on a hostile large body), or `max_bytes: <n>` raises it to n. The cap is shared (the\n// body is buffered once), so the effective cap is the MAX across all active response rules.\nfunction responseScreenCap(rules) {\n let cap = DEFAULT_SCREEN_CAP;\n for (const r of rules ?? []) {\n if (r && r.bypass_limit === true) return Infinity;\n const n = Number(r && r.max_bytes);\n if (Number.isFinite(n) && n > cap) cap = n;\n }\n return cap;\n}\n\n// Apply jsonPath redactors structurally: parse the JSON body, mask each targeted leaf (fanning out\n// over arrays at every path segment), re-serialize. Fail-open — a non-JSON / oversized / unparseable\n// body is returned unchanged, and any per-leaf error is swallowed.\nfunction applyPathRedactors(text, pathRedactors, mask, cap, transform) {\n if (!pathRedactors.length || typeof text !== 'string' || text.length > cap) return text;\n const head = text.trimStart()[0];\n if (head !== '{' && head !== '[') return text; // not a JSON object/array\n // Preserve out-of-safe-range integers across the parse→stringify round-trip: JSON.parse would\n // round e.g. a 20-digit id. We quote such number tokens to a sentinel string before parsing and\n // unquote them after stringifying, so untouched big ints survive losslessly.\n const preserved = preserveBigInts(text);\n let obj;\n try {\n obj = JSON.parse(preserved);\n } catch {\n return text;\n }\n let changed = false;\n for (const r of pathRedactors) {\n const pred = conditionPredicate(r.condition);\n walkLeaves(obj, r.jsonPath, (loc) => {\n try {\n if (pred(loc.value)) {\n // `encode`: escape the leaf's own value in place; `redact`: replace it with the mask.\n loc.parent[loc.key] = transform ? transform(String(loc.value)) : mask;\n changed = true;\n }\n } catch {\n /* skip this leaf */\n }\n });\n }\n return changed ? restoreBigInts(JSON.stringify(obj)) : text;\n}\n\nconst BIGINT_OPEN = '__PSBIGINT_9c2f__';\nconst BIGINT_CLOSE = '__DNEGIB__';\n\n// Quote every out-of-safe-range integer *value* (a bare number token outside a string) into a\n// sentinel string, so JSON.parse keeps it verbatim. String-aware scan (respects \\ escapes) so a\n// number inside a string value is never touched. Plain-ASCII sentinel → survives JSON.stringify.\nfunction preserveBigInts(text) {\n let out = '';\n let inStr = false;\n for (let i = 0; i < text.length; ) {\n const ch = text[i];\n if (inStr) {\n out += ch;\n if (ch === '\\\\') { out += text[i + 1] ?? ''; i += 2; continue; }\n if (ch === '\"') inStr = false;\n i++;\n continue;\n }\n if (ch === '\"') { inStr = true; out += ch; i++; continue; }\n if (ch === '-' || (ch >= '0' && ch <= '9')) {\n let j = ch === '-' ? i + 1 : i;\n let digits = 0;\n while (j < text.length && text[j] >= '0' && text[j] <= '9') { digits++; j++; }\n const next = text[j];\n const isIntToken = digits > 0 && next !== '.' && next !== 'e' && next !== 'E';\n if (isIntToken && digits >= 16) {\n out += `\"${BIGINT_OPEN}${text.slice(i, j)}${BIGINT_CLOSE}\"`;\n } else {\n out += text.slice(i, j || i + 1);\n }\n i = j > i ? j : i + 1;\n continue;\n }\n out += ch;\n i++;\n }\n return out;\n}\n\nfunction restoreBigInts(text) {\n if (!text.includes(BIGINT_OPEN)) return text;\n return text.replace(new RegExp(`\"${BIGINT_OPEN}(-?\\\\d+)${BIGINT_CLOSE}\"`, 'g'), '$1');\n}\n\n// Response-hardening actions. Mutate the (lowercase-keyed) headers object in place; a `null` value\n// signals removal to rebuildResponse / the node path. `set-header` sets/overwrites (or `ensure`s only\n// when absent); `remove-header` strips; `harden-cookie` adds missing HttpOnly/Secure/SameSite flags.\nfunction isHeaderMutation(action) {\n return action === 'set-header' || action === 'remove-header' || action === 'harden-cookie';\n}\n\nfunction applyHeaderMutation(headers, rule) {\n if (rule.action === 'remove-header') {\n for (const name of rule.remove_headers ?? []) headers[String(name).toLowerCase()] = null;\n return;\n }\n if (rule.action === 'set-header') {\n const ensure = rule.ensure === true; // set only when the header is absent (don't clobber)\n for (const [name, value] of Object.entries(rule.set_headers ?? {})) {\n const key = String(name).toLowerCase();\n const present = headers[key] != null && headers[key] !== '';\n if (ensure && present) continue;\n headers[key] = String(value);\n }\n return;\n }\n if (rule.action === 'harden-cookie') {\n const cookie = headers['set-cookie'];\n const flags = rule.cookie_flags ?? {};\n if (Array.isArray(cookie)) {\n headers['set-cookie'] = cookie.map((c) => (typeof c === 'string' ? hardenCookie(c, flags) : c));\n } else if (typeof cookie === 'string') {\n headers['set-cookie'] = hardenCookie(cookie, flags);\n }\n }\n}\n\nfunction hardenCookie(cookie, { httpOnly = true, secure = true, sameSite = 'Lax' } = {}) {\n let out = String(cookie);\n if (httpOnly && !/;\\s*httponly/i.test(out)) out += '; HttpOnly';\n if (secure && !/;\\s*secure/i.test(out)) out += '; Secure';\n if (sameSite && !/;\\s*samesite\\s*=/i.test(out)) out += `; SameSite=${sameSite}`;\n return out;\n}\n\nfunction rebuildResponse(response, body, redactedHeaders) {\n const headers = new Headers(response.headers);\n headers.delete('content-length'); // body length changed after redaction\n if (redactedHeaders) {\n for (const [name, value] of Object.entries(redactedHeaders)) {\n if (value === null || value === undefined) {\n try { headers.delete(name); } catch { /* skip */ } // header-mutation removal\n } else if (typeof value === 'string') {\n if (headers.get(name) !== value) {\n try { headers.set(name, value); } catch { /* invalid header name — skip */ }\n }\n } else if (Array.isArray(value)) {\n // Re-emit each (possibly redacted) Set-Cookie separately (Headers collapses them otherwise).\n try {\n headers.delete(name);\n for (const item of value) headers.append(name, String(item));\n } catch { /* skip */ }\n }\n }\n }\n // Null-body statuses (204/205/304/101) must not carry a body, or the Response constructor throws.\n const nullBody = response.status === 101 || response.status === 204 || response.status === 205 || response.status === 304;\n return new Response(nullBody ? null : body, { status: response.status, statusText: response.statusText, headers });\n}\n\nfunction leakResponse() {\n return new Response(JSON.stringify({ error: 'Response withheld by Patchstack (sensitive data detected)' }), {\n status: 500,\n headers: { 'content-type': 'application/json' }\n });\n}\n\n// (rule source / tiered store moved to ./rules/source.js + ./rules/store.js)\n\n// --- responses ----------------------------------------------------------\n\n// Client-facing block text — deliberately generic: no WAF narrative, no rule title. The reason\n// detail stays in the server-side log via onDetect.\nconst BLOCK_MESSAGE = 'This request has been blocked by Patchstack.';\n\nfunction blockBody(result) {\n // Human text is masked (no WAF narrative, no rule title). The opaque rule id stays for machine\n // consumers (server-fn receipts, support reference); full rule detail lives in the server log.\n return { error: BLOCK_MESSAGE, message: BLOCK_MESSAGE, rule: result?.rule?.id };\n}\n\n// A top-level document navigation (vs an XHR/fetch)? Browsers set Sec-Fetch-Dest on navigations;\n// fall back to the Accept header. Governs whether a block returns the HTML page or JSON.\nfunction isDocumentNavigation(getHeader) {\n const dest = getHeader('sec-fetch-dest');\n if (dest) return dest === 'document';\n return (getHeader('accept') || '').includes('text/html');\n}\n\n// Request-phase block. Serves the branded HTML \"Access Denied\" page to a browser navigation, and\n// masked JSON to XHR/fetch/programmatic clients.\nfunction blockResponse(result, request) {\n if (request && isDocumentNavigation((n) => request.headers.get(n))) {\n return new Response(renderBlockPage({ url: request.url, code: result?.rule?.id }), {\n status: 403,\n headers: { 'content-type': 'text/html; charset=utf-8' },\n });\n }\n return new Response(JSON.stringify(blockBody(result)), {\n status: 403,\n headers: { 'content-type': 'application/json' },\n });\n}\n\nfunction defaultOnDetect({ phase, mode, category, rule, message }) {\n const tag = mode === 'block' ? 'BLOCK' : 'DETECT (dry-run)';\n console.warn(`[patchstack] ${tag} phase=${phase ?? 'request'} category=${category ?? '?'} rule=${rule?.id ?? '?'} ${message ?? ''}`.trim());\n}\n\n/** @param {Request} request */\nfunction fetchRequestMeta(request) {\n if (!request) return {};\n let path = null;\n try {\n path = new URL(request.url).pathname;\n } catch {\n path = typeof request.url === 'string' ? request.url : null;\n }\n return {\n method: request.method ?? null,\n path,\n ip: request.headers?.get?.('x-forwarded-for') ?? null,\n userAgent: request.headers?.get?.('user-agent') ?? null,\n };\n}\n\n/** @param {import('http').IncomingMessage & { ip?: string, originalUrl?: string }} req */\nfunction nodeRequestMeta(req) {\n if (!req) return {};\n const headers = req.headers ?? {};\n const ua = headers['user-agent'] ?? headers['User-Agent'];\n const fwd = headers['x-forwarded-for'] ?? headers['X-Forwarded-For'];\n return {\n method: req.method ?? null,\n path: req.originalUrl || req.url || null,\n ip: req.ip ?? (typeof fwd === 'string' ? fwd : null),\n userAgent: typeof ua === 'string' ? ua : Array.isArray(ua) ? ua[0] : null,\n };\n}\n","// Which origins this guard is willing to talk to. Both of its remote conversations are security\n// sensitive: the RULE endpoint delivers policy the engine then executes on every request (an\n// attacker-controlled endpoint could remove protection wholesale or serve a CPU-expensive ruleset), and\n// the telemetry endpoint receives the site api_key. Env/CI injection is the realistic threat, so a\n// non-default override must be https — with localhost permitted so local development and tests work.\nexport function isSafeOrigin(value) {\n try {\n const u = new URL(value);\n if (u.protocol === 'https:') return true;\n return u.protocol === 'http:' && (u.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '[::1]' || u.hostname === '::1');\n } catch {\n return false;\n }\n}\n\n/**\n * Accept `candidate` only if it is a safe origin; otherwise warn once and fall back to `fallback`.\n * @param {string|undefined} candidate @param {string} fallback @param {string} label\n */\nexport function safeBaseUrl(candidate, fallback, label) {\n if (typeof candidate !== 'string' || candidate === '') return fallback;\n if (isSafeOrigin(candidate)) return candidate;\n warnOnce(label, `[patchstack] ignoring unsafe ${label} override (${candidate}): must be https (or localhost). Using the default.`);\n return fallback;\n}\n\nconst warned = new Set();\nfunction warnOnce(key, message) {\n if (warned.has(key)) return;\n warned.add(key);\n // eslint-disable-next-line no-console\n console.warn(message);\n}\n","import { safeBaseUrl } from '../safe-origin.js';\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com';\nconst DEFAULT_CACHE_TTL = 300_000;\n// Randomly shorten the effective TTL by up to this fraction so many long-lived clients don't all\n// revalidate on the same tick (spreads load against the rules API).\nconst JITTER_FRACTION = 0.1;\n\n// Token-authenticated rules client. Conditional fetch (If-None-Match / 304) and the persisted\n// `etag` mirror PulseRuleClient; both are a no-op until the API emits an ETag and honors the\n// conditional request — with no ETag in the response the client behaves as before (full fetch).\nexport class PatchstackRuleClient {\n #token;\n #baseUrl;\n #cacheTtl;\n #cache = null;\n #cacheTime = null;\n #ttlEffective = 0;\n #etag;\n #timeoutMs;\n\n constructor({ token, baseUrl, cacheTtl, etag, timeoutMs } = {}) {\n // Bounded so app STARTUP can't hang on a slow API (see the boot budget in rules/source.js).\n this.#timeoutMs = Number(timeoutMs) > 0 ? Number(timeoutMs) : 30_000;\n this.#token = token ?? process.env.PATCHSTACK_WAF_TOKEN;\n this.#baseUrl = safeBaseUrl(baseUrl ?? process.env.PATCHSTACK_WAF_API_URL, DEFAULT_BASE_URL, 'rule endpoint');\n this.#cacheTtl = Number.isFinite(cacheTtl) && cacheTtl > 0 ? cacheTtl : DEFAULT_CACHE_TTL;\n this.#etag = etag ?? null;\n\n if (!this.#token) {\n throw new Error('Patchstack WAF token is required. Pass { token } or set PATCHSTACK_WAF_TOKEN env var.');\n }\n }\n\n async getRules() {\n const now = Date.now();\n\n if (this.#cache && this.#cacheTime !== null && (now - this.#cacheTime) < this.#ttlEffective) {\n return this.#cache;\n }\n\n const url = `${this.#baseUrl}/api/get-rules/3`;\n\n try {\n const headers = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.#token}`,\n 'LicenseID': '1' // Hard-coded, is never actually used but needed by the API\n };\n if (this.#etag) headers['If-None-Match'] = this.#etag;\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n signal: AbortSignal.timeout(this.#timeoutMs)\n });\n\n if (response.status === 304) {\n this.#touch(now); // revalidated — reset the clock (fresh jitter)\n return this.#cache ?? { success: true, notModified: true, etag: this.#etag, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n\n if (!response.ok) {\n return {\n success: false,\n error: `API returned ${response.status}: ${response.statusText}`,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n\n const data = await response.json();\n\n // A 200 that isn't a genuine rule envelope must not be treated as \"no rules\". Report failure\n // so the caller falls back to the cache / bundled rules rather than caching an empty bundle.\n if (!data || !Array.isArray(data.firewall)) {\n return { success: false, error: 'unexpected response shape (no firewall array)', firewall: [], whitelists: [], whitelist_keys: {} };\n }\n\n const result = {\n success: true,\n notModified: false,\n etag: response.headers?.get?.('etag') ?? null,\n firewall: data.firewall,\n whitelists: Array.isArray(data.whitelists) ? data.whitelists : [],\n whitelist_keys: data.whitelist_keys ?? {}\n };\n\n this.#cache = result;\n this.#etag = result.etag;\n this.#touch(now);\n\n return result;\n } catch (err) {\n return {\n success: false,\n error: err.name === 'TimeoutError'\n ? 'Request timed out'\n : err.message,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n }\n\n #touch(now) {\n this.#cacheTime = now;\n this.#ttlEffective = this.#cacheTtl - Math.floor(Math.random() * this.#cacheTtl * JITTER_FRACTION);\n }\n\n clearCache() {\n this.#cache = null;\n this.#cacheTime = null;\n this.#etag = null;\n }\n}\n","// Resolvable DATA attributes of an uploaded file part (files.<name>.<attr>). The engine only exposes\n// the raw data — WHAT counts as a malicious upload (signatures, type-vs-content mismatch) is expressed\n// in rules (see the triage-vpatch-npm skill), not hardcoded here.\nconst FILE_ATTRS = new Set(['content', 'filename', 'type']);\n\n// A captured file part is { filename, type, content }; tolerate the legacy bare-filename string.\nconst fileFilename = (f) => (f && typeof f === 'object' ? f.filename : f);\nconst fileAttribute = (f, attr) => (f && typeof f === 'object' ? f[attr] : attr === 'filename' ? f : undefined);\n\n// WinterCG-safe base64 decode: use Buffer on Node, fall back to atob/TextDecoder on\n// edge runtimes (Cloudflare Workers, Deno, Bun) where Buffer may be absent. Keeps the\n// engine hot path free of Node-only APIs (per the ADR engine-language decision).\nfunction base64DecodeUtf8(value) {\n const str = String(value);\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n const binary = atob(str);\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n}\n\nexport class RequestResolver {\n #req;\n #cookies;\n\n constructor(req) {\n this.#req = req;\n this.#cookies = null;\n }\n\n resolve(parameter) {\n if (!parameter || parameter === 'false') {\n return [null];\n }\n\n if (parameter === 'rules') {\n return [null];\n }\n\n if (parameter === 'raw') {\n return this.#resolveRaw();\n }\n\n if (parameter === 'all') {\n return this.#resolveAll();\n }\n\n const dotIndex = parameter.indexOf('.');\n if (dotIndex === -1) {\n return [];\n }\n\n const source = parameter.substring(0, dotIndex);\n const key = parameter.substring(dotIndex + 1);\n\n switch (source) {\n case 'get':\n return this.#resolveGet(key);\n case 'post':\n return this.#resolvePost(key);\n case 'request':\n return this.#resolveRequest(key);\n case 'cookie':\n return this.#resolveCookie(key);\n case 'server':\n return this.#resolveServer(key);\n case 'files':\n return this.#resolveFiles(key);\n case 'response':\n return this.#resolveResponse(key);\n case 'egress':\n return this.#resolveEgress(key);\n default:\n return [];\n }\n }\n\n // Response-phase sources (req._response = { status, headers, body }). Lets rules inspect\n // what the app is about to SEND — e.g. a leaked secret in the body — regardless of route.\n #resolveResponse(key) {\n const resp = this.#req._response;\n if (!resp) {\n return [];\n }\n if (key === 'status') {\n return resp.status !== undefined ? [String(resp.status)] : [];\n }\n if (key === 'body') {\n return resp.body != null && resp.body !== '' ? [resp.body] : [];\n }\n if (key === 'headers') {\n const headers = resp.headers ?? {};\n return [Object.entries(headers).map(([k, v]) => `${k}: ${v}`).join('\\n')];\n }\n if (key.startsWith('header.')) {\n const name = key.slice('header.'.length).toLowerCase();\n const value = (resp.headers ?? {})[name];\n return value !== undefined ? [value] : [];\n }\n return [];\n }\n\n // Egress-phase sources (req._egress = { url, host, method }). Lets rules inspect an\n // OUTBOUND request the app is about to make — SSRF at the egress boundary.\n #resolveEgress(key) {\n const eg = this.#req._egress;\n if (!eg) {\n return [];\n }\n if (key === 'url') return eg.url ? [eg.url] : [];\n if (key === 'host') return eg.host ? [eg.host] : [];\n if (key === 'method') return eg.method ? [eg.method] : [];\n return [];\n }\n\n applyMutations(mutations, value) {\n if (!mutations || !Array.isArray(mutations)) {\n return value;\n }\n\n let result = value;\n\n for (const mutation of mutations) {\n result = this.#applyMutation(mutation, result);\n }\n\n return result;\n }\n\n #applyMutation(mutation, value) {\n if (value === null || value === undefined) {\n return value;\n }\n\n switch (mutation) {\n case 'base64_decode':\n try {\n return base64DecodeUtf8(value);\n } catch {\n return value;\n }\n\n case 'json_decode':\n try {\n return JSON.parse(String(value));\n } catch {\n return value;\n }\n\n case 'json_encode':\n try {\n return JSON.stringify(value);\n } catch {\n return value;\n }\n\n case 'urldecode':\n try {\n return decodeURIComponent(String(value));\n } catch {\n return value;\n }\n\n case 'intval':\n return parseInt(String(value), 10) || 0;\n\n case 'getArrayValues':\n if (typeof value === 'object' && value !== null) {\n return Object.values(value);\n }\n return value;\n\n default:\n return value;\n }\n }\n\n #resolveGet(key) {\n const query = this.#req.query ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(query, key);\n }\n\n const value = this.#getNestedValue(query, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolvePost(key) {\n const body = this.#req.body ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(body, key);\n }\n\n const value = this.#getNestedValue(body, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolveRequest(key) {\n const query = this.#req.query ?? {};\n const body = this.#req.body ?? {};\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return [\n ...this.#resolveWildcard(query, key),\n ...this.#resolveWildcard(body, key),\n ...this.#resolveWildcard(cookies, key)\n ];\n }\n\n const value = this.#getNestedValue(query, key)\n ?? this.#getNestedValue(body, key)\n ?? cookies[key];\n\n return value !== undefined ? [value] : [];\n }\n\n #resolveCookie(key) {\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(cookies, key);\n }\n\n const value = cookies[key];\n return value !== undefined ? [value] : [];\n }\n\n #resolveServer(key) {\n const req = this.#req;\n\n switch (key) {\n case 'REQUEST_URI':\n return [req.originalUrl ?? req.url ?? '/'];\n case 'REQUEST_METHOD':\n return [req.method ?? 'GET'];\n case 'HTTP_USER_AGENT':\n return req.headers?.['user-agent'] ? [req.headers['user-agent']] : [];\n case 'HTTP_REFERER':\n return req.headers?.referer ? [req.headers.referer] : [];\n case 'HTTP_HOST':\n return req.headers?.host ? [req.headers.host] : [];\n case 'REMOTE_ADDR':\n case 'ip':\n return [req.ip ?? req.socket?.remoteAddress ?? ''];\n case 'CONTENT_TYPE':\n return req.headers?.['content-type'] ? [req.headers['content-type']] : [];\n case 'CONTENT_LENGTH':\n return req.headers?.['content-length'] ? [req.headers['content-length']] : [];\n default: {\n if (key.startsWith('HTTP_')) {\n const headerName = key.substring(5).toLowerCase().replace(/_/g, '-');\n const headers = req.headers;\n // Presence, not truthiness. A header sent with an empty value IS present, and an `isset`\n // rule authored against it must see it — some bypasses are carried by the header existing\n // at all, so treating `Header:` as absent would make the rule quietly miss the shape it\n // was written for. (The named cases above keep value semantics: for host/origin/referer an\n // empty string and an absent header mean the same thing to the matchers that read them.)\n if (headers === null || typeof headers !== 'object') return [];\n return Object.prototype.hasOwnProperty.call(headers, headerName) ? [headers[headerName]] : [];\n }\n return [];\n }\n }\n }\n\n #resolveFiles(key) {\n const files = this.#req.files;\n if (!files || typeof files !== 'object') {\n return [];\n }\n\n // files.<name>.<attr> — content | filename | type. Fans out over multiple files uploaded under\n // the same field name.\n const dot = key.lastIndexOf('.');\n if (dot !== -1 && FILE_ATTRS.has(key.slice(dot + 1)) && Object.prototype.hasOwnProperty.call(files, key.slice(0, dot))) {\n const attr = key.slice(dot + 1);\n const entry = files[key.slice(0, dot)];\n const list = Array.isArray(entry) ? entry : [entry];\n const out = [];\n for (const f of list) {\n const v = fileAttribute(f, attr);\n if (v !== undefined && v !== '') out.push(v);\n }\n return out;\n }\n\n // Bare files.<name> (or wildcard) → the filename(s), preserving the legacy behavior that\n // filename-scoped rules rely on (the parser now stores a { filename, type, content } object).\n const filenamesOf = (entry) => (Array.isArray(entry) ? entry.map(fileFilename) : [fileFilename(entry)]);\n if (key.endsWith('*')) {\n const prefix = key.slice(0, -1);\n const out = [];\n for (const [k, entry] of Object.entries(files)) {\n if (k.startsWith(prefix)) out.push(...filenamesOf(entry));\n }\n return out.filter((v) => v !== undefined);\n }\n if (!Object.prototype.hasOwnProperty.call(files, key)) return [];\n return filenamesOf(files[key]).filter((v) => v !== undefined);\n }\n\n #resolveRaw() {\n // Use pre-captured raw body if available (set by normalizeRequest).\n // For string bodies, the original text is preserved verbatim.\n // For pre-parsed objects, serializeForRawDetection uses Object.getOwnPropertyNames()\n // to include __proto__ own-property keys that JSON.stringify() would silently drop.\n if (typeof this.#req._rawBody === 'string') {\n return this.#req._rawBody ? [this.#req._rawBody] : [];\n }\n\n const body = this.#req.body;\n\n if (body === undefined || body === null) {\n return [];\n }\n\n if (typeof body === 'string') {\n return [body];\n }\n\n try {\n return [JSON.stringify(body)];\n } catch {\n return [String(body)];\n }\n }\n\n #resolveAll() {\n const parts = [];\n\n const uri = this.#req.originalUrl ?? this.#req.url ?? '/';\n parts.push(uri);\n\n const queryString = uri.includes('?') ? uri.split('?')[1] : '';\n if (queryString) {\n parts.push(queryString);\n }\n\n const body = this.#req.body;\n if (body) {\n parts.push(typeof body === 'string' ? body : JSON.stringify(body));\n }\n // Also fold in the verbatim body: a body the adapter couldn't structurally parse (an unusual\n // content-type, a non-JSON payload) leaves `body` empty, but the raw text must still be matchable\n // by an `all` rule — otherwise it's only visible via `raw`.\n if (typeof this.#req._rawBody === 'string' && this.#req._rawBody) {\n parts.push(this.#req._rawBody);\n }\n\n const headers = this.#req.headers ?? {};\n const excludedHeaders = new Set([\n 'host', 'connection', 'cache-control', 'accept', 'accept-encoding',\n 'accept-language', 'priority', 'sec-ch-ua', 'sec-ch-ua-mobile',\n 'sec-ch-ua-platform', 'sec-fetch-dest', 'sec-fetch-mode',\n 'sec-fetch-site', 'sec-fetch-user', 'upgrade-insecure-requests'\n ]);\n\n for (const [name, value] of Object.entries(headers)) {\n if (!excludedHeaders.has(name)) {\n parts.push(`${name}: ${value}`);\n }\n }\n\n const cookies = this.#parseCookies();\n const cookieStr = Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ');\n if (cookieStr) {\n parts.push(cookieStr);\n }\n\n return [parts.join(' ')];\n }\n\n #resolveWildcard(obj, pattern) {\n if (typeof obj !== 'object' || obj === null) {\n return [];\n }\n\n const prefix = pattern.slice(0, -1);\n const values = [];\n\n for (const [key, value] of Object.entries(obj)) {\n if (key.startsWith(prefix)) {\n values.push(value);\n }\n }\n\n return values;\n }\n\n #getNestedValue(obj, key) {\n if (typeof obj !== 'object' || obj === null) {\n return undefined;\n }\n\n // Own-property only: `key in obj` would resolve `__proto__`/`constructor`/`toString` to the\n // prototype chain, so a rule like `post.__proto__` (detecting a literal `__proto__` field) would\n // read Object.prototype instead of request data and never match. Use hasOwnProperty.\n const own = (o, k) => o !== null && typeof o === 'object' && Object.prototype.hasOwnProperty.call(o, k);\n\n if (own(obj, key)) {\n return obj[key];\n }\n\n const parts = key.split('.');\n let current = obj;\n\n for (const part of parts) {\n if (!own(current, part)) {\n return undefined;\n }\n current = current[part];\n }\n\n return current;\n }\n\n #parseCookies() {\n if (this.#cookies !== null) {\n return this.#cookies;\n }\n\n if (this.#req.cookies) {\n this.#cookies = this.#req.cookies;\n return this.#cookies;\n }\n\n const header = this.#req.headers?.cookie;\n if (!header) {\n this.#cookies = {};\n return this.#cookies;\n }\n\n const cookies = {};\n\n for (const pair of header.split(';')) {\n const eqIndex = pair.indexOf('=');\n if (eqIndex === -1) {\n continue;\n }\n const name = pair.substring(0, eqIndex).trim();\n const value = pair.substring(eqIndex + 1).trim();\n cookies[name] = value;\n }\n\n this.#cookies = cookies;\n return this.#cookies;\n }\n}\n","/**\n * Request Normalization Pipeline\n *\n * Normalizes request data to prevent encoding-based bypass attacks.\n * This module handles URL encoding, HTML entities, SQL comments, and other\n * obfuscation techniques that attackers use to evade pattern matching.\n */\n\nconst HTML_ENTITIES = {\n '&amp;': '&',\n '&lt;': '<',\n '&gt;': '>',\n '&quot;': '\"',\n '&#39;': \"'\",\n '&apos;': \"'\",\n '&nbsp;': ' ',\n '&#x27;': \"'\",\n '&#x22;': '\"',\n '&#x3C;': '<',\n '&#x3E;': '>',\n '&#x26;': '&',\n '&#34;': '\"',\n '&#60;': '<',\n '&#62;': '>',\n '&#38;': '&'\n};\n\nconst MAX_DECODE_ITERATIONS = 5;\n\nexport function normalize(value, options = {}) {\n if (typeof value !== 'string') {\n return value;\n }\n\n const opts = {\n urlDecode: true,\n htmlDecode: true,\n sqlComments: true,\n nullBytes: true,\n whitespace: true,\n ...options\n };\n\n let result = value;\n\n if (opts.nullBytes) {\n result = removeNullBytes(result);\n }\n\n if (opts.urlDecode) {\n result = urlDecode(result);\n }\n\n if (opts.htmlDecode) {\n result = htmlEntityDecode(result);\n }\n\n if (opts.sqlComments) {\n result = removeSqlComments(result);\n }\n\n if (opts.whitespace) {\n result = normalizeWhitespace(result);\n }\n\n return result;\n}\n\nexport function urlDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n let previous = '';\n let iterations = 0;\n\n while (result !== previous && iterations < MAX_DECODE_ITERATIONS) {\n previous = result;\n iterations++;\n\n try {\n result = decodeURIComponent(result);\n } catch {\n result = safeUrlDecode(result);\n break;\n }\n }\n\n return result;\n}\n\nfunction safeUrlDecode(value) {\n return value.replace(/%([0-9A-Fa-f]{2})/g, (match, hex) => {\n try {\n return String.fromCharCode(parseInt(hex, 16));\n } catch {\n return match;\n }\n });\n}\n\nexport function htmlEntityDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n\n for (const [entity, char] of Object.entries(HTML_ENTITIES)) {\n result = result.split(entity).join(char);\n }\n\n result = result.replace(/&#(\\d+);/g, (match, code) => {\n const num = parseInt(code, 10);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n result = result.replace(/&#x([0-9A-Fa-f]+);/g, (match, hex) => {\n const num = parseInt(hex, 16);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n return result;\n}\n\nexport function removeSqlComments(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n // Collapse inline block comments to a space (the anti-obfuscation goal). We must NOT strip the\n // line-comment forms (`--…`, `#…`) to end-of-line: on the WAF inspection path that DELETES\n // attacker-controlled spans from the value the engine sees while the app still processes the\n // original — e.g. `#<script>…` becomes empty and evades an XSS rule, though the browser still\n // runs it. Keeping the content only ever ADDS matches (more of the value is inspected), never\n // hides one. SQLi keyword detection is unaffected — the keywords remain visible.\n let result = value;\n\n result = result.replace(/\\/\\*[\\s\\S]*?\\*\\//g, ' ');\n result = result.replace(/\\/\\*![\\s\\S]*?\\*\\//g, ' ');\n\n return result;\n}\n\nexport function removeNullBytes(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/\\x00/g, '');\n\n result = result.replace(/[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F]/g, '');\n\n return result;\n}\n\nexport function normalizeWhitespace(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/[\\t\\r\\n\\f\\v]+/g, ' ');\n\n result = result.replace(/ {2,}/g, ' ');\n\n return result;\n}\n\nfunction serializeForRawDetection(body, visited = new Set(), isRoot = true) {\n if (body === null || body === undefined) {\n return isRoot ? '' : 'null';\n }\n\n if (typeof body === 'string') {\n return isRoot ? body : JSON.stringify(body);\n }\n\n if (typeof body !== 'object') {\n return String(body);\n }\n\n if (visited.has(body)) {\n return '[Circular]';\n }\n visited.add(body);\n\n if (Array.isArray(body)) {\n const items = body.map(item => serializeForRawDetection(item, visited, false));\n return '[' + items.join(',') + ']';\n }\n\n // Object.getOwnPropertyNames includes non-enumerable own properties, so a __proto__ key\n // set via Object.defineProperty (as modern JSON.parse may do) is included in the output.\n const keys = Object.getOwnPropertyNames(body);\n const parts = keys.map(key => {\n const val = serializeForRawDetection(body[key], visited, false);\n return JSON.stringify(key) + ':' + val;\n });\n\n return '{' + parts.join(',') + '}';\n}\n\nexport function normalizeRequest(req, options = {}) {\n // Prefer a caller-provided verbatim body string (set by the fetch/node adapters):\n // it preserves literal keys like `__proto__` that JSON.stringify drops, which is\n // what makes prototype-pollution rules on `raw` robust. Fall back to a\n // reconstruction from the parsed body (the Express path, which has no raw text).\n const rawBody = typeof req._rawBody === 'string'\n ? req._rawBody\n : serializeForRawDetection(req.body ?? null);\n\n return {\n query: normalizeObject(req.query || {}, options),\n body: normalizeObject(req.body || {}, options),\n headers: normalizeObject(req.headers || {}, options),\n url: normalize(req.url || '', options),\n originalUrl: normalize(req.originalUrl || req.url || '', options),\n _rawBody: rawBody\n };\n}\n\n// Depth bound for the recursive walk: a pathologically deep object would otherwise overflow the\n// stack, and the engine's per-rule catch would swallow that into a fail-open. Beyond the bound the\n// sub-value is left un-normalized (still matched, just in its raw form) rather than crashing.\nconst MAX_NORMALIZE_DEPTH = 200;\n\nexport function normalizeObject(value, options = {}, depth = 0) {\n if (typeof value === 'string') {\n return normalize(value, options);\n }\n\n if (depth >= MAX_NORMALIZE_DEPTH) {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map(item => normalizeObject(item, options, depth + 1));\n }\n\n if (typeof value === 'object' && value !== null) {\n const result = {};\n\n for (const [key, val] of Object.entries(value)) {\n result[key] = normalizeObject(val, options, depth + 1);\n }\n\n return result;\n }\n\n return value;\n}\n\nexport function createMatchVariants(value) {\n if (typeof value !== 'string') {\n return [value];\n }\n\n const variants = new Set();\n\n variants.add(value);\n\n const urlDecoded = urlDecode(value);\n variants.add(urlDecoded);\n\n const htmlDecoded = htmlEntityDecode(value);\n variants.add(htmlDecoded);\n\n const bothDecoded = htmlEntityDecode(urlDecoded);\n variants.add(bothDecoded);\n\n const normalized = normalize(value);\n variants.add(normalized);\n\n variants.add(value.toLowerCase());\n variants.add(normalized.toLowerCase());\n\n return Array.from(variants);\n}\n\nexport const _testExports = {\n safeUrlDecode,\n serializeForRawDetection,\n HTML_ENTITIES,\n MAX_DECODE_ITERATIONS\n};\n","import { RequestResolver } from './request.js';\nimport { normalizeRequest } from './normalizer.js';\n\n// Catastrophic-backtracking shapes. Broad on purpose: a group whose inner content is quantified\n// (+, *, or {n,}) and is itself quantified — (a+)+, (\\w+)+, (.*)*, ([a-z]+)*, (ab+)+ — or an\n// alternation under an outer quantifier — (a|a)*, (x|y)+. A rule matching one of these is skipped\n// (safer than hanging the event loop). The `NEST` variants allow ONE level of nested parentheses in\n// the outer group so a quantified SUBGROUP is caught too (`((ab)+)+`, `((a|b)+)*`) — the earlier\n// `[^)]*` forms stopped at the first inner `)` and missed those. `.test()` scans every start offset,\n// so deeper nestings match at an inner window too.\nconst GRP = '(?:[^()]|\\\\([^()]*\\\\))*'; // group body allowing one level of nested parens\nconst REDOS_PATTERNS = [\n /\\([^)]*[+*}][^)]*\\)\\s*[+*]/,\n /\\([^)]*\\|[^)]*\\)\\s*[+*]/,\n new RegExp('\\\\(' + GRP + '[+*}]' + GRP + '\\\\)\\\\s*[+*]'), // nested quantified subgroup\n new RegExp('\\\\(' + GRP + '\\\\|' + GRP + '\\\\)\\\\s*[+*]') // nested alternation under a quantifier\n];\n\n// Report once when a rule's regex is rejected (ReDoS-shaped or unparseable). Unlike an unknown match\n// type, a rejected regex used to fail silently — so a delivered rule protected nothing and nobody knew.\nconst warnedRejectedPatterns = new Set();\nfunction warnRejectedPatternOnce(pattern) {\n const key = String(pattern);\n if (warnedRejectedPatterns.has(key)) return;\n warnedRejectedPatterns.add(key);\n console.warn(\n `[patchstack] rule_v2 regex pattern rejected (unsafe or invalid) — condition treated as no-match. ` +\n `The rule relying on it is NOT enforced: ${key}`\n );\n}\n\n// An absurdly long pattern is either a mistake or an attack on our own matcher; compiling and running\n// it on every request is unbounded work. The rule-bundle validator rejects these upstream — this is the\n// backstop for a caller-supplied bundle that never went through it.\nconst MAX_PATTERN_LENGTH = 1000;\n\nexport function safeRegExp(pattern) {\n if (!pattern) {\n return null;\n }\n if (typeof pattern !== 'string' || pattern.length > MAX_PATTERN_LENGTH) {\n return null;\n }\n\n for (const dangerous of REDOS_PATTERNS) {\n if (dangerous.test(pattern)) {\n return null;\n }\n }\n\n const match = pattern.match(/^\\/(.+?)\\/([gimsuy]*)$/s);\n if (!match) {\n return null;\n }\n\n try {\n return new RegExp(match[1], match[2]);\n } catch {\n return null;\n }\n}\n\n// ctype_*/is_numeric compare their character-class result to the rule's expected\n// `value`. Rules are almost always written `{ \"type\": \"ctype_digit\", \"value\": false }`\n// meaning \"flag when NOT of this class\" — so the result must be compared to matchVal,\n// not returned raw. Empty/absent values never match (mirrors engine-php's `$value != ''`\n// guard), otherwise every missing parameter false-positives a `value:false` rule.\n// When `value` is not a boolean (null/omitted) the legacy default of `true` is used, so\n// existing rules that relied on the raw class check are unaffected.\nfunction ctypeResult(strValue, isClass, matchVal) {\n if (strValue === '') {\n return false;\n }\n const expected = typeof matchVal === 'boolean' ? matchVal : true;\n return isClass === expected;\n}\n\n// Walk a dot-path into a decoded object and invoke `cb({ parent, key, value })` for every leaf it\n// reaches, fanning out over arrays at EVERY segment (not just the last) — so `orders.customers.email`\n// visits the email of each customer of each order, for arbitrary-length / arbitrarily-nested arrays.\n// `cb` gets the container + key so callers can either read (match) or set (redact) the leaf.\nexport function walkLeaves(node, segments, cb) {\n if (node === null || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n for (const el of node) walkLeaves(el, segments, cb);\n return;\n }\n const [head, ...rest] = segments;\n if (head === undefined || !Object.prototype.hasOwnProperty.call(node, head)) return;\n if (rest.length === 0) {\n const leaf = node[head];\n if (Array.isArray(leaf)) {\n for (let i = 0; i < leaf.length; i++) cb({ parent: leaf, key: i, value: leaf[i] });\n } else {\n cb({ parent: node, key: head, value: leaf });\n }\n } else {\n walkLeaves(node[head], rest, cb);\n }\n}\n\n// array_key_value: navigate `match.key` (a dot-path, or an array of paths) inside the decoded value\n// and run the nested `match.match` against every leaf it finds (fanning out over arrays at every\n// segment). Mirrors engine-php's recursive array_key_value handling.\nfunction arrayKeyValue(value, matchObj) {\n if (!matchObj || !matchObj.match || value === null || typeof value !== 'object') {\n return false;\n }\n const keys = Array.isArray(matchObj.key) ? matchObj.key : [matchObj.key];\n const sub = matchObj.match;\n\n for (const key of keys) {\n let matched = false;\n walkLeaves(value, String(key).split('.'), ({ value: leaf }) => {\n if (!matched && matchValue(sub.type, leaf, sub.value, sub)) matched = true;\n });\n if (matched) return true;\n }\n return false;\n}\n\n// Match types that operate on the whole container (not per-leaf): `isset` (presence) and\n// `array_in_array` / `array_key_value` (structural). Everything else is a scalar matcher that must\n// fan out over the leaves of an object/array value.\nconst WHOLE_VALUE_MATCH_TYPES = new Set(['isset', 'array_in_array', 'array_key_value']);\n\n// Iteratively collect every scalar (non-object) leaf of a structured value. Iterative + bounded\n// (depth and node caps) so a pathologically deep/large attacker payload STOPS at the bound rather\n// than throwing a RangeError that the per-rule catch would swallow into a fail-open bypass.\nfunction collectLeafValues(root, nodeCap = 20000, maxDepth = 1000) {\n const out = [];\n const stack = [[root, 0]];\n let visited = 0;\n while (stack.length) {\n const [node, depth] = stack.pop();\n if (node === null || node === undefined) continue;\n if (typeof node !== 'object') {\n out.push(node);\n continue;\n }\n if (depth >= maxDepth || visited >= nodeCap) continue;\n visited++;\n if (Array.isArray(node)) {\n for (let i = node.length - 1; i >= 0; i--) stack.push([node[i], depth + 1]);\n } else {\n for (const k of Object.keys(node)) stack.push([node[k], depth + 1]);\n }\n }\n return out;\n}\n\n// Emit a warning at most once per distinct key (keeps a persistent misconfiguration from spamming).\nconst warnedKeys = new Set();\nfunction warnOnce(key, message) {\n if (warnedKeys.has(key)) return;\n warnedKeys.add(key);\n console.warn(message);\n}\n\n// Report an unknown/removed match type once, so a rule referencing it is not silently\n// unenforced (ADR: \"unknown match type → skipped and logged, never silently passed\").\nconst warnedMatchTypes = new Set();\nfunction warnUnsupportedMatchType(type) {\n if (type == null || warnedMatchTypes.has(type)) {\n return;\n }\n warnedMatchTypes.add(type);\n console.warn(\n `[patchstack] Unsupported rule_v2 match type \"${type}\" — condition treated as no-match. ` +\n `Rules relying on it are not enforced by this engine.`\n );\n}\n\n// Internal / private / loopback / link-local / cloud-metadata host check behind the `internal_host`\n// match type. It CANONICALIZES the host before classifying —\n// a textual/prefix check is bypassable by alternate encodings (decimal/hex/octal IPv4, expanded or\n// IPv4-mapped IPv6), which is a classic SSRF evasion. Handles localhost / *.local / GCP metadata\n// names, every IPv4 spelling inet_aton accepts, and IPv6 loopback/link-local/unique-local/mapped.\n/**\n * The host to classify out of a rule parameter's value.\n *\n * `internal_host` was written for the egress phase, where the value IS the destination host. On the\n * request phase the same question arrives as an application parameter, and there the value is almost\n * always a full URL (`?url=http://169.254.169.254/latest/meta-data/`) or a `host:port` pair — neither of\n * which is a hostname, so classifying the raw string answered \"not internal\" for every one of them. A\n * request-phase SSRF rule was therefore expressible, servable and permanently inert: the exact failure\n * this engine has been repeatedly hardened against, in the one match type meant to prevent it.\n *\n * Only the host is extracted; the classification itself is unchanged, so every canonicalization defence\n * (decimal/hex IPv4, expanded and IPv4-mapped IPv6, trailing dots) still applies to what comes out. A\n * value that is already a bare host passes through untouched, which is what keeps the egress path and\n * the built-in default rule behaving exactly as before.\n */\nfunction hostFromValue(value) {\n const raw = String(value ?? '').trim();\n if (raw === '') return '';\n\n // A scheme (`http://`, and deliberately any other) or a protocol-relative URL. Parsing rather than\n // string-slicing is what makes the userinfo evasion (`http://trusted@169.254.169.254/`) resolve to the\n // host actually contacted, and keeps `http://evil.com#@127.0.0.1` resolving to evil.com.\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(raw) || raw.startsWith('//')) {\n try {\n return new URL(raw.startsWith('//') ? `http:${raw}` : raw).hostname;\n } catch {\n // Unparseable: hand the raw value on, where the host check rejects it rather than guessing.\n return raw;\n }\n }\n\n // `[::1]:8080` — bracketed IPv6 with or without a port.\n if (raw.startsWith('[')) {\n const end = raw.indexOf(']');\n if (end > 0) return raw.slice(1, end);\n }\n\n // `169.254.169.254:80`. Only a single colon followed by digits: a bare IPv6 address has several, and\n // must not have its last group mistaken for a port.\n const colon = raw.indexOf(':');\n if (colon > 0 && raw.indexOf(':', colon + 1) === -1 && /^\\d+$/.test(raw.slice(colon + 1))) {\n return raw.slice(0, colon);\n }\n\n return raw;\n}\n\nfunction isInternalHost(hostname) {\n if (!hostname) return false;\n let host = String(hostname).toLowerCase().replace(/^\\[|\\]$/g, '');\n host = host.replace(/%[^\\]]*$/, ''); // strip an IPv6 zone id (fe80::1%eth0)\n host = host.replace(/\\.$/, ''); // strip a single trailing dot (127.0.0.1.)\n\n if (host === 'localhost' || host.endsWith('.localhost') || host.endsWith('.local')) return true;\n if (host === 'metadata.google.internal') return true;\n\n // IPv6 (contains a colon): expand to 8 groups, then classify on the canonical form.\n if (host.includes(':')) {\n const g = expandIPv6(host);\n if (!g) return false;\n const allZeroHi = g[0] === 0 && g[1] === 0 && g[2] === 0 && g[3] === 0 && g[4] === 0;\n if (allZeroHi && g[5] === 0 && g[6] === 0 && (g[7] === 0 || g[7] === 1)) return true; // ::, ::1 loopback\n if ((g[0] & 0xffc0) === 0xfe80) return true; // link-local fe80::/10\n if ((g[0] & 0xfe00) === 0xfc00) return true; // unique-local fc00::/7\n if (allZeroHi && (g[5] === 0xffff || g[5] === 0)) {\n // IPv4-mapped (::ffff:a.b.c.d) / IPv4-compatible (::a.b.c.d) — classify the embedded v4.\n return isPrivateV4Int((((g[6] << 16) >>> 0) | g[7]) >>> 0);\n }\n return false;\n }\n\n // IPv4 in any inet_aton spelling (dotted quad, shorthand, decimal, hex, octal).\n const v4 = parseIPv4ToInt(host);\n if (v4 !== null) return isPrivateV4Int(v4);\n return false;\n}\n\n// Private / loopback / link-local / this-host / CGNAT test on a 32-bit IPv4 integer.\nfunction isPrivateV4Int(n) {\n const a = (n >>> 24) & 0xff;\n const b = (n >>> 16) & 0xff;\n if (a === 127 || a === 10 || a === 0) return true; // loopback / private / this-host\n if (a === 169 && b === 254) return true; // link-local incl. 169.254.169.254 metadata\n if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12\n if (a === 192 && b === 168) return true; // 192.168.0.0/16\n if (a === 100 && b >= 64 && b <= 127) return true; // 100.64.0.0/10 CGNAT (Alibaba metadata 100.100.100.200)\n return false;\n}\n\n// inet_aton-style parse: 1–4 dot-separated parts, each decimal / 0x-hex / 0-octal; the final part\n// fills the remaining low bytes. Returns a uint32, or null if the host isn't a numeric IPv4 form\n// (so ordinary hostnames — which contain letters outside [0-9a-fx] or other chars — return null).\nfunction parseIPv4ToInt(host) {\n if (!/^[0-9a-fx]+(\\.[0-9a-fx]+)*$/i.test(host)) return null;\n const parts = host.split('.');\n if (parts.length > 4) return null;\n const nums = [];\n for (const p of parts) {\n let n;\n if (/^0x[0-9a-f]+$/i.test(p)) n = parseInt(p, 16);\n else if (/^0[0-7]+$/.test(p)) n = parseInt(p, 8);\n else if (/^[0-9]+$/.test(p)) n = parseInt(p, 10);\n else return null; // e.g. a bare hex like \"7f\" (not inet_aton-valid without 0x)\n if (!Number.isInteger(n) || n < 0) return null;\n nums.push(n);\n }\n const last = nums.length - 1;\n let value = 0;\n for (let i = 0; i < last; i++) {\n if (nums[i] > 0xff) return null;\n value += nums[i] * 2 ** (8 * (3 - i));\n }\n const maxLast = 2 ** (8 * (5 - nums.length)) - 1; // the final part fills the remaining low bytes\n if (nums[last] > maxLast) return null;\n value += nums[last];\n if (value < 0 || value > 0xffffffff) return null;\n return value >>> 0;\n}\n\n// Expand an IPv6 string (any `::` compression, optional embedded IPv4 tail) to 8 numeric groups,\n// or null if it isn't valid IPv6. Lets the classifier compare the canonical form, not a string prefix.\nfunction expandIPv6(host) {\n if (!host.includes(':')) return null;\n let s = host;\n // Embedded IPv4 tail (::ffff:127.0.0.1) → convert the dotted part to two hex groups.\n const lastColon = s.lastIndexOf(':');\n const tail = s.slice(lastColon + 1);\n if (tail.includes('.')) {\n const v4 = parseIPv4ToInt(tail);\n if (v4 === null) return null;\n s = s.slice(0, lastColon + 1) + ((v4 >>> 16) & 0xffff).toString(16) + ':' + (v4 & 0xffff).toString(16);\n }\n const halves = s.split('::');\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(':') : [];\n const back = halves.length === 2 ? (halves[1] ? halves[1].split(':') : []) : null;\n let groups;\n if (back === null) {\n groups = head;\n } else {\n const fill = 8 - head.length - back.length;\n if (fill < 0) return null;\n groups = [...head, ...Array(fill).fill('0'), ...back];\n }\n if (groups.length !== 8) return null;\n const out = [];\n for (const g of groups) {\n if (!/^[0-9a-f]{1,4}$/i.test(g)) return null;\n out.push(parseInt(g, 16));\n }\n return out;\n}\n\n// Report a `when` block that names nothing this engine understands. Fail-open is correct for a scope that\n// cannot be EVALUATED, but a scope that cannot be UNDERSTOOD is an authoring mistake with the opposite\n// consequence: the rule silently applies to every request instead of one route, which for a blocking rule\n// is a false-positive surface across the whole app. Warned once so it is discoverable in a log.\nconst warnedScopes = new Set();\nfunction warnUnrecognisedScope(when) {\n const key = Object.keys(when).sort().join(',');\n if (warnedScopes.has(key)) return;\n warnedScopes.add(key);\n console.warn(\n `[patchstack] Rule scope \\`when: { ${key} }\\` names no supported key — the engine understands ` +\n `\\`method\\` and \\`path\\`. The scope is IGNORED and the rule applies to every request.`\n );\n}\n\n// Route/method scope for a rule's optional `when: { method, path }`. Fail-open: if the scope can't\n// be evaluated, the rule still applies (never silently suppress a rule).\nfunction ruleAppliesTo(when, resolver) {\n try {\n if (when.method === undefined && when.path === undefined && Object.keys(when).length > 0) {\n warnUnrecognisedScope(when);\n }\n if (when.method) {\n const methods = (Array.isArray(when.method) ? when.method : [when.method]).map((m) => String(m).toUpperCase());\n const actual = String(resolver.resolve('server.REQUEST_METHOD')[0] ?? 'GET').toUpperCase();\n if (!methods.includes(actual)) return false;\n }\n if (when.path) {\n const uri = String(resolver.resolve('server.REQUEST_URI')[0] ?? '/');\n if (!pathMatches(String(when.path), uri.split('?')[0])) return false;\n }\n return true;\n } catch {\n return true;\n }\n}\n\n// `when.path`: an exact path, a glob with `*`, or an explicit `/regex/` (starts & ends with `/`).\nfunction pathMatches(pattern, path) {\n if (pattern.length > 2 && pattern.startsWith('/') && pattern.endsWith('/')) {\n const re = safeRegExp(pattern);\n return re ? re.test(path) : false;\n }\n if (pattern.includes('*')) {\n const re = safeRegExp('/^' + pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '[^?]*') + '$/');\n return re ? re.test(path) : false;\n }\n return path === pattern;\n}\n\n// Drop a default port so `app.com:443` and `app.com` compare equal (a real proxy shape), without\n// dropping a non-default port (so genuine cross-port stays distinguishable).\nfunction normalizeDefaultPort(host) {\n return String(host).toLowerCase().replace(/:(?:80|443)$/, '');\n}\n\n// CSRF primitive: does the request come from a different origin than its own Host? Lenient only when\n// the Origin AND Referer are TRULY ABSENT (a non-browser client). A present-but-opaque `Origin: null`\n// / empty / unparseable value is NOT same-origin — it's the sandboxed-iframe / opaque-origin signal a\n// CSRF attacker supplies, so it is treated as cross-origin.\nfunction isCrossOrigin(resolver) {\n try {\n const host = normalizeDefaultPort(String(resolver.resolve('server.HTTP_HOST')[0] ?? ''));\n if (!host) return false;\n const originRaw = resolver.resolve('server.HTTP_ORIGIN')[0];\n const hasOrigin = originRaw !== undefined && originRaw !== null;\n const src = hasOrigin ? originRaw : resolver.resolve('server.HTTP_REFERER')[0];\n if (src === undefined || src === null) return false; // both absent → lenient\n const s = String(src).trim();\n if (hasOrigin && (s === '' || s.toLowerCase() === 'null')) return true; // present but opaque → cross-origin\n const srcHost = hostFromUrl(s);\n if (srcHost === null) return hasOrigin; // present-but-unparseable Origin → treat as cross-origin\n return normalizeDefaultPort(srcHost) !== host;\n } catch {\n return false;\n }\n}\n\nfunction hostFromUrl(value) {\n try {\n return new URL(value).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n// Open-redirect primitive (response phase): a 3xx whose Location header points to a DIFFERENT origin\n// than the request's own Host. A relative Location (same-origin) never matches. Needs the request\n// Host, which the response phase threads in via reqCtx. Lenient: no Location, no request Host, or a\n// same-origin / relative target → not flagged (so it can't false-positive without the signal).\nfunction isOffOriginRedirect(resolver) {\n try {\n const status = Number(resolver.resolve('response.status')[0] ?? 0);\n if (status < 300 || status >= 400) return false;\n const location = resolver.resolve('response.header.location')[0];\n if (!location) return false;\n const host = String(resolver.resolve('server.HTTP_HOST')[0] ?? '').toLowerCase();\n if (!host) return false;\n // Resolve the Location the way a browser would before comparing hosts: strip TAB/CR/LF and\n // normalize backslashes to forward slashes (browsers do), then resolve against the request origin\n // as a base. A relative `/path` resolves to our own host (not flagged); a protocol-relative\n // `//evil.com` or backslash `/\\evil.com` resolves off-origin (flagged) — the canonical\n // open-redirect payloads that a base-less `new URL()` used to treat as \"relative & safe\".\n const loc = String(location).replace(/[\\t\\r\\n]/g, '').replace(/\\\\/g, '/');\n let target;\n try {\n target = new URL(loc, 'http://' + host).host.toLowerCase();\n } catch {\n return false; // unresolvable even with a base → not a redirect we can judge\n }\n return normalizeDefaultPort(target) !== normalizeDefaultPort(host);\n } catch {\n return false;\n }\n}\n\n// CORS-reflection primitive (response phase): the response allows credentials AND lets any origin\n// read it — either `Access-Control-Allow-Origin: *`, or it reflects the caller's own Origin (so\n// every origin is allowed) — rather than a fixed allowlisted origin. That combination lets any\n// malicious site read the authenticated response. Needs the request Origin (threaded via reqCtx).\n// Lenient: credentials not allowed, no ACAO, or a fixed (non-reflected, non-*) ACAO → not flagged.\nfunction isReflectedCorsWithCredentials(resolver) {\n try {\n const acac = String(resolver.resolve('response.header.access-control-allow-credentials')[0] ?? '').toLowerCase();\n if (acac !== 'true') return false; // only dangerous when credentials are allowed\n const acao = String(resolver.resolve('response.header.access-control-allow-origin')[0] ?? '');\n if (!acao) return false;\n if (acao === '*') return true; // wildcard + credentials\n if (acao.toLowerCase() === 'null') return true; // `null` + credentials is readable from a sandboxed iframe (Origin: null)\n const origin = String(resolver.resolve('server.HTTP_ORIGIN')[0] ?? '');\n if (!origin) return false;\n return acao === origin; // ACAO echoes the caller's Origin → any origin is allowed\n } catch {\n return false;\n }\n}\n\n// `matchObj` is the full match object; needed by types that read sibling fields\n// (array_key_value reads `key`/`match`). Optional so direct callers/tests can keep\n// using the (type, value, matchVal) signature.\nexport function matchValue(type, value, matchVal, matchObj) {\n if (value === null || value === undefined) {\n if (type === 'isset') {\n return false;\n }\n return false;\n }\n\n // Guard the coercion: String() on a pathologically deep array/object can throw RangeError\n // (stack overflow). Catching it here keeps a hostile nested value from failing the rule open.\n let strValue;\n if (typeof value === 'string') {\n strValue = value;\n } else {\n try {\n strValue = String(value);\n } catch {\n strValue = '';\n }\n }\n\n switch (type) {\n case 'equals':\n return strValue == matchVal;\n\n case 'equals_strict':\n // Type-strict by design (`'1' !== 1`). A rule that wants loose matching should use `equals`.\n return strValue === matchVal;\n\n case 'contains':\n // engine-php exposes `stripos` as an alias of `contains`.\n case 'stripos': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return lower.includes(target);\n }\n\n case 'not_contains': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return !lower.includes(target);\n }\n\n case 'regex': {\n const regex = safeRegExp(matchVal);\n if (!regex) {\n warnRejectedPatternOnce(matchVal);\n return false;\n }\n return regex.test(strValue);\n }\n\n case 'more_than':\n return Number(strValue) > Number(matchVal);\n\n case 'less_than':\n return Number(strValue) < Number(matchVal);\n\n case 'ctype_digit':\n return ctypeResult(strValue, /^\\d+$/.test(strValue), matchVal);\n\n case 'ctype_alnum':\n return ctypeResult(strValue, /^[\\w$\\u0080-\\uFFFF]+$/.test(strValue), matchVal);\n\n case 'is_numeric':\n return ctypeResult(strValue, !isNaN(strValue) && strValue.trim() !== '', matchVal);\n\n case 'isset':\n return true;\n\n case 'in_array': {\n const arr = (Array.isArray(matchVal) ? matchVal : [matchVal]).map((x) => String(x));\n return arr.includes(strValue);\n }\n\n case 'not_in_array': {\n const arr = (Array.isArray(matchVal) ? matchVal : [matchVal]).map((x) => String(x));\n return !arr.includes(strValue);\n }\n\n case 'array_in_array': {\n if (!Array.isArray(value) || !Array.isArray(matchVal)) {\n return false;\n }\n const target = matchVal.map((x) => String(x));\n return value.some((v) => target.includes(String(v)));\n }\n\n case 'hostname': {\n try {\n const url = new URL(strValue);\n return url.hostname === matchVal;\n } catch {\n return false;\n }\n }\n\n case 'internal_host':\n // SSRF: private / loopback / link-local / cloud-metadata destinations. The value may be a bare\n // host (egress) or a URL / host:port in an application parameter (request) — see `hostFromValue`.\n return isInternalHost(hostFromValue(strValue));\n\n case 'quotes':\n // engine-php exposes `inline_js_xss` as an alias of `quotes`.\n case 'inline_js_xss':\n return /['\"]/.test(strValue);\n\n case 'inline_xss':\n // Attribute-breakout heuristic: a quote AND a `>` or `=` (matches engine-php).\n return /['\"]/.test(strValue) && /[>=]/.test(strValue);\n\n case 'ctype_special': {\n // engine-php strips space, `_`, `-`, `,` then applies the alnum/unicode check,\n // and compares the result to `value` (usually false = \"flag when NOT clean\").\n const stripped = strValue.replace(/[ _\\-,]/g, '');\n return ctypeResult(strValue, /^[\\w$€-￿]*$/.test(stripped), matchVal);\n }\n\n case 'array_key_value':\n // Navigate `match.key` (dot-path, or array of paths) inside the decoded value and\n // run the nested `match.match` against it — e.g. a json_decode'd body where a\n // specific key must hold a specific value.\n return arrayKeyValue(value, matchObj);\n\n // file_contains (uploaded-file content scanning) is NOT WordPress-specific, but is\n // not yet implemented in the JS engine (needs multipart body access). Kept as an\n // explicit, documented no-match rather than silently hitting `default`.\n case 'file_contains':\n return false;\n\n // WordPress-only match types (current_user_cannot, general_xss, getShortcodeAtts,\n // getBlockAtts) have been removed: they depend on WP capabilities, wp_kses_post, and\n // the shortcode/block parsers, and have no meaning in a JS app. They now fall through\n // here and are reported once instead of pretending to be a no-match.\n default:\n warnUnsupportedMatchType(type);\n return false;\n }\n}\n\nexport class RuleEngine {\n #rules;\n #whitelists;\n #whitelistKeys;\n #onError;\n #reportedErrors = new Set();\n\n constructor({ firewall = [], whitelists = [], whitelist_keys = {}, onError } = {}) {\n this.#rules = firewall;\n this.#whitelists = whitelists;\n this.#whitelistKeys = whitelist_keys;\n this.#onError = onError;\n // A whitelist with no `rule_id` suppresses EVERY rule when its (attacker-reachable) condition\n // trips — almost never intended. And `whitelist_keys` is accepted but not implemented. Warn once\n // for each so a misconfiguration that silently weakens the firewall is visible to the operator.\n if (Array.isArray(whitelists) && whitelists.some((w) => w && Array.isArray(w.rule_v2) && !w.rule_id)) {\n warnOnce(\n 'whitelist-no-rule-id',\n '[patchstack] a whitelist has no `rule_id` — it suppresses ALL rules when it matches. ' +\n 'Scope each whitelist to a specific rule_id, and key it only on values an attacker cannot set.'\n );\n }\n if (whitelist_keys && typeof whitelist_keys === 'object' && Object.keys(whitelist_keys).length > 0) {\n warnOnce('whitelist-keys-unimplemented', '[patchstack] `whitelist_keys` is not implemented and has no effect.');\n }\n }\n\n // A mitigation engine must never take down the app it protects: any error while\n // evaluating a rule is reported and the request is allowed through (fail open). A\n // malformed rule is skipped without aborting the rest of the ruleset.\n #reportError(err) {\n if (this.#onError) {\n this.#onError(err);\n return;\n }\n // Default: log once per distinct message so a persistently-broken rule doesn't\n // spam per request (never silently swallow — hosts can pass `onError` for structured logging).\n const key = err && err.message ? err.message : String(err);\n if (this.#reportedErrors.has(key)) {\n return;\n }\n this.#reportedErrors.add(key);\n console.error('[patchstack] rule evaluation error (rule skipped, request allowed):', err);\n }\n\n evaluate(req) {\n let normalizedReq;\n let resolver;\n try {\n normalizedReq = { ...req, ...normalizeRequest(req) };\n resolver = new RequestResolver(normalizedReq);\n } catch (err) {\n this.#reportError(err);\n return { blocked: false, rule: null, message: null }; // fail open\n }\n\n for (const rule of this.#rules) {\n try {\n // Route/method scope: a rule may declare `when: { method, path }` to apply only on matching\n // requests (so an auto-generated per-endpoint rule doesn't fire elsewhere).\n if (rule.when && !ruleAppliesTo(rule.when, resolver)) {\n continue;\n }\n\n const conditions = rule.rule_v2;\n\n if (!Array.isArray(conditions) || conditions.length === 0) {\n continue;\n }\n\n if (this.#evaluateRule(conditions, resolver)) {\n if (this.#isWhitelisted(normalizedReq, rule)) {\n continue;\n }\n\n return {\n blocked: true,\n rule,\n message: rule.message ?? `Blocked by Patchstack WAF rule: ${rule.title ?? rule.id}`\n };\n }\n } catch (err) {\n // Skip this rule and keep evaluating the rest — one bad rule never blocks a\n // request nor shadows the rules after it.\n this.#reportError(err);\n }\n }\n\n return { blocked: false, rule: null, message: null };\n }\n\n #evaluateRule(conditions, resolver) {\n const inclusiveConditions = conditions.filter(c => c.inclusive);\n const nonInclusiveConditions = conditions.filter(c => !c.inclusive);\n\n if (inclusiveConditions.length > 0) {\n let inclusiveHits = 0;\n\n for (const condition of inclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n inclusiveHits++;\n }\n }\n\n if (inclusiveHits === inclusiveConditions.length) {\n return true;\n }\n }\n\n for (const condition of nonInclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n\n #evaluateCondition(condition, resolver) {\n const { parameter, match, mutations } = condition;\n\n if (parameter === 'rules' && Array.isArray(condition.rules)) {\n return this.#evaluateRule(condition.rules, resolver);\n }\n\n // `cross_origin` needs the request as a whole (Origin/Referer vs Host), not a single resolved\n // parameter — it's the CSRF primitive: true (→ block) when the request comes from another origin.\n if (match && match.type === 'cross_origin') {\n return isCrossOrigin(resolver);\n }\n\n // `off_origin` (response phase): true (→ block) when a 3xx redirects to a different origin than\n // the request Host — the open-redirect primitive. Like cross_origin, it needs the whole resolver.\n if (match && match.type === 'off_origin') {\n return isOffOriginRedirect(resolver);\n }\n\n // `cors_reflected` (response phase): true (→ block) when the response allows credentials and\n // reflects the caller's Origin (or uses `*`) — the CORS-misconfiguration primitive. Needs the\n // whole resolver (request Origin vs response ACAO/ACAC).\n if (match && match.type === 'cors_reflected') {\n return isReflectedCorsWithCredentials(resolver);\n }\n\n // `parameter` may be an array (e.g. [\"get.action\",\"post.action\"]) — the rule_v2 format\n // uses these pervasively to mean \"any of these sources\". Resolve each and OR the\n // candidate values together. (A bare string resolves as a single source.)\n const params = Array.isArray(parameter) ? parameter : [parameter];\n const values = params.flatMap((p) => resolver.resolve(p));\n\n if (values.length === 0) {\n return false;\n }\n\n for (let value of values) {\n if (mutations) {\n value = resolver.applyMutations(mutations, value);\n }\n\n // array_key_value inspects the object as a whole (it navigates a key path),\n // so it must not go through the per-value object iteration below.\n if (match.type === 'array_key_value') {\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n continue;\n }\n\n // A structured (object / array-of-object) value must be inspected at every leaf: a payload\n // nested deeper than a scalar rule expects would otherwise stringify to \"[object Object]\" and\n // evade the match, while the app still reads the live value. Whole-value match types\n // (isset / array_in_array) see the container; scalar matchers fan out over all leaves.\n if (typeof value === 'object' && value !== null) {\n if (WHOLE_VALUE_MATCH_TYPES.has(match.type)) {\n if (matchValue(match.type, value, match.value, match)) return true;\n } else {\n for (const leaf of collectLeafValues(value)) {\n if (matchValue(match.type, leaf, match.value, match)) return true;\n }\n }\n continue;\n }\n\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n }\n\n return false;\n }\n\n #isWhitelisted(req, rule) {\n if (!this.#whitelists || this.#whitelists.length === 0) {\n return false;\n }\n\n for (const whitelist of this.#whitelists) {\n if (!Array.isArray(whitelist.rule_v2)) {\n continue;\n }\n\n if (whitelist.rule_id && whitelist.rule_id !== rule.id) {\n continue;\n }\n\n const resolver = new RequestResolver(req);\n if (this.#evaluateRule(whitelist.rule_v2, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n}\n\nexport const _testExports = { matchValue, safeRegExp, isInternalHost };\n","import { safeBaseUrl } from '../safe-origin.js';\nimport { pulseAuthHeader } from '../../pulse-token.js';\n\nconst DEFAULT_BASE_URL = 'https://api.patchstack.com/monitor/pulse';\nconst DEFAULT_CACHE_TTL = 300_000;\n// Randomly shorten the effective TTL by up to this fraction so many long-lived clients don't all\n// revalidate on the same tick (spreads load / avoids a thundering herd against the rules API).\nconst JITTER_FRACTION = 0.1;\n\n// Per-site rules client for Pulse (npm/JS) apps. Public endpoint — the site UUID is the only\n// credential, passed in the path. Fail-open: any error returns success:false + empty rules so\n// createProtection falls back to the disk cache or the bundled rules.\n//\n// Conditional fetch: pass a prior `etag` (persisted with the last cached bundle) and the client\n// sends `If-None-Match`; a 304 returns `{ notModified: true }` so the caller reuses its cache\n// without re-downloading, and a 200 returns the new bundle plus its `etag` to persist. This is a\n// no-op until the rules API emits an ETag and honors If-None-Match — with no ETag in the response\n// the client simply behaves as before (a full fetch every refresh).\nexport class PulseRuleClient {\n #siteUuid;\n #timeoutMs;\n #baseUrl;\n #cacheTtl;\n #cache = null;\n #cacheTime = null;\n #ttlEffective = 0;\n #etag;\n #pulseAuth;\n\n constructor({ siteUuid, baseUrl, cacheTtl, etag, timeoutMs, pulseAuth } = {}) {\n // Bounded so app STARTUP can't hang on a slow API: hosted platforms fail a deploy whose health\n // check is slow, and we always have a cache/bundled fallback to boot from.\n this.#timeoutMs = Number(timeoutMs) > 0 ? Number(timeoutMs) : 30_000;\n this.#siteUuid = siteUuid ?? process.env.PATCHSTACK_SITE_UUID;\n // Rules are executed policy — refuse a plaintext remote override (see safe-origin.js).\n this.#baseUrl = safeBaseUrl(baseUrl ?? process.env.PATCHSTACK_PULSE_RULES_URL, DEFAULT_BASE_URL, 'rule endpoint');\n this.#cacheTtl = Number.isFinite(cacheTtl) && cacheTtl > 0 ? cacheTtl : DEFAULT_CACHE_TTL;\n this.#etag = etag ?? null;\n this.#pulseAuth = pulseAuth ?? null;\n if (!this.#siteUuid) {\n throw new Error('Patchstack site UUID is required. Pass { siteUuid } or set PATCHSTACK_SITE_UUID.');\n }\n }\n\n async getRules() {\n const now = Date.now();\n if (this.#cache && this.#cacheTime !== null && now - this.#cacheTime < this.#ttlEffective) {\n return this.#cache;\n }\n const url = `${this.#baseUrl}/rules/${encodeURIComponent(this.#siteUuid)}`;\n try {\n // Unauthenticated when no credential resolved, or when the exchange\n // fails — the server still accepts the UUID, and protection must never\n // hinge on getting a token.\n const headers = {\n Accept: 'application/json',\n ...(await pulseAuthHeader(\n { pulseAuth: this.#pulseAuth, endpoint: this.#baseUrl, timeoutMs: this.#timeoutMs },\n fetch,\n )),\n };\n if (this.#etag) headers['If-None-Match'] = this.#etag;\n const response = await fetch(url, { method: 'GET', headers, signal: AbortSignal.timeout(this.#timeoutMs) });\n\n if (response.status === 304) {\n this.#touch(now); // revalidated — reset the clock (fresh jitter)\n return this.#cache ?? { success: true, notModified: true, etag: this.#etag, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n if (!response.ok) {\n return { success: false, error: `API returned ${response.status}`, firewall: [], whitelists: [], whitelist_keys: {} };\n }\n const data = await response.json();\n // A 200 that isn't a genuine rule envelope (schema drift, a proxy/interstitial page, an\n // {error} body) must not be treated as \"no rules\". Report failure so the caller falls back to\n // the cache / bundled rules rather than caching an empty bundle.\n if (!data || !Array.isArray(data.firewall)) {\n return { success: false, error: 'unexpected response shape (no firewall array)', firewall: [], whitelists: [], whitelist_keys: {} };\n }\n const result = {\n success: true,\n notModified: false,\n etag: response.headers?.get?.('etag') ?? null,\n firewall: data.firewall,\n whitelists: Array.isArray(data.whitelists) ? data.whitelists : [],\n whitelist_keys: data.whitelist_keys ?? {},\n ...enforcementField(data),\n };\n this.#cache = result;\n this.#etag = result.etag;\n this.#touch(now);\n return result;\n } catch (err) {\n return {\n success: false,\n error: err.name === 'TimeoutError' ? 'Request timed out' : err.message,\n firewall: [], whitelists: [], whitelist_keys: {},\n };\n }\n }\n\n #touch(now) {\n this.#cacheTime = now;\n this.#ttlEffective = this.#cacheTtl - Math.floor(Math.random() * this.#cacheTtl * JITTER_FRACTION);\n }\n\n clearCache() {\n this.#cache = null;\n this.#cacheTime = null;\n this.#etag = null;\n }\n}\n\n/** @param {unknown} data @returns {{ enforcement?: 'block'|'dry-run' }} */\nfunction enforcementField(data) {\n if (!data || typeof data !== 'object') return {};\n const v = data.enforcement ?? data.mode;\n return v === 'block' || v === 'dry-run' ? { enforcement: v } : {};\n}\n","// Web Fetch adapter — runs the node-waf engine on any runtime where a request is a\n// WHATWG `Request` and a handler returns a `Response`: Cloudflare Workers, Bun, Deno,\n// Hono, Next.js edge, and TanStack Start's `server.ts`. This is the surface AI-builder\n// apps actually deploy to, and where require-based instrumentation can't reach.\n//\n// The engine already consumes a runtime-neutral request shape; this adapter builds that\n// shape from a `Request`, so no engine changes are needed beyond keeping the hot path\n// free of Node-only APIs.\nimport { RuleEngine } from './engine.js';\n\n// Cap how much request body we buffer for inspection. A larger body is left UNSCANNED\n// (fail-open) rather than buffered into memory — matches the node adapter's maxBodyBytes.\nconst MAX_BODY_BYTES = 1024 * 1024;\n\n// Build the engine's request shape from a WHATWG Request. The body is read from a\n// CLONE so the downstream handler still receives an intact request.\nexport async function fromFetchRequest(request, options = {}) {\n const maxBodyBytes = options.maxBodyBytes ?? MAX_BODY_BYTES;\n const url = new URL(request.url);\n const method = (request.method || 'GET').toUpperCase();\n\n const headers = {};\n request.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n let rawBody = '';\n if (method !== 'GET' && method !== 'HEAD') {\n rawBody = await readCappedText(request, maxBodyBytes);\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n let files;\n if (rawBody) {\n const parsed = parseBody(rawBody, contentType);\n body = parsed.body;\n files = parsed.files;\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n files,\n headers,\n ip: forwarded.split(',')[0].trim(),\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify\n // drops, so prototype-pollution rules on `raw` are robust.\n _rawBody: rawBody\n };\n}\n\n// Read a request body as text for inspection. Memory is bounded by a hard ceiling (4× the scan\n// cap): a body whose declared Content-Length exceeds it is left UNSCANNED (fail-open). Within the\n// ceiling, an oversize body is TRUNCATED to `max` and its prefix is still scanned — so a\n// front-loaded payload is caught — rather than being discarded outright. Reads a clone so the\n// downstream handler keeps an intact body. (`max` is compared in bytes against Content-Length; the\n// prefix slice is by character, which can only over-scan a multibyte body — the safe direction.)\nasync function readCappedText(request, max) {\n // Do NOT skip scanning based on a declared Content-Length: an attacker can declare a huge length\n // (or none) to dodge inspection while sending a small exploit body. Always stream-scan the prefix\n // up to `max` (buffering is bounded to `max`; the rest is drained but not retained). Anything past\n // the cap is unscanned — the documented prefix-scan tradeoff — but the body is never skipped whole.\n let clone;\n try {\n clone = request.clone();\n } catch {\n return '';\n }\n\n // Stream the read so a body WITHOUT a Content-Length can't buffer unbounded: retain only up to the\n // scan cap (`max`) for inspection, but keep draining to completion so the original request stays\n // intact. A front-loaded payload is still caught; anything past the cap is not scanned (same\n // partial-scan tradeoff as before, now with a hard memory bound regardless of Content-Length).\n const body = clone.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks = [];\n let buffered = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value || buffered >= max) continue; // keep draining, stop buffering past the cap\n const take = Math.min(value.byteLength, max - buffered);\n chunks.push(take === value.byteLength ? value : value.subarray(0, take));\n buffered += take;\n }\n } catch {\n return '';\n }\n try {\n return new TextDecoder().decode(concatChunks(chunks, buffered));\n } catch {\n return '';\n }\n }\n\n // No readable stream — fall back to text() with the post-read guard.\n try {\n const text = await clone.text();\n return text.length > max ? text.slice(0, max) : text;\n } catch {\n return '';\n }\n}\n\nfunction concatChunks(chunks, total) {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n\n// Minimal multipart/form-data parser: enough to expose field names + values (so `post.<field>`\n// and `raw` rules match uploads, e.g. a `__proto__` field name) and file metadata (filename via\n// `files.<field>`). We only need the textual structure, not the binary file contents.\n// Parse a request body into { body, files } for parameter-scoped rules. Content-type detection is\n// deliberately permissive: many AI-built apps `JSON.parse(await req.text())` regardless of the\n// declared type, so a JSON body arriving as `application/vnd.api+json`, `application/ld+json`,\n// `text/plain`, `application/csp-report`, or with NO content-type must still populate post.<field>\n// (otherwise a field-scoped rule silently resolves to nothing). Unrecognized/binary bodies stay `{}`\n// and are still matchable via `raw`.\nexport function parseBody(rawBody, contentType) {\n const ct = String(contentType || '').toLowerCase();\n const isJson = ct.includes('application/json') || /\\+json\\b/.test(ct);\n const isForm = ct.includes('application/x-www-form-urlencoded');\n const isMultipart = ct.includes('multipart/form-data');\n // \"ambiguous\" = a type an app commonly parses as JSON/form even though it isn't declared as such.\n const isAmbiguous = ct === '' || ct.startsWith('text/plain') || ct.includes('csp-report') || ct.includes('/json');\n\n if (isMultipart) {\n const boundary = /boundary=(\"?)([^\";]+)\\1/i.exec(contentType)?.[2];\n if (boundary) return parseMultipart(rawBody, boundary);\n return { body: {}, files: undefined };\n }\n if (isForm) {\n const body = {};\n for (const [k, v] of new URLSearchParams(rawBody)) body[k] = k in body ? [].concat(body[k], v) : v;\n return { body, files: undefined };\n }\n if (isJson || isAmbiguous) {\n try {\n const parsed = JSON.parse(rawBody);\n if (parsed && typeof parsed === 'object') return { body: parsed, files: undefined };\n } catch {\n /* not JSON — leave body empty; `raw`/`all` still see the verbatim text */\n }\n }\n return { body: {}, files: undefined };\n}\n\nexport function parseMultipart(rawBody, boundary) {\n const body = {};\n const files = {};\n for (const part of rawBody.split('--' + boundary)) {\n // Tolerate both CRLF and LF-only line endings (some clients/proxies send bare \\n).\n const sep = /\\r?\\n\\r?\\n/.exec(part);\n if (!sep) continue;\n const rawHeaders = part.slice(0, sep.index);\n const disposition = /content-disposition:[^\\r\\n]*/i.exec(rawHeaders)?.[0];\n if (!disposition) continue;\n const name = /name=\"([^\"]*)\"/i.exec(disposition)?.[1];\n if (name == null) continue;\n const content = part.slice(sep.index + sep[0].length).replace(/\\r?\\n$/, '');\n const filename = /filename=\"([^\"]*)\"/i.exec(disposition)?.[1];\n if (filename !== undefined) {\n // Capture the part's declared content-type and CONTENT (not just the filename), so rules can\n // inspect an upload's bytes (files.<name>.content) and detect a declared-vs-actual type\n // mismatch (files.<name>.mismatch). The content rides inside the already-capped rawBody.\n const partType = /content-type:\\s*([^\\r\\n;]+)/i.exec(rawHeaders)?.[1]?.trim() || '';\n const file = { filename, type: partType, content };\n files[name] = name in files ? [].concat(files[name], file) : file;\n } else {\n body[name] = name in body ? [].concat(body[name], content) : content;\n }\n }\n return { body, files };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlockResponse(result) {\n return new Response(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n }),\n { status: 403, headers: { 'content-type': 'application/json' } }\n );\n}\n\n/**\n * Returns a guard `(request) => Promise<Response|null>`. A `Response` means blocked;\n * `null` means allowed — the caller should proceed to its handler. Accepts either a\n * rules bundle (`{ firewall, whitelists, whitelist_keys }`) or a `RuleEngine` instance.\n * Fails open: a rule that throws never blocks the request.\n */\nexport function createFetchMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function'\n ? rulesData\n : new RuleEngine(rulesData);\n\n const guard = async (request) => {\n let req;\n let result;\n try {\n req = await fromFetchRequest(request); // shaping inside the try — a bad/relative request.url must fail open\n result = engine.evaluate(req);\n } catch (err) {\n if (options.onError) {\n options.onError(err);\n }\n return null; // fail open\n }\n\n if (result.blocked) {\n if (options.onBlock) {\n options.onBlock({\n rule: result.rule,\n message: result.message,\n request: { method: req.method, url: req.url, ip: req.ip }\n });\n }\n return (options.response || defaultBlockResponse)(result);\n }\n\n return null;\n };\n\n guard.engine = engine;\n return guard;\n}\n\n/**\n * Wrap a fetch handler so every request is screened first. One-line hook:\n * export default { fetch: wrapFetchHandler(app.fetch, rulesData) }\n */\nexport function wrapFetchHandler(handler, rulesData, options = {}) {\n const guard = createFetchMiddleware(rulesData, options);\n const wrapped = async (request, ...rest) => {\n const blocked = await guard(request);\n return blocked ?? handler(request, ...rest);\n };\n wrapped.engine = guard.engine;\n return wrapped;\n}\n","// Raw Node.js / Connect adapter — for traditional Node servers where the request is a\n// `http.IncomingMessage` and the body is NOT already parsed (plain `http`, Connect,\n// Express without a body-parser, Fastify via its Node req). It buffers the body once,\n// builds the engine's request shape, and blocks or calls `next()`.\n//\n// This complements the Express `createMiddleware` (which assumes `req.body`/`req.query`\n// are already populated) and the Web-Fetch adapter (Workers/edge). Mount it FIRST, before\n// any body-parser — it consumes the stream and exposes the parsed body as `req.body`.\nimport { RuleEngine } from './engine.js';\nimport { parseBody } from './fetch.js';\n\n// Build the engine's request shape from a Node IncomingMessage + its raw body text.\nexport function fromNodeRequest(req, rawBody = '') {\n const method = (req.method || 'GET').toUpperCase();\n\n const headers = {};\n for (const [key, value] of Object.entries(req.headers || {})) {\n headers[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value;\n }\n\n // An unusual Host header or req.url can make `new URL` throw; shaping must never crash the\n // request (fail-open), so fall back to a safe base.\n const host = headers.host || 'localhost';\n let url;\n try {\n url = new URL(req.url || '/', `http://${host}`);\n } catch {\n try {\n url = new URL(req.url || '/', 'http://localhost');\n } catch {\n url = new URL('http://localhost/');\n }\n }\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n let files;\n if (rawBody) {\n // Same permissive content-type handling as the fetch adapter (+json / text/plain / no-CT bodies\n // still populate post.<field>; multipart exposes field + file metadata) on a raw-Node server too.\n const parsed = parseBody(rawBody, contentType);\n body = parsed.body;\n files = parsed.files;\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n files,\n headers,\n ip: forwarded.split(',')[0].trim() || (req.socket && req.socket.remoteAddress) || '',\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify drops.\n _rawBody: rawBody\n };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlock(res, result) {\n res.statusCode = 403;\n res.setHeader('content-type', 'application/json');\n res.end(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n })\n );\n}\n\n/**\n * Connect/Express-style middleware `(req, res, next)` that buffers the body itself.\n * Accepts a `RuleEngine` instance or a `{ firewall, whitelists, whitelist_keys }` bundle.\n * Fails open: an engine error (or oversized body) never blocks the request.\n * Options: `{ maxBodyBytes = 1MiB, onBlock, onError, response }`.\n */\nexport function createNodeMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function' ? rulesData : new RuleEngine(rulesData);\n const maxBytes = options.maxBodyBytes ?? 1024 * 1024;\n\n return function guard(req, res, next) {\n const chunks = [];\n let size = 0;\n let overflow = false;\n\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n\n req.on('error', (err) => next(err));\n\n req.on('end', () => {\n let result;\n let shaped;\n try {\n const rawBody = overflow ? '' : Buffer.concat(chunks).toString('utf8');\n shaped = fromNodeRequest(req, rawBody); // shaping is inside the try too — never crash\n result = engine.evaluate(shaped);\n } catch (err) {\n if (options.onError) {\n options.onError(err);\n }\n return next(); // fail open\n }\n\n if (result.blocked) {\n if (options.onBlock) {\n options.onBlock({\n rule: result.rule,\n message: result.message,\n request: { method: shaped.method, url: shaped.url, ip: shaped.ip }\n });\n }\n return (options.response || defaultBlock)(res, result);\n }\n\n // Expose the parsed body downstream so a body-parser isn't also required.\n req.body = shaped.body;\n next();\n });\n };\n}\n","// Egress guard MECHANISM only. Wraps the app's outbound calls so they can be screened; the\n// block DECISION is delegated to a caller-supplied `shouldBlock` predicate (which the runtime\n// builds from egress-phase rules — see defaults.js). No policy is hardcoded here.\n// WinterCG-first: always wraps the global `fetch` (Node 18+, Workers, Bun, Deno). On Node it\n// ALSO patches `node:http`/`node:https` so outbound calls made via those modules (axios, got,\n// the raw http client, …) — which never touch `fetch` — are screened too.\n\n/**\n * @param {{ shouldBlock: (url:string, host:string|null, method:string)=>boolean,\n * onBlock?: (info:{url:string,host:string|null,method:string})=>void,\n * dnsScreen?: boolean,\n * lookup?: Function }} opts\n * @returns {Promise<() => void>} uninstall (restores every patched surface)\n */\nexport async function installEgressGuard({ shouldBlock, onBlock, onSkip, dnsScreen = true, lookup, allowHosts } = {}) {\n const restores = [];\n if (typeof shouldBlock !== 'function') return () => {};\n const exempt = new Set((allowHosts ?? []).map((h) => String(h).toLowerCase()));\n\n const block = (url, host, method) => {\n if (!shouldBlock(url, host, method)) return false;\n onBlock?.({ url, host, method });\n return true;\n };\n\n // DNS resolution, shared by the fetch wrapper and the node http path. Instead of trusting the\n // hostname, resolve it and check the resolved address(es). On the node path we also PIN the socket\n // to the vetted IP (no time-of-check/use gap); on fetch we can't pin without a custom undici\n // dispatcher, so we screen the resolution but a re-resolve at connect is a residual window.\n // Needs node:dns + node:net; absent on edge runtimes, where the hostname rules still apply.\n let screen = null;\n if (dnsScreen) {\n try {\n const resolveLookup = lookup ?? (await import('node:dns')).lookup;\n const { isIP } = await import('node:net');\n if (typeof resolveLookup === 'function' && typeof isIP === 'function') {\n screen = { lookup: resolveLookup, isIP, isExempt: (h) => exempt.has(String(h).toLowerCase()) };\n }\n } catch {\n screen = null; // no node:dns/net here — skip, hostname rules still apply\n }\n }\n\n // A resolver failure means the destination was NOT screened by IP — the hostname check alone let it\n // through. That's a real (if rare) coverage hole, so report it via onSkip instead of failing open\n // silently. Still fail-open: a broken resolver must not take the app's outbound traffic down.\n const skip = (reason, detail) => { try { onSkip?.({ phase: 'egress', reason, detail }); } catch { /* never affect traffic */ } };\n\n // True when a hostname resolves to a disallowed address. Fail-open: any resolver error → false.\n const resolvesToDisallowed = (url, host, method) =>\n new Promise((resolve) => {\n if (!screen) {\n // No node:dns/net here (edge runtime) or screening disabled — hostname rules only.\n if (host && dnsScreen) skip('resolver-unavailable', { host });\n return resolve(false);\n }\n if (!host || screen.isIP(host) !== 0 || screen.isExempt(host)) return resolve(false);\n try {\n screen.lookup(host, { all: true }, (err, addresses) => {\n if (err || !Array.isArray(addresses)) { skip('resolver-failed', { host }); return resolve(false); }\n for (const a of addresses) {\n const ip = a && typeof a === 'object' ? a.address : a;\n if (ip && block(url, ip, method)) return resolve(true);\n }\n resolve(false);\n });\n } catch {\n skip('resolver-failed', { host });\n resolve(false);\n }\n });\n\n // 1. global fetch — synchronous install, so it's active the instant this returns (no startup race).\n const originalFetch = globalThis.fetch;\n if (typeof originalFetch === 'function' && !originalFetch.__patchstackGuarded) {\n const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\n const MAX_REDIRECTS = 20;\n\n // Screen one outbound URL: hostname/allowlist/literal-IP check, then a DNS-resolution check for\n // real hostnames. Throws if the destination is disallowed.\n const screenUrl = async (u, method) => {\n let host = null;\n try {\n host = new URL(u).hostname;\n } catch {\n host = null;\n }\n if (block(u, host, method) || (await resolvesToDisallowed(u, host, method))) {\n throw new Error(`Patchstack blocked an outbound request to a disallowed address: ${host ?? u}`);\n }\n };\n\n const guarded = async (input, init) => {\n let cur;\n try {\n cur = new Request(input, { ...(init || {}), redirect: 'manual' });\n } catch {\n return originalFetch(input, init); // odd input we can't normalize — fail open, don't break the caller\n }\n const callerRedirect = (init && init.redirect) || (input && input.redirect) || 'follow';\n\n let url = cur.url;\n let method = cur.method;\n await screenUrl(url, method);\n\n // Caller manages redirects itself (manual/error) → screen once, hand back the raw response.\n if (callerRedirect !== 'follow') return originalFetch(input, init);\n\n // Otherwise follow redirects ourselves so EVERY hop is screened. Native `follow` re-resolves\n // internally and would let a 3xx to an internal address slip past the initial check — SSRF via\n // an open redirect. Buffer the body once (a stream can't be re-read) so 307/308 can replay it.\n const headers = new Headers(cur.headers);\n const signal = cur.signal;\n let body = method === 'GET' || method === 'HEAD' ? undefined : await cur.clone().arrayBuffer();\n\n for (let hop = 0; ; hop++) {\n const resp = await originalFetch(\n hop === 0 ? cur : new Request(url, { method, headers, body, redirect: 'manual', signal }),\n );\n const location = REDIRECT_STATUSES.has(resp.status) ? resp.headers.get('location') : null;\n if (!location) return resp;\n if (hop >= MAX_REDIRECTS) throw new Error('Patchstack blocked an outbound request: too many redirects');\n\n const next = new URL(location, url).href;\n // Fetch redirect semantics: 303, and 301/302 on a POST, become a bodyless GET.\n if (resp.status === 303 || ((resp.status === 301 || resp.status === 302) && method === 'POST')) {\n method = 'GET';\n body = undefined;\n headers.delete('content-type');\n headers.delete('content-length');\n }\n // Drop credentials on a cross-origin hop, mirroring the browser.\n if (new URL(next).origin !== new URL(url).origin) {\n headers.delete('authorization');\n headers.delete('cookie');\n }\n await screenUrl(next, method);\n url = next;\n }\n };\n guarded.__patchstackGuarded = true;\n globalThis.fetch = guarded;\n restores.push(() => {\n if (globalThis.fetch === guarded) globalThis.fetch = originalFetch;\n });\n }\n\n // node:http / node:https — best-effort; absent on Workers/Deno-without-node (import throws).\n for (const moduleName of ['node:http', 'node:https']) {\n try {\n const mod = await import(moduleName);\n const restore = patchHttpModule(mod.default ?? mod, block, screen);\n if (restore) restores.push(restore);\n } catch {\n /* module not available on this runtime — skip */\n }\n }\n\n // WebSocket egress is intentionally NOT screened. The WebSocket constructor is synchronous, so\n // the only check possible inline is a textual hostname match — which can't offer the\n // DNS-resolution guarantee the fetch and node:http/https paths give (a name that resolves to an\n // internal address would pass). A connection-pinning dispatcher could close that, but the\n // server-side, attacker-controlled-WebSocket sink is rare, and a partial hostname-only check\n // over-promises the control. Outbound SSRF screening covers fetch + node:http/https.\n\n return () => {\n for (const restore of restores) {\n try {\n restore();\n } catch {\n /* ignore */\n }\n }\n };\n}\n\n// Wrap http(s).request/get so a blocked destination throws before the socket opens.\nfunction patchHttpModule(http, block, screen) {\n if (!http || typeof http.request !== 'function' || http.__patchstackGuarded) return null;\n const originalRequest = http.request;\n const originalGet = http.get;\n\n const wrap = (original) =>\n function (...args) {\n const target = extractHttpTarget(args);\n if (target && block(target.url, target.host, target.method)) {\n throw new Error(`Patchstack blocked an outbound request to a disallowed address: ${target.host ?? target.url}`);\n }\n // DNS screen: only for real hostnames (a literal IP was already covered by the check above),\n // and skip an explicitly allowlisted host (the operator trusts it — don't second-guess its DNS).\n if (target && screen && target.host && screen.isIP(target.host) === 0 && !screen.isExempt(target.host)) {\n try {\n args = withScreeningLookup(args, target, block, screen.lookup);\n } catch {\n /* injection failed — proceed unscreened (fail-open) */\n }\n }\n return original.apply(this, args);\n };\n\n const guardedRequest = wrap(originalRequest);\n http.request = guardedRequest;\n let guardedGet;\n if (typeof originalGet === 'function') {\n guardedGet = wrap(originalGet);\n http.get = guardedGet;\n }\n http.__patchstackGuarded = true;\n\n return () => {\n // Only restore if our wrapper is still installed — don't clobber a wrapper another library\n // (an APM agent, etc.) layered on top of us after install.\n if (http.request === guardedRequest) http.request = originalRequest;\n if (guardedGet && http.get === guardedGet) http.get = originalGet;\n delete http.__patchstackGuarded;\n };\n}\n\n// http.request accepts (url), (url, options), or (options) — with an optional trailing callback.\nfunction extractHttpTarget(args) {\n const first = args[0];\n try {\n if (typeof first === 'string' || first instanceof URL) {\n const url = new URL(String(first));\n const opts = args.find((a) => a && typeof a === 'object' && !(a instanceof URL));\n return { url: url.href, host: url.hostname, method: (opts && opts.method) || 'GET' };\n }\n if (first && typeof first === 'object') {\n // Node defaults an absent host to localhost — reflect that so the block/screen see a real target.\n const host = normalizeHost(first.hostname || first.host) || 'localhost';\n const protocol = first.protocol || 'http:';\n const port = first.port ? `:${first.port}` : '';\n const path = first.path || '/';\n return { url: `${protocol}//${host}${port}${path}`, host, method: first.method || 'GET' };\n }\n } catch {\n /* fall through */\n }\n return null;\n}\n\n// Extract the bare host from a node http(s) options `host`/`hostname`, WITHOUT mangling IPv6.\n// `[::1]:8080` → `::1`, bare `::1`/`fe80::1` → unchanged, `example.com:443` → `example.com`.\n// (isInternalHost strips brackets and matches ::1 / fe80: / fc / fd, so this must not corrupt them.)\nfunction normalizeHost(raw) {\n const host = String(raw || '').trim();\n const bracketed = /^\\[([^\\]]+)\\]/.exec(host);\n if (bracketed) return bracketed[1];\n if ((host.match(/:/g) || []).length > 1) return host; // bare IPv6 — no host:port to split\n return host.split(':')[0];\n}\n\n// Given the addresses a hostname resolved to, return the first one the policy blocks (else null).\n// Reuses the same `block` predicate as the hostname check, so egress rules + allowlist apply to\n// the resolved IP too. Exported for tests.\nexport function screenResolved(addresses, target, block) {\n for (const a of addresses || []) {\n const ip = a && typeof a === 'object' ? a.address : a;\n if (ip && block(target.url, ip, target.method)) return ip;\n }\n return null;\n}\n\n// Build a DNS `lookup` that screens every resolved address before the socket connects, then hands\n// back the vetted addresses (pinning the connection to what we checked). A blocked address errors\n// the connection; a resolver error or our own failure falls through to normal resolution (fail-open).\nfunction withScreeningLookup(args, target, block, lookup) {\n const screeningLookup = (hostname, options, callback) => {\n let opts = options;\n let cb = callback;\n if (typeof opts === 'function') {\n cb = opts;\n opts = {};\n }\n if (!opts || typeof opts !== 'object') opts = {};\n try {\n lookup(hostname, { ...opts, all: true }, (err, addresses) => {\n if (err) return cb(err);\n const list = Array.isArray(addresses) ? addresses : [];\n const blocked = screenResolved(list, target, block);\n if (blocked) {\n return cb(new Error(`Patchstack blocked an outbound request to a disallowed address: ${target.host} resolved to ${blocked}`));\n }\n if (opts.all) return cb(null, list);\n const first = list[0];\n if (!first) return cb(new Error(`Patchstack: could not resolve ${hostname}`));\n return cb(null, first.address, first.family);\n });\n } catch {\n // Our screening threw — fall back to a plain resolution so we never break a request ourselves.\n try {\n lookup(hostname, opts, cb);\n } catch {\n cb(new Error(`Patchstack: lookup failed for ${hostname}`));\n }\n }\n };\n return injectLookupOption(args, screeningLookup);\n}\n\n// Return a new args array for http(s).request with our `lookup` set on the options object (cloned,\n// never mutating the caller's object), inserting an options object when the call didn't pass one.\nfunction injectLookupOption(args, lookup) {\n const first = args[0];\n if (first && typeof first === 'object' && !(first instanceof URL)) {\n return [{ ...first, lookup }, ...args.slice(1)];\n }\n const rest = args.slice(1);\n const optIdx = rest.findIndex((a) => a && typeof a === 'object' && !(a instanceof URL));\n if (optIdx !== -1) {\n const next = [...rest];\n next[optIdx] = { ...rest[optIdx], lookup };\n return [first, ...next];\n }\n const cbIdx = rest.findIndex((a) => typeof a === 'function');\n if (cbIdx === -1) return [first, { lookup }, ...rest];\n return [first, ...rest.slice(0, cbIdx), { lookup }, ...rest.slice(cbIdx)];\n}\n","// Default response- and egress-phase rule bundles, as JS constants (not JSON files) so\n// tsup bundles them into dist/protect.js without a copy step. These are just rules —\n// override per app via createProtection({ responseRules, egressRules }) or extend by\n// adding phase-tagged rules to the delivered bundle. Patterns are high-precision\n// (low false positive): structural markers a real secret has and normal content doesn't.\n//\n// Each rule carries a `prefilter`: cheap literal anchor(s) that MUST appear in the body for the\n// regex to have any chance of matching. The response screener runs the (expensive) regex only when\n// an anchor is present (case-insensitive), so a body with no candidate — the common case — skips\n// the scan entirely. This cuts CPU/latency and shrinks the regex/ReDoS surface. (Honored by the\n// response phase once the prefilter mechanism lands; a no-op before that.)\n\n// Response phase — secret / info exposure. Default action `redact` masks only the\n// offending span and still serves the page (a legit response that leaks one key gets\n// that key masked, not withheld). Use `action: \"block\"` to withhold the whole response.\nexport const DEFAULT_RESPONSE_RULES = [\n {\n id: 'resp-private-key',\n title: 'Private key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['PRIVATE KEY'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/' } }]\n },\n {\n id: 'resp-aws-access-key',\n title: 'AWS access key id in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['AKIA', 'ASIA'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\\\b/' } }]\n },\n {\n id: 'resp-gcp-api-key',\n title: 'Google API key in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['AIza'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\bAIza[0-9A-Za-z_-]{35}\\\\b/' } }]\n },\n {\n id: 'resp-vendor-api-key',\n title: 'Vendor API key / token in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n // High-signal, prefix-anchored provider tokens that never legitimately appear in a response\n // body: Stripe (sk_live_/rk_live_), GitHub (gh[opsu]_ / github_pat_), GitLab (glpat-),\n // Slack (xox[baprs]-), Anthropic (sk-ant-), Google OAuth (ya29.), npm (npm_). Trailing\n // (?![0-9A-Za-z]) instead of \\\\b since some tokens end in - / _ .\n prefilter: ['sk_live_', 'rk_live_', 'ghp_', 'gho_', 'ghs_', 'ghu_', 'github_pat_', 'glpat-', 'xox', 'sk-ant-', 'ya29.', 'npm_'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:sk_live_[0-9A-Za-z]{16,}|rk_live_[0-9A-Za-z]{16,}|gh[opsu]_[0-9A-Za-z]{36}|github_pat_[0-9A-Za-z_]{60,}|glpat-[0-9A-Za-z_-]{20,}|xox[baprs]-[0-9A-Za-z-]{10,}|sk-ant-[0-9A-Za-z_-]{20,}|ya29\\\\.[0-9A-Za-z_-]{20,}|npm_[0-9A-Za-z]{36})(?![0-9A-Za-z])/' } }]\n },\n {\n id: 'resp-jwt',\n title: 'JWT in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['eyJ'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\beyJ[A-Za-z0-9_-]{8,}\\\\.eyJ[A-Za-z0-9_-]{8,}\\\\.[A-Za-z0-9_-]{8,}\\\\b/' } }]\n },\n {\n id: 'resp-db-connection-string',\n title: 'Database connection string with credentials in response body',\n phase: 'response',\n category: 'secret-exposure',\n action: 'redact',\n prefilter: ['mongodb', 'postgres', 'mysql', 'redis', 'amqp'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\b(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqps?):\\\\/\\\\/[^\\\\s:@\\\\/]+:[^\\\\s:@\\\\/]+@/i' } }]\n },\n {\n id: 'resp-stack-trace',\n title: 'Node stack trace leaking in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n // No prefilter: a Node stack frame has no single distinctive literal (` at ` is too common to\n // gate on). The pattern is linearly bounded per line, so it runs on every screened body.\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/\\\\n\\\\s+at\\\\s+.+\\\\(.+:\\\\d+:\\\\d+\\\\)/' } }]\n },\n {\n id: 'resp-sql-error',\n title: 'SQL / ORM error disclosure in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n prefilter: ['SQLSTATE', 'Sequelize', 'ER_', 'ORA-', 'PG::', 'SQLITE_ERROR', 'SQL syntax'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/(SQLSTATE\\\\[[0-9A-Z]+\\\\]|SequelizeDatabaseError|ER_[A-Z_]+|ORA-\\\\d{5}|PG::[A-Za-z]+Error|SQLITE_ERROR|You have an error in your SQL syntax)/i' } }]\n },\n {\n id: 'resp-exception-trace',\n title: 'Backend exception / stack trace disclosure in response body',\n phase: 'response',\n category: 'info-exposure',\n action: 'redact',\n // Multi-language exception/traceback signatures a normal API response never carries:\n // Python traceback, Java \"Exception in thread\", .NET System.*Exception, JVM stack frames,\n // Go goroutine dumps. (Node `at fn (file:line:col)` frames are handled by resp-stack-trace.)\n prefilter: ['Traceback', 'Exception in thread', 'System.', 'goroutine ', '.java:', '.kt:', '.scala:', '.rb:', '.py:', '.cs:'],\n rule_v2: [{ parameter: 'response.body', match: { type: 'regex', value: '/(Traceback \\\\(most recent call last\\\\)|Exception in thread \"|System\\\\.[A-Za-z.]+Exception|\\\\bat [\\\\w.$]+\\\\([\\\\w]+\\\\.(?:java|kt|scala|rb|py|cs):\\\\d+\\\\)|goroutine \\\\d+ \\\\[)/' } }]\n }\n];\n\n// Egress phase — SSRF: block the app's outbound calls to internal / metadata addresses.\nexport const DEFAULT_EGRESS_RULES = [\n {\n id: 'egress-internal-address',\n title: 'Outbound request to an internal / metadata address (SSRF)',\n phase: 'egress',\n category: 'ssrf',\n rule_v2: [{ parameter: 'egress.host', match: { type: 'internal_host' } }]\n }\n];\n","// Full-page \"Access Denied\" block view, ported from the Patchstack WordPress plugin's\n// sample.php. Served (instead of JSON) when a BLOCKED request is a top-level document navigation\n// rather than an XHR/fetch — so a user who browses into a blocked URL sees a branded page, while\n// programmatic clients still get JSON. Self-contained (inline CSS + base64 SVGs); exposes no WAF\n// narrative and no rule details.\n\nfunction escapeHtml(s) {\n return String(s).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n}\n\n/** @param {{ url?: string, code?: string }} opts @returns {string} a complete HTML document */\nexport function renderBlockPage({ url = '', code = 'patchstack' } = {}) {\n const safeUrl = escapeHtml(url);\n const safeCode = escapeHtml(code || 'patchstack');\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style type=\"text/css\">\n\t\t.access-denied,.site-url-text{letter-spacing:.01em;color:#fff}body{background:#111216}.main-container{display:flex;justify-content:center;flex-direction:column;align-items:center}.container{width:688px;height:456px;background:#1c1e22;border-radius:16px;margin-top:120px}.content{height:330px;margin:64px}.access-denied{font-family:Verdana;font-style:normal;font-weight:600;font-size:24px;line-height:32px;display:flex;align-items:center}.descp-text,.site-url{display:flex;align-items:center}.site-url{margin-top:32px;width:560px;height:64px;background:#26282c;border-radius:4px}.reason-code-text,.site-url-text{font-family:Verdana;font-style:normal;border:none;background:#26282c}.site-url-text{text-overflow: ellipsis;font-weight:400;font-size:16px;line-height:24px;margin-left:16px;width:85%}.copy-icon{width:19px;height:22px;margin-left:20px}.copy-icon:hover{cursor:pointer}.reason-code-text{font-weight:600;font-size:24px;line-height:32px;letter-spacing:.01em;color:#AFE614;margin-left:16px;width:85%}.descp-text,.return-text{font-size:16px;line-height:24px;letter-spacing:.01em;font-weight:400}.return-text:hover{color: #AFE614}.descp-text,.logoText,.return-text,.secured-text{font-family:Verdana;font-style:normal;color:#fff}.descp{margin-top:32px;width:560px}.bottom-second-containers,.bottom-text-containers{align-items:center;display:flex}.bottom-container{margin-top:40px;display:flex;width:688px}.bottom-second-containers{margin-left:auto}.arrow-vector{margin-left:20px}.return-text{text-align:center;margin-left:12.59px;text-decoration:none}.secured-text{font-weight:400;font-size:11.2px;line-height:16px;display:flex;align-items:center;letter-spacing:.02em;margin-right:8px}.logoText{font-weight:600;font-size:16px;line-height:24px;letter-spacing:.01em;flex:none;order:1;flex-grow:0}.patch-logo{width:120px}.mobile-bottom-container{margin-top:40px;width:372px;display:none}.mobile-bottom-second-containers{margin-top:64px;display:flex;margin-left:16.5px}@media only screen and (max-width:900px){.container{background:#111216;width:370px;height:auto;margin-top:0}.content{margin-left:16.5px}.descp,.site-url{width:340px}.site-url-text{font-weight:400;font-size:14.4px;line-height:20px;letter-spacing:.02em;width:80%}.copy-icon{margin-left:20px}.reason-code-text{font-size:16px;line-height:32px;letter-spacing:.02em;margin-left:16px;width:80%}.descp-text,.return-text{font-size:14.4px;line-height:20px}.descp-text{display:flex;align-items:center;letter-spacing:.02em}.bottom-container{display:none}.mobile-bottom-container{display:block}.return-text{text-align:center;letter-spacing:.01em;color:#fff;margin-left:9.73px}}\n </style>\n <title>Access Denied</title>\n</head>\n<body>\n <div class=\"main-container\">\n <div class=\"container\">\n <div class=\"content\">\n <span class=\"access-denied\">Access Denied</span>\n <div class=\"site-url\">\n <input id=\"url\" class=\"site-url-text\" type=\"text\"\n value=\"${safeUrl}\" readonly>\n <img class=\"copy-icon\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=\" alt=\"Copy Text\" onmouseover=\"hover(this);\" onmouseout=\"unhover(this);\" onclick=\"copyText(id='url')\">\n </div>\n <div class=\"site-url\">\n <input id=\"response\" class=\"reason-code-text\" type=\"text\" value=\"${safeCode}\" readonly>\n <img class=\"copy-icon\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=\" alt=\"Copy Text\" onmouseover=\"hover(this);\" onmouseout=\"unhover(this);\" onclick=\"copyText(id='response')\">\n </div>\n <div class=\"descp\">\n <span class=\"descp-text\">This request has been blocked by Patchstack. If this message persists and you are a legitimate user, contact the site administrator with the reference above.</span>\n </div>\n </div>\n </div>\n <div class=\"bottom-container\">\n <div class=\"bottom-text-containers\">\n <img class=\"arrow-vector\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSIxMyIgdmlld0JveD0iMCAwIDggMTMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03LjQxIDEuNDMzNDRMNiAwLjAyMzQzNzVMMCA2LjAyMzQ0TDYgMTIuMDIzNEw3LjQxIDEwLjYxMzRMMi44MyA2LjAyMzQ0TDcuNDEgMS40MzM0NFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==\" alt=\"Arrow Left\">\n <a href=\"/\" class=\"return-text\">Return to homepage</a>\n </div>\n <div class=\"bottom-second-containers\">\n <span class=\"secured-text\">This website is secured by</span>\n <a href=\"https://patchstack.com\" class=\"logoText\">\n <img class=\"patch-logo\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg0IiBoZWlnaHQ9IjIyIiB2aWV3Qm94PSIwIDAgMTg0IDIyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUwLjQwMDEgMTEuNTAwOEM1MC40MDAxIDkuMTAwNzggNDkuMzAwMSA4LjEwMDc4IDQ2LjYwMDEgOC4xMDA3OEM0NC4wMDAxIDguMTAwNzggNDIuNzAwMSA5LjIwMDc4IDQyLjcwMDEgMTEuNTAwOEM0Mi43MDAxIDEzLjgwMDggNDQuMDAwMSAxNC45MDA4IDQ2LjYwMDEgMTQuOTAwOEM0OS4zMDAxIDE0LjkwMDggNTAuNDAwMSAxMy44MDA4IDUwLjQwMDEgMTEuNTAwOFpNNTMuNDAwMSAxMS41MDA4QzUzLjQwMDEgMTUuMzAwOCA1MS4zMDAxIDE3LjYwMDggNDcuNDAwMSAxNy42MDA4QzQ1LjQwMDEgMTcuNjAwOCA0My42MDAxIDE2LjUwMDggNDIuNzAwMSAxNC41MDA4VjIxLjUwMDhIMzkuNjAwMVY1LjYwMDc4SDQyLjcwMDFWOC41MDA3OEM0My42MDAxIDYuNTAwNzggNDUuNDAwMSA1LjMwMDc4IDQ3LjUwMDEgNS4zMDA3OEM1MS40MDAxIDUuMzAwNzggNTMuNDAwMSA3LjcwMDc4IDUzLjQwMDEgMTEuNTAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjYuNDAwMSAxMS40OTg0QzY2LjQwMDEgOS4xOTg0NCA2NS4xMDAxIDguMDk4NDQgNjIuNTAwMSA4LjA5ODQ0QzU5LjgwMDEgOC4wOTg0NCA1OC43MDAxIDkuMDk4NDQgNTguNzAwMSAxMS40OTg0QzU4LjcwMDEgMTMuNzk4NCA1OS44MDAxIDE0Ljg5ODQgNjIuNTAwMSAxNC44OTg0QzY1LjEwMDEgMTQuODk4NCA2Ni40MDAxIDEzLjc5ODQgNjYuNDAwMSAxMS40OTg0Wk02Ni4zMDAxIDUuNTk4NDRINjkuNDAwMVYxNy4zOTg0SDY2LjMwMDFWMTQuNDk4NEM2NS40MDAxIDE2LjQ5ODQgNjMuNzAwMSAxNy42OTg0IDYxLjYwMDEgMTcuNjk4NEM1Ny43MDAxIDE3LjY5ODQgNTUuNjAwMSAxNS4yOTg0IDU1LjYwMDEgMTEuNTk4NEM1NS42MDAxIDcuNzk4NDQgNTcuNjAwMSA1LjM5ODQ0IDYxLjQwMDEgNS4zOTg0NEM2My41MDAxIDUuMzk4NDQgNjUuNDAwMSA2LjU5ODQ0IDY2LjIwMDEgOC41OTg0NFY1LjU5ODQ0SDY2LjMwMDFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTgxLjM5OTkgOC4zSDc3LjM5OTlWMTQuN0g4MS4wOTk5VjE3LjRINzcuOTk5OUM3NS43OTk5IDE3LjQgNzQuMjk5OSAxNS45IDc0LjI5OTkgMTMuN1Y4LjNINzEuMzk5OVY1LjZINzQuMjk5OVYySDc3LjM5OTlWNS41SDgxLjM5OTlWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik05My42OTk5IDEyLjIwMDhMOTYuNjk5OSAxMi45MDA4Qzk1Ljk5OTkgMTYuMDAwOCA5My42OTk5IDE3LjYwMDggODkuOTk5OSAxNy42MDA4Qzg3Ljc5OTkgMTcuNjAwOCA4Ni4wOTk5IDE3LjAwMDggODQuNzk5OSAxNS45MDA4QzgzLjQ5OTkgMTQuODAwOCA4Mi44OTk5IDEzLjMwMDggODIuODk5OSAxMS41MDA4QzgyLjg5OTkgOS42MDA3OCA4My40OTk5IDguMTAwNzggODQuNzk5OSA3LjAwMDc4Qzg2LjA5OTkgNS45MDA3OCA4Ny43OTk5IDUuMzAwNzggODkuOTk5OSA1LjMwMDc4QzkzLjY5OTkgNS4zMDA3OCA5NS45OTk5IDYuOTAwNzggOTYuNjk5OSAxMC4wMDA4TDkzLjY5OTkgMTAuNzAwOEM5My4yOTk5IDguODAwNzggOTIuMjk5OSA4LjEwMDc4IDg5Ljg5OTkgOC4xMDA3OEM4Ni45OTk5IDguMTAwNzggODUuODk5OSA5LjIwMDc4IDg1Ljg5OTkgMTEuNTAwOEM4NS44OTk5IDEzLjgwMDggODYuOTk5OSAxNC45MDA4IDg5Ljg5OTkgMTQuOTAwOEM5Mi4yOTk5IDE0LjkwMDggOTMuMjk5OSAxNC4yMDA4IDkzLjY5OTkgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTExLjggMTAuNlYxNy40SDEwOC43VjExLjJDMTA4LjcgOSAxMDcuOCA4LjEgMTA1LjkgOC4xQzEwMy42IDguMSAxMDIuNCA5LjMgMTAyLjQgMTEuN1YxNy4zSDk5LjI5OThWMC41SDEwMi40VjguM0MxMDMuMSA2LjMgMTA0LjYgNS4zIDEwNyA1LjNDMTEwLjIgNS4zIDExMS44IDcuMSAxMTEuOCAxMC42WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMTQuMSAxNC4wOTg0TDExNy4xIDEzLjI5ODRDMTE3LjQgMTQuNzk4NCAxMTguMyAxNS4yOTg0IDEyMC4yIDE1LjI5ODRDMTIxLjggMTUuMjk4NCAxMjIuNyAxNC44OTg0IDEyMi43IDE0LjA5ODRDMTIyLjcgMTMuMjk4NCAxMjIuMiAxMi45OTg0IDEyMC42IDEyLjc5ODRMMTE4LjIgMTIuNDk4NEMxMTUuOSAxMi4xOTg0IDExNC41IDEwLjg5ODQgMTE0LjUgOC45OTg0NEMxMTQuNSA2Ljc5ODQ0IDExNi41IDUuMzk4NDQgMTE5LjggNS4zOTg0NEMxMjMuMSA1LjM5ODQ0IDEyNSA2LjY5ODQ0IDEyNS41IDguODk4NDRMMTIyLjUgOS41OTg0NEMxMjIuMyA4LjA5ODQ0IDEyMS41IDcuNjk4NDQgMTE5LjkgNy42OTg0NEMxMTguNCA3LjY5ODQ0IDExNy42IDguMDk4NDQgMTE3LjYgOC44OTg0NEMxMTcuNiA5LjU5ODQ0IDExOC4xIDkuOTk4NDQgMTE5LjMgMTAuMDk4NEwxMjIuMiAxMC40OTg0QzEyNC40IDEwLjc5ODQgMTI1LjggMTEuOTk4NCAxMjUuOCAxMy44OTg0QzEyNS44IDE2LjI5ODQgMTIzLjcgMTcuNzk4NCAxMjAuMSAxNy43OTg0QzExNi43IDE3LjU5ODQgMTE0LjUgMTYuMTk4NCAxMTQuMSAxNC4wOTg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzYuNyA4LjNIMTMyLjdWMTQuN0gxMzYuNFYxNy40SDEzMy4zQzEzMS4xIDE3LjQgMTI5LjYgMTUuOSAxMjkuNiAxMy43VjguM0gxMjYuN1Y1LjZIMTI5LjZWMkgxMzIuN1Y1LjVIMTM2LjdWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNDkuMiAxMS40OTg0QzE0OS4yIDkuMTk4NDQgMTQ3LjkgOC4wOTg0NCAxNDUuMyA4LjA5ODQ0QzE0Mi42IDguMDk4NDQgMTQxLjUgOS4wOTg0NCAxNDEuNSAxMS40OTg0QzE0MS41IDEzLjc5ODQgMTQyLjYgMTQuODk4NCAxNDUuMyAxNC44OTg0QzE0Ny45IDE0Ljg5ODQgMTQ5LjIgMTMuNzk4NCAxNDkuMiAxMS40OTg0Wk0xNDkuMSA1LjU5ODQ0SDE1Mi4yVjE3LjM5ODRIMTQ5LjFWMTQuNDk4NEMxNDguMiAxNi40OTg0IDE0Ni41IDE3LjY5ODQgMTQ0LjQgMTcuNjk4NEMxNDAuNSAxNy42OTg0IDEzOC40IDE1LjI5ODQgMTM4LjQgMTEuNTk4NEMxMzguNCA3Ljc5ODQ0IDE0MC40IDUuMzk4NDQgMTQ0LjIgNS4zOTg0NEMxNDYuMyA1LjM5ODQ0IDE0OC4yIDYuNTk4NDQgMTQ5IDguNTk4NDRWNS41OTg0NEgxNDkuMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTY1LjYgMTIuMjAwOEwxNjguNiAxMi45MDA4QzE2Ny45IDE2LjAwMDggMTY1LjYgMTcuNjAwOCAxNjEuOSAxNy42MDA4QzE1OS43IDE3LjYwMDggMTU4IDE3LjAwMDggMTU2LjcgMTUuOTAwOEMxNTUuNCAxNC44MDA4IDE1NC44IDEzLjMwMDggMTU0LjggMTEuNTAwOEMxNTQuOCA5LjYwMDc4IDE1NS40IDguMTAwNzggMTU2LjcgNy4wMDA3OEMxNTggNS45MDA3OCAxNTkuNyA1LjMwMDc4IDE2MS45IDUuMzAwNzhDMTY1LjYgNS4zMDA3OCAxNjcuOSA2LjkwMDc4IDE2OC42IDEwLjAwMDhMMTY1LjYgMTAuNzAwOEMxNjUuMiA4LjgwMDc4IDE2NC4yIDguMTAwNzggMTYxLjggOC4xMDA3OEMxNTguOSA4LjEwMDc4IDE1Ny44IDkuMjAwNzggMTU3LjggMTEuNTAwOEMxNTcuOCAxMy44MDA4IDE1OC45IDE0LjkwMDggMTYxLjggMTQuOTAwOEMxNjQuMSAxNC45MDA4IDE2NS4xIDE0LjIwMDggMTY1LjYgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTgwLjI1NyAxNy40TDE3Ni42NTcgMTIuM0wxNzQuMTU3IDE0LjZWMTcuNEgxNzEuMTU3VjAuNUgxNzQuMTU3VjExLjFMMTc5Ljg1NyA1LjZIMTgzLjc1N0wxNzguNTU3IDEwLjVMMTgzLjk1NyAxNy40SDE4MC4yNTdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgMjEuNTAwOEg3LjNWMTYuMzAwOEgwVjIxLjUwMDhaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNy4yOTk4IDIxLjUwMDhIMTQuNTk5OFYxNi4zMDA4SDcuMjk5OFYyMS41MDA4WiIgZmlsbD0iIzZFQkUwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE0LjYwMDEgMTYuM0gyOS4yMDAxVjAuNUgxNC42MDAxVjE2LjNaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMCAxMUgxNC42VjAuNUgwVjExWiIgZmlsbD0iIzZFQkUwMCIvPgo8L3N2Zz4K\" alt=\"Patchstack Logo\">\n\t\t\t\t</a>\n </div>\n </div>\n \n <div class=\"mobile-bottom-container\">\n <div class=\"mobile-bottom-text-containers\">\n <img class=\"arrow-vector\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSIxMyIgdmlld0JveD0iMCAwIDggMTMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik03LjQxIDEuNDMzNDRMNiAwLjAyMzQzNzVMMCA2LjAyMzQ0TDYgMTIuMDIzNEw3LjQxIDEwLjYxMzRMMi44MyA2LjAyMzQ0TDcuNDEgMS40MzM0NFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==\" alt=\"Arrow Left\">\n <a href=\"/\" class=\"return-text\">Return to homepage</a>\n </div>\n <div class=\"mobile-bottom-second-containers\">\n <span class=\"secured-text\">This website is secured by</span>\n <a href=\"https://patchstack.com\" class=\"logoText\">\n <img class=\"patch-logo\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg0IiBoZWlnaHQ9IjIyIiB2aWV3Qm94PSIwIDAgMTg0IDIyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTUwLjQwMDEgMTEuNTAwOEM1MC40MDAxIDkuMTAwNzggNDkuMzAwMSA4LjEwMDc4IDQ2LjYwMDEgOC4xMDA3OEM0NC4wMDAxIDguMTAwNzggNDIuNzAwMSA5LjIwMDc4IDQyLjcwMDEgMTEuNTAwOEM0Mi43MDAxIDEzLjgwMDggNDQuMDAwMSAxNC45MDA4IDQ2LjYwMDEgMTQuOTAwOEM0OS4zMDAxIDE0LjkwMDggNTAuNDAwMSAxMy44MDA4IDUwLjQwMDEgMTEuNTAwOFpNNTMuNDAwMSAxMS41MDA4QzUzLjQwMDEgMTUuMzAwOCA1MS4zMDAxIDE3LjYwMDggNDcuNDAwMSAxNy42MDA4QzQ1LjQwMDEgMTcuNjAwOCA0My42MDAxIDE2LjUwMDggNDIuNzAwMSAxNC41MDA4VjIxLjUwMDhIMzkuNjAwMVY1LjYwMDc4SDQyLjcwMDFWOC41MDA3OEM0My42MDAxIDYuNTAwNzggNDUuNDAwMSA1LjMwMDc4IDQ3LjUwMDEgNS4zMDA3OEM1MS40MDAxIDUuMzAwNzggNTMuNDAwMSA3LjcwMDc4IDUzLjQwMDEgMTEuNTAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjYuNDAwMSAxMS40OTg0QzY2LjQwMDEgOS4xOTg0NCA2NS4xMDAxIDguMDk4NDQgNjIuNTAwMSA4LjA5ODQ0QzU5LjgwMDEgOC4wOTg0NCA1OC43MDAxIDkuMDk4NDQgNTguNzAwMSAxMS40OTg0QzU4LjcwMDEgMTMuNzk4NCA1OS44MDAxIDE0Ljg5ODQgNjIuNTAwMSAxNC44OTg0QzY1LjEwMDEgMTQuODk4NCA2Ni40MDAxIDEzLjc5ODQgNjYuNDAwMSAxMS40OTg0Wk02Ni4zMDAxIDUuNTk4NDRINjkuNDAwMVYxNy4zOTg0SDY2LjMwMDFWMTQuNDk4NEM2NS40MDAxIDE2LjQ5ODQgNjMuNzAwMSAxNy42OTg0IDYxLjYwMDEgMTcuNjk4NEM1Ny43MDAxIDE3LjY5ODQgNTUuNjAwMSAxNS4yOTg0IDU1LjYwMDEgMTEuNTk4NEM1NS42MDAxIDcuNzk4NDQgNTcuNjAwMSA1LjM5ODQ0IDYxLjQwMDEgNS4zOTg0NEM2My41MDAxIDUuMzk4NDQgNjUuNDAwMSA2LjU5ODQ0IDY2LjIwMDEgOC41OTg0NFY1LjU5ODQ0SDY2LjMwMDFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTgxLjM5OTkgOC4zSDc3LjM5OTlWMTQuN0g4MS4wOTk5VjE3LjRINzcuOTk5OUM3NS43OTk5IDE3LjQgNzQuMjk5OSAxNS45IDc0LjI5OTkgMTMuN1Y4LjNINzEuMzk5OVY1LjZINzQuMjk5OVYySDc3LjM5OTlWNS41SDgxLjM5OTlWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik05My42OTk5IDEyLjIwMDhMOTYuNjk5OSAxMi45MDA4Qzk1Ljk5OTkgMTYuMDAwOCA5My42OTk5IDE3LjYwMDggODkuOTk5OSAxNy42MDA4Qzg3Ljc5OTkgMTcuNjAwOCA4Ni4wOTk5IDE3LjAwMDggODQuNzk5OSAxNS45MDA4QzgzLjQ5OTkgMTQuODAwOCA4Mi44OTk5IDEzLjMwMDggODIuODk5OSAxMS41MDA4QzgyLjg5OTkgOS42MDA3OCA4My40OTk5IDguMTAwNzggODQuNzk5OSA3LjAwMDc4Qzg2LjA5OTkgNS45MDA3OCA4Ny43OTk5IDUuMzAwNzggODkuOTk5OSA1LjMwMDc4QzkzLjY5OTkgNS4zMDA3OCA5NS45OTk5IDYuOTAwNzggOTYuNjk5OSAxMC4wMDA4TDkzLjY5OTkgMTAuNzAwOEM5My4yOTk5IDguODAwNzggOTIuMjk5OSA4LjEwMDc4IDg5Ljg5OTkgOC4xMDA3OEM4Ni45OTk5IDguMTAwNzggODUuODk5OSA5LjIwMDc4IDg1Ljg5OTkgMTEuNTAwOEM4NS44OTk5IDEzLjgwMDggODYuOTk5OSAxNC45MDA4IDg5Ljg5OTkgMTQuOTAwOEM5Mi4yOTk5IDE0LjkwMDggOTMuMjk5OSAxNC4yMDA4IDkzLjY5OTkgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTExLjggMTAuNlYxNy40SDEwOC43VjExLjJDMTA4LjcgOSAxMDcuOCA4LjEgMTA1LjkgOC4xQzEwMy42IDguMSAxMDIuNCA5LjMgMTAyLjQgMTEuN1YxNy4zSDk5LjI5OThWMC41SDEwMi40VjguM0MxMDMuMSA2LjMgMTA0LjYgNS4zIDEwNyA1LjNDMTEwLjIgNS4zIDExMS44IDcuMSAxMTEuOCAxMC42WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMTQuMSAxNC4wOTg0TDExNy4xIDEzLjI5ODRDMTE3LjQgMTQuNzk4NCAxMTguMyAxNS4yOTg0IDEyMC4yIDE1LjI5ODRDMTIxLjggMTUuMjk4NCAxMjIuNyAxNC44OTg0IDEyMi43IDE0LjA5ODRDMTIyLjcgMTMuMjk4NCAxMjIuMiAxMi45OTg0IDEyMC42IDEyLjc5ODRMMTE4LjIgMTIuNDk4NEMxMTUuOSAxMi4xOTg0IDExNC41IDEwLjg5ODQgMTE0LjUgOC45OTg0NEMxMTQuNSA2Ljc5ODQ0IDExNi41IDUuMzk4NDQgMTE5LjggNS4zOTg0NEMxMjMuMSA1LjM5ODQ0IDEyNSA2LjY5ODQ0IDEyNS41IDguODk4NDRMMTIyLjUgOS41OTg0NEMxMjIuMyA4LjA5ODQ0IDEyMS41IDcuNjk4NDQgMTE5LjkgNy42OTg0NEMxMTguNCA3LjY5ODQ0IDExNy42IDguMDk4NDQgMTE3LjYgOC44OTg0NEMxMTcuNiA5LjU5ODQ0IDExOC4xIDkuOTk4NDQgMTE5LjMgMTAuMDk4NEwxMjIuMiAxMC40OTg0QzEyNC40IDEwLjc5ODQgMTI1LjggMTEuOTk4NCAxMjUuOCAxMy44OTg0QzEyNS44IDE2LjI5ODQgMTIzLjcgMTcuNzk4NCAxMjAuMSAxNy43OTg0QzExNi43IDE3LjU5ODQgMTE0LjUgMTYuMTk4NCAxMTQuMSAxNC4wOTg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzYuNyA4LjNIMTMyLjdWMTQuN0gxMzYuNFYxNy40SDEzMy4zQzEzMS4xIDE3LjQgMTI5LjYgMTUuOSAxMjkuNiAxMy43VjguM0gxMjYuN1Y1LjZIMTI5LjZWMkgxMzIuN1Y1LjVIMTM2LjdWOC4zWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNDkuMiAxMS40OTg0QzE0OS4yIDkuMTk4NDQgMTQ3LjkgOC4wOTg0NCAxNDUuMyA4LjA5ODQ0QzE0Mi42IDguMDk4NDQgMTQxLjUgOS4wOTg0NCAxNDEuNSAxMS40OTg0QzE0MS41IDEzLjc5ODQgMTQyLjYgMTQuODk4NCAxNDUuMyAxNC44OTg0QzE0Ny45IDE0Ljg5ODQgMTQ5LjIgMTMuNzk4NCAxNDkuMiAxMS40OTg0Wk0xNDkuMSA1LjU5ODQ0SDE1Mi4yVjE3LjM5ODRIMTQ5LjFWMTQuNDk4NEMxNDguMiAxNi40OTg0IDE0Ni41IDE3LjY5ODQgMTQ0LjQgMTcuNjk4NEMxNDAuNSAxNy42OTg0IDEzOC40IDE1LjI5ODQgMTM4LjQgMTEuNTk4NEMxMzguNCA3Ljc5ODQ0IDE0MC40IDUuMzk4NDQgMTQ0LjIgNS4zOTg0NEMxNDYuMyA1LjM5ODQ0IDE0OC4yIDYuNTk4NDQgMTQ5IDguNTk4NDRWNS41OTg0NEgxNDkuMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTY1LjYgMTIuMjAwOEwxNjguNiAxMi45MDA4QzE2Ny45IDE2LjAwMDggMTY1LjYgMTcuNjAwOCAxNjEuOSAxNy42MDA4QzE1OS43IDE3LjYwMDggMTU4IDE3LjAwMDggMTU2LjcgMTUuOTAwOEMxNTUuNCAxNC44MDA4IDE1NC44IDEzLjMwMDggMTU0LjggMTEuNTAwOEMxNTQuOCA5LjYwMDc4IDE1NS40IDguMTAwNzggMTU2LjcgNy4wMDA3OEMxNTggNS45MDA3OCAxNTkuNyA1LjMwMDc4IDE2MS45IDUuMzAwNzhDMTY1LjYgNS4zMDA3OCAxNjcuOSA2LjkwMDc4IDE2OC42IDEwLjAwMDhMMTY1LjYgMTAuNzAwOEMxNjUuMiA4LjgwMDc4IDE2NC4yIDguMTAwNzggMTYxLjggOC4xMDA3OEMxNTguOSA4LjEwMDc4IDE1Ny44IDkuMjAwNzggMTU3LjggMTEuNTAwOEMxNTcuOCAxMy44MDA4IDE1OC45IDE0LjkwMDggMTYxLjggMTQuOTAwOEMxNjQuMSAxNC45MDA4IDE2NS4xIDE0LjIwMDggMTY1LjYgMTIuMjAwOFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTgwLjI1NyAxNy40TDE3Ni42NTcgMTIuM0wxNzQuMTU3IDE0LjZWMTcuNEgxNzEuMTU3VjAuNUgxNzQuMTU3VjExLjFMMTc5Ljg1NyA1LjZIMTgzLjc1N0wxNzguNTU3IDEwLjVMMTgzLjk1NyAxNy40SDE4MC4yNTdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgMjEuNTAwOEg3LjNWMTYuMzAwOEgwVjIxLjUwMDhaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNy4yOTk4IDIxLjUwMDhIMTQuNTk5OFYxNi4zMDA4SDcuMjk5OFYyMS41MDA4WiIgZmlsbD0iIzZFQkUwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE0LjYwMDEgMTYuM0gyOS4yMDAxVjAuNUgxNC42MDAxVjE2LjNaIiBmaWxsPSIjQUZFNjE0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMCAxMUgxNC42VjAuNUgwVjExWiIgZmlsbD0iIzZFQkUwMCIvPgo8L3N2Zz4K\" alt=\"Patchstack Logo\">\n\t\t\t\t</a>\n </div>\n </div>\n </div>\n</body>\n<script>\n function copyText(id) {\n var copyText = document.getElementById(id)\n copyText.select();\n copyText.setSelectionRange(0, 99999);\n navigator.clipboard.writeText(copyText.value);\n }\n\n function hover(element) {\n element.setAttribute('src', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IiNBRkU2MTQiLz4KPC9zdmc+Cg==');\n }\n\n function unhover(element) {\n element.setAttribute('src', 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkiIGhlaWdodD0iMjMiIHZpZXdCb3g9IjAgMCAxOSAyMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDAuMDIzNDM3NUgwVjE2LjAyMzRIMlYyLjAyMzQ0SDE0VjAuMDIzNDM3NVpNMTkgNC4wMjM0NEg0VjIyLjAyMzRIMTlWNC4wMjM0NFpNMTcgMjAuMDIzNEg2VjYuMDIzNDRIMTdWMjAuMDIzNFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=');\n }\n</script>\n</html>`;\n}\n","// Rule cache as a TIERED store of a `{ bundle, etag }` envelope:\n// 1. memory — always present; last-known-good within the process. Survives refreshes and is\n// the fallback when the disk isn't writable (read-only FS, sandbox).\n// 2. durable — filesystem (default, via `cacheDir`) OR a pluggable adapter (`ruleCache`, e.g.\n// a KV store for filesystem-less runtimes). Survives process restarts.\n// read: memory → durable → null. write: memory + best-effort durable. Everything is fail-open —\n// a read/write error yields \"no cache\" rather than throwing.\n//\n// Node's fs/path are loaded LAZILY (dynamic import), never as a static top-level import: this module\n// is part of the WinterCG/edge-safe graph (Next edge middleware, Workers, Deno, Supabase Functions),\n// where a static `node:fs` import fails to resolve at build/load time and would take the whole guard\n// down. On those runtimes the disk tier simply reports \"no cache\" and the memory tier (or a pluggable\n// `ruleCache` adapter) carries last-known-good.\n\nlet fsMod; // memoized { readFileSync, writeFileSync, mkdirSync, join } | null (unavailable)\nasync function loadFs() {\n if (fsMod !== undefined) return fsMod;\n try {\n const [fs, path] = await Promise.all([import('node:fs'), import('node:path')]);\n fsMod = {\n readFileSync: fs.readFileSync,\n writeFileSync: fs.writeFileSync,\n mkdirSync: fs.mkdirSync,\n join: path.join,\n };\n } catch {\n fsMod = null; // no filesystem here (edge runtime) — memory/adapter tiers still work\n }\n return fsMod;\n}\n\nexport function makeStore(options = {}) {\n let mem = null;\n const durable = durableTier(options);\n return {\n async read() {\n if (mem) return mem;\n const env = await durable.read();\n if (env) mem = env;\n return env;\n },\n async write(env) {\n if (env) mem = env;\n await durable.write(env);\n },\n };\n}\n\n// The durable tier: a caller-supplied adapter wins; otherwise a disk cache under `cacheDir`; if\n// neither is configured, a no-op (memory-only — fine for a long-lived process).\nfunction durableTier(options) {\n const adapter = options.ruleCache;\n if (adapter && typeof adapter.read === 'function' && typeof adapter.write === 'function') {\n return {\n read: async () => {\n try {\n return toEnvelope(await adapter.read());\n } catch {\n return null;\n }\n },\n write: async (env) => {\n try {\n await adapter.write(env);\n } catch {\n /* best-effort */\n }\n },\n };\n }\n const dir = options.cacheDir;\n return {\n read: async () => cacheRead(dir),\n write: async (env) => cacheWrite(dir, env),\n };\n}\n\nasync function cacheWrite(dir, env) {\n if (!dir) return;\n const fs = await loadFs();\n if (!fs) return; // filesystem-less runtime — memory tier only\n try {\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(fs.join(dir, 'patchstack-rules.json'), JSON.stringify(env));\n } catch {\n /* cache is best-effort — the memory tier still holds last-known-good */\n }\n}\n\nasync function cacheRead(dir) {\n if (!dir) return null;\n const fs = await loadFs();\n if (!fs) return null;\n try {\n return toEnvelope(JSON.parse(fs.readFileSync(fs.join(dir, 'patchstack-rules.json'), 'utf8')));\n } catch {\n return null;\n }\n}\n\n// Accept the current { bundle, etag } envelope and a legacy bare bundle (pre-envelope cache files).\nexport function toEnvelope(value) {\n if (!value || typeof value !== 'object') return null;\n if (value.bundle && typeof value.bundle === 'object') {\n return { bundle: value.bundle, etag: value.etag ?? null };\n }\n if (Array.isArray(value.firewall) || Array.isArray(value.whitelists)) {\n return { bundle: value, etag: null }; // legacy bare-bundle cache file\n }\n return null;\n}\n","// Rule-bundle validation. Delivered rules are POLICY fetched over the network, and the engine executes\n// them on every request — so an upstream compromise, a schema drift, or a mistake in the corpus could\n// otherwise hand the app unbounded work (a 50k-rule bundle, a 500-deep condition tree, a pathological\n// regex) or silently unenforceable junk.\n//\n// Two principles:\n// 1. REJECT, don't silently skip. A rule that fails validation is dropped WITH a reported reason, so\n// an unenforceable rule is visible instead of quietly protecting nothing.\n// 2. Bound everything the engine will walk: rule count, conditions per rule, nesting depth, and\n// pattern length. Caps are deliberately far above any real corpus rule.\n//\n// Fail-open in spirit: validation never throws, and a bundle whose rules are all rejected simply means\n// \"no rules\" (the app keeps serving) — never a crash.\n\nexport const LIMITS = {\n maxRules: 5000,\n maxWhitelists: 2000,\n maxConditionsPerRule: 250,\n maxNestingDepth: 12,\n maxRegexLength: 1000,\n maxValueLength: 8192,\n};\n\nconst PHASES = new Set(['request', 'response', 'egress']);\nconst ACTIONS = new Set(['block', 'redact', 'encode', 'set-header', 'remove-header', 'harden-cookie']);\n\n/**\n * Validate a delivered bundle. Returns `{ bundle, rejected }` where `bundle` contains only rules that\n * passed and `rejected` is `[{ id, reason }]` for everything dropped.\n * @param {object} bundle\n * @returns {{ bundle: object, rejected: Array<{id: string, reason: string}> }}\n */\nexport function validateBundle(bundle, opts = {}) {\n const rejected = [];\n const inFirewall = Array.isArray(bundle?.firewall) ? bundle.firewall : [];\n const inWhitelists = Array.isArray(bundle?.whitelists) ? bundle.whitelists : [];\n\n const firewall = [];\n for (const rule of inFirewall) {\n if (firewall.length >= LIMITS.maxRules) {\n rejected.push({ id: idOf(rule), reason: `bundle exceeds maxRules (${LIMITS.maxRules})` });\n continue;\n }\n const reason = ruleProblem(rule);\n if (reason) rejected.push({ id: idOf(rule), reason });\n else firewall.push(rule);\n }\n\n const whitelists = [];\n for (const wl of inWhitelists) {\n if (whitelists.length >= LIMITS.maxWhitelists) {\n rejected.push({ id: idOf(wl), reason: `bundle exceeds maxWhitelists (${LIMITS.maxWhitelists})` });\n continue;\n }\n // A whitelist SUPPRESSES rules, so a malformed one is a protection risk, not a detection risk.\n // One with no `rule_id` applies to EVERY rule — a single tripped condition disables the whole\n // firewall for that request — so it must be opted into explicitly.\n if (!opts.allowGlobalWhitelists && wl && Array.isArray(wl.rule_v2) && !wl.rule_id) {\n rejected.push({ id: idOf(wl), reason: 'whitelist has no rule_id (would suppress every rule); set allowGlobalWhitelists to permit' });\n continue;\n }\n const reason = conditionsProblem(wl?.rule_v2);\n if (reason) rejected.push({ id: idOf(wl), reason: `whitelist: ${reason}` });\n else whitelists.push(wl);\n }\n\n return {\n bundle: { ...bundle, firewall, whitelists },\n rejected,\n };\n}\n\nfunction idOf(rule) {\n const id = rule?.id ?? rule?.rule_id;\n return id === undefined || id === null ? '(unidentified)' : String(id);\n}\n\n/** @returns {string|null} a reason the rule must be dropped, or null when it's acceptable. */\nfunction ruleProblem(rule) {\n if (!rule || typeof rule !== 'object') return 'not an object';\n if (rule.phase !== undefined && !PHASES.has(rule.phase)) return `unknown phase \"${rule.phase}\"`;\n if (rule.action !== undefined && !ACTIONS.has(rule.action)) return `unknown action \"${rule.action}\"`;\n const capOverride = rule.max_bytes;\n if (capOverride !== undefined && !(Number(capOverride) > 0)) return 'max_bytes must be a positive number';\n return conditionsProblem(rule.rule_v2);\n}\n\nfunction conditionsProblem(conditions, depth = 0) {\n if (!Array.isArray(conditions)) return 'rule_v2 must be an array of conditions';\n if (conditions.length === 0) return 'rule_v2 is empty (would never match)';\n if (depth > LIMITS.maxNestingDepth) return `nesting deeper than ${LIMITS.maxNestingDepth}`;\n if (conditions.length > LIMITS.maxConditionsPerRule) {\n return `more than ${LIMITS.maxConditionsPerRule} conditions`;\n }\n for (const c of conditions) {\n if (!c || typeof c !== 'object') return 'condition is not an object';\n if (Array.isArray(c.rules)) {\n const nested = conditionsProblem(c.rules, depth + 1);\n if (nested) return nested;\n continue; // a group carries no match of its own\n }\n const m = c.match;\n if (!m || typeof m !== 'object') return 'condition has no match object';\n if (typeof m.type !== 'string' || m.type === '') return 'match.type must be a non-empty string';\n if (m.type === 'regex') {\n if (typeof m.value !== 'string') return 'regex match.value must be a string';\n if (m.value.length > LIMITS.maxRegexLength) return `regex longer than ${LIMITS.maxRegexLength} chars`;\n } else if (typeof m.value === 'string' && m.value.length > LIMITS.maxValueLength) {\n return `match.value longer than ${LIMITS.maxValueLength} chars`;\n }\n if (m.match) {\n // array_key_value nests a sub-match; count it toward depth so a chain can't be unbounded.\n const nested = conditionsProblem([{ match: m.match }], depth + 1);\n if (nested) return nested;\n }\n }\n return null;\n}\n","// Rule source: pick where the ruleset comes from — an explicit bundle, or the live API by site\n// UUID (Pulse) or token — fetch it (conditional/If-None-Match via the persisted etag), and fall\n// back through last-known-good → bundled → empty. Fail-open: a fetch/parse error never throws.\n// The `store` (see ./store.js) is passed in so a refresh reuses the same tiered cache.\nimport { PatchstackRuleClient } from '../engine/index.js';\nimport { PulseRuleClient } from '../engine/pulse-client.js';\nimport { validateBundle } from './validate.js';\n\n// A LIVE update is accepted ATOMICALLY. Dropping individual invalid rules is fine for a bundle we\n// already trust (a cache entry, a bundled fallback), but for a fresh remote response it would let a\n// broken update REPLACE known-good policy with partial or empty policy — turning \"we validated it\" into\n// a loss of protection, and caching that loss. So: if any rule/whitelist fails validation, reject the\n// whole update, keep last-known-good, report it, and do NOT write the cache. Opt in to the old\n// behaviour with `acceptPartialBundle: true` (metrics still report every drop).\nfunction liveUpdateRejections(res, options) {\n if (options.acceptPartialBundle) return [];\n const { rejected } = validateBundle(\n { firewall: Array.isArray(res.firewall) ? res.firewall : [], whitelists: Array.isArray(res.whitelists) ? res.whitelists : [] },\n { allowGlobalWhitelists: options.allowGlobalWhitelists },\n );\n return rejected;\n}\n\nfunction reportRejections(rejected, options, label) {\n const report = options.onRuleRejected;\n for (const r of rejected) {\n if (typeof report === 'function') {\n try { report({ ...r, accepted: false }); } catch { /* reporting must never break rule loading */ }\n }\n }\n const sample = rejected.slice(0, 3).map((r) => `${r.id} (${r.reason})`).join('; ');\n options.onError?.(new Error(\n `${label}: rejected the entire update because ${rejected.length} rule(s) failed validation — ` +\n `keeping the previous ruleset and NOT caching this response: ${sample}${rejected.length > 3 ? ', …' : ''}`,\n ));\n}\n\nexport async function resolveRules(options, store, ctx = {}) {\n // The INITIAL load is on the app's startup path, so the runtime gives it a short budget (see\n // bootTimeoutMs) and falls back to cache/bundled rather than delaying boot; refreshes get the full\n // budget. A timeout here is not a protection gap by itself — last-known-good still applies.\n const timeoutMs = ctx.timeoutMs;\n if (options.siteUuid) {\n const prior = await store.read(); // { bundle, etag } | null\n const client = new PulseRuleClient({ siteUuid: options.siteUuid, baseUrl: options.pulseRulesUrl, etag: prior?.etag, timeoutMs, pulseAuth: ctx.pulseAuth });\n const res = await client.getRules();\n if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (res.success && !res.notModified) {\n const rejected = liveUpdateRejections(res, options);\n if (rejected.length > 0) {\n reportRejections(rejected, options, 'rule update rejected');\n if (prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (options.rules) return normalizeBundle(options.rules, options);\n return emptyBundle();\n }\n const bundle = normalizeBundle(res, options);\n await store.write({ bundle, etag: res.etag ?? null });\n return bundle;\n }\n if (prior?.bundle) {\n options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); using cached bundle`));\n return normalizeBundle(prior.bundle, options);\n }\n if (options.rules) {\n options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); using bundled fallback`));\n return normalizeBundle(options.rules, options);\n }\n options.onError?.(new Error(`pulse rule fetch failed (${res.error ?? 'no usable response'}); no cache — running with no rules`));\n return emptyBundle();\n }\n\n if (options.token) {\n const prior = await store.read();\n const client = new PatchstackRuleClient({ token: options.token, baseUrl: options.baseUrl, etag: prior?.etag, timeoutMs });\n const res = await client.getRules();\n if (res.success && res.notModified && prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (res.success && !res.notModified) {\n const rejected = liveUpdateRejections(res, options);\n if (rejected.length > 0) {\n reportRejections(rejected, options, 'rule update rejected');\n if (prior?.bundle) return normalizeBundle(prior.bundle, options);\n if (options.rules) return normalizeBundle(options.rules, options);\n return emptyBundle();\n }\n const bundle = normalizeBundle(res, options);\n await store.write({ bundle, etag: res.etag ?? null });\n return bundle;\n }\n if (prior?.bundle) {\n options.onError?.(new Error(`rule fetch failed (${res.error ?? 'no usable response'}); using cached bundle`));\n return normalizeBundle(prior.bundle, options);\n }\n options.onError?.(new Error(`rule fetch failed (${res.error ?? 'no usable response'}); no cache — running with no rules`));\n return emptyBundle();\n }\n\n if (options.rules) {\n return normalizeBundle(options.rules, options);\n }\n\n return emptyBundle();\n}\n\n// Every rule path (live fetch, cache, bundled fallback) funnels through here, so this is where the\n// delivered policy is VALIDATED before the engine ever executes it: bounded rule count / conditions /\n// nesting / pattern length, known phases + actions. A rule that fails is dropped with a reported reason\n// (`onRuleRejected`) rather than silently kept — an unenforceable rule must never look enforced.\nexport function normalizeBundle(b, options = {}) {\n const enforcement = b?.enforcement ?? b?.mode;\n const { bundle: checked, rejected } = validateBundle(\n {\n firewall: Array.isArray(b.firewall) ? b.firewall : [],\n whitelists: Array.isArray(b.whitelists) ? b.whitelists : [],\n },\n { allowGlobalWhitelists: options.allowGlobalWhitelists },\n );\n if (rejected.length > 0) {\n const report = options.onRuleRejected;\n if (typeof report === 'function') {\n for (const r of rejected) {\n try { report(r); } catch { /* reporting must never break rule loading */ }\n }\n } else {\n const sample = rejected.slice(0, 3).map((r) => `${r.id} (${r.reason})`).join('; ');\n // eslint-disable-next-line no-console\n console.warn(\n `[patchstack] ${rejected.length} delivered rule(s) rejected as invalid/oversized and are NOT enforced: ${sample}` +\n (rejected.length > 3 ? ', …' : ''),\n );\n }\n }\n return {\n firewall: checked.firewall,\n whitelists: checked.whitelists,\n whitelist_keys: b.whitelist_keys ?? {},\n ...(enforcement === 'block' || enforcement === 'dry-run' ? { enforcement } : {}),\n };\n}\n\nexport function emptyBundle() {\n return { firewall: [], whitelists: [], whitelist_keys: {} };\n}\n","// Rule refresh scheduling — how a long-lived guard picks up rules that become relevant after boot\n// (a dependency added mid-session, a zero-day published). Two triggers, both driving the same\n// caller-supplied `tick` (which re-fetches + hot-swaps the engines):\n// - startRefresh: a self-scheduling poll LOOP (reschedules after each tick settles, so runs never\n// overlap), with ±jitter (avoid a thundering herd) and exponential backoff on consecutive\n// failures. `unref`'d — it never keeps the process alive.\n// - makeRefreshHandler: a PUSH endpoint — an authenticated fetch handler the platform/SaaS hits\n// for an immediate refresh (zero-day fast lane) instead of waiting for the next poll.\n\nconst JITTER_FRACTION = 0.1;\nconst MAX_BACKOFF_MULTIPLIER = 8; // cap consecutive-failure backoff at 8× the base interval\n\nexport function startRefresh(tick, { refreshMs, onError } = {}) {\n let stopped = false;\n let failures = 0;\n let timer = null;\n\n const schedule = () => {\n if (stopped) return;\n const backoff = Math.min(2 ** failures, MAX_BACKOFF_MULTIPLIER);\n const jitter = 1 - Math.random() * JITTER_FRACTION; // shorten by up to 10% so clients don't align\n const delay = refreshMs * backoff * jitter;\n timer = setTimeout(run, delay);\n timer?.unref?.();\n };\n\n const run = async () => {\n if (stopped) return;\n try {\n await tick();\n failures = 0;\n } catch (err) {\n failures++;\n onError?.(err);\n }\n schedule();\n };\n\n schedule();\n return {\n stop() {\n stopped = true;\n if (timer) clearTimeout(timer);\n },\n };\n}\n\nexport function makeRefreshHandler(tick, secret) {\n return async (request) => {\n // No secret configured → the endpoint doesn't exist (never an open refresh-DoS surface).\n if (!secret) return new Response('not found', { status: 404 });\n let provided = null;\n try {\n provided = request?.headers?.get?.('x-patchstack-refresh') ?? new URL(request.url).searchParams.get('token');\n } catch {\n provided = null;\n }\n if (provided !== secret) return new Response('forbidden', { status: 403 });\n let refreshed = true;\n try {\n await tick();\n } catch {\n refreshed = false; // fail-open: report the outcome, never throw\n }\n return new Response(JSON.stringify({ refreshed }), { status: 200, headers: { 'content-type': 'application/json' } });\n };\n}\n","import { isSafeOrigin } from './safe-origin.js';\n// Fire-and-forget reporter: Connect runtime → existing connector POST /api/logs/log\n// (same path WordPress uses). Auth: WP-style api_key (`{secret}-{oauth.id}`) →\n// POST /oauth/token (client_credentials) → Bearer JWT on /api/logs/log.\n// Opt out: PATCHSTACK_TELEMETRY=off. Never put api_key in the public widget.\n\nconst DEFAULT_API_BASE = 'https://api.patchstack.com';\nconst DEFAULT_FLUSH_MS = 1000;\nconst MAX_BATCH = 50;\nconst TOKEN_SKEW_MS = 60_000;\n\n/**\n * Parse WP plugin api_key (`{secret}-{oauth.id}`) into client credentials.\n * @param {string} apiKey\n * @returns {{ clientId: string, clientSecret: string } | null}\n */\nexport function parseApiKey(apiKey) {\n if (typeof apiKey !== 'string' || apiKey.length === 0) return null;\n const idx = apiKey.lastIndexOf('-');\n if (idx <= 0 || idx === apiKey.length - 1) return null;\n const clientSecret = apiKey.slice(0, idx);\n const clientId = apiKey.slice(idx + 1);\n if (!/^\\d+$/.test(clientId) || clientSecret.length === 0) return null;\n return { clientId, clientSecret };\n}\n\n/**\n * Derive api.patchstack.com origin from a Pulse manifest/rules URL override.\n * @param {string | undefined} pulseOrManifestUrl\n */\nexport function resolveApiBase(pulseOrManifestUrl) {\n const fromEnv = typeof process !== 'undefined' ? process.env?.PATCHSTACK_API_BASE : undefined;\n if (typeof fromEnv === 'string' && fromEnv.length > 0) {\n // The site api_key is exchanged for a token against this origin, so a hostile/injected env value\n // would be a credential-exfiltration path. Require HTTPS (localhost excepted for local testing);\n // anything else falls back to the default origin rather than shipping the key off-platform.\n const candidate = fromEnv.replace(/\\/$/, '');\n if (isSafeOrigin(candidate)) return candidate;\n // eslint-disable-next-line no-console\n console.warn(\n '[patchstack] ignoring PATCHSTACK_API_BASE: block-log reporting requires an https origin ' +\n '(or localhost). Falling back to the default API origin.',\n );\n }\n if (typeof pulseOrManifestUrl === 'string' && pulseOrManifestUrl.length > 0) {\n try {\n return new URL(pulseOrManifestUrl).origin;\n } catch {\n /* fall through */\n }\n }\n const endpoint = typeof process !== 'undefined' ? process.env?.PATCHSTACK_ENDPOINT : undefined;\n if (typeof endpoint === 'string' && endpoint.length > 0) {\n try {\n return new URL(endpoint).origin;\n } catch {\n /* fall through */\n }\n }\n return DEFAULT_API_BASE;\n}\n\n/**\n * @param {{\n * apiKey: string,\n * apiBase?: string,\n * sourceHost?: string,\n * fetchImpl?: typeof fetch,\n * flushMs?: number,\n * }} opts\n */\nexport function createFirewallLogReporter(opts) {\n const creds = parseApiKey(opts.apiKey);\n if (!creds) {\n return { record() {}, flush() {}, stop() {} };\n }\n\n const apiBase = (opts.apiBase ?? DEFAULT_API_BASE).replace(/\\/$/, '');\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n const flushMs = Number.isFinite(opts.flushMs) ? opts.flushMs : DEFAULT_FLUSH_MS;\n const sourceHost = typeof opts.sourceHost === 'string' ? opts.sourceHost : '';\n\n /** @type {Array<Record<string, unknown>>} */\n let queue = [];\n /** @type {ReturnType<typeof setTimeout> | null} */\n let timer = null;\n let stopped = false;\n\n /** @type {{ token: string, expiresAt: number } | null} */\n let cachedToken = null;\n /** @type {Promise<string | null> | null} */\n let tokenInflight = null;\n\n const fetchAccessToken = async () => {\n if (cachedToken && Date.now() < cachedToken.expiresAt - TOKEN_SKEW_MS) {\n return cachedToken.token;\n }\n if (tokenInflight) return tokenInflight;\n\n tokenInflight = (async () => {\n try {\n const res = await fetchImpl(`${apiBase}/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': '@patchstack/connect',\n },\n body: JSON.stringify({\n grant_type: 'client_credentials',\n client_id: creds.clientId,\n client_secret: creds.clientSecret,\n }),\n });\n if (!res || !res.ok) return null;\n const body = await res.json();\n const token = body?.access_token;\n if (typeof token !== 'string' || token.length === 0) return null;\n const expiresIn = Number(body?.expires_in);\n const ttlMs = Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn * 1000 : 3600_000;\n cachedToken = { token, expiresAt: Date.now() + ttlMs };\n return token;\n } catch {\n return null;\n } finally {\n tokenInflight = null;\n }\n })();\n\n return tokenInflight;\n };\n\n const flush = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (queue.length === 0 || typeof fetchImpl !== 'function') return;\n\n const batch = queue.splice(0, MAX_BATCH);\n void (async () => {\n const token = await fetchAccessToken();\n if (!token) return;\n\n const body = new URLSearchParams();\n body.set('type', 'firewall');\n body.set('logs', JSON.stringify(batch));\n\n try {\n const p = fetchImpl(`${apiBase}/api/logs/log`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'User-Agent': '@patchstack/connect',\n ...(sourceHost ? { 'Source-Host': sourceHost } : {}),\n },\n body,\n });\n if (p && typeof p.then === 'function') p.catch(() => {});\n } catch {\n /* ignore */\n }\n })();\n };\n\n return {\n /**\n * @param {{\n * rule?: { id?: string | number },\n * method?: string | null,\n * path?: string | null,\n * ip?: string | null,\n * userAgent?: string | null,\n * }} event\n */\n record(event) {\n if (stopped) return;\n const fid = event?.rule?.id;\n if (fid === undefined || fid === null || fid === '') return;\n\n queue.push({\n fid,\n method: event.method ?? null,\n request_uri: event.path ?? null,\n ip: event.ip ?? null,\n user_agent: event.userAgent ?? null,\n log_date: new Date().toISOString(),\n });\n\n if (queue.length >= MAX_BATCH) {\n flush();\n return;\n }\n if (!timer) timer = setTimeout(flush, flushMs);\n },\n flush,\n stop() {\n stopped = true;\n flush();\n },\n };\n}\n\n/** @returns {boolean} */\nexport function telemetryEnabled() {\n const v = typeof process !== 'undefined' ? process.env?.PATCHSTACK_TELEMETRY : undefined;\n if (v === undefined || v === '') return true;\n return !/^(0|false|off|no)$/i.test(String(v));\n}\n","// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\n//\n// The vibe-coded app's browser talks straight to Supabase, bypassing the app's own server —\n// so a normal in-app WAF never sees the data traffic. The installer patches the generated\n// Supabase client to tunnel every call through this guard (running in the app's own server /\n// Worker). The guard runs the Patchstack protection policy on the tunneled request, then\n// forwards it to Supabase — pinned to the app's own project so it can't be turned into an\n// open proxy (SSRF).\n//\n// The heavy lifting (rule evaluation, dry-run/block, fail-open) is `protection.fetchGuard()`\n// from `createProtection` — this module is just the Supabase-specific tunnel around it.\n\nexport const GUARD_PATH = '/_patchstack/guard';\n\n// Headers we must not copy verbatim when re-emitting the upstream response.\nconst HOP_BY_HOP = new Set(['content-encoding', 'content-length', 'transfer-encoding', 'connection']);\n\n/**\n * @param {object} opts\n * @param {{ fetchGuard: () => (req: Request) => Promise<Response|null> }} opts.protection a createProtection() result\n * @param {string|undefined} opts.supabaseUrl the app's Supabase project URL (server-side env) — the only allowed forward target\n * @param {typeof fetch} [opts.fetchImpl] injectable fetch (tests)\n * @returns {(request: Request) => Promise<Response>}\n */\nexport function createSupabaseGuard({ protection, supabaseUrl, fetchImpl = fetch }) {\n const guard = protection.fetchGuard();\n const allowedOrigin = supabaseUrl ? new URL(supabaseUrl).origin : null;\n\n return async function handleGuardRequest(request) {\n const target = request.headers.get('x-ps-target');\n if (!target) return new Response('patchstack: missing x-ps-target', { status: 400 });\n\n let targetUrl;\n try {\n targetUrl = new URL(target);\n } catch {\n return new Response('patchstack: invalid target', { status: 400 });\n }\n\n // SSRF pin: only ever forward to the app's own Supabase project (origin from server-side\n // env, never the client-supplied header). Anything else — internal hosts, cloud metadata,\n // a different scheme — is rejected before any outbound request.\n if (!allowedOrigin || targetUrl.protocol !== 'https:' || targetUrl.origin !== allowedOrigin) {\n return new Response('patchstack: target not allowed', { status: 403 });\n }\n\n const hasBody = request.method !== 'GET' && request.method !== 'HEAD';\n const bodyText = hasBody ? await request.text() : '';\n\n // Evaluate the tunneled call against the policy. fetchGuard returns a 403 Response when it\n // blocks (block mode + match), or null to allow (allow, or dry-run — it records via onDetect).\n const evalReq = new Request(targetUrl.toString(), {\n method: request.method,\n headers: request.headers,\n body: hasBody ? bodyText : undefined,\n });\n const blocked = await guard(evalReq);\n if (blocked) return blocked;\n\n // Allowed → forward to Supabase, server-side.\n const forwardHeaders = new Headers(request.headers);\n forwardHeaders.delete('x-ps-target');\n forwardHeaders.delete('host');\n const upstream = await fetchImpl(targetUrl.toString(), {\n method: request.method,\n headers: forwardHeaders,\n body: hasBody ? bodyText : undefined,\n redirect: 'manual',\n });\n\n const outHeaders = new Headers();\n upstream.headers.forEach((value, key) => {\n if (!HOP_BY_HOP.has(key.toLowerCase())) outHeaders.set(key, value);\n });\n const buf = await upstream.arrayBuffer();\n const forwarded = new Response(buf, { status: upstream.status, statusText: upstream.statusText, headers: outHeaders });\n\n // Response phase: screen what Supabase returned (query results can leak secrets/PII) —\n // redact the offending spans / withhold, per the response rules. Fail-open if unavailable.\n return protection.screenResponse ? protection.screenResponse(forwarded) : forwarded;\n };\n}\n"],"mappings":"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","names":["path","path","path","import_promises","import_node_path","path","import_promises","import_node_path","path","import_promises","import_node_path","import_promises","path","import_promises","import_node_path","path","import_promises","import_node_path","warnOnce","path","DEFAULT_BASE_URL","DEFAULT_CACHE_TTL","JITTER_FRACTION","parseCookies","path","path","JITTER_FRACTION","TOKEN_SKEW_MS","allow","walk","path"]}