-
Notifications
You must be signed in to change notification settings - Fork 0
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
Feature/remove unirest #1
base: master
Are you sure you want to change the base?
Changes from 37 commits
c7a7074
39f31f3
7e40086
6cc00de
2c99bf2
b1dca1d
e691dda
89ab958
f7d6c1c
9e45ee7
d718caa
c5b0e96
d405ab8
21d2ae5
abba6a7
abd98a9
3ccef34
7298bde
bedb383
5d7141b
7a7b938
f24701a
f156265
d2bdcad
18524f7
5c5da89
5b2bcd0
3fa7d4f
c34cec8
9496c4c
4a34be1
2a7f648
119b8f5
8a049f1
4309ca9
2e8af4a
860c90e
a119f8b
9cfd580
58d863f
b95545a
3d780f4
e2c6eee
46e6640
c974b72
1c1b1d3
38b3115
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -19,15 +19,25 @@ | |||||||||
package org.dependencytrack.integrations.defectdojo; | ||||||||||
|
||||||||||
import alpine.common.logging.Logger; | ||||||||||
import kong.unirest.UnirestInstance; | ||||||||||
import kong.unirest.HttpRequestWithBody; | ||||||||||
import kong.unirest.HttpResponse; | ||||||||||
import kong.unirest.JsonNode; | ||||||||||
import kong.unirest.json.JSONArray; | ||||||||||
import kong.unirest.json.JSONObject; | ||||||||||
import org.dependencytrack.common.UnirestFactory; | ||||||||||
import org.apache.http.HttpEntity; | ||||||||||
import org.apache.http.HttpStatus; | ||||||||||
import org.apache.http.client.methods.CloseableHttpResponse; | ||||||||||
import org.apache.http.client.methods.HttpGet; | ||||||||||
import org.apache.http.client.methods.HttpPost; | ||||||||||
import org.apache.http.client.utils.URIBuilder; | ||||||||||
import org.apache.http.entity.ContentType; | ||||||||||
import org.apache.http.entity.mime.HttpMultipartMode; | ||||||||||
import org.apache.http.entity.mime.MultipartEntityBuilder; | ||||||||||
import org.apache.http.entity.mime.content.InputStreamBody; | ||||||||||
import org.apache.http.entity.mime.content.StringBody; | ||||||||||
import org.apache.http.util.EntityUtils; | ||||||||||
import org.dependencytrack.common.HttpClientPool; | ||||||||||
import org.json.JSONArray; | ||||||||||
import org.json.JSONObject; | ||||||||||
|
||||||||||
import java.io.IOException; | ||||||||||
import java.io.InputStream; | ||||||||||
import java.net.URISyntaxException; | ||||||||||
import java.net.URL; | ||||||||||
import java.text.SimpleDateFormat; | ||||||||||
import java.util.ArrayList; | ||||||||||
|
@@ -40,86 +50,98 @@ public class DefectDojoClient { | |||||||||
private final DefectDojoUploader uploader; | ||||||||||
private final URL baseURL; | ||||||||||
|
||||||||||
public DefectDojoClient(final DefectDojoUploader uploader, final URL baseURL) { | ||||||||||
public DefectDojoClient(final DefectDojoUploader uploader, final URL baseURL) { | ||||||||||
this.uploader = uploader; | ||||||||||
this.baseURL = baseURL; | ||||||||||
} | ||||||||||
|
||||||||||
public void uploadDependencyTrackFindings(final String token, final String engagementId, final InputStream findingsJson) { | ||||||||||
LOGGER.debug("Uploading Dependency-Track findings to DefectDojo"); | ||||||||||
final UnirestInstance ui = UnirestFactory.getUnirestInstance(); | ||||||||||
final HttpRequestWithBody request = ui.post(baseURL + "/api/v2/import-scan/"); | ||||||||||
HttpPost request = new HttpPost(baseURL + "/api/v2/import-scan/"); | ||||||||||
InputStreamBody inputStreamBody = new InputStreamBody(findingsJson, ContentType.DEFAULT_BINARY, "findings.json"); | ||||||||||
request.addHeader("accept", "application/json"); | ||||||||||
request.addHeader("Authorization", "Token " + token); | ||||||||||
HttpEntity data = MultipartEntityBuilder.create().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("true", 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("push_to_jira", ContentType.MULTIPART_FORM_DATA)) | ||||||||||
.addPart("scan_date", new StringBody(DATE_FORMAT.format(new Date()), ContentType.MULTIPART_FORM_DATA)) | ||||||||||
.build(); | ||||||||||
request.setEntity(data); | ||||||||||
|
||||||||||
final HttpResponse<String> response = request | ||||||||||
.header("accept", "application/json") | ||||||||||
.header("Authorization", "Token " + token) | ||||||||||
.field("file", findingsJson, "findings.json") | ||||||||||
.field("engagement", engagementId) | ||||||||||
.field("scan_type", "Dependency Track Finding Packaging Format (FPF) Export") | ||||||||||
.field("verified", "true") | ||||||||||
.field("active", "true") | ||||||||||
.field("minimum_severity", "Info") | ||||||||||
.field("close_old_findings", "true") | ||||||||||
.field("push_to_jira", "false") | ||||||||||
.field("scan_date", DATE_FORMAT.format(new Date())) | ||||||||||
.asString(); | ||||||||||
if (response.getStatus() == 201) { | ||||||||||
LOGGER.debug("Successfully uploaded findings to DefectDojo"); | ||||||||||
} else { | ||||||||||
LOGGER.warn("DefectDojo Client did not receive expected response while attempting to upload " | ||||||||||
+ "Dependency-Track findings. HTTP response code: " | ||||||||||
+ response.getStatus() + " - " + response.getStatusText()); | ||||||||||
uploader.handleUnexpectedHttpResponse(LOGGER, request.getUrl(), response.getStatus(), response.getStatusText()); | ||||||||||
|
||||||||||
try (CloseableHttpResponse response = HttpClientPool.getClient().execute(request)) { | ||||||||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) { | ||||||||||
LOGGER.debug("Successfully uploaded findings to DefectDojo"); | ||||||||||
} else { | ||||||||||
uploader.handleUnexpectedHttpResponse(LOGGER, request.getURI().toString(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); | ||||||||||
} | ||||||||||
} catch (IOException ex) { | ||||||||||
uploader.handleException(LOGGER, ex); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
// Pulling DefectDojo 'tests' API endpoint with engagementID filter on, and retrieve a list of existing tests | ||||||||||
public ArrayList getDojoTestIds(final String token, final String eid) { | ||||||||||
LOGGER.debug("Pulling DefectDojo Tests API ..."); | ||||||||||
String tests_uri = "/api/v2/tests/"; | ||||||||||
final UnirestInstance ui = UnirestFactory.getUnirestInstance(); | ||||||||||
String testsUri = "/api/v2/tests/"; | ||||||||||
LOGGER.debug("Make the first pagination call"); | ||||||||||
HttpResponse<JsonNode> response = ui.get(baseURL + tests_uri + "?limit=100&engagement=" + eid) | ||||||||||
.header("accept", "application/json") | ||||||||||
.header("Authorization", "Token " + token) | ||||||||||
.asJson(); | ||||||||||
if (response.getStatus() == 200) { | ||||||||||
if (response.getBody() != null && response.getBody().getObject() != null) { | ||||||||||
JsonNode root = response.getBody(); | ||||||||||
JSONObject dojoObj = root.getObject(); | ||||||||||
JSONArray dojoArray = dojoObj.getJSONArray("results"); | ||||||||||
ArrayList dojoTests = jsonToList(dojoArray); | ||||||||||
String nextUrl = ""; | ||||||||||
while (dojoObj.get("next") != null ) { | ||||||||||
nextUrl = dojoObj.get("next").toString(); | ||||||||||
LOGGER.debug("Making the subsequent pagination call on " + nextUrl); | ||||||||||
response = ui.get(nextUrl) | ||||||||||
.header("accept", "application/json") | ||||||||||
.header("Authorization", "Token " + token) | ||||||||||
.asJson(); | ||||||||||
nextUrl = dojoObj.get("next").toString(); | ||||||||||
root = response.getBody(); | ||||||||||
dojoObj = root.getObject(); | ||||||||||
dojoArray = dojoObj.getJSONArray("results"); | ||||||||||
dojoTests.addAll(jsonToList(dojoArray)); | ||||||||||
try { | ||||||||||
URIBuilder uriBuilder = new URIBuilder(baseURL + testsUri); | ||||||||||
uriBuilder.addParameter("limit", "100"); | ||||||||||
uriBuilder.addParameter("engagement", eid); | ||||||||||
HttpGet request = new HttpGet(uriBuilder.build().toString()); | ||||||||||
request.addHeader("accept", "application/json"); | ||||||||||
request.addHeader("Authorization", "Token " + token); | ||||||||||
try (CloseableHttpResponse response = HttpClientPool.getClient().execute(request)) { | ||||||||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { | ||||||||||
if (response.getEntity() != null) { | ||||||||||
String stringResponse = EntityUtils.toString(response.getEntity()); | ||||||||||
JSONObject dojoObj = new JSONObject(stringResponse); | ||||||||||
JSONArray dojoArray = dojoObj.getJSONArray("results"); | ||||||||||
ArrayList dojoTests = jsonToList(dojoArray); | ||||||||||
String nextUrl = ""; | ||||||||||
while (dojoObj.get("next") != null) { | ||||||||||
nextUrl = dojoObj.get("next").toString(); | ||||||||||
LOGGER.debug("Making the subsequent pagination call on " + nextUrl); | ||||||||||
uriBuilder = new URIBuilder(nextUrl); | ||||||||||
request = new HttpGet(uriBuilder.build().toString()); | ||||||||||
request.addHeader("accept", "application/json"); | ||||||||||
request.addHeader("Authorization", "Token " + token); | ||||||||||
try (CloseableHttpResponse response1 = HttpClientPool.getClient().execute(request)) { | ||||||||||
nextUrl = dojoObj.get("next").toString(); | ||||||||||
stringResponse = EntityUtils.toString(response1.getEntity()); | ||||||||||
} | ||||||||||
dojoObj = new JSONObject(stringResponse); | ||||||||||
dojoArray = dojoObj.getJSONArray("results"); | ||||||||||
dojoTests.addAll(jsonToList(dojoArray)); | ||||||||||
} | ||||||||||
LOGGER.debug("Successfully retrieved the test list "); | ||||||||||
return dojoTests; | ||||||||||
} | ||||||||||
} else { | ||||||||||
LOGGER.warn("DefectDojo Client did not receive expected response while attempting to retrieve tests list " | ||||||||||
+ response.getStatusLine().getStatusCode() + " - " + response.getStatusLine().getReasonPhrase()); | ||||||||||
} | ||||||||||
LOGGER.debug("Successfully retrieved the test list "); | ||||||||||
return dojoTests; | ||||||||||
} | ||||||||||
} else { | ||||||||||
LOGGER.warn("DefectDojo Client did not receive expected response while attempting to retrieve tests list " | ||||||||||
+ response.getStatus() + " - " + response.getBody()); | ||||||||||
} catch (IOException | URISyntaxException ex) { | ||||||||||
uploader.handleException(LOGGER, ex); | ||||||||||
} | ||||||||||
return null; | ||||||||||
return new ArrayList<>(); | ||||||||||
} | ||||||||||
|
||||||||||
// Given the engagement id and scan type, search for existing test id | ||||||||||
public String getDojoTestId(final String engagementID, final ArrayList dojoTests) { | ||||||||||
public String getDojoTestId(final String engagementID, final ArrayList dojoTests) { | ||||||||||
for (int i = 0; i < dojoTests.size(); i++) { | ||||||||||
String s = dojoTests.get(i).toString(); | ||||||||||
JSONObject dojoTest = new JSONObject(s); | ||||||||||
if (dojoTest.get("engagement").toString().equals(engagementID) && | ||||||||||
dojoTest.get("scan_type").toString().equals("Dependency Track Finding Packaging Format (FPF) Export")) { | ||||||||||
if (dojoTest.get("engagement").toString().equals(engagementID) && | ||||||||||
dojoTest.get("scan_type").toString().equals("Dependency Track Finding Packaging Format (FPF) Export")) { | ||||||||||
return dojoTest.get("id").toString(); | ||||||||||
} | ||||||||||
} | ||||||||||
|
@@ -140,32 +162,34 @@ public ArrayList<String> jsonToList(final JSONArray jsonArray) { | |||||||||
/* | ||||||||||
* 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) { | ||||||||||
LOGGER.debug("Re-reimport Dependency-Track findings to DefectDojo per Engagement"); | ||||||||||
final UnirestInstance ui = UnirestFactory.getUnirestInstance(); | ||||||||||
final HttpRequestWithBody request = ui.post(baseURL + "/api/v2/reimport-scan/"); | ||||||||||
final HttpResponse<String> response = request | ||||||||||
.header("accept", "application/json") | ||||||||||
.header("Authorization", "Token " + token) | ||||||||||
.field("file", findingsJson, "findings.json") | ||||||||||
.field("engagement", engagementId) | ||||||||||
.field("scan_type", "Dependency Track Finding Packaging Format (FPF) Export") | ||||||||||
.field("verified", "true") | ||||||||||
.field("active", "true") | ||||||||||
.field("minimum_severity", "Info") | ||||||||||
.field("close_old_findings", "true") | ||||||||||
.field("push_to_jira", "false") | ||||||||||
.field("test", testId) | ||||||||||
.field("scan_date", DATE_FORMAT.format(new Date())) | ||||||||||
.asString(); | ||||||||||
if (response.getStatus() == 201) { | ||||||||||
LOGGER.debug("Successfully reimport findings to DefectDojo"); | ||||||||||
} else { | ||||||||||
LOGGER.warn("DefectDojo Client did not receive expected response while attempting to reimport" | ||||||||||
+ "Dependency-Track findings. HTTP response code: " | ||||||||||
+ response.getStatus() + " - " + response.getStatusText()); | ||||||||||
uploader.handleUnexpectedHttpResponse(LOGGER, request.getUrl(), response.getStatus(), response.getBody()); | ||||||||||
HttpPost request = new HttpPost(baseURL + "/api/v2/reimport-scan/"); | ||||||||||
request.addHeader("accept", "application/json"); | ||||||||||
request.addHeader("Authorization", "Token " + token); | ||||||||||
InputStreamBody inputStreamBody = new InputStreamBody(findingsJson, ContentType.DEFAULT_BINARY, "findings.json"); | ||||||||||
HttpEntity fileData = MultipartEntityBuilder.create().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("true", 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("push_to_jira", 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)) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JavaUtilDate: Date has a bad API that leads to bugs; prefer java.time.Instant or LocalDate. ℹ️ Expand to see all @sonatype-lift commandsYou can reply with the following commands. For example, reply with @sonatype-lift ignoreall to leave out all findings.
Note: When talking to LiftBot, you need to refresh the page to see its response. Help us improve LIFT! (Sonatype LiftBot external survey) Was this a good recommendation for you? Answering this survey will not impact your Lift settings. [ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ] |
||||||||||
.build(); | ||||||||||
request.setEntity(fileData); | ||||||||||
try (CloseableHttpResponse response = HttpClientPool.getClient().execute(request)) { | ||||||||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) { | ||||||||||
LOGGER.debug("Successfully reimport findings to DefectDojo"); | ||||||||||
} else { | ||||||||||
uploader.handleUnexpectedHttpResponse(LOGGER, request.getURI().toString(), response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); | ||||||||||
} | ||||||||||
} catch (IOException ex) { | ||||||||||
uploader.handleException(LOGGER, ex); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JavaUtilDate: Date has a bad API that leads to bugs; prefer java.time.Instant or LocalDate.
ℹ️ Expand to see all @sonatype-lift commands
You can reply with the following commands. For example, reply with @sonatype-lift ignoreall to leave out all findings.
@sonatype-lift ignore
@sonatype-lift ignoreall
@sonatype-lift exclude <file|issue|path|tool>
file|issue|path|tool
from Lift findings by updating your config.toml fileNote: When talking to LiftBot, you need to refresh the page to see its response.
Click here to add LiftBot to another repo.
Help us improve LIFT! (Sonatype LiftBot external survey)
Was this a good recommendation for you? Answering this survey will not impact your Lift settings.
[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]