{"record":{"id":"507216dab1464bc7","repo":"microsoft/playwright","slug":"har-zip-entry-entry-escapes-output-directory","errorCode":null,"errorMessage":"HAR zip entry '${entry}' escapes output directory","messagePattern":"HAR zip entry '(.+?)' escapes output directory","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/localUtils.ts","lineNumber":205,"sourceCode":"}\n\nexport async function harUnzip(progress: Progress, params: channels.LocalUtilsHarUnzipParams): Promise<void> {\n  const resourcesDir = params.resourcesDir ?? path.dirname(params.zipFile);\n  const zipFile = new ZipFile(params.zipFile);\n  let resourcesDirCreated = false;\n  try {\n    for (const entry of await progress.race(zipFile.entries())) {\n      const buffer = await progress.race(zipFile.read(entry));\n      if (entry === 'har.har') {\n        await progress.race(fs.promises.writeFile(params.harFile, buffer));\n      } else {\n        if (!resourcesDirCreated) {\n          await progress.race(fs.promises.mkdir(resourcesDir, { recursive: true }));\n          resourcesDirCreated = true;\n        }\n        const outPath = resolveWithinRoot(resourcesDir, entry);\n        if (!outPath)\n          throw new Error(`HAR zip entry '${entry}' escapes output directory`);\n        await progress.race(fs.promises.writeFile(outPath, buffer));\n      }\n    }\n    await progress.race(fs.promises.unlink(params.zipFile));\n  } finally {\n    zipFile.close();\n  }\n}\n\nexport async function tracingStarted(progress: Progress, stackSessions: Map<string, StackSession>, params: channels.LocalUtilsTracingStartedParams): Promise<channels.LocalUtilsTracingStartedResult> {\n  let tmpDir = undefined;\n  if (!params.tracesDir)\n    tmpDir = await progress.race(fs.promises.mkdtemp(path.join(os.tmpdir(), 'playwright-tracing-')));\n  const traceStacksFile = path.join(params.tracesDir || tmpDir!, params.traceName + '.stacks');\n  // Ensure the directory exists before addStackToTracingNoReply races ahead of\n  // the tracing recorder's own (separately queued) mkdir.\n  await progress.race(fs.promises.mkdir(path.dirname(traceStacksFile), { recursive: true }));\n  stackSessions.set(traceStacksFile, { callStacks: [], file: traceStacksFile, writer: Promise.resolve(), tmpDir, live: params.live });","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/localUtils.ts#L187-L223","documentation":"Thrown by harUnzip() in localUtils when a zip entry name, after resolution via resolveWithinRoot(), falls outside the designated resources directory. This is a zip-slip / path-traversal security guard preventing malicious zip entries with names like '../../etc/passwd' from writing files outside the intended output directory.","triggerScenarios":"Calling harUnzip (triggered internally when Playwright processes a zipped HAR file) where the zip archive contains entries with path traversal sequences in their names. The resolveWithinRoot function rejects any entry that would resolve outside the resourcesDir.","commonSituations":"Zipped HAR file was produced by a faulty tool or manually constructed with unsafe entry names. HAR zip downloaded from an untrusted source. Zip file created on Windows with absolute paths or backslash-based traversal. Race condition or corruption in zip creation producing malformed entry names.","solutions":["Re-create the HAR zip using Playwright's built-in HAR recording to ensure clean entry names.","Inspect the zip file's entry list (unzip -l) and fix any entries containing '..' or absolute paths.","Use a non-zipped HAR format instead, or ensure all zip entries are simple relative filenames."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Validate zip entries before extraction\nconst zip = new (require('yauzl-with-promise'))(zipPath); // or similar\nfor (const entry of await zip.entries()) {\n  if (entry.includes('..') || path.isAbsolute(entry))\n    throw new Error(`Unsafe zip entry: ${entry}`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use Playwright's built-in HAR recording to produce safe zip files.","Inspect zip entry names with unzip -l before processing.","Avoid processing HAR zips from untrusted sources."],"tags":["har","zip","path-traversal","security","harunzip","localutils"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}