{"version":3,"file":"bundle-scan.cjs","names":["DEFAULT_ENV_PREFIXES","isClientPermittedEnvVar","TraceMap"],"sources":["../src/bundle-scan.ts"],"sourcesContent":["import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping'\nimport type { SourceMapInput } from '@jridgewell/trace-mapping'\n\nimport { isClientPermittedEnvVar, DEFAULT_ENV_PREFIXES } from '#env-classification'\nimport type { BundleLeak } from '#types'\n\nconst MINIMUM_SECRET_LENGTH = 4\n\nconst UNRESOLVED_LEAK_ORIGIN = { originalFile: 'unknown', originalLine: 0, originalColumn: 0 } as const\n\n/**\n * Options accepted by {@link scanOutputChunk} for scanning a single compiled\n * output chunk for leaked server-only environment variable values.\n */\nexport type ScanOutputChunkOptions = {\n  /** The compiled source code of the output chunk to scan. */\n  chunkCode: string\n  /** The file name of the output chunk, used for reporting. */\n  chunkFileName: string\n  /** A map of environment variable name to its current string value. */\n  knownSecrets: Map<string, string>\n  /** The source map for the chunk, used to resolve original file and line. */\n  sourceMap: SourceMapInput | null\n}\n\n/**\n * Scans a compiled output chunk for the literal string values of known\n * server-only environment variables. For each match, attempts to resolve\n * the original source file and line number using the chunk's source map.\n *\n * The source map is parsed once per chunk, not once per matched secret.\n * Secrets shorter than {@link MINIMUM_SECRET_LENGTH} characters are skipped\n * to avoid false positives on common short strings.\n *\n * @param options - The chunk code, file name, known secret values, and optional source map.\n * @returns An array of confirmed bundle leaks found within the chunk.\n */\nexport function scanOutputChunk(options: ScanOutputChunkOptions): BundleLeak[] {\n  const { chunkCode, chunkFileName, knownSecrets, sourceMap } = options\n  const confirmedLeaks: BundleLeak[] = []\n\n  const traceMap = sourceMap ? tryBuildTraceMap(sourceMap) : null\n\n  for (const [envVarName, secretValue] of knownSecrets) {\n    if (!secretValue || secretValue.length < MINIMUM_SECRET_LENGTH) {\n      continue\n    }\n\n    const secretValueIndex = chunkCode.indexOf(secretValue)\n    if (secretValueIndex === -1) {\n      continue\n    }\n\n    confirmedLeaks.push({\n      chunkFileName,\n      envVarName,\n      ...resolveLeakOrigin(chunkCode, secretValueIndex, traceMap),\n    })\n  }\n\n  return confirmedLeaks\n}\n\n/**\n * Collects the current string values of all environment variables that are\n * considered server-only: those without a configured env prefix (`VITE_` by\n * default) and not listed in the explicit allowlist.\n *\n * @param allowClientAccess - Variable names explicitly permitted for client use.\n * @param envPrefixes - Prefixes Vite exposes to the client (resolved `envPrefix`).\n *   Defaults to {@link DEFAULT_ENV_PREFIXES} (`VITE_`).\n * @returns A Map of environment variable name to its current process value.\n */\nexport function collectServerEnvVarValues(\n  allowClientAccess: string[],\n  envPrefixes: readonly string[] = DEFAULT_ENV_PREFIXES\n): Map<string, string> {\n  const serverEnvVarValues = new Map<string, string>()\n  const allowSet = new Set(allowClientAccess)\n\n  for (const [envVarName, envVarValue] of Object.entries(process.env)) {\n    if (envVarValue === undefined || isClientPermittedEnvVar(envVarName, allowSet, envPrefixes)) {\n      continue\n    }\n\n    serverEnvVarValues.set(envVarName, envVarValue)\n  }\n\n  return serverEnvVarValues\n}\n\n/**\n * Attempts to construct a `TraceMap` from the given source map input.\n * Returns `null` if the source map is malformed or cannot be parsed.\n *\n * @param sourceMap - The source map to parse.\n * @returns A parsed `TraceMap`, or `null` on failure.\n */\nfunction tryBuildTraceMap(sourceMap: SourceMapInput): TraceMap | null {\n  try {\n    return new TraceMap(sourceMap)\n  } catch {\n    return null\n  }\n}\n\n/**\n * Resolves the character offset of a match in a compiled chunk to the\n * original source file, line, and column using a pre-parsed `TraceMap`.\n * Returns fallback values when the trace map is absent or resolution fails.\n *\n * @param chunkCode - The full compiled chunk source code.\n * @param matchCharacterOffset - The zero-based character index of the match.\n * @param traceMap - A pre-parsed `TraceMap` for the chunk, or `null`.\n * @returns The resolved original file path, one-based line number, and one-based column number.\n */\nfunction resolveLeakOrigin(\n  chunkCode: string,\n  matchCharacterOffset: number,\n  traceMap: TraceMap | null\n): { originalFile: string; originalLine: number; originalColumn: number } {\n  if (!traceMap) {\n    return UNRESOLVED_LEAK_ORIGIN\n  }\n\n  const precedingText = chunkCode.slice(0, matchCharacterOffset)\n  const precedingLines = precedingText.split('\\n')\n\n  try {\n    const resolvedPosition = originalPositionFor(traceMap, {\n      line: precedingLines.length,\n      column: precedingLines[precedingLines.length - 1].length,\n    })\n\n    return {\n      originalFile: resolvedPosition.source ?? 'unknown',\n      originalLine: resolvedPosition.line ?? 0,\n      originalColumn: resolvedPosition.column ?? 0,\n    }\n  } catch {\n    return UNRESOLVED_LEAK_ORIGIN\n  }\n}\n"],"mappings":";;;;AAMA,MAAM,wBAAwB;AAE9B,MAAM,yBAAyB;CAAE,cAAc;CAAW,cAAc;CAAG,gBAAgB;CAAG;;;;;;;;;;;;;AA6B9F,SAAgB,gBAAgB,SAA+C;CAC7E,MAAM,EAAE,WAAW,eAAe,cAAc,cAAc;CAC9D,MAAM,iBAA+B,EAAE;CAEvC,MAAM,WAAW,YAAY,iBAAiB,UAAU,GAAG;AAE3D,MAAK,MAAM,CAAC,YAAY,gBAAgB,cAAc;AACpD,MAAI,CAAC,eAAe,YAAY,SAAS,sBACvC;EAGF,MAAM,mBAAmB,UAAU,QAAQ,YAAY;AACvD,MAAI,qBAAqB,GACvB;AAGF,iBAAe,KAAK;GAClB;GACA;GACA,GAAG,kBAAkB,WAAW,kBAAkB,SAAS;GAC5D,CAAC;;AAGJ,QAAO;;;;;;;;;;;;AAaT,SAAgB,0BACd,mBACA,cAAiCA,2BAAAA,sBACZ;CACrB,MAAM,qCAAqB,IAAI,KAAqB;CACpD,MAAM,WAAW,IAAI,IAAI,kBAAkB;AAE3C,MAAK,MAAM,CAAC,YAAY,gBAAgB,OAAO,QAAQ,QAAQ,IAAI,EAAE;AACnE,MAAI,gBAAgB,KAAA,KAAaC,2BAAAA,wBAAwB,YAAY,UAAU,YAAY,CACzF;AAGF,qBAAmB,IAAI,YAAY,YAAY;;AAGjD,QAAO;;;;;;;;;AAUT,SAAS,iBAAiB,WAA4C;AACpE,KAAI;AACF,SAAO,IAAIC,0BAAAA,SAAS,UAAU;SACxB;AACN,SAAO;;;;;;;;;;;;;AAcX,SAAS,kBACP,WACA,sBACA,UACwE;AACxE,KAAI,CAAC,SACH,QAAO;CAIT,MAAM,iBADgB,UAAU,MAAM,GAAG,qBACL,CAAC,MAAM,KAAK;AAEhD,KAAI;EACF,MAAM,oBAAA,GAAA,0BAAA,qBAAuC,UAAU;GACrD,MAAM,eAAe;GACrB,QAAQ,eAAe,eAAe,SAAS,GAAG;GACnD,CAAC;AAEF,SAAO;GACL,cAAc,iBAAiB,UAAU;GACzC,cAAc,iBAAiB,QAAQ;GACvC,gBAAgB,iBAAiB,UAAU;GAC5C;SACK;AACN,SAAO"}