From 9ef5f10f9ef3af99cb19cd2b1d9f6d3dc5713d56 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Wed, 26 Aug 2026 13:20:02 -0600 Subject: [PATCH 1/2] [Java] add generateInsecureTlsHook option for jersey2/jersey3 The jersey2 and jersey3 ApiClient templates always emit disableCertificateValidation(), which installs an X509TrustManager with empty checkClientTrusted/checkServerTrusted bodies. CodeQL reports it as java/insecure-trustmanager at high severity, which fails the code scanning check on any PR touching the generated client. The method is a protected opt-in hook that nothing in the generated client calls, but analysers flag code as written rather than as reached, so projects carry a high-severity alert for a method they never use. There is currently no way to opt out: no option gates the block, suppression comments do not survive regeneration, and .openapi-generator-ignore cannot skip ApiClient because it is the class you configure. Add a generateInsecureTlsHook option gating the method, the javadoc that points at it, and the seven imports that become unused without it. It defaults to true, so generated output is unchanged and existing subclasses that call the hook keep compiling. Regenerating every java* sample produces no diff. Scoped to jersey2 and jersey3. okhttp-gson contains a similar trust-all block, but there it sits inside applySslSettings() behind the runtime verifyingSsl field, which has a public setVerifyingSsl setter. That is a live feature rather than a dead hook, so removing it would be a breaking change and is left alone. --- docs/generators/java-microprofile.md | 1 + docs/generators/java.md | 1 + .../codegen/languages/JavaClientCodegen.java | 11 +++++ .../Java/libraries/jersey2/ApiClient.mustache | 6 +++ .../Java/libraries/jersey3/ApiClient.mustache | 6 +++ .../codegen/java/JavaClientCodegenTest.java | 43 +++++++++++++++++++ 6 files changed, 68 insertions(+) diff --git a/docs/generators/java-microprofile.md b/docs/generators/java-microprofile.md index 2dfaf5e0bcf4..aaffe499e6f0 100644 --- a/docs/generators/java-microprofile.md +++ b/docs/generators/java-microprofile.md @@ -56,6 +56,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |generateBuilders|Whether to generate builders for models| |false| |generateClientAsBean|For resttemplate, restclient and webclient, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).| |false| |generateConstructorWithAllArgs|whether to generate a constructor for all arguments| |false| +|generateInsecureTlsHook|Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.| |false| |gradleProperties|Append additional Gradle properties to the gradle.properties file| |null| |groupId|groupId in generated pom.xml| |org.openapitools| |hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false| diff --git a/docs/generators/java.md b/docs/generators/java.md index 6740780102b9..b4110f29ac4b 100644 --- a/docs/generators/java.md +++ b/docs/generators/java.md @@ -56,6 +56,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |generateBuilders|Whether to generate builders for models| |false| |generateClientAsBean|For resttemplate, restclient and webclient, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).| |false| |generateConstructorWithAllArgs|whether to generate a constructor for all arguments| |false| +|generateInsecureTlsHook|Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.| |false| |gradleProperties|Append additional Gradle properties to the gradle.properties file| |null| |groupId|groupId in generated pom.xml| |org.openapitools| |hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index 6f6cb0af8471..ef30c09f5105 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -90,6 +90,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen public static final String DYNAMIC_OPERATIONS = "dynamicOperations"; public static final String SUPPORT_STREAMING = "supportStreaming"; public static final String SUPPORT_URL_QUERY = "supportUrlQuery"; + public static final String GENERATE_INSECURE_TLS_HOOK = "generateInsecureTlsHook"; public static final String GRADLE_PROPERTIES = "gradleProperties"; public static final String ERROR_OBJECT_TYPE = "errorObjectType"; @@ -283,6 +284,7 @@ public JavaClientCodegen() { cliOptions.add(CliOption.newBoolean(WEBCLIENT_BLOCKING_OPERATIONS, "Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync", this.webclientBlockingOperations)); cliOptions.add(CliOption.newBoolean(GENERATE_CLIENT_AS_BEAN, "For resttemplate, restclient and webclient, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).", this.generateClientAsBean)); cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries.")); + cliOptions.add(CliOption.newBoolean(GENERATE_INSECURE_TLS_HOOK, "Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.")); cliOptions.add(CliOption.newBoolean(USE_ENUM_CASE_INSENSITIVE, "Use `equalsIgnoreCase` when String for enum comparison", useEnumCaseInsensitive)); cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", this.failOnUnknownProperties)); cliOptions.add(CliOption.newBoolean(USE_JACKSON_3, "Use Jackson 3 instead of Jackson 2. Supported for 'native', 'apache-httpclient', and 'jersey3' libraries (requires Java 17+) and for Spring 'resttemplate', 'webclient', and 'restclient' libraries (require useSpringBoot4=true).", this.useJackson3)); @@ -527,6 +529,15 @@ public void processOpts() { additionalProperties.put(SUPPORT_URL_QUERY, Boolean.parseBoolean(additionalProperties.get(SUPPORT_URL_QUERY).toString())); } + // the disableCertificateValidation hook is emitted by default, to keep + // existing subclasses that call it compiling + if (!additionalProperties.containsKey(GENERATE_INSECURE_TLS_HOOK)) { + additionalProperties.put(GENERATE_INSECURE_TLS_HOOK, true); + } else { + additionalProperties.put(GENERATE_INSECURE_TLS_HOOK, + Boolean.parseBoolean(additionalProperties.get(GENERATE_INSECURE_TLS_HOOK).toString())); + } + convertPropertyToBooleanAndWriteBack(GENERATE_CLIENT_AS_BEAN, this::setGenerateClientAsBean); convertPropertyToBooleanAndWriteBack(USE_ENUM_CASE_INSENSITIVE, this::setUseEnumCaseInsensitive); convertPropertyToTypeAndWriteBack(CodegenConstants.MAX_ATTEMPTS_FOR_RETRY, Integer::parseInt, this::setMaxAttemptsForRetry); diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache index 2de6367d903d..bf1136d4e7ef 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; +{{#generateInsecureTlsHook}} import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; @@ -36,6 +37,7 @@ import java.security.cert.X509Certificate; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +{{/generateInsecureTlsHook}} import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @@ -1437,14 +1439,17 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { * server endpoints from web targets created by the client instance that is using this SSL context. * 4. Set the client-side trust store. * +{{#generateInsecureTlsHook}} * To completely disable certificate validation (at your own risk), you can * override this method and invoke disableCertificateValidation(clientBuilder). * +{{/generateInsecureTlsHook}} * @param clientBuilder a {@link {{javaxPackage}}.ws.rs.client.ClientBuilder} object. */ protected void customizeClientBuilder(ClientBuilder clientBuilder) { // No-op extension point } +{{#generateInsecureTlsHook}} /** * Disable X.509 certificate validation in TLS connections. @@ -1475,6 +1480,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { sslContext.init(null, trustAllCerts, new SecureRandom()); clientBuilder.sslContext(sslContext); } +{{/generateInsecureTlsHook}} /** *

