{"record":{"id":"3bc33624fb69e14b","repo":"cypress-io/cypress","slug":"refusing-to-extract-entry-outside-of-destination","errorCode":null,"errorMessage":"Refusing to extract entry outside of destination: ${entry.fileName}","messagePattern":"Refusing to extract entry outside of destination: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/lib/tasks/extract-with-yauzl.ts","lineNumber":95,"sourceCode":"          zipFile.readEntry()\n        })\n        .catch(fail)\n      })\n\n      zipFile.readEntry()\n    })\n  })\n}\n\nconst handleEntry = async (zipFile: any, entry: any, resolvedDest: string): Promise<void> => {\n  const fileDest = path.resolve(resolvedDest, entry.fileName)\n\n  // refuse anything that would write outside the install dir\n  if (\n    fileDest !== resolvedDest &&\n    !fileDest.startsWith(resolvedDest + path.sep)\n  ) {\n    throw new Error(`Refusing to extract entry outside of destination: ${entry.fileName}`)\n  }\n\n  const unixMode = (entry.externalFileAttributes >>> 16) & 0xffff\n  // Some archivers mark directories by Unix mode bits instead of (or in\n  // addition to) a trailing slash; honor both so we don't extract a\n  // directory entry as a zero-byte file.\n  const isDir = /\\/$/.test(entry.fileName) || (unixMode & S_IFMT) === S_IFDIR\n  const isSymlink = (unixMode & S_IFMT) === S_IFLNK\n\n  if (isDir) {\n    await fsp.mkdir(fileDest, { recursive: true })\n\n    return\n  }\n\n  await fsp.mkdir(path.dirname(fileDest), { recursive: true })\n\n  if (isSymlink) {","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/cypress-io/cypress/blob/0d85fdc91230885bca0a91df278312800a48b727/cli/lib/tasks/extract-with-yauzl.ts#L77-L113","documentation":"Raised by handleEntry() during zip extraction (extract-with-yauzl) when an archive entry's fileName resolves to a path outside the destination directory — the classic 'Zip Slip' traversal (e.g. an entry named ../../etc/passwd). This is a deliberate security guard: the installer refuses to write any file that would escape the install root, rather than refuse the whole archive.","triggerScenarios":"Extracting a Cypress binary zip (cypress.zip) that contains an entry whose path, joined to resolvedDest and normalized, does not share the resolvedDest prefix. In practice this means the published artifact is malformed/tampered, or a custom CYPRESS_INSTALL_BINARY points at a crafted zip.","commonSituations":"Pointing CYPRESS_INSTALL_BINARY at an untrusted or hand-modified zip; a mirror serving a tampered artifact; a corrupted download that produced garbage entry names; an attacker-supplied test fixture fed to the extractor.","solutions":["Do not trust the failing artifact: re-download the official cypress.zip from the default source after clearing the cache.","Unset CYPRESS_INSTALL_BINARY and CYPRESS_DOWNLOAD_MIRROR so the installer uses the official CDN.","If you control the zip, regenerate it with relative entry paths that stay under the extraction root (no leading slash, no .. segments).","Scan the archive with `unzip -l` and inspect any entry whose path contains `..` or an absolute prefix."],"exampleFix":"# before: CYPRESS_INSTALL_BINARY=./modified-cypress.zip triggers the guard\n# after:\nunset CYPRESS_INSTALL_BINARY\nrm -rf ~/.cache/Cypress\ncypress install","handlingStrategy":"try-catch","validationCode":"// Pre-scan a zip before extracting to confirm every entry resolves inside dest:\nimport yauzl from 'yauzl'\nimport path from 'path'\n\nasync function assertSafeZip(zipPath: string, destDir: string) {\n  const dest = path.resolve(destDir)\n  await new Promise<void>((resolve, reject) => {\n    yauzl.open(zipPath, { lazyEntries: true }, (err, zf) => {\n      if (err) return reject(err)\n      zf.on('entry', (entry) => {\n        const resolved = path.resolve(dest, entry.fileName)\n        if (resolved !== dest && !resolved.startsWith(dest + path.sep)) {\n          return reject(new Error(`Unsafe entry: ${entry.fileName}`))\n        }\n        zf.readEntry()\n      })\n      zf.on('end', () => resolve())\n      zf.readEntry()\n    })\n  })\n}","typeGuard":null,"tryCatchPattern":"try {\n  await extractWithYauzl(zipPath, destDir, () => {})\n} catch (e) {\n  if (e instanceof Error && /Refusing to extract entry outside of destination/.test(e.message)) {\n    // artifact is untrusted — do not retry; re-download from the official source\n  }\n  throw e\n}","preventionTips":["Only feed the official cypress.zip to the installer.","Never point CYPRESS_INSTALL_BINARY at an untrusted archive.","Validate downloaded zips with the published checksum before extracting."],"tags":["install","extract","security","zip-slip"],"backgroundTag":null,"analyzedSha":"0d85fdc91230885bca0a91df278312800a48b727","analyzedAt":"2026-08-12T16:24:28.056Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}