VYPR
High severityNVD Advisory· Published Apr 10, 2026· Updated Apr 13, 2026

CVE-2026-40180

CVE-2026-40180

Description

Quarkus OpenAPI Generator is Quarkus' extensions for generation of Rest Clients and server stubs generation. Prior to 2.16.0 and 2.15.0-lts, the unzip() method in ApicurioCodegenWrapper.java extracts ZIP entries without validating that the resolved file path stays within the intended output directory. At line 101, the destination is constructed as new File(toOutputDir, entry.getName()) and the content is written immediately. A malicious ZIP archive containing entries with path traversal sequences (e.g., ../../malicious.java) would write files outside the target directory. This vulnerability is fixed in 2.16.0 and 2.15.0-lts.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
io.quarkiverse.openapi.generator:quarkus-openapi-generatorMaven
< 2.16.02.16.0

Affected products

1

Patches

2
e2a9c629a3df

Solve Zip Slip Path Traversal in quarkus-openapi-generator ApicurioCodegenWrapper class (#1527)

2 files changed · +87 1
  • server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/apicurio/ApicurioCodegenWrapper.java+6 1 modified
    @@ -93,12 +93,17 @@ public void generate(Path openApiResource) throws CodeGenException {
         }
     
         private void unzip(File fromZipFile, File toOutputDir) throws IOException, CodeGenException {
    +        Path outputDirPath = toOutputDir.toPath().toAbsolutePath().normalize();
             try (java.util.zip.ZipFile zipFile = new ZipFile(fromZipFile)) {
                 Enumeration<? extends ZipEntry> entries = zipFile.entries();
                 while (entries.hasMoreElements()) {
                     ZipEntry entry = entries.nextElement();
    +                Path entryPath = outputDirPath.resolve(entry.getName()).toAbsolutePath().normalize();
    +                if (!entryPath.startsWith(outputDirPath)) {
    +                    throw new IOException("Invalid ZIP entry: " + entry.getName());
    +                }
    +                File entryDestination = entryPath.toFile();
                     assertGenerationSucceeded(zipFile, entry);
    -                File entryDestination = new File(toOutputDir, entry.getName());
                     if (entry.isDirectory()) {
                         entryDestination.mkdirs();
                     } else {
    
  • server/deployment/src/test/java/io/quarkiverse/openapi/server/generator/deployment/codegen/apicurio/ApicurioCodegenWrapperTest.java+81 0 added
    @@ -0,0 +1,81 @@
    +package io.quarkiverse.openapi.server.generator.deployment.codegen.apicurio;
    +
    +import static org.junit.jupiter.api.Assertions.assertEquals;
    +import static org.junit.jupiter.api.Assertions.assertThrows;
    +import static org.junit.jupiter.api.Assertions.assertTrue;
    +
    +import java.io.File;
    +import java.io.IOException;
    +import java.lang.reflect.InvocationTargetException;
    +import java.lang.reflect.Method;
    +import java.nio.file.Files;
    +import java.nio.file.Path;
    +import java.util.zip.ZipEntry;
    +import java.util.zip.ZipOutputStream;
    +
    +import org.eclipse.microprofile.config.Config;
    +import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
    +import org.junit.jupiter.api.Test;
    +import org.junit.jupiter.api.io.TempDir;
    +
    +class ApicurioCodegenWrapperTest {
    +
    +    @TempDir
    +    Path tempDir;
    +
    +    @Test
    +    void shouldExtractRegularEntryInsideOutputDir() throws Exception {
    +        Path zipPath = tempDir.resolve("safe.zip");
    +        createZip(zipPath, "nested/proof.txt", "ok");
    +
    +        Path outputDir = tempDir.resolve("out");
    +        Files.createDirectories(outputDir);
    +
    +        invokeUnzip(zipPath.toFile(), outputDir.toFile());
    +
    +        Path extractedFile = outputDir.resolve("nested/proof.txt");
    +        assertTrue(Files.exists(extractedFile));
    +        assertEquals("ok", Files.readString(extractedFile));
    +    }
    +
    +    @Test
    +    void shouldRejectPathTraversalEntry() throws Exception {
    +        Path zipPath = tempDir.resolve("traversal.zip");
    +        createZip(zipPath, "../../proof.txt", "pwned");
    +
    +        Path outputDir = tempDir.resolve("out");
    +        Files.createDirectories(outputDir);
    +
    +        IOException error = assertThrows(IOException.class, () -> invokeUnzip(zipPath.toFile(), outputDir.toFile()));
    +        assertTrue(error.getMessage().contains("Invalid ZIP entry"));
    +    }
    +
    +    private static void createZip(Path zipPath, String entryName, String content) throws IOException {
    +        try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zipPath))) {
    +            zipOutputStream.putNextEntry(new ZipEntry(entryName));
    +            zipOutputStream.write(content.getBytes());
    +            zipOutputStream.closeEntry();
    +        }
    +    }
    +
    +    private static void invokeUnzip(File zipFile, File outputDir) throws Exception {
    +        Method unzipMethod = ApicurioCodegenWrapper.class.getDeclaredMethod("unzip", File.class, File.class);
    +        unzipMethod.setAccessible(true);
    +
    +        Config config = ConfigProviderResolver.instance().getBuilder().build();
    +        ApicurioCodegenWrapper wrapper = new ApicurioCodegenWrapper(config, outputDir);
    +
    +        try {
    +            unzipMethod.invoke(wrapper, zipFile, outputDir);
    +        } catch (InvocationTargetException e) {
    +            Throwable cause = e.getCause();
    +            if (cause instanceof IOException ioException) {
    +                throw ioException;
    +            }
    +            if (cause instanceof RuntimeException runtimeException) {
    +                throw runtimeException;
    +            }
    +            throw new RuntimeException(cause);
    +        }
    +    }
    +}
    
08b406414ff3

Solve Zip Slip Path Traversal in quarkus-openapi-generator ApicurioCodegenWrapper class (#1524)

2 files changed · +87 1
  • server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/apicurio/ApicurioCodegenWrapper.java+6 1 modified
    @@ -93,12 +93,17 @@ public void generate(Path openApiResource) throws CodeGenException {
         }
     
         private void unzip(File fromZipFile, File toOutputDir) throws IOException, CodeGenException {
    +        Path outputDirPath = toOutputDir.toPath().toAbsolutePath().normalize();
             try (java.util.zip.ZipFile zipFile = new ZipFile(fromZipFile)) {
                 Enumeration<? extends ZipEntry> entries = zipFile.entries();
                 while (entries.hasMoreElements()) {
                     ZipEntry entry = entries.nextElement();
    +                Path entryPath = outputDirPath.resolve(entry.getName()).toAbsolutePath().normalize();
    +                if (!entryPath.startsWith(outputDirPath)) {
    +                    throw new IOException("Invalid ZIP entry: " + entry.getName());
    +                }
    +                File entryDestination = entryPath.toFile();
                     assertGenerationSucceeded(zipFile, entry);
    -                File entryDestination = new File(toOutputDir, entry.getName());
                     if (entry.isDirectory()) {
                         entryDestination.mkdirs();
                     } else {
    
  • server/deployment/src/test/java/io/quarkiverse/openapi/server/generator/deployment/codegen/apicurio/ApicurioCodegenWrapperTest.java+81 0 added
    @@ -0,0 +1,81 @@
    +package io.quarkiverse.openapi.server.generator.deployment.codegen.apicurio;
    +
    +import static org.junit.jupiter.api.Assertions.assertEquals;
    +import static org.junit.jupiter.api.Assertions.assertThrows;
    +import static org.junit.jupiter.api.Assertions.assertTrue;
    +
    +import java.io.File;
    +import java.io.IOException;
    +import java.lang.reflect.InvocationTargetException;
    +import java.lang.reflect.Method;
    +import java.nio.file.Files;
    +import java.nio.file.Path;
    +import java.util.zip.ZipEntry;
    +import java.util.zip.ZipOutputStream;
    +
    +import org.eclipse.microprofile.config.Config;
    +import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
    +import org.junit.jupiter.api.Test;
    +import org.junit.jupiter.api.io.TempDir;
    +
    +class ApicurioCodegenWrapperTest {
    +
    +    @TempDir
    +    Path tempDir;
    +
    +    @Test
    +    void shouldExtractRegularEntryInsideOutputDir() throws Exception {
    +        Path zipPath = tempDir.resolve("safe.zip");
    +        createZip(zipPath, "nested/proof.txt", "ok");
    +
    +        Path outputDir = tempDir.resolve("out");
    +        Files.createDirectories(outputDir);
    +
    +        invokeUnzip(zipPath.toFile(), outputDir.toFile());
    +
    +        Path extractedFile = outputDir.resolve("nested/proof.txt");
    +        assertTrue(Files.exists(extractedFile));
    +        assertEquals("ok", Files.readString(extractedFile));
    +    }
    +
    +    @Test
    +    void shouldRejectPathTraversalEntry() throws Exception {
    +        Path zipPath = tempDir.resolve("traversal.zip");
    +        createZip(zipPath, "../../proof.txt", "pwned");
    +
    +        Path outputDir = tempDir.resolve("out");
    +        Files.createDirectories(outputDir);
    +
    +        IOException error = assertThrows(IOException.class, () -> invokeUnzip(zipPath.toFile(), outputDir.toFile()));
    +        assertTrue(error.getMessage().contains("Invalid ZIP entry"));
    +    }
    +
    +    private static void createZip(Path zipPath, String entryName, String content) throws IOException {
    +        try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zipPath))) {
    +            zipOutputStream.putNextEntry(new ZipEntry(entryName));
    +            zipOutputStream.write(content.getBytes());
    +            zipOutputStream.closeEntry();
    +        }
    +    }
    +
    +    private static void invokeUnzip(File zipFile, File outputDir) throws Exception {
    +        Method unzipMethod = ApicurioCodegenWrapper.class.getDeclaredMethod("unzip", File.class, File.class);
    +        unzipMethod.setAccessible(true);
    +
    +        Config config = ConfigProviderResolver.instance().getBuilder().build();
    +        ApicurioCodegenWrapper wrapper = new ApicurioCodegenWrapper(config, outputDir);
    +
    +        try {
    +            unzipMethod.invoke(wrapper, zipFile, outputDir);
    +        } catch (InvocationTargetException e) {
    +            Throwable cause = e.getCause();
    +            if (cause instanceof IOException ioException) {
    +                throw ioException;
    +            }
    +            if (cause instanceof RuntimeException runtimeException) {
    +                throw runtimeException;
    +            }
    +            throw new RuntimeException(cause);
    +        }
    +    }
    +}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.