{"record":{"id":"ee536f4456e73b5c","repo":"pnpm/pnpm","slug":"path-traversal","errorCode":"PATH_TRAVERSAL","errorMessage":"Refusing to extract path \"${targetPath}\" - absolute paths are not allowed","messagePattern":"Refusing to extract path \"(.+?)\" - absolute paths are not allowed","errorType":"exception","errorClass":"PnpmError","httpStatus":null,"severity":"critical","filePath":"pnpm11/fetching/binary-fetcher/src/index.ts","lineNumber":251,"sourceCode":"  if (!regex.global && !regex.sticky) {\n    return (input) => regex.test(input)\n  }\n  const safeFlags = regex.flags.replace(/[gy]/g, '')\n  const clone = new RegExp(regex.source, safeFlags)\n  return (input) => clone.test(input)\n}\n\n/**\n * Validates that a path does not escape the base directory via path traversal.\n *\n * @param basePath - The base directory that should contain the target\n * @param targetPath - The relative path to validate\n * @throws {PnpmError} When path traversal is detected\n */\nfunction validatePathSecurity (basePath: string, targetPath: string): void {\n  // Explicitly reject absolute paths - they should never be allowed as prefixes or entry names\n  if (path.isAbsolute(targetPath)) {\n    throw new PnpmError('PATH_TRAVERSAL',\n      `Refusing to extract path \"${targetPath}\" - absolute paths are not allowed`)\n  }\n  const normalizedTarget = path.resolve(basePath, targetPath)\n  if (!isSubdir(basePath, normalizedTarget) && normalizedTarget !== basePath) {\n    throw new PnpmError('PATH_TRAVERSAL',\n      `Refusing to extract path \"${targetPath}\" outside of target directory`)\n  }\n}\n","sourceCodeStart":233,"sourceCodeEnd":260,"githubUrl":"https://github.com/pnpm/pnpm/blob/5b11d3a15b9022a2109cb18ed96a5d652630371f/pnpm11/fetching/binary-fetcher/src/index.ts#L233-L260","documentation":"During extraction of a binary package archive, an entry name (or prefix) is an absolute path (path.isAbsolute(targetPath)), which would make extraction write to a fixed location outside the destination directory. validatePathSecurity rejects it before any file is written. Well-formed archives never contain absolute entry names, so this almost always indicates a malicious or badly packed archive.","triggerScenarios":"A zip/tarball dependency whose entries include paths like /etc/init.d/x or Windows drive-absolute paths, or zip entries stored with a leading slash, extracted by the binary fetcher.","commonSituations":"Malicious packages trying to plant files outside node_modules; archives packed with absolute paths by broken tooling; zip processing tools that rewrite entry names.","solutions":["Treat the dependency as untrusted: remove it, and report it if it came from a third party","If you control the artifact, repack it with relative entry names","As a mitigation, add an archiveFilters entry to exclude the offending paths if the rest of the artifact is legitimate"],"exampleFix":"# repacking with relative paths\n# before - packed with absolute names\ntar czf pkg.tgz /usr/share/pkg\n\n# after\ncd /usr/share && tar czf pkg.tgz pkg","handlingStrategy":"try-catch","validationCode":"import path from 'node:path'\n\n// scan entry names before extracting an archive you process yourself\nfunction hasAbsoluteEntries (entryNames: string[]): boolean {\n  return entryNames.some(name => path.isAbsolute(name))\n}","typeGuard":null,"tryCatchPattern":"catch code === 'PATH_TRAVERSAL'; do not retry — quarantine the package, audit its provenance and publisher, and remove it from the dependency graph","preventionTips":["Consume binary archives only from trusted publishers and pinned integrity values","Keep allowBuilds and archiveFilters policies tight so unvetted artifacts cannot execute or extract","If you publish archives yourself, pack with relative entry names and verify with a listing step"],"tags":["security","path-traversal","zip","extraction","binary"],"backgroundTag":null,"analyzedSha":"5b11d3a15b9022a2109cb18ed96a5d652630371f","analyzedAt":"2026-08-16T13:18:59.198Z","schemaVersion":2},"datasetVersion":"2026-08-16T18:17:16.020Z"}