Build the response headers.

diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/jersey3/ApiClient.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/jersey3/ApiClient.mustache index c5d1cbcb64b5..dcab60b8f93a 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/jersey3/ApiClient.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/jersey3/ApiClient.mustache @@ -34,6 +34,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; +{{#generateInsecureTlsHook}} import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; @@ -41,6 +42,7 @@ import java.security.cert.X509Certificate; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +{{/generateInsecureTlsHook}} import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @@ -1477,14 +1479,17 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { * server endpoints from web targets created by the client instance that is using this SSL context. * 4. Set the client-side trust store. * +{{#generateInsecureTlsHook}} * To completely disable certificate validation (at your own risk), you can * override this method and invoke disableCertificateValidation(clientBuilder). * +{{/generateInsecureTlsHook}} * @param clientBuilder a {@link {{javaxPackage}}.ws.rs.client.ClientBuilder} object. */ protected void customizeClientBuilder(ClientBuilder clientBuilder) { // No-op extension point } +{{#generateInsecureTlsHook}} /** * Disable X.509 certificate validation in TLS connections. @@ -1515,6 +1520,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { sslContext.init(null, trustAllCerts, new SecureRandom()); clientBuilder.sslContext(sslContext); } +{{/generateInsecureTlsHook}} /** *

Build the response headers.

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 b3940c9078ba..d21e044d2db9 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 @@ -5003,4 +5003,47 @@ private static JavaClientCodegen newRetrofit2Codegen(Map propert codegen.additionalProperties().putAll(properties); return codegen; } + + @DataProvider(name = "jerseyLibraries") + public static Object[][] jerseyLibraries() { + return new Object[][]{{JavaClientCodegen.JERSEY2}, {JavaClientCodegen.JERSEY3}}; + } + + @Test(dataProvider = "jerseyLibraries") + public void testInsecureTlsHookGeneratedByDefault(String library) { + Path output = generateJerseyClient(library, null); + + JavaFileAssert.assertThat(output.resolve("src/main/java/xyz/abcdef/invoker/ApiClient.java").toFile()) + .assertMethod("disableCertificateValidation"); + } + + @Test(dataProvider = "jerseyLibraries") + public void testInsecureTlsHookOmittedWhenDisabled(String library) { + Path output = generateJerseyClient(library, false); + + assertThat(output.resolve("src/main/java/xyz/abcdef/invoker/ApiClient.java")).content() + .doesNotContain("disableCertificateValidation") + .doesNotContain("X509TrustManager") + .doesNotContain("import javax.net.ssl.SSLContext;") + .doesNotContain("import java.security.SecureRandom;") + .doesNotContain("import java.security.KeyManagementException;") + .doesNotContain("import java.security.NoSuchAlgorithmException;") + .doesNotContain("import java.security.cert.X509Certificate;"); + } + + private static Path generateJerseyClient(String library, Boolean generateInsecureTlsHook) { + Path output = newTempFolder(); + CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName(JAVA_GENERATOR) + .setLibrary(library) + .addAdditionalProperty(CodegenConstants.INVOKER_PACKAGE, "xyz.abcdef.invoker") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setOutputDir(output.toString().replace("\\", "/")); + if (generateInsecureTlsHook != null) { + configurator.addAdditionalProperty(GENERATE_INSECURE_TLS_HOOK, generateInsecureTlsHook); + } + + new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + return output; + } } From 8d3bace32b132b979fdb9d466e1a7c2e6df1c9f7 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Wed, 26 Aug 2026 13:37:33 -0600 Subject: [PATCH 2/2] Register generateInsecureTlsHook with a true default CliOption.newBoolean(opt, description) records false, so the generated docs tables and config-help reported a default of false while processOpts treats an absent property as true. For an option controlling whether a trust-all TrustManager is emitted, advertising the wrong default is worse than for most, so pass the default explicitly. --- docs/generators/java-microprofile.md | 2 +- docs/generators/java.md | 2 +- .../org/openapitools/codegen/languages/JavaClientCodegen.java | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/generators/java-microprofile.md b/docs/generators/java-microprofile.md index aaffe499e6f0..6ee156c99dab 100644 --- a/docs/generators/java-microprofile.md +++ b/docs/generators/java-microprofile.md @@ -56,7 +56,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |generateBuilders|Whether to generate builders for models| |false| |generateClientAsBean|For resttemplate, restclient and webclient, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).| |false| |generateConstructorWithAllArgs|whether to generate a constructor for all arguments| |false| -|generateInsecureTlsHook|Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.| |false| +|generateInsecureTlsHook|Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.| |true| |gradleProperties|Append additional Gradle properties to the gradle.properties file| |null| |groupId|groupId in generated pom.xml| |org.openapitools| |hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false| diff --git a/docs/generators/java.md b/docs/generators/java.md index b4110f29ac4b..3cee431791da 100644 --- a/docs/generators/java.md +++ b/docs/generators/java.md @@ -56,7 +56,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |generateBuilders|Whether to generate builders for models| |false| |generateClientAsBean|For resttemplate, restclient and webclient, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).| |false| |generateConstructorWithAllArgs|whether to generate a constructor for all arguments| |false| -|generateInsecureTlsHook|Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.| |false| +|generateInsecureTlsHook|Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.| |true| |gradleProperties|Append additional Gradle properties to the gradle.properties file| |null| |groupId|groupId in generated pom.xml| |org.openapitools| |hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index ef30c09f5105..bf0978b1e6de 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -284,7 +284,7 @@ public JavaClientCodegen() { cliOptions.add(CliOption.newBoolean(WEBCLIENT_BLOCKING_OPERATIONS, "Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync", this.webclientBlockingOperations)); cliOptions.add(CliOption.newBoolean(GENERATE_CLIENT_AS_BEAN, "For resttemplate, restclient and webclient, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).", this.generateClientAsBean)); cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries.")); - cliOptions.add(CliOption.newBoolean(GENERATE_INSECURE_TLS_HOOK, "Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.")); + cliOptions.add(CliOption.newBoolean(GENERATE_INSECURE_TLS_HOOK, "Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.", true)); cliOptions.add(CliOption.newBoolean(USE_ENUM_CASE_INSENSITIVE, "Use `equalsIgnoreCase` when String for enum comparison", useEnumCaseInsensitive)); cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", this.failOnUnknownProperties)); cliOptions.add(CliOption.newBoolean(USE_JACKSON_3, "Use Jackson 3 instead of Jackson 2. Supported for 'native', 'apache-httpclient', and 'jersey3' libraries (requires Java 17+) and for Spring 'resttemplate', 'webclient', and 'restclient' libraries (require useSpringBoot4=true).", this.useJackson3)); @@ -529,8 +529,6 @@ public void processOpts() { additionalProperties.put(SUPPORT_URL_QUERY, Boolean.parseBoolean(additionalProperties.get(SUPPORT_URL_QUERY).toString())); } - // the disableCertificateValidation hook is emitted by default, to keep - // existing subclasses that call it compiling if (!additionalProperties.containsKey(GENERATE_INSECURE_TLS_HOOK)) { additionalProperties.put(GENERATE_INSECURE_TLS_HOOK, true); } else {