@patchstack/connect 0.3.3 → 0.3.5
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/AGENT-INSTALL.md +9 -5
- package/README.md +16 -3
- package/dist/cli.js +427 -105
- package/dist/cli.js.map +1 -1
- package/dist/protect/templates/guard.ts +52 -95
- package/dist/protect/templates/rules.json +4 -7
- package/dist/protect.cjs +1240 -0
- package/dist/protect.cjs.map +1 -0
- package/dist/protect.d.ts +51 -0
- package/dist/protect.js +1210 -0
- package/dist/protect.js.map +1 -0
- package/package.json +6 -1
- package/dist/protect/templates/engine.d.ts +0 -57
- package/dist/protect/templates/engine.js +0 -137
- package/dist/protect/templates/manifest.d.ts +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/protect/engine/client.js","../src/protect/engine/request.js","../src/protect/engine/normalizer.js","../src/protect/engine/engine.js","../src/protect/engine/fetch.js","../src/protect/engine/node.js","../src/protect/runtime.js","../src/protect/supabase-guard.js"],"sourcesContent":["const DEFAULT_BASE_URL = 'https://api.patchstack.com';\nconst DEFAULT_CACHE_TTL = 300_000;\n\nexport class PatchstackRuleClient {\n #token;\n #baseUrl;\n #cacheTtl;\n #cache = null;\n #cacheTime = null;\n\n constructor({ token, baseUrl, cacheTtl } = {}) {\n this.#token = token ?? process.env.PATCHSTACK_WAF_TOKEN;\n this.#baseUrl = baseUrl ?? process.env.PATCHSTACK_WAF_API_URL ?? DEFAULT_BASE_URL;\n this.#cacheTtl = cacheTtl ?? DEFAULT_CACHE_TTL;\n\n if (!this.#token) {\n throw new Error('Patchstack WAF token is required. Pass { token } or set PATCHSTACK_WAF_TOKEN env var.');\n }\n }\n\n async getRules() {\n const now = Date.now();\n\n if (this.#cache && this.#cacheTime && (now - this.#cacheTime) < this.#cacheTtl) {\n return this.#cache;\n }\n\n const url = `${this.#baseUrl}/api/get-rules/3`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.#token}`,\n 'LicenseID': '1' // Hard-coded, is never actually used but needed by the API\n },\n body: JSON.stringify({}),\n signal: AbortSignal.timeout(30_000)\n });\n\n if (!response.ok) {\n return {\n success: false,\n error: `API returned ${response.status}: ${response.statusText}`,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n\n const data = await response.json();\n\n const result = {\n success: true,\n firewall: Array.isArray(data.firewall) ? data.firewall : [],\n whitelists: Array.isArray(data.whitelists) ? data.whitelists : [],\n whitelist_keys: data.whitelist_keys ?? {}\n };\n\n this.#cache = result;\n this.#cacheTime = now;\n\n return result;\n } catch (err) {\n return {\n success: false,\n error: err.name === 'TimeoutError'\n ? 'Request timed out'\n : err.message,\n firewall: [],\n whitelists: [],\n whitelist_keys: {}\n };\n }\n }\n\n clearCache() {\n this.#cache = null;\n this.#cacheTime = null;\n }\n}\n","// WinterCG-safe base64 decode: use Buffer on Node, fall back to atob/TextDecoder on\n// edge runtimes (Cloudflare Workers, Deno, Bun) where Buffer may be absent. Keeps the\n// engine hot path free of Node-only APIs (per the ADR engine-language decision).\nfunction base64DecodeUtf8(value) {\n const str = String(value);\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n const binary = atob(str);\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n}\n\nexport class RequestResolver {\n #req;\n #cookies;\n\n constructor(req) {\n this.#req = req;\n this.#cookies = null;\n }\n\n resolve(parameter) {\n if (!parameter || parameter === 'false') {\n return [null];\n }\n\n if (parameter === 'rules') {\n return [null];\n }\n\n if (parameter === 'raw') {\n return this.#resolveRaw();\n }\n\n if (parameter === 'all') {\n return this.#resolveAll();\n }\n\n const dotIndex = parameter.indexOf('.');\n if (dotIndex === -1) {\n return [];\n }\n\n const source = parameter.substring(0, dotIndex);\n const key = parameter.substring(dotIndex + 1);\n\n switch (source) {\n case 'get':\n return this.#resolveGet(key);\n case 'post':\n return this.#resolvePost(key);\n case 'request':\n return this.#resolveRequest(key);\n case 'cookie':\n return this.#resolveCookie(key);\n case 'server':\n return this.#resolveServer(key);\n case 'files':\n return this.#resolveFiles(key);\n default:\n return [];\n }\n }\n\n applyMutations(mutations, value) {\n if (!mutations || !Array.isArray(mutations)) {\n return value;\n }\n\n let result = value;\n\n for (const mutation of mutations) {\n result = this.#applyMutation(mutation, result);\n }\n\n return result;\n }\n\n #applyMutation(mutation, value) {\n if (value === null || value === undefined) {\n return value;\n }\n\n switch (mutation) {\n case 'base64_decode':\n try {\n return base64DecodeUtf8(value);\n } catch {\n return value;\n }\n\n case 'json_decode':\n try {\n return JSON.parse(String(value));\n } catch {\n return value;\n }\n\n case 'json_encode':\n try {\n return JSON.stringify(value);\n } catch {\n return value;\n }\n\n case 'urldecode':\n try {\n return decodeURIComponent(String(value));\n } catch {\n return value;\n }\n\n case 'intval':\n return parseInt(String(value), 10) || 0;\n\n case 'getArrayValues':\n if (typeof value === 'object' && value !== null) {\n return Object.values(value);\n }\n return value;\n\n default:\n return value;\n }\n }\n\n #resolveGet(key) {\n const query = this.#req.query ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(query, key);\n }\n\n const value = this.#getNestedValue(query, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolvePost(key) {\n const body = this.#req.body ?? {};\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(body, key);\n }\n\n const value = this.#getNestedValue(body, key);\n return value !== undefined ? [value] : [];\n }\n\n #resolveRequest(key) {\n const query = this.#req.query ?? {};\n const body = this.#req.body ?? {};\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return [\n ...this.#resolveWildcard(query, key),\n ...this.#resolveWildcard(body, key),\n ...this.#resolveWildcard(cookies, key)\n ];\n }\n\n const value = this.#getNestedValue(query, key)\n ?? this.#getNestedValue(body, key)\n ?? cookies[key];\n\n return value !== undefined ? [value] : [];\n }\n\n #resolveCookie(key) {\n const cookies = this.#parseCookies();\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(cookies, key);\n }\n\n const value = cookies[key];\n return value !== undefined ? [value] : [];\n }\n\n #resolveServer(key) {\n const req = this.#req;\n\n switch (key) {\n case 'REQUEST_URI':\n return [req.originalUrl ?? req.url ?? '/'];\n case 'REQUEST_METHOD':\n return [req.method ?? 'GET'];\n case 'HTTP_USER_AGENT':\n return req.headers?.['user-agent'] ? [req.headers['user-agent']] : [];\n case 'HTTP_REFERER':\n return req.headers?.referer ? [req.headers.referer] : [];\n case 'HTTP_HOST':\n return req.headers?.host ? [req.headers.host] : [];\n case 'REMOTE_ADDR':\n case 'ip':\n return [req.ip ?? req.socket?.remoteAddress ?? ''];\n case 'CONTENT_TYPE':\n return req.headers?.['content-type'] ? [req.headers['content-type']] : [];\n case 'CONTENT_LENGTH':\n return req.headers?.['content-length'] ? [req.headers['content-length']] : [];\n default: {\n if (key.startsWith('HTTP_')) {\n const headerName = key.substring(5).toLowerCase().replace(/_/g, '-');\n return req.headers?.[headerName] ? [req.headers[headerName]] : [];\n }\n return [];\n }\n }\n }\n\n #resolveFiles(key) {\n const files = this.#req.files;\n if (!files) {\n return [];\n }\n\n if (key.endsWith('*')) {\n return this.#resolveWildcard(files, key);\n }\n\n const value = files[key];\n return value !== undefined ? [value] : [];\n }\n\n #resolveRaw() {\n // Use pre-captured raw body if available (set by normalizeRequest).\n // For string bodies, the original text is preserved verbatim.\n // For pre-parsed objects, serializeForRawDetection uses Object.getOwnPropertyNames()\n // to include __proto__ own-property keys that JSON.stringify() would silently drop.\n if (typeof this.#req._rawBody === 'string') {\n return this.#req._rawBody ? [this.#req._rawBody] : [];\n }\n\n const body = this.#req.body;\n\n if (body === undefined || body === null) {\n return [];\n }\n\n if (typeof body === 'string') {\n return [body];\n }\n\n try {\n return [JSON.stringify(body)];\n } catch {\n return [String(body)];\n }\n }\n\n #resolveAll() {\n const parts = [];\n\n const uri = this.#req.originalUrl ?? this.#req.url ?? '/';\n parts.push(uri);\n\n const queryString = uri.includes('?') ? uri.split('?')[1] : '';\n if (queryString) {\n parts.push(queryString);\n }\n\n const body = this.#req.body;\n if (body) {\n parts.push(typeof body === 'string' ? body : JSON.stringify(body));\n }\n\n const headers = this.#req.headers ?? {};\n const excludedHeaders = new Set([\n 'host', 'connection', 'cache-control', 'accept', 'accept-encoding',\n 'accept-language', 'priority', 'sec-ch-ua', 'sec-ch-ua-mobile',\n 'sec-ch-ua-platform', 'sec-fetch-dest', 'sec-fetch-mode',\n 'sec-fetch-site', 'sec-fetch-user', 'upgrade-insecure-requests'\n ]);\n\n for (const [name, value] of Object.entries(headers)) {\n if (!excludedHeaders.has(name)) {\n parts.push(`${name}: ${value}`);\n }\n }\n\n const cookies = this.#parseCookies();\n const cookieStr = Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ');\n if (cookieStr) {\n parts.push(cookieStr);\n }\n\n return [parts.join(' ')];\n }\n\n #resolveWildcard(obj, pattern) {\n if (typeof obj !== 'object' || obj === null) {\n return [];\n }\n\n const prefix = pattern.slice(0, -1);\n const values = [];\n\n for (const [key, value] of Object.entries(obj)) {\n if (key.startsWith(prefix)) {\n values.push(value);\n }\n }\n\n return values;\n }\n\n #getNestedValue(obj, key) {\n if (typeof obj !== 'object' || obj === null) {\n return undefined;\n }\n\n if (key in obj) {\n return obj[key];\n }\n\n const parts = key.split('.');\n let current = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== 'object') {\n return undefined;\n }\n current = current[part];\n }\n\n return current;\n }\n\n #parseCookies() {\n if (this.#cookies !== null) {\n return this.#cookies;\n }\n\n if (this.#req.cookies) {\n this.#cookies = this.#req.cookies;\n return this.#cookies;\n }\n\n const header = this.#req.headers?.cookie;\n if (!header) {\n this.#cookies = {};\n return this.#cookies;\n }\n\n const cookies = {};\n\n for (const pair of header.split(';')) {\n const eqIndex = pair.indexOf('=');\n if (eqIndex === -1) {\n continue;\n }\n const name = pair.substring(0, eqIndex).trim();\n const value = pair.substring(eqIndex + 1).trim();\n cookies[name] = value;\n }\n\n this.#cookies = cookies;\n return this.#cookies;\n }\n}\n","/**\n * Request Normalization Pipeline\n *\n * Normalizes request data to prevent encoding-based bypass attacks.\n * This module handles URL encoding, HTML entities, SQL comments, and other\n * obfuscation techniques that attackers use to evade pattern matching.\n */\n\nconst HTML_ENTITIES = {\n '&': '&',\n '<': '<',\n '>': '>',\n '"': '\"',\n ''': \"'\",\n ''': \"'\",\n ' ': ' ',\n ''': \"'\",\n '"': '\"',\n '<': '<',\n '>': '>',\n '&': '&',\n '"': '\"',\n '<': '<',\n '>': '>',\n '&': '&'\n};\n\nconst MAX_DECODE_ITERATIONS = 5;\n\nexport function normalize(value, options = {}) {\n if (typeof value !== 'string') {\n return value;\n }\n\n const opts = {\n urlDecode: true,\n htmlDecode: true,\n sqlComments: true,\n nullBytes: true,\n whitespace: true,\n ...options\n };\n\n let result = value;\n\n if (opts.nullBytes) {\n result = removeNullBytes(result);\n }\n\n if (opts.urlDecode) {\n result = urlDecode(result);\n }\n\n if (opts.htmlDecode) {\n result = htmlEntityDecode(result);\n }\n\n if (opts.sqlComments) {\n result = removeSqlComments(result);\n }\n\n if (opts.whitespace) {\n result = normalizeWhitespace(result);\n }\n\n return result;\n}\n\nexport function urlDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n let previous = '';\n let iterations = 0;\n\n while (result !== previous && iterations < MAX_DECODE_ITERATIONS) {\n previous = result;\n iterations++;\n\n try {\n result = decodeURIComponent(result);\n } catch {\n result = safeUrlDecode(result);\n break;\n }\n }\n\n return result;\n}\n\nfunction safeUrlDecode(value) {\n return value.replace(/%([0-9A-Fa-f]{2})/g, (match, hex) => {\n try {\n return String.fromCharCode(parseInt(hex, 16));\n } catch {\n return match;\n }\n });\n}\n\nexport function htmlEntityDecode(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n\n for (const [entity, char] of Object.entries(HTML_ENTITIES)) {\n result = result.split(entity).join(char);\n }\n\n result = result.replace(/&#(\\d+);/g, (match, code) => {\n const num = parseInt(code, 10);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n result = result.replace(/&#x([0-9A-Fa-f]+);/g, (match, hex) => {\n const num = parseInt(hex, 16);\n return num > 0 && num < 65536 ? String.fromCharCode(num) : match;\n });\n\n return result;\n}\n\nexport function removeSqlComments(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value;\n\n result = result.replace(/\\/\\*[\\s\\S]*?\\*\\//g, ' ');\n result = result.replace(/\\/\\*![\\s\\S]*?\\*\\//g, ' ');\n result = result.replace(/--[^\\r\\n]*/g, '');\n result = result.replace(/#[^\\r\\n]*/g, '');\n\n return result;\n}\n\nexport function removeNullBytes(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/\\x00/g, '');\n\n result = result.replace(/[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F]/g, '');\n\n return result;\n}\n\nexport function normalizeWhitespace(value) {\n if (typeof value !== 'string') {\n return value;\n }\n\n let result = value.replace(/[\\t\\r\\n\\f\\v]+/g, ' ');\n\n result = result.replace(/ {2,}/g, ' ');\n\n return result;\n}\n\nfunction serializeForRawDetection(body, visited = new Set(), isRoot = true) {\n if (body === null || body === undefined) {\n return isRoot ? '' : 'null';\n }\n\n if (typeof body === 'string') {\n return isRoot ? body : JSON.stringify(body);\n }\n\n if (typeof body !== 'object') {\n return String(body);\n }\n\n if (visited.has(body)) {\n return '[Circular]';\n }\n visited.add(body);\n\n if (Array.isArray(body)) {\n const items = body.map(item => serializeForRawDetection(item, visited, false));\n return '[' + items.join(',') + ']';\n }\n\n // Object.getOwnPropertyNames includes non-enumerable own properties, so a __proto__ key\n // set via Object.defineProperty (as modern JSON.parse may do) is included in the output.\n const keys = Object.getOwnPropertyNames(body);\n const parts = keys.map(key => {\n const val = serializeForRawDetection(body[key], visited, false);\n return JSON.stringify(key) + ':' + val;\n });\n\n return '{' + parts.join(',') + '}';\n}\n\nexport function normalizeRequest(req, options = {}) {\n // Prefer a caller-provided verbatim body string (set by the fetch/node adapters):\n // it preserves literal keys like `__proto__` that JSON.stringify drops, which is\n // what makes prototype-pollution rules on `raw` robust. Fall back to a\n // reconstruction from the parsed body (the Express path, which has no raw text).\n const rawBody = typeof req._rawBody === 'string'\n ? req._rawBody\n : serializeForRawDetection(req.body ?? null);\n\n return {\n query: normalizeObject(req.query || {}, options),\n body: normalizeObject(req.body || {}, options),\n headers: normalizeObject(req.headers || {}, options),\n url: normalize(req.url || '', options),\n originalUrl: normalize(req.originalUrl || req.url || '', options),\n _rawBody: rawBody\n };\n}\n\nexport function normalizeObject(value, options = {}) {\n if (typeof value === 'string') {\n return normalize(value, options);\n }\n\n if (Array.isArray(value)) {\n return value.map(item => normalizeObject(item, options));\n }\n\n if (typeof value === 'object' && value !== null) {\n const result = {};\n\n for (const [key, val] of Object.entries(value)) {\n result[key] = normalizeObject(val, options);\n }\n\n return result;\n }\n\n return value;\n}\n\nexport function createMatchVariants(value) {\n if (typeof value !== 'string') {\n return [value];\n }\n\n const variants = new Set();\n\n variants.add(value);\n\n const urlDecoded = urlDecode(value);\n variants.add(urlDecoded);\n\n const htmlDecoded = htmlEntityDecode(value);\n variants.add(htmlDecoded);\n\n const bothDecoded = htmlEntityDecode(urlDecoded);\n variants.add(bothDecoded);\n\n const normalized = normalize(value);\n variants.add(normalized);\n\n variants.add(value.toLowerCase());\n variants.add(normalized.toLowerCase());\n\n return Array.from(variants);\n}\n\nexport const _testExports = {\n safeUrlDecode,\n serializeForRawDetection,\n HTML_ENTITIES,\n MAX_DECODE_ITERATIONS\n};\n","import { RequestResolver } from './request.js';\nimport { normalizeRequest } from './normalizer.js';\n\nconst REDOS_PATTERNS = [\n /\\(\\w\\+\\)\\+/,\n /\\(\\w\\*\\)\\+/,\n /\\(\\w\\+\\)\\*/,\n /\\(\\w\\*\\)\\*/,\n /\\(\\w\\|\\w\\)\\+/\n];\n\nfunction safeRegExp(pattern) {\n if (!pattern) {\n return null;\n }\n\n for (const dangerous of REDOS_PATTERNS) {\n if (dangerous.test(pattern)) {\n return null;\n }\n }\n\n const match = pattern.match(/^\\/(.+?)\\/([gimsuy]*)$/s);\n if (!match) {\n return null;\n }\n\n try {\n return new RegExp(match[1], match[2]);\n } catch {\n return null;\n }\n}\n\n// ctype_*/is_numeric compare their character-class result to the rule's expected\n// `value`. Rules are almost always written `{ \"type\": \"ctype_digit\", \"value\": false }`\n// meaning \"flag when NOT of this class\" — so the result must be compared to matchVal,\n// not returned raw. Empty/absent values never match (mirrors engine-php's `$value != ''`\n// guard), otherwise every missing parameter false-positives a `value:false` rule.\n// When `value` is not a boolean (null/omitted) the legacy default of `true` is used, so\n// existing rules that relied on the raw class check are unaffected.\nfunction ctypeResult(strValue, isClass, matchVal) {\n if (strValue === '') {\n return false;\n }\n const expected = typeof matchVal === 'boolean' ? matchVal : true;\n return isClass === expected;\n}\n\n// array_key_value: navigate `match.key` (a dot-path, or an array of paths) inside the\n// decoded value and run the nested `match.match` against whatever it finds. Mirrors\n// engine-php's recursive array_key_value handling.\nfunction arrayKeyValue(value, matchObj) {\n if (!matchObj || !matchObj.match || value === null || typeof value !== 'object') {\n return false;\n }\n const keys = Array.isArray(matchObj.key) ? matchObj.key : [matchObj.key];\n const sub = matchObj.match;\n\n for (const key of keys) {\n let node = value;\n for (const part of String(key).split('.')) {\n if (node === null || typeof node !== 'object') {\n node = undefined;\n break;\n }\n node = node[part];\n }\n if (node === undefined) {\n continue;\n }\n const candidates = Array.isArray(node) ? node : [node];\n for (const candidate of candidates) {\n if (matchValue(sub.type, candidate, sub.value, sub)) {\n return true;\n }\n }\n }\n return false;\n}\n\n// Report an unknown/removed match type once, so a rule referencing it is not silently\n// unenforced (ADR: \"unknown match type → skipped and logged, never silently passed\").\nconst warnedMatchTypes = new Set();\nfunction warnUnsupportedMatchType(type) {\n if (type == null || warnedMatchTypes.has(type)) {\n return;\n }\n warnedMatchTypes.add(type);\n console.warn(\n `[patchstack] Unsupported rule_v2 match type \"${type}\" — condition treated as no-match. ` +\n `Rules relying on it are not enforced by this engine.`\n );\n}\n\n// `matchObj` is the full match object; needed by types that read sibling fields\n// (array_key_value reads `key`/`match`). Optional so direct callers/tests can keep\n// using the (type, value, matchVal) signature.\nfunction matchValue(type, value, matchVal, matchObj) {\n if (value === null || value === undefined) {\n if (type === 'isset') {\n return false;\n }\n return false;\n }\n\n const strValue = typeof value === 'string' ? value : String(value);\n\n switch (type) {\n case 'equals':\n return strValue == matchVal;\n\n case 'equals_strict':\n return strValue === matchVal;\n\n case 'contains':\n // engine-php exposes `stripos` as an alias of `contains`.\n case 'stripos': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return lower.includes(target);\n }\n\n case 'not_contains': {\n const lower = strValue.toLowerCase();\n const target = String(matchVal).toLowerCase();\n return !lower.includes(target);\n }\n\n case 'regex': {\n const regex = safeRegExp(matchVal);\n if (!regex) {\n return false;\n }\n return regex.test(strValue);\n }\n\n case 'more_than':\n return Number(strValue) > Number(matchVal);\n\n case 'less_than':\n return Number(strValue) < Number(matchVal);\n\n case 'ctype_digit':\n return ctypeResult(strValue, /^\\d+$/.test(strValue), matchVal);\n\n case 'ctype_alnum':\n return ctypeResult(strValue, /^[\\w$\\u0080-\\uFFFF]+$/.test(strValue), matchVal);\n\n case 'is_numeric':\n return ctypeResult(strValue, !isNaN(strValue) && strValue.trim() !== '', matchVal);\n\n case 'isset':\n return true;\n\n case 'in_array': {\n const arr = Array.isArray(matchVal) ? matchVal : [matchVal];\n return arr.includes(strValue);\n }\n\n case 'not_in_array': {\n const arr = Array.isArray(matchVal) ? matchVal : [matchVal];\n return !arr.includes(strValue);\n }\n\n case 'array_in_array': {\n if (!Array.isArray(value) || !Array.isArray(matchVal)) {\n return false;\n }\n return value.some(v => matchVal.includes(v));\n }\n\n case 'hostname': {\n try {\n const url = new URL(strValue);\n return url.hostname === matchVal;\n } catch {\n return false;\n }\n }\n\n case 'quotes':\n // engine-php exposes `inline_js_xss` as an alias of `quotes`.\n case 'inline_js_xss':\n return /['\"]/.test(strValue);\n\n case 'inline_xss':\n // Attribute-breakout heuristic: a quote AND a `>` or `=` (matches engine-php).\n return /['\"]/.test(strValue) && /[>=]/.test(strValue);\n\n case 'ctype_special': {\n // engine-php strips space, `_`, `-`, `,` then applies the alnum/unicode check,\n // and compares the result to `value` (usually false = \"flag when NOT clean\").\n const stripped = strValue.replace(/[ _\\-,]/g, '');\n return ctypeResult(strValue, /^[\\w$-]*$/.test(stripped), matchVal);\n }\n\n case 'array_key_value':\n // Navigate `match.key` (dot-path, or array of paths) inside the decoded value and\n // run the nested `match.match` against it — e.g. a json_decode'd body where a\n // specific key must hold a specific value.\n return arrayKeyValue(value, matchObj);\n\n // file_contains (uploaded-file content scanning) is NOT WordPress-specific, but is\n // not yet implemented in the JS engine (needs multipart body access). Kept as an\n // explicit, documented no-match rather than silently hitting `default`.\n case 'file_contains':\n return false;\n\n // WordPress-only match types (current_user_cannot, general_xss, getShortcodeAtts,\n // getBlockAtts) have been removed: they depend on WP capabilities, wp_kses_post, and\n // the shortcode/block parsers, and have no meaning in a JS app. They now fall through\n // here and are reported once instead of pretending to be a no-match.\n default:\n warnUnsupportedMatchType(type);\n return false;\n }\n}\n\nexport class RuleEngine {\n #rules;\n #whitelists;\n #whitelistKeys;\n #onError;\n #reportedErrors = new Set();\n\n constructor({ firewall = [], whitelists = [], whitelist_keys = {}, onError } = {}) {\n this.#rules = firewall;\n this.#whitelists = whitelists;\n this.#whitelistKeys = whitelist_keys;\n this.#onError = onError;\n }\n\n // A mitigation engine must never take down the app it protects: any error while\n // evaluating a rule is reported and the request is allowed through (fail open). A\n // malformed rule is skipped without aborting the rest of the ruleset.\n #reportError(err) {\n if (this.#onError) {\n this.#onError(err);\n return;\n }\n // Default: log once per distinct message so a persistently-broken rule doesn't\n // spam per request (never silently swallow — hosts can pass `onError` for structured logging).\n const key = err && err.message ? err.message : String(err);\n if (this.#reportedErrors.has(key)) {\n return;\n }\n this.#reportedErrors.add(key);\n console.error('[patchstack] rule evaluation error (rule skipped, request allowed):', err);\n }\n\n evaluate(req) {\n let normalizedReq;\n let resolver;\n try {\n normalizedReq = { ...req, ...normalizeRequest(req) };\n resolver = new RequestResolver(normalizedReq);\n } catch (err) {\n this.#reportError(err);\n return { blocked: false, rule: null, message: null }; // fail open\n }\n\n for (const rule of this.#rules) {\n try {\n const conditions = rule.rule_v2;\n\n if (!Array.isArray(conditions) || conditions.length === 0) {\n continue;\n }\n\n if (this.#evaluateRule(conditions, resolver)) {\n if (this.#isWhitelisted(normalizedReq, rule)) {\n continue;\n }\n\n return {\n blocked: true,\n rule,\n message: rule.message ?? `Blocked by Patchstack WAF rule: ${rule.title ?? rule.id}`\n };\n }\n } catch (err) {\n // Skip this rule and keep evaluating the rest — one bad rule never blocks a\n // request nor shadows the rules after it.\n this.#reportError(err);\n }\n }\n\n return { blocked: false, rule: null, message: null };\n }\n\n #evaluateRule(conditions, resolver) {\n const inclusiveConditions = conditions.filter(c => c.inclusive);\n const nonInclusiveConditions = conditions.filter(c => !c.inclusive);\n\n if (inclusiveConditions.length > 0) {\n let inclusiveHits = 0;\n\n for (const condition of inclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n inclusiveHits++;\n }\n }\n\n if (inclusiveHits === inclusiveConditions.length) {\n return true;\n }\n }\n\n for (const condition of nonInclusiveConditions) {\n if (this.#evaluateCondition(condition, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n\n #evaluateCondition(condition, resolver) {\n const { parameter, match, mutations } = condition;\n\n if (parameter === 'rules' && Array.isArray(condition.rules)) {\n return this.#evaluateRule(condition.rules, resolver);\n }\n\n const values = resolver.resolve(parameter);\n\n if (values.length === 0) {\n return false;\n }\n\n for (let value of values) {\n if (mutations) {\n value = resolver.applyMutations(mutations, value);\n }\n\n // array_key_value inspects the object as a whole (it navigates a key path),\n // so it must not go through the per-value object iteration below.\n if (match.type === 'array_key_value') {\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n continue;\n }\n\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n for (const v of Object.values(value)) {\n if (matchValue(match.type, v, match.value, match)) {\n return true;\n }\n }\n continue;\n }\n\n if (matchValue(match.type, value, match.value, match)) {\n return true;\n }\n }\n\n return false;\n }\n\n #isWhitelisted(req, rule) {\n if (!this.#whitelists || this.#whitelists.length === 0) {\n return false;\n }\n\n for (const whitelist of this.#whitelists) {\n if (!Array.isArray(whitelist.rule_v2)) {\n continue;\n }\n\n if (whitelist.rule_id && whitelist.rule_id !== rule.id) {\n continue;\n }\n\n const resolver = new RequestResolver(req);\n if (this.#evaluateRule(whitelist.rule_v2, resolver)) {\n return true;\n }\n }\n\n return false;\n }\n}\n\nexport const _testExports = { matchValue, safeRegExp };\n","// Web Fetch adapter — runs the node-waf engine on any runtime where a request is a\n// WHATWG `Request` and a handler returns a `Response`: Cloudflare Workers, Bun, Deno,\n// Hono, Next.js edge, and TanStack Start's `server.ts`. This is the surface AI-builder\n// apps actually deploy to, and where require-based instrumentation can't reach.\n//\n// The engine already consumes a runtime-neutral request shape; this adapter builds that\n// shape from a `Request`, so no engine changes are needed beyond keeping the hot path\n// free of Node-only APIs.\nimport { RuleEngine } from './engine.js';\n\n// Build the engine's request shape from a WHATWG Request. The body is read from a\n// CLONE so the downstream handler still receives an intact request.\nexport async function fromFetchRequest(request) {\n const url = new URL(request.url);\n const method = (request.method || 'GET').toUpperCase();\n\n const headers = {};\n request.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n let rawBody = '';\n if (method !== 'GET' && method !== 'HEAD') {\n try {\n rawBody = await request.clone().text();\n } catch {\n rawBody = '';\n }\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n if (rawBody) {\n if (contentType.includes('application/json')) {\n try {\n body = JSON.parse(rawBody);\n } catch {\n body = {};\n }\n } else if (contentType.includes('application/x-www-form-urlencoded')) {\n body = {};\n for (const [k, v] of new URLSearchParams(rawBody)) {\n body[k] = k in body ? [].concat(body[k], v) : v;\n }\n }\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n headers,\n ip: forwarded.split(',')[0].trim(),\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify\n // drops, so prototype-pollution rules on `raw` are robust.\n _rawBody: rawBody\n };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlockResponse(result) {\n return new Response(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n }),\n { status: 403, headers: { 'content-type': 'application/json' } }\n );\n}\n\n/**\n * Returns a guard `(request) => Promise<Response|null>`. A `Response` means blocked;\n * `null` means allowed — the caller should proceed to its handler. Accepts either a\n * rules bundle (`{ firewall, whitelists, whitelist_keys }`) or a `RuleEngine` instance.\n * Fails open: a rule that throws never blocks the request.\n */\nexport function createFetchMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function'\n ? rulesData\n : new RuleEngine(rulesData);\n\n const guard = async (request) => {\n const req = await fromFetchRequest(request);\n\n let result;\n try {\n result = engine.evaluate(req);\n } catch (err) {\n if (options.onError) {\n options.onError(err);\n }\n return null; // fail open\n }\n\n if (result.blocked) {\n if (options.onBlock) {\n options.onBlock({\n rule: result.rule,\n message: result.message,\n request: { method: req.method, url: req.url, ip: req.ip }\n });\n }\n return (options.response || defaultBlockResponse)(result);\n }\n\n return null;\n };\n\n guard.engine = engine;\n return guard;\n}\n\n/**\n * Wrap a fetch handler so every request is screened first. One-line hook:\n * export default { fetch: wrapFetchHandler(app.fetch, rulesData) }\n */\nexport function wrapFetchHandler(handler, rulesData, options = {}) {\n const guard = createFetchMiddleware(rulesData, options);\n const wrapped = async (request, ...rest) => {\n const blocked = await guard(request);\n return blocked ?? handler(request, ...rest);\n };\n wrapped.engine = guard.engine;\n return wrapped;\n}\n","// Raw Node.js / Connect adapter — for traditional Node servers where the request is a\n// `http.IncomingMessage` and the body is NOT already parsed (plain `http`, Connect,\n// Express without a body-parser, Fastify via its Node req). It buffers the body once,\n// builds the engine's request shape, and blocks or calls `next()`.\n//\n// This complements the Express `createMiddleware` (which assumes `req.body`/`req.query`\n// are already populated) and the Web-Fetch adapter (Workers/edge). Mount it FIRST, before\n// any body-parser — it consumes the stream and exposes the parsed body as `req.body`.\nimport { RuleEngine } from './engine.js';\n\n// Build the engine's request shape from a Node IncomingMessage + its raw body text.\nexport function fromNodeRequest(req, rawBody = '') {\n const method = (req.method || 'GET').toUpperCase();\n\n const headers = {};\n for (const [key, value] of Object.entries(req.headers || {})) {\n headers[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value;\n }\n\n const host = headers.host || 'localhost';\n const url = new URL(req.url || '/', `http://${host}`);\n\n const query = {};\n for (const [key, value] of url.searchParams) {\n if (key in query) {\n query[key] = Array.isArray(query[key]) ? [...query[key], value] : [query[key], value];\n } else {\n query[key] = value;\n }\n }\n\n const contentType = headers['content-type'] || '';\n let body = {};\n if (rawBody) {\n if (contentType.includes('application/json')) {\n try {\n body = JSON.parse(rawBody);\n } catch {\n body = {};\n }\n } else if (contentType.includes('application/x-www-form-urlencoded')) {\n body = {};\n for (const [k, v] of new URLSearchParams(rawBody)) {\n body[k] = k in body ? [].concat(body[k], v) : v;\n }\n }\n }\n\n const uri = url.pathname + url.search;\n const forwarded =\n headers['cf-connecting-ip'] || headers['x-forwarded-for'] || headers['x-real-ip'] || '';\n\n return {\n method,\n url: uri,\n originalUrl: uri,\n query,\n body,\n headers,\n ip: forwarded.split(',')[0].trim() || (req.socket && req.socket.remoteAddress) || '',\n cookies: parseCookies(headers.cookie),\n // Verbatim body text: preserves literal keys (e.g. `__proto__`) that JSON.stringify drops.\n _rawBody: rawBody\n };\n}\n\nfunction parseCookies(header) {\n const cookies = {};\n if (!header) {\n return cookies;\n }\n for (const pair of header.split(';')) {\n const idx = pair.indexOf('=');\n if (idx === -1) {\n continue;\n }\n cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n return cookies;\n}\n\nfunction defaultBlock(res, result) {\n res.statusCode = 403;\n res.setHeader('content-type', 'application/json');\n res.end(\n JSON.stringify({\n error: 'Blocked by Patchstack WAF',\n message: result.message,\n timestamp: new Date().toISOString()\n })\n );\n}\n\n/**\n * Connect/Express-style middleware `(req, res, next)` that buffers the body itself.\n * Accepts a `RuleEngine` instance or a `{ firewall, whitelists, whitelist_keys }` bundle.\n * Fails open: an engine error (or oversized body) never blocks the request.\n * Options: `{ maxBodyBytes = 1MiB, onBlock, onError, response }`.\n */\nexport function createNodeMiddleware(rulesData, options = {}) {\n const engine =\n rulesData && typeof rulesData.evaluate === 'function' ? rulesData : new RuleEngine(rulesData);\n const maxBytes = options.maxBodyBytes ?? 1024 * 1024;\n\n return function guard(req, res, next) {\n const chunks = [];\n let size = 0;\n let overflow = false;\n\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n\n req.on('error', (err) => next(err));\n\n req.on('end', () => {\n const rawBody = overflow ? '' : Buffer.concat(chunks).toString('utf8');\n const shaped = fromNodeRequest(req, rawBody);\n\n let result;\n try {\n result = engine.evaluate(shaped);\n } catch (err) {\n if (options.onError) {\n options.onError(err);\n }\n return next(); // fail open\n }\n\n if (result.blocked) {\n if (options.onBlock) {\n options.onBlock({\n rule: result.rule,\n message: result.message,\n request: { method: shaped.method, url: shaped.url, ip: shaped.ip }\n });\n }\n return (options.response || defaultBlock)(res, result);\n }\n\n // Expose the parsed body downstream so a body-parser isn't also required.\n req.body = shaped.body;\n next();\n });\n };\n}\n","// @patchstack/protect — \"Protect = respond\".\n//\n// One entry point that composes the node-waf engine + adapters with:\n// - a rule source: an explicit bundle, or fetched from the Patchstack API (token),\n// with a disk cache so the engine keeps working on last-known-good if the API is down\n// - execution modes: 'dry-run' (detect + log, never block — the safe onramp) and\n// 'block' (enforce). Default is 'dry-run'.\n// - fail-open everywhere: a rule/engine error never blocks (or crashes) a request.\n//\n// Runtime guards: .express(), .node(), .fetch(handler) / .fetchGuard() — same policy,\n// every runtime an AI builder deploys to.\n// Vendored node-waf engine (this package is self-contained — no @patchstack/node-waf dep).\nimport { RuleEngine, PatchstackRuleClient } from './engine/index.js';\nimport { fromFetchRequest } from './engine/fetch.js';\nimport { fromNodeRequest } from './engine/node.js';\nimport { readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\n// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\nexport { createSupabaseGuard, GUARD_PATH } from './supabase-guard.js';\n\n// Server-function guard. Modern Lovable apps mutate data through TanStack server functions\n// (browser → server fn → server-side Supabase client), which bypass the browser-side tunnel the\n// Supabase guard relies on. This inspects the decoded server-fn call args against the SAME policy\n// by feeding them through fetchGuard (the args become the request body, so the engine resolves\n// `post.<field>` exactly as it does for a tunneled Supabase insert). Returns a block receipt\n// { rule?, message } to throw on, or null to allow (also null in dry-run — the detection is still\n// recorded by fetchGuard). Fail-open on any error.\nexport function createServerFnGuard({ protection }) {\n const guard = protection.fetchGuard();\n return async (data) => {\n let res;\n try {\n const req = new Request('https://patchstack.local/_serverfn', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(data ?? {}),\n });\n res = await guard(req);\n } catch {\n return null; // fail open\n }\n if (!res) return null; // allowed (or dry-run)\n let body = {};\n try {\n body = await res.clone().json();\n } catch {\n /* non-JSON block response */\n }\n return { rule: body.rule, message: body.message || 'Blocked by Patchstack' };\n };\n}\n\nexport async function createProtection(options = {}) {\n const mode = options.mode === 'block' ? 'block' : 'dry-run';\n const onError = options.onError;\n const onDetect = options.onDetect ?? defaultOnDetect;\n\n const bundle = await resolveRules(options);\n const engine = new RuleEngine({ ...bundle, onError });\n\n // Given an evaluation result, either enforce (block mode) or just record (dry-run).\n const decide = (result, block, allow) => {\n if (!result || !result.blocked) return allow();\n onDetect({ mode, rule: result.rule, message: result.message });\n return mode === 'block' ? block() : allow();\n };\n\n const protection = {\n mode,\n rules: bundle,\n\n // (request) => Response | null (null = allow, caller proceeds)\n fetchGuard() {\n return async (request) => {\n let result;\n try {\n result = engine.evaluate(await fromFetchRequest(request));\n } catch (err) {\n onError?.(err);\n return null; // fail open\n }\n return decide(result, () => blockResponse(result), () => null);\n };\n },\n\n // Wrap a fetch handler: export default { fetch: protection.fetch(app.fetch) }\n fetch(handler) {\n const guard = protection.fetchGuard();\n return async (request, ...rest) => (await guard(request)) ?? handler(request, ...rest);\n },\n\n // Express middleware (expects express-parsed req.query/req.body).\n express() {\n return (req, res, next) => {\n let result;\n try {\n result = engine.evaluate(req);\n } catch (err) {\n onError?.(err);\n return next();\n }\n decide(result, () => res.status(403).json(blockBody(result)), () => next());\n };\n },\n\n // Node / Connect middleware — buffers the body itself (no body-parser needed).\n node(nodeOptions = {}) {\n const maxBytes = nodeOptions.maxBodyBytes ?? 1024 * 1024;\n return (req, res, next) => {\n const chunks = [];\n let size = 0;\n let overflow = false;\n req.on('data', (chunk) => {\n size += chunk.length;\n if (size > maxBytes) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n req.on('error', (err) => {\n onError?.(err);\n next();\n });\n req.on('end', () => {\n const rawBody = overflow ? '' : Buffer.concat(chunks).toString('utf8');\n let result;\n try {\n result = engine.evaluate(fromNodeRequest(req, rawBody));\n } catch (err) {\n onError?.(err);\n return next();\n }\n decide(\n result,\n () => {\n res.statusCode = 403;\n res.setHeader('content-type', 'application/json');\n res.end(JSON.stringify(blockBody(result)));\n },\n () => next(),\n );\n });\n };\n },\n };\n\n return protection;\n}\n\n// --- rule source --------------------------------------------------------\n\nasync function resolveRules(options) {\n if (options.rules) {\n return normalizeBundle(options.rules);\n }\n\n if (options.token) {\n const client = new PatchstackRuleClient({ token: options.token, baseUrl: options.baseUrl });\n const res = await client.getRules();\n if (res.success) {\n const bundle = normalizeBundle(res);\n cacheWrite(options.cacheDir, bundle);\n return bundle;\n }\n const cached = cacheRead(options.cacheDir);\n if (cached) {\n options.onError?.(new Error(`rule fetch failed (${res.error}); using cached bundle`));\n return normalizeBundle(cached);\n }\n options.onError?.(new Error(`rule fetch failed (${res.error}); no cache — running with no rules`));\n return emptyBundle();\n }\n\n return emptyBundle();\n}\n\nfunction normalizeBundle(b) {\n return {\n firewall: Array.isArray(b.firewall) ? b.firewall : [],\n whitelists: Array.isArray(b.whitelists) ? b.whitelists : [],\n whitelist_keys: b.whitelist_keys ?? {},\n };\n}\n\nfunction emptyBundle() {\n return { firewall: [], whitelists: [], whitelist_keys: {} };\n}\n\nfunction cachePath(dir) {\n return join(dir, 'patchstack-rules.json');\n}\n\nfunction cacheWrite(dir, bundle) {\n if (!dir) return;\n try {\n mkdirSync(dir, { recursive: true });\n writeFileSync(cachePath(dir), JSON.stringify(bundle));\n } catch {\n /* cache is best-effort */\n }\n}\n\nfunction cacheRead(dir) {\n if (!dir) return null;\n try {\n return JSON.parse(readFileSync(cachePath(dir), 'utf8'));\n } catch {\n return null;\n }\n}\n\n// --- responses ----------------------------------------------------------\n\nfunction blockBody(result) {\n return {\n error: 'Blocked by Patchstack',\n rule: result.rule?.id,\n message: result.message,\n };\n}\n\nfunction blockResponse(result) {\n return new Response(JSON.stringify(blockBody(result)), {\n status: 403,\n headers: { 'content-type': 'application/json' },\n });\n}\n\nfunction defaultOnDetect({ mode, rule, message }) {\n const tag = mode === 'block' ? 'BLOCK' : 'DETECT (dry-run)';\n console.warn(`[patchstack] ${tag} rule=${rule?.id ?? '?'} ${message ?? ''}`.trim());\n}\n","// Supabase-tunnel guard for AI-builder apps (Lovable / TanStack Start + Supabase).\n//\n// The vibe-coded app's browser talks straight to Supabase, bypassing the app's own server —\n// so a normal in-app WAF never sees the data traffic. The installer patches the generated\n// Supabase client to tunnel every call through this guard (running in the app's own server /\n// Worker). The guard runs the Patchstack protection policy on the tunneled request, then\n// forwards it to Supabase — pinned to the app's own project so it can't be turned into an\n// open proxy (SSRF).\n//\n// The heavy lifting (rule evaluation, dry-run/block, fail-open) is `protection.fetchGuard()`\n// from `createProtection` — this module is just the Supabase-specific tunnel around it.\n\nexport const GUARD_PATH = '/_patchstack/guard';\n\n// Headers we must not copy verbatim when re-emitting the upstream response.\nconst HOP_BY_HOP = new Set(['content-encoding', 'content-length', 'transfer-encoding', 'connection']);\n\n/**\n * @param {object} opts\n * @param {{ fetchGuard: () => (req: Request) => Promise<Response|null> }} opts.protection a createProtection() result\n * @param {string|undefined} opts.supabaseUrl the app's Supabase project URL (server-side env) — the only allowed forward target\n * @param {typeof fetch} [opts.fetchImpl] injectable fetch (tests)\n * @returns {(request: Request) => Promise<Response>}\n */\nexport function createSupabaseGuard({ protection, supabaseUrl, fetchImpl = fetch }) {\n const guard = protection.fetchGuard();\n const allowedOrigin = supabaseUrl ? new URL(supabaseUrl).origin : null;\n\n return async function handleGuardRequest(request) {\n const target = request.headers.get('x-ps-target');\n if (!target) return new Response('patchstack: missing x-ps-target', { status: 400 });\n\n let targetUrl;\n try {\n targetUrl = new URL(target);\n } catch {\n return new Response('patchstack: invalid target', { status: 400 });\n }\n\n // SSRF pin: only ever forward to the app's own Supabase project (origin from server-side\n // env, never the client-supplied header). Anything else — internal hosts, cloud metadata,\n // a different scheme — is rejected before any outbound request.\n if (!allowedOrigin || targetUrl.protocol !== 'https:' || targetUrl.origin !== allowedOrigin) {\n return new Response('patchstack: target not allowed', { status: 403 });\n }\n\n const hasBody = request.method !== 'GET' && request.method !== 'HEAD';\n const bodyText = hasBody ? await request.text() : '';\n\n // Evaluate the tunneled call against the policy. fetchGuard returns a 403 Response when it\n // blocks (block mode + match), or null to allow (allow, or dry-run — it records via onDetect).\n const evalReq = new Request(targetUrl.toString(), {\n method: request.method,\n headers: request.headers,\n body: hasBody ? bodyText : undefined,\n });\n const blocked = await guard(evalReq);\n if (blocked) return blocked;\n\n // Allowed → forward to Supabase, server-side.\n const forwardHeaders = new Headers(request.headers);\n forwardHeaders.delete('x-ps-target');\n forwardHeaders.delete('host');\n const upstream = await fetchImpl(targetUrl.toString(), {\n method: request.method,\n headers: forwardHeaders,\n body: hasBody ? bodyText : undefined,\n redirect: 'manual',\n });\n\n const outHeaders = new Headers();\n upstream.headers.forEach((value, key) => {\n if (!HOP_BY_HOP.has(key.toLowerCase())) outHeaders.set(key, value);\n });\n const buf = await upstream.arrayBuffer();\n return new Response(buf, { status: upstream.status, statusText: upstream.statusText, headers: outHeaders });\n };\n}\n"],"mappings":"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","names":["parseCookies"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patchstack/connect",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Patchstack connector for JavaScript applications. Scans your lockfile and reports installed packages to Patchstack for vulnerability monitoring.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"patchstack",
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
"types": "./dist/index.d.ts",
|
|
22
22
|
"import": "./dist/index.js",
|
|
23
23
|
"require": "./dist/index.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./protect": {
|
|
26
|
+
"types": "./dist/protect.d.ts",
|
|
27
|
+
"import": "./dist/protect.js",
|
|
28
|
+
"require": "./dist/protect.cjs"
|
|
24
29
|
}
|
|
25
30
|
},
|
|
26
31
|
"bin": {
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export type Action = "ALLOW" | "LOG" | "BLOCK" | "REDIRECT";
|
|
2
|
-
|
|
3
|
-
export declare const ACTIONS: {
|
|
4
|
-
ALLOW: "ALLOW";
|
|
5
|
-
LOG: "LOG";
|
|
6
|
-
BLOCK: "BLOCK";
|
|
7
|
-
REDIRECT: "REDIRECT";
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/** A normalized snapshot of one request. The framework guard builds this. */
|
|
11
|
-
export interface RequestContext {
|
|
12
|
-
method: string;
|
|
13
|
-
url: string;
|
|
14
|
-
headers: Record<string, string>;
|
|
15
|
-
/** Parsed request payload; rule parameters read from here (e.g. "insert.title"). */
|
|
16
|
-
body: Record<string, unknown>;
|
|
17
|
-
ip?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface RuleCondition {
|
|
21
|
-
parameter: string | string[];
|
|
22
|
-
match: { type: "inline_xss" | "contains" | "equals" | string; value?: unknown };
|
|
23
|
-
mutations?: string[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Only apply the rule if the app has this package at a vulnerable version. */
|
|
27
|
-
export interface PackageCond {
|
|
28
|
-
package: string;
|
|
29
|
-
vulnerable_versions?: string[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface Rule {
|
|
33
|
-
id: string;
|
|
34
|
-
title?: string;
|
|
35
|
-
vulnerability_id?: string;
|
|
36
|
-
package_cond?: PackageCond;
|
|
37
|
-
rule_v2: RuleCondition[];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** The app's installed package list, so package_cond can gate. */
|
|
41
|
-
export interface Manifest {
|
|
42
|
-
packages: Record<string, string>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface Verdict {
|
|
46
|
-
matched: boolean;
|
|
47
|
-
action: Action;
|
|
48
|
-
rule_id: string | null;
|
|
49
|
-
vulnerability_id: string | null;
|
|
50
|
-
package: string | null;
|
|
51
|
-
version: string | null;
|
|
52
|
-
explain: string[];
|
|
53
|
-
trace: unknown[];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export declare function evaluate(ctx: RequestContext, rules: Rule[], manifest?: Manifest): Verdict;
|
|
57
|
-
export declare function urldecode(value: string): string;
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
// @patchstack/protect — the runtime protection engine.
|
|
2
|
-
//
|
|
3
|
-
// One pure function: evaluate(ctx, rules, manifest) -> Verdict.
|
|
4
|
-
// No framework coupling — the framework adapter (the guard) builds `ctx` and acts on the
|
|
5
|
-
// Verdict. This is the offsite-frozen contract between Group 1 (engine) and Group 2 (surface).
|
|
6
|
-
//
|
|
7
|
-
// SCOPE OF THIS BUILD: implements the two demo-critical pieces from the prototype —
|
|
8
|
-
// - `inline_xss` (ps-node never implemented it; npm XSS attacks need it)
|
|
9
|
-
// - `package_cond` (the line between virtual patching and a dumb wall)
|
|
10
|
-
// plus `equals` / `contains` and the `urldecode` mutation. The full rule_v2 zoo
|
|
11
|
-
// (AND/OR, nested rules, whitelists, the ~15 other match types) lands when the
|
|
12
|
-
// ps-node RuleEngine is ported in — this file keeps the same signature so that swap
|
|
13
|
-
// is drop-in.
|
|
14
|
-
|
|
15
|
-
/** @type {{ALLOW:'ALLOW',LOG:'LOG',BLOCK:'BLOCK',REDIRECT:'REDIRECT'}} */
|
|
16
|
-
export const ACTIONS = { ALLOW: "ALLOW", LOG: "LOG", BLOCK: "BLOCK", REDIRECT: "REDIRECT" };
|
|
17
|
-
|
|
18
|
-
/** Iteratively URL-decode (catches %2527 -> %27 -> '). Safe on malformed input. */
|
|
19
|
-
export function urldecode(value) {
|
|
20
|
-
let out = String(value);
|
|
21
|
-
for (let i = 0; i < 5; i++) {
|
|
22
|
-
let next;
|
|
23
|
-
try {
|
|
24
|
-
next = decodeURIComponent(out.replace(/\+/g, " "));
|
|
25
|
-
} catch {
|
|
26
|
-
break;
|
|
27
|
-
}
|
|
28
|
-
if (next === out) break;
|
|
29
|
-
out = next;
|
|
30
|
-
}
|
|
31
|
-
return out;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const MUTATIONS = { urldecode };
|
|
35
|
-
|
|
36
|
-
function applyMutations(value, mutations) {
|
|
37
|
-
let v = String(value ?? "");
|
|
38
|
-
for (const m of mutations ?? []) {
|
|
39
|
-
if (MUTATIONS[m]) v = MUTATIONS[m](v);
|
|
40
|
-
}
|
|
41
|
-
return v;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Heuristic script-injection detector. Deliberately simple and readable for the demo;
|
|
45
|
-
// the ported engine replaces this with the audited ps-node inline_xss implementation.
|
|
46
|
-
const XSS_PATTERNS = [
|
|
47
|
-
/<\s*script\b/i,
|
|
48
|
-
/<\s*\/\s*script\s*>/i,
|
|
49
|
-
/\bon\w+\s*=/i, // onerror=, onload=, ...
|
|
50
|
-
/javascript\s*:/i,
|
|
51
|
-
/<\s*img\b[^>]*\bon\w+\s*=/i,
|
|
52
|
-
/<\s*svg\b[^>]*\bon\w+\s*=/i,
|
|
53
|
-
/\bdata\s*:\s*text\/html/i,
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
function looksLikeInlineXss(value) {
|
|
57
|
-
const v = String(value ?? "");
|
|
58
|
-
return XSS_PATTERNS.some((re) => re.test(v));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Resolve a rule parameter like "insert.title" or "body.content" against ctx.
|
|
62
|
-
// The guard normalizes framework specifics into ctx.body; parameters read from there.
|
|
63
|
-
function resolveParameter(ctx, parameter) {
|
|
64
|
-
const [, ...rest] = String(parameter).split(".");
|
|
65
|
-
const key = rest.length ? rest.join(".") : parameter;
|
|
66
|
-
const body = ctx?.body ?? {};
|
|
67
|
-
if (key in body) return body[key];
|
|
68
|
-
// shallow scan for nested payloads (e.g. { record: { title } })
|
|
69
|
-
for (const v of Object.values(body)) {
|
|
70
|
-
if (v && typeof v === "object" && key in v) return v[key];
|
|
71
|
-
}
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function matchCondition(ctx, cond) {
|
|
76
|
-
const params = Array.isArray(cond.parameter) ? cond.parameter : [cond.parameter];
|
|
77
|
-
for (const param of params) {
|
|
78
|
-
const raw = resolveParameter(ctx, param);
|
|
79
|
-
if (raw == null) continue;
|
|
80
|
-
const value = applyMutations(raw, cond.mutations);
|
|
81
|
-
const type = cond.match?.type;
|
|
82
|
-
let hit = false;
|
|
83
|
-
if (type === "inline_xss") hit = looksLikeInlineXss(value);
|
|
84
|
-
else if (type === "contains") hit = value.includes(cond.match.value);
|
|
85
|
-
else if (type === "equals") hit = value === cond.match.value;
|
|
86
|
-
if (hit) {
|
|
87
|
-
return { param, type, value };
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// package_cond: only fire if the app actually has the vulnerable package@version.
|
|
94
|
-
// If no manifest is supplied, fail-open on the gate (assume present) — detection already
|
|
95
|
-
// flagged it; the guard can tighten this once it passes the real manifest.
|
|
96
|
-
function packageCondSatisfied(pkgCond, manifest) {
|
|
97
|
-
if (!pkgCond) return true;
|
|
98
|
-
if (!manifest || !manifest.packages) return true;
|
|
99
|
-
const installed = manifest.packages[pkgCond.package];
|
|
100
|
-
if (installed == null) return false;
|
|
101
|
-
if (!pkgCond.vulnerable_versions) return true;
|
|
102
|
-
return pkgCond.vulnerable_versions.includes(installed);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Evaluate one request against the rule set.
|
|
107
|
-
* @param {import('./index.js').RequestContext} ctx
|
|
108
|
-
* @param {import('./index.js').Rule[]} rules
|
|
109
|
-
* @param {import('./index.js').Manifest} [manifest]
|
|
110
|
-
* @returns {import('./index.js').Verdict}
|
|
111
|
-
*/
|
|
112
|
-
export function evaluate(ctx, rules, manifest) {
|
|
113
|
-
const trace = [];
|
|
114
|
-
for (const rule of rules ?? []) {
|
|
115
|
-
if (!packageCondSatisfied(rule.package_cond, manifest)) {
|
|
116
|
-
trace.push({ rule_id: rule.id, skipped: "package_cond not satisfied" });
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
for (const cond of rule.rule_v2 ?? []) {
|
|
120
|
-
const m = matchCondition(ctx, cond);
|
|
121
|
-
if (m) {
|
|
122
|
-
return {
|
|
123
|
-
matched: true,
|
|
124
|
-
action: ACTIONS.BLOCK,
|
|
125
|
-
rule_id: rule.id,
|
|
126
|
-
vulnerability_id: rule.vulnerability_id ?? null,
|
|
127
|
-
package: rule.package_cond?.package ?? null,
|
|
128
|
-
version: manifest?.packages?.[rule.package_cond?.package] ?? null,
|
|
129
|
-
explain: [`${m.param} matched ${m.type}${cond.mutations?.length ? ` after ${cond.mutations.join("+")}` : ""}`],
|
|
130
|
-
trace,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
trace.push({ rule_id: rule.id, matched: false });
|
|
135
|
-
}
|
|
136
|
-
return { matched: false, action: ACTIONS.ALLOW, rule_id: null, vulnerability_id: null, package: null, version: null, explain: [], trace };
|
|
137
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const manifest: { packages: Record<string, string> };
|