Skip to content

[BUG][java native] querystring Objects toString not checked if null prior #21329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
duttonw opened this issue May 26, 2025 · 0 comments · Fixed by #21330
Closed

[BUG][java native] querystring Objects toString not checked if null prior #21329

duttonw opened this issue May 26, 2025 · 0 comments · Fixed by #21330

Comments

@duttonw
Copy link
Contributor

duttonw commented May 26, 2025

Description

Native Java client code generated by org.openapitools:openapi-generator-maven-plugin:7.13.0 with optional query params are not null tested prior to being 'toString()' called.

openapi-generator version

7.13.0

OpenAPI declaration file content or url

https://github.com/qld-gov-au/kiteworks-integration/blob/main/kiteworks-swagger-gen/src/main/resources/kiteworks.28.swagger.json

"/rest/folders/{id}/actions/fileBase64Encoded": {
      "post": {
        "tags": [
          "files"
        ],
        "summary": "upload  base64 encoded content",
        "description": "uploads base64 encoded file content to a folder",
        "responses": {
          "422": {
            "description": "Errors\\ERR_ENTITY_LOCKED<br />Errors\\ERR_ENTITY_RESTRICTED_EXTENSION<br />Errors\\ERR_ENTITY_RESTRICTED_EXTENSION_CUSTOM<br />Errors\\ERR_ENTITY_RESTRICTED_TYPE<br />Errors\\ERR_ENTITY_RESTRICTED_TYPE_CUSTOM<br />Errors\\ERR_ENTITY_RESTRICTED_TYPE_GROUP<br />Errors\\ERR_ENTITY_EXISTS"
          },
          "490": {
            "description": "Request blocked by WAF"
          }
        },
        "deprecated": false,
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "description": "ID of the folder",
            "type": "string",
            "required": true
          },
          {
            "in": "body",
            "name": "Body",
            "description": "File information",
            "schema": {
              "$ref": "#/definitions/Content.Post"
            },
            "required": true
          },
          {
            "in": "query",
            "name": "returnEntity",
            "description": "Return information about newly created entity",
            "type": "boolean"
          },
          {
            "in": "query",
            "name": "mode",
            "description": "Response mode",
            "type": "string"
          }
        ]
      }
    },
Generation Details
<plugin>
                <groupId>org.openapitools</groupId>
                <artifactId>openapi-generator-maven-plugin</artifactId>
                <version>7.13.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/src/main/resources/kiteworks.28.swagger.json</inputSpec>
                            <generatorName>java</generatorName>
                            <configOptions>
                                <dateLibrary>java8</dateLibrary> <!--java8 - Java 8 native JSR310 (preferred for jdk 1.8+) -->
                                <useJakartaEe>true</useJakartaEe>
                                <useTags>true</useTags>
                                <serializationLibrary>jackson</serializationLibrary>
                            </configOptions>

                            <library>native</library>
                            <apiPackage>com.kiteworks.client.api</apiPackage>
                            <modelPackage>com.kiteworks.client.model</modelPackage>
                            <invokerPackage>com.kiteworks.client</invokerPackage>
                            <cleanupOutput>true</cleanupOutput>
                            <generateApiDocumentation>false</generateApiDocumentation>
                            <generateApiTests>false</generateApiTests>
                            <generateModelDocumentation>false</generateModelDocumentation>
                            <generateModelTests>false</generateModelTests>
<!--                            <configHelp>true</configHelp>-->
                        </configuration>
                    </execution>
                </executions>
            </plugin>

outputs

