cypress-io/cypress · error · Error
Refusing to extract entry outside of destination: ${entry.fi
Error message
Refusing to extract entry outside of destination: ${entry.fileName} What it means
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.
Source
Thrown at cli/lib/tasks/extract-with-yauzl.ts:95
zipFile.readEntry()
})
.catch(fail)
})
zipFile.readEntry()
})
})
}
const handleEntry = async (zipFile: any, entry: any, resolvedDest: string): Promise<void> => {
const fileDest = path.resolve(resolvedDest, entry.fileName)
// refuse anything that would write outside the install dir
if (
fileDest !== resolvedDest &&
!fileDest.startsWith(resolvedDest + path.sep)
) {
throw new Error(`Refusing to extract entry outside of destination: ${entry.fileName}`)
}
const unixMode = (entry.externalFileAttributes >>> 16) & 0xffff
// Some archivers mark directories by Unix mode bits instead of (or in
// addition to) a trailing slash; honor both so we don't extract a
// directory entry as a zero-byte file.
const isDir = /\/$/.test(entry.fileName) || (unixMode & S_IFMT) === S_IFDIR
const isSymlink = (unixMode & S_IFMT) === S_IFLNK
if (isDir) {
await fsp.mkdir(fileDest, { recursive: true })
return
}
await fsp.mkdir(path.dirname(fileDest), { recursive: true })
if (isSymlink) {View on GitHub (pinned to 0d85fdc912)
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.
Example fix
# before: CYPRESS_INSTALL_BINARY=./modified-cypress.zip triggers the guard # after: unset CYPRESS_INSTALL_BINARY rm -rf ~/.cache/Cypress cypress install
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-scan a zip before extracting to confirm every entry resolves inside dest:
import yauzl from 'yauzl'
import path from 'path'
async function assertSafeZip(zipPath: string, destDir: string) {
const dest = path.resolve(destDir)
await new Promise<void>((resolve, reject) => {
yauzl.open(zipPath, { lazyEntries: true }, (err, zf) => {
if (err) return reject(err)
zf.on('entry', (entry) => {
const resolved = path.resolve(dest, entry.fileName)
if (resolved !== dest && !resolved.startsWith(dest + path.sep)) {
return reject(new Error(`Unsafe entry: ${entry.fileName}`))
}
zf.readEntry()
})
zf.on('end', () => resolve())
zf.readEntry()
})
})
} Try / catch
try {
await extractWithYauzl(zipPath, destDir, () => {})
} catch (e) {
if (e instanceof Error && /Refusing to extract entry outside of destination/.test(e.message)) {
// artifact is untrusted — do not retry; re-download from the official source
}
throw e
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Refusing to extract symlink with target larger than ${MAX_SY
- Refusing to extract symlink pointing outside of destination:
- Corrupted download Expected downloaded file to have checksu
- Corrupted download Expected downloaded file to have checksu
- Corrupted download Expected downloaded file to have size: $
AI-assisted analysis of cypress-io/cypress@0d85fdc912 (2026-08-12).
Data as JSON: /api/errors/3bc33624fb69e14b.
Report an issue: GitHub.