Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions docs/_docs/integrations/defectdojo.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,79 @@ The DefectDojo documentation says 'If no test_title is provided, the latest test
* Dependency-Track v4.6.0 or higher
![Configure Project](/images/screenshots/defectdojo_global_reimport.png)
Alternatively, you can turn on the above reimport feature for all projects in one click, by checking on 'Enable reimport' box as shown in the screenshot above.

### Auto Context Creation (Optional)

Instead of manually creating Products and Engagements in DefectDojo for each project, you can enable automatic context creation. When enabled, DefectDojo will automatically create the Product Type, Product, and Engagement if they don't already exist.

#### Prerequisites
* Manual `defectdojo.engagementId` configuration always takes precedence over auto-create
* Auto-create is opt-in via global configuration

#### Global Configuration

Enable auto-create by setting the following configuration property:

| Property Name | Default Value | Description |
|--------------|---------------|-------------|
| `defectdojo.autocreate.enabled` | `false` | Enable/disable auto context creation |
| `defectdojo.autocreate.engagementName` | `dependencytrack` | Default engagement name for all projects |
| `defectdojo.autocreate.productTypeName` | `Dependency Track` | Default product type name for all projects |
| `defectdojo.autocreate.deduplicationOnEngagement` | `false` | Enable deduplication at engagement level instead of product level |

**About Deduplication:**
By default, DefectDojo deduplicates findings at the **Product level**, meaning duplicate findings are identified across all engagements within a product. Setting `deduplicationOnEngagement` to `true` changes this to deduplicate at the **Engagement level** instead, isolating duplicate detection within each engagement.

#### Per-project Property Overrides (Optional)

You can override the default names on a per-project basis:

| Attribute | Value |
| ---------------| --------------------------------- |
| Group Name | `integrations` |
| Property Name | `defectdojo.autocreate.productName` |
| Property Value | Custom product name (defaults to project name if not set) |
| Property Type | `STRING` |

| Attribute | Value |
| ---------------| --------------------------------- |
| Group Name | `integrations` |
| Property Name | `defectdojo.autocreate.engagementName` |
| Property Value | Custom engagement name (defaults to global config if not set) |
| Property Type | `STRING` |

| Attribute | Value |
| ---------------| --------------------------------- |
| Group Name | `integrations` |
| Property Name | `defectdojo.autocreate.productTypeName` |
| Property Value | Custom product type name (defaults to global config if not set) |
| Property Type | `STRING` |

| Attribute | Value |
| ---------------| --------------------------------- |
| Group Name | `integrations` |
| Property Name | `defectdojo.autocreate.deduplicationOnEngagement` |
| Property Value | `true` or `false` (defaults to global config if not set) |
| Property Type | `BOOLEAN` |

#### Configuration Priority

The configuration follows this priority order:

1. **Manual `defectdojo.engagementId`** (highest priority) - Uses traditional flow with engagement ID
2. **Auto-create with per-project overrides** - Uses auto-create with project-specific names
3. **Auto-create with global defaults** - Uses auto-create with global configuration values

#### Example: Using Auto-create

1. Enable auto-create globally in Dependency-Track configuration:
- Set `defectdojo.autocreate.enabled` to `true`
- Optionally customize `defectdojo.autocreate.engagementName` and `defectdojo.autocreate.productTypeName`

2. For each project, the system will automatically:
- Use the project name as the Product name (or override with `defectdojo.autocreate.productName`)
- Use the global engagement name (or override with `defectdojo.autocreate.engagementName`)
- Use the global product type name (or override with `defectdojo.autocreate.productTypeName`)
- Create these entities in DefectDojo if they don't exist

3. No manual Product or Engagement creation in DefectDojo is required!
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ public DefectDojoClient(final DefectDojoUploader uploader, final URL baseURL) {
}

public void uploadDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final Boolean verifyFindings, final String testTitle) {
uploadDependencyTrackFindings(token, engagementId, findingsJson, verifyFindings, testTitle, null, null, null, false, false);
}

public void uploadDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final Boolean verifyFindings, final String testTitle,
final String productTypeName, final String productName, final String engagementName, final boolean autoCreateContext, final boolean deduplicationOnEngagement) {
LOGGER.debug("Uploading Dependency-Track findings to DefectDojo");
HttpPost request = new HttpPost(baseURL + "/api/v2/import-scan/");
InputStreamBody inputStreamBody = new InputStreamBody(findingsJson, ContentType.APPLICATION_OCTET_STREAM, "findings.json");
Expand All @@ -64,14 +69,32 @@ public void uploadDependencyTrackFindings(final String token, final String engag
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.addPart("file", inputStreamBody)
.addPart("engagement", new StringBody(engagementId, ContentType.MULTIPART_FORM_DATA))
.addPart("scan_type", new StringBody("Dependency Track Finding Packaging Format (FPF) Export", ContentType.MULTIPART_FORM_DATA))
.addPart("verified", new StringBody(Boolean.toString(verifyFindings), ContentType.MULTIPART_FORM_DATA))
.addPart("active", new StringBody("true", ContentType.MULTIPART_FORM_DATA))
.addPart("minimum_severity", new StringBody("Info", ContentType.MULTIPART_FORM_DATA))
.addPart("close_old_findings", new StringBody("true", ContentType.MULTIPART_FORM_DATA))
.addPart("push_to_jira", new StringBody("false", ContentType.MULTIPART_FORM_DATA))
.addPart("scan_date", new StringBody(DATE_FORMAT.format(new Date()), ContentType.MULTIPART_FORM_DATA));

if (autoCreateContext) {
// Use auto_create_context with product and engagement names
builder.addPart("auto_create_context", new StringBody("true", ContentType.MULTIPART_FORM_DATA));
builder.addPart("deduplication_on_engagement", new StringBody(Boolean.toString(deduplicationOnEngagement), ContentType.MULTIPART_FORM_DATA));
if (productTypeName != null) {
builder.addPart("product_type_name", new StringBody(productTypeName, ContentType.MULTIPART_FORM_DATA));
}
if (productName != null) {
builder.addPart("product_name", new StringBody(productName, ContentType.MULTIPART_FORM_DATA));
}
if (engagementName != null) {
builder.addPart("engagement_name", new StringBody(engagementName, ContentType.MULTIPART_FORM_DATA));
}
} else {
// Use traditional engagement ID
builder.addPart("engagement", new StringBody(engagementId, ContentType.MULTIPART_FORM_DATA));
}

if(testTitle != null) {
builder.addPart("test_title", new StringBody(testTitle, ContentType.MULTIPART_FORM_DATA));
}
Expand Down Expand Up @@ -160,37 +183,60 @@ public ArrayList<String> jsonToList(final JSONArray jsonArray) {
return list;
}

public void reimportDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final String testId, final Boolean doNotReactivate, final Boolean verifyFindings, final String testTitle) {
reimportDependencyTrackFindings(token, engagementId, findingsJson, testId, doNotReactivate, verifyFindings, testTitle, null, null, null, false, false);
}

/*
* A Reimport will reuse (overwrite) the existing test, instead of create a new test.
* The Successfully reimport will also increase the reimport counter by 1.
*/
public void reimportDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final String testId, final Boolean doNotReactivate, final Boolean verifyFindings, final String testTitle) {
LOGGER.debug("Re-reimport Dependency-Track findings to DefectDojo per Engagement");
public void reimportDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final String testId, final Boolean doNotReactivate, final Boolean verifyFindings, final String testTitle,
final String productTypeName, final String productName, final String engagementName, final boolean autoCreateContext, final boolean deduplicationOnEngagement) {
LOGGER.debug("Reimporting Dependency-Track findings to DefectDojo");
HttpPost request = new HttpPost(baseURL + "/api/v2/reimport-scan/");
InputStreamBody inputStreamBody = new InputStreamBody(findingsJson, ContentType.APPLICATION_OCTET_STREAM, "findings.json");
request.addHeader("accept", "application/json");
request.addHeader("Authorization", "Token " + token);
InputStreamBody inputStreamBody = new InputStreamBody(findingsJson, ContentType.APPLICATION_OCTET_STREAM, "findings.json");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.addPart("file", inputStreamBody)
.addPart("engagement", new StringBody(engagementId, ContentType.MULTIPART_FORM_DATA))
.addPart("scan_type", new StringBody("Dependency Track Finding Packaging Format (FPF) Export", ContentType.MULTIPART_FORM_DATA))
.addPart("verified", new StringBody(Boolean.toString(verifyFindings), ContentType.MULTIPART_FORM_DATA))
.addPart("active", new StringBody("true", ContentType.MULTIPART_FORM_DATA))
.addPart("minimum_severity", new StringBody("Info", ContentType.MULTIPART_FORM_DATA))
.addPart("close_old_findings", new StringBody("true", ContentType.MULTIPART_FORM_DATA))
.addPart("push_to_jira", new StringBody("false", ContentType.MULTIPART_FORM_DATA))
.addPart("do_not_reactivate", new StringBody(doNotReactivate.toString(), ContentType.MULTIPART_FORM_DATA))
.addPart("test", new StringBody(testId, ContentType.MULTIPART_FORM_DATA))
.addPart("scan_date", new StringBody(DATE_FORMAT.format(new Date()), ContentType.MULTIPART_FORM_DATA))
.build();
if(testTitle != null) {
.addPart("do_not_reactivate", new StringBody(Boolean.toString(doNotReactivate), ContentType.MULTIPART_FORM_DATA));

if (autoCreateContext) {
// Use auto_create_context with product and engagement names
builder.addPart("auto_create_context", new StringBody("true", ContentType.MULTIPART_FORM_DATA));
builder.addPart("deduplication_on_engagement", new StringBody(Boolean.toString(deduplicationOnEngagement), ContentType.MULTIPART_FORM_DATA));
if (productTypeName != null) {
builder.addPart("product_type_name", new StringBody(productTypeName, ContentType.MULTIPART_FORM_DATA));
}
if (productName != null) {
builder.addPart("product_name", new StringBody(productName, ContentType.MULTIPART_FORM_DATA));
}
if (engagementName != null) {
builder.addPart("engagement_name", new StringBody(engagementName, ContentType.MULTIPART_FORM_DATA));
}
} else {
// Use traditional engagement ID and test ID
builder.addPart("engagement", new StringBody(engagementId, ContentType.MULTIPART_FORM_DATA));
builder.addPart("test", new StringBody(testId, ContentType.MULTIPART_FORM_DATA));
}

if (testTitle != null) {
builder.addPart("test_title", new StringBody(testTitle, ContentType.MULTIPART_FORM_DATA));
}

request.setEntity(builder.build());
try (CloseableHttpResponse response = HttpClientPool.getClient().execute(request)) {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
LOGGER.debug("Successfully reimport findings to DefectDojo");
LOGGER.debug("Successfully reimported findings to DefectDojo");
} else {
uploader.handleUnexpectedHttpResponse(LOGGER, request.getURI().toString(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
}
Expand Down
Loading
Loading