Defect Dojo

Defect Dojo

Overview

StackHawk has contributed a custom parser to the Defect Dojo project. The parser is capable of importing webhook event JSON as findings into the Defect Dojo platform.

This integration will require you to create and host a webhook consuming application, capable of feeding these webhook events to your Defect Dojo installation. There are some example snippets below to assist you with your own webhook consuming application.

When you’re ready to set up your Defect Dojo integration, be sure to check out the guide to enable the webhook integration, and have your application’s webhook endpoint ready to go!

Features

  • HawkScan findings can be auto imported as Defect Dojo findings.
  • Updates to scan results (re-imports) can auto close findings in Defect Dojo.

Requirements

StackHawk:

  • You must have a StackHawk account.
  • Your StackHawk Organization must belong to a plan with The Webhook Integration enabled. Contact StackHawk Support to enable it.

Defect Dojo:

  • You must have login permissions to the Defect Dojo workspace you wish to set up this workflow for.
  • You must be on Defect Dojo version 2.8.0 or above.

API References

Defect Dojo API references will be provided via the hosted Defect Dojo demo application. Information about this demo site (including how to log in to it) is available on the official Defect Dojo README.

There are 2 important API operations provided by Defect Dojo that allow automatically importing scans via custom parsers.

To interact with the StackHawk parser, please specify the scan_type as StackHawk HawkScan for both operations.

It is important that you make use of both of the above operations in your webhook consumer, as the reimport of a scan in Defect Dojo has useful behavior associated to it, such as automatically closing out resolved issues, allowing you to better track findings by StackHawk in Defect Dojo.

Authenticating with Defect Dojo

You can find example documentation for authenticating to Defect Dojo on the demo site.

When you’re ready to get your own API key for the Defect Dojo API, you can find personalized instructions at <YOUR_DEFECT_DOJO_DOMAIN>/api/key-v2.

Guide

The Defect Dojo custom parser works on the webhook payload verbatim. No modifications are necessary. We strongly recommend saving the webhook payload contents as-is to a file for upload into Defect Dojo.

Downloading the Webhook from StackHawk

These examples will showcase an endpoint (/my-webhook) downloading the contents of the StackHawk webhook payload to stackhawk-webhook.json.

import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import java.io.File

@PostMapping("/my-webhook")
fun receiveStackHawkWebhook(@RequestBody webhook: String) {
    File("stackhawk-webhook.json")
            .bufferedWriter()
            .use { out -> out.write(webhook) }
}
const fs = require('fs');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));

app.post('/my-webhook', (request, response) => {
    request.pipe(fs.createWriteStream("stackhawk-webhook.json", {flags: 'a'}));

    response.sendStatus(200);
});
from flask import request

@app.route('/my-webhook', methods=['POST'])
def receiveStackHawkWebhook():
    with open("stackhawk-webhook.json") as fo:
        fo.write(request.json)

Importing Webhook to Data Defect Dojo

These examples will demonstrate calling the Defect Dojo API with stackhawk-webhook.json, downloaded in the above example. The filename parameter should be the reference to the stackhawk-webhook.json.

import org.springframework.core.io.ClassPathResource
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.client.MultipartBodyBuilder
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.BodyInserters
import org.springframework.web.reactive.function.client.WebClient
// You provide / declare this object.
import your.own.package.for.DefectDojoProperties

