{"version":3,"sources":["../src/capture/urlSanitizer.ts"],"sourcesContent":["/**\n * URL sanitizer for captured network request URLs.\n *\n * Two layers of defense:\n *\n * 1) Param NAME match — anything whose name suggests a credential is\n *    redacted unconditionally (token, key, password, secret, auth, etc.).\n *\n * 2) Param VALUE shape match — even when the name is innocent (\"id\",\n *    \"redirect_uri\", \"context\"), if the VALUE looks like a JWT, a\n *    Bearer token, an mhosaic-feedback API key, or a long opaque hex/b64\n *    string we redact it. This catches the realistic case where a backend\n *    routes credentials through generic-named params or where a careless\n *    developer puts a token in a URL fragment for \"convenience.\"\n *\n * The path portion is preserved as-is — its purpose is operator-side\n * \"which page was this report filed from\", and stripping it would\n * destroy that signal. Hosts that route secrets through path segments\n * (uncommon) should mark those routes for the widget to skip via the\n * sanitizeUrl hook in createFeedback().\n */\n\nconst SENSITIVE_NAME = /token|key|password|secret|auth|session|sig|credential|bearer|cookie/i\n\nconst SENSITIVE_VALUE_PATTERNS: RegExp[] = [\n  // JWT: three base64url segments separated by dots, header begins with eyJ.\n  /^eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/,\n  // Mhosaic feedback keys (the project's own format).\n  /^(?:sk|pk)_proj_[A-Za-z0-9_-]{16,}$/,\n  // GitHub PATs (ghp/gho/ghu/ghs/ghr prefixes, 36+ chars).\n  /^gh[pousr]_[A-Za-z0-9]{30,}$/,\n  // Stripe live/test secret keys.\n  /^(?:sk|pk|rk|whsec)_(?:live|test)_[A-Za-z0-9]{16,}$/,\n  // AWS access key id.\n  /^AKIA[0-9A-Z]{12,}$/,\n  // Slack bot tokens.\n  /^xox[abprso]-[A-Za-z0-9-]{12,}$/,\n  // Generic high-entropy: hex 40+ chars (SHA-1, SHA-256) or long alnum 32+.\n  /^[a-f0-9]{40,}$/i,\n  // Long opaque base64-ish string (common for OAuth codes & session ids).\n  /^[A-Za-z0-9_-]{40,}$/,\n]\n\nfunction looksLikeCredential(value: string): boolean {\n  return SENSITIVE_VALUE_PATTERNS.some((re) => re.test(value))\n}\n\nexport function sanitizeUrl(url: string): string {\n  try {\n    // Accept absolute URLs or root-relative paths; reject everything else\n    const isAbsolute = /^https?:\\/\\//i.test(url)\n    const isRootRelative = url.startsWith('/')\n    if (!isAbsolute && !isRootRelative) return url\n\n    const base = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'\n    const u = new URL(url, base)\n    const clean = new URLSearchParams()\n    u.searchParams.forEach((value, name) => {\n      if (SENSITIVE_NAME.test(name) || looksLikeCredential(value)) {\n        clean.set(name, '[redacted]')\n      } else {\n        clean.set(name, value)\n      }\n    })\n    u.search = clean.toString()\n    // Hash fragments occasionally carry credentials (OAuth implicit flow):\n    // never log them.\n    u.hash = u.hash ? '#[redacted]' : ''\n    return u.toString()\n  } catch {\n    return url\n  }\n}\n"],"mappings":";AAsBA,IAAM,iBAAiB;AAEvB,IAAM,2BAAqC;AAAA;AAAA,EAEzC;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF;AAEA,SAAS,oBAAoB,OAAwB;AACnD,SAAO,yBAAyB,KAAK,CAAC,OAAO,GAAG,KAAK,KAAK,CAAC;AAC7D;AAEO,SAAS,YAAY,KAAqB;AAC/C,MAAI;AAEF,UAAM,aAAa,gBAAgB,KAAK,GAAG;AAC3C,UAAM,iBAAiB,IAAI,WAAW,GAAG;AACzC,QAAI,CAAC,cAAc,CAAC,eAAgB,QAAO;AAE3C,UAAM,OAAO,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS;AACtE,UAAM,IAAI,IAAI,IAAI,KAAK,IAAI;AAC3B,UAAM,QAAQ,IAAI,gBAAgB;AAClC,MAAE,aAAa,QAAQ,CAAC,OAAO,SAAS;AACtC,UAAI,eAAe,KAAK,IAAI,KAAK,oBAAoB,KAAK,GAAG;AAC3D,cAAM,IAAI,MAAM,YAAY;AAAA,MAC9B,OAAO;AACL,cAAM,IAAI,MAAM,KAAK;AAAA,MACvB;AAAA,IACF,CAAC;AACD,MAAE,SAAS,MAAM,SAAS;AAG1B,MAAE,OAAO,EAAE,OAAO,gBAAgB;AAClC,WAAO,EAAE,SAAS;AAAA,EACpB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}