Skip to content

Commit eb2e8cb

Browse files
committed
Add defectdojo auto create
1 parent 41380dd commit eb2e8cb

5 files changed

Lines changed: 408 additions & 19 deletions

File tree

docs/_docs/integrations/defectdojo.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,68 @@ The DefectDojo documentation says 'If no test_title is provided, the latest test
101101
* Dependency-Track v4.6.0 or higher
102102
![Configure Project](/images/screenshots/defectdojo_global_reimport.png)
103103
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.
104+
105+
### Auto Context Creation (Optional)
106+
107+
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.
108+
109+
#### Prerequisites
110+
* Manual `defectdojo.engagementId` configuration always takes precedence over auto-create
111+
* Auto-create is opt-in via global configuration
112+
113+
#### Global Configuration
114+
115+
Enable auto-create by setting the following configuration property:
116+
117+
| Property Name | Default Value | Description |
118+
|--------------|---------------|-------------|
119+
| `defectdojo.autocreate.enabled` | `false` | Enable/disable auto context creation |
120+
| `defectdojo.autocreate.engagementName` | `dependencytrack` | Default engagement name for all projects |
121+
| `defectdojo.autocreate.productTypeName` | `Dependency Track` | Default product type name for all projects |
122+
123+
#### Per-project Property Overrides (Optional)
124+
125+
You can override the default names on a per-project basis:
126+
127+
| Attribute | Value |
128+
| ---------------| --------------------------------- |
129+
| Group Name | `integrations` |
130+
| Property Name | `defectdojo.autocreate.productName` |
131+
| Property Value | Custom product name (defaults to project name if not set) |
132+
| Property Type | `STRING` |
133+
134+
| Attribute | Value |
135+
| ---------------| --------------------------------- |
136+
| Group Name | `integrations` |
137+
| Property Name | `defectdojo.autocreate.engagementName` |
138+
| Property Value | Custom engagement name (defaults to global config if not set) |
139+
| Property Type | `STRING` |
140+
141+
| Attribute | Value |
142+
| ---------------| --------------------------------- |
143+
| Group Name | `integrations` |
144+
| Property Name | `defectdojo.autocreate.productTypeName` |
145+
| Property Value | Custom product type name (defaults to global config if not set) |
146+
| Property Type | `STRING` |
147+
148+
#### Configuration Priority
149+
150+
The configuration follows this priority order:
151+
152+
1. **Manual `defectdojo.engagementId`** (highest priority) - Uses traditional flow with engagement ID
153+
2. **Auto-create with per-project overrides** - Uses auto-create with project-specific names
154+
3. **Auto-create with global defaults** - Uses auto-create with global configuration values
155+
156+
#### Example: Using Auto-create
157+
158+
1. Enable auto-create globally in Dependency-Track configuration:
159+
- Set `defectdojo.autocreate.enabled` to `true`
160+
- Optionally customize `defectdojo.autocreate.engagementName` and `defectdojo.autocreate.productTypeName`
161+
162+
2. For each project, the system will automatically:
163+
- Use the project name as the Product name (or override with `defectdojo.autocreate.productName`)
164+
- Use the global engagement name (or override with `defectdojo.autocreate.engagementName`)
165+
- Use the global product type name (or override with `defectdojo.autocreate.productTypeName`)
166+
- Create these entities in DefectDojo if they don't exist
167+
168+
3. No manual Product or Engagement creation in DefectDojo is required!

src/main/java/org/dependencytrack/integrations/defectdojo/DefectDojoClient.java

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ public DefectDojoClient(final DefectDojoUploader uploader, final URL baseURL) {
5656
}
5757