private HttpRequest.Builder restFoldersIdActionsFilePostRequestBuilder(@jakarta.annotation.Nonnull String id, @jakarta.annotation.Nonnull File body, @jakarta.annotation.Nullable Boolean returnEntity, @jakarta.annotation.Nullable String mode, @jakarta.annotation.Nullable LocalDate clientCreated, @jakarta.annotation.Nullable LocalDate clientModified, @jakarta.annotation.Nullable Boolean disableAutoVersion, @jakarta.annotation.Nullable Boolean note) throws ApiException {
    // verify the required parameter 'id' is set
    if (id == null) {
      throw new ApiException(400, "Missing the required parameter 'id' when calling restFoldersIdActionsFilePost");
    }
    // verify the required parameter 'body' is set
    if (body == null) {
      throw new ApiException(400, "Missing the required parameter 'body' when calling restFoldersIdActionsFilePost");
    }

    HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();

    String localVarPath = "/rest/folders/{id}/actions/file"
        .replace("{id}", ApiClient.urlEncode(id.toString()));

    List<Pair> localVarQueryParams = new ArrayList<>();
    StringJoiner localVarQueryStringJoiner = new StringJoiner("&");
    String localVarQueryParameterBaseName;
    localVarQueryParameterBaseName = "returnEntity";
    localVarQueryParams.addAll(ApiClient.parameterToPairs("returnEntity", returnEntity));
    localVarQueryParameterBaseName = "mode";
    localVarQueryParams.addAll(ApiClient.parameterToPairs("mode", mode));

    if (!localVarQueryParams.isEmpty() || localVarQueryStringJoiner.length() != 0) {
      StringJoiner queryJoiner = new StringJoiner("&");
      localVarQueryParams.forEach(p -> queryJoiner.add(p.getName() + '=' + p.getValue()));
      if (localVarQueryStringJoiner.length() != 0) {
        queryJoiner.add(localVarQueryStringJoiner.toString());
      }
      localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath + '?' + queryJoiner.toString()));
    } else {
      localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
    }

    localVarRequestBuilder.header("Accept", "application/json");

    MultipartEntityBuilder multiPartBuilder = MultipartEntityBuilder.create();
    boolean hasFiles = false;
    multiPartBuilder.addBinaryBody("body", body);
    hasFiles = true;
    multiPartBuilder.addTextBody("clientCreated", clientCreated.toString());
    multiPartBuilder.addTextBody("clientModified", clientModified.toString());
    multiPartBuilder.addTextBody("disableAutoVersion", disableAutoVersion.toString());
    multiPartBuilder.addTextBody("note", note.toString());
    HttpEntity entity = multiPartBuilder.build();
    HttpRequest.BodyPublisher formDataPublisher;
    if (hasFiles) {
        Pipe pipe;
        try {
            pipe = Pipe.open();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        new Thread(() -> {
            try (OutputStream outputStream = Channels.newOutputStream(pipe.sink())) {
                entity.writeTo(outputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        formDataPublisher = HttpRequest.BodyPublishers.ofInputStream(() -> Channels.newInputStream(pipe.source()));
    } else {
        ByteArrayOutputStream formOutputStream = new ByteArrayOutputStream();
        try {
            entity.writeTo(formOutputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        formDataPublisher = HttpRequest.BodyPublishers
            .ofInputStream(() -> new ByteArrayInputStream(formOutputStream.toByteArray()));
    }
    localVarRequestBuilder
        .header("Content-Type", entity.getContentType().getValue())
        .method("POST", formDataPublisher);
    if (memberVarReadTimeout != null) {
      localVarRequestBuilder.timeout(memberVarReadTimeout);
    }
    if (memberVarInterceptor != null) {
      memberVarInterceptor.accept(localVarRequestBuilder);
    }
    return localVarRequestBuilder;
  }
Steps to reproduce

Issues in generated code are
multiPartBuilder.addTextBody("clientCreated", clientCreated.toString());
multiPartBuilder.addTextBody("clientModified", clientModified.toString());
multiPartBuilder.addTextBody("disableAutoVersion", disableAutoVersion.toString());
multiPartBuilder.addTextBody("note", note.toString());

As inputs are
@jakarta.annotation.Nullable Boolean returnEntity
@jakarta.annotation.Nullable String mode
@jakarta.annotation.Nullable LocalDate clientCreated
@jakarta.annotation.Nullable LocalDate clientModified
@jakarta.annotation.Nullable Boolean disableAutoVersion
@jakarta.annotation.Nullable Boolean note

Related issues/PRs
Suggest a fix

https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/Java/libraries/native/api.mustache#L487C65-L487C84

Generated code when not having a String input should wrap with a
if (Objects.isNonNull(clientCreated)) {
multiPartBuilder.addTextBody("clientCreated", clientCreated.toString());
}

duttonw added a commit to duttonw/openapi-generator that referenced this issue May 26, 2025
duttonw added a commit to duttonw/openapi-generator that referenced this issue May 26, 2025
wing328 pushed a commit that referenced this issue Jun 2, 2025
…variables (#21330)

* fix: #21329 Java Native, Provide Null Check before toString on param variables

* #21329 - Samples Generated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant