apache/pulsar · error · IOException
Invalid zip file. Aborting unpacking.
Error message
Invalid zip file. Aborting unpacking.
What it means
unpack() guards against Zip Slip: every zip entry path is resolved and normalized, and if the resulting path escapes the NAR's working directory the unpack is aborted with this IOException. This protects the classloader from a malicious NAR whose entries contain '../' or absolute paths that would write outside the extraction directory.
Source
Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/nar/NarUnpacker.java:144
/**
* Unpacks the NAR to the specified directory.
*
* @param workingDirectory
* the root directory to which the NAR should be unpacked.
* @throws IOException
* if the NAR could not be unpacked.
*/
private static void unpack(final File nar, final File workingDirectory) throws IOException {
Path workingDirectoryPath = workingDirectory.toPath().normalize();
try (ZipFile zipFile = new ZipFile(nar)) {
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry zipEntry = zipEntries.nextElement();
String name = zipEntry.getName();
Path targetFilePath = workingDirectoryPath.resolve(name).normalize();
if (!targetFilePath.startsWith(workingDirectoryPath)) {
log.error().attr("entry", name).log("Invalid zip file with entry");
throw new IOException("Invalid zip file. Aborting unpacking.");
}
File f = targetFilePath.toFile();
if (zipEntry.isDirectory()) {
FileUtils.ensureDirectoryExistAndCanReadAndWrite(f);
} else {
// The directory entry might appear after the file entry
FileUtils.ensureDirectoryExistAndCanReadAndWrite(f.getParentFile());
makeFile(zipFile.getInputStream(zipEntry), f);
}
}
}
}
/**
* Creates the specified file, whose contents will come from the <tt>InputStream</tt>.
*
* @param inputStream
* the contents of the file to create.View on GitHub (pinned to 820761864e)
Solutions
- Inspect the offending NAR (the log names the bad entry) and remove/replace it with a NAR built from a trusted source.
- Rebuild the NAR with correct packaging (jar/zip entries must be relative and stay within the archive root).
- Verify checksum/signature of third-party NARs before placing them in the functions/offloaders directory.
- If you own the zip-creation code, ensure entry names never contain '..' segments or leading '/' (e.g. don't pass absolute File paths to ZipEntry).
Example fix
// before (malformed packaging)
ZipEntry entry = new ZipEntry(new File("/etc/passwd").getAbsolutePath());
// after
ZipEntry entry = new ZipEntry("META-INF/manifest.xml"); // relative, stays inside archive root Defensive patterns
Strategy: validation
Validate before calling
static boolean isZipSafe(File nar) throws IOException {
Path root = nar.getAbsoluteFile().getParentFile().toPath().normalize();
try (ZipFile zf = new ZipFile(nar)) {
Enumeration<? extends ZipEntry> es = zf.entries();
while (es.hasMoreElements()) {
Path resolved = root.resolve(es.nextElement().getName()).normalize();
if (!resolved.startsWith(root)) return false;
}
}
return true;
} Type guard
static boolean isSafeEntryName(String name) {
return name != null && !name.startsWith("/")
&& !name.contains("..");
} Try / catch
try {
NarUnpacker.unpackNar(nar, dir);
} catch (IOException e) {
if ("Invalid zip file. Aborting unpacking.".equals(e.getMessage())) {
log.error("Rejected NAR with zip-slip entry; verify source/signature", e);
}
throw e;
} Prevention
- Only load NARs from trusted, checksum-verified sources.
- Verify NAR signatures before placing them in the functions/offloaders directory.
- Build NARs with standard jar/zip tooling using relative entry names.
- Treat this exception as a security event and quarantine the offending archive.
When it happens
Trigger: Loading a NAR via NarUnpacker.unpackNar(...) where the archive contains an entry whose name resolves outside the target directory, e.g. '../evil.so' or an absolute entry path produced by a tampered or incorrectly built zip/jar.
Common situations: Using a NAR downloaded from an untrusted source or built by a buggy packaging script; a corrupted/truncated download whose central directory was patched; supply-chain attack attempts.
Related errors
- Invalid broker configuration. Authentication must be enabled
- Unauthorized to validateBothSuperuserAndClusterOperation for
- Unauthorized to validateBothSuperuserAndClusterPolicyOperati
- Unauthorized to validateBothTenantOperationAndSuperUser for
- Protocol handler `${name}` does NOT provide a protocol handl
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/a788d62725e99ee2.
Report an issue: GitHub.