From e0d29aeb325372c59469a853e6efa37cdaa75365 Mon Sep 17 00:00:00 2001 From: "johannes.breit" Date: Sun, 22 Mar 2026 21:17:38 +0100 Subject: [PATCH] fix(java/restclient): avoid IndexOutOfBoundsException for empty multipart list params (#23153) --- .../libraries/restclient/ApiClient.mustache | 14 +++++----- .../codegen/java/JavaClientCodegenTest.java | 26 +++++++++++++++++++ .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- .../org/openapitools/client/ApiClient.java | 14 +++++----- 13 files changed, 110 insertions(+), 84 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/restclient/ApiClient.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/restclient/ApiClient.mustache index febe974bcabb..e63ea452fadc 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/restclient/ApiClient.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/restclient/ApiClient.mustache @@ -846,14 +846,14 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java index 35bf0bdfe05c..a110e44dfdd1 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java @@ -4211,4 +4211,30 @@ public void testFeignHc5CustomTemplateDirIsPreserved() { "feign-hc5 must preserve a user-provided templateDir and not overwrite it with 'feign'"); } + @Test(description = "Regression test for multipart/form-data list handling in restclient ApiClient to avoid IndexOutOfBoundsException on empty lists") + public void testRestClientMultipartFormParamsGuardAgainstEmptyLists() { + final Path output = newTempFolder(); + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName(JAVA_GENERATOR) + .setLibrary(JavaClientCodegen.RESTCLIENT) + .setAdditionalProperties(Map.of(CodegenConstants.API_PACKAGE, "xyz.abcdef.api")) + .setInputSpec("src/test/resources/3_0/form-multipart-binary-array.yaml") + .setOutputDir(output.toString().replace("\\", "/")); + + List files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + + validateJavaSourceFiles(files); + + assertFileContains( + output.resolve("src/main/java/xyz/abcdef/ApiClient.java"), + "if (v instanceof java.util.ArrayList && !v.isEmpty()) {", + "first.getClass().isEnum()" + ); + + TestUtils.assertFileNotContains( + output.resolve("src/main/java/xyz/abcdef/ApiClient.java"), + "if (v instanceof java.util.ArrayList) {", + "o.getClass().getEnumConstants() != null" + ); + } } diff --git a/samples/client/echo_api/java/restclient/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/echo_api/java/restclient/src/main/java/org/openapitools/client/ApiClient.java index a36c156035dc..fcbb47a9b9c7 100644 --- a/samples/client/echo_api/java/restclient/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/echo_api/java/restclient/src/main/java/org/openapitools/client/ApiClient.java @@ -724,14 +724,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/others/java/restclient-enum-in-multipart/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/others/java/restclient-enum-in-multipart/src/main/java/org/openapitools/client/ApiClient.java index 8b7a9843f00a..f291a96095dd 100644 --- a/samples/client/others/java/restclient-enum-in-multipart/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/others/java/restclient-enum-in-multipart/src/main/java/org/openapitools/client/ApiClient.java @@ -723,14 +723,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/others/java/restclient-sealedInterface/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/others/java/restclient-sealedInterface/src/main/java/org/openapitools/client/ApiClient.java index 24aed5a26630..8003b67565db 100644 --- a/samples/client/others/java/restclient-sealedInterface/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/others/java/restclient-sealedInterface/src/main/java/org/openapitools/client/ApiClient.java @@ -722,14 +722,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/others/java/restclient-useAbstractionForFiles/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/others/java/restclient-useAbstractionForFiles/src/main/java/org/openapitools/client/ApiClient.java index f93712bb48f0..137964568ef3 100644 --- a/samples/client/others/java/restclient-useAbstractionForFiles/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/others/java/restclient-useAbstractionForFiles/src/main/java/org/openapitools/client/ApiClient.java @@ -722,14 +722,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/petstore/java/restclient-nullable-arrays/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/petstore/java/restclient-nullable-arrays/src/main/java/org/openapitools/client/ApiClient.java index d8ec26664f9c..947431f51668 100644 --- a/samples/client/petstore/java/restclient-nullable-arrays/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/petstore/java/restclient-nullable-arrays/src/main/java/org/openapitools/client/ApiClient.java @@ -722,14 +722,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/petstore/java/restclient-springBoot4-jackson2/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/petstore/java/restclient-springBoot4-jackson2/src/main/java/org/openapitools/client/ApiClient.java index 139169e96493..91a85cf0339b 100644 --- a/samples/client/petstore/java/restclient-springBoot4-jackson2/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/petstore/java/restclient-springBoot4-jackson2/src/main/java/org/openapitools/client/ApiClient.java @@ -748,14 +748,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/petstore/java/restclient-springBoot4-jackson3/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/petstore/java/restclient-springBoot4-jackson3/src/main/java/org/openapitools/client/ApiClient.java index 2de44b956c3b..0e4641bdf078 100644 --- a/samples/client/petstore/java/restclient-springBoot4-jackson3/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/petstore/java/restclient-springBoot4-jackson3/src/main/java/org/openapitools/client/ApiClient.java @@ -741,14 +741,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/petstore/java/restclient-swagger2/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/petstore/java/restclient-swagger2/src/main/java/org/openapitools/client/ApiClient.java index b44b8d99b9e9..66b79471aac2 100644 --- a/samples/client/petstore/java/restclient-swagger2/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/petstore/java/restclient-swagger2/src/main/java/org/openapitools/client/ApiClient.java @@ -795,14 +795,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/petstore/java/restclient-useSingleRequestParameter-static/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/petstore/java/restclient-useSingleRequestParameter-static/src/main/java/org/openapitools/client/ApiClient.java index b44b8d99b9e9..66b79471aac2 100644 --- a/samples/client/petstore/java/restclient-useSingleRequestParameter-static/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/petstore/java/restclient-useSingleRequestParameter-static/src/main/java/org/openapitools/client/ApiClient.java @@ -795,14 +795,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/petstore/java/restclient-useSingleRequestParameter/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/petstore/java/restclient-useSingleRequestParameter/src/main/java/org/openapitools/client/ApiClient.java index b44b8d99b9e9..66b79471aac2 100644 --- a/samples/client/petstore/java/restclient-useSingleRequestParameter/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/petstore/java/restclient-useSingleRequestParameter/src/main/java/org/openapitools/client/ApiClient.java @@ -795,14 +795,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType); diff --git a/samples/client/petstore/java/restclient/src/main/java/org/openapitools/client/ApiClient.java b/samples/client/petstore/java/restclient/src/main/java/org/openapitools/client/ApiClient.java index b44b8d99b9e9..66b79471aac2 100644 --- a/samples/client/petstore/java/restclient/src/main/java/org/openapitools/client/ApiClient.java +++ b/samples/client/petstore/java/restclient/src/main/java/org/openapitools/client/ApiClient.java @@ -795,14 +795,14 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { formParams.forEach( - (k, v) -> { - if (v instanceof java.util.ArrayList) { - Object o = v.get(0); - if (o != null && o.getClass().getEnumConstants() != null) { - v.set(0, o.toString()); - } + (k, v) -> { + if (v instanceof java.util.ArrayList && !v.isEmpty()) { + Object first = v.get(0); + if (first != null && first.getClass().isEnum()) { + v.set(0, first.toString()); } - }); + } + }); } var selectedBody = selectBody(body, formParams, contentType);