5858
public void uploadDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final Boolean verifyFindings, final String testTitle) {
59+
uploadDependencyTrackFindings(token, engagementId, findingsJson, verifyFindings, testTitle, null, null, null, false);
60+
}
61+
62+
public void uploadDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final Boolean verifyFindings, final String testTitle,
63+
final String productTypeName, final String productName, final String engagementName, final boolean autoCreateContext) {
5964
LOGGER.debug("Uploading Dependency-Track findings to DefectDojo");
6065
HttpPost request = new HttpPost(baseURL + "/api/v2/import-scan/");
6166
InputStreamBody inputStreamBody = new InputStreamBody(findingsJson, ContentType.APPLICATION_OCTET_STREAM, "findings.json");
@@ -64,14 +69,31 @@ public void uploadDependencyTrackFindings(final String token, final String engag
6469
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
6570
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
6671
.addPart("file", inputStreamBody)
67-
.addPart("engagement", new StringBody(engagementId, ContentType.MULTIPART_FORM_DATA))
6872
.addPart("scan_type", new StringBody("Dependency Track Finding Packaging Format (FPF) Export", ContentType.MULTIPART_FORM_DATA))
6973
.addPart("verified", new StringBody(Boolean.toString(verifyFindings), ContentType.MULTIPART_FORM_DATA))
7074
.addPart("active", new StringBody("true", ContentType.MULTIPART_FORM_DATA))
7175
.addPart("minimum_severity", new StringBody("Info", ContentType.MULTIPART_FORM_DATA))
7276
.addPart("close_old_findings", new StringBody("true", ContentType.MULTIPART_FORM_DATA))
7377
.addPart("push_to_jira", new StringBody("false", ContentType.MULTIPART_FORM_DATA))
7478
.addPart("scan_date", new StringBody(DATE_FORMAT.format(new Date()), ContentType.MULTIPART_FORM_DATA));
79+
80+
if (autoCreateContext) {
81+
// Use auto_create_context with product and engagement names
82+
builder.addPart("auto_create_context", new StringBody("true", ContentType.MULTIPART_FORM_DATA));
83+
if (productTypeName != null) {
84+
builder.addPart("product_type_name", new StringBody(productTypeName, ContentType.MULTIPART_FORM_DATA));
85+
}
86+
if (productName != null) {
87+
builder.addPart("product_name", new StringBody(productName, ContentType.MULTIPART_FORM_DATA));
88+
}
89+
if (engagementName != null) {
90+
builder.addPart("engagement_name", new StringBody(engagementName, ContentType.MULTIPART_FORM_DATA));
91+
}
92+
} else {
93+
// Use traditional engagement ID
94+
builder.addPart("engagement", new StringBody(engagementId, ContentType.MULTIPART_FORM_DATA));
95+
}
96+
7597
if(testTitle != null) {
7698
builder.addPart("test_title", new StringBody(testTitle, ContentType.MULTIPART_FORM_DATA));
7799
}
@@ -160,37 +182,59 @@ public ArrayList<String> jsonToList(final JSONArray jsonArray) {
160182
return list;
161183
}
162184

185+
public void reimportDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final String testId, final Boolean doNotReactivate, final Boolean verifyFindings, final String testTitle) {
186+
reimportDependencyTrackFindings(token, engagementId, findingsJson, testId, doNotReactivate, verifyFindings, testTitle, null, null, null, false);
187+
}
188+
163189
/*
164190
* A Reimport will reuse (overwrite) the existing test, instead of create a new test.
165191
* The Successfully reimport will also increase the reimport counter by 1.
166192
*/
167-
public void reimportDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final String testId, final Boolean doNotReactivate, final Boolean verifyFindings, final String testTitle) {
168-
LOGGER.debug("Re-reimport Dependency-Track findings to DefectDojo per Engagement");
193+
public void reimportDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson, final String testId, final Boolean doNotReactivate, final Boolean verifyFindings, final String testTitle,
194+
final String productTypeName, final String productName, final String engagementName, final boolean autoCreateContext) {
195+
LOGGER.debug("Reimporting Dependency-Track findings to DefectDojo");
169196
HttpPost request = new HttpPost(baseURL + "/api/v2/reimport-scan/");
197+
InputStreamBody inputStreamBody = new InputStreamBody(findingsJson, ContentType.APPLICATION_OCTET_STREAM, "findings.json");
170198
request.addHeader("accept", "application/json");
171199
request.addHeader("Authorization", "Token " + token);
172-
InputStreamBody inputStreamBody = new InputStreamBody(findingsJson, ContentType.APPLICATION_OCTET_STREAM, "findings.json");
173200
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
174201
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
175202
.addPart("file", inputStreamBody)
176-
.addPart("engagement", new StringBody(engagementId, ContentType.MULTIPART_FORM_DATA))
177203
.addPart("scan_type", new StringBody("Dependency Track Finding Packaging Format (FPF) Export", ContentType.MULTIPART_FORM_DATA))
178204
.addPart("verified", new StringBody(Boolean.toString(verifyFindings), ContentType.MULTIPART_FORM_DATA))
179205
.addPart("active", new StringBody("true", ContentType.MULTIPART_FORM_DATA))
180206
.addPart("minimum_severity", new StringBody("Info", ContentType.MULTIPART_FORM_DATA))
181207
.addPart("close_old_findings", new StringBody("true", ContentType.MULTIPART_FORM_DATA))
182208
.addPart("push_to_jira", new StringBody("false", ContentType.MULTIPART_FORM_DATA))
183-
.addPart("do_not_reactivate", new StringBody(doNotReactivate.toString(), ContentType.MULTIPART_FORM_DATA))
184-
.addPart("test", new StringBody(testId, ContentType.MULTIPART_FORM_DATA))
185209
.addPart("scan_date", new StringBody(DATE_FORMAT.format(new Date()), ContentType.MULTIPART_FORM_DATA))
186-
.build();
187-
if(testTitle != null) {
210+
.addPart("do_not_reactivate", new StringBody(Boolean.toString(doNotReactivate), ContentType.MULTIPART_FORM_DATA));
211+
212+
if (autoCreateContext) {
213+
// Use auto_create_context with product and engagement names
214+
builder.addPart("auto_create_context", new StringBody("true", ContentType.MULTIPART_FORM_DATA));
215+
if (productTypeName != null) {
216+
builder.addPart("product_type_name", new StringBody(productTypeName, ContentType.MULTIPART_FORM_DATA));
217+
}
218+
if (productName != null) {
219+
builder.addPart("product_name", new StringBody(productName, ContentType.MULTIPART_FORM_DATA));
220+
}
221+
if (engagementName != null) {
222+
builder.addPart("engagement_name", new StringBody(engagementName, ContentType.MULTIPART_FORM_DATA));
223+
}
224+
} else {
225+
// Use traditional engagement ID and test ID
226+
builder.addPart("engagement", new StringBody(engagementId, ContentType.MULTIPART_FORM_DATA));
227+
builder.addPart("test", new StringBody(testId, ContentType.MULTIPART_FORM_DATA));
228+
}
229+
230+
if (testTitle != null) {
188231
builder.addPart("test_title", new StringBody(testTitle, ContentType.MULTIPART_FORM_DATA));
189232
}
233+
190234
request.setEntity(builder.build());
191235
try (CloseableHttpResponse response = HttpClientPool.getClient().execute(request)) {
192236
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
193-
LOGGER.debug("Successfully reimport findings to DefectDojo");
237+
LOGGER.debug("Successfully reimported findings to DefectDojo");
194238
} else {
195239
uploader.handleUnexpectedHttpResponse(LOGGER, request.getURI().toString(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
196240
}

0 commit comments

Comments
 (0)