Curl to Jenkins httprequest: POST file to DefectDojo REST api import-scan

Hi,

Context:
We’re trying to create shared libraries to integrate DefectDojo into the pipeline.
So far, I’ve implemented code to check for a product (read: application) in Defect Dojo and for creating, checking and deleting a test suite.

The next step is to actually import a test / scan file into Defect Dojo. I have successfully tried this using curl (see below), but I’m unable to implement this in a shared library using Jenkins’ httprequest.

Can someone help me “translate” the curl command to Jenkins’ httprequest code?

curl code (-H are headers, -F is form-data)

curl -X 'POST' \
  'http://defectdojo-server:8080/api/v2/import-scan/' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -H 'X-CSRFToken: <token>' \
  -H 'Authorization: Token <token>' \
  -F 'scan_date=2023-11-20' \
  -F 'minimum_severity=Info' \
  -F 'active=true' \
  -F 'verified=true' \
  -F 'scan_type=ZAP Scan' \
  -F 'file=@zapreport.xml;type=text/xml' \
  -F 'product_name=Test Product' \
  -F 'engagement_name=Test Engagement' \
  -F 'close_old_findings=false' \
  -F 'close_old_findings_product_scope=false' \
  -F 'push_to_jira=false' \
  -F 'create_finding_groups_for_all_findings=true'

Jenkins Http-request:

ResponseContentSupplier response = steps.httpRequest url: "${env.DEFECT_DOJO_HOST_URL}${api}",
                httpMode: 'POST',
                acceptType: 'APPLICATION_JSON',
                customHeaders: customHeadersDummy(authHeader, headers),
                formData: [
                        [body: '''{
    "product_name": "Dummy project",
    "engagement_name": "Dummy Engagement",
    "scan_date": "2023-11-20",
    "minimum_severity": "Info",
    "active": "true",
    "verified": "true",
    "scan_type": "ZAP Scan",
    "file": "@zapreport.xml;type=text/xml",
    "close_old_findings": "false",
    "close_old_findings_product_scope": "false",
    "push_to_jira": "false",
    "create_finding_groups_for_all_findings": "true"
}
''',
                        contentType: 'text/xml',
                        fileName: 'zapreport.xml',
                        name: 'zapreport.xml',
                        uploadFile: 'zapreport.xml'
                ]],
                        product_name: 'Dummy project',
                consoleLogResponseBody: true,
                contentType: 'APPLICATION_FORM_DATA',
                wrapAsMultipart: false

Current response is: 400 bad request
{“detail”:“Multipart form parse error - Invalid boundary in multipart: None”}

Hello @brampat, and welcome to this community. :wave:

The error message you’re seeing suggests that the server is expecting a multipart form data request, but it’s not receiving it in the correct format.

As far as I understand, in your Jenkins httpRequest, you’re trying to send the file content as a string in the JSON body, which is not the correct way to send a file in a multipart form data request. :person_shrugging:

What about something like:

CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost uploadFile = new HttpPost("http://defectdojo-server:8080/api/v2/import-scan/");

        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.addTextBody("scan_date", "2023-11-20", ContentType.TEXT_PLAIN);
        builder.addTextBody("minimum_severity", "Info", ContentType.TEXT_PLAIN);
        builder.addTextBody("active", "true", ContentType.TEXT_PLAIN);
        builder.addTextBody("verified", "true", ContentType.TEXT_PLAIN);
        builder.addTextBody("scan_type", "ZAP Scan", ContentType.TEXT_PLAIN);
        builder.addTextBody("product_name", "Test Product", ContentType.TEXT_PLAIN);
        builder.addTextBody("engagement_name", "Test Engagement", ContentType.TEXT_PLAIN);
        builder.addTextBody("close_old_findings", "false", ContentType.TEXT_PLAIN);
        builder.addTextBody("close_old_findings_product_scope", "false", ContentType.TEXT_PLAIN);
        builder.addTextBody("push_to_jira", "false", ContentType.TEXT_PLAIN);
        builder.addTextBody("create_finding_groups_for_all_findings", "true", ContentType.TEXT_PLAIN);

        // This attaches the file to the POST:
        File f = new File("zapreport.xml");
        builder.addBinaryBody(
            "file",
            f,
            ContentType.APPLICATION_OCTET_STREAM,
            f.getName()
        );

        HttpEntity multipart = builder.build();
        uploadFile.setEntity(multipart);

        uploadFile.setHeader("accept", "application/json");
        uploadFile.setHeader("X-CSRFToken", "<token>");
        uploadFile.setHeader("Authorization", "Token <token>");

        CloseableHttpResponse response = httpClient.execute(uploadFile);
        HttpEntity responseEntity = response.getEntity();
        String responseString = EntityUtils.toString(responseEntity, "UTF-8");

        System.out.println("Response: " + responseString);

?