@Service
class DefectDojoService(
    private val webClient: WebClient,
    private val properties: DefectDojoProperties
) {

    fun uploadToDefectDojo(filename: String, isNewImport: Boolean) {
        val multipartBodyBuilder = MultipartBodyBuilder()
    
        multipartBodyBuilder.addTextValue("scan_type", "StackHawk HawkScan")

        multipartBodyBuilder.part("file", ClassPathResource(filename).file.readBytes())
            .header(HttpHeaders.CONTENT_DISPOSITION, "form-data; name=file; filename=$filename;")

        multipartBodyBuilder.addTextValue("product_name", "{YOUR_DEFECT_DOJO_PRODUCT_NAME}")
        multipartBodyBuilder.addTextValue("engagement_name", "<YOUR_DEFECT_DOJO_ENGAGEMENT_NAME>")
        
        // optional, used for `import-scan`
        multipartBodyBuilder.addTextValue("product_type_name", "<YOUR_DEFECT_DOJO_PRODUCT_TYPE_NAME>")

        // optional, used for `reimport-scan`
        multipartBodyBuilder.addTextValue("test_title", "<YOUR_DEFECT_DOJO_TEST_TITLE>")

        // Example available form options can be found @
        // https://demo.defectdojo.org/api/v2/oa3/swagger-ui/

        // determine if this is a new import or an existing one.
        val uri = if (isNewImport) {
            "/api/v2/import-scan/"
        } else {
            "/api/v2/reimport-scan/"
        }
        webClient.post()
            .uri(properties.domain + uri)
            .contentType(MediaType.MULTIPART_FORM_DATA)
            .header(HttpHeaders.AUTHORIZATION, "Token <defect dojo api key>")
            .body(BodyInserters.fromMultipartData(multipartBodyBuilder.build()))
            .retrieve()
    }

    private fun MultipartBodyBuilder.addTextValue(name: String, value: String) {
        part(name, value, MediaType.TEXT_PLAIN)
            .header(HttpHeaders.CONTENT_DISPOSITION, "form-data; name=$name;")
            .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE)
    }
}
const fs = require('fs');

uploadToDefectDojo(filename, isNewImport) {
    const uri = isNewImport ? "/api/v2/import-scan/" : "/api/v2/reimport-scan/";
    const options = {
        method: "POST",
        url: defectDojoDomain + uri,
        port: 443,
        headers: {
            "Content-Type": "multipart/form-data",
            "Authorization": "Token <defect dojo api key>"
        },
        formData : {
            "file" : fs.createReadStream(filename),
            "scan_type": "StackHawk HawkScan",
            "product_name": "<YOUR_DEFECT_DOJO_PRODUCT_NAME>",
            "engagement_name": "<YOUR_DEFECT_DOJO_ENGAGEMENT_NAME>",

            // optional, used for `import-scan`
            "product_type_name": "<YOUR_DEFECT_DOJO_PRODUCT_TYPE_NAME>",

            // optional, used for `reimport-scan`
            "test_title": "<YOUR_DEFECT_DOJO_TEST_TITLE>"

            // Example available form options can be found @
            // https://demo.defectdojo.org/api/v2/oa3/swagger-ui/
        }
    };

    request(options, function (err, res, body) {
        if(err) console.log(err);
        console.log(body);
    });
}
import requests

def uploadToDefectDojo(filename, is_new_import):
    multipart_form_data = {
        'file': (filename, open(filename, 'rb')),
        'scan_type': (None, 'StackHawk HawkScan'),
        'product_name': (None, '<YOUR_DEFECT_DOJO_PRODUCT_NAME>'),
        'engagement_name': (None, '<YOUR_DEFECT_DOJO_ENGAGEMENT_NAME>'),

        # optional, used for `import-scan`
        'product_type_name': (None, '<YOUR_DEFECT_DOJO_PRODUCT_TYPE_NAME>'),

        # optional, used for `reimport-scan`
        'test_title': (None, '<YOUR_DEFECT_DOJO_TEST_TITLE>'),

        # Example available form options can be found @
        # https://demo.defectdojo.org/api/v2/oa3/swagger-ui/
    }

    uri = '/api/v2/import-scan/' if is_new_import else '/api/v2/reimport-scan/'
    response = requests.post(
        defect_dojo_domain + uri,
        files=multipart_form_data,
        headers={
            'Authorization': 'Token <defect dojo api key>',
        }
    )
    print(response.content)

Feedback

Have any suggestions, feature requests, or feedback to share? Contact StackHawk Support .