@zarex/core 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/protector.ts","../src/headers.ts","../src/ip.ts","../src/ipaddr.ts","../src/ranges.ts","../src/sha256.ts","../src/rules.ts","../src/index.ts","../src/sensitive.ts","../src/responseguard.ts","../src/secureheaders.ts","../src/web.ts"],"sourcesContent":["import {\r\n SDK_VERSION,\r\n ZarexClientError,\r\n ZarexConfigError,\r\n createClient,\r\n type BreakerOptions,\r\n type DecideRequest,\r\n type DecideResponse,\r\n type Verdict,\r\n type ZarexClient,\r\n} from \"@zarex/client\";\r\nimport { selectHeadersDetailed, type HeaderBag } from \"./headers.js\";\r\nimport { createIpResolver, type IpAdvice, type IpSource } from \"./ip.js\";\r\nimport type { Rule } from \"./rules.js\";\r\nimport { sha256Hex } from \"./sha256.js\";\r\n\r\nexport interface Logger {\r\n warn(message: string): void;\r\n error?(message: string): void;\r\n}\r\n\r\nexport interface ZarexOptions {\r\n /** Clé API du projet (jamais journalisée). Ex. process.env.ZAREX_KEY. */\r\n key: string;\r\n /** URL du moteur Zarex (ou variable d'environnement ZAREX_URL). */\r\n baseUrl?: string;\r\n /** Règles appliquées à chaque requête (1 à 10). */\r\n rules: Rule[];\r\n /**\r\n * OBLIGATOIRE. Comment trouver l'IP du visiteur — voir le type IpSource.\r\n * Un mauvais choix fait passer tous vos visiteurs pour une seule adresse.\r\n */\r\n ipSource: IpSource;\r\n /** Délai maximal d'un appel au moteur, en ms (défaut 500). Au-delà : la requête passe (fail-open). */\r\n timeoutMs?: number;\r\n allowInsecureHttp?: boolean;\r\n /** Disjoncteur (défaut : 5 échecs → 10 s de pause). `false` pour le désactiver. */\r\n breaker?: BreakerOptions | false;\r\n /** Transmettre aussi l'en-tête Cookie au Shield (défaut false : il contient les sessions des visiteurs). */\r\n inspectCookies?: boolean;\r\n /** En-têtes supplémentaires à transmettre (jamais Authorization / Set-Cookie). */\r\n extraHeaders?: string[];\r\n /** Messages d'avertissement. `false` pour les couper. Défaut : console. */\r\n logger?: Logger | false;\r\n fetch?: typeof fetch;\r\n /** Horloge (tests). */\r\n now?: () => number;\r\n}\r\n\r\nexport interface ProtectInput {\r\n method: string;\r\n /** Chemin AVEC la query string (le Shield l'analyse ; elle n'est pas conservée dans les statistiques). */\r\n path: string;\r\n host?: string | undefined;\r\n headers: HeaderBag;\r\n /** Adresse de la connexion TCP (req.socket.remoteAddress). */\r\n socketIp?: string | undefined;\r\n /** Adresse calculée par le framework (SvelteKit, Fastify…). */\r\n frameworkIp?: string | undefined;\r\n /** Express : req.ip et état de « trust proxy ». */\r\n expressIp?: string | undefined;\r\n expressTrustProxy?: boolean | undefined;\r\n /** Corps déjà lu (facultatif — voir inspectBody dans l'adaptateur). Tronqué à 48 Ko. */\r\n body?: string | undefined;\r\n}\r\n\r\nexport interface ProtectCall {\r\n /**\r\n * Identifiants personnalisés pour les règles (ex. { userId: \"42\" }). Hachés en SHA-256 avant envoi.\r\n * Au plus 16 par requête (MAX_CHARACTERISTICS) : au-delà, les entrées en trop sont retirées et un\r\n * avertissement est journalisé (trouvé lors de l'audit du 04-05/11/2026 : cette limite n'était\r\n * documentée nulle part et le dépassement passait jusqu'ici totalement inaperçu).\r\n */\r\n characteristics?: Record<string, string>;\r\n /** Jetons consommés par cette requête (règle tokenBucket). */\r\n tokensRequested?: number;\r\n /** Adresse e-mail à valider (règle emailValidation). Jamais journalisée côté Zarex. */\r\n email?: string;\r\n /** Numéro de téléphone à valider (règle phoneValidation). */\r\n phone?: string;\r\n /** Texte à analyser (règles sensitiveInfo et promptInjection) : message destiné à une IA, champ libre… Tronqué à 32 Ko. */\r\n text?: string;\r\n /** Remplace les règles configurées pour cet appel. */\r\n rules?: Rule[];\r\n}\r\n\r\nexport interface RuleOutcome {\r\n ruleType: string;\r\n verdict: Verdict;\r\n dryRun: boolean;\r\n reason?: Record<string, unknown>;\r\n}\r\n\r\nexport interface Decision {\r\n /** true = laisser passer. Toujours true en cas de panne (fail-open). */\r\n allowed: boolean;\r\n denied: boolean;\r\n verdict: Verdict;\r\n /** Identifiant de la décision (support, journaux). Vide si le moteur n'a pas répondu. */\r\n id: string;\r\n results: RuleOutcome[];\r\n /** Première règle ayant refusé (hors mode DRY_RUN). */\r\n deniedBy?: RuleOutcome;\r\n /** Secondes à attendre avant de réessayer (limite de débit). */\r\n retryAfterSeconds?: number;\r\n /** true = le moteur n'a pas pu décider (panne, délai, IP inconnue…) et la requête a été laissée passer. */\r\n degraded: boolean;\r\n error?: { code: string; message: string };\r\n /** IP du visiteur utilisée pour la décision. */\r\n ip?: string;\r\n /** Version des règles du moteur ayant décidé (ex. \"11.3.0+shield-2.0\"), pour comparer avant/après une mise à jour. Absente d'un ancien moteur. */\r\n rulesetVersion?: string;\r\n /** Pays (code ISO, \"ZZ\" = inconnu) et réseau (\"AS15169\", \"AS0\" = inconnu) de l'IP du visiteur. Absent si le moteur n'a pas de base d'adresses. */\r\n ipInfo?: { country: string; asn: string };\r\n}\r\n\r\nexport interface Zarex {\r\n protect(input: ProtectInput, call?: ProtectCall): Promise<Decision>;\r\n /** Valeur de ipSource déclarée au moteur. */\r\n readonly ipSourceLabel: string;\r\n}\r\n\r\nconst MAX_RULES = 10;\r\nconst CHAR_KEY_RE = /^[A-Za-z0-9_.:-]{1,64}$/;\r\nconst MAX_BODY_BYTES = 48 * 1024;\r\nconst MAX_PATH_BYTES = 8 * 1024;\r\nconst MAX_TEXT_BYTES = 32 * 1024;\r\nconst MAX_FIELD_CHARS = 512;\r\nconst MAX_CHARACTERISTICS = 16;\r\nconst ADVICE_REPEAT_MS = 10 * 60_000;\r\nconst ERROR_REPEAT_MS = 10_000;\r\n\r\nconst ADVICE_TEXT: Record<IpAdvice, (label: string) => string> = {\r\n forwarded_header_ignored: (l) =>\r\n `A proxy header (X-Forwarded-For / Forwarded / X-Real-IP) is present but ipSource is \"${l}\", which ignores it. ` +\r\n \"If your app is behind a proxy or CDN, every visitor will look like the proxy's address. Set ipSource accordingly (e.g. { proxies: 1 }, \\\"cloudflare\\\", \\\"vercel\\\").\",\r\n not_from_trusted_proxy: (l) =>\r\n `A request did not come from the proxy declared in ipSource (\"${l}\"), so its forwarding headers were ignored. ` +\r\n \"Either your origin server is reachable directly (lock it down so only your proxy can connect), or ipSource does not match your setup.\",\r\n chain_too_short: (l) =>\r\n `X-Forwarded-For has fewer addresses than the proxies declared in ipSource (\"${l}\"). The connection address was used instead. ` +\r\n \"Check the number of proxies in front of your app.\",\r\n missing_provider_header: (l) =>\r\n `The connection comes from the declared provider but its client-IP header is missing or unreadable (ipSource \"${l}\"). The request was not checked.`,\r\n weak_provider_header: (l) =>\r\n `The platform's primary client-IP header was missing (ipSource \"${l}\"), so a more generic fallback header was used instead. ` +\r\n \"If your app really runs on that platform this is usually harmless (a CDN/WAF in front of it, or an atypical runtime), but double-check: a wrong ipSource would let visitors forge this fallback header themselves.\",\r\n invalid_forwarded_chain: () =>\r\n \"X-Forwarded-For is unreadable or abnormally long: the visitor's IP could not be determined and the request was not checked.\",\r\n all_addresses_trusted: (l) =>\r\n `Every address in the chain is a trusted proxy (ipSource \"${l}\"), so no visitor IP was found and the request was not checked.`,\r\n no_socket_ip: () => \"The connection's remote address is unknown: the request was not checked.\",\r\n};\r\n\r\nconst ENGINE_WARNING_TEXT: Record<string, (label: string) => string> = {\r\n ip_not_public: (l) =>\r\n `The engine received a private/local/shared IP address. This is normal in local development. In production it almost always means a proxy is not configured: every visitor then shares one address and rate limits hit everyone. Check ipSource (currently \"${l}\").`,\r\n path_truncated: () =>\r\n \"A request path was longer than 8 KB, so only its beginning and end were inspected. Rules apply their `oversize` policy (default for shield() and requestFilter(): deny). Very long URLs are almost always attacks; if they are legitimate on your site, set `oversize: \\\"inspect\\\"` on that rule.\",\r\n headers_truncated: () =>\r\n \"Some request headers were longer than 8 KB (or too numerous), so only part of them was inspected. Rules apply their `oversize` policy (default for shield() and requestFilter(): deny).\",\r\n body_truncated: () =>\r\n \"A request body was larger than 48 KB, so only its beginning and end were inspected (the middle was not). Use `oversize: \\\"deny\\\"` on the rule if large bodies are never expected.\",\r\n ip_db_absent: () =>\r\n \"A rule uses ip.country or ip.asn but the engine has no IP address database (ZAREX_IPDB_FILE; build it with `zarex-ipdb fetch`). Those conditions see every visitor as unknown (country \\\"ZZ\\\", AS0): a deny-list never matches, and an allow-list (default: \\\"DENY\\\") refuses everyone.\",\r\n ip_db_stale: () =>\r\n \"The engine's IP address database is more than 45 days old, so countries and networks may be wrong. Re-run `zarex-ipdb fetch` (it should run nightly).\",\r\n text_truncated: () =>\r\n \"A text sent for analysis was longer than 32 KB, so only its beginning and end were inspected.\",\r\n};\r\n\r\nfunction defaultLogger(): Logger {\r\n return {\r\n warn: (m) => console.warn(m),\r\n error: (m) => console.error(m),\r\n };\r\n}\r\n\r\nfunction readEnv(name: string): string | undefined {\r\n try {\r\n return typeof process !== \"undefined\" ? process.env?.[name] : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction isCloudflareWorker(): boolean {\r\n try {\r\n return typeof navigator !== \"undefined\" && navigator.userAgent === \"Cloudflare-Workers\";\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/** Même séparateur que le moteur Go (promptguard.CutMarker) : le moteur n'aura rien à re-couper. */\r\nconst CUT_MARKER = \"\\n[...cut...]\\n\";\r\n/** Séparateur du chemin : sans saut de ligne (il reste dans une URL). Même valeur que le moteur Go (cutMarker). */\r\nconst PATH_CUT_MARKER = \" [...cut...] \";\r\n\r\n/**\r\n * Réduit un texte trop long à `max` octets en gardant le DÉBUT et la FIN (jamais au milieu d'un\r\n * caractère). Garder seulement le début permettrait de cacher une attaque après un long remplissage.\r\n * Le milieu du texte n'est pas transmis : `truncated` est alors vrai.\r\n */\r\nfunction truncateHeadTail(s: string, max: number, markerText: string = CUT_MARKER): { text: string; truncated: boolean } {\r\n if (s.length <= max / 4) return { text: s, truncated: false };\r\n const enc = new TextEncoder().encode(s);\r\n if (enc.length <= max) return { text: s, truncated: false };\r\n const marker = new TextEncoder().encode(markerText);\r\n const room = max - marker.length;\r\n const dec = new TextDecoder();\r\n if (room < 2) return { text: dec.decode(enc.subarray(0, max)), truncated: true };\r\n let head = Math.floor(room / 2);\r\n while (head > 0 && (enc[head]! & 0xc0) === 0x80) head--;\r\n let start = enc.length - (room - Math.floor(room / 2));\r\n while (start < enc.length && (enc[start]! & 0xc0) === 0x80) start++;\r\n return { text: dec.decode(enc.subarray(0, head)) + markerText + dec.decode(enc.subarray(start)), truncated: true };\r\n}\r\n\r\n/** En-têtes cités par les règles requestFilter : ils doivent faire partie de ceux transmis au moteur. */\r\nfunction filterHeaderNames(rules: readonly Rule[]): string[] {\r\n const out = new Set<string>();\r\n for (const r of rules) {\r\n const rf = (r as { request_filter?: { expressions: { field: string }[] } }).request_filter;\r\n for (const e of rf?.expressions ?? []) if (e.field.startsWith(\"headers.\")) out.add(e.field.slice(8).toLowerCase());\r\n else if (e.field.startsWith(\"cookies.\")) out.add(\"cookie\");\r\n }\r\n return [...out];\r\n}\r\n\r\nexport function validateRules(rules: unknown): Rule[] {\r\n if (!Array.isArray(rules) || rules.length < 1 || rules.length > MAX_RULES) {\r\n throw new ZarexConfigError(`Zarex: \\`rules\\` must contain between 1 and ${MAX_RULES} rules (use shield(), rateLimit(), …).`);\r\n }\r\n const known = new Set([\r\n \"shield\", \"rate_limit\", \"bot_detection\", \"token_bucket\",\r\n \"request_filter\", \"sensitive_info\", \"email_validation\", \"phone_validation\", \"prompt_injection\",\r\n ]);\r\n for (const r of rules) {\r\n const keys = r && typeof r === \"object\" ? Object.keys(r) : [];\r\n if (keys.length !== 1 || !known.has(keys[0]!)) {\r\n throw new ZarexConfigError(\"Zarex: each rule must be created with shield(), rateLimit(), tokenBucket(), detectBot(), requestFilter(), sensitiveInfo(), emailValidation(), phoneValidation() or promptInjection().\");\r\n }\r\n }\r\n return rules as Rule[];\r\n}\r\n\r\nexport function createZarex(options: ZarexOptions): Zarex {\r\n if (!options || typeof options !== \"object\") throw new ZarexConfigError(\"Zarex: options are required.\");\r\n const resolver = createIpResolver(options.ipSource);\r\n const rules = validateRules(options.rules);\r\n const now = options.now ?? Date.now;\r\n const logger: Logger | null = options.logger === false ? null : (options.logger ?? defaultLogger());\r\n\r\n const client: ZarexClient = createClient({\r\n key: options.key,\r\n ...(options.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {}),\r\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\r\n ...(options.allowInsecureHttp !== undefined ? { allowInsecureHttp: options.allowInsecureHttp } : {}),\r\n ...(options.breaker !== undefined ? { breaker: options.breaker } : {}),\r\n ...(options.fetch ? { fetch: options.fetch } : {}),\r\n });\r\n\r\n // Fournisseur déclaré mais l'environnement ne correspond pas : les en-têtes ne sont\r\n // fiables que sur cette plateforme (ailleurs, n'importe qui peut les écrire).\r\n if (logger) {\r\n if (options.ipSource === \"vercel\" && !readEnv(\"VERCEL\")) {\r\n logger.warn('[zarex] ipSource \"vercel\" is only safe when the app runs on Vercel (VERCEL is not set here): outside Vercel, anyone can forge these headers.');\r\n }\r\n if (options.ipSource === \"cloudflare-workers\" && !isCloudflareWorker()) {\r\n logger.warn('[zarex] ipSource \"cloudflare-workers\" is only safe inside a Cloudflare Worker: anywhere else, anyone can forge the CF-Connecting-IP header.');\r\n }\r\n if (options.ipSource === \"fly\" && !readEnv(\"FLY_APP_NAME\")) {\r\n logger.warn('[zarex] ipSource \"fly\" is only safe when the app runs on Fly.io (FLY_APP_NAME is not set here): outside Fly, anyone can forge this header.');\r\n }\r\n }\r\n\r\n if (filterHeaderNames(rules).includes(\"cookie\") && !options.inspectCookies) {\r\n throw new ZarexConfigError(\"Zarex: a requestFilter on `headers.cookie` or `cookies.<name>` needs `inspectCookies: true` (cookies are not sent to the engine by default).\");\r\n }\r\n\r\n const lastLogged = new Map<string, number>();\r\n const suppressed = new Map<string, number>();\r\n function logLimited(key: string, repeatMs: number, level: \"warn\" | \"error\", message: string): void {\r\n if (!logger) return;\r\n const t = now();\r\n const last = lastLogged.get(key);\r\n if (last !== undefined && t - last < repeatMs) {\r\n suppressed.set(key, (suppressed.get(key) ?? 0) + 1);\r\n return;\r\n }\r\n lastLogged.set(key, t);\r\n const n = suppressed.get(key) ?? 0;\r\n suppressed.delete(key);\r\n const full = `[zarex] ${message}${n > 0 ? ` (${n} similar message(s) omitted)` : \"\"}`;\r\n try {\r\n (level === \"error\" && logger.error ? logger.error : logger.warn).call(logger, full);\r\n } catch {\r\n /* un logger défaillant ne doit jamais casser une requête */\r\n }\r\n }\r\n\r\n function degraded(code: string, message: string, ip?: string): Decision {\r\n return {\r\n allowed: true,\r\n denied: false,\r\n verdict: \"ALLOW\",\r\n id: \"\",\r\n results: [],\r\n degraded: true,\r\n error: { code, message },\r\n ...(ip ? { ip } : {}),\r\n };\r\n }\r\n\r\n function fromResponse(res: DecideResponse, ip: string): Decision {\r\n const results: RuleOutcome[] = res.results.map((r) => ({\r\n ruleType: r.rule_type,\r\n verdict: r.verdict,\r\n dryRun: r.is_dry_run,\r\n ...(r.reason ? { reason: r.reason } : {}),\r\n }));\r\n if (res.verdict === \"ERROR\") {\r\n return { ...degraded(\"engine_error\", \"The engine could not evaluate the rules; request allowed.\"), results, id: res.decision_id, ip };\r\n }\r\n const denied = res.verdict === \"DENY\";\r\n const deniedBy = denied ? results.find((r) => r.verdict === \"DENY\" && !r.dryRun) : undefined;\r\n let retryAfterSeconds: number | undefined;\r\n if (deniedBy?.reason) {\r\n const ms = Number(deniedBy.reason[\"reset_in_ms\"] ?? deniedBy.reason[\"retry_in_ms\"]);\r\n if (Number.isFinite(ms) && ms > 0) retryAfterSeconds = Math.min(86_400, Math.max(1, Math.ceil(ms / 1000)));\r\n }\r\n return {\r\n allowed: !denied,\r\n denied,\r\n verdict: res.verdict,\r\n id: res.decision_id,\r\n results,\r\n ...(res.ruleset_version ? { rulesetVersion: res.ruleset_version } : {}),\r\n ...(res.ip ? { ipInfo: res.ip } : {}),\r\n ...(deniedBy ? { deniedBy } : {}),\r\n ...(retryAfterSeconds !== undefined ? { retryAfterSeconds } : {}),\r\n degraded: false,\r\n ip,\r\n };\r\n }\r\n\r\n async function protect(input: ProtectInput, call: ProtectCall = {}): Promise<Decision> {\r\n try {\r\n const resolved = resolver.resolve({\r\n socketIp: input.socketIp,\r\n expressIp: input.expressIp,\r\n frameworkIp: input.frameworkIp,\r\n expressTrustProxy: input.expressTrustProxy,\r\n headers: input.headers,\r\n });\r\n for (const a of resolved.advice) {\r\n logLimited(`advice:${a}`, ADVICE_REPEAT_MS, \"warn\", ADVICE_TEXT[a](resolver.label));\r\n }\r\n if (!resolved.ip) {\r\n return degraded(\"ip_unresolved\", \"The visitor's IP could not be determined; request allowed without checking.\");\r\n }\r\n\r\n const reqRules = call.rules ? validateRules(call.rules) : rules;\r\n const characteristics: { key: string; value: string }[] = [];\r\n // Au-delà de MAX_CHARACTERISTICS, les entrées en trop étaient retirées sans AUCUN signal (ni log,\r\n // ni documentation de la limite) — un développeur qui ajoute une 17ᵉ caractéristique pour une règle\r\n // de rate-limit la croit prise en compte alors qu'elle est purement ignorée. Trouvé lors de\r\n // l'audit du 04-05/11/2026, voir AUDIT_SECURITE_COMPLET.md.\r\n let droppedCharacteristics = 0;\r\n for (const [k, v] of Object.entries(call.characteristics ?? {})) {\r\n if (k === \"ip\" || !CHAR_KEY_RE.test(k) || typeof v !== \"string\" || v === \"\") continue;\r\n if (characteristics.length >= MAX_CHARACTERISTICS) {\r\n droppedCharacteristics++;\r\n continue;\r\n }\r\n characteristics.push({ key: k, value: sha256Hex(v) });\r\n }\r\n if (droppedCharacteristics > 0) {\r\n logLimited(\r\n \"characteristics_truncated\",\r\n ADVICE_REPEAT_MS,\r\n \"warn\",\r\n `${droppedCharacteristics} characteristic(s) were dropped: at most ${MAX_CHARACTERISTICS} are sent per request. ` +\r\n \"If a rule keys off one of the dropped names, it is no longer enforced for it.\"\r\n );\r\n }\r\n\r\n const body = input.body !== undefined ? truncateHeadTail(input.body, MAX_BODY_BYTES) : undefined;\r\n const pathCut = truncateHeadTail(String(input.path || \"/\"), MAX_PATH_BYTES, PATH_CUT_MARKER);\r\n const fwd = selectHeadersDetailed(input.headers, {\r\n extraHeaders: [...(options.extraHeaders ?? []), ...filterHeaderNames(reqRules)],\r\n ...(options.inspectCookies ? { inspectCookies: true } : {}),\r\n });\r\n const text = typeof call.text === \"string\" && call.text !== \"\" ? truncateHeadTail(call.text, MAX_TEXT_BYTES) : undefined;\r\n const email = typeof call.email === \"string\" && call.email !== \"\" ? call.email.slice(0, MAX_FIELD_CHARS) : undefined;\r\n const phone = typeof call.phone === \"string\" && call.phone !== \"\" ? call.phone.slice(0, 64) : undefined;\r\n const payload: DecideRequest = {\r\n site_key: options.key,\r\n sdk_version: SDK_VERSION,\r\n sdk_lang: \"js\",\r\n entry_point: \"REQUEST\",\r\n ip_source: resolver.label,\r\n rules: reqRules,\r\n context: {\r\n ip: resolved.ip,\r\n method: String(input.method || \"GET\").slice(0, 16),\r\n path: pathCut.text,\r\n ...(pathCut.truncated ? { path_truncated: true } : {}),\r\n host: String(input.host ?? \"\").slice(0, 255),\r\n headers: fwd.headers,\r\n ...(fwd.truncated ? { headers_truncated: true } : {}),\r\n ...(body ? { body: body.text, ...(body.truncated ? { body_truncated: true } : {}) } : {}),\r\n ...(characteristics.length ? { characteristics } : {}),\r\n ...(email ? { email } : {}),\r\n ...(phone ? { phone } : {}),\r\n ...(text ? { text_content: text.text, ...(text.truncated ? { text_truncated: true } : {}) } : {}),\r\n ...(call.tokensRequested !== undefined && Number.isInteger(call.tokensRequested) && call.tokensRequested > 0\r\n ? { tokens_requested: Math.min(call.tokensRequested, 1_000_000) }\r\n : {}),\r\n },\r\n };\r\n\r\n let res: DecideResponse;\r\n try {\r\n res = await client.decide(payload);\r\n } catch (err) {\r\n const e = err instanceof ZarexClientError ? err : new ZarexClientError(\"network\", \"Zarex request failed\");\r\n const hint =\r\n e.code === \"unauthorized\"\r\n ? \" — check your API key (revoked, wrong environment, or typo).\"\r\n : e.code === \"quota_exceeded\"\r\n ? \" — your plan's monthly quota is used up.\"\r\n : e.code === \"circuit_open\"\r\n ? \" — the engine failed repeatedly; calls are paused briefly.\"\r\n : \"\";\r\n // circuit_open est attendu pendant une panne : un seul message suffit.\r\n logLimited(`error:${e.code}`, e.code === \"circuit_open\" ? 60_000 : ERROR_REPEAT_MS, \"error\", `Request allowed without protection: ${e.message}${hint}`);\r\n return degraded(e.code, e.message, resolved.ip);\r\n }\r\n\r\n for (const w of res.warnings ?? []) {\r\n const text = ENGINE_WARNING_TEXT[w];\r\n if (text) logLimited(`engine:${w}`, ADVICE_REPEAT_MS, \"warn\", text(resolver.label));\r\n }\r\n return fromResponse(res, resolved.ip);\r\n } catch (err) {\r\n // Dernier filet : une erreur INATTENDUE du SDK ne doit jamais faire échouer la requête du client.\r\n const msg = err instanceof Error ? err.message : \"unexpected error\";\r\n logLimited(\"error:internal\", ERROR_REPEAT_MS, \"error\", `Request allowed without protection: unexpected SDK error (${msg.slice(0, 120)})`);\r\n return degraded(\"internal_error\", \"Unexpected SDK error\");\r\n }\r\n }\r\n\r\n return { protect, ipSourceLabel: resolver.label };\r\n}","/** Un « sac d'en-têtes » : objet Node (clés en minuscules), Map, ou Headers (fetch). */\r\nexport type HeaderBag =\r\n | Record<string, string | string[] | undefined>\r\n | { get(name: string): string | null };\r\n\r\nfunction isHeadersLike(h: HeaderBag): h is { get(name: string): string | null } {\r\n return typeof (h as { get?: unknown }).get === \"function\";\r\n}\r\n\r\n/** Valeur d'un en-tête (plusieurs valeurs jointes par une virgule). */\r\nexport function getHeader(bag: HeaderBag, name: string): string | undefined {\r\n const lower = name.toLowerCase();\r\n if (isHeadersLike(bag)) return bag.get(lower) ?? undefined;\r\n const direct = (bag as Record<string, string | string[] | undefined>)[lower];\r\n let v = direct;\r\n if (v === undefined) {\r\n // Certains objets conservent la casse d'origine.\r\n for (const k of Object.keys(bag)) {\r\n if (k.toLowerCase() === lower) {\r\n v = (bag as Record<string, string | string[] | undefined>)[k];\r\n break;\r\n }\r\n }\r\n }\r\n if (v === undefined) return undefined;\r\n return Array.isArray(v) ? v.join(\", \") : v;\r\n}\r\n\r\n/**\r\n * En-têtes envoyés au moteur : LISTE BLANCHE.\r\n *\r\n * Pourquoi pas « tout sauf les secrets » : les secrets ont des noms que nous ne\r\n * pouvons pas deviner (x-api-key, x-auth-token, x-session…). Une liste blanche ne\r\n * laisse jamais partir un secret par oubli. Le moteur n'a besoin que de ces\r\n * en-têtes pour le Shield et la détection de robots.\r\n *\r\n * `cookie` n'est PAS dans la liste : il contient les sessions des visiteurs.\r\n * Activez `inspectCookies` seulement si vous acceptez qu'il soit transmis.\r\n */\r\nexport const DEFAULT_FORWARDED_HEADERS: readonly string[] = [\r\n \"user-agent\",\r\n \"accept\",\r\n \"accept-language\",\r\n \"accept-encoding\",\r\n \"referer\",\r\n \"origin\",\r\n \"content-type\",\r\n \"x-requested-with\",\r\n \"x-forwarded-for\", // lu par le Shield (les attaquants y glissent des charges)\r\n \"sec-fetch-site\",\r\n \"sec-fetch-mode\",\r\n \"sec-fetch-dest\",\r\n \"sec-ch-ua\",\r\n \"sec-ch-ua-mobile\",\r\n \"sec-ch-ua-platform\",\r\n];\r\n\r\n/** Jamais transmis, même si le développeur les ajoute dans `extraHeaders`. */\r\nexport const NEVER_FORWARD: ReadonlySet<string> = new Set([\r\n \"authorization\",\r\n \"proxy-authorization\",\r\n \"set-cookie\",\r\n \"x-zarex-key\",\r\n]);\r\n\r\n/** Mêmes limites que le moteur (8 Ko par valeur, 32 Ko au total) : on ne coupe pas avant lui. */\r\nconst MAX_VALUE = 8 * 1024;\r\nconst MAX_TOTAL = 32 * 1024;\r\nconst CUT = \" [...cut...] \";\r\n\r\n/** Garde le début ET la fin d'une valeur trop longue (une attaque après du remplissage reste visible). */\r\nfunction cutValue(v: string, max: number): string {\r\n if (v.length <= max) return v;\r\n const room = max - CUT.length;\r\n const h = Math.floor(room / 2);\r\n return v.slice(0, h) + CUT + v.slice(v.length - (room - h));\r\n}\r\n\r\nexport interface ForwardOptions {\r\n extraHeaders?: readonly string[];\r\n inspectCookies?: boolean;\r\n}\r\n\r\nexport function selectHeaders(bag: HeaderBag, opts: ForwardOptions = {}): Record<string, string> {\r\n return selectHeadersDetailed(bag, opts).headers;\r\n}\r\n\r\n/** Comme selectHeaders, et dit si quelque chose a été coupé ou écarté (jamais en silence). */\r\nexport function selectHeadersDetailed(bag: HeaderBag, opts: ForwardOptions = {}): { headers: Record<string, string>; truncated: boolean } {\r\n const names = new Set<string>(DEFAULT_FORWARDED_HEADERS);\r\n for (const h of opts.extraHeaders ?? []) names.add(h.toLowerCase());\r\n if (opts.inspectCookies) names.add(\"cookie\");\r\n const out: Record<string, string> = {};\r\n let total = 0;\r\n let truncated = false;\r\n for (const name of names) {\r\n if (NEVER_FORWARD.has(name)) continue;\r\n const v = getHeader(bag, name);\r\n if (v === undefined || v === \"\") continue;\r\n const cut = cutValue(v, MAX_VALUE);\r\n if (cut !== v) truncated = true;\r\n if (total + name.length + cut.length > MAX_TOTAL) {\r\n truncated = true;\r\n break;\r\n }\r\n total += name.length + cut.length;\r\n out[name] = cut;\r\n }\r\n return { headers: out, truncated };\r\n}","import { ZarexConfigError } from \"@zarex/client\";\r\nimport { getHeader, type HeaderBag } from \"./headers.js\";\r\nimport {\r\n anyCidrContains,\r\n formatIp,\r\n parseCidr,\r\n parseIp,\r\n parseIpToken,\r\n type Cidr,\r\n type ParsedIp,\r\n} from \"./ipaddr.js\";\r\nimport { CLOUDFLARE_RANGES } from \"./ranges.js\";\r\n\r\n/**\r\n * Comment déterminer l'adresse IP du VISITEUR.\r\n *\r\n * Règle d'or (MDN, RFC 7239, documentations Cloudflare/Vercel/Fly) : on ne croit\r\n * que ce qu'a écrit NOTRE propre proxy. Dans `X-Forwarded-For`, la partie de\r\n * gauche est écrite par le visiteur (donc falsifiable) ; on lit de DROITE à\r\n * GAUCHE en sautant nos proxys.\r\n *\r\n * Aucune valeur par défaut : le développeur DOIT choisir (voir ZarexOptions).\r\n */\r\nexport type IpSource =\r\n | \"socket\" // pas de proxy : l'adresse de la connexion TCP\r\n | \"express\" // Express décide (req.ip + « trust proxy ») — adaptateur Express\r\n | \"framework\" // le framework décide (SvelteKit getClientAddress, Fastify request.ip + trustProxy…) — adaptateurs\r\n | \"auto\" // détecte Vercel / Fly par la VARIABLE D'ENVIRONNEMENT du serveur\r\n | \"cloudflare\" // en-tête CF-Connecting-IP, lu seulement si la connexion vient de Cloudflare\r\n | \"vercel\" // Vercel écrase x-forwarded-for lui-même\r\n | \"fly\" // en-tête Fly-Client-IP\r\n | \"cloudflare-workers\" // Cloudflare Workers : CF-Connecting-IP posé par Cloudflare (pas d'accès direct possible)\r\n | { proxies: number } // nombre de proxys de confiance devant l'application\r\n | { trustedProxies: string[] }; // adresses/plages (CIDR) de vos proxys\r\n\r\nexport interface IpInput {\r\n /** Adresse de la connexion TCP (req.socket.remoteAddress). */\r\n socketIp?: string | undefined;\r\n /** req.ip d'Express (déjà calculé selon « trust proxy »). */\r\n expressIp?: string | undefined;\r\n /** Adresse calculée par le framework (SvelteKit, Fastify…), selon SA configuration de proxys. */\r\n frameworkIp?: string | undefined;\r\n /** Express : « trust proxy » est-il activé ? Sert uniquement au diagnostic. */\r\n expressTrustProxy?: boolean | undefined;\r\n headers: HeaderBag;\r\n}\r\n\r\n/** Problèmes détectés — jamais bloquants (le SDK reste fail-open), toujours signalés au développeur. */\r\nexport type IpAdvice =\r\n | \"forwarded_header_ignored\" // en-tête de proxy présent mais ipSource ne s'en sert pas\r\n | \"not_from_trusted_proxy\" // la connexion ne vient pas du proxy déclaré (accès direct au serveur ?)\r\n | \"chain_too_short\" // moins de proxys que déclaré dans la chaîne\r\n | \"missing_provider_header\" // connexion du fournisseur, mais en-tête client absent/illisible\r\n | \"weak_provider_header\" // l'en-tête privilégié du fournisseur était absent ; repli sur un\r\n // en-tête générique moins garanti (trouvé lors de l'audit du 04/11/2026)\r\n | \"invalid_forwarded_chain\" // chaîne X-Forwarded-For illisible ou trop longue\r\n | \"all_addresses_trusted\" // toute la chaîne est constituée de proxys\r\n | \"no_socket_ip\"; // adresse de connexion inconnue\r\n\r\nexport interface ResolvedIp {\r\n /** Adresse canonique du visiteur ; undefined = inconnue (le SDK laisse passer sans appeler le moteur). */\r\n ip?: string;\r\n advice: IpAdvice[];\r\n}\r\n\r\nexport interface IpResolver {\r\n /** Valeur envoyée au moteur dans `ip_source` (diagnostic). */\r\n label: string;\r\n resolve(input: IpInput): ResolvedIp;\r\n}\r\n\r\nconst MAX_XFF_ENTRIES = 100;\r\nconst MAX_XFF_LENGTH = 8 * 1024;\r\n\r\nfunction readEnv(name: string): string | undefined {\r\n try {\r\n return typeof process !== \"undefined\" ? process.env?.[name] : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction compileCidrs(list: readonly string[], what: string): Cidr[] {\r\n return list.map((c) => {\r\n try {\r\n return parseCidr(c);\r\n } catch (e) {\r\n throw new ZarexConfigError(`Zarex: ${what}: ${(e as Error).message}`);\r\n }\r\n });\r\n}\r\n\r\n/** Liste « 1.2.3.4, 5.6.7.8 » (un ou plusieurs en-têtes) → entrées brutes, ou null si abusive. */\r\nfunction splitChain(raw: string | undefined): string[] | null {\r\n if (raw === undefined || raw.trim() === \"\") return [];\r\n if (raw.length > MAX_XFF_LENGTH) return null;\r\n const parts = raw.split(\",\").map((s) => s.trim());\r\n if (parts.length > MAX_XFF_ENTRIES) return null;\r\n return parts;\r\n}\r\n\r\nfunction socketOf(input: IpInput): ParsedIp | null {\r\n return input.socketIp ? parseIpToken(input.socketIp) : null;\r\n}\r\n\r\nfunction hasForwardedHeader(h: HeaderBag): boolean {\r\n return (\r\n getHeader(h, \"x-forwarded-for\") !== undefined ||\r\n getHeader(h, \"forwarded\") !== undefined ||\r\n getHeader(h, \"x-real-ip\") !== undefined\r\n );\r\n}\r\n\r\n/** Lecture de droite à gauche, en sautant les proxys de confiance (liste de CIDR). */\r\nfunction walkTrusted(input: IpInput, trusted: readonly Cidr[]): ResolvedIp {\r\n const sock = socketOf(input);\r\n if (!sock) return { advice: [\"no_socket_ip\"] };\r\n if (!anyCidrContains(trusted, sock)) {\r\n // La connexion ne vient PAS d'un de nos proxys : l'en-tête n'est pas fiable\r\n // (quelqu'un joint le serveur directement). Seule l'adresse TCP est sûre.\r\n const advice: IpAdvice[] = [\"not_from_trusted_proxy\"];\r\n return { ip: formatIp(sock), advice };\r\n }\r\n const chain = splitChain(getHeader(input.headers, \"x-forwarded-for\"));\r\n if (chain === null) return { advice: [\"invalid_forwarded_chain\"] };\r\n const entries = [...chain.map((e) => ({ raw: e })), { raw: \"\", parsed: sock }];\r\n for (let i = entries.length - 1; i >= 0; i--) {\r\n const e = entries[i]!;\r\n const parsed = \"parsed\" in e && e.parsed ? e.parsed : parseIpToken(e.raw);\r\n if (!parsed) return { advice: [\"invalid_forwarded_chain\"] }; // illisible : on échoue FERMÉ\r\n if (!anyCidrContains(trusted, parsed)) return { ip: formatIp(parsed), advice: [] };\r\n }\r\n return { advice: [\"all_addresses_trusted\"] };\r\n}\r\n\r\nfunction walkCount(input: IpInput, count: number): ResolvedIp {\r\n const sock = socketOf(input);\r\n if (!sock) return { advice: [\"no_socket_ip\"] };\r\n const chain = splitChain(getHeader(input.headers, \"x-forwarded-for\"));\r\n if (chain === null) return { advice: [\"invalid_forwarded_chain\"] };\r\n // chaîne complète = XFF + adresse de connexion ; N proxys → le visiteur est N rangs avant la fin.\r\n const total = chain.length + 1;\r\n if (total < count + 1) {\r\n // Moins de maillons que de proxys déclarés : la requête n'a pas traversé tous\r\n // nos proxys. L'adresse TCP est la seule fiable.\r\n return { ip: formatIp(sock), advice: [\"chain_too_short\"] };\r\n }\r\n const idx = total - 1 - count;\r\n const raw = idx === chain.length ? null : chain[idx]!;\r\n const parsed = raw === null ? sock : parseIpToken(raw);\r\n if (!parsed) return { advice: [\"invalid_forwarded_chain\"] };\r\n return { ip: formatIp(parsed), advice: [] };\r\n}\r\n\r\nfunction singleHeaderIp(input: IpInput, header: string): ResolvedIp | null {\r\n const raw = getHeader(input.headers, header);\r\n if (raw === undefined) return null;\r\n if (raw.includes(\",\")) return null; // une liste n'est pas une adresse unique\r\n const p = parseIpToken(raw);\r\n return p ? { ip: formatIp(p), advice: [] } : null;\r\n}\r\n\r\n/**\r\n * Prépare la lecture de l'IP. Lève ZarexConfigError (au démarrage) si le choix\r\n * est absent, incohérent ou invalide : un oubli doit se voir tout de suite.\r\n */\r\nexport function createIpResolver(source: IpSource | undefined): IpResolver {\r\n if (source === undefined || source === null) {\r\n throw new ZarexConfigError(\r\n \"Zarex: `ipSource` is required. It tells the SDK how to find the visitor's IP:\\n\" +\r\n ' - \"socket\" no proxy in front of your app\\n' +\r\n ' - \"express\" (Express adapter) let Express decide through app.set(\"trust proxy\", …)\\n' +\r\n ' - \"framework\" (SvelteKit / Fastify adapters) let the framework decide\\n' +\r\n ' - \"cloudflare\" | \"vercel\" | \"fly\" | \"cloudflare-workers\" hosted behind / on that provider\\n' +\r\n ' - \"auto\" detect Vercel/Fly from the server environment\\n' +\r\n \" - { proxies: N } N trusted proxies in front of your app\\n\" +\r\n \" - { trustedProxies: [...] } CIDR ranges of your proxies\\n\" +\r\n \"A wrong choice makes every visitor look like one single address (or lets attackers forge theirs).\"\r\n );\r\n }\r\n\r\n if (source === \"socket\") {\r\n return {\r\n label: \"socket\",\r\n resolve(input) {\r\n const sock = socketOf(input);\r\n if (!sock) return { advice: [\"no_socket_ip\"] };\r\n const advice: IpAdvice[] = hasForwardedHeader(input.headers) ? [\"forwarded_header_ignored\"] : [];\r\n return { ip: formatIp(sock), advice };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"express\") {\r\n return {\r\n label: \"express\",\r\n resolve(input) {\r\n const fromExpress = input.expressIp ? parseIpToken(input.expressIp) : null;\r\n const ip = fromExpress ?? socketOf(input);\r\n if (!ip) return { advice: [\"no_socket_ip\"] };\r\n // Express n'a pas été configuré pour les proxys mais un proxy semble présent.\r\n const advice: IpAdvice[] =\r\n input.expressTrustProxy === false && hasForwardedHeader(input.headers) ? [\"forwarded_header_ignored\"] : [];\r\n return { ip: formatIp(ip), advice };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"framework\") {\r\n return {\r\n label: \"framework\",\r\n resolve(input) {\r\n const fromFramework = input.frameworkIp ? parseIpToken(input.frameworkIp) : null;\r\n const ip = fromFramework ?? socketOf(input);\r\n if (!ip) return { advice: [\"no_socket_ip\"] };\r\n return { ip: formatIp(ip), advice: [] };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"auto\") {\r\n // Détection par l'ENVIRONNEMENT du serveur uniquement (un visiteur ne peut pas\r\n // l'influencer). Jamais par des en-têtes de requête : n'importe qui peut en envoyer.\r\n if (readEnv(\"VERCEL\")) return withLabel(createIpResolver(\"vercel\"), \"auto:vercel\");\r\n if (readEnv(\"FLY_APP_NAME\")) return withLabel(createIpResolver(\"fly\"), \"auto:fly\");\r\n throw new ZarexConfigError(\r\n 'Zarex: ipSource \"auto\" could not detect your platform (looked for the VERCEL and FLY_APP_NAME environment variables). ' +\r\n 'Choose explicitly: \"socket\", \"cloudflare\", { proxies: N } or { trustedProxies: [...] }.'\r\n );\r\n }\r\n\r\n if (source === \"cloudflare\") {\r\n const cf = compileCidrs(CLOUDFLARE_RANGES, \"built-in Cloudflare ranges\");\r\n return {\r\n label: \"provider:cloudflare\",\r\n resolve(input) {\r\n const sock = socketOf(input);\r\n if (!sock) return { advice: [\"no_socket_ip\"] };\r\n if (!anyCidrContains(cf, sock)) {\r\n // Pas une adresse Cloudflare : on ne croit pas CF-Connecting-IP (falsifiable).\r\n return { ip: formatIp(sock), advice: [\"not_from_trusted_proxy\"] };\r\n }\r\n const r = singleHeaderIp(input, \"cf-connecting-ip\");\r\n return r ?? { advice: [\"missing_provider_header\"] };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"vercel\") {\r\n // Vercel écrase lui-même X-Forwarded-For et n'y transmet pas d'adresse externe\r\n // (doc Vercel « Request headers »). Valable UNIQUEMENT quand l'application tourne sur Vercel.\r\n //\r\n // `x-vercel-forwarded-for` est le seul en-tête des trois posé EXCLUSIVEMENT par la plateforme\r\n // (jamais par le visiteur) : c'est le seul qu'on accepte SANS avertissement. Les deux autres\r\n // (`x-real-ip`, `x-forwarded-for`) sont des repis : utiles si l'en-tête privilégié est absent\r\n // pour une raison quelconque (CDN/WAF devant Vercel, runtime atypique…), mais on le SIGNALE\r\n // toujours — sans ça, un repli silencieux sur un en-tête potentiellement falsifiable (si on ne\r\n // tourne pas vraiment sur Vercel, par exemple un mauvais choix d'`ipSource`) ne laisserait\r\n // aucune trace. Trouvé lors de l'audit du 04/11/2026 (voir AUDIT_SECURITE_COMPLET.md).\r\n return {\r\n label: \"provider:vercel\",\r\n resolve(input) {\r\n const headersByTrust = [\"x-vercel-forwarded-for\", \"x-real-ip\", \"x-forwarded-for\"];\r\n for (let i = 0; i < headersByTrust.length; i++) {\r\n const h = headersByTrust[i]!;\r\n const raw = getHeader(input.headers, h);\r\n if (raw === undefined) continue;\r\n const last = raw.split(\",\").pop()!; // en cas de liste : le plus proche de la plateforme\r\n const p = parseIpToken(last);\r\n if (p) {\r\n const advice: IpAdvice[] = i === 0 ? [] : [\"weak_provider_header\"];\r\n return { ip: formatIp(p), advice };\r\n }\r\n }\r\n return { advice: [\"missing_provider_header\"] };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"cloudflare-workers\") {\r\n // Un Worker n'est joignable QUE via Cloudflare, qui écrase CF-Connecting-IP. Il n'y a pas d'adresse de\r\n // connexion dans un Worker, d'où l'absence de contrôle de plage. Valable uniquement dans un Worker.\r\n return {\r\n label: \"provider:cloudflare-workers\",\r\n resolve(input) {\r\n return singleHeaderIp(input, \"cf-connecting-ip\") ?? { advice: [\"missing_provider_header\"] };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"fly\") {\r\n return {\r\n label: \"provider:fly\",\r\n resolve(input) {\r\n return singleHeaderIp(input, \"fly-client-ip\") ?? { advice: [\"missing_provider_header\"] };\r\n },\r\n };\r\n }\r\n\r\n if (typeof source === \"object\" && \"proxies\" in source) {\r\n const n = source.proxies;\r\n if (!Number.isInteger(n) || n < 1 || n > 10) {\r\n throw new ZarexConfigError('Zarex: ipSource { proxies: N } needs an integer between 1 and 10 (use \"socket\" for no proxy).');\r\n }\r\n return { label: `proxies:${n}`, resolve: (input) => walkCount(input, n) };\r\n }\r\n\r\n if (typeof source === \"object\" && \"trustedProxies\" in source) {\r\n if (!Array.isArray(source.trustedProxies) || source.trustedProxies.length === 0) {\r\n throw new ZarexConfigError(\"Zarex: ipSource { trustedProxies } needs a non-empty list of IPs or CIDR ranges.\");\r\n }\r\n const trusted = compileCidrs(source.trustedProxies, \"trustedProxies\");\r\n return { label: \"trusted-proxies\", resolve: (input) => walkTrusted(input, trusted) };\r\n }\r\n\r\n throw new ZarexConfigError(\"Zarex: unknown `ipSource` value.\");\r\n}\r\n\r\nfunction withLabel(r: IpResolver, label: string): IpResolver {\r\n return { label, resolve: r.resolve };\r\n}\r\n\r\n/** Utilitaire exporté : vrai si l'adresse est lisible (sert aux adaptateurs). */\r\nexport function isValidIp(s: string): boolean {\r\n return parseIp(s) !== null;\r\n}","/**\r\n * Lecture et comparaison d'adresses IP SANS API Node (le cœur doit tourner dans\r\n * le middleware Next.js / les Workers, où `node:net` n'existe pas).\r\n *\r\n * Choix de sécurité : on est STRICT. Tout ce qui est ambigu est refusé\r\n * (zéros initiaux « 010.0.0.1 » lus en octal par certains logiciels, zone\r\n * « %eth0 », espaces, texte autour). Une IP refusée = « IP inconnue », jamais\r\n * « IP devinée ».\r\n */\r\n\r\nexport interface ParsedIp {\r\n version: 4 | 6;\r\n bytes: Uint8Array; // 4 ou 16 octets\r\n}\r\n\r\nexport function parseIPv4(s: string): Uint8Array | null {\r\n const parts = s.split(\".\");\r\n if (parts.length !== 4) return null;\r\n const out = new Uint8Array(4);\r\n for (let i = 0; i < 4; i++) {\r\n const p = parts[i]!;\r\n if (!/^(0|[1-9][0-9]{0,2})$/.test(p)) return null; // pas de zéro initial, 1 à 3 chiffres\r\n const n = Number(p);\r\n if (n > 255) return null;\r\n out[i] = n;\r\n }\r\n return out;\r\n}\r\n\r\nexport function parseIPv6(s: string): Uint8Array | null {\r\n if (s.length < 2 || s.length > 45 || s.includes(\"%\")) return null;\r\n // Queue IPv4 intégrée (::ffff:1.2.3.4)\r\n let tail: Uint8Array | null = null;\r\n const lastColon = s.lastIndexOf(\":\");\r\n if (lastColon === -1) return null;\r\n const maybeV4 = s.slice(lastColon + 1);\r\n let head = s;\r\n if (maybeV4.includes(\".\")) {\r\n tail = parseIPv4(maybeV4);\r\n if (!tail) return null;\r\n head = s.slice(0, lastColon + 1) + \"0:0\"; // remplace par deux groupes, réécrits ensuite\r\n }\r\n const dbl = head.indexOf(\"::\");\r\n if (dbl !== head.lastIndexOf(\"::\")) return null; // un seul « :: »\r\n const toGroups = (part: string): string[] | null => {\r\n if (part === \"\") return [];\r\n const g = part.split(\":\");\r\n return g.every((x) => /^[0-9a-fA-F]{1,4}$/.test(x)) ? g : null;\r\n };\r\n let groups: string[];\r\n if (dbl !== -1) {\r\n const left = toGroups(head.slice(0, dbl));\r\n const right = toGroups(head.slice(dbl + 2));\r\n if (!left || !right) return null;\r\n const missing = 8 - left.length - right.length;\r\n if (missing < 1) return null; // « :: » doit remplacer au moins un groupe\r\n groups = [...left, ...Array<string>(missing).fill(\"0\"), ...right];\r\n } else {\r\n const all = toGroups(head);\r\n if (!all || all.length !== 8) return null;\r\n groups = all;\r\n }\r\n if (groups.length !== 8) return null;\r\n const out = new Uint8Array(16);\r\n groups.forEach((g, i) => {\r\n const n = parseInt(g, 16);\r\n out[i * 2] = n >> 8;\r\n out[i * 2 + 1] = n & 0xff;\r\n });\r\n if (tail) out.set(tail, 12);\r\n return out;\r\n}\r\n\r\nconst V4_MAPPED_PREFIX = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff];\r\n\r\n/** Lit une adresse seule (sans port, sans crochets). IPv4 mappée en IPv6 → IPv4. */\r\nexport function parseIp(input: string): ParsedIp | null {\r\n if (typeof input !== \"string\" || input.length === 0 || input.length > 45) return null;\r\n if (input.includes(\":\")) {\r\n const b = parseIPv6(input);\r\n if (!b) return null;\r\n if (V4_MAPPED_PREFIX.every((v, i) => b[i] === v)) return { version: 4, bytes: b.slice(12) };\r\n return { version: 6, bytes: b };\r\n }\r\n const b = parseIPv4(input);\r\n return b ? { version: 4, bytes: b } : null;\r\n}\r\n\r\n/**\r\n * Extrait l'adresse d'un jeton d'en-tête tel qu'on le rencontre en pratique :\r\n * « 1.2.3.4 », « 1.2.3.4:5678 », « [2001:db8::1] », « [2001:db8::1]:443 ».\r\n */\r\nexport function parseIpToken(raw: string): ParsedIp | null {\r\n const t = raw.trim();\r\n if (t.length === 0 || t.length > 64) return null;\r\n if (t.startsWith(\"[\")) {\r\n const end = t.indexOf(\"]\");\r\n if (end === -1) return null;\r\n const rest = t.slice(end + 1);\r\n if (rest !== \"\" && !/^:[0-9]{1,5}$/.test(rest)) return null;\r\n return parseIp(t.slice(1, end));\r\n }\r\n const colons = (t.match(/:/g) ?? []).length;\r\n if (colons === 1) {\r\n const [addr, port] = t.split(\":\") as [string, string];\r\n if (!/^[0-9]{1,5}$/.test(port)) return null;\r\n return parseIp(addr);\r\n }\r\n return parseIp(t);\r\n}\r\n\r\n/** Forme canonique : IPv4 en points, IPv6 compressée en minuscules (RFC 5952). */\r\nexport function formatIp(ip: ParsedIp): string {\r\n if (ip.version === 4) return Array.from(ip.bytes).join(\".\");\r\n const g: number[] = [];\r\n for (let i = 0; i < 16; i += 2) g.push((ip.bytes[i]! << 8) | ip.bytes[i + 1]!);\r\n // plus longue suite de zéros (≥ 2 groupes)\r\n let bestStart = -1;\r\n let bestLen = 0;\r\n for (let i = 0; i < 8; ) {\r\n if (g[i] !== 0) {\r\n i++;\r\n continue;\r\n }\r\n let j = i;\r\n while (j < 8 && g[j] === 0) j++;\r\n if (j - i > bestLen) {\r\n bestStart = i;\r\n bestLen = j - i;\r\n }\r\n i = j;\r\n }\r\n if (bestLen < 2) return g.map((x) => x.toString(16)).join(\":\");\r\n const left = g.slice(0, bestStart).map((x) => x.toString(16)).join(\":\");\r\n const right = g.slice(bestStart + bestLen).map((x) => x.toString(16)).join(\":\");\r\n return `${left}::${right}`;\r\n}\r\n\r\n/** Normalise un texte d'IP (avec ou sans port/crochets) ; null si illisible. */\r\nexport function normalizeIp(raw: string | undefined | null): string | null {\r\n if (!raw) return null;\r\n const p = parseIpToken(raw);\r\n return p ? formatIp(p) : null;\r\n}\r\n\r\nexport interface Cidr {\r\n version: 4 | 6;\r\n bytes: Uint8Array;\r\n prefix: number;\r\n}\r\n\r\n/** « 10.0.0.0/8 », « 2400:cb00::/32 », ou une IP seule (= /32 ou /128). Lève Error si invalide. */\r\nexport function parseCidr(text: string): Cidr {\r\n const [addr, len, extra] = text.trim().split(\"/\");\r\n if (extra !== undefined || !addr) throw new Error(`invalid CIDR: ${JSON.stringify(text.slice(0, 64))}`);\r\n const ip = parseIp(addr);\r\n if (!ip) throw new Error(`invalid CIDR address: ${JSON.stringify(text.slice(0, 64))}`);\r\n const max = ip.version === 4 ? 32 : 128;\r\n let prefix = max;\r\n if (len !== undefined) {\r\n if (!/^[0-9]{1,3}$/.test(len)) throw new Error(`invalid CIDR prefix: ${JSON.stringify(text.slice(0, 64))}`);\r\n prefix = Number(len);\r\n if (prefix > max) throw new Error(`invalid CIDR prefix: ${JSON.stringify(text.slice(0, 64))}`);\r\n }\r\n return { version: ip.version, bytes: ip.bytes, prefix };\r\n}\r\n\r\nexport function cidrContains(c: Cidr, ip: ParsedIp): boolean {\r\n if (c.version !== ip.version) return false;\r\n const full = Math.floor(c.prefix / 8);\r\n for (let i = 0; i < full; i++) if (c.bytes[i] !== ip.bytes[i]) return false;\r\n const rem = c.prefix % 8;\r\n if (rem === 0) return true;\r\n const mask = (0xff << (8 - rem)) & 0xff;\r\n return (c.bytes[full]! & mask) === (ip.bytes[full]! & mask);\r\n}\r\n\r\nexport function anyCidrContains(list: readonly Cidr[], ip: ParsedIp): boolean {\r\n return list.some((c) => cidrContains(c, ip));\r\n}","/**\r\n * Plages d'adresses de fournisseurs. Elles CHANGENT : ces listes sont embarquées\r\n * à chaque version du SDK. Pour garder des plages à jour sans attendre une\r\n * version, passez votre propre liste : `ipSource: { trustedProxies: [...] }`.\r\n */\r\n\r\n/** Réseaux privés / locaux : typiquement un proxy ou un répartiteur de charge interne. */\r\nexport const PRIVATE_NETWORKS: readonly string[] = [\r\n \"127.0.0.0/8\",\r\n \"10.0.0.0/8\",\r\n \"172.16.0.0/12\",\r\n \"192.168.0.0/16\",\r\n \"169.254.0.0/16\",\r\n \"100.64.0.0/10\", // CGNAT (RFC 6598)\r\n \"::1/128\",\r\n \"fc00::/7\",\r\n \"fe80::/10\",\r\n];\r\n\r\n/** Plages publiées par Cloudflare (https://www.cloudflare.com/ips-v4 et /ips-v6) — vérifiées le 24/10/2026. */\r\nexport const CLOUDFLARE_RANGES: readonly string[] = [\r\n \"173.245.48.0/20\",\r\n \"103.21.244.0/22\",\r\n \"103.22.200.0/22\",\r\n \"103.31.4.0/22\",\r\n \"141.101.64.0/18\",\r\n \"108.162.192.0/18\",\r\n \"190.93.240.0/20\",\r\n \"188.114.96.0/20\",\r\n \"197.234.240.0/22\",\r\n \"198.41.128.0/17\",\r\n \"162.158.0.0/15\",\r\n \"104.16.0.0/13\",\r\n \"104.24.0.0/14\",\r\n \"172.64.0.0/13\",\r\n \"131.0.72.0/22\",\r\n \"2400:cb00::/32\",\r\n \"2606:4700::/32\",\r\n \"2803:f800::/32\",\r\n \"2405:b500::/32\",\r\n \"2405:8100::/32\",\r\n \"2a06:98c0::/29\",\r\n \"2c0f:f248::/32\",\r\n];","/**\r\n * SHA-256 en JavaScript pur (synchrone, sans dépendance, sans API Node).\r\n * Sert à hacher les « caractéristiques » (identifiant d'utilisateur…) AVANT envoi :\r\n * le moteur n'a besoin que d'une empreinte stable, pas de l'identifiant lui-même.\r\n * Vérifié contre node:crypto dans les tests.\r\n */\r\nconst K = new Uint32Array([\r\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,\r\n 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,\r\n 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,\r\n 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\r\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,\r\n 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\r\n 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\r\n]);\r\n\r\nexport function sha256Hex(text: string): string {\r\n const data = new TextEncoder().encode(text);\r\n const bitLen = data.length * 8;\r\n const padded = new Uint8Array(((data.length + 9 + 63) >> 6) << 6);\r\n padded.set(data);\r\n padded[data.length] = 0x80;\r\n const view = new DataView(padded.buffer);\r\n view.setUint32(padded.length - 8, Math.floor(bitLen / 0x100000000));\r\n view.setUint32(padded.length - 4, bitLen >>> 0);\r\n\r\n const h = new Uint32Array([0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]);\r\n const w = new Uint32Array(64);\r\n const rotr = (x: number, n: number) => (x >>> n) | (x << (32 - n));\r\n\r\n for (let off = 0; off < padded.length; off += 64) {\r\n for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);\r\n for (let i = 16; i < 64; i++) {\r\n const s0 = rotr(w[i - 15]!, 7) ^ rotr(w[i - 15]!, 18) ^ (w[i - 15]! >>> 3);\r\n const s1 = rotr(w[i - 2]!, 17) ^ rotr(w[i - 2]!, 19) ^ (w[i - 2]! >>> 10);\r\n w[i] = (w[i - 16]! + s0 + w[i - 7]! + s1) >>> 0;\r\n }\r\n let [a, b, c, d, e, f, g, hh] = h as unknown as number[] as [number, number, number, number, number, number, number, number];\r\n for (let i = 0; i < 64; i++) {\r\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\r\n const ch = (e & f) ^ (~e & g);\r\n const t1 = (hh + S1 + ch + K[i]! + w[i]!) >>> 0;\r\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\r\n const maj = (a & b) ^ (a & c) ^ (b & c);\r\n const t2 = (S0 + maj) >>> 0;\r\n hh = g;\r\n g = f;\r\n f = e;\r\n e = (d + t1) >>> 0;\r\n d = c;\r\n c = b;\r\n b = a;\r\n a = (t1 + t2) >>> 0;\r\n }\r\n h[0] = (h[0]! + a) >>> 0;\r\n h[1] = (h[1]! + b) >>> 0;\r\n h[2] = (h[2]! + c) >>> 0;\r\n h[3] = (h[3]! + d) >>> 0;\r\n h[4] = (h[4]! + e) >>> 0;\r\n h[5] = (h[5]! + f) >>> 0;\r\n h[6] = (h[6]! + g) >>> 0;\r\n h[7] = (h[7]! + hh) >>> 0;\r\n }\r\n return Array.from(h, (x) => x.toString(16).padStart(8, \"0\")).join(\"\");\r\n}","import { ZarexConfigError, type Algorithm, type Mode, type Oversize, type RuleDTO } from \"@zarex/client\";\r\nimport { NEVER_FORWARD } from \"./headers.js\";\r\n\r\n/** Une règle prête à être envoyée au moteur. Créez-les avec shield(), rateLimit(), tokenBucket(), detectBot(). */\r\nexport type Rule = RuleDTO;\r\n\r\nconst MODES: readonly Mode[] = [\"LIVE\", \"DRY_RUN\", \"OFF\"];\r\nconst ALGOS: readonly Algorithm[] = [\"FIXED_WINDOW\", \"SLIDING_WINDOW\"];\r\nconst WINDOW_RE = /^[1-9][0-9]{0,8}[smhd]$/;\r\nconst NAME_RE = /^[A-Za-z0-9_.:\\-]{1,64}$/;\r\n\r\nfunction mode(fn: string, m: unknown): Mode {\r\n const v = m ?? \"LIVE\";\r\n if (!MODES.includes(v as Mode)) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`mode\\` must be \"LIVE\", \"DRY_RUN\" or \"OFF\".`);\r\n }\r\n return v as Mode;\r\n}\r\n\r\nconst OVERSIZE: readonly Oversize[] = [\"deny\", \"inspect\", \"allow\"];\r\n/** `oversize` : absent = valeur par défaut du moteur (chemin/en-têtes : deny ; corps/texte : inspect). */\r\nfunction oversize(fn: string, v: unknown): { oversize?: Oversize } {\r\n if (v === undefined) return {};\r\n if (!OVERSIZE.includes(v as Oversize)) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`oversize\\` must be \"deny\", \"inspect\" or \"allow\".`);\r\n }\r\n return { oversize: v as Oversize };\r\n}\r\n\r\nfunction int(fn: string, name: string, v: unknown, min: number, max: number): number {\r\n if (typeof v !== \"number\" || !Number.isInteger(v) || v < min || v > max) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`${name}\\` must be an integer between ${min} and ${max}.`);\r\n }\r\n return v;\r\n}\r\n\r\nfunction windowStr(fn: string, name: string, v: unknown): string {\r\n if (typeof v !== \"string\" || !WINDOW_RE.test(v)) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`${name}\\` must look like \"30s\", \"1m\", \"1h\" or \"1d\".`);\r\n }\r\n return v;\r\n}\r\n\r\nfunction names(fn: string, name: string, v: unknown, max: number): string[] {\r\n if (v === undefined) return [];\r\n if (!Array.isArray(v) || v.length > max || !v.every((x) => typeof x === \"string\" && NAME_RE.test(x))) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`${name}\\` must be a list of up to ${max} simple names.`);\r\n }\r\n return [...new Set(v as string[])];\r\n}\r\n\r\nexport interface ShieldOptions {\r\n mode?: Mode;\r\n /**\r\n * Que faire d'un chemin, d'en-têtes ou d'un corps PLUS GRANDS que ce que Zarex inspecte (8 Ko pour le chemin\r\n * et chaque en-tête, 48 Ko pour le corps). \"deny\" : refuser (défaut pour le chemin et les en-têtes : une URL\r\n * de 8 Ko est presque toujours une attaque) · \"inspect\" : analyser le début et la fin et le signaler (défaut\r\n * pour le corps) · \"allow\" : laisser passer. Dans tous les cas, l'avertissement `path_truncated` (etc.) apparaît.\r\n */\r\n oversize?: Oversize;\r\n /**\r\n * Seuil de refus, de 1 à 100. Chaque règle du Shield pèse selon sa certitude (injection SQL `UNION SELECT` : 100 ;\r\n * simple commentaire `--` : 45…) et les règles déclenchées se combinent. Défaut (non renseigné) : TOUTE règle\r\n * déclenchée refuse. Avec `threshold: 80`, un indice faible isolé passe (et le score est signalé dans la raison),\r\n * mais une attaque nette ou plusieurs indices cumulés sont refusés. Augmenter le seuil réduit les faux positifs ET la protection :\r\n * à n'utiliser qu'après avoir observé votre trafic en mode DRY_RUN.\r\n */\r\n threshold?: number;\r\n}\r\n/** Pare-feu applicatif : injections SQL, XSS, traversée de répertoires… */\r\nexport function shield(o: ShieldOptions = {}): Rule {\r\n if (o.threshold !== undefined && (!Number.isInteger(o.threshold) || o.threshold < 1 || o.threshold > 100)) {\r\n throw new ZarexConfigError(\"Zarex: shield: `threshold` must be a whole number between 1 and 100.\");\r\n }\r\n return { shield: { mode: mode(\"shield\", o.mode), ...oversize(\"shield\", o.oversize), ...(o.threshold !== undefined ? { threshold: o.threshold } : {}) } };\r\n}\r\n\r\nexport interface RateLimitOptions {\r\n mode?: Mode;\r\n /** Requêtes autorisées par fenêtre (1 à 1 000 000). */\r\n max: number;\r\n /** Durée de la fenêtre : \"30s\", \"1m\", \"1h\", \"1d\". */\r\n window: string;\r\n algorithm?: Algorithm;\r\n /**\r\n * Ce qui identifie « un visiteur ». Par défaut l'adresse IP.\r\n * Ajoutez ici des noms comme \"userId\" (valeur donnée à protect(…, { characteristics })).\r\n * Recommandé sur les routes sensibles : des opérateurs mobiles font partager une\r\n * même adresse IP à de nombreux utilisateurs.\r\n */\r\n characteristics?: string[];\r\n}\r\nexport function rateLimit(o: RateLimitOptions): Rule {\r\n const algorithm = o.algorithm ?? \"FIXED_WINDOW\";\r\n if (!ALGOS.includes(algorithm)) {\r\n throw new ZarexConfigError('Zarex: rateLimit: `algorithm` must be \"FIXED_WINDOW\" or \"SLIDING_WINDOW\".');\r\n }\r\n return {\r\n rate_limit: {\r\n mode: mode(\"rateLimit\", o.mode),\r\n max: int(\"rateLimit\", \"max\", o.max, 1, 1_000_000),\r\n window: windowStr(\"rateLimit\", \"window\", o.window),\r\n algorithm,\r\n characteristics: names(\"rateLimit\", \"characteristics\", o.characteristics ?? [\"ip\"], 16),\r\n },\r\n };\r\n}\r\n\r\nexport interface TokenBucketOptions {\r\n mode?: Mode;\r\n /** Jetons maximum dans le seau. */\r\n capacity: number;\r\n /** Jetons rendus à chaque intervalle. */\r\n refillRate: number;\r\n /** \"1m\", \"1h\"… */\r\n interval: string;\r\n characteristics?: string[];\r\n}\r\n/** Budget de jetons (ex. appels à un modèle d'IA). Le nombre consommé se donne via protect(…, { tokensRequested }). */\r\nexport function tokenBucket(o: TokenBucketOptions): Rule {\r\n return {\r\n token_bucket: {\r\n mode: mode(\"tokenBucket\", o.mode),\r\n capacity: int(\"tokenBucket\", \"capacity\", o.capacity, 1, 1_000_000),\r\n refill_rate: int(\"tokenBucket\", \"refillRate\", o.refillRate, 1, 1_000_000),\r\n interval: windowStr(\"tokenBucket\", \"interval\", o.interval),\r\n characteristics: names(\"tokenBucket\", \"characteristics\", o.characteristics ?? [\"ip\"], 16),\r\n },\r\n };\r\n}\r\n\r\nexport interface DetectBotOptions {\r\n mode?: Mode;\r\n /** Robots autorisés : \"CATEGORY:SEARCH_ENGINE\", \"GOOGLEBOT\"… */\r\n allow?: string[];\r\n /** Robots à bloquer explicitement. */\r\n deny?: string[];\r\n}\r\nexport function detectBot(o: DetectBotOptions = {}): Rule {\r\n return {\r\n bot_detection: {\r\n mode: mode(\"detectBot\", o.mode),\r\n allow: names(\"detectBot\", \"allow\", o.allow, 64),\r\n deny: names(\"detectBot\", \"deny\", o.deny, 64),\r\n },\r\n };\r\n}\r\n\r\n// ───────────────────────────── Étape 11 ─────────────────────────────\r\n\r\nconst SENSITIVE_TYPES = [\"EMAIL\", \"PHONE\", \"CREDIT_CARD\", \"PASSWORD\", \"SSN\", \"API_KEY\", \"JWT\", \"PRIVATE_KEY\", \"IBAN\", \"HIGH_ENTROPY\"] as const;\r\nexport type SensitiveType = (typeof SENSITIVE_TYPES)[number];\r\nexport { SENSITIVE_TYPES };\r\n\r\nconst EMAIL_DENY = [\"INVALID\", \"DISPOSABLE\", \"NO_MX\", \"FREE\", \"TYPO\"] as const;\r\nconst PHONE_DENY = [\"INVALID\", \"VOIP\", \"FIXED\", \"PREMIUM_RATE\", \"TOLL_FREE\", \"LEGACY_FORMAT\"] as const;\r\nconst INSPECT = [\"body\", \"text_content\"] as const;\r\nconst FILTER_OPS = [\"EQ\", \"NE\", \"CONTAINS\", \"STARTS_WITH\", \"IN\", \"NOT_IN\", \"REGEX\", \"LIKE\", \"LEN_EQ\", \"LEN_GT\", \"LEN_LT\"] as const;\r\nconst REGION_RE = /^(?:[A-Z]{2}|AFRICA)$/;\r\n\r\nfunction oneOf<T extends string>(fn: string, name: string, v: unknown, allowed: readonly T[], max = 64): T[] {\r\n if (v === undefined) return [];\r\n if (!Array.isArray(v) || v.length > max || !v.every((x) => typeof x === \"string\" && (allowed as readonly string[]).includes(x))) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`${name}\\` accepts only: ${allowed.join(\", \")}.`);\r\n }\r\n return [...new Set(v as T[])];\r\n}\r\n\r\nexport interface SensitiveInfoOptions {\r\n mode?: Mode;\r\n /**\r\n * Types à refuser. Défaut : les SECRETS (CREDIT_CARD, SSN, API_KEY, JWT, PRIVATE_KEY, IBAN).\r\n * EMAIL, PHONE et PASSWORD ne sont refusés que si vous les demandez : un formulaire d'inscription ou de\r\n * connexion en contient légitimement.\r\n * HIGH_ENTROPY (sur demande) : valeur aléatoire derrière un mot comme `secret`, `token`, `api_key`, `Bearer`… dont le format\r\n * est inconnu (ex. clé MTN MoMo : 32 caractères hexadécimaux sans préfixe). Plus de faux positifs possibles que API_KEY : essayez d'abord en DRY_RUN.\r\n * API_KEY reconnaît aussi Wave (`wave_sn_prod_…`), Flutterwave (`FLWSECK…`) et Paystack (`sk_live_…`).\r\n */\r\n deny?: SensitiveType[];\r\n /** Où chercher : \"body\" (corps de la requête, nécessite inspectBody) et/ou \"text_content\" (texte donné via call). Défaut : les deux. */\r\n inspect?: (\"body\" | \"text_content\")[];\r\n /** Entrée plus grande que ce qu'on analyse (corps 48 Ko, texte 32 Ko). Défaut \"inspect\" : début + fin analysés, signalé. */\r\n oversize?: Oversize;\r\n}\r\n/** Refuse une requête qui contient des données sensibles (carte bancaire, clé d'API, mot de passe…). */\r\nexport function sensitiveInfo(o: SensitiveInfoOptions = {}): Rule {\r\n return {\r\n sensitive_info: {\r\n mode: mode(\"sensitiveInfo\", o.mode),\r\n deny: oneOf(\"sensitiveInfo\", \"deny\", o.deny, SENSITIVE_TYPES),\r\n inspect: oneOf(\"sensitiveInfo\", \"inspect\", o.inspect, INSPECT),\r\n ...oversize(\"sensitiveInfo\", o.oversize),\r\n },\r\n };\r\n}\r\n\r\nexport interface EmailValidationOptions {\r\n mode?: Mode;\r\n /**\r\n * Défaut : [\"INVALID\", \"DISPOSABLE\", \"NO_MX\"].\r\n * INVALID = mal formée · DISPOSABLE = adresse jetable · NO_MX = le domaine ne reçoit pas de courrier\r\n * · FREE = Gmail/Yahoo… · TYPO = faute probable (« gmial.com »).\r\n */\r\n deny?: (typeof EMAIL_DENY)[number][];\r\n /**\r\n * Domaines (et leurs sous-domaines) JAMAIS jugés jetables / gratuits / sans MX : vos clients connus,\r\n * un domaine absent de la liste mondiale… Une adresse mal formée reste refusée.\r\n */\r\n allowDomains?: string[];\r\n /** Domaines (et sous-domaines) toujours refusés, ex. un concurrent ou un domaine de fraude connu. */\r\n denyDomains?: string[];\r\n}\r\nconst DOMAIN_RE = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/;\r\nfunction domainList(name: string, v: string[] | undefined): string[] | undefined {\r\n if (v === undefined) return undefined;\r\n if (!Array.isArray(v) || v.length > 500) {\r\n throw new ZarexConfigError(`Zarex: emailValidation: \\`${name}\\` must be an array of at most 500 domains.`);\r\n }\r\n const out = v.map((d) => (typeof d === \"string\" ? d.trim().toLowerCase().replace(/^@/, \"\") : \"\"));\r\n if (!out.every((d) => DOMAIN_RE.test(d))) {\r\n throw new ZarexConfigError(`Zarex: emailValidation: \\`${name}\\` must contain valid domain names such as \"societe.bj\".`);\r\n }\r\n return out.length ? [...new Set(out)] : undefined;\r\n}\r\n/** Valide l'adresse donnée via protect(…, { email }). */\r\nexport function emailValidation(o: EmailValidationOptions = {}): Rule {\r\n const allow = domainList(\"allowDomains\", o.allowDomains);\r\n const deny = domainList(\"denyDomains\", o.denyDomains);\r\n return {\r\n email_validation: {\r\n mode: mode(\"emailValidation\", o.mode),\r\n deny: oneOf(\"emailValidation\", \"deny\", o.deny, EMAIL_DENY),\r\n ...(allow ? { allow_domains: allow } : {}),\r\n ...(deny ? { deny_domains: deny } : {}),\r\n },\r\n };\r\n}\r\n\r\nexport interface PhoneValidationOptions {\r\n mode?: Mode;\r\n /** Défaut : [\"INVALID\"]. */\r\n deny?: (typeof PHONE_DENY)[number][];\r\n /** N'accepter que ces opérateurs (ex. [\"MTN\", \"MOOV\"]). Un opérateur inconnu n'est jamais refusé. */\r\n operators?: string[];\r\n /** Pays PAR DÉFAUT pour un numéro écrit sans « + » (ex. \"BJ\"). */\r\n countryCode?: string;\r\n /** Pays acceptés : codes ISO (\"BJ\", \"TG\") ou \"AFRICA\". Vide = tous. */\r\n countries?: string[];\r\n}\r\n/** Valide le numéro donné via protect(…, { phone }). Couvre l'Afrique (mobile et fixe). */\r\nexport function phoneValidation(o: PhoneValidationOptions = {}): Rule {\r\n if (o.countryCode !== undefined && !/^[A-Z]{2}$/.test(o.countryCode)) {\r\n throw new ZarexConfigError('Zarex: phoneValidation: `countryCode` must be a 2-letter ISO code such as \"BJ\".');\r\n }\r\n if (o.countries !== undefined && (!Array.isArray(o.countries) || o.countries.length > 64 || !o.countries.every((c) => typeof c === \"string\" && REGION_RE.test(c)))) {\r\n throw new ZarexConfigError('Zarex: phoneValidation: `countries` must be ISO codes (\"BJ\", \"TG\"…) or \"AFRICA\".');\r\n }\r\n return {\r\n phone_validation: {\r\n mode: mode(\"phoneValidation\", o.mode),\r\n deny: oneOf(\"phoneValidation\", \"deny\", o.deny, PHONE_DENY),\r\n operators: names(\"phoneValidation\", \"operators\", o.operators, 64),\r\n ...(o.countryCode ? { country_code: o.countryCode } : {}),\r\n countries: o.countries ? [...new Set(o.countries)] : [],\r\n },\r\n };\r\n}\r\n\r\nexport interface PromptInjectionOptions {\r\n mode?: Mode;\r\n /** Où chercher : \"text_content\" (défaut, via protect(…, { text })) et/ou \"body\". */\r\n inspect?: (\"body\" | \"text_content\")[];\r\n /** Score de 0 à 1 à partir duquel on refuse (défaut 0.75). Plus bas = plus strict, plus de faux positifs. */\r\n confidenceThreshold?: number;\r\n /** Texte plus grand que ce qu'on analyse (32 Ko). Défaut \"inspect\" : début + fin analysés, signalé. */\r\n oversize?: Oversize;\r\n}\r\n/** Détecte les tentatives d'injection de prompt dans un message destiné à une IA (heuristique, pas une garantie). */\r\nexport function promptInjection(o: PromptInjectionOptions = {}): Rule {\r\n const t = o.confidenceThreshold;\r\n if (t !== undefined && (typeof t !== \"number\" || !(t > 0 && t <= 1))) {\r\n throw new ZarexConfigError(\"Zarex: promptInjection: `confidenceThreshold` must be a number above 0 and at most 1.\");\r\n }\r\n return {\r\n prompt_injection: {\r\n mode: mode(\"promptInjection\", o.mode),\r\n inspect: oneOf(\"promptInjection\", \"inspect\", o.inspect, INSPECT),\r\n ...(t !== undefined ? { confidence_threshold: t } : {}),\r\n ...oversize(\"promptInjection\", o.oversize),\r\n },\r\n };\r\n}\r\n\r\nexport interface FilterCondition {\r\n /**\r\n * \"ip\" · \"path\" · \"method\" · \"host\" · \"headers.<nom>\" · \"query.<nom>\" (paramètre d'URL) · \"cookies.<nom>\"\r\n * · \"ip.country\" (code ISO : \"BJ\", \"SN\"… ; \"ZZ\" = inconnu ou adresse non publique) · \"ip.asn\" (\"AS15169\" ; \"AS0\" = inconnu).\r\n * ip.country / ip.asn exigent une base d'adresses côté moteur (ZAREX_IPDB_FILE) ; sans elle, tout visiteur est « inconnu »\r\n * et un avertissement `ip_db_absent` apparaît dans vos journaux. Opérateurs : EQ, NE, IN, NOT_IN. `cookies.*` demande `inspectCookies: true` (les cookies ne sont sinon pas transmis).\r\n * Un paramètre répété (`?a=1&a=2`) est vu avec toutes ses valeurs ; un champ absent vaut \"\".\r\n */\r\n field: string;\r\n operator: (typeof FILTER_OPS)[number];\r\n /**\r\n * Valeur simple (EQ, NE, CONTAINS, STARTS_WITH, REGEX).\r\n * LIKE : joker `*` (suite quelconque) et `?` (un caractère), ex. \"/admin/*\".\r\n * LEN_EQ / LEN_GT / LEN_LT : un nombre (longueur en caractères), ex. \"2000\".\r\n * Toutes les comparaisons ignorent la casse (pas besoin de « lower »).\r\n */\r\n value?: string;\r\n /** Liste (IN, NOT_IN). Pour le champ « ip », les plages CIDR sont acceptées (\"10.0.0.0/8\"). */\r\n values?: string[];\r\n /** DENY (défaut) = refuser si la condition est vraie · ALLOW = laisser passer (liste blanche). */\r\n action?: \"DENY\" | \"ALLOW\";\r\n /** true = inverse la condition : « NOT (champ opérateur valeur) ». */\r\n not?: boolean;\r\n}\r\nexport interface RequestFilterOptions {\r\n mode?: Mode;\r\n /** Au moins 1, au plus 32. */\r\n conditions: FilterCondition[];\r\n /**\r\n * \"OR\" (défaut) : la première condition vraie décide.\r\n * \"AND\" : toutes doivent être vraies, et l'action de la dernière s'applique.\r\n */\r\n logic?: \"AND\" | \"OR\";\r\n /** Chemin ou en-tête plus grand que 8 Ko. Défaut \"deny\" (l'entrée n'a pas pu être lue en entier). */\r\n oversize?: Oversize;\r\n /**\r\n * Que faire d'une requête qu'AUCUNE condition ne reconnaît ?\r\n * \"ALLOW\" (défaut) : elle passe. \"DENY\" : elle est refusée → mode « tout refuser sauf » :\r\n * listez seulement des conditions `action: \"ALLOW\"` et le reste est bloqué.\r\n * Attention : ALLOW n'exempte PAS des autres règles (Shield, débit…) ; il ne fait que « reconnaître » la requête.\r\n * Les comparaisons ignorent la casse.\r\n */\r\n default?: \"ALLOW\" | \"DENY\";\r\n}\r\n/** Filtre de requêtes selon VOS règles (chemin, méthode, IP, en-têtes…). Le chemin est normalisé avant comparaison. */\r\nexport function requestFilter(o: RequestFilterOptions): Rule {\r\n if (!o || !Array.isArray(o.conditions) || o.conditions.length < 1 || o.conditions.length > 32) {\r\n throw new ZarexConfigError(\"Zarex: requestFilter: `conditions` must contain between 1 and 32 conditions.\");\r\n }\r\n const logic = o.logic ?? \"OR\";\r\n if (logic !== \"AND\" && logic !== \"OR\") throw new ZarexConfigError('Zarex: requestFilter: `logic` must be \"AND\" or \"OR\".');\r\n const expressions = o.conditions.map((c, i) => {\r\n const at = `requestFilter: conditions[${i}]`;\r\n if (!c || typeof c.field !== \"string\" || !/^(?:ip|path|method|host|headers\\.[A-Za-z0-9_-]{1,128}|ip\\.country|ip\\.asn|(?:query|cookies)\\.[A-Za-z0-9_.\\[\\]-]{1,120})$/.test(c.field)) {\r\n throw new ZarexConfigError(`Zarex: ${at}.field must be \"ip\", \"path\", \"method\", \"host\", \"headers.<name>\", \"query.<name>\", \"cookies.<name>\", \"ip.country\" or \"ip.asn\".`);\r\n }\r\n if (!(FILTER_OPS as readonly string[]).includes(c.operator)) {\r\n throw new ZarexConfigError(`Zarex: ${at}.operator must be one of ${FILTER_OPS.join(\", \")}.`);\r\n }\r\n if (c.field.startsWith(\"headers.\") && NEVER_FORWARD.has(c.field.slice(8).toLowerCase())) {\r\n throw new ZarexConfigError(`Zarex: ${at}.field: the \"${c.field.slice(8)}\" header is never sent to the engine (it carries secrets), so a filter on it could never match.`);\r\n }\r\n const action = c.action ?? \"DENY\";\r\n if (action !== \"DENY\" && action !== \"ALLOW\") throw new ZarexConfigError(`Zarex: ${at}.action must be \"DENY\" or \"ALLOW\".`);\r\n const set = c.operator === \"IN\" || c.operator === \"NOT_IN\";\r\n if (c.field === \"ip.country\" || c.field === \"ip.asn\") {\r\n if (![\"EQ\", \"NE\", \"IN\", \"NOT_IN\"].includes(c.operator)) {\r\n throw new ZarexConfigError(`Zarex: ${at}: ${c.field} only supports EQ, NE, IN and NOT_IN.`);\r\n }\r\n const vals = c.operator === \"IN\" || c.operator === \"NOT_IN\" ? c.values ?? [] : [c.value ?? \"\"];\r\n const ok = c.field === \"ip.country\" ? (v: string) => /^[A-Za-z]{2}$/.test(v) : (v: string) => /^(?:[Aa][Ss])?\\d{1,10}$/.test(v) && Number(v.replace(/^[Aa][Ss]/, \"\")) <= 4294967295;\r\n if (!vals.every((v) => typeof v === \"string\" && ok(v))) {\r\n throw new ZarexConfigError(`Zarex: ${at}: ${c.field === \"ip.country\" ? 'use 2-letter country codes such as \"BJ\" (\"ZZ\" = unknown)' : 'use AS numbers such as \"AS15169\" (\"AS0\" = unknown)'}.`);\r\n }\r\n }\r\n if (c.not !== undefined && typeof c.not !== \"boolean\") throw new ZarexConfigError(`Zarex: ${at}.not must be a boolean.`);\r\n if (c.operator.startsWith(\"LEN_\") && !/^\\d{1,7}$/.test(c.value ?? \"\")) {\r\n throw new ZarexConfigError(`Zarex: ${at}: ${c.operator} needs \\`value\\` as a whole number between 0 and 1000000.`);\r\n }\r\n if (c.operator.startsWith(\"LEN_\") && Number(c.value) > 1_000_000) throw new ZarexConfigError(`Zarex: ${at}: ${c.operator} value must be at most 1000000.`);\r\n if (c.value !== undefined && (typeof c.value !== \"string\" || c.value.length > 256)) {\r\n throw new ZarexConfigError(`Zarex: ${at}.value must be a string of at most 256 characters.`);\r\n }\r\n if (c.values !== undefined && (!Array.isArray(c.values) || c.values.length > 64 || !c.values.every((v) => typeof v === \"string\" && v.length <= 256))) {\r\n throw new ZarexConfigError(`Zarex: ${at}.values must be a list of at most 64 strings.`);\r\n }\r\n if (set && !(c.values && c.values.length > 0)) throw new ZarexConfigError(`Zarex: ${at}: ${c.operator} needs \\`values\\`.`);\r\n if (!set && c.operator !== \"EQ\" && c.operator !== \"NE\" && !c.value) throw new ZarexConfigError(`Zarex: ${at}: ${c.operator} needs \\`value\\`.`);\r\n if (c.operator === \"REGEX\") {\r\n try {\r\n new RegExp(c.value ?? \"\");\r\n } catch {\r\n throw new ZarexConfigError(`Zarex: ${at}.value is not a valid regular expression.`);\r\n }\r\n // Le moteur utilise RE2 (temps linéaire, donc sans ReDoS) : pas de lookaround ni de rétro-références.\r\n if (/\\(\\?[=!]|\\(\\?<[=!]|\\\\[1-9]|\\\\k</.test(c.value ?? \"\")) {\r\n throw new ZarexConfigError(`Zarex: ${at}.value uses lookaround or back-references, which the engine does not support (it guarantees linear-time matching).`);\r\n }\r\n }\r\n return { field: c.field, operator: c.operator, ...(c.value !== undefined ? { value: c.value } : {}), ...(c.values ? { values: c.values } : {}), action, ...(c.not ? { not: true } : {}) };\r\n });\r\n if (o.default !== undefined && o.default !== \"ALLOW\" && o.default !== \"DENY\") {\r\n throw new ZarexConfigError('Zarex: requestFilter: `default` must be \"ALLOW\" or \"DENY\".');\r\n }\r\n return {\r\n request_filter: {\r\n mode: mode(\"requestFilter\", o.mode),\r\n expressions,\r\n logic,\r\n ...(o.default === \"DENY\" ? { default: \"DENY\" as const } : {}),\r\n ...oversize(\"requestFilter\", o.oversize),\r\n },\r\n };\r\n}","export { createZarex, validateRules } from \"./protector.js\";\r\nexport type {\r\n Decision,\r\n Logger,\r\n ProtectCall,\r\n ProtectInput,\r\n RuleOutcome,\r\n Zarex,\r\n ZarexOptions,\r\n} from \"./protector.js\";\r\nexport { shield, rateLimit, tokenBucket, detectBot, requestFilter, sensitiveInfo, emailValidation, phoneValidation, promptInjection, SENSITIVE_TYPES } from \"./rules.js\";\r\nexport type {\r\n Rule,\r\n ShieldOptions,\r\n RateLimitOptions,\r\n TokenBucketOptions,\r\n DetectBotOptions,\r\n RequestFilterOptions,\r\n FilterCondition,\r\n SensitiveInfoOptions,\r\n SensitiveType,\r\n EmailValidationOptions,\r\n PhoneValidationOptions,\r\n PromptInjectionOptions,\r\n} from \"./rules.js\";\r\nexport { createIpResolver } from \"./ip.js\";\r\nexport type { IpSource, IpInput, IpAdvice, IpResolver, ResolvedIp } from \"./ip.js\";\r\nexport { CLOUDFLARE_RANGES, PRIVATE_NETWORKS } from \"./ranges.js\";\r\nexport { selectHeaders, selectHeadersDetailed, getHeader, DEFAULT_FORWARDED_HEADERS, NEVER_FORWARD } from \"./headers.js\";\r\nexport type { HeaderBag, ForwardOptions } from \"./headers.js\";\r\nexport { sha256Hex } from \"./sha256.js\";\r\nexport { ZarexClientError, ZarexConfigError } from \"@zarex/client\";\r\nexport type { ClientErrorCode, Verdict, Mode, Algorithm } from \"@zarex/client\";\r\nexport { guardWebRequest, protectWebRequest, denyResponse } from \"./web.js\";\r\nexport { createWebFinalizer } from \"./web.js\";\r\nexport type { WebGuardOptions, WebGuardResult, WebFinalizeOptions } from \"./web.js\";\r\nexport { secureHeaders, prepareSecureHeaders, withSecureHeaders, applyPreparedToResponse, isLocalHost, removesPoweredBy, DEFAULT_SECURE_HEADERS } from \"./secureheaders.js\";\r\nexport type { SecureHeadersOptions, SecureHeaders, PreparedSecureHeaders } from \"./secureheaders.js\";\r\nexport { detectSensitive, maskSensitive, findSensitive, SENSITIVE_ALL } from \"./sensitive.js\";\r\nexport type { SensitiveFindings, SensitiveKind } from \"./sensitive.js\";\r\nexport { guardResponseText, applyGuard, normalizeGuardOptions, notify as notifyResponseGuard, isScannableContentType, DEFAULT_RESPONSE_TYPES } from \"./responseguard.js\";\r\nexport type { ResponseGuardOptions, ResponseGuardEvent, GuardedBody } from \"./responseguard.js\";","/**\r\n * Détection d'informations sensibles dans un texte (cartes, clés d'API, JWT, IBAN…).\r\n *\r\n * C'est le jumeau du package Go internal/sensitive : mêmes types, mêmes règles de validation\r\n * (Luhn, modulo 97…). Un fichier de vecteurs commun (testdata/sensitive_vectors.json) est rejoué\r\n * des deux côtés ; si les deux implémentations divergent, un test échoue.\r\n *\r\n * Sécurité : les expressions ci-dessous ont toutes des répétitions BORNÉES et aucune\r\n * alternative ambiguë imbriquée (pas de ReDoS), et le texte est coupé à MAX_SCAN_CHARS.\r\n */\r\n\r\nexport const SENSITIVE_ALL = [\"EMAIL\", \"PHONE\", \"CREDIT_CARD\", \"PASSWORD\", \"SSN\", \"API_KEY\", \"JWT\", \"PRIVATE_KEY\", \"IBAN\"] as const;\r\nexport type SensitiveKind = (typeof SENSITIVE_ALL)[number];\r\nexport type SensitiveFindings = Partial<Record<SensitiveKind, number>>;\r\n\r\nexport const MAX_SCAN_CHARS = 1_000_000;\r\nconst WS = \"[ \\\\t\\\\n\\\\f\\\\r]\";\r\n\r\nconst reEmail = /[A-Za-z0-9._%+\\-]{1,64}@[A-Za-z0-9\\-]{1,63}(?:\\.[A-Za-z0-9\\-]{1,63})*\\.[A-Za-z]{2,24}/g;\r\nconst rePhone = /(?:\\+|\\b00)[1-9][0-9]{0,2}(?:[ .\\-]?\\(?[0-9]{1,4}\\)?){2,6}/g;\r\nconst reCard = /\\b[0-9](?:[ \\-]?[0-9]){12,18}\\b/g;\r\n// Groupe 1 = la clé et le séparateur (conservés), groupe 2 = la VALEUR (masquée).\r\nconst rePass = new RegExp(\r\n `(\\\\b(?:pass(?:word|wd|phrase)?|pwd|mot[ _\\\\-]?de[ _\\\\-]?passe|mdp|secret|api[_\\\\-]?secret|client[_\\\\-]?secret)[\"']?${WS}*[:=]${WS}*[\"']?)([^ \\\\t\\\\n\\\\f\\\\r\"',;&<>]{4,})`,\r\n \"gi\"\r\n);\r\nconst reSSN = /\\b([0-9]{3})-([0-9]{2})-([0-9]{4})\\b/g;\r\nconst reJWT = /\\beyJ[A-Za-z0-9_\\-]{8,}\\.eyJ[A-Za-z0-9_\\-]{8,}\\.[A-Za-z0-9_\\-]*/g;\r\nconst rePriv = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/g;\r\nconst reIBAN = /\\b[A-Z]{2}[0-9]{2}(?: ?[A-Z0-9]{4}){2,7}(?: ?[A-Z0-9]{1,3})?\\b/g;\r\nconst reKeys: RegExp[] = [\r\n /\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/g,\r\n /\\bgh[pousr]_[A-Za-z0-9]{36,}\\b/g,\r\n /\\bgithub_pat_[A-Za-z0-9_]{22,}\\b/g,\r\n /\\b[sr]k_(?:live|test)_[0-9a-zA-Z]{16,}\\b/g,\r\n /\\bxox[abprs]-[A-Za-z0-9\\-]{10,}\\b/g,\r\n /\\bAIza[0-9A-Za-z_\\-]{35}\\b/g,\r\n /\\bsk-(?:proj-|ant-|svcacct-)?[A-Za-z0-9_\\-]{20,}\\b/g,\r\n /\\bSG\\.[A-Za-z0-9_\\-]{22}\\.[A-Za-z0-9_\\-]{43}\\b/g,\r\n /\\bzarex_(?:live|test)_[0-9a-zA-Z]{16,}\\b/g,\r\n /\\b(?:SK|AC)[0-9a-f]{32}\\b/g,\r\n /\\bwave_sn_(?:prod|AKS)_[A-Za-z0-9_-]{16,}/g, // Wave (clé d'API et secret de signature)\r\n /\\bFLWSECK(?:_TEST)?-[A-Za-z0-9_-]{16,}/g, // Flutterwave (secret ; FLWPUBK volontairement exclu)\r\n];\r\n\r\nexport function luhn(digits: string): boolean {\r\n let sum = 0;\r\n let alt = false;\r\n for (let i = digits.length - 1; i >= 0; i--) {\r\n let d = digits.charCodeAt(i) - 48;\r\n if (alt) {\r\n d *= 2;\r\n if (d > 9) d -= 9;\r\n }\r\n sum += d;\r\n alt = !alt;\r\n }\r\n return sum % 10 === 0;\r\n}\r\n\r\nexport function plausibleCardPrefix(d: string): boolean {\r\n const n = d.length;\r\n if (n < 13 || n > 19) return false;\r\n const c = (i: number) => d.charCodeAt(i) - 48;\r\n const p2 = c(0) * 10 + c(1);\r\n const p4 = p2 * 100 + c(2) * 10 + c(3);\r\n if (c(0) === 4 && (n === 13 || n === 16 || n === 19)) return true;\r\n if (p2 >= 51 && p2 <= 55 && n === 16) return true;\r\n if (p4 >= 2221 && p4 <= 2720 && n === 16) return true;\r\n if ((p2 === 34 || p2 === 37) && n === 15) return true;\r\n if ((p4 === 6011 || p2 === 65 || (p4 >= 6440 && p4 <= 6499)) && (n === 16 || n === 19)) return true;\r\n if (p2 === 62 && n >= 16 && n <= 19) return true;\r\n if ((p2 === 36 || p2 === 38 || p2 === 39 || (p2 === 30 && c(2) >= 0 && c(2) <= 5)) && (n === 14 || n === 16)) return true;\r\n if (p4 >= 3528 && p4 <= 3589 && n >= 16 && n <= 19) return true;\r\n if ((p2 === 50 || (p2 >= 56 && p2 <= 69)) && n >= 12 && n <= 19) return n >= 13;\r\n return false;\r\n}\r\n\r\nexport function mod97(iban: string): boolean {\r\n let s = iban.replace(/ /g, \"\");\r\n if (s.length < 15 || s.length > 34) return false;\r\n s = s.slice(4) + s.slice(0, 4);\r\n let rem = 0;\r\n for (let i = 0; i < s.length; i++) {\r\n const ch = s.charCodeAt(i);\r\n if (ch >= 48 && ch <= 57) rem = (rem * 10 + (ch - 48)) % 97;\r\n else if (ch >= 65 && ch <= 90) rem = (rem * 100 + (ch - 65 + 10)) % 97;\r\n else return false;\r\n }\r\n return rem === 1;\r\n}\r\n\r\nfunction b64urlDecode(s: string): string | undefined {\r\n try {\r\n const t = s.replace(/=+$/, \"\").replace(/-/g, \"+\").replace(/_/g, \"/\");\r\n if (!/^[A-Za-z0-9+/]*$/.test(t)) return undefined;\r\n const padded = t + \"===\".slice((t.length + 3) % 4);\r\n if (typeof atob === \"function\") return atob(padded);\r\n return undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction jwtLooksReal(tok: string): boolean {\r\n const first = tok.split(\".\", 1)[0] ?? \"\";\r\n const s = b64urlDecode(first);\r\n return s !== undefined && s.startsWith(\"{\") && s.includes('\"alg\"');\r\n}\r\n\r\nfunction phoneDigits(s: string): number {\r\n let n = 0;\r\n for (let i = 0; i < s.length; i++) {\r\n const c = s.charCodeAt(i);\r\n if (c >= 48 && c <= 57) n++;\r\n }\r\n return n;\r\n}\r\n\r\n/**\r\n * Caractères invisibles (largeur nulle, trait d'union conditionnel, marqueurs bidi…) : un seul\r\n * caractère de ce type intercalé dans une clé d'API, un JWT ou un IBAN suffisait à échapper\r\n * entièrement à la détection — même défaut, même correctif que côté Go (internal/sensitive,\r\n * trouvé lors de l'audit du 04/11/2026, voir AUDIT_SECURITE_COMPLET.md, C10). Mêmes plages\r\n * Unicode que le jumeau Go.\r\n */\r\nfunction isInvisible(cp: number): boolean {\r\n return (\r\n cp === 0x00ad ||\r\n cp === 0xfeff ||\r\n (cp >= 0x200b && cp <= 0x200f) ||\r\n (cp >= 0x202a && cp <= 0x202e) ||\r\n (cp >= 0x2060 && cp <= 0x2064)\r\n );\r\n}\r\n\r\ninterface Cleaned {\r\n text: string;\r\n // map[i] = position dans le texte D'ORIGINE du caractère cleaned.text[i] ; map[text.length] =\r\n // position de fin. Nécessaire ici (contrairement au Go, qui ne renvoie que des comptes) parce\r\n // que findSensitive renvoie des Span dans le texte d'origine, utilisés pour le masquage : on ne\r\n // peut pas simplement matcher sur une copie nettoyée sans perdre la correspondance des positions.\r\n // undefined = cas courant, rien à retirer : pas de copie ni de tableau (sinon un texte hostile\r\n // de 1 Mo sans aucun caractère invisible paierait le coût d'une copie complète pour rien).\r\n map?: number[];\r\n}\r\n\r\nfunction stripInvisible(s: string): Cleaned {\r\n let has = false;\r\n for (let i = 0; i < s.length; i++) {\r\n if (isInvisible(s.charCodeAt(i))) {\r\n has = true;\r\n break;\r\n }\r\n }\r\n if (!has) return { text: s };\r\n let out = \"\";\r\n const map: number[] = [];\r\n for (let i = 0; i < s.length; i++) {\r\n if (isInvisible(s.charCodeAt(i))) continue;\r\n map.push(i);\r\n out += s[i];\r\n }\r\n map.push(s.length);\r\n return { text: out, map };\r\n}\r\n\r\n/** Position d'une trouvaille dans le texte (sert à masquer). */\r\nexport interface Span {\r\n kind: SensitiveKind;\r\n start: number;\r\n end: number;\r\n}\r\n\r\nconst MAX_MATCHES = 10_000;\r\n\r\nfunction* matches(re: RegExp, text: string): Generator<RegExpExecArray> {\r\n re.lastIndex = 0;\r\n let m: RegExpExecArray | null;\r\n let n = 0;\r\n while ((m = re.exec(text)) !== null) {\r\n yield m;\r\n if (m[0].length === 0) re.lastIndex++; // sécurité : jamais de boucle infinie\r\n if (++n >= MAX_MATCHES) break;\r\n }\r\n}\r\n\r\n/** Toutes les trouvailles VALIDÉES des types demandés (tous si `types` est vide). */\r\nexport function findSensitive(text: string, types?: readonly string[]): Span[] {\r\n if (text.length > MAX_SCAN_CHARS) text = text.slice(0, MAX_SCAN_CHARS);\r\n const want = new Set<string>(types && types.length ? types : SENSITIVE_ALL);\r\n const out: Span[] = [];\r\n const push = (kind: SensitiveKind, m: RegExpExecArray, start = m.index, len = m[0].length) => out.push({ kind, start, end: start + len });\r\n\r\n if (want.has(\"EMAIL\")) for (const m of matches(reEmail, text)) push(\"EMAIL\", m);\r\n if (want.has(\"PHONE\")) {\r\n for (const m of matches(rePhone, text)) {\r\n const d = phoneDigits(m[0]);\r\n if (d >= 8 && d <= 15) push(\"PHONE\", m);\r\n }\r\n }\r\n if (want.has(\"CREDIT_CARD\")) {\r\n for (const m of matches(reCard, text)) {\r\n const d = m[0].replace(/[ -]/g, \"\");\r\n if (plausibleCardPrefix(d) && luhn(d)) push(\"CREDIT_CARD\", m);\r\n }\r\n }\r\n // Détecteurs de SECRETS ci-dessous : matchent sur une copie nettoyée des caractères invisibles\r\n // (voir stripInvisible) — calculée une seule fois, à la demande, pas pour EMAIL/PHONE/CREDIT_CARD.\r\n let cleaned: Cleaned | undefined;\r\n const getCleaned = (): Cleaned => {\r\n if (!cleaned) cleaned = stripInvisible(text);\r\n return cleaned;\r\n };\r\n const pushClean = (kind: SensitiveKind, map: number[] | undefined, m: RegExpExecArray, start = m.index, len = m[0].length) =>\r\n out.push(map ? { kind, start: map[start]!, end: map[start + len]! } : { kind, start, end: start + len });\r\n\r\n if (want.has(\"PASSWORD\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(rePass, ct)) {\r\n const prefix = m[1] ?? \"\";\r\n pushClean(\"PASSWORD\", map, m, m.index + prefix.length, (m[2] ?? \"\").length); // seule la valeur est masquée\r\n }\r\n }\r\n if (want.has(\"SSN\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(reSSN, ct)) {\r\n const [a, g, s] = [m[1]!, m[2]!, m[3]!];\r\n if (a === \"000\" || a === \"666\" || a[0] === \"9\" || g === \"00\" || s === \"0000\") continue;\r\n pushClean(\"SSN\", map, m);\r\n }\r\n }\r\n if (want.has(\"API_KEY\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const re of reKeys) for (const m of matches(re, ct)) pushClean(\"API_KEY\", map, m);\r\n }\r\n if (want.has(\"JWT\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(reJWT, ct)) if (jwtLooksReal(m[0])) pushClean(\"JWT\", map, m);\r\n }\r\n if (want.has(\"PRIVATE_KEY\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(rePriv, ct)) pushClean(\"PRIVATE_KEY\", map, m);\r\n }\r\n if (want.has(\"IBAN\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(reIBAN, ct)) if (mod97(m[0])) pushClean(\"IBAN\", map, m);\r\n }\r\n return out;\r\n}\r\n\r\n/** Compte par type — même résultat que Detect() côté Go. Ne renvoie jamais les valeurs. */\r\nexport function detectSensitive(text: string, types?: readonly string[]): SensitiveFindings {\r\n const f: SensitiveFindings = {};\r\n for (const s of findSensitive(text, types)) f[s.kind] = (f[s.kind] ?? 0) + 1;\r\n return f;\r\n}\r\n\r\n/** Remplace les trouvailles par `[REDACTED:TYPE]` (la valeur d'un mot de passe seulement : `password=[REDACTED:PASSWORD]`). */\r\nexport function maskSensitive(text: string, types?: readonly string[]): { text: string; findings: SensitiveFindings } {\r\n const spans = findSensitive(text, types).sort((a, b) => a.start - b.start || b.end - a.end);\r\n const findings: SensitiveFindings = {};\r\n if (spans.length === 0) return { text, findings };\r\n let out = \"\";\r\n let pos = 0;\r\n for (const s of spans) {\r\n findings[s.kind] = (findings[s.kind] ?? 0) + 1;\r\n if (s.start < pos) {\r\n // chevauchement (ex. un numéro vu à la fois comme carte et téléphone) : on étend la zone masquée\r\n if (s.end > pos) pos = s.end;\r\n continue;\r\n }\r\n out += text.slice(pos, s.start) + `[REDACTED:${s.kind}]`;\r\n pos = s.end;\r\n }\r\n out += text.slice(pos);\r\n return { text: out, findings };\r\n}","import { SENSITIVE_ALL, findSensitive, maskSensitive, type SensitiveFindings, type SensitiveKind } from \"./sensitive.js\";\r\n\r\n/**\r\n * Surveillance des RÉPONSES de l'application : empêche une fuite accidentelle de données\r\n * sensibles (carte bancaire, clé d'API, JWT, mot de passe…) vers le visiteur.\r\n *\r\n * Tout se passe dans le SDK, sur votre serveur : le contenu de vos réponses n'est jamais envoyé à Zarex.\r\n *\r\n * Par défaut EMAIL et PHONE ne sont PAS surveillés : une application renvoie légitimement\r\n * l'e-mail de l'utilisateur connecté. Ajoutez-les via `types` si vous le souhaitez.\r\n */\r\nexport interface ResponseGuardOptions {\r\n /** \"mask\" (défaut) remplace par [REDACTED:TYPE] · \"block\" renvoie une erreur 500 générique · \"report\" ne change rien, signale seulement. */\r\n mode?: \"mask\" | \"block\" | \"report\";\r\n /** Types surveillés. Défaut : CREDIT_CARD PASSWORD SSN API_KEY JWT PRIVATE_KEY IBAN. */\r\n types?: SensitiveKind[];\r\n /** Réponses plus grandes que ceci (octets, défaut 1 Mo) : non analysées (laissées telles quelles) et signalées. */\r\n maxBytes?: number;\r\n /**\r\n * Appelé à chaque fuite détectée (ou réponse non analysée). Ne reçoit JAMAIS les valeurs : seulement types et nombres.\r\n * Une erreur dans ce rappel est ignorée.\r\n */\r\n onFinding?: (event: ResponseGuardEvent) => void;\r\n}\r\n\r\nexport interface ResponseGuardEvent {\r\n // skipped_unscannable : format TEXTE en principe analysable mais hors de ce que ce garde sait\r\n // traiter aujourd'hui (flux SSE, corps compressé) — contrairement à un contenu binaire (image…),\r\n // jamais dans son périmètre, qui ne déclenche aucun événement. Trouvé lors de l'audit du\r\n // 04/11/2026 : ces deux cas étaient ignorés en silence (voir web.ts, guardWebResponse).\r\n action: \"masked\" | \"blocked\" | \"reported\" | \"skipped_too_large\" | \"skipped_unscannable\";\r\n findings: SensitiveFindings;\r\n /** Type de contenu de la réponse. */\r\n contentType: string;\r\n}\r\n\r\nexport const DEFAULT_RESPONSE_TYPES: SensitiveKind[] = [\"CREDIT_CARD\", \"PASSWORD\", \"SSN\", \"API_KEY\", \"JWT\", \"PRIVATE_KEY\", \"IBAN\"];\r\nconst DEFAULT_MAX_BYTES = 1_000_000;\r\nconst SCANNABLE = /^(?:text\\/|application\\/(?:json|xml|javascript|x-www-form-urlencoded|graphql)|application\\/[a-z0-9.+-]*\\+(?:json|xml))/i;\r\nconst JSON_TYPE = /^application\\/(?:json|[a-z0-9.+-]*\\+json)/i;\r\nconst PASS_KEY = /^(?:pass(?:word|wd|phrase)?|pwd|mot[ _-]?de[ _-]?passe|mdp|secret|api[_-]?secret|client[_-]?secret)$/i;\r\nconst MAX_DEPTH = 64;\r\nconst MAX_NODES = 200_000;\r\n\r\n/** true si ce type de contenu peut contenir du texte à surveiller (pas les images, PDF, flux binaires). */\r\nexport function isScannableContentType(ct: string | undefined | null): boolean {\r\n return !!ct && SCANNABLE.test(ct.trim());\r\n}\r\n\r\nexport interface GuardedBody {\r\n action: \"pass\" | \"masked\" | \"blocked\" | \"reported\" | \"skipped_too_large\";\r\n body: string;\r\n findings: SensitiveFindings;\r\n}\r\n\r\nfunction mergeCounts(into: SensitiveFindings, from: SensitiveFindings): void {\r\n for (const [k, v] of Object.entries(from)) into[k as SensitiveKind] = (into[k as SensitiveKind] ?? 0) + (v ?? 0);\r\n}\r\n\r\n// Masque les chaînes (et les nombres) d'un JSON déjà analysé. Le résultat reste du JSON valide.\r\nfunction maskJsonValue(v: unknown, types: readonly string[], found: SensitiveFindings, budget: { nodes: number }, depth: number): unknown {\r\n if (depth > MAX_DEPTH || ++budget.nodes > MAX_NODES) return v;\r\n if (typeof v === \"string\") {\r\n const r = maskSensitive(v, types);\r\n mergeCounts(found, r.findings);\r\n return r.text;\r\n }\r\n if (typeof v === \"number\") {\r\n // Un numéro de carte peut être écrit sans guillemets : 4111111111111111\r\n if (Number.isFinite(v) && Math.abs(v) >= 1e11 && Number.isInteger(v)) {\r\n const spans = findSensitive(String(v), types);\r\n if (spans.length) {\r\n for (const s of spans) found[s.kind] = (found[s.kind] ?? 0) + 1;\r\n return \"[REDACTED]\";\r\n }\r\n }\r\n return v;\r\n }\r\n if (Array.isArray(v)) return v.map((x) => maskJsonValue(x, types, found, budget, depth + 1));\r\n if (v && typeof v === \"object\") {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, x] of Object.entries(v as Record<string, unknown>)) {\r\n // En JSON la clé et la valeur sont séparées : {\"password\":\"abc\"} n'est pas visible en analysant la valeur seule.\r\n // Pas de restriction de caractères ici (contrairement à rePass dans sensitive.ts, qui en a besoin\r\n // comme terminateur de valeur en texte libre) : la structure JSON délimite déjà la valeur entière\r\n // (entre guillemets), donc un mot de passe/passphrase avec un espace (\"correct horse battery staple\")\r\n // doit être masqué comme n'importe quel autre — trouvé lors de l'audit du 04/11/2026, voir\r\n // AUDIT_SECURITE_COMPLET.md : cette exclusion laissait fuiter en clair tout mot de passe à espaces.\r\n if (types.includes(\"PASSWORD\") && PASS_KEY.test(k) && typeof x === \"string\" && x.length >= 4) {\r\n found.PASSWORD = (found.PASSWORD ?? 0) + 1;\r\n out[k] = \"[REDACTED:PASSWORD]\";\r\n } else out[k] = maskJsonValue(x, types, found, budget, depth + 1);\r\n }\r\n return out;\r\n }\r\n return v;\r\n}\r\n\r\n/**\r\n * Analyse un corps de réponse TEXTE. Ne lève jamais : en cas de problème, le corps d'origine est rendu tel quel.\r\n * (Le corps d'origine n'est modifié que s'il contient réellement des données sensibles.)\r\n */\r\nexport function guardResponseText(text: string, contentType: string, options: ResponseGuardOptions = {}): GuardedBody {\r\n try {\r\n const mode = options.mode ?? \"mask\";\r\n const types = options.types?.length ? options.types : DEFAULT_RESPONSE_TYPES;\r\n const max = options.maxBytes ?? DEFAULT_MAX_BYTES;\r\n if (text.length > max) return { action: \"skipped_too_large\", body: text, findings: {} };\r\n\r\n const spans = findSensitive(text, types);\r\n if (spans.length === 0) return { action: \"pass\", body: text, findings: {} };\r\n const findings: SensitiveFindings = {};\r\n for (const s of spans) findings[s.kind] = (findings[s.kind] ?? 0) + 1;\r\n\r\n if (mode === \"report\") return { action: \"reported\", body: text, findings };\r\n if (mode === \"block\") {\r\n return {\r\n action: \"blocked\",\r\n body: JSON.stringify({ error: { code: \"response_blocked\", message: \"The response was withheld because it contained sensitive data\" } }),\r\n findings,\r\n };\r\n }\r\n\r\n if (JSON_TYPE.test(contentType)) {\r\n try {\r\n const parsed: unknown = JSON.parse(text);\r\n const found: SensitiveFindings = {};\r\n const masked = JSON.stringify(maskJsonValue(parsed, types, found, { nodes: 0 }, 0));\r\n // Ceinture et bretelles : si quelque chose reste visible (cas non prévu), on masque le texte brut\r\n // plutôt que de laisser fuiter (le JSON peut alors devenir invalide, mais rien ne fuit).\r\n if (findSensitive(masked, types).filter((sp) => !masked.startsWith(\"[REDACTED\", sp.start)).length === 0) {\r\n return { action: \"masked\", body: masked, findings: Object.keys(found).length ? found : findings };\r\n }\r\n const r2 = maskSensitive(masked, types);\r\n return { action: \"masked\", body: r2.text, findings };\r\n } catch {\r\n /* JSON illisible : on masque comme du texte */\r\n }\r\n }\r\n const r = maskSensitive(text, types);\r\n return { action: \"masked\", body: r.text, findings: r.findings };\r\n } catch {\r\n return { action: \"pass\", body: text, findings: {} };\r\n }\r\n}\r\n\r\nexport function notify(options: ResponseGuardOptions, event: ResponseGuardEvent): void {\r\n try {\r\n options.onFinding?.(event);\r\n } catch {\r\n /* le rappel du développeur ne doit jamais casser une réponse */\r\n }\r\n}\r\n\r\nexport { SENSITIVE_ALL };\r\n\r\nexport function normalizeGuardOptions(o: ResponseGuardOptions | boolean | undefined): ResponseGuardOptions | undefined {\r\n if (!o) return undefined;\r\n const g = o === true ? {} : o;\r\n if (g.mode !== undefined && ![\"mask\", \"block\", \"report\"].includes(g.mode)) throw new Error('Zarex: responseGuard.mode must be \"mask\", \"block\" or \"report\".');\r\n if (g.maxBytes !== undefined && (!Number.isInteger(g.maxBytes) || g.maxBytes < 1 || g.maxBytes > 16_000_000)) throw new Error(\"Zarex: responseGuard.maxBytes must be an integer between 1 and 16000000.\");\r\n for (const t of g.types ?? []) if (!(SENSITIVE_ALL as readonly string[]).includes(t)) throw new Error(`Zarex: responseGuard.types: unknown type \"${String(t).slice(0, 20)}\".`);\r\n return g;\r\n}\r\n\r\n/** Analyse + signalement (onFinding). Sert aux adaptateurs. */\r\nexport function applyGuard(text: string, contentType: string, options: ResponseGuardOptions): GuardedBody {\r\n const r = guardResponseText(text, contentType, options);\r\n if (r.action !== \"pass\") {\r\n notify(options, { action: r.action === \"skipped_too_large\" ? \"skipped_too_large\" : r.action, findings: r.findings, contentType });\r\n }\r\n return r;\r\n}","/**\r\n * En-têtes de sécurité HTTP, calculés par le SDK lui-même (aucun appel réseau).\r\n *\r\n * Défauts prudents : ce qu'on ajoute ne doit pas casser un site qui marche.\r\n * - Content-Security-Policy : DÉSACTIVÉ par défaut (une CSP mal réglée casse un site entier).\r\n * Utilisez d'abord `contentSecurityPolicyReportOnly`, puis passez en mode strict.\r\n * - Strict-Transport-Security : 6 mois, sans includeSubDomains ni preload (ces deux options\r\n * sont difficiles à annuler : à activer en connaissance de cause).\r\n */\r\n\r\nexport interface SecureHeadersOptions {\r\n /** Strict-Transport-Security. `true` = 6 mois. Défaut : true. Ignoré si la requête n'est pas en HTTPS. */\r\n hsts?: boolean | { maxAgeSeconds?: number; includeSubDomains?: boolean; preload?: boolean };\r\n /** Content-Security-Policy complète (chaîne). Défaut : absente. */\r\n contentSecurityPolicy?: string | false;\r\n /** Même chose en mode « rapport seulement » : le navigateur signale sans bloquer. */\r\n contentSecurityPolicyReportOnly?: string | false;\r\n /** X-Frame-Options. Défaut : \"DENY\". */\r\n frameOptions?: \"DENY\" | \"SAMEORIGIN\" | false;\r\n /** X-Content-Type-Options: nosniff. Défaut : true. */\r\n contentTypeOptions?: boolean;\r\n /** Referrer-Policy. Défaut : \"strict-origin-when-cross-origin\". */\r\n referrerPolicy?: string | false;\r\n /** Permissions-Policy. Défaut : caméra, micro et géolocalisation désactivés. */\r\n permissionsPolicy?: string | false;\r\n /** Cross-Origin-Opener-Policy. Défaut : \"same-origin\". */\r\n crossOriginOpenerPolicy?: \"same-origin\" | \"same-origin-allow-popups\" | \"unsafe-none\" | false;\r\n /** Cross-Origin-Resource-Policy. Défaut : absente (peut casser les images/scripts partagés entre domaines). */\r\n crossOriginResourcePolicy?: \"same-origin\" | \"same-site\" | \"cross-origin\" | false;\r\n /** Supprimer X-Powered-By (révèle le framework). Défaut : true. */\r\n removePoweredBy?: boolean;\r\n}\r\n\r\nexport type SecureHeaders = Record<string, string>;\r\n\r\nexport const DEFAULT_SECURE_HEADERS: Readonly<Required<Pick<SecureHeadersOptions, \"frameOptions\" | \"contentTypeOptions\" | \"referrerPolicy\" | \"permissionsPolicy\" | \"crossOriginOpenerPolicy\">>> = {\r\n frameOptions: \"DENY\",\r\n contentTypeOptions: true,\r\n referrerPolicy: \"strict-origin-when-cross-origin\",\r\n permissionsPolicy: \"camera=(), microphone=(), geolocation=()\",\r\n crossOriginOpenerPolicy: \"same-origin\",\r\n};\r\n\r\nconst DEFAULT_HSTS_SECONDS = 15_552_000; // 180 jours\r\nconst MAX_HSTS_SECONDS = 63_072_000; // 2 ans\r\nconst MAX_VALUE_LEN = 4096;\r\n\r\n// Aucun saut de ligne ni caractère de contrôle : sinon une valeur venant d'une variable\r\n// d'environnement ou d'un fichier pourrait injecter un en-tête supplémentaire (CRLF injection).\r\nfunction safeValue(name: string, v: string): string {\r\n if (typeof v !== \"string\" || v.length === 0 || v.length > MAX_VALUE_LEN || /[\\u0000-\\u001f\\u007f]/.test(v)) {\r\n throw new Error(`Zarex: secureHeaders: the value of \\`${name}\\` is empty, too long, or contains control characters.`);\r\n }\r\n return v;\r\n}\r\n\r\n/**\r\n * Calcule les en-têtes à ajouter. Lève une erreur de configuration claire au démarrage\r\n * si une option est invalide (à appeler une fois, pas à chaque requête).\r\n */\r\nexport function secureHeaders(options: SecureHeadersOptions = {}, ctx: { https?: boolean } = {}): SecureHeaders {\r\n const o = options ?? {};\r\n const out: SecureHeaders = {};\r\n\r\n const hsts = o.hsts ?? true;\r\n if (hsts && ctx.https !== false) {\r\n const cfg = typeof hsts === \"object\" ? hsts : {};\r\n const age = cfg.maxAgeSeconds ?? DEFAULT_HSTS_SECONDS;\r\n if (!Number.isInteger(age) || age < 0 || age > MAX_HSTS_SECONDS) {\r\n throw new Error(`Zarex: secureHeaders: hsts.maxAgeSeconds must be an integer between 0 and ${MAX_HSTS_SECONDS}.`);\r\n }\r\n if (cfg.preload && (!cfg.includeSubDomains || age < 31_536_000)) {\r\n throw new Error(\"Zarex: secureHeaders: hsts.preload requires includeSubDomains and at least one year (31536000 s), otherwise browsers' preload lists reject the site.\");\r\n }\r\n out[\"Strict-Transport-Security\"] = `max-age=${age}${cfg.includeSubDomains ? \"; includeSubDomains\" : \"\"}${cfg.preload ? \"; preload\" : \"\"}`;\r\n }\r\n\r\n if (o.contentSecurityPolicy) out[\"Content-Security-Policy\"] = safeValue(\"contentSecurityPolicy\", o.contentSecurityPolicy);\r\n if (o.contentSecurityPolicyReportOnly) out[\"Content-Security-Policy-Report-Only\"] = safeValue(\"contentSecurityPolicyReportOnly\", o.contentSecurityPolicyReportOnly);\r\n\r\n const frame = o.frameOptions === undefined ? DEFAULT_SECURE_HEADERS.frameOptions : o.frameOptions;\r\n if (frame) {\r\n if (frame !== \"DENY\" && frame !== \"SAMEORIGIN\") throw new Error('Zarex: secureHeaders: frameOptions must be \"DENY\", \"SAMEORIGIN\" or false.');\r\n out[\"X-Frame-Options\"] = frame;\r\n }\r\n if (o.contentTypeOptions ?? DEFAULT_SECURE_HEADERS.contentTypeOptions) out[\"X-Content-Type-Options\"] = \"nosniff\";\r\n\r\n const ref = o.referrerPolicy === undefined ? DEFAULT_SECURE_HEADERS.referrerPolicy : o.referrerPolicy;\r\n if (ref) out[\"Referrer-Policy\"] = safeValue(\"referrerPolicy\", ref);\r\n const perm = o.permissionsPolicy === undefined ? DEFAULT_SECURE_HEADERS.permissionsPolicy : o.permissionsPolicy;\r\n if (perm) out[\"Permissions-Policy\"] = safeValue(\"permissionsPolicy\", perm);\r\n\r\n const coop = o.crossOriginOpenerPolicy === undefined ? DEFAULT_SECURE_HEADERS.crossOriginOpenerPolicy : o.crossOriginOpenerPolicy;\r\n if (coop) {\r\n if (![\"same-origin\", \"same-origin-allow-popups\", \"unsafe-none\"].includes(coop)) throw new Error(\"Zarex: secureHeaders: invalid crossOriginOpenerPolicy.\");\r\n out[\"Cross-Origin-Opener-Policy\"] = coop;\r\n }\r\n if (o.crossOriginResourcePolicy) {\r\n if (![\"same-origin\", \"same-site\", \"cross-origin\"].includes(o.crossOriginResourcePolicy)) throw new Error(\"Zarex: secureHeaders: invalid crossOriginResourcePolicy.\");\r\n out[\"Cross-Origin-Resource-Policy\"] = o.crossOriginResourcePolicy;\r\n }\r\n return out;\r\n}\r\n\r\n/** true si l'option demande de retirer X-Powered-By (défaut). */\r\nexport function removesPoweredBy(options: SecureHeadersOptions | undefined): boolean {\r\n return options?.removePoweredBy !== false;\r\n}\r\n\r\n/**\r\n * Prépare les en-têtes une fois (au démarrage) : renvoie deux jeux, pour requête HTTPS et non-HTTPS\r\n * (HSTS n'a de sens que sur HTTPS ; navigateurs l'ignorent sinon).\r\n */\r\nexport function prepareSecureHeaders(options: SecureHeadersOptions | boolean | undefined): { https: SecureHeaders; http: SecureHeaders; removePoweredBy: boolean } | undefined {\r\n if (!options) return undefined;\r\n const o = options === true ? {} : options;\r\n return { https: secureHeaders(o, { https: true }), http: secureHeaders(o, { https: false }), removePoweredBy: removesPoweredBy(o) };\r\n}\r\n\r\nexport type PreparedSecureHeaders = NonNullable<ReturnType<typeof prepareSecureHeaders>>;\r\n\r\n/**\r\n * HSTS sur localhost : un navigateur qui voit « Strict-Transport-Security » sur https://localhost\r\n * mémorise ce choix pour des mois et refuse ensuite http://localhost. On ne l'envoie donc pas en local.\r\n */\r\nexport function isLocalHost(host: string | undefined | null): boolean {\r\n if (!host) return false;\r\n const h = host.toLowerCase().replace(/:\\d+$/, \"\").replace(/^\\[|\\]$/g, \"\");\r\n return h === \"localhost\" || h.endsWith(\".localhost\") || h === \"127.0.0.1\" || h === \"::1\";\r\n}\r\n\r\n/** Ajoute des en-têtes préparés à une Response standard (jamais d'écrasement d'un en-tête posé par l'application). */\r\nexport function applyPreparedToResponse(response: Response, prepared: PreparedSecureHeaders, host?: string | null): Response {\r\n try {\r\n const set = isLocalHost(host) ? prepared.http : prepared.https;\r\n const headers = new Headers(response.headers);\r\n for (const [k, v] of Object.entries(set)) if (!headers.has(k)) headers.set(k, v);\r\n if (prepared.removePoweredBy) headers.delete(\"x-powered-by\");\r\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers });\r\n } catch {\r\n return response; // jamais casser la réponse de l'application (ex. statut 101 d'un WebSocket)\r\n }\r\n}\r\n\r\n/** Version simple : prépare puis applique. Pour un usage répété, préférez prepareSecureHeaders() une fois au démarrage. */\r\nexport function withSecureHeaders(response: Response, options: SecureHeadersOptions | boolean | undefined, ctx: { host?: string | null } = {}): Response {\r\n const prepared = prepareSecureHeaders(options);\r\n return prepared ? applyPreparedToResponse(response, prepared, ctx.host) : response;\r\n}","import type { Decision, ProtectCall, Zarex } from \"./protector.js\";\r\nimport { applyGuard, isScannableContentType, normalizeGuardOptions, notify, type ResponseGuardOptions } from \"./responseguard.js\";\r\nimport { applyPreparedToResponse, prepareSecureHeaders, type SecureHeadersOptions } from \"./secureheaders.js\";\r\n\r\n/**\r\n * Outils communs aux adaptateurs « fetch standard » (Next.js, Bun, SvelteKit, Workers…).\r\n * N'utilise que `Request` / `Response` / `URL` : aucune API propre à Node.\r\n */\r\nexport interface WebGuardOptions {\r\n /** Adresse de la connexion TCP, si le runtime la donne (Bun : server.requestIP). Absente en edge. */\r\n socketIp?: string | undefined;\r\n /** Adresse calculée par le framework (SvelteKit : getClientAddress()). */\r\n frameworkIp?: string | undefined;\r\n /** Transmettre le corps au Shield (défaut false). Ignoré si le corps est gros, inconnu ou non textuel. */\r\n inspectBody?: boolean;\r\n /** Identifiants personnalisés / jetons pour CETTE requête. */\r\n call?: ProtectCall | undefined;\r\n /** Réponse de refus personnalisée. */\r\n onDeny?: (request: Request, decision: Decision) => Response | Promise<Response>;\r\n /** Appelé quand le moteur n'a pas pu décider (la requête passe). */\r\n onDegraded?: (request: Request, decision: Decision) => void;\r\n}\r\n\r\nexport interface WebGuardResult {\r\n decision: Decision | undefined;\r\n /** Défini seulement si la requête doit être refusée. */\r\n deny?: Response;\r\n}\r\n\r\nconst BODY_LIMIT_BYTES = 48 * 1024;\r\nconst TEXTUAL = /^(text\\/|application\\/(json|x-www-form-urlencoded|xml|graphql)|application\\/[a-z0-9.+-]*\\+(json|xml))/i;\r\n\r\n/** Réponse de refus par défaut : générique, sans détail interne. */\r\nexport function denyResponse(d: Decision): Response {\r\n const limited = d.deniedBy?.ruleType === \"RATE_LIMIT\" || d.deniedBy?.ruleType === \"TOKEN_BUCKET\";\r\n const headers: Record<string, string> = { \"content-type\": \"application/json; charset=utf-8\", \"cache-control\": \"no-store\" };\r\n if (limited) {\r\n if (d.retryAfterSeconds !== undefined) headers[\"retry-after\"] = String(d.retryAfterSeconds);\r\n return new Response(JSON.stringify({ error: { code: \"rate_limited\", message: \"Too many requests\", request_id: d.id } }), { status: 429, headers });\r\n }\r\n return new Response(JSON.stringify({ error: { code: \"forbidden\", message: \"Request blocked\", request_id: d.id } }), { status: 403, headers });\r\n}\r\n\r\n// readStreamLimited : lit un flux par morceaux et s'arrête dès que la taille RÉELLE lue dépasse max —\r\n// contrairement à une vérification du seul Content-Length déclaré (une promesse du visiteur, pas une\r\n// garantie), ceci borne vraiment la mémoire utilisée. Partagée par readBodySafely (requêtes) et\r\n// readLimited (réponses) ci-dessous.\r\nasync function readStreamLimited(body: ReadableStream<Uint8Array> | null, maxBytes: number): Promise<string | null> {\r\n if (!body) return \"\";\r\n const reader = body.getReader();\r\n const chunks: Uint8Array[] = [];\r\n let total = 0;\r\n try {\r\n for (;;) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n total += value.byteLength;\r\n if (total > maxBytes) {\r\n // Ne PAS attendre : sur une branche de tee(), cancel() ne se termine que lorsque l'autre branche\r\n // (celle du client) est elle aussi terminée — l'attendre bloquerait la réponse/la requête.\r\n void reader.cancel().catch(() => undefined);\r\n return null;\r\n }\r\n chunks.push(value);\r\n }\r\n } finally {\r\n try {\r\n reader.releaseLock();\r\n } catch {\r\n /* déjà libéré */\r\n }\r\n }\r\n const all = new Uint8Array(total);\r\n let off = 0;\r\n for (const c of chunks) {\r\n all.set(c, off);\r\n off += c.byteLength;\r\n }\r\n return new TextDecoder(\"utf-8\").decode(all);\r\n}\r\n\r\nasync function readBodySafely(request: Request): Promise<string | undefined> {\r\n try {\r\n const len = request.headers.get(\"content-length\");\r\n // Longueur DÉCLARÉE inconnue (chunked) ou démesurée : on ne lit pas. Mais cette longueur n'est qu'une\r\n // PROMESSE du visiteur, pas une garantie — un en-tête mensonger (Content-Length: 10, corps réel de\r\n // plusieurs Mo) laissait jusqu'ici `request.clone().text()` lire le corps ENTIER en mémoire avant\r\n // qu'aucune limite ne s'applique, malgré ce que ce commentaire prétendait déjà. `readStreamLimited`\r\n // (partagée avec `readLimited` ci-dessous) borne la lecture RÉELLE, pas seulement l'en-tête déclaré —\r\n // trouvé lors de l'audit du 04-05/11/2026, voir AUDIT_SECURITE_COMPLET.md.\r\n if (len === null || !/^\\d{1,9}$/.test(len)) return undefined;\r\n if (Number(len) === 0 || Number(len) > BODY_LIMIT_BYTES) return undefined;\r\n if (!TEXTUAL.test(request.headers.get(\"content-type\") ?? \"\")) return undefined;\r\n if (request.bodyUsed) return undefined;\r\n // clone() : le framework peut encore lire le corps d'origine.\r\n const text = await readStreamLimited(request.clone().body, BODY_LIMIT_BYTES);\r\n return text === null ? undefined : text;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/** Demande une décision pour une `Request` standard. Ne lève jamais (fail-open). */\r\nexport async function protectWebRequest(zarex: Zarex, request: Request, opts: WebGuardOptions = {}): Promise<Decision> {\r\n let path = \"/\";\r\n let host: string | undefined;\r\n try {\r\n const u = new URL(request.url);\r\n path = u.pathname + u.search;\r\n host = u.host;\r\n } catch {\r\n /* URL illisible : chemin par défaut */\r\n }\r\n const body = opts.inspectBody ? await readBodySafely(request) : undefined;\r\n return zarex.protect(\r\n {\r\n method: request.method,\r\n path,\r\n host: request.headers.get(\"host\") ?? host,\r\n headers: request.headers,\r\n socketIp: opts.socketIp,\r\n frameworkIp: opts.frameworkIp,\r\n body,\r\n },\r\n opts.call\r\n );\r\n}\r\n\r\n/**\r\n * Décision + réponse de refus. Quoi qu'il arrive (erreur interne, rappel défaillant),\r\n * la requête du client passe : `deny` n'est défini que sur un vrai refus.\r\n */\r\nexport async function guardWebRequest(zarex: Zarex, request: Request, opts: WebGuardOptions = {}): Promise<WebGuardResult> {\r\n let decision: Decision;\r\n try {\r\n decision = await protectWebRequest(zarex, request, opts);\r\n } catch {\r\n return { decision: undefined };\r\n }\r\n if (decision.denied) {\r\n try {\r\n const res = opts.onDeny ? await opts.onDeny(request, decision) : denyResponse(decision);\r\n return { decision, deny: res };\r\n } catch {\r\n return { decision, deny: denyResponse(decision) };\r\n }\r\n }\r\n if (decision.degraded && opts.onDegraded) {\r\n try {\r\n opts.onDegraded(request, decision);\r\n } catch {\r\n /* le rappel du développeur ne doit pas casser la requête */\r\n }\r\n }\r\n return { decision };\r\n}\r\n\r\n// ───────── Réponses : en-têtes de sécurité et surveillance des fuites ─────────\r\n\r\nexport interface WebFinalizeOptions {\r\n /** En-têtes de sécurité ajoutés à chaque réponse (true = défauts prudents). */\r\n secureHeaders?: SecureHeadersOptions | boolean | undefined;\r\n /** Surveillance des fuites de données sensibles dans les réponses texte/JSON (true = défauts). */\r\n responseGuard?: ResponseGuardOptions | boolean | undefined;\r\n}\r\n\r\nconst DEFAULT_MAX_BYTES = 1_000_000;\r\n\r\nasync function readLimited(response: Response, maxBytes: number): Promise<string | null> {\r\n return readStreamLimited(response.clone().body, maxBytes);\r\n}\r\n\r\nasync function guardWebResponse(response: Response, g: ResponseGuardOptions): Promise<Response> {\r\n const ct = response.headers.get(\"content-type\") ?? \"\";\r\n if (response.body === null || [101, 204, 205, 304].includes(response.status)) return response; // rien à analyser, rien à signaler\r\n const enc = (response.headers.get(\"content-encoding\") ?? \"identity\").toLowerCase();\r\n // SSE et corps compressé : des formats TEXTE en principe analysables, simplement hors de ce que ce\r\n // garde sait traiter aujourd'hui (voir ResponseGuardEvent.action) — à signaler, contrairement à un\r\n // contenu binaire (image…) ci-dessous, qui n'a jamais été dans son périmètre. Trouvé lors de\r\n // l'audit du 04/11/2026 : ces deux cas étaient ignorés en silence. Voir AUDIT_SECURITE_COMPLET.md.\r\n if (/^text\\/event-stream/i.test(ct) || enc !== \"identity\") {\r\n notify(g, { action: \"skipped_unscannable\", findings: {}, contentType: ct });\r\n return response;\r\n }\r\n if (!isScannableContentType(ct)) return response;\r\n const max = g.maxBytes ?? DEFAULT_MAX_BYTES;\r\n const len = response.headers.get(\"content-length\");\r\n if (len !== null && /^\\d{1,12}$/.test(len) && Number(len) > max) {\r\n notify(g, { action: \"skipped_too_large\", findings: {}, contentType: ct });\r\n return response;\r\n }\r\n const text = await readLimited(response, max);\r\n if (text === null) {\r\n notify(g, { action: \"skipped_too_large\", findings: {}, contentType: ct });\r\n return response;\r\n }\r\n const r = applyGuard(text, ct, g);\r\n if (r.action === \"masked\" || r.action === \"blocked\") {\r\n const headers = new Headers(response.headers);\r\n headers.delete(\"content-length\");\r\n headers.delete(\"etag\");\r\n if (r.action === \"blocked\") headers.set(\"content-type\", \"application/json; charset=utf-8\");\r\n return new Response(r.body, { status: r.action === \"blocked\" ? 500 : response.status, statusText: r.action === \"blocked\" ? \"Internal Server Error\" : response.statusText, headers });\r\n }\r\n return response;\r\n}\r\n\r\n/**\r\n * Prépare (une fois, au démarrage : les erreurs de configuration sont levées ici) la fonction qui\r\n * traite chaque réponse. Renvoie `undefined` si aucune option n'est demandée.\r\n * La fonction renvoyée ne lève jamais : en cas de souci, la réponse d'origine est rendue.\r\n */\r\nexport function createWebFinalizer(o: WebFinalizeOptions): ((response: Response, request?: Request) => Promise<Response>) | undefined {\r\n const prepared = prepareSecureHeaders(o.secureHeaders);\r\n const guard = normalizeGuardOptions(o.responseGuard);\r\n if (!prepared && !guard) return undefined;\r\n return async (response, request) => {\r\n let out = response;\r\n try {\r\n if (guard) out = await guardWebResponse(out, guard);\r\n } catch {\r\n out = response;\r\n }\r\n if (prepared) {\r\n let host: string | null = null;\r\n try {\r\n host = request ? (request.headers.get(\"host\") ?? new URL(request.url).host) : null;\r\n } catch {\r\n host = null;\r\n }\r\n out = applyPreparedToResponse(out, prepared, host);\r\n }\r\n return out;\r\n };\r\n}"],"mappings":"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","names":["ZarexConfigError","names","b","readEnv","ZarexConfigError","text","ZarexConfigError","ZarexConfigError","ZarexConfigError","ZarexClientError","ZarexConfigError","mode","DEFAULT_MAX_BYTES"]}
1
+ {"version":3,"sources":["../src/protector.ts","../src/headers.ts","../src/ip.ts","../src/ipaddr.ts","../src/ranges.ts","../src/sha256.ts","../src/rules.ts","../src/index.ts","../src/sensitive.ts","../src/responseguard.ts","../src/secureheaders.ts","../src/web.ts"],"sourcesContent":["import {\r\n SDK_VERSION,\r\n ZarexClientError,\r\n ZarexConfigError,\r\n createClient,\r\n type BreakerOptions,\r\n type DecideRequest,\r\n type DecideResponse,\r\n type Verdict,\r\n type ZarexClient,\r\n} from \"@zarex/client\";\r\nimport { selectHeadersDetailed, type HeaderBag } from \"./headers.js\";\r\nimport { createIpResolver, type IpAdvice, type IpSource } from \"./ip.js\";\r\nimport type { Rule } from \"./rules.js\";\r\nimport { sha256Hex } from \"./sha256.js\";\r\n\r\nexport interface Logger {\r\n warn(message: string): void;\r\n error?(message: string): void;\r\n}\r\n\r\nexport interface ZarexOptions {\r\n /** Clé API du projet (jamais journalisée). Ex. process.env.ZAREX_KEY. */\r\n key: string;\r\n /** Inutile en usage normal (défaut : le service Zarex de production) — à ne définir que pour les tests ou un environnement local. Variable ZAREX_URL acceptée aussi. */\r\n baseUrl?: string;\r\n /** Règles appliquées à chaque requête (1 à 10). */\r\n rules: Rule[];\r\n /**\r\n * OBLIGATOIRE. Comment trouver l'IP du visiteur — voir le type IpSource.\r\n * Un mauvais choix fait passer tous vos visiteurs pour une seule adresse.\r\n */\r\n ipSource: IpSource;\r\n /** Délai maximal d'un appel au moteur, en ms (défaut 500). Au-delà : la requête passe (fail-open). */\r\n timeoutMs?: number;\r\n allowInsecureHttp?: boolean;\r\n /** Disjoncteur (défaut : 5 échecs → 10 s de pause). `false` pour le désactiver. */\r\n breaker?: BreakerOptions | false;\r\n /** Transmettre aussi l'en-tête Cookie au Shield (défaut false : il contient les sessions des visiteurs). */\r\n inspectCookies?: boolean;\r\n /** En-têtes supplémentaires à transmettre (jamais Authorization / Set-Cookie). */\r\n extraHeaders?: string[];\r\n /** Messages d'avertissement. `false` pour les couper. Défaut : console. */\r\n logger?: Logger | false;\r\n fetch?: typeof fetch;\r\n /** Horloge (tests). */\r\n now?: () => number;\r\n}\r\n\r\nexport interface ProtectInput {\r\n method: string;\r\n /** Chemin AVEC la query string (le Shield l'analyse ; elle n'est pas conservée dans les statistiques). */\r\n path: string;\r\n host?: string | undefined;\r\n headers: HeaderBag;\r\n /** Adresse de la connexion TCP (req.socket.remoteAddress). */\r\n socketIp?: string | undefined;\r\n /** Adresse calculée par le framework (SvelteKit, Fastify…). */\r\n frameworkIp?: string | undefined;\r\n /** Express : req.ip et état de « trust proxy ». */\r\n expressIp?: string | undefined;\r\n expressTrustProxy?: boolean | undefined;\r\n /** Corps déjà lu (facultatif — voir inspectBody dans l'adaptateur). Tronqué à 48 Ko. */\r\n body?: string | undefined;\r\n}\r\n\r\nexport interface ProtectCall {\r\n /**\r\n * Identifiants personnalisés pour les règles (ex. { userId: \"42\" }). Hachés en SHA-256 avant envoi.\r\n * Au plus 16 par requête (MAX_CHARACTERISTICS) : au-delà, les entrées en trop sont retirées et un\r\n * avertissement est journalisé (trouvé lors de l'audit du 04-05/11/2026 : cette limite n'était\r\n * documentée nulle part et le dépassement passait jusqu'ici totalement inaperçu).\r\n */\r\n characteristics?: Record<string, string>;\r\n /** Jetons consommés par cette requête (règle tokenBucket). */\r\n tokensRequested?: number;\r\n /** Adresse e-mail à valider (règle emailValidation). Jamais journalisée côté Zarex. */\r\n email?: string;\r\n /** Numéro de téléphone à valider (règle phoneValidation). */\r\n phone?: string;\r\n /** Texte à analyser (règles sensitiveInfo et promptInjection) : message destiné à une IA, champ libre… Tronqué à 32 Ko. */\r\n text?: string;\r\n /** Remplace les règles configurées pour cet appel. */\r\n rules?: Rule[];\r\n}\r\n\r\nexport interface RuleOutcome {\r\n ruleType: string;\r\n verdict: Verdict;\r\n dryRun: boolean;\r\n reason?: Record<string, unknown>;\r\n}\r\n\r\nexport interface Decision {\r\n /** true = laisser passer. Toujours true en cas de panne (fail-open). */\r\n allowed: boolean;\r\n denied: boolean;\r\n verdict: Verdict;\r\n /** Identifiant de la décision (support, journaux). Vide si le moteur n'a pas répondu. */\r\n id: string;\r\n results: RuleOutcome[];\r\n /** Première règle ayant refusé (hors mode DRY_RUN). */\r\n deniedBy?: RuleOutcome;\r\n /** Secondes à attendre avant de réessayer (limite de débit). */\r\n retryAfterSeconds?: number;\r\n /** true = le moteur n'a pas pu décider (panne, délai, IP inconnue…) et la requête a été laissée passer. */\r\n degraded: boolean;\r\n error?: { code: string; message: string };\r\n /** IP du visiteur utilisée pour la décision. */\r\n ip?: string;\r\n /** Version des règles du moteur ayant décidé (ex. \"11.3.0+shield-2.0\"), pour comparer avant/après une mise à jour. Absente d'un ancien moteur. */\r\n rulesetVersion?: string;\r\n /** Pays (code ISO, \"ZZ\" = inconnu) et réseau (\"AS15169\", \"AS0\" = inconnu) de l'IP du visiteur. Absent si le moteur n'a pas de base d'adresses. */\r\n ipInfo?: { country: string; asn: string };\r\n}\r\n\r\nexport interface Zarex {\r\n protect(input: ProtectInput, call?: ProtectCall): Promise<Decision>;\r\n /** Valeur de ipSource déclarée au moteur. */\r\n readonly ipSourceLabel: string;\r\n}\r\n\r\nconst MAX_RULES = 10;\r\nconst CHAR_KEY_RE = /^[A-Za-z0-9_.:-]{1,64}$/;\r\nconst MAX_BODY_BYTES = 48 * 1024;\r\nconst MAX_PATH_BYTES = 8 * 1024;\r\nconst MAX_TEXT_BYTES = 32 * 1024;\r\nconst MAX_FIELD_CHARS = 512;\r\nconst MAX_CHARACTERISTICS = 16;\r\nconst ADVICE_REPEAT_MS = 10 * 60_000;\r\nconst ERROR_REPEAT_MS = 10_000;\r\n\r\nconst ADVICE_TEXT: Record<IpAdvice, (label: string) => string> = {\r\n forwarded_header_ignored: (l) =>\r\n `A proxy header (X-Forwarded-For / Forwarded / X-Real-IP) is present but ipSource is \"${l}\", which ignores it. ` +\r\n \"If your app is behind a proxy or CDN, every visitor will look like the proxy's address. Set ipSource accordingly (e.g. { proxies: 1 }, \\\"cloudflare\\\", \\\"vercel\\\").\",\r\n not_from_trusted_proxy: (l) =>\r\n `A request did not come from the proxy declared in ipSource (\"${l}\"), so its forwarding headers were ignored. ` +\r\n \"Either your origin server is reachable directly (lock it down so only your proxy can connect), or ipSource does not match your setup.\",\r\n chain_too_short: (l) =>\r\n `X-Forwarded-For has fewer addresses than the proxies declared in ipSource (\"${l}\"). The connection address was used instead. ` +\r\n \"Check the number of proxies in front of your app.\",\r\n missing_provider_header: (l) =>\r\n `The connection comes from the declared provider but its client-IP header is missing or unreadable (ipSource \"${l}\"). The request was not checked.`,\r\n weak_provider_header: (l) =>\r\n `The platform's primary client-IP header was missing (ipSource \"${l}\"), so a more generic fallback header was used instead. ` +\r\n \"If your app really runs on that platform this is usually harmless (a CDN/WAF in front of it, or an atypical runtime), but double-check: a wrong ipSource would let visitors forge this fallback header themselves.\",\r\n invalid_forwarded_chain: () =>\r\n \"X-Forwarded-For is unreadable or abnormally long: the visitor's IP could not be determined and the request was not checked.\",\r\n all_addresses_trusted: (l) =>\r\n `Every address in the chain is a trusted proxy (ipSource \"${l}\"), so no visitor IP was found and the request was not checked.`,\r\n no_socket_ip: () => \"The connection's remote address is unknown: the request was not checked.\",\r\n};\r\n\r\nconst ENGINE_WARNING_TEXT: Record<string, (label: string) => string> = {\r\n ip_not_public: (l) =>\r\n `The engine received a private/local/shared IP address. This is normal in local development. In production it almost always means a proxy is not configured: every visitor then shares one address and rate limits hit everyone. Check ipSource (currently \"${l}\").`,\r\n path_truncated: () =>\r\n \"A request path was longer than 8 KB, so only its beginning and end were inspected. Rules apply their `oversize` policy (default for shield() and requestFilter(): deny). Very long URLs are almost always attacks; if they are legitimate on your site, set `oversize: \\\"inspect\\\"` on that rule.\",\r\n headers_truncated: () =>\r\n \"Some request headers were longer than 8 KB (or too numerous), so only part of them was inspected. Rules apply their `oversize` policy (default for shield() and requestFilter(): deny).\",\r\n body_truncated: () =>\r\n \"A request body was larger than 48 KB, so only its beginning and end were inspected (the middle was not). Use `oversize: \\\"deny\\\"` on the rule if large bodies are never expected.\",\r\n ip_db_absent: () =>\r\n \"A rule uses ip.country or ip.asn but the engine has no IP address database (ZAREX_IPDB_FILE; build it with `zarex-ipdb fetch`). Those conditions see every visitor as unknown (country \\\"ZZ\\\", AS0): a deny-list never matches, and an allow-list (default: \\\"DENY\\\") refuses everyone.\",\r\n ip_db_stale: () =>\r\n \"The engine's IP address database is more than 45 days old, so countries and networks may be wrong. Re-run `zarex-ipdb fetch` (it should run nightly).\",\r\n text_truncated: () =>\r\n \"A text sent for analysis was longer than 32 KB, so only its beginning and end were inspected.\",\r\n};\r\n\r\nfunction defaultLogger(): Logger {\r\n return {\r\n warn: (m) => console.warn(m),\r\n error: (m) => console.error(m),\r\n };\r\n}\r\n\r\nfunction readEnv(name: string): string | undefined {\r\n try {\r\n return typeof process !== \"undefined\" ? process.env?.[name] : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction isCloudflareWorker(): boolean {\r\n try {\r\n return typeof navigator !== \"undefined\" && navigator.userAgent === \"Cloudflare-Workers\";\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/** Même séparateur que le moteur Go (promptguard.CutMarker) : le moteur n'aura rien à re-couper. */\r\nconst CUT_MARKER = \"\\n[...cut...]\\n\";\r\n/** Séparateur du chemin : sans saut de ligne (il reste dans une URL). Même valeur que le moteur Go (cutMarker). */\r\nconst PATH_CUT_MARKER = \" [...cut...] \";\r\n\r\n/**\r\n * Réduit un texte trop long à `max` octets en gardant le DÉBUT et la FIN (jamais au milieu d'un\r\n * caractère). Garder seulement le début permettrait de cacher une attaque après un long remplissage.\r\n * Le milieu du texte n'est pas transmis : `truncated` est alors vrai.\r\n */\r\nfunction truncateHeadTail(s: string, max: number, markerText: string = CUT_MARKER): { text: string; truncated: boolean } {\r\n if (s.length <= max / 4) return { text: s, truncated: false };\r\n const enc = new TextEncoder().encode(s);\r\n if (enc.length <= max) return { text: s, truncated: false };\r\n const marker = new TextEncoder().encode(markerText);\r\n const room = max - marker.length;\r\n const dec = new TextDecoder();\r\n if (room < 2) return { text: dec.decode(enc.subarray(0, max)), truncated: true };\r\n let head = Math.floor(room / 2);\r\n while (head > 0 && (enc[head]! & 0xc0) === 0x80) head--;\r\n let start = enc.length - (room - Math.floor(room / 2));\r\n while (start < enc.length && (enc[start]! & 0xc0) === 0x80) start++;\r\n return { text: dec.decode(enc.subarray(0, head)) + markerText + dec.decode(enc.subarray(start)), truncated: true };\r\n}\r\n\r\n/** En-têtes cités par les règles requestFilter : ils doivent faire partie de ceux transmis au moteur. */\r\nfunction filterHeaderNames(rules: readonly Rule[]): string[] {\r\n const out = new Set<string>();\r\n for (const r of rules) {\r\n const rf = (r as { request_filter?: { expressions: { field: string }[] } }).request_filter;\r\n for (const e of rf?.expressions ?? []) if (e.field.startsWith(\"headers.\")) out.add(e.field.slice(8).toLowerCase());\r\n else if (e.field.startsWith(\"cookies.\")) out.add(\"cookie\");\r\n }\r\n return [...out];\r\n}\r\n\r\nexport function validateRules(rules: unknown): Rule[] {\r\n if (!Array.isArray(rules) || rules.length < 1 || rules.length > MAX_RULES) {\r\n throw new ZarexConfigError(`Zarex: \\`rules\\` must contain between 1 and ${MAX_RULES} rules (use shield(), rateLimit(), …).`);\r\n }\r\n const known = new Set([\r\n \"shield\", \"rate_limit\", \"bot_detection\", \"token_bucket\",\r\n \"request_filter\", \"sensitive_info\", \"email_validation\", \"phone_validation\", \"prompt_injection\",\r\n ]);\r\n for (const r of rules) {\r\n const keys = r && typeof r === \"object\" ? Object.keys(r) : [];\r\n if (keys.length !== 1 || !known.has(keys[0]!)) {\r\n throw new ZarexConfigError(\"Zarex: each rule must be created with shield(), rateLimit(), tokenBucket(), detectBot(), requestFilter(), sensitiveInfo(), emailValidation(), phoneValidation() or promptInjection().\");\r\n }\r\n }\r\n return rules as Rule[];\r\n}\r\n\r\nexport function createZarex(options: ZarexOptions): Zarex {\r\n if (!options || typeof options !== \"object\") throw new ZarexConfigError(\"Zarex: options are required.\");\r\n const resolver = createIpResolver(options.ipSource);\r\n const rules = validateRules(options.rules);\r\n const now = options.now ?? Date.now;\r\n const logger: Logger | null = options.logger === false ? null : (options.logger ?? defaultLogger());\r\n\r\n const client: ZarexClient = createClient({\r\n key: options.key,\r\n ...(options.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {}),\r\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\r\n ...(options.allowInsecureHttp !== undefined ? { allowInsecureHttp: options.allowInsecureHttp } : {}),\r\n ...(options.breaker !== undefined ? { breaker: options.breaker } : {}),\r\n ...(options.fetch ? { fetch: options.fetch } : {}),\r\n });\r\n\r\n // Fournisseur déclaré mais l'environnement ne correspond pas : les en-têtes ne sont\r\n // fiables que sur cette plateforme (ailleurs, n'importe qui peut les écrire).\r\n if (logger) {\r\n if (options.ipSource === \"vercel\" && !readEnv(\"VERCEL\")) {\r\n logger.warn('[zarex] ipSource \"vercel\" is only safe when the app runs on Vercel (VERCEL is not set here): outside Vercel, anyone can forge these headers.');\r\n }\r\n if (options.ipSource === \"cloudflare-workers\" && !isCloudflareWorker()) {\r\n logger.warn('[zarex] ipSource \"cloudflare-workers\" is only safe inside a Cloudflare Worker: anywhere else, anyone can forge the CF-Connecting-IP header.');\r\n }\r\n if (options.ipSource === \"fly\" && !readEnv(\"FLY_APP_NAME\")) {\r\n logger.warn('[zarex] ipSource \"fly\" is only safe when the app runs on Fly.io (FLY_APP_NAME is not set here): outside Fly, anyone can forge this header.');\r\n }\r\n }\r\n\r\n if (filterHeaderNames(rules).includes(\"cookie\") && !options.inspectCookies) {\r\n throw new ZarexConfigError(\"Zarex: a requestFilter on `headers.cookie` or `cookies.<name>` needs `inspectCookies: true` (cookies are not sent to the engine by default).\");\r\n }\r\n\r\n const lastLogged = new Map<string, number>();\r\n const suppressed = new Map<string, number>();\r\n function logLimited(key: string, repeatMs: number, level: \"warn\" | \"error\", message: string): void {\r\n if (!logger) return;\r\n const t = now();\r\n const last = lastLogged.get(key);\r\n if (last !== undefined && t - last < repeatMs) {\r\n suppressed.set(key, (suppressed.get(key) ?? 0) + 1);\r\n return;\r\n }\r\n lastLogged.set(key, t);\r\n const n = suppressed.get(key) ?? 0;\r\n suppressed.delete(key);\r\n const full = `[zarex] ${message}${n > 0 ? ` (${n} similar message(s) omitted)` : \"\"}`;\r\n try {\r\n (level === \"error\" && logger.error ? logger.error : logger.warn).call(logger, full);\r\n } catch {\r\n /* un logger défaillant ne doit jamais casser une requête */\r\n }\r\n }\r\n\r\n function degraded(code: string, message: string, ip?: string): Decision {\r\n return {\r\n allowed: true,\r\n denied: false,\r\n verdict: \"ALLOW\",\r\n id: \"\",\r\n results: [],\r\n degraded: true,\r\n error: { code, message },\r\n ...(ip ? { ip } : {}),\r\n };\r\n }\r\n\r\n function fromResponse(res: DecideResponse, ip: string): Decision {\r\n const results: RuleOutcome[] = res.results.map((r) => ({\r\n ruleType: r.rule_type,\r\n verdict: r.verdict,\r\n dryRun: r.is_dry_run,\r\n ...(r.reason ? { reason: r.reason } : {}),\r\n }));\r\n if (res.verdict === \"ERROR\") {\r\n return { ...degraded(\"engine_error\", \"The engine could not evaluate the rules; request allowed.\"), results, id: res.decision_id, ip };\r\n }\r\n const denied = res.verdict === \"DENY\";\r\n const deniedBy = denied ? results.find((r) => r.verdict === \"DENY\" && !r.dryRun) : undefined;\r\n let retryAfterSeconds: number | undefined;\r\n if (deniedBy?.reason) {\r\n const ms = Number(deniedBy.reason[\"reset_in_ms\"] ?? deniedBy.reason[\"retry_in_ms\"]);\r\n if (Number.isFinite(ms) && ms > 0) retryAfterSeconds = Math.min(86_400, Math.max(1, Math.ceil(ms / 1000)));\r\n }\r\n return {\r\n allowed: !denied,\r\n denied,\r\n verdict: res.verdict,\r\n id: res.decision_id,\r\n results,\r\n ...(res.ruleset_version ? { rulesetVersion: res.ruleset_version } : {}),\r\n ...(res.ip ? { ipInfo: res.ip } : {}),\r\n ...(deniedBy ? { deniedBy } : {}),\r\n ...(retryAfterSeconds !== undefined ? { retryAfterSeconds } : {}),\r\n degraded: false,\r\n ip,\r\n };\r\n }\r\n\r\n async function protect(input: ProtectInput, call: ProtectCall = {}): Promise<Decision> {\r\n try {\r\n const resolved = resolver.resolve({\r\n socketIp: input.socketIp,\r\n expressIp: input.expressIp,\r\n frameworkIp: input.frameworkIp,\r\n expressTrustProxy: input.expressTrustProxy,\r\n headers: input.headers,\r\n });\r\n for (const a of resolved.advice) {\r\n logLimited(`advice:${a}`, ADVICE_REPEAT_MS, \"warn\", ADVICE_TEXT[a](resolver.label));\r\n }\r\n if (!resolved.ip) {\r\n return degraded(\"ip_unresolved\", \"The visitor's IP could not be determined; request allowed without checking.\");\r\n }\r\n\r\n const reqRules = call.rules ? validateRules(call.rules) : rules;\r\n const characteristics: { key: string; value: string }[] = [];\r\n // Au-delà de MAX_CHARACTERISTICS, les entrées en trop étaient retirées sans AUCUN signal (ni log,\r\n // ni documentation de la limite) — un développeur qui ajoute une 17ᵉ caractéristique pour une règle\r\n // de rate-limit la croit prise en compte alors qu'elle est purement ignorée. Trouvé lors de\r\n // l'audit du 04-05/11/2026, voir AUDIT_SECURITE_COMPLET.md.\r\n let droppedCharacteristics = 0;\r\n for (const [k, v] of Object.entries(call.characteristics ?? {})) {\r\n if (k === \"ip\" || !CHAR_KEY_RE.test(k) || typeof v !== \"string\" || v === \"\") continue;\r\n if (characteristics.length >= MAX_CHARACTERISTICS) {\r\n droppedCharacteristics++;\r\n continue;\r\n }\r\n characteristics.push({ key: k, value: sha256Hex(v) });\r\n }\r\n if (droppedCharacteristics > 0) {\r\n logLimited(\r\n \"characteristics_truncated\",\r\n ADVICE_REPEAT_MS,\r\n \"warn\",\r\n `${droppedCharacteristics} characteristic(s) were dropped: at most ${MAX_CHARACTERISTICS} are sent per request. ` +\r\n \"If a rule keys off one of the dropped names, it is no longer enforced for it.\"\r\n );\r\n }\r\n\r\n const body = input.body !== undefined ? truncateHeadTail(input.body, MAX_BODY_BYTES) : undefined;\r\n const pathCut = truncateHeadTail(String(input.path || \"/\"), MAX_PATH_BYTES, PATH_CUT_MARKER);\r\n const fwd = selectHeadersDetailed(input.headers, {\r\n extraHeaders: [...(options.extraHeaders ?? []), ...filterHeaderNames(reqRules)],\r\n ...(options.inspectCookies ? { inspectCookies: true } : {}),\r\n });\r\n const text = typeof call.text === \"string\" && call.text !== \"\" ? truncateHeadTail(call.text, MAX_TEXT_BYTES) : undefined;\r\n const email = typeof call.email === \"string\" && call.email !== \"\" ? call.email.slice(0, MAX_FIELD_CHARS) : undefined;\r\n const phone = typeof call.phone === \"string\" && call.phone !== \"\" ? call.phone.slice(0, 64) : undefined;\r\n const payload: DecideRequest = {\r\n site_key: options.key,\r\n sdk_version: SDK_VERSION,\r\n sdk_lang: \"js\",\r\n entry_point: \"REQUEST\",\r\n ip_source: resolver.label,\r\n rules: reqRules,\r\n context: {\r\n ip: resolved.ip,\r\n method: String(input.method || \"GET\").slice(0, 16),\r\n path: pathCut.text,\r\n ...(pathCut.truncated ? { path_truncated: true } : {}),\r\n host: String(input.host ?? \"\").slice(0, 255),\r\n headers: fwd.headers,\r\n ...(fwd.truncated ? { headers_truncated: true } : {}),\r\n ...(body ? { body: body.text, ...(body.truncated ? { body_truncated: true } : {}) } : {}),\r\n ...(characteristics.length ? { characteristics } : {}),\r\n ...(email ? { email } : {}),\r\n ...(phone ? { phone } : {}),\r\n ...(text ? { text_content: text.text, ...(text.truncated ? { text_truncated: true } : {}) } : {}),\r\n ...(call.tokensRequested !== undefined && Number.isInteger(call.tokensRequested) && call.tokensRequested > 0\r\n ? { tokens_requested: Math.min(call.tokensRequested, 1_000_000) }\r\n : {}),\r\n },\r\n };\r\n\r\n let res: DecideResponse;\r\n try {\r\n res = await client.decide(payload);\r\n } catch (err) {\r\n const e = err instanceof ZarexClientError ? err : new ZarexClientError(\"network\", \"Zarex request failed\");\r\n const hint =\r\n e.code === \"unauthorized\"\r\n ? \" — check your API key (revoked, wrong environment, or typo).\"\r\n : e.code === \"quota_exceeded\"\r\n ? \" — your plan's monthly quota is used up.\"\r\n : e.code === \"circuit_open\"\r\n ? \" — the engine failed repeatedly; calls are paused briefly.\"\r\n : \"\";\r\n // circuit_open est attendu pendant une panne : un seul message suffit.\r\n logLimited(`error:${e.code}`, e.code === \"circuit_open\" ? 60_000 : ERROR_REPEAT_MS, \"error\", `Request allowed without protection: ${e.message}${hint}`);\r\n return degraded(e.code, e.message, resolved.ip);\r\n }\r\n\r\n for (const w of res.warnings ?? []) {\r\n const text = ENGINE_WARNING_TEXT[w];\r\n if (text) logLimited(`engine:${w}`, ADVICE_REPEAT_MS, \"warn\", text(resolver.label));\r\n }\r\n return fromResponse(res, resolved.ip);\r\n } catch (err) {\r\n // Dernier filet : une erreur INATTENDUE du SDK ne doit jamais faire échouer la requête du client.\r\n const msg = err instanceof Error ? err.message : \"unexpected error\";\r\n logLimited(\"error:internal\", ERROR_REPEAT_MS, \"error\", `Request allowed without protection: unexpected SDK error (${msg.slice(0, 120)})`);\r\n return degraded(\"internal_error\", \"Unexpected SDK error\");\r\n }\r\n }\r\n\r\n return { protect, ipSourceLabel: resolver.label };\r\n}","/** Un « sac d'en-têtes » : objet Node (clés en minuscules), Map, ou Headers (fetch). */\r\nexport type HeaderBag =\r\n | Record<string, string | string[] | undefined>\r\n | { get(name: string): string | null };\r\n\r\nfunction isHeadersLike(h: HeaderBag): h is { get(name: string): string | null } {\r\n return typeof (h as { get?: unknown }).get === \"function\";\r\n}\r\n\r\n/** Valeur d'un en-tête (plusieurs valeurs jointes par une virgule). */\r\nexport function getHeader(bag: HeaderBag, name: string): string | undefined {\r\n const lower = name.toLowerCase();\r\n if (isHeadersLike(bag)) return bag.get(lower) ?? undefined;\r\n const direct = (bag as Record<string, string | string[] | undefined>)[lower];\r\n let v = direct;\r\n if (v === undefined) {\r\n // Certains objets conservent la casse d'origine.\r\n for (const k of Object.keys(bag)) {\r\n if (k.toLowerCase() === lower) {\r\n v = (bag as Record<string, string | string[] | undefined>)[k];\r\n break;\r\n }\r\n }\r\n }\r\n if (v === undefined) return undefined;\r\n return Array.isArray(v) ? v.join(\", \") : v;\r\n}\r\n\r\n/**\r\n * En-têtes envoyés au moteur : LISTE BLANCHE.\r\n *\r\n * Pourquoi pas « tout sauf les secrets » : les secrets ont des noms que nous ne\r\n * pouvons pas deviner (x-api-key, x-auth-token, x-session…). Une liste blanche ne\r\n * laisse jamais partir un secret par oubli. Le moteur n'a besoin que de ces\r\n * en-têtes pour le Shield et la détection de robots.\r\n *\r\n * `cookie` n'est PAS dans la liste : il contient les sessions des visiteurs.\r\n * Activez `inspectCookies` seulement si vous acceptez qu'il soit transmis.\r\n */\r\nexport const DEFAULT_FORWARDED_HEADERS: readonly string[] = [\r\n \"user-agent\",\r\n \"accept\",\r\n \"accept-language\",\r\n \"accept-encoding\",\r\n \"referer\",\r\n \"origin\",\r\n \"content-type\",\r\n \"x-requested-with\",\r\n \"x-forwarded-for\", // lu par le Shield (les attaquants y glissent des charges)\r\n \"sec-fetch-site\",\r\n \"sec-fetch-mode\",\r\n \"sec-fetch-dest\",\r\n \"sec-ch-ua\",\r\n \"sec-ch-ua-mobile\",\r\n \"sec-ch-ua-platform\",\r\n];\r\n\r\n/** Jamais transmis, même si le développeur les ajoute dans `extraHeaders`. */\r\nexport const NEVER_FORWARD: ReadonlySet<string> = new Set([\r\n \"authorization\",\r\n \"proxy-authorization\",\r\n \"set-cookie\",\r\n \"x-zarex-key\",\r\n]);\r\n\r\n/** Mêmes limites que le moteur (8 Ko par valeur, 32 Ko au total) : on ne coupe pas avant lui. */\r\nconst MAX_VALUE = 8 * 1024;\r\nconst MAX_TOTAL = 32 * 1024;\r\nconst CUT = \" [...cut...] \";\r\n\r\n/** Garde le début ET la fin d'une valeur trop longue (une attaque après du remplissage reste visible). */\r\nfunction cutValue(v: string, max: number): string {\r\n if (v.length <= max) return v;\r\n const room = max - CUT.length;\r\n const h = Math.floor(room / 2);\r\n return v.slice(0, h) + CUT + v.slice(v.length - (room - h));\r\n}\r\n\r\nexport interface ForwardOptions {\r\n extraHeaders?: readonly string[];\r\n inspectCookies?: boolean;\r\n}\r\n\r\nexport function selectHeaders(bag: HeaderBag, opts: ForwardOptions = {}): Record<string, string> {\r\n return selectHeadersDetailed(bag, opts).headers;\r\n}\r\n\r\n/** Comme selectHeaders, et dit si quelque chose a été coupé ou écarté (jamais en silence). */\r\nexport function selectHeadersDetailed(bag: HeaderBag, opts: ForwardOptions = {}): { headers: Record<string, string>; truncated: boolean } {\r\n const names = new Set<string>(DEFAULT_FORWARDED_HEADERS);\r\n for (const h of opts.extraHeaders ?? []) names.add(h.toLowerCase());\r\n if (opts.inspectCookies) names.add(\"cookie\");\r\n const out: Record<string, string> = {};\r\n let total = 0;\r\n let truncated = false;\r\n for (const name of names) {\r\n if (NEVER_FORWARD.has(name)) continue;\r\n const v = getHeader(bag, name);\r\n if (v === undefined || v === \"\") continue;\r\n const cut = cutValue(v, MAX_VALUE);\r\n if (cut !== v) truncated = true;\r\n if (total + name.length + cut.length > MAX_TOTAL) {\r\n truncated = true;\r\n break;\r\n }\r\n total += name.length + cut.length;\r\n out[name] = cut;\r\n }\r\n return { headers: out, truncated };\r\n}","import { ZarexConfigError } from \"@zarex/client\";\r\nimport { getHeader, type HeaderBag } from \"./headers.js\";\r\nimport {\r\n anyCidrContains,\r\n formatIp,\r\n parseCidr,\r\n parseIp,\r\n parseIpToken,\r\n type Cidr,\r\n type ParsedIp,\r\n} from \"./ipaddr.js\";\r\nimport { CLOUDFLARE_RANGES } from \"./ranges.js\";\r\n\r\n/**\r\n * Comment déterminer l'adresse IP du VISITEUR.\r\n *\r\n * Règle d'or (MDN, RFC 7239, documentations Cloudflare/Vercel/Fly) : on ne croit\r\n * que ce qu'a écrit NOTRE propre proxy. Dans `X-Forwarded-For`, la partie de\r\n * gauche est écrite par le visiteur (donc falsifiable) ; on lit de DROITE à\r\n * GAUCHE en sautant nos proxys.\r\n *\r\n * Aucune valeur par défaut : le développeur DOIT choisir (voir ZarexOptions).\r\n */\r\nexport type IpSource =\r\n | \"socket\" // pas de proxy : l'adresse de la connexion TCP\r\n | \"express\" // Express décide (req.ip + « trust proxy ») — adaptateur Express\r\n | \"framework\" // le framework décide (SvelteKit getClientAddress, Fastify request.ip + trustProxy…) — adaptateurs\r\n | \"auto\" // détecte Vercel / Fly par la VARIABLE D'ENVIRONNEMENT du serveur\r\n | \"cloudflare\" // en-tête CF-Connecting-IP, lu seulement si la connexion vient de Cloudflare\r\n | \"vercel\" // Vercel écrase x-forwarded-for lui-même\r\n | \"fly\" // en-tête Fly-Client-IP\r\n | \"cloudflare-workers\" // Cloudflare Workers : CF-Connecting-IP posé par Cloudflare (pas d'accès direct possible)\r\n | { proxies: number } // nombre de proxys de confiance devant l'application\r\n | { trustedProxies: string[] }; // adresses/plages (CIDR) de vos proxys\r\n\r\nexport interface IpInput {\r\n /** Adresse de la connexion TCP (req.socket.remoteAddress). */\r\n socketIp?: string | undefined;\r\n /** req.ip d'Express (déjà calculé selon « trust proxy »). */\r\n expressIp?: string | undefined;\r\n /** Adresse calculée par le framework (SvelteKit, Fastify…), selon SA configuration de proxys. */\r\n frameworkIp?: string | undefined;\r\n /** Express : « trust proxy » est-il activé ? Sert uniquement au diagnostic. */\r\n expressTrustProxy?: boolean | undefined;\r\n headers: HeaderBag;\r\n}\r\n\r\n/** Problèmes détectés — jamais bloquants (le SDK reste fail-open), toujours signalés au développeur. */\r\nexport type IpAdvice =\r\n | \"forwarded_header_ignored\" // en-tête de proxy présent mais ipSource ne s'en sert pas\r\n | \"not_from_trusted_proxy\" // la connexion ne vient pas du proxy déclaré (accès direct au serveur ?)\r\n | \"chain_too_short\" // moins de proxys que déclaré dans la chaîne\r\n | \"missing_provider_header\" // connexion du fournisseur, mais en-tête client absent/illisible\r\n | \"weak_provider_header\" // l'en-tête privilégié du fournisseur était absent ; repli sur un\r\n // en-tête générique moins garanti (trouvé lors de l'audit du 04/11/2026)\r\n | \"invalid_forwarded_chain\" // chaîne X-Forwarded-For illisible ou trop longue\r\n | \"all_addresses_trusted\" // toute la chaîne est constituée de proxys\r\n | \"no_socket_ip\"; // adresse de connexion inconnue\r\n\r\nexport interface ResolvedIp {\r\n /** Adresse canonique du visiteur ; undefined = inconnue (le SDK laisse passer sans appeler le moteur). */\r\n ip?: string;\r\n advice: IpAdvice[];\r\n}\r\n\r\nexport interface IpResolver {\r\n /** Valeur envoyée au moteur dans `ip_source` (diagnostic). */\r\n label: string;\r\n resolve(input: IpInput): ResolvedIp;\r\n}\r\n\r\nconst MAX_XFF_ENTRIES = 100;\r\nconst MAX_XFF_LENGTH = 8 * 1024;\r\n\r\nfunction readEnv(name: string): string | undefined {\r\n try {\r\n return typeof process !== \"undefined\" ? process.env?.[name] : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction compileCidrs(list: readonly string[], what: string): Cidr[] {\r\n return list.map((c) => {\r\n try {\r\n return parseCidr(c);\r\n } catch (e) {\r\n throw new ZarexConfigError(`Zarex: ${what}: ${(e as Error).message}`);\r\n }\r\n });\r\n}\r\n\r\n/** Liste « 1.2.3.4, 5.6.7.8 » (un ou plusieurs en-têtes) → entrées brutes, ou null si abusive. */\r\nfunction splitChain(raw: string | undefined): string[] | null {\r\n if (raw === undefined || raw.trim() === \"\") return [];\r\n if (raw.length > MAX_XFF_LENGTH) return null;\r\n const parts = raw.split(\",\").map((s) => s.trim());\r\n if (parts.length > MAX_XFF_ENTRIES) return null;\r\n return parts;\r\n}\r\n\r\nfunction socketOf(input: IpInput): ParsedIp | null {\r\n return input.socketIp ? parseIpToken(input.socketIp) : null;\r\n}\r\n\r\nfunction hasForwardedHeader(h: HeaderBag): boolean {\r\n return (\r\n getHeader(h, \"x-forwarded-for\") !== undefined ||\r\n getHeader(h, \"forwarded\") !== undefined ||\r\n getHeader(h, \"x-real-ip\") !== undefined\r\n );\r\n}\r\n\r\n/** Lecture de droite à gauche, en sautant les proxys de confiance (liste de CIDR). */\r\nfunction walkTrusted(input: IpInput, trusted: readonly Cidr[]): ResolvedIp {\r\n const sock = socketOf(input);\r\n if (!sock) return { advice: [\"no_socket_ip\"] };\r\n if (!anyCidrContains(trusted, sock)) {\r\n // La connexion ne vient PAS d'un de nos proxys : l'en-tête n'est pas fiable\r\n // (quelqu'un joint le serveur directement). Seule l'adresse TCP est sûre.\r\n const advice: IpAdvice[] = [\"not_from_trusted_proxy\"];\r\n return { ip: formatIp(sock), advice };\r\n }\r\n const chain = splitChain(getHeader(input.headers, \"x-forwarded-for\"));\r\n if (chain === null) return { advice: [\"invalid_forwarded_chain\"] };\r\n const entries = [...chain.map((e) => ({ raw: e })), { raw: \"\", parsed: sock }];\r\n for (let i = entries.length - 1; i >= 0; i--) {\r\n const e = entries[i]!;\r\n const parsed = \"parsed\" in e && e.parsed ? e.parsed : parseIpToken(e.raw);\r\n if (!parsed) return { advice: [\"invalid_forwarded_chain\"] }; // illisible : on échoue FERMÉ\r\n if (!anyCidrContains(trusted, parsed)) return { ip: formatIp(parsed), advice: [] };\r\n }\r\n return { advice: [\"all_addresses_trusted\"] };\r\n}\r\n\r\nfunction walkCount(input: IpInput, count: number): ResolvedIp {\r\n const sock = socketOf(input);\r\n if (!sock) return { advice: [\"no_socket_ip\"] };\r\n const chain = splitChain(getHeader(input.headers, \"x-forwarded-for\"));\r\n if (chain === null) return { advice: [\"invalid_forwarded_chain\"] };\r\n // chaîne complète = XFF + adresse de connexion ; N proxys → le visiteur est N rangs avant la fin.\r\n const total = chain.length + 1;\r\n if (total < count + 1) {\r\n // Moins de maillons que de proxys déclarés : la requête n'a pas traversé tous\r\n // nos proxys. L'adresse TCP est la seule fiable.\r\n return { ip: formatIp(sock), advice: [\"chain_too_short\"] };\r\n }\r\n const idx = total - 1 - count;\r\n const raw = idx === chain.length ? null : chain[idx]!;\r\n const parsed = raw === null ? sock : parseIpToken(raw);\r\n if (!parsed) return { advice: [\"invalid_forwarded_chain\"] };\r\n return { ip: formatIp(parsed), advice: [] };\r\n}\r\n\r\nfunction singleHeaderIp(input: IpInput, header: string): ResolvedIp | null {\r\n const raw = getHeader(input.headers, header);\r\n if (raw === undefined) return null;\r\n if (raw.includes(\",\")) return null; // une liste n'est pas une adresse unique\r\n const p = parseIpToken(raw);\r\n return p ? { ip: formatIp(p), advice: [] } : null;\r\n}\r\n\r\n/**\r\n * Prépare la lecture de l'IP. Lève ZarexConfigError (au démarrage) si le choix\r\n * est absent, incohérent ou invalide : un oubli doit se voir tout de suite.\r\n */\r\nexport function createIpResolver(source: IpSource | undefined): IpResolver {\r\n if (source === undefined || source === null) {\r\n throw new ZarexConfigError(\r\n \"Zarex: `ipSource` is required. It tells the SDK how to find the visitor's IP:\\n\" +\r\n ' - \"socket\" no proxy in front of your app\\n' +\r\n ' - \"express\" (Express adapter) let Express decide through app.set(\"trust proxy\", …)\\n' +\r\n ' - \"framework\" (SvelteKit / Fastify adapters) let the framework decide\\n' +\r\n ' - \"cloudflare\" | \"vercel\" | \"fly\" | \"cloudflare-workers\" hosted behind / on that provider\\n' +\r\n ' - \"auto\" detect Vercel/Fly from the server environment\\n' +\r\n \" - { proxies: N } N trusted proxies in front of your app\\n\" +\r\n \" - { trustedProxies: [...] } CIDR ranges of your proxies\\n\" +\r\n \"A wrong choice makes every visitor look like one single address (or lets attackers forge theirs).\"\r\n );\r\n }\r\n\r\n if (source === \"socket\") {\r\n return {\r\n label: \"socket\",\r\n resolve(input) {\r\n const sock = socketOf(input);\r\n if (!sock) return { advice: [\"no_socket_ip\"] };\r\n const advice: IpAdvice[] = hasForwardedHeader(input.headers) ? [\"forwarded_header_ignored\"] : [];\r\n return { ip: formatIp(sock), advice };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"express\") {\r\n return {\r\n label: \"express\",\r\n resolve(input) {\r\n const fromExpress = input.expressIp ? parseIpToken(input.expressIp) : null;\r\n const ip = fromExpress ?? socketOf(input);\r\n if (!ip) return { advice: [\"no_socket_ip\"] };\r\n // Express n'a pas été configuré pour les proxys mais un proxy semble présent.\r\n const advice: IpAdvice[] =\r\n input.expressTrustProxy === false && hasForwardedHeader(input.headers) ? [\"forwarded_header_ignored\"] : [];\r\n return { ip: formatIp(ip), advice };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"framework\") {\r\n return {\r\n label: \"framework\",\r\n resolve(input) {\r\n const fromFramework = input.frameworkIp ? parseIpToken(input.frameworkIp) : null;\r\n const ip = fromFramework ?? socketOf(input);\r\n if (!ip) return { advice: [\"no_socket_ip\"] };\r\n return { ip: formatIp(ip), advice: [] };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"auto\") {\r\n // Détection par l'ENVIRONNEMENT du serveur uniquement (un visiteur ne peut pas\r\n // l'influencer). Jamais par des en-têtes de requête : n'importe qui peut en envoyer.\r\n if (readEnv(\"VERCEL\")) return withLabel(createIpResolver(\"vercel\"), \"auto:vercel\");\r\n if (readEnv(\"FLY_APP_NAME\")) return withLabel(createIpResolver(\"fly\"), \"auto:fly\");\r\n throw new ZarexConfigError(\r\n 'Zarex: ipSource \"auto\" could not detect your platform (looked for the VERCEL and FLY_APP_NAME environment variables). ' +\r\n 'Choose explicitly: \"socket\", \"cloudflare\", { proxies: N } or { trustedProxies: [...] }.'\r\n );\r\n }\r\n\r\n if (source === \"cloudflare\") {\r\n const cf = compileCidrs(CLOUDFLARE_RANGES, \"built-in Cloudflare ranges\");\r\n return {\r\n label: \"provider:cloudflare\",\r\n resolve(input) {\r\n const sock = socketOf(input);\r\n if (!sock) return { advice: [\"no_socket_ip\"] };\r\n if (!anyCidrContains(cf, sock)) {\r\n // Pas une adresse Cloudflare : on ne croit pas CF-Connecting-IP (falsifiable).\r\n return { ip: formatIp(sock), advice: [\"not_from_trusted_proxy\"] };\r\n }\r\n const r = singleHeaderIp(input, \"cf-connecting-ip\");\r\n return r ?? { advice: [\"missing_provider_header\"] };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"vercel\") {\r\n // Vercel écrase lui-même X-Forwarded-For et n'y transmet pas d'adresse externe\r\n // (doc Vercel « Request headers »). Valable UNIQUEMENT quand l'application tourne sur Vercel.\r\n //\r\n // `x-vercel-forwarded-for` est le seul en-tête des trois posé EXCLUSIVEMENT par la plateforme\r\n // (jamais par le visiteur) : c'est le seul qu'on accepte SANS avertissement. Les deux autres\r\n // (`x-real-ip`, `x-forwarded-for`) sont des repis : utiles si l'en-tête privilégié est absent\r\n // pour une raison quelconque (CDN/WAF devant Vercel, runtime atypique…), mais on le SIGNALE\r\n // toujours — sans ça, un repli silencieux sur un en-tête potentiellement falsifiable (si on ne\r\n // tourne pas vraiment sur Vercel, par exemple un mauvais choix d'`ipSource`) ne laisserait\r\n // aucune trace. Trouvé lors de l'audit du 04/11/2026 (voir AUDIT_SECURITE_COMPLET.md).\r\n return {\r\n label: \"provider:vercel\",\r\n resolve(input) {\r\n const headersByTrust = [\"x-vercel-forwarded-for\", \"x-real-ip\", \"x-forwarded-for\"];\r\n for (let i = 0; i < headersByTrust.length; i++) {\r\n const h = headersByTrust[i]!;\r\n const raw = getHeader(input.headers, h);\r\n if (raw === undefined) continue;\r\n const last = raw.split(\",\").pop()!; // en cas de liste : le plus proche de la plateforme\r\n const p = parseIpToken(last);\r\n if (p) {\r\n const advice: IpAdvice[] = i === 0 ? [] : [\"weak_provider_header\"];\r\n return { ip: formatIp(p), advice };\r\n }\r\n }\r\n return { advice: [\"missing_provider_header\"] };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"cloudflare-workers\") {\r\n // Un Worker n'est joignable QUE via Cloudflare, qui écrase CF-Connecting-IP. Il n'y a pas d'adresse de\r\n // connexion dans un Worker, d'où l'absence de contrôle de plage. Valable uniquement dans un Worker.\r\n return {\r\n label: \"provider:cloudflare-workers\",\r\n resolve(input) {\r\n return singleHeaderIp(input, \"cf-connecting-ip\") ?? { advice: [\"missing_provider_header\"] };\r\n },\r\n };\r\n }\r\n\r\n if (source === \"fly\") {\r\n return {\r\n label: \"provider:fly\",\r\n resolve(input) {\r\n return singleHeaderIp(input, \"fly-client-ip\") ?? { advice: [\"missing_provider_header\"] };\r\n },\r\n };\r\n }\r\n\r\n if (typeof source === \"object\" && \"proxies\" in source) {\r\n const n = source.proxies;\r\n if (!Number.isInteger(n) || n < 1 || n > 10) {\r\n throw new ZarexConfigError('Zarex: ipSource { proxies: N } needs an integer between 1 and 10 (use \"socket\" for no proxy).');\r\n }\r\n return { label: `proxies:${n}`, resolve: (input) => walkCount(input, n) };\r\n }\r\n\r\n if (typeof source === \"object\" && \"trustedProxies\" in source) {\r\n if (!Array.isArray(source.trustedProxies) || source.trustedProxies.length === 0) {\r\n throw new ZarexConfigError(\"Zarex: ipSource { trustedProxies } needs a non-empty list of IPs or CIDR ranges.\");\r\n }\r\n const trusted = compileCidrs(source.trustedProxies, \"trustedProxies\");\r\n return { label: \"trusted-proxies\", resolve: (input) => walkTrusted(input, trusted) };\r\n }\r\n\r\n throw new ZarexConfigError(\"Zarex: unknown `ipSource` value.\");\r\n}\r\n\r\nfunction withLabel(r: IpResolver, label: string): IpResolver {\r\n return { label, resolve: r.resolve };\r\n}\r\n\r\n/** Utilitaire exporté : vrai si l'adresse est lisible (sert aux adaptateurs). */\r\nexport function isValidIp(s: string): boolean {\r\n return parseIp(s) !== null;\r\n}","/**\r\n * Lecture et comparaison d'adresses IP SANS API Node (le cœur doit tourner dans\r\n * le middleware Next.js / les Workers, où `node:net` n'existe pas).\r\n *\r\n * Choix de sécurité : on est STRICT. Tout ce qui est ambigu est refusé\r\n * (zéros initiaux « 010.0.0.1 » lus en octal par certains logiciels, zone\r\n * « %eth0 », espaces, texte autour). Une IP refusée = « IP inconnue », jamais\r\n * « IP devinée ».\r\n */\r\n\r\nexport interface ParsedIp {\r\n version: 4 | 6;\r\n bytes: Uint8Array; // 4 ou 16 octets\r\n}\r\n\r\nexport function parseIPv4(s: string): Uint8Array | null {\r\n const parts = s.split(\".\");\r\n if (parts.length !== 4) return null;\r\n const out = new Uint8Array(4);\r\n for (let i = 0; i < 4; i++) {\r\n const p = parts[i]!;\r\n if (!/^(0|[1-9][0-9]{0,2})$/.test(p)) return null; // pas de zéro initial, 1 à 3 chiffres\r\n const n = Number(p);\r\n if (n > 255) return null;\r\n out[i] = n;\r\n }\r\n return out;\r\n}\r\n\r\nexport function parseIPv6(s: string): Uint8Array | null {\r\n if (s.length < 2 || s.length > 45 || s.includes(\"%\")) return null;\r\n // Queue IPv4 intégrée (::ffff:1.2.3.4)\r\n let tail: Uint8Array | null = null;\r\n const lastColon = s.lastIndexOf(\":\");\r\n if (lastColon === -1) return null;\r\n const maybeV4 = s.slice(lastColon + 1);\r\n let head = s;\r\n if (maybeV4.includes(\".\")) {\r\n tail = parseIPv4(maybeV4);\r\n if (!tail) return null;\r\n head = s.slice(0, lastColon + 1) + \"0:0\"; // remplace par deux groupes, réécrits ensuite\r\n }\r\n const dbl = head.indexOf(\"::\");\r\n if (dbl !== head.lastIndexOf(\"::\")) return null; // un seul « :: »\r\n const toGroups = (part: string): string[] | null => {\r\n if (part === \"\") return [];\r\n const g = part.split(\":\");\r\n return g.every((x) => /^[0-9a-fA-F]{1,4}$/.test(x)) ? g : null;\r\n };\r\n let groups: string[];\r\n if (dbl !== -1) {\r\n const left = toGroups(head.slice(0, dbl));\r\n const right = toGroups(head.slice(dbl + 2));\r\n if (!left || !right) return null;\r\n const missing = 8 - left.length - right.length;\r\n if (missing < 1) return null; // « :: » doit remplacer au moins un groupe\r\n groups = [...left, ...Array<string>(missing).fill(\"0\"), ...right];\r\n } else {\r\n const all = toGroups(head);\r\n if (!all || all.length !== 8) return null;\r\n groups = all;\r\n }\r\n if (groups.length !== 8) return null;\r\n const out = new Uint8Array(16);\r\n groups.forEach((g, i) => {\r\n const n = parseInt(g, 16);\r\n out[i * 2] = n >> 8;\r\n out[i * 2 + 1] = n & 0xff;\r\n });\r\n if (tail) out.set(tail, 12);\r\n return out;\r\n}\r\n\r\nconst V4_MAPPED_PREFIX = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff];\r\n\r\n/** Lit une adresse seule (sans port, sans crochets). IPv4 mappée en IPv6 → IPv4. */\r\nexport function parseIp(input: string): ParsedIp | null {\r\n if (typeof input !== \"string\" || input.length === 0 || input.length > 45) return null;\r\n if (input.includes(\":\")) {\r\n const b = parseIPv6(input);\r\n if (!b) return null;\r\n if (V4_MAPPED_PREFIX.every((v, i) => b[i] === v)) return { version: 4, bytes: b.slice(12) };\r\n return { version: 6, bytes: b };\r\n }\r\n const b = parseIPv4(input);\r\n return b ? { version: 4, bytes: b } : null;\r\n}\r\n\r\n/**\r\n * Extrait l'adresse d'un jeton d'en-tête tel qu'on le rencontre en pratique :\r\n * « 1.2.3.4 », « 1.2.3.4:5678 », « [2001:db8::1] », « [2001:db8::1]:443 ».\r\n */\r\nexport function parseIpToken(raw: string): ParsedIp | null {\r\n const t = raw.trim();\r\n if (t.length === 0 || t.length > 64) return null;\r\n if (t.startsWith(\"[\")) {\r\n const end = t.indexOf(\"]\");\r\n if (end === -1) return null;\r\n const rest = t.slice(end + 1);\r\n if (rest !== \"\" && !/^:[0-9]{1,5}$/.test(rest)) return null;\r\n return parseIp(t.slice(1, end));\r\n }\r\n const colons = (t.match(/:/g) ?? []).length;\r\n if (colons === 1) {\r\n const [addr, port] = t.split(\":\") as [string, string];\r\n if (!/^[0-9]{1,5}$/.test(port)) return null;\r\n return parseIp(addr);\r\n }\r\n return parseIp(t);\r\n}\r\n\r\n/** Forme canonique : IPv4 en points, IPv6 compressée en minuscules (RFC 5952). */\r\nexport function formatIp(ip: ParsedIp): string {\r\n if (ip.version === 4) return Array.from(ip.bytes).join(\".\");\r\n const g: number[] = [];\r\n for (let i = 0; i < 16; i += 2) g.push((ip.bytes[i]! << 8) | ip.bytes[i + 1]!);\r\n // plus longue suite de zéros (≥ 2 groupes)\r\n let bestStart = -1;\r\n let bestLen = 0;\r\n for (let i = 0; i < 8; ) {\r\n if (g[i] !== 0) {\r\n i++;\r\n continue;\r\n }\r\n let j = i;\r\n while (j < 8 && g[j] === 0) j++;\r\n if (j - i > bestLen) {\r\n bestStart = i;\r\n bestLen = j - i;\r\n }\r\n i = j;\r\n }\r\n if (bestLen < 2) return g.map((x) => x.toString(16)).join(\":\");\r\n const left = g.slice(0, bestStart).map((x) => x.toString(16)).join(\":\");\r\n const right = g.slice(bestStart + bestLen).map((x) => x.toString(16)).join(\":\");\r\n return `${left}::${right}`;\r\n}\r\n\r\n/** Normalise un texte d'IP (avec ou sans port/crochets) ; null si illisible. */\r\nexport function normalizeIp(raw: string | undefined | null): string | null {\r\n if (!raw) return null;\r\n const p = parseIpToken(raw);\r\n return p ? formatIp(p) : null;\r\n}\r\n\r\nexport interface Cidr {\r\n version: 4 | 6;\r\n bytes: Uint8Array;\r\n prefix: number;\r\n}\r\n\r\n/** « 10.0.0.0/8 », « 2400:cb00::/32 », ou une IP seule (= /32 ou /128). Lève Error si invalide. */\r\nexport function parseCidr(text: string): Cidr {\r\n const [addr, len, extra] = text.trim().split(\"/\");\r\n if (extra !== undefined || !addr) throw new Error(`invalid CIDR: ${JSON.stringify(text.slice(0, 64))}`);\r\n const ip = parseIp(addr);\r\n if (!ip) throw new Error(`invalid CIDR address: ${JSON.stringify(text.slice(0, 64))}`);\r\n const max = ip.version === 4 ? 32 : 128;\r\n let prefix = max;\r\n if (len !== undefined) {\r\n if (!/^[0-9]{1,3}$/.test(len)) throw new Error(`invalid CIDR prefix: ${JSON.stringify(text.slice(0, 64))}`);\r\n prefix = Number(len);\r\n if (prefix > max) throw new Error(`invalid CIDR prefix: ${JSON.stringify(text.slice(0, 64))}`);\r\n }\r\n return { version: ip.version, bytes: ip.bytes, prefix };\r\n}\r\n\r\nexport function cidrContains(c: Cidr, ip: ParsedIp): boolean {\r\n if (c.version !== ip.version) return false;\r\n const full = Math.floor(c.prefix / 8);\r\n for (let i = 0; i < full; i++) if (c.bytes[i] !== ip.bytes[i]) return false;\r\n const rem = c.prefix % 8;\r\n if (rem === 0) return true;\r\n const mask = (0xff << (8 - rem)) & 0xff;\r\n return (c.bytes[full]! & mask) === (ip.bytes[full]! & mask);\r\n}\r\n\r\nexport function anyCidrContains(list: readonly Cidr[], ip: ParsedIp): boolean {\r\n return list.some((c) => cidrContains(c, ip));\r\n}","/**\r\n * Plages d'adresses de fournisseurs. Elles CHANGENT : ces listes sont embarquées\r\n * à chaque version du SDK. Pour garder des plages à jour sans attendre une\r\n * version, passez votre propre liste : `ipSource: { trustedProxies: [...] }`.\r\n */\r\n\r\n/** Réseaux privés / locaux : typiquement un proxy ou un répartiteur de charge interne. */\r\nexport const PRIVATE_NETWORKS: readonly string[] = [\r\n \"127.0.0.0/8\",\r\n \"10.0.0.0/8\",\r\n \"172.16.0.0/12\",\r\n \"192.168.0.0/16\",\r\n \"169.254.0.0/16\",\r\n \"100.64.0.0/10\", // CGNAT (RFC 6598)\r\n \"::1/128\",\r\n \"fc00::/7\",\r\n \"fe80::/10\",\r\n];\r\n\r\n/** Plages publiées par Cloudflare (https://www.cloudflare.com/ips-v4 et /ips-v6) — vérifiées le 24/10/2026. */\r\nexport const CLOUDFLARE_RANGES: readonly string[] = [\r\n \"173.245.48.0/20\",\r\n \"103.21.244.0/22\",\r\n \"103.22.200.0/22\",\r\n \"103.31.4.0/22\",\r\n \"141.101.64.0/18\",\r\n \"108.162.192.0/18\",\r\n \"190.93.240.0/20\",\r\n \"188.114.96.0/20\",\r\n \"197.234.240.0/22\",\r\n \"198.41.128.0/17\",\r\n \"162.158.0.0/15\",\r\n \"104.16.0.0/13\",\r\n \"104.24.0.0/14\",\r\n \"172.64.0.0/13\",\r\n \"131.0.72.0/22\",\r\n \"2400:cb00::/32\",\r\n \"2606:4700::/32\",\r\n \"2803:f800::/32\",\r\n \"2405:b500::/32\",\r\n \"2405:8100::/32\",\r\n \"2a06:98c0::/29\",\r\n \"2c0f:f248::/32\",\r\n];","/**\r\n * SHA-256 en JavaScript pur (synchrone, sans dépendance, sans API Node).\r\n * Sert à hacher les « caractéristiques » (identifiant d'utilisateur…) AVANT envoi :\r\n * le moteur n'a besoin que d'une empreinte stable, pas de l'identifiant lui-même.\r\n * Vérifié contre node:crypto dans les tests.\r\n */\r\nconst K = new Uint32Array([\r\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,\r\n 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,\r\n 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,\r\n 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\r\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,\r\n 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\r\n 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\r\n]);\r\n\r\nexport function sha256Hex(text: string): string {\r\n const data = new TextEncoder().encode(text);\r\n const bitLen = data.length * 8;\r\n const padded = new Uint8Array(((data.length + 9 + 63) >> 6) << 6);\r\n padded.set(data);\r\n padded[data.length] = 0x80;\r\n const view = new DataView(padded.buffer);\r\n view.setUint32(padded.length - 8, Math.floor(bitLen / 0x100000000));\r\n view.setUint32(padded.length - 4, bitLen >>> 0);\r\n\r\n const h = new Uint32Array([0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]);\r\n const w = new Uint32Array(64);\r\n const rotr = (x: number, n: number) => (x >>> n) | (x << (32 - n));\r\n\r\n for (let off = 0; off < padded.length; off += 64) {\r\n for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);\r\n for (let i = 16; i < 64; i++) {\r\n const s0 = rotr(w[i - 15]!, 7) ^ rotr(w[i - 15]!, 18) ^ (w[i - 15]! >>> 3);\r\n const s1 = rotr(w[i - 2]!, 17) ^ rotr(w[i - 2]!, 19) ^ (w[i - 2]! >>> 10);\r\n w[i] = (w[i - 16]! + s0 + w[i - 7]! + s1) >>> 0;\r\n }\r\n let [a, b, c, d, e, f, g, hh] = h as unknown as number[] as [number, number, number, number, number, number, number, number];\r\n for (let i = 0; i < 64; i++) {\r\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\r\n const ch = (e & f) ^ (~e & g);\r\n const t1 = (hh + S1 + ch + K[i]! + w[i]!) >>> 0;\r\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\r\n const maj = (a & b) ^ (a & c) ^ (b & c);\r\n const t2 = (S0 + maj) >>> 0;\r\n hh = g;\r\n g = f;\r\n f = e;\r\n e = (d + t1) >>> 0;\r\n d = c;\r\n c = b;\r\n b = a;\r\n a = (t1 + t2) >>> 0;\r\n }\r\n h[0] = (h[0]! + a) >>> 0;\r\n h[1] = (h[1]! + b) >>> 0;\r\n h[2] = (h[2]! + c) >>> 0;\r\n h[3] = (h[3]! + d) >>> 0;\r\n h[4] = (h[4]! + e) >>> 0;\r\n h[5] = (h[5]! + f) >>> 0;\r\n h[6] = (h[6]! + g) >>> 0;\r\n h[7] = (h[7]! + hh) >>> 0;\r\n }\r\n return Array.from(h, (x) => x.toString(16).padStart(8, \"0\")).join(\"\");\r\n}","import { ZarexConfigError, type Algorithm, type Mode, type Oversize, type RuleDTO } from \"@zarex/client\";\r\nimport { NEVER_FORWARD } from \"./headers.js\";\r\n\r\n/** Une règle prête à être envoyée au moteur. Créez-les avec shield(), rateLimit(), tokenBucket(), detectBot(). */\r\nexport type Rule = RuleDTO;\r\n\r\nconst MODES: readonly Mode[] = [\"LIVE\", \"DRY_RUN\", \"OFF\"];\r\nconst ALGOS: readonly Algorithm[] = [\"FIXED_WINDOW\", \"SLIDING_WINDOW\"];\r\nconst WINDOW_RE = /^[1-9][0-9]{0,8}[smhd]$/;\r\nconst NAME_RE = /^[A-Za-z0-9_.:\\-]{1,64}$/;\r\n\r\nfunction mode(fn: string, m: unknown): Mode {\r\n const v = m ?? \"LIVE\";\r\n if (!MODES.includes(v as Mode)) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`mode\\` must be \"LIVE\", \"DRY_RUN\" or \"OFF\".`);\r\n }\r\n return v as Mode;\r\n}\r\n\r\nconst OVERSIZE: readonly Oversize[] = [\"deny\", \"inspect\", \"allow\"];\r\n/** `oversize` : absent = valeur par défaut du moteur (chemin/en-têtes : deny ; corps/texte : inspect). */\r\nfunction oversize(fn: string, v: unknown): { oversize?: Oversize } {\r\n if (v === undefined) return {};\r\n if (!OVERSIZE.includes(v as Oversize)) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`oversize\\` must be \"deny\", \"inspect\" or \"allow\".`);\r\n }\r\n return { oversize: v as Oversize };\r\n}\r\n\r\nfunction int(fn: string, name: string, v: unknown, min: number, max: number): number {\r\n if (typeof v !== \"number\" || !Number.isInteger(v) || v < min || v > max) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`${name}\\` must be an integer between ${min} and ${max}.`);\r\n }\r\n return v;\r\n}\r\n\r\nfunction windowStr(fn: string, name: string, v: unknown): string {\r\n if (typeof v !== \"string\" || !WINDOW_RE.test(v)) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`${name}\\` must look like \"30s\", \"1m\", \"1h\" or \"1d\".`);\r\n }\r\n return v;\r\n}\r\n\r\nfunction names(fn: string, name: string, v: unknown, max: number): string[] {\r\n if (v === undefined) return [];\r\n if (!Array.isArray(v) || v.length > max || !v.every((x) => typeof x === \"string\" && NAME_RE.test(x))) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`${name}\\` must be a list of up to ${max} simple names.`);\r\n }\r\n return [...new Set(v as string[])];\r\n}\r\n\r\nexport interface ShieldOptions {\r\n mode?: Mode;\r\n /**\r\n * Que faire d'un chemin, d'en-têtes ou d'un corps PLUS GRANDS que ce que Zarex inspecte (8 Ko pour le chemin\r\n * et chaque en-tête, 48 Ko pour le corps). \"deny\" : refuser (défaut pour le chemin et les en-têtes : une URL\r\n * de 8 Ko est presque toujours une attaque) · \"inspect\" : analyser le début et la fin et le signaler (défaut\r\n * pour le corps) · \"allow\" : laisser passer. Dans tous les cas, l'avertissement `path_truncated` (etc.) apparaît.\r\n */\r\n oversize?: Oversize;\r\n /**\r\n * Seuil de refus, de 1 à 100. Chaque règle du Shield pèse selon sa certitude (injection SQL `UNION SELECT` : 100 ;\r\n * simple commentaire `--` : 45…) et les règles déclenchées se combinent. Défaut (non renseigné) : TOUTE règle\r\n * déclenchée refuse. Avec `threshold: 80`, un indice faible isolé passe (et le score est signalé dans la raison),\r\n * mais une attaque nette ou plusieurs indices cumulés sont refusés. Augmenter le seuil réduit les faux positifs ET la protection :\r\n * à n'utiliser qu'après avoir observé votre trafic en mode DRY_RUN.\r\n */\r\n threshold?: number;\r\n}\r\n/** Pare-feu applicatif : injections SQL, XSS, traversée de répertoires… */\r\nexport function shield(o: ShieldOptions = {}): Rule {\r\n if (o.threshold !== undefined && (!Number.isInteger(o.threshold) || o.threshold < 1 || o.threshold > 100)) {\r\n throw new ZarexConfigError(\"Zarex: shield: `threshold` must be a whole number between 1 and 100.\");\r\n }\r\n return { shield: { mode: mode(\"shield\", o.mode), ...oversize(\"shield\", o.oversize), ...(o.threshold !== undefined ? { threshold: o.threshold } : {}) } };\r\n}\r\n\r\nexport interface RateLimitOptions {\r\n mode?: Mode;\r\n /** Requêtes autorisées par fenêtre (1 à 1 000 000). */\r\n max: number;\r\n /** Durée de la fenêtre : \"30s\", \"1m\", \"1h\", \"1d\". */\r\n window: string;\r\n algorithm?: Algorithm;\r\n /**\r\n * Ce qui identifie « un visiteur ». Par défaut l'adresse IP.\r\n * Ajoutez ici des noms comme \"userId\" (valeur donnée à protect(…, { characteristics })).\r\n * Recommandé sur les routes sensibles : des opérateurs mobiles font partager une\r\n * même adresse IP à de nombreux utilisateurs.\r\n */\r\n characteristics?: string[];\r\n}\r\nexport function rateLimit(o: RateLimitOptions): Rule {\r\n const algorithm = o.algorithm ?? \"FIXED_WINDOW\";\r\n if (!ALGOS.includes(algorithm)) {\r\n throw new ZarexConfigError('Zarex: rateLimit: `algorithm` must be \"FIXED_WINDOW\" or \"SLIDING_WINDOW\".');\r\n }\r\n return {\r\n rate_limit: {\r\n mode: mode(\"rateLimit\", o.mode),\r\n max: int(\"rateLimit\", \"max\", o.max, 1, 1_000_000),\r\n window: windowStr(\"rateLimit\", \"window\", o.window),\r\n algorithm,\r\n characteristics: names(\"rateLimit\", \"characteristics\", o.characteristics ?? [\"ip\"], 16),\r\n },\r\n };\r\n}\r\n\r\nexport interface TokenBucketOptions {\r\n mode?: Mode;\r\n /** Jetons maximum dans le seau. */\r\n capacity: number;\r\n /** Jetons rendus à chaque intervalle. */\r\n refillRate: number;\r\n /** \"1m\", \"1h\"… */\r\n interval: string;\r\n characteristics?: string[];\r\n}\r\n/** Budget de jetons (ex. appels à un modèle d'IA). Le nombre consommé se donne via protect(…, { tokensRequested }). */\r\nexport function tokenBucket(o: TokenBucketOptions): Rule {\r\n return {\r\n token_bucket: {\r\n mode: mode(\"tokenBucket\", o.mode),\r\n capacity: int(\"tokenBucket\", \"capacity\", o.capacity, 1, 1_000_000),\r\n refill_rate: int(\"tokenBucket\", \"refillRate\", o.refillRate, 1, 1_000_000),\r\n interval: windowStr(\"tokenBucket\", \"interval\", o.interval),\r\n characteristics: names(\"tokenBucket\", \"characteristics\", o.characteristics ?? [\"ip\"], 16),\r\n },\r\n };\r\n}\r\n\r\nexport interface DetectBotOptions {\r\n mode?: Mode;\r\n /** Robots autorisés : \"CATEGORY:SEARCH_ENGINE\", \"GOOGLEBOT\"… */\r\n allow?: string[];\r\n /** Robots à bloquer explicitement. */\r\n deny?: string[];\r\n}\r\nexport function detectBot(o: DetectBotOptions = {}): Rule {\r\n return {\r\n bot_detection: {\r\n mode: mode(\"detectBot\", o.mode),\r\n allow: names(\"detectBot\", \"allow\", o.allow, 64),\r\n deny: names(\"detectBot\", \"deny\", o.deny, 64),\r\n },\r\n };\r\n}\r\n\r\n// ───────────────────────────── Étape 11 ─────────────────────────────\r\n\r\nconst SENSITIVE_TYPES = [\"EMAIL\", \"PHONE\", \"CREDIT_CARD\", \"PASSWORD\", \"SSN\", \"API_KEY\", \"JWT\", \"PRIVATE_KEY\", \"IBAN\", \"HIGH_ENTROPY\"] as const;\r\nexport type SensitiveType = (typeof SENSITIVE_TYPES)[number];\r\nexport { SENSITIVE_TYPES };\r\n\r\nconst EMAIL_DENY = [\"INVALID\", \"DISPOSABLE\", \"NO_MX\", \"FREE\", \"TYPO\"] as const;\r\nconst PHONE_DENY = [\"INVALID\", \"VOIP\", \"FIXED\", \"PREMIUM_RATE\", \"TOLL_FREE\", \"LEGACY_FORMAT\"] as const;\r\nconst INSPECT = [\"body\", \"text_content\"] as const;\r\nconst FILTER_OPS = [\"EQ\", \"NE\", \"CONTAINS\", \"STARTS_WITH\", \"IN\", \"NOT_IN\", \"REGEX\", \"LIKE\", \"LEN_EQ\", \"LEN_GT\", \"LEN_LT\"] as const;\r\nconst REGION_RE = /^(?:[A-Z]{2}|AFRICA)$/;\r\n\r\nfunction oneOf<T extends string>(fn: string, name: string, v: unknown, allowed: readonly T[], max = 64): T[] {\r\n if (v === undefined) return [];\r\n if (!Array.isArray(v) || v.length > max || !v.every((x) => typeof x === \"string\" && (allowed as readonly string[]).includes(x))) {\r\n throw new ZarexConfigError(`Zarex: ${fn}: \\`${name}\\` accepts only: ${allowed.join(\", \")}.`);\r\n }\r\n return [...new Set(v as T[])];\r\n}\r\n\r\nexport interface SensitiveInfoOptions {\r\n mode?: Mode;\r\n /**\r\n * Types à refuser. Défaut : les SECRETS (CREDIT_CARD, SSN, API_KEY, JWT, PRIVATE_KEY, IBAN).\r\n * EMAIL, PHONE et PASSWORD ne sont refusés que si vous les demandez : un formulaire d'inscription ou de\r\n * connexion en contient légitimement.\r\n * HIGH_ENTROPY (sur demande) : valeur aléatoire derrière un mot comme `secret`, `token`, `api_key`, `Bearer`… dont le format\r\n * est inconnu (ex. clé MTN MoMo : 32 caractères hexadécimaux sans préfixe). Plus de faux positifs possibles que API_KEY : essayez d'abord en DRY_RUN.\r\n * API_KEY reconnaît aussi Wave (`wave_sn_prod_…`), Flutterwave (`FLWSECK…`) et Paystack (`sk_live_…`).\r\n */\r\n deny?: SensitiveType[];\r\n /** Où chercher : \"body\" (corps de la requête, nécessite inspectBody) et/ou \"text_content\" (texte donné via call). Défaut : les deux. */\r\n inspect?: (\"body\" | \"text_content\")[];\r\n /** Entrée plus grande que ce qu'on analyse (corps 48 Ko, texte 32 Ko). Défaut \"inspect\" : début + fin analysés, signalé. */\r\n oversize?: Oversize;\r\n}\r\n/** Refuse une requête qui contient des données sensibles (carte bancaire, clé d'API, mot de passe…). */\r\nexport function sensitiveInfo(o: SensitiveInfoOptions = {}): Rule {\r\n return {\r\n sensitive_info: {\r\n mode: mode(\"sensitiveInfo\", o.mode),\r\n deny: oneOf(\"sensitiveInfo\", \"deny\", o.deny, SENSITIVE_TYPES),\r\n inspect: oneOf(\"sensitiveInfo\", \"inspect\", o.inspect, INSPECT),\r\n ...oversize(\"sensitiveInfo\", o.oversize),\r\n },\r\n };\r\n}\r\n\r\nexport interface EmailValidationOptions {\r\n mode?: Mode;\r\n /**\r\n * Défaut : [\"INVALID\", \"DISPOSABLE\", \"NO_MX\"].\r\n * INVALID = mal formée · DISPOSABLE = adresse jetable · NO_MX = le domaine ne reçoit pas de courrier\r\n * · FREE = Gmail/Yahoo… · TYPO = faute probable (« gmial.com »).\r\n */\r\n deny?: (typeof EMAIL_DENY)[number][];\r\n /**\r\n * Domaines (et leurs sous-domaines) JAMAIS jugés jetables / gratuits / sans MX : vos clients connus,\r\n * un domaine absent de la liste mondiale… Une adresse mal formée reste refusée.\r\n */\r\n allowDomains?: string[];\r\n /** Domaines (et sous-domaines) toujours refusés, ex. un concurrent ou un domaine de fraude connu. */\r\n denyDomains?: string[];\r\n}\r\nconst DOMAIN_RE = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/;\r\nfunction domainList(name: string, v: string[] | undefined): string[] | undefined {\r\n if (v === undefined) return undefined;\r\n if (!Array.isArray(v) || v.length > 500) {\r\n throw new ZarexConfigError(`Zarex: emailValidation: \\`${name}\\` must be an array of at most 500 domains.`);\r\n }\r\n const out = v.map((d) => (typeof d === \"string\" ? d.trim().toLowerCase().replace(/^@/, \"\") : \"\"));\r\n if (!out.every((d) => DOMAIN_RE.test(d))) {\r\n throw new ZarexConfigError(`Zarex: emailValidation: \\`${name}\\` must contain valid domain names such as \"societe.bj\".`);\r\n }\r\n return out.length ? [...new Set(out)] : undefined;\r\n}\r\n/** Valide l'adresse donnée via protect(…, { email }). */\r\nexport function emailValidation(o: EmailValidationOptions = {}): Rule {\r\n const allow = domainList(\"allowDomains\", o.allowDomains);\r\n const deny = domainList(\"denyDomains\", o.denyDomains);\r\n return {\r\n email_validation: {\r\n mode: mode(\"emailValidation\", o.mode),\r\n deny: oneOf(\"emailValidation\", \"deny\", o.deny, EMAIL_DENY),\r\n ...(allow ? { allow_domains: allow } : {}),\r\n ...(deny ? { deny_domains: deny } : {}),\r\n },\r\n };\r\n}\r\n\r\nexport interface PhoneValidationOptions {\r\n mode?: Mode;\r\n /** Défaut : [\"INVALID\"]. */\r\n deny?: (typeof PHONE_DENY)[number][];\r\n /** N'accepter que ces opérateurs (ex. [\"MTN\", \"MOOV\"]). Un opérateur inconnu n'est jamais refusé. */\r\n operators?: string[];\r\n /** Pays PAR DÉFAUT pour un numéro écrit sans « + » (ex. \"BJ\"). */\r\n countryCode?: string;\r\n /** Pays acceptés : codes ISO (\"BJ\", \"TG\") ou \"AFRICA\". Vide = tous. */\r\n countries?: string[];\r\n}\r\n/** Valide le numéro donné via protect(…, { phone }). Couvre l'Afrique (mobile et fixe). */\r\nexport function phoneValidation(o: PhoneValidationOptions = {}): Rule {\r\n if (o.countryCode !== undefined && !/^[A-Z]{2}$/.test(o.countryCode)) {\r\n throw new ZarexConfigError('Zarex: phoneValidation: `countryCode` must be a 2-letter ISO code such as \"BJ\".');\r\n }\r\n if (o.countries !== undefined && (!Array.isArray(o.countries) || o.countries.length > 64 || !o.countries.every((c) => typeof c === \"string\" && REGION_RE.test(c)))) {\r\n throw new ZarexConfigError('Zarex: phoneValidation: `countries` must be ISO codes (\"BJ\", \"TG\"…) or \"AFRICA\".');\r\n }\r\n return {\r\n phone_validation: {\r\n mode: mode(\"phoneValidation\", o.mode),\r\n deny: oneOf(\"phoneValidation\", \"deny\", o.deny, PHONE_DENY),\r\n operators: names(\"phoneValidation\", \"operators\", o.operators, 64),\r\n ...(o.countryCode ? { country_code: o.countryCode } : {}),\r\n countries: o.countries ? [...new Set(o.countries)] : [],\r\n },\r\n };\r\n}\r\n\r\nexport interface PromptInjectionOptions {\r\n mode?: Mode;\r\n /** Où chercher : \"text_content\" (défaut, via protect(…, { text })) et/ou \"body\". */\r\n inspect?: (\"body\" | \"text_content\")[];\r\n /** Score de 0 à 1 à partir duquel on refuse (défaut 0.75). Plus bas = plus strict, plus de faux positifs. */\r\n confidenceThreshold?: number;\r\n /** Texte plus grand que ce qu'on analyse (32 Ko). Défaut \"inspect\" : début + fin analysés, signalé. */\r\n oversize?: Oversize;\r\n}\r\n/** Détecte les tentatives d'injection de prompt dans un message destiné à une IA (heuristique, pas une garantie). */\r\nexport function promptInjection(o: PromptInjectionOptions = {}): Rule {\r\n const t = o.confidenceThreshold;\r\n if (t !== undefined && (typeof t !== \"number\" || !(t > 0 && t <= 1))) {\r\n throw new ZarexConfigError(\"Zarex: promptInjection: `confidenceThreshold` must be a number above 0 and at most 1.\");\r\n }\r\n return {\r\n prompt_injection: {\r\n mode: mode(\"promptInjection\", o.mode),\r\n inspect: oneOf(\"promptInjection\", \"inspect\", o.inspect, INSPECT),\r\n ...(t !== undefined ? { confidence_threshold: t } : {}),\r\n ...oversize(\"promptInjection\", o.oversize),\r\n },\r\n };\r\n}\r\n\r\nexport interface FilterCondition {\r\n /**\r\n * \"ip\" · \"path\" · \"method\" · \"host\" · \"headers.<nom>\" · \"query.<nom>\" (paramètre d'URL) · \"cookies.<nom>\"\r\n * · \"ip.country\" (code ISO : \"BJ\", \"SN\"… ; \"ZZ\" = inconnu ou adresse non publique) · \"ip.asn\" (\"AS15169\" ; \"AS0\" = inconnu).\r\n * ip.country / ip.asn exigent une base d'adresses côté moteur (ZAREX_IPDB_FILE) ; sans elle, tout visiteur est « inconnu »\r\n * et un avertissement `ip_db_absent` apparaît dans vos journaux. Opérateurs : EQ, NE, IN, NOT_IN. `cookies.*` demande `inspectCookies: true` (les cookies ne sont sinon pas transmis).\r\n * Un paramètre répété (`?a=1&a=2`) est vu avec toutes ses valeurs ; un champ absent vaut \"\".\r\n */\r\n field: string;\r\n operator: (typeof FILTER_OPS)[number];\r\n /**\r\n * Valeur simple (EQ, NE, CONTAINS, STARTS_WITH, REGEX).\r\n * LIKE : joker `*` (suite quelconque) et `?` (un caractère), ex. \"/admin/*\".\r\n * LEN_EQ / LEN_GT / LEN_LT : un nombre (longueur en caractères), ex. \"2000\".\r\n * Toutes les comparaisons ignorent la casse (pas besoin de « lower »).\r\n */\r\n value?: string;\r\n /** Liste (IN, NOT_IN). Pour le champ « ip », les plages CIDR sont acceptées (\"10.0.0.0/8\"). */\r\n values?: string[];\r\n /** DENY (défaut) = refuser si la condition est vraie · ALLOW = laisser passer (liste blanche). */\r\n action?: \"DENY\" | \"ALLOW\";\r\n /** true = inverse la condition : « NOT (champ opérateur valeur) ». */\r\n not?: boolean;\r\n}\r\nexport interface RequestFilterOptions {\r\n mode?: Mode;\r\n /** Au moins 1, au plus 32. */\r\n conditions: FilterCondition[];\r\n /**\r\n * \"OR\" (défaut) : la première condition vraie décide.\r\n * \"AND\" : toutes doivent être vraies, et l'action de la dernière s'applique.\r\n */\r\n logic?: \"AND\" | \"OR\";\r\n /** Chemin ou en-tête plus grand que 8 Ko. Défaut \"deny\" (l'entrée n'a pas pu être lue en entier). */\r\n oversize?: Oversize;\r\n /**\r\n * Que faire d'une requête qu'AUCUNE condition ne reconnaît ?\r\n * \"ALLOW\" (défaut) : elle passe. \"DENY\" : elle est refusée → mode « tout refuser sauf » :\r\n * listez seulement des conditions `action: \"ALLOW\"` et le reste est bloqué.\r\n * Attention : ALLOW n'exempte PAS des autres règles (Shield, débit…) ; il ne fait que « reconnaître » la requête.\r\n * Les comparaisons ignorent la casse.\r\n */\r\n default?: \"ALLOW\" | \"DENY\";\r\n}\r\n/** Filtre de requêtes selon VOS règles (chemin, méthode, IP, en-têtes…). Le chemin est normalisé avant comparaison. */\r\nexport function requestFilter(o: RequestFilterOptions): Rule {\r\n if (!o || !Array.isArray(o.conditions) || o.conditions.length < 1 || o.conditions.length > 32) {\r\n throw new ZarexConfigError(\"Zarex: requestFilter: `conditions` must contain between 1 and 32 conditions.\");\r\n }\r\n const logic = o.logic ?? \"OR\";\r\n if (logic !== \"AND\" && logic !== \"OR\") throw new ZarexConfigError('Zarex: requestFilter: `logic` must be \"AND\" or \"OR\".');\r\n const expressions = o.conditions.map((c, i) => {\r\n const at = `requestFilter: conditions[${i}]`;\r\n if (!c || typeof c.field !== \"string\" || !/^(?:ip|path|method|host|headers\\.[A-Za-z0-9_-]{1,128}|ip\\.country|ip\\.asn|(?:query|cookies)\\.[A-Za-z0-9_.\\[\\]-]{1,120})$/.test(c.field)) {\r\n throw new ZarexConfigError(`Zarex: ${at}.field must be \"ip\", \"path\", \"method\", \"host\", \"headers.<name>\", \"query.<name>\", \"cookies.<name>\", \"ip.country\" or \"ip.asn\".`);\r\n }\r\n if (!(FILTER_OPS as readonly string[]).includes(c.operator)) {\r\n throw new ZarexConfigError(`Zarex: ${at}.operator must be one of ${FILTER_OPS.join(\", \")}.`);\r\n }\r\n if (c.field.startsWith(\"headers.\") && NEVER_FORWARD.has(c.field.slice(8).toLowerCase())) {\r\n throw new ZarexConfigError(`Zarex: ${at}.field: the \"${c.field.slice(8)}\" header is never sent to the engine (it carries secrets), so a filter on it could never match.`);\r\n }\r\n const action = c.action ?? \"DENY\";\r\n if (action !== \"DENY\" && action !== \"ALLOW\") throw new ZarexConfigError(`Zarex: ${at}.action must be \"DENY\" or \"ALLOW\".`);\r\n const set = c.operator === \"IN\" || c.operator === \"NOT_IN\";\r\n if (c.field === \"ip.country\" || c.field === \"ip.asn\") {\r\n if (![\"EQ\", \"NE\", \"IN\", \"NOT_IN\"].includes(c.operator)) {\r\n throw new ZarexConfigError(`Zarex: ${at}: ${c.field} only supports EQ, NE, IN and NOT_IN.`);\r\n }\r\n const vals = c.operator === \"IN\" || c.operator === \"NOT_IN\" ? c.values ?? [] : [c.value ?? \"\"];\r\n const ok = c.field === \"ip.country\" ? (v: string) => /^[A-Za-z]{2}$/.test(v) : (v: string) => /^(?:[Aa][Ss])?\\d{1,10}$/.test(v) && Number(v.replace(/^[Aa][Ss]/, \"\")) <= 4294967295;\r\n if (!vals.every((v) => typeof v === \"string\" && ok(v))) {\r\n throw new ZarexConfigError(`Zarex: ${at}: ${c.field === \"ip.country\" ? 'use 2-letter country codes such as \"BJ\" (\"ZZ\" = unknown)' : 'use AS numbers such as \"AS15169\" (\"AS0\" = unknown)'}.`);\r\n }\r\n }\r\n if (c.not !== undefined && typeof c.not !== \"boolean\") throw new ZarexConfigError(`Zarex: ${at}.not must be a boolean.`);\r\n if (c.operator.startsWith(\"LEN_\") && !/^\\d{1,7}$/.test(c.value ?? \"\")) {\r\n throw new ZarexConfigError(`Zarex: ${at}: ${c.operator} needs \\`value\\` as a whole number between 0 and 1000000.`);\r\n }\r\n if (c.operator.startsWith(\"LEN_\") && Number(c.value) > 1_000_000) throw new ZarexConfigError(`Zarex: ${at}: ${c.operator} value must be at most 1000000.`);\r\n if (c.value !== undefined && (typeof c.value !== \"string\" || c.value.length > 256)) {\r\n throw new ZarexConfigError(`Zarex: ${at}.value must be a string of at most 256 characters.`);\r\n }\r\n if (c.values !== undefined && (!Array.isArray(c.values) || c.values.length > 64 || !c.values.every((v) => typeof v === \"string\" && v.length <= 256))) {\r\n throw new ZarexConfigError(`Zarex: ${at}.values must be a list of at most 64 strings.`);\r\n }\r\n if (set && !(c.values && c.values.length > 0)) throw new ZarexConfigError(`Zarex: ${at}: ${c.operator} needs \\`values\\`.`);\r\n if (!set && c.operator !== \"EQ\" && c.operator !== \"NE\" && !c.value) throw new ZarexConfigError(`Zarex: ${at}: ${c.operator} needs \\`value\\`.`);\r\n if (c.operator === \"REGEX\") {\r\n try {\r\n new RegExp(c.value ?? \"\");\r\n } catch {\r\n throw new ZarexConfigError(`Zarex: ${at}.value is not a valid regular expression.`);\r\n }\r\n // Le moteur utilise RE2 (temps linéaire, donc sans ReDoS) : pas de lookaround ni de rétro-références.\r\n if (/\\(\\?[=!]|\\(\\?<[=!]|\\\\[1-9]|\\\\k</.test(c.value ?? \"\")) {\r\n throw new ZarexConfigError(`Zarex: ${at}.value uses lookaround or back-references, which the engine does not support (it guarantees linear-time matching).`);\r\n }\r\n }\r\n return { field: c.field, operator: c.operator, ...(c.value !== undefined ? { value: c.value } : {}), ...(c.values ? { values: c.values } : {}), action, ...(c.not ? { not: true } : {}) };\r\n });\r\n if (o.default !== undefined && o.default !== \"ALLOW\" && o.default !== \"DENY\") {\r\n throw new ZarexConfigError('Zarex: requestFilter: `default` must be \"ALLOW\" or \"DENY\".');\r\n }\r\n return {\r\n request_filter: {\r\n mode: mode(\"requestFilter\", o.mode),\r\n expressions,\r\n logic,\r\n ...(o.default === \"DENY\" ? { default: \"DENY\" as const } : {}),\r\n ...oversize(\"requestFilter\", o.oversize),\r\n },\r\n };\r\n}","export { createZarex, validateRules } from \"./protector.js\";\r\nexport type {\r\n Decision,\r\n Logger,\r\n ProtectCall,\r\n ProtectInput,\r\n RuleOutcome,\r\n Zarex,\r\n ZarexOptions,\r\n} from \"./protector.js\";\r\nexport { shield, rateLimit, tokenBucket, detectBot, requestFilter, sensitiveInfo, emailValidation, phoneValidation, promptInjection, SENSITIVE_TYPES } from \"./rules.js\";\r\nexport type {\r\n Rule,\r\n ShieldOptions,\r\n RateLimitOptions,\r\n TokenBucketOptions,\r\n DetectBotOptions,\r\n RequestFilterOptions,\r\n FilterCondition,\r\n SensitiveInfoOptions,\r\n SensitiveType,\r\n EmailValidationOptions,\r\n PhoneValidationOptions,\r\n PromptInjectionOptions,\r\n} from \"./rules.js\";\r\nexport { createIpResolver } from \"./ip.js\";\r\nexport type { IpSource, IpInput, IpAdvice, IpResolver, ResolvedIp } from \"./ip.js\";\r\nexport { CLOUDFLARE_RANGES, PRIVATE_NETWORKS } from \"./ranges.js\";\r\nexport { selectHeaders, selectHeadersDetailed, getHeader, DEFAULT_FORWARDED_HEADERS, NEVER_FORWARD } from \"./headers.js\";\r\nexport type { HeaderBag, ForwardOptions } from \"./headers.js\";\r\nexport { sha256Hex } from \"./sha256.js\";\r\nexport { ZarexClientError, ZarexConfigError } from \"@zarex/client\";\r\nexport type { ClientErrorCode, Verdict, Mode, Algorithm } from \"@zarex/client\";\r\nexport { guardWebRequest, protectWebRequest, denyResponse } from \"./web.js\";\r\nexport { createWebFinalizer } from \"./web.js\";\r\nexport type { WebGuardOptions, WebGuardResult, WebFinalizeOptions } from \"./web.js\";\r\nexport { secureHeaders, prepareSecureHeaders, withSecureHeaders, applyPreparedToResponse, isLocalHost, removesPoweredBy, DEFAULT_SECURE_HEADERS } from \"./secureheaders.js\";\r\nexport type { SecureHeadersOptions, SecureHeaders, PreparedSecureHeaders } from \"./secureheaders.js\";\r\nexport { detectSensitive, maskSensitive, findSensitive, SENSITIVE_ALL } from \"./sensitive.js\";\r\nexport type { SensitiveFindings, SensitiveKind } from \"./sensitive.js\";\r\nexport { guardResponseText, applyGuard, normalizeGuardOptions, notify as notifyResponseGuard, isScannableContentType, DEFAULT_RESPONSE_TYPES } from \"./responseguard.js\";\r\nexport type { ResponseGuardOptions, ResponseGuardEvent, GuardedBody } from \"./responseguard.js\";","/**\r\n * Détection d'informations sensibles dans un texte (cartes, clés d'API, JWT, IBAN…).\r\n *\r\n * C'est le jumeau du package Go internal/sensitive : mêmes types, mêmes règles de validation\r\n * (Luhn, modulo 97…). Un fichier de vecteurs commun (testdata/sensitive_vectors.json) est rejoué\r\n * des deux côtés ; si les deux implémentations divergent, un test échoue.\r\n *\r\n * Sécurité : les expressions ci-dessous ont toutes des répétitions BORNÉES et aucune\r\n * alternative ambiguë imbriquée (pas de ReDoS), et le texte est coupé à MAX_SCAN_CHARS.\r\n */\r\n\r\nexport const SENSITIVE_ALL = [\"EMAIL\", \"PHONE\", \"CREDIT_CARD\", \"PASSWORD\", \"SSN\", \"API_KEY\", \"JWT\", \"PRIVATE_KEY\", \"IBAN\"] as const;\r\nexport type SensitiveKind = (typeof SENSITIVE_ALL)[number];\r\nexport type SensitiveFindings = Partial<Record<SensitiveKind, number>>;\r\n\r\nexport const MAX_SCAN_CHARS = 1_000_000;\r\nconst WS = \"[ \\\\t\\\\n\\\\f\\\\r]\";\r\n\r\nconst reEmail = /[A-Za-z0-9._%+\\-]{1,64}@[A-Za-z0-9\\-]{1,63}(?:\\.[A-Za-z0-9\\-]{1,63})*\\.[A-Za-z]{2,24}/g;\r\nconst rePhone = /(?:\\+|\\b00)[1-9][0-9]{0,2}(?:[ .\\-]?\\(?[0-9]{1,4}\\)?){2,6}/g;\r\nconst reCard = /\\b[0-9](?:[ \\-]?[0-9]){12,18}\\b/g;\r\n// Groupe 1 = la clé et le séparateur (conservés), groupe 2 = la VALEUR (masquée).\r\nconst rePass = new RegExp(\r\n `(\\\\b(?:pass(?:word|wd|phrase)?|pwd|mot[ _\\\\-]?de[ _\\\\-]?passe|mdp|secret|api[_\\\\-]?secret|client[_\\\\-]?secret)[\"']?${WS}*[:=]${WS}*[\"']?)([^ \\\\t\\\\n\\\\f\\\\r\"',;&<>]{4,})`,\r\n \"gi\"\r\n);\r\nconst reSSN = /\\b([0-9]{3})-([0-9]{2})-([0-9]{4})\\b/g;\r\nconst reJWT = /\\beyJ[A-Za-z0-9_\\-]{8,}\\.eyJ[A-Za-z0-9_\\-]{8,}\\.[A-Za-z0-9_\\-]*/g;\r\nconst rePriv = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/g;\r\nconst reIBAN = /\\b[A-Z]{2}[0-9]{2}(?: ?[A-Z0-9]{4}){2,7}(?: ?[A-Z0-9]{1,3})?\\b/g;\r\nconst reKeys: RegExp[] = [\r\n /\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/g,\r\n /\\bgh[pousr]_[A-Za-z0-9]{36,}\\b/g,\r\n /\\bgithub_pat_[A-Za-z0-9_]{22,}\\b/g,\r\n /\\b[sr]k_(?:live|test)_[0-9a-zA-Z]{16,}\\b/g,\r\n /\\bxox[abprs]-[A-Za-z0-9\\-]{10,}\\b/g,\r\n /\\bAIza[0-9A-Za-z_\\-]{35}\\b/g,\r\n /\\bsk-(?:proj-|ant-|svcacct-)?[A-Za-z0-9_\\-]{20,}\\b/g,\r\n /\\bSG\\.[A-Za-z0-9_\\-]{22}\\.[A-Za-z0-9_\\-]{43}\\b/g,\r\n /\\bzarex_(?:live|test)_[0-9a-zA-Z]{16,}\\b/g,\r\n /\\b(?:SK|AC)[0-9a-f]{32}\\b/g,\r\n /\\bwave_sn_(?:prod|AKS)_[A-Za-z0-9_-]{16,}/g, // Wave (clé d'API et secret de signature)\r\n /\\bFLWSECK(?:_TEST)?-[A-Za-z0-9_-]{16,}/g, // Flutterwave (secret ; FLWPUBK volontairement exclu)\r\n];\r\n\r\nexport function luhn(digits: string): boolean {\r\n let sum = 0;\r\n let alt = false;\r\n for (let i = digits.length - 1; i >= 0; i--) {\r\n let d = digits.charCodeAt(i) - 48;\r\n if (alt) {\r\n d *= 2;\r\n if (d > 9) d -= 9;\r\n }\r\n sum += d;\r\n alt = !alt;\r\n }\r\n return sum % 10 === 0;\r\n}\r\n\r\nexport function plausibleCardPrefix(d: string): boolean {\r\n const n = d.length;\r\n if (n < 13 || n > 19) return false;\r\n const c = (i: number) => d.charCodeAt(i) - 48;\r\n const p2 = c(0) * 10 + c(1);\r\n const p4 = p2 * 100 + c(2) * 10 + c(3);\r\n if (c(0) === 4 && (n === 13 || n === 16 || n === 19)) return true;\r\n if (p2 >= 51 && p2 <= 55 && n === 16) return true;\r\n if (p4 >= 2221 && p4 <= 2720 && n === 16) return true;\r\n if ((p2 === 34 || p2 === 37) && n === 15) return true;\r\n if ((p4 === 6011 || p2 === 65 || (p4 >= 6440 && p4 <= 6499)) && (n === 16 || n === 19)) return true;\r\n if (p2 === 62 && n >= 16 && n <= 19) return true;\r\n if ((p2 === 36 || p2 === 38 || p2 === 39 || (p2 === 30 && c(2) >= 0 && c(2) <= 5)) && (n === 14 || n === 16)) return true;\r\n if (p4 >= 3528 && p4 <= 3589 && n >= 16 && n <= 19) return true;\r\n if ((p2 === 50 || (p2 >= 56 && p2 <= 69)) && n >= 12 && n <= 19) return n >= 13;\r\n return false;\r\n}\r\n\r\nexport function mod97(iban: string): boolean {\r\n let s = iban.replace(/ /g, \"\");\r\n if (s.length < 15 || s.length > 34) return false;\r\n s = s.slice(4) + s.slice(0, 4);\r\n let rem = 0;\r\n for (let i = 0; i < s.length; i++) {\r\n const ch = s.charCodeAt(i);\r\n if (ch >= 48 && ch <= 57) rem = (rem * 10 + (ch - 48)) % 97;\r\n else if (ch >= 65 && ch <= 90) rem = (rem * 100 + (ch - 65 + 10)) % 97;\r\n else return false;\r\n }\r\n return rem === 1;\r\n}\r\n\r\nfunction b64urlDecode(s: string): string | undefined {\r\n try {\r\n const t = s.replace(/=+$/, \"\").replace(/-/g, \"+\").replace(/_/g, \"/\");\r\n if (!/^[A-Za-z0-9+/]*$/.test(t)) return undefined;\r\n const padded = t + \"===\".slice((t.length + 3) % 4);\r\n if (typeof atob === \"function\") return atob(padded);\r\n return undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction jwtLooksReal(tok: string): boolean {\r\n const first = tok.split(\".\", 1)[0] ?? \"\";\r\n const s = b64urlDecode(first);\r\n return s !== undefined && s.startsWith(\"{\") && s.includes('\"alg\"');\r\n}\r\n\r\nfunction phoneDigits(s: string): number {\r\n let n = 0;\r\n for (let i = 0; i < s.length; i++) {\r\n const c = s.charCodeAt(i);\r\n if (c >= 48 && c <= 57) n++;\r\n }\r\n return n;\r\n}\r\n\r\n/**\r\n * Caractères invisibles (largeur nulle, trait d'union conditionnel, marqueurs bidi…) : un seul\r\n * caractère de ce type intercalé dans une clé d'API, un JWT ou un IBAN suffisait à échapper\r\n * entièrement à la détection — même défaut, même correctif que côté Go (internal/sensitive,\r\n * trouvé lors de l'audit du 04/11/2026, voir AUDIT_SECURITE_COMPLET.md, C10). Mêmes plages\r\n * Unicode que le jumeau Go.\r\n */\r\nfunction isInvisible(cp: number): boolean {\r\n return (\r\n cp === 0x00ad ||\r\n cp === 0xfeff ||\r\n (cp >= 0x200b && cp <= 0x200f) ||\r\n (cp >= 0x202a && cp <= 0x202e) ||\r\n (cp >= 0x2060 && cp <= 0x2064)\r\n );\r\n}\r\n\r\ninterface Cleaned {\r\n text: string;\r\n // map[i] = position dans le texte D'ORIGINE du caractère cleaned.text[i] ; map[text.length] =\r\n // position de fin. Nécessaire ici (contrairement au Go, qui ne renvoie que des comptes) parce\r\n // que findSensitive renvoie des Span dans le texte d'origine, utilisés pour le masquage : on ne\r\n // peut pas simplement matcher sur une copie nettoyée sans perdre la correspondance des positions.\r\n // undefined = cas courant, rien à retirer : pas de copie ni de tableau (sinon un texte hostile\r\n // de 1 Mo sans aucun caractère invisible paierait le coût d'une copie complète pour rien).\r\n map?: number[];\r\n}\r\n\r\nfunction stripInvisible(s: string): Cleaned {\r\n let has = false;\r\n for (let i = 0; i < s.length; i++) {\r\n if (isInvisible(s.charCodeAt(i))) {\r\n has = true;\r\n break;\r\n }\r\n }\r\n if (!has) return { text: s };\r\n let out = \"\";\r\n const map: number[] = [];\r\n for (let i = 0; i < s.length; i++) {\r\n if (isInvisible(s.charCodeAt(i))) continue;\r\n map.push(i);\r\n out += s[i];\r\n }\r\n map.push(s.length);\r\n return { text: out, map };\r\n}\r\n\r\n/** Position d'une trouvaille dans le texte (sert à masquer). */\r\nexport interface Span {\r\n kind: SensitiveKind;\r\n start: number;\r\n end: number;\r\n}\r\n\r\nconst MAX_MATCHES = 10_000;\r\n\r\nfunction* matches(re: RegExp, text: string): Generator<RegExpExecArray> {\r\n re.lastIndex = 0;\r\n let m: RegExpExecArray | null;\r\n let n = 0;\r\n while ((m = re.exec(text)) !== null) {\r\n yield m;\r\n if (m[0].length === 0) re.lastIndex++; // sécurité : jamais de boucle infinie\r\n if (++n >= MAX_MATCHES) break;\r\n }\r\n}\r\n\r\n/** Toutes les trouvailles VALIDÉES des types demandés (tous si `types` est vide). */\r\nexport function findSensitive(text: string, types?: readonly string[]): Span[] {\r\n if (text.length > MAX_SCAN_CHARS) text = text.slice(0, MAX_SCAN_CHARS);\r\n const want = new Set<string>(types && types.length ? types : SENSITIVE_ALL);\r\n const out: Span[] = [];\r\n const push = (kind: SensitiveKind, m: RegExpExecArray, start = m.index, len = m[0].length) => out.push({ kind, start, end: start + len });\r\n\r\n if (want.has(\"EMAIL\")) for (const m of matches(reEmail, text)) push(\"EMAIL\", m);\r\n if (want.has(\"PHONE\")) {\r\n for (const m of matches(rePhone, text)) {\r\n const d = phoneDigits(m[0]);\r\n if (d >= 8 && d <= 15) push(\"PHONE\", m);\r\n }\r\n }\r\n if (want.has(\"CREDIT_CARD\")) {\r\n for (const m of matches(reCard, text)) {\r\n const d = m[0].replace(/[ -]/g, \"\");\r\n if (plausibleCardPrefix(d) && luhn(d)) push(\"CREDIT_CARD\", m);\r\n }\r\n }\r\n // Détecteurs de SECRETS ci-dessous : matchent sur une copie nettoyée des caractères invisibles\r\n // (voir stripInvisible) — calculée une seule fois, à la demande, pas pour EMAIL/PHONE/CREDIT_CARD.\r\n let cleaned: Cleaned | undefined;\r\n const getCleaned = (): Cleaned => {\r\n if (!cleaned) cleaned = stripInvisible(text);\r\n return cleaned;\r\n };\r\n const pushClean = (kind: SensitiveKind, map: number[] | undefined, m: RegExpExecArray, start = m.index, len = m[0].length) =>\r\n out.push(map ? { kind, start: map[start]!, end: map[start + len]! } : { kind, start, end: start + len });\r\n\r\n if (want.has(\"PASSWORD\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(rePass, ct)) {\r\n const prefix = m[1] ?? \"\";\r\n pushClean(\"PASSWORD\", map, m, m.index + prefix.length, (m[2] ?? \"\").length); // seule la valeur est masquée\r\n }\r\n }\r\n if (want.has(\"SSN\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(reSSN, ct)) {\r\n const [a, g, s] = [m[1]!, m[2]!, m[3]!];\r\n if (a === \"000\" || a === \"666\" || a[0] === \"9\" || g === \"00\" || s === \"0000\") continue;\r\n pushClean(\"SSN\", map, m);\r\n }\r\n }\r\n if (want.has(\"API_KEY\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const re of reKeys) for (const m of matches(re, ct)) pushClean(\"API_KEY\", map, m);\r\n }\r\n if (want.has(\"JWT\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(reJWT, ct)) if (jwtLooksReal(m[0])) pushClean(\"JWT\", map, m);\r\n }\r\n if (want.has(\"PRIVATE_KEY\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(rePriv, ct)) pushClean(\"PRIVATE_KEY\", map, m);\r\n }\r\n if (want.has(\"IBAN\")) {\r\n const { text: ct, map } = getCleaned();\r\n for (const m of matches(reIBAN, ct)) if (mod97(m[0])) pushClean(\"IBAN\", map, m);\r\n }\r\n return out;\r\n}\r\n\r\n/** Compte par type — même résultat que Detect() côté Go. Ne renvoie jamais les valeurs. */\r\nexport function detectSensitive(text: string, types?: readonly string[]): SensitiveFindings {\r\n const f: SensitiveFindings = {};\r\n for (const s of findSensitive(text, types)) f[s.kind] = (f[s.kind] ?? 0) + 1;\r\n return f;\r\n}\r\n\r\n/** Remplace les trouvailles par `[REDACTED:TYPE]` (la valeur d'un mot de passe seulement : `password=[REDACTED:PASSWORD]`). */\r\nexport function maskSensitive(text: string, types?: readonly string[]): { text: string; findings: SensitiveFindings } {\r\n const spans = findSensitive(text, types).sort((a, b) => a.start - b.start || b.end - a.end);\r\n const findings: SensitiveFindings = {};\r\n if (spans.length === 0) return { text, findings };\r\n let out = \"\";\r\n let pos = 0;\r\n for (const s of spans) {\r\n findings[s.kind] = (findings[s.kind] ?? 0) + 1;\r\n if (s.start < pos) {\r\n // chevauchement (ex. un numéro vu à la fois comme carte et téléphone) : on étend la zone masquée\r\n if (s.end > pos) pos = s.end;\r\n continue;\r\n }\r\n out += text.slice(pos, s.start) + `[REDACTED:${s.kind}]`;\r\n pos = s.end;\r\n }\r\n out += text.slice(pos);\r\n return { text: out, findings };\r\n}","import { SENSITIVE_ALL, findSensitive, maskSensitive, type SensitiveFindings, type SensitiveKind } from \"./sensitive.js\";\r\n\r\n/**\r\n * Surveillance des RÉPONSES de l'application : empêche une fuite accidentelle de données\r\n * sensibles (carte bancaire, clé d'API, JWT, mot de passe…) vers le visiteur.\r\n *\r\n * Tout se passe dans le SDK, sur votre serveur : le contenu de vos réponses n'est jamais envoyé à Zarex.\r\n *\r\n * Par défaut EMAIL et PHONE ne sont PAS surveillés : une application renvoie légitimement\r\n * l'e-mail de l'utilisateur connecté. Ajoutez-les via `types` si vous le souhaitez.\r\n */\r\nexport interface ResponseGuardOptions {\r\n /** \"mask\" (défaut) remplace par [REDACTED:TYPE] · \"block\" renvoie une erreur 500 générique · \"report\" ne change rien, signale seulement. */\r\n mode?: \"mask\" | \"block\" | \"report\";\r\n /** Types surveillés. Défaut : CREDIT_CARD PASSWORD SSN API_KEY JWT PRIVATE_KEY IBAN. */\r\n types?: SensitiveKind[];\r\n /** Réponses plus grandes que ceci (octets, défaut 1 Mo) : non analysées (laissées telles quelles) et signalées. */\r\n maxBytes?: number;\r\n /**\r\n * Appelé à chaque fuite détectée (ou réponse non analysée). Ne reçoit JAMAIS les valeurs : seulement types et nombres.\r\n * Une erreur dans ce rappel est ignorée.\r\n */\r\n onFinding?: (event: ResponseGuardEvent) => void;\r\n}\r\n\r\nexport interface ResponseGuardEvent {\r\n // skipped_unscannable : format TEXTE en principe analysable mais hors de ce que ce garde sait\r\n // traiter aujourd'hui (flux SSE, corps compressé) — contrairement à un contenu binaire (image…),\r\n // jamais dans son périmètre, qui ne déclenche aucun événement. Trouvé lors de l'audit du\r\n // 04/11/2026 : ces deux cas étaient ignorés en silence (voir web.ts, guardWebResponse).\r\n action: \"masked\" | \"blocked\" | \"reported\" | \"skipped_too_large\" | \"skipped_unscannable\";\r\n findings: SensitiveFindings;\r\n /** Type de contenu de la réponse. */\r\n contentType: string;\r\n}\r\n\r\nexport const DEFAULT_RESPONSE_TYPES: SensitiveKind[] = [\"CREDIT_CARD\", \"PASSWORD\", \"SSN\", \"API_KEY\", \"JWT\", \"PRIVATE_KEY\", \"IBAN\"];\r\nconst DEFAULT_MAX_BYTES = 1_000_000;\r\nconst SCANNABLE = /^(?:text\\/|application\\/(?:json|xml|javascript|x-www-form-urlencoded|graphql)|application\\/[a-z0-9.+-]*\\+(?:json|xml))/i;\r\nconst JSON_TYPE = /^application\\/(?:json|[a-z0-9.+-]*\\+json)/i;\r\nconst PASS_KEY = /^(?:pass(?:word|wd|phrase)?|pwd|mot[ _-]?de[ _-]?passe|mdp|secret|api[_-]?secret|client[_-]?secret)$/i;\r\nconst MAX_DEPTH = 64;\r\nconst MAX_NODES = 200_000;\r\n\r\n/** true si ce type de contenu peut contenir du texte à surveiller (pas les images, PDF, flux binaires). */\r\nexport function isScannableContentType(ct: string | undefined | null): boolean {\r\n return !!ct && SCANNABLE.test(ct.trim());\r\n}\r\n\r\nexport interface GuardedBody {\r\n action: \"pass\" | \"masked\" | \"blocked\" | \"reported\" | \"skipped_too_large\";\r\n body: string;\r\n findings: SensitiveFindings;\r\n}\r\n\r\nfunction mergeCounts(into: SensitiveFindings, from: SensitiveFindings): void {\r\n for (const [k, v] of Object.entries(from)) into[k as SensitiveKind] = (into[k as SensitiveKind] ?? 0) + (v ?? 0);\r\n}\r\n\r\n// Masque les chaînes (et les nombres) d'un JSON déjà analysé. Le résultat reste du JSON valide.\r\nfunction maskJsonValue(v: unknown, types: readonly string[], found: SensitiveFindings, budget: { nodes: number }, depth: number): unknown {\r\n if (depth > MAX_DEPTH || ++budget.nodes > MAX_NODES) return v;\r\n if (typeof v === \"string\") {\r\n const r = maskSensitive(v, types);\r\n mergeCounts(found, r.findings);\r\n return r.text;\r\n }\r\n if (typeof v === \"number\") {\r\n // Un numéro de carte peut être écrit sans guillemets : 4111111111111111\r\n if (Number.isFinite(v) && Math.abs(v) >= 1e11 && Number.isInteger(v)) {\r\n const spans = findSensitive(String(v), types);\r\n if (spans.length) {\r\n for (const s of spans) found[s.kind] = (found[s.kind] ?? 0) + 1;\r\n return \"[REDACTED]\";\r\n }\r\n }\r\n return v;\r\n }\r\n if (Array.isArray(v)) return v.map((x) => maskJsonValue(x, types, found, budget, depth + 1));\r\n if (v && typeof v === \"object\") {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, x] of Object.entries(v as Record<string, unknown>)) {\r\n // En JSON la clé et la valeur sont séparées : {\"password\":\"abc\"} n'est pas visible en analysant la valeur seule.\r\n // Pas de restriction de caractères ici (contrairement à rePass dans sensitive.ts, qui en a besoin\r\n // comme terminateur de valeur en texte libre) : la structure JSON délimite déjà la valeur entière\r\n // (entre guillemets), donc un mot de passe/passphrase avec un espace (\"correct horse battery staple\")\r\n // doit être masqué comme n'importe quel autre — trouvé lors de l'audit du 04/11/2026, voir\r\n // AUDIT_SECURITE_COMPLET.md : cette exclusion laissait fuiter en clair tout mot de passe à espaces.\r\n if (types.includes(\"PASSWORD\") && PASS_KEY.test(k) && typeof x === \"string\" && x.length >= 4) {\r\n found.PASSWORD = (found.PASSWORD ?? 0) + 1;\r\n out[k] = \"[REDACTED:PASSWORD]\";\r\n } else out[k] = maskJsonValue(x, types, found, budget, depth + 1);\r\n }\r\n return out;\r\n }\r\n return v;\r\n}\r\n\r\n/**\r\n * Analyse un corps de réponse TEXTE. Ne lève jamais : en cas de problème, le corps d'origine est rendu tel quel.\r\n * (Le corps d'origine n'est modifié que s'il contient réellement des données sensibles.)\r\n */\r\nexport function guardResponseText(text: string, contentType: string, options: ResponseGuardOptions = {}): GuardedBody {\r\n try {\r\n const mode = options.mode ?? \"mask\";\r\n const types = options.types?.length ? options.types : DEFAULT_RESPONSE_TYPES;\r\n const max = options.maxBytes ?? DEFAULT_MAX_BYTES;\r\n if (text.length > max) return { action: \"skipped_too_large\", body: text, findings: {} };\r\n\r\n const spans = findSensitive(text, types);\r\n if (spans.length === 0) return { action: \"pass\", body: text, findings: {} };\r\n const findings: SensitiveFindings = {};\r\n for (const s of spans) findings[s.kind] = (findings[s.kind] ?? 0) + 1;\r\n\r\n if (mode === \"report\") return { action: \"reported\", body: text, findings };\r\n if (mode === \"block\") {\r\n return {\r\n action: \"blocked\",\r\n body: JSON.stringify({ error: { code: \"response_blocked\", message: \"The response was withheld because it contained sensitive data\" } }),\r\n findings,\r\n };\r\n }\r\n\r\n if (JSON_TYPE.test(contentType)) {\r\n try {\r\n const parsed: unknown = JSON.parse(text);\r\n const found: SensitiveFindings = {};\r\n const masked = JSON.stringify(maskJsonValue(parsed, types, found, { nodes: 0 }, 0));\r\n // Ceinture et bretelles : si quelque chose reste visible (cas non prévu), on masque le texte brut\r\n // plutôt que de laisser fuiter (le JSON peut alors devenir invalide, mais rien ne fuit).\r\n if (findSensitive(masked, types).filter((sp) => !masked.startsWith(\"[REDACTED\", sp.start)).length === 0) {\r\n return { action: \"masked\", body: masked, findings: Object.keys(found).length ? found : findings };\r\n }\r\n const r2 = maskSensitive(masked, types);\r\n return { action: \"masked\", body: r2.text, findings };\r\n } catch {\r\n /* JSON illisible : on masque comme du texte */\r\n }\r\n }\r\n const r = maskSensitive(text, types);\r\n return { action: \"masked\", body: r.text, findings: r.findings };\r\n } catch {\r\n return { action: \"pass\", body: text, findings: {} };\r\n }\r\n}\r\n\r\nexport function notify(options: ResponseGuardOptions, event: ResponseGuardEvent): void {\r\n try {\r\n options.onFinding?.(event);\r\n } catch {\r\n /* le rappel du développeur ne doit jamais casser une réponse */\r\n }\r\n}\r\n\r\nexport { SENSITIVE_ALL };\r\n\r\nexport function normalizeGuardOptions(o: ResponseGuardOptions | boolean | undefined): ResponseGuardOptions | undefined {\r\n if (!o) return undefined;\r\n const g = o === true ? {} : o;\r\n if (g.mode !== undefined && ![\"mask\", \"block\", \"report\"].includes(g.mode)) throw new Error('Zarex: responseGuard.mode must be \"mask\", \"block\" or \"report\".');\r\n if (g.maxBytes !== undefined && (!Number.isInteger(g.maxBytes) || g.maxBytes < 1 || g.maxBytes > 16_000_000)) throw new Error(\"Zarex: responseGuard.maxBytes must be an integer between 1 and 16000000.\");\r\n for (const t of g.types ?? []) if (!(SENSITIVE_ALL as readonly string[]).includes(t)) throw new Error(`Zarex: responseGuard.types: unknown type \"${String(t).slice(0, 20)}\".`);\r\n return g;\r\n}\r\n\r\n/** Analyse + signalement (onFinding). Sert aux adaptateurs. */\r\nexport function applyGuard(text: string, contentType: string, options: ResponseGuardOptions): GuardedBody {\r\n const r = guardResponseText(text, contentType, options);\r\n if (r.action !== \"pass\") {\r\n notify(options, { action: r.action === \"skipped_too_large\" ? \"skipped_too_large\" : r.action, findings: r.findings, contentType });\r\n }\r\n return r;\r\n}","/**\r\n * En-têtes de sécurité HTTP, calculés par le SDK lui-même (aucun appel réseau).\r\n *\r\n * Défauts prudents : ce qu'on ajoute ne doit pas casser un site qui marche.\r\n * - Content-Security-Policy : DÉSACTIVÉ par défaut (une CSP mal réglée casse un site entier).\r\n * Utilisez d'abord `contentSecurityPolicyReportOnly`, puis passez en mode strict.\r\n * - Strict-Transport-Security : 6 mois, sans includeSubDomains ni preload (ces deux options\r\n * sont difficiles à annuler : à activer en connaissance de cause).\r\n */\r\n\r\nexport interface SecureHeadersOptions {\r\n /** Strict-Transport-Security. `true` = 6 mois. Défaut : true. Ignoré si la requête n'est pas en HTTPS. */\r\n hsts?: boolean | { maxAgeSeconds?: number; includeSubDomains?: boolean; preload?: boolean };\r\n /** Content-Security-Policy complète (chaîne). Défaut : absente. */\r\n contentSecurityPolicy?: string | false;\r\n /** Même chose en mode « rapport seulement » : le navigateur signale sans bloquer. */\r\n contentSecurityPolicyReportOnly?: string | false;\r\n /** X-Frame-Options. Défaut : \"DENY\". */\r\n frameOptions?: \"DENY\" | \"SAMEORIGIN\" | false;\r\n /** X-Content-Type-Options: nosniff. Défaut : true. */\r\n contentTypeOptions?: boolean;\r\n /** Referrer-Policy. Défaut : \"strict-origin-when-cross-origin\". */\r\n referrerPolicy?: string | false;\r\n /** Permissions-Policy. Défaut : caméra, micro et géolocalisation désactivés. */\r\n permissionsPolicy?: string | false;\r\n /** Cross-Origin-Opener-Policy. Défaut : \"same-origin\". */\r\n crossOriginOpenerPolicy?: \"same-origin\" | \"same-origin-allow-popups\" | \"unsafe-none\" | false;\r\n /** Cross-Origin-Resource-Policy. Défaut : absente (peut casser les images/scripts partagés entre domaines). */\r\n crossOriginResourcePolicy?: \"same-origin\" | \"same-site\" | \"cross-origin\" | false;\r\n /** Supprimer X-Powered-By (révèle le framework). Défaut : true. */\r\n removePoweredBy?: boolean;\r\n}\r\n\r\nexport type SecureHeaders = Record<string, string>;\r\n\r\nexport const DEFAULT_SECURE_HEADERS: Readonly<Required<Pick<SecureHeadersOptions, \"frameOptions\" | \"contentTypeOptions\" | \"referrerPolicy\" | \"permissionsPolicy\" | \"crossOriginOpenerPolicy\">>> = {\r\n frameOptions: \"DENY\",\r\n contentTypeOptions: true,\r\n referrerPolicy: \"strict-origin-when-cross-origin\",\r\n permissionsPolicy: \"camera=(), microphone=(), geolocation=()\",\r\n crossOriginOpenerPolicy: \"same-origin\",\r\n};\r\n\r\nconst DEFAULT_HSTS_SECONDS = 15_552_000; // 180 jours\r\nconst MAX_HSTS_SECONDS = 63_072_000; // 2 ans\r\nconst MAX_VALUE_LEN = 4096;\r\n\r\n// Aucun saut de ligne ni caractère de contrôle : sinon une valeur venant d'une variable\r\n// d'environnement ou d'un fichier pourrait injecter un en-tête supplémentaire (CRLF injection).\r\nfunction safeValue(name: string, v: string): string {\r\n if (typeof v !== \"string\" || v.length === 0 || v.length > MAX_VALUE_LEN || /[\\u0000-\\u001f\\u007f]/.test(v)) {\r\n throw new Error(`Zarex: secureHeaders: the value of \\`${name}\\` is empty, too long, or contains control characters.`);\r\n }\r\n return v;\r\n}\r\n\r\n/**\r\n * Calcule les en-têtes à ajouter. Lève une erreur de configuration claire au démarrage\r\n * si une option est invalide (à appeler une fois, pas à chaque requête).\r\n */\r\nexport function secureHeaders(options: SecureHeadersOptions = {}, ctx: { https?: boolean } = {}): SecureHeaders {\r\n const o = options ?? {};\r\n const out: SecureHeaders = {};\r\n\r\n const hsts = o.hsts ?? true;\r\n if (hsts && ctx.https !== false) {\r\n const cfg = typeof hsts === \"object\" ? hsts : {};\r\n const age = cfg.maxAgeSeconds ?? DEFAULT_HSTS_SECONDS;\r\n if (!Number.isInteger(age) || age < 0 || age > MAX_HSTS_SECONDS) {\r\n throw new Error(`Zarex: secureHeaders: hsts.maxAgeSeconds must be an integer between 0 and ${MAX_HSTS_SECONDS}.`);\r\n }\r\n if (cfg.preload && (!cfg.includeSubDomains || age < 31_536_000)) {\r\n throw new Error(\"Zarex: secureHeaders: hsts.preload requires includeSubDomains and at least one year (31536000 s), otherwise browsers' preload lists reject the site.\");\r\n }\r\n out[\"Strict-Transport-Security\"] = `max-age=${age}${cfg.includeSubDomains ? \"; includeSubDomains\" : \"\"}${cfg.preload ? \"; preload\" : \"\"}`;\r\n }\r\n\r\n if (o.contentSecurityPolicy) out[\"Content-Security-Policy\"] = safeValue(\"contentSecurityPolicy\", o.contentSecurityPolicy);\r\n if (o.contentSecurityPolicyReportOnly) out[\"Content-Security-Policy-Report-Only\"] = safeValue(\"contentSecurityPolicyReportOnly\", o.contentSecurityPolicyReportOnly);\r\n\r\n const frame = o.frameOptions === undefined ? DEFAULT_SECURE_HEADERS.frameOptions : o.frameOptions;\r\n if (frame) {\r\n if (frame !== \"DENY\" && frame !== \"SAMEORIGIN\") throw new Error('Zarex: secureHeaders: frameOptions must be \"DENY\", \"SAMEORIGIN\" or false.');\r\n out[\"X-Frame-Options\"] = frame;\r\n }\r\n if (o.contentTypeOptions ?? DEFAULT_SECURE_HEADERS.contentTypeOptions) out[\"X-Content-Type-Options\"] = \"nosniff\";\r\n\r\n const ref = o.referrerPolicy === undefined ? DEFAULT_SECURE_HEADERS.referrerPolicy : o.referrerPolicy;\r\n if (ref) out[\"Referrer-Policy\"] = safeValue(\"referrerPolicy\", ref);\r\n const perm = o.permissionsPolicy === undefined ? DEFAULT_SECURE_HEADERS.permissionsPolicy : o.permissionsPolicy;\r\n if (perm) out[\"Permissions-Policy\"] = safeValue(\"permissionsPolicy\", perm);\r\n\r\n const coop = o.crossOriginOpenerPolicy === undefined ? DEFAULT_SECURE_HEADERS.crossOriginOpenerPolicy : o.crossOriginOpenerPolicy;\r\n if (coop) {\r\n if (![\"same-origin\", \"same-origin-allow-popups\", \"unsafe-none\"].includes(coop)) throw new Error(\"Zarex: secureHeaders: invalid crossOriginOpenerPolicy.\");\r\n out[\"Cross-Origin-Opener-Policy\"] = coop;\r\n }\r\n if (o.crossOriginResourcePolicy) {\r\n if (![\"same-origin\", \"same-site\", \"cross-origin\"].includes(o.crossOriginResourcePolicy)) throw new Error(\"Zarex: secureHeaders: invalid crossOriginResourcePolicy.\");\r\n out[\"Cross-Origin-Resource-Policy\"] = o.crossOriginResourcePolicy;\r\n }\r\n return out;\r\n}\r\n\r\n/** true si l'option demande de retirer X-Powered-By (défaut). */\r\nexport function removesPoweredBy(options: SecureHeadersOptions | undefined): boolean {\r\n return options?.removePoweredBy !== false;\r\n}\r\n\r\n/**\r\n * Prépare les en-têtes une fois (au démarrage) : renvoie deux jeux, pour requête HTTPS et non-HTTPS\r\n * (HSTS n'a de sens que sur HTTPS ; navigateurs l'ignorent sinon).\r\n */\r\nexport function prepareSecureHeaders(options: SecureHeadersOptions | boolean | undefined): { https: SecureHeaders; http: SecureHeaders; removePoweredBy: boolean } | undefined {\r\n if (!options) return undefined;\r\n const o = options === true ? {} : options;\r\n return { https: secureHeaders(o, { https: true }), http: secureHeaders(o, { https: false }), removePoweredBy: removesPoweredBy(o) };\r\n}\r\n\r\nexport type PreparedSecureHeaders = NonNullable<ReturnType<typeof prepareSecureHeaders>>;\r\n\r\n/**\r\n * HSTS sur localhost : un navigateur qui voit « Strict-Transport-Security » sur https://localhost\r\n * mémorise ce choix pour des mois et refuse ensuite http://localhost. On ne l'envoie donc pas en local.\r\n */\r\nexport function isLocalHost(host: string | undefined | null): boolean {\r\n if (!host) return false;\r\n const h = host.toLowerCase().replace(/:\\d+$/, \"\").replace(/^\\[|\\]$/g, \"\");\r\n return h === \"localhost\" || h.endsWith(\".localhost\") || h === \"127.0.0.1\" || h === \"::1\";\r\n}\r\n\r\n/** Ajoute des en-têtes préparés à une Response standard (jamais d'écrasement d'un en-tête posé par l'application). */\r\nexport function applyPreparedToResponse(response: Response, prepared: PreparedSecureHeaders, host?: string | null): Response {\r\n try {\r\n const set = isLocalHost(host) ? prepared.http : prepared.https;\r\n const headers = new Headers(response.headers);\r\n for (const [k, v] of Object.entries(set)) if (!headers.has(k)) headers.set(k, v);\r\n if (prepared.removePoweredBy) headers.delete(\"x-powered-by\");\r\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers });\r\n } catch {\r\n return response; // jamais casser la réponse de l'application (ex. statut 101 d'un WebSocket)\r\n }\r\n}\r\n\r\n/** Version simple : prépare puis applique. Pour un usage répété, préférez prepareSecureHeaders() une fois au démarrage. */\r\nexport function withSecureHeaders(response: Response, options: SecureHeadersOptions | boolean | undefined, ctx: { host?: string | null } = {}): Response {\r\n const prepared = prepareSecureHeaders(options);\r\n return prepared ? applyPreparedToResponse(response, prepared, ctx.host) : response;\r\n}","import type { Decision, ProtectCall, Zarex } from \"./protector.js\";\r\nimport { applyGuard, isScannableContentType, normalizeGuardOptions, notify, type ResponseGuardOptions } from \"./responseguard.js\";\r\nimport { applyPreparedToResponse, prepareSecureHeaders, type SecureHeadersOptions } from \"./secureheaders.js\";\r\n\r\n/**\r\n * Outils communs aux adaptateurs « fetch standard » (Next.js, Bun, SvelteKit, Workers…).\r\n * N'utilise que `Request` / `Response` / `URL` : aucune API propre à Node.\r\n */\r\nexport interface WebGuardOptions {\r\n /** Adresse de la connexion TCP, si le runtime la donne (Bun : server.requestIP). Absente en edge. */\r\n socketIp?: string | undefined;\r\n /** Adresse calculée par le framework (SvelteKit : getClientAddress()). */\r\n frameworkIp?: string | undefined;\r\n /** Transmettre le corps au Shield (défaut false). Ignoré si le corps est gros, inconnu ou non textuel. */\r\n inspectBody?: boolean;\r\n /** Identifiants personnalisés / jetons pour CETTE requête. */\r\n call?: ProtectCall | undefined;\r\n /** Réponse de refus personnalisée. */\r\n onDeny?: (request: Request, decision: Decision) => Response | Promise<Response>;\r\n /** Appelé quand le moteur n'a pas pu décider (la requête passe). */\r\n onDegraded?: (request: Request, decision: Decision) => void;\r\n}\r\n\r\nexport interface WebGuardResult {\r\n decision: Decision | undefined;\r\n /** Défini seulement si la requête doit être refusée. */\r\n deny?: Response;\r\n}\r\n\r\nconst BODY_LIMIT_BYTES = 48 * 1024;\r\nconst TEXTUAL = /^(text\\/|application\\/(json|x-www-form-urlencoded|xml|graphql)|application\\/[a-z0-9.+-]*\\+(json|xml))/i;\r\n\r\n/** Réponse de refus par défaut : générique, sans détail interne. */\r\nexport function denyResponse(d: Decision): Response {\r\n const limited = d.deniedBy?.ruleType === \"RATE_LIMIT\" || d.deniedBy?.ruleType === \"TOKEN_BUCKET\";\r\n const headers: Record<string, string> = { \"content-type\": \"application/json; charset=utf-8\", \"cache-control\": \"no-store\" };\r\n if (limited) {\r\n if (d.retryAfterSeconds !== undefined) headers[\"retry-after\"] = String(d.retryAfterSeconds);\r\n return new Response(JSON.stringify({ error: { code: \"rate_limited\", message: \"Too many requests\", request_id: d.id } }), { status: 429, headers });\r\n }\r\n return new Response(JSON.stringify({ error: { code: \"forbidden\", message: \"Request blocked\", request_id: d.id } }), { status: 403, headers });\r\n}\r\n\r\n// readStreamLimited : lit un flux par morceaux et s'arrête dès que la taille RÉELLE lue dépasse max —\r\n// contrairement à une vérification du seul Content-Length déclaré (une promesse du visiteur, pas une\r\n// garantie), ceci borne vraiment la mémoire utilisée. Partagée par readBodySafely (requêtes) et\r\n// readLimited (réponses) ci-dessous.\r\nasync function readStreamLimited(body: ReadableStream<Uint8Array> | null, maxBytes: number): Promise<string | null> {\r\n if (!body) return \"\";\r\n const reader = body.getReader();\r\n const chunks: Uint8Array[] = [];\r\n let total = 0;\r\n try {\r\n for (;;) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n total += value.byteLength;\r\n if (total > maxBytes) {\r\n // Ne PAS attendre : sur une branche de tee(), cancel() ne se termine que lorsque l'autre branche\r\n // (celle du client) est elle aussi terminée — l'attendre bloquerait la réponse/la requête.\r\n void reader.cancel().catch(() => undefined);\r\n return null;\r\n }\r\n chunks.push(value);\r\n }\r\n } finally {\r\n try {\r\n reader.releaseLock();\r\n } catch {\r\n /* déjà libéré */\r\n }\r\n }\r\n const all = new Uint8Array(total);\r\n let off = 0;\r\n for (const c of chunks) {\r\n all.set(c, off);\r\n off += c.byteLength;\r\n }\r\n return new TextDecoder(\"utf-8\").decode(all);\r\n}\r\n\r\nasync function readBodySafely(request: Request): Promise<string | undefined> {\r\n try {\r\n const len = request.headers.get(\"content-length\");\r\n // Longueur DÉCLARÉE inconnue (chunked) ou démesurée : on ne lit pas. Mais cette longueur n'est qu'une\r\n // PROMESSE du visiteur, pas une garantie — un en-tête mensonger (Content-Length: 10, corps réel de\r\n // plusieurs Mo) laissait jusqu'ici `request.clone().text()` lire le corps ENTIER en mémoire avant\r\n // qu'aucune limite ne s'applique, malgré ce que ce commentaire prétendait déjà. `readStreamLimited`\r\n // (partagée avec `readLimited` ci-dessous) borne la lecture RÉELLE, pas seulement l'en-tête déclaré —\r\n // trouvé lors de l'audit du 04-05/11/2026, voir AUDIT_SECURITE_COMPLET.md.\r\n if (len === null || !/^\\d{1,9}$/.test(len)) return undefined;\r\n if (Number(len) === 0 || Number(len) > BODY_LIMIT_BYTES) return undefined;\r\n if (!TEXTUAL.test(request.headers.get(\"content-type\") ?? \"\")) return undefined;\r\n if (request.bodyUsed) return undefined;\r\n // clone() : le framework peut encore lire le corps d'origine.\r\n const text = await readStreamLimited(request.clone().body, BODY_LIMIT_BYTES);\r\n return text === null ? undefined : text;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/** Demande une décision pour une `Request` standard. Ne lève jamais (fail-open). */\r\nexport async function protectWebRequest(zarex: Zarex, request: Request, opts: WebGuardOptions = {}): Promise<Decision> {\r\n let path = \"/\";\r\n let host: string | undefined;\r\n try {\r\n const u = new URL(request.url);\r\n path = u.pathname + u.search;\r\n host = u.host;\r\n } catch {\r\n /* URL illisible : chemin par défaut */\r\n }\r\n const body = opts.inspectBody ? await readBodySafely(request) : undefined;\r\n return zarex.protect(\r\n {\r\n method: request.method,\r\n path,\r\n host: request.headers.get(\"host\") ?? host,\r\n headers: request.headers,\r\n socketIp: opts.socketIp,\r\n frameworkIp: opts.frameworkIp,\r\n body,\r\n },\r\n opts.call\r\n );\r\n}\r\n\r\n/**\r\n * Décision + réponse de refus. Quoi qu'il arrive (erreur interne, rappel défaillant),\r\n * la requête du client passe : `deny` n'est défini que sur un vrai refus.\r\n */\r\nexport async function guardWebRequest(zarex: Zarex, request: Request, opts: WebGuardOptions = {}): Promise<WebGuardResult> {\r\n let decision: Decision;\r\n try {\r\n decision = await protectWebRequest(zarex, request, opts);\r\n } catch {\r\n return { decision: undefined };\r\n }\r\n if (decision.denied) {\r\n try {\r\n const res = opts.onDeny ? await opts.onDeny(request, decision) : denyResponse(decision);\r\n return { decision, deny: res };\r\n } catch {\r\n return { decision, deny: denyResponse(decision) };\r\n }\r\n }\r\n if (decision.degraded && opts.onDegraded) {\r\n try {\r\n opts.onDegraded(request, decision);\r\n } catch {\r\n /* le rappel du développeur ne doit pas casser la requête */\r\n }\r\n }\r\n return { decision };\r\n}\r\n\r\n// ───────── Réponses : en-têtes de sécurité et surveillance des fuites ─────────\r\n\r\nexport interface WebFinalizeOptions {\r\n /** En-têtes de sécurité ajoutés à chaque réponse (true = défauts prudents). */\r\n secureHeaders?: SecureHeadersOptions | boolean | undefined;\r\n /** Surveillance des fuites de données sensibles dans les réponses texte/JSON (true = défauts). */\r\n responseGuard?: ResponseGuardOptions | boolean | undefined;\r\n}\r\n\r\nconst DEFAULT_MAX_BYTES = 1_000_000;\r\n\r\nasync function readLimited(response: Response, maxBytes: number): Promise<string | null> {\r\n return readStreamLimited(response.clone().body, maxBytes);\r\n}\r\n\r\nasync function guardWebResponse(response: Response, g: ResponseGuardOptions): Promise<Response> {\r\n const ct = response.headers.get(\"content-type\") ?? \"\";\r\n if (response.body === null || [101, 204, 205, 304].includes(response.status)) return response; // rien à analyser, rien à signaler\r\n const enc = (response.headers.get(\"content-encoding\") ?? \"identity\").toLowerCase();\r\n // SSE et corps compressé : des formats TEXTE en principe analysables, simplement hors de ce que ce\r\n // garde sait traiter aujourd'hui (voir ResponseGuardEvent.action) — à signaler, contrairement à un\r\n // contenu binaire (image…) ci-dessous, qui n'a jamais été dans son périmètre. Trouvé lors de\r\n // l'audit du 04/11/2026 : ces deux cas étaient ignorés en silence. Voir AUDIT_SECURITE_COMPLET.md.\r\n if (/^text\\/event-stream/i.test(ct) || enc !== \"identity\") {\r\n notify(g, { action: \"skipped_unscannable\", findings: {}, contentType: ct });\r\n return response;\r\n }\r\n if (!isScannableContentType(ct)) return response;\r\n const max = g.maxBytes ?? DEFAULT_MAX_BYTES;\r\n const len = response.headers.get(\"content-length\");\r\n if (len !== null && /^\\d{1,12}$/.test(len) && Number(len) > max) {\r\n notify(g, { action: \"skipped_too_large\", findings: {}, contentType: ct });\r\n return response;\r\n }\r\n const text = await readLimited(response, max);\r\n if (text === null) {\r\n notify(g, { action: \"skipped_too_large\", findings: {}, contentType: ct });\r\n return response;\r\n }\r\n const r = applyGuard(text, ct, g);\r\n if (r.action === \"masked\" || r.action === \"blocked\") {\r\n const headers = new Headers(response.headers);\r\n headers.delete(\"content-length\");\r\n headers.delete(\"etag\");\r\n if (r.action === \"blocked\") headers.set(\"content-type\", \"application/json; charset=utf-8\");\r\n return new Response(r.body, { status: r.action === \"blocked\" ? 500 : response.status, statusText: r.action === \"blocked\" ? \"Internal Server Error\" : response.statusText, headers });\r\n }\r\n return response;\r\n}\r\n\r\n/**\r\n * Prépare (une fois, au démarrage : les erreurs de configuration sont levées ici) la fonction qui\r\n * traite chaque réponse. Renvoie `undefined` si aucune option n'est demandée.\r\n * La fonction renvoyée ne lève jamais : en cas de souci, la réponse d'origine est rendue.\r\n */\r\nexport function createWebFinalizer(o: WebFinalizeOptions): ((response: Response, request?: Request) => Promise<Response>) | undefined {\r\n const prepared = prepareSecureHeaders(o.secureHeaders);\r\n const guard = normalizeGuardOptions(o.responseGuard);\r\n if (!prepared && !guard) return undefined;\r\n return async (response, request) => {\r\n let out = response;\r\n try {\r\n if (guard) out = await guardWebResponse(out, guard);\r\n } catch {\r\n out = response;\r\n }\r\n if (prepared) {\r\n let host: string | null = null;\r\n try {\r\n host = request ? (request.headers.get(\"host\") ?? new URL(request.url).host) : null;\r\n } catch {\r\n host = null;\r\n }\r\n out = applyPreparedToResponse(out, prepared, host);\r\n }\r\n return out;\r\n };\r\n}"],"mappings":"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","names":["ZarexConfigError","names","b","readEnv","ZarexConfigError","text","ZarexConfigError","ZarexConfigError","ZarexConfigError","ZarexClientError","ZarexConfigError","mode","DEFAULT_MAX_BYTES"]}