Skip to content

Commit

Permalink
Merge pull request #2723 from nscuro/backport-to-4.8.1
Browse files Browse the repository at this point in the history
Backport fixes to v4.8.1
  • Loading branch information
nscuro authored May 16, 2023
2 parents fa5d2c7 + 61b29e3 commit ae2d9cb
Show file tree
Hide file tree
Showing 21 changed files with 1,834 additions and 234 deletions.
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/defect-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ body:
options:
- 4.7.x
- 4.8.0
- 4.8.1
- 4.9.0-SNAPSHOT
validations:
required: true
Expand Down
6 changes: 6 additions & 0 deletions docs/_docs/getting-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,12 @@ alpine.metrics.auth.password=
# If enabled, alpine.oidc.* properties should be set accordingly.
alpine.oidc.enabled=false

# Optional
# Defines the client ID to be used for OpenID Connect.
# The client ID should be the same as the one configured for the frontend,
# and will only be used to validate ID tokens.
alpine.oidc.client.id=

# Optional
# Defines the issuer URL to be used for OpenID Connect.
# This issuer MUST support provider configuration via the /.well-known/openid-configuration endpoint.
Expand Down
77 changes: 77 additions & 0 deletions docs/_posts/2023-05-16-v4.8.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: v4.8.1
type: patch
---

**Fixes:**

* Fix unrelated vulnerabilities being correlated during alias synchronization - [apiserver/#2194]
* Fix `NullPointerException` when email alert is configured with just teams as destination - [apiserver/#2698]
* Fix broken pagination in DefectDojo integration - [apiserver/#2707]
* Fix search function in policy violation tab not working - [apiserver/#2622]
* Fix `PATCH /api/v1/project` endpoint not updating external references - [apiserver/#2695]
* Fix `NullPointerException` in DefectDojo integration - [apiserver/#2628]
* Fix retrieval of OIDC JWK sets not respecting HTTP proxy settings - [apiserver/#2696]
* Lower log level for repository meta analyzer to `WARN` and include exception details - [apiserver/#2697]
* Add missing config docs for `alpine.oidc.client.id` - [apiserver/#2743]
* Fix not all vulnerability aliases being displayed in the UI - [frontend/#477]
* Fix broken vulnerability alias links - [frontend/#486]
* Fix broken project tag links on tabs other than "Overview" - [frontend/#483]
* Fix broken project version links on tabs other than "Overview" - [frontend/#495]

For a complete list of changes, refer to the respective GitHub milestones:

* [API server milestone 4.8.1](https://github.com/DependencyTrack/dependency-track/milestone/32?closed=1)
* [Frontend milestone 4.8.1](https://github.com/DependencyTrack/frontend/milestone/15?closed=1)

We thank all organizations and individuals who contributed to this release, from logging issues to taking part in
discussions on GitHub & Slack to testing of fixes.

Special thanks to everyone who contributed code to fix defects:
[@heubeck], [@jakubrak], [@sahibamittal], [@valentijnscholten]

###### dependency-track-apiserver.jar

| Algorithm | Checksum |
|:----------|:---------|
| SHA-1 | |
| SHA-256 | |

###### dependency-track-bundled.jar

| Algorithm | Checksum |
|:----------|:---------|
| SHA-1 | |
| SHA-256 | |

###### frontend-dist.zip

| Algorithm | Checksum |
|:----------|:-----------------------------------------------------------------|
| SHA-1 | 01bc042e1f510e089b9db937852dbcde69eca603 |
| SHA-256 | f946994c0f66647bd34c9e10997f2b62c08ab17ebbfe42edf149be12a47b2278 |

###### Software Bill of Materials (SBOM)

* API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.1/bom.json)
* Frontend: [bom.json](https://github.com/DependencyTrack/frontend/releases/download/4.8.1/bom.json)

[apiserver/#2194]: https://github.com/DependencyTrack/dependency-track/issues/2194
[apiserver/#2622]: https://github.com/DependencyTrack/dependency-track/issues/2622
[apiserver/#2628]: https://github.com/DependencyTrack/dependency-track/issues/2628
[apiserver/#2695]: https://github.com/DependencyTrack/dependency-track/issues/2695
[apiserver/#2696]: https://github.com/DependencyTrack/dependency-track/issues/2696
[apiserver/#2697]: https://github.com/DependencyTrack/dependency-track/pull/2697
[apiserver/#2698]: https://github.com/DependencyTrack/dependency-track/issues/2698
[apiserver/#2707]: https://github.com/DependencyTrack/dependency-track/issues/2707
[apiserver/#2743]: https://github.com/DependencyTrack/dependency-track/pull/2743

[frontend/#477]: https://github.com/DependencyTrack/frontend/issues/477
[frontend/#483]: https://github.com/DependencyTrack/frontend/issues/483
[frontend/#486]: https://github.com/DependencyTrack/frontend/issues/486
[frontend/#495]: https://github.com/DependencyTrack/frontend/issues/495

[@heubeck]: https://github.com/heubeck
[@jakubrak]: https://github.com/jakubrak
[@sahibamittal]: https://github.com/sahibamittal
[@valentijnscholten]: https://github.com/valentijnscholten
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<parent>
<groupId>us.springett</groupId>
<artifactId>alpine-parent</artifactId>
<version>2.2.1</version>
<version>2.2.2</version>
</parent>

<modelVersion>4.0.0</modelVersion>
Expand Down Expand Up @@ -81,7 +81,7 @@

<properties>
<!-- Dependency Versions -->
<frontend.version>4.8.0</frontend.version>
<frontend.version>4.8.1</frontend.version>
<lib.alpine.version>${project.parent.version}</lib.alpine.version>
<lib.cpe-parser.version>2.0.2</lib.cpe-parser.version>
<lib.cvss-calculator.version>1.4.1</lib.cvss-calculator.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.dependencytrack.integrations.defectdojo;

import alpine.common.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
Expand Down Expand Up @@ -69,7 +70,7 @@ public void uploadDependencyTrackFindings(final String token, final String engag
.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("push_to_jira", new StringBody("false", ContentType.MULTIPART_FORM_DATA))
.addPart("scan_date", new StringBody(DATE_FORMAT.format(new Date()), ContentType.MULTIPART_FORM_DATA))
.build();
request.setEntity(data);
Expand All @@ -87,7 +88,7 @@ public void uploadDependencyTrackFindings(final String token, final String engag
}

// 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) {
public ArrayList<String> getDojoTestIds(final String token, final String eid) {
LOGGER.debug("Pulling DefectDojo Tests API ...");
String testsUri = "/api/v2/tests/";
LOGGER.debug("Make the first pagination call");
Expand All @@ -104,22 +105,22 @@ public ArrayList getDojoTestIds(final String token, final String eid) {
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();
ArrayList<String> dojoTests = jsonToList(dojoArray);
while (StringUtils.isNotBlank(dojoObj.optString("next"))) {
final String nextUrl = dojoObj.getString("next");
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));
dojoArray = dojoObj.optJSONArray("results");
if (dojoArray != null) {
dojoTests.addAll(jsonToList(dojoArray));
}
}
LOGGER.debug("Successfully retrieved the test list ");
return dojoTests;
Expand All @@ -136,21 +137,20 @@ public ArrayList getDojoTestIds(final String token, final String eid) {
}

// Given the engagement id and scan type, search for existing test id
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")) {
return dojoTest.get("id").toString();
public String getDojoTestId(final String engagementID, final ArrayList<String> dojoTests) {
for (final String dojoTestJson : dojoTests) {
JSONObject dojoTest = new JSONObject(dojoTestJson);
if (dojoTest.optString("engagement").equals(engagementID) &&
dojoTest.optString("scan_type").equals("Dependency Track Finding Packaging Format (FPF) Export")) {
return dojoTest.optString("id");
}
}
return "";
}

// JSONArray to ArrayList simple converter
public ArrayList<String> jsonToList(final JSONArray jsonArray) {
ArrayList<String> list = new ArrayList<String>();
ArrayList<String> list = new ArrayList<>();
if (jsonArray != null) {
for (int i = 0; i < jsonArray.length(); i++) {
list.add(jsonArray.get(i).toString());
Expand All @@ -177,7 +177,7 @@ public void reimportDependencyTrackFindings(final String token, final String eng
.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("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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ public InputStream process(final Project project, final List<Finding> findings)
public void upload(final Project project, final InputStream payload) {
final ConfigProperty defectDojoUrl = qm.getConfigProperty(DEFECTDOJO_URL.getGroupName(), DEFECTDOJO_URL.getPropertyName());
final ConfigProperty apiKey = qm.getConfigProperty(DEFECTDOJO_API_KEY.getGroupName(), DEFECTDOJO_API_KEY.getPropertyName());
final ConfigProperty globalReimportEnabled = qm.getConfigProperty(DEFECTDOJO_REIMPORT_ENABLED.getGroupName(), DEFECTDOJO_REIMPORT_ENABLED.getPropertyName());
final boolean globalReimportEnabled = qm.isEnabled(DEFECTDOJO_REIMPORT_ENABLED);
final ProjectProperty engagementId = qm.getProjectProperty(project, DEFECTDOJO_ENABLED.getGroupName(), ENGAGEMENTID_PROPERTY);
try {
final DefectDojoClient client = new DefectDojoClient(this, new URL(defectDojoUrl.getPropertyValue()));
final ArrayList testsIds = client.getDojoTestIds(apiKey.getPropertyValue(), engagementId.getPropertyValue());
final String testId = client.getDojoTestId(engagementId.getPropertyValue(), testsIds);
if (isReimportConfigured(project) || Boolean.parseBoolean(globalReimportEnabled.getPropertyValue())) {
if (isReimportConfigured(project) || globalReimportEnabled) {
final ArrayList<String> testsIds = client.getDojoTestIds(apiKey.getPropertyValue(), engagementId.getPropertyValue());
final String testId = client.getDojoTestId(engagementId.getPropertyValue(), testsIds);
LOGGER.debug("Found existing test Id: " + testId);
if (testId.equals("")) {
client.uploadDependencyTrackFindings(apiKey.getPropertyValue(), engagementId.getPropertyValue(), payload);
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/org/dependencytrack/model/VulnerabilityAlias.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,57 @@ private static String firstNonNull(String first, String second) {
return first != null ? first : second;
}

/**
* Compute how many vulnerability identifiers of this {@link VulnerabilityAlias}
* match with those of a given other {@link VulnerabilityAlias}.
* <p>
* Identifiers are considered to be matches when they are equal but not {@code null}.
*
* @param other The {@link VulnerabilityAlias} to compute matches with
* @return Number of matching identifiers
*/
public int computeMatches(final VulnerabilityAlias other) {
var matches = 0;

if (this.getCveId() != null && this.getCveId().equals(other.getCveId())) {
matches++;
}
if (this.getGhsaId() != null && this.getGhsaId().equals(other.getGhsaId())) {
matches++;
}
if (this.getGsdId() != null && this.getGsdId().equals(other.getGsdId())) {
matches++;
}
if (this.getOsvId() != null && this.getOsvId().equals(other.getOsvId())) {
matches++;
}
if (this.getSnykId() != null && this.getSnykId().equals(other.getSnykId())) {
matches++;
}
if (this.getSonatypeId() != null && this.getSonatypeId().equals(other.getSonatypeId())) {
matches++;
}
if (this.getVulnDbId() != null && this.getVulnDbId().equals(other.getVulnDbId())) {
matches++;
}

return matches;
}

@Override
public String toString() {
return "VulnerabilityAlias{" +
"id=" + id +
", internalId='" + internalId + '\'' +
", cveId='" + cveId + '\'' +
", ghsaId='" + ghsaId + '\'' +
", sonatypeId='" + sonatypeId + '\'' +
", osvId='" + osvId + '\'' +
", snykId='" + snykId + '\'' +
", gsdId='" + gsdId + '\'' +
", vulnDbId='" + vulnDbId + '\'' +
", uuid=" + uuid +
'}';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.dependencytrack.persistence.QueryManager;

import javax.json.JsonObject;
import javax.json.JsonString;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -122,17 +123,21 @@ public PebbleEngine getTemplateEngine() {
}

static String[] parseDestination(final JsonObject config) {
String destinationString = config.getString("destination");
if ((destinationString == null) || destinationString.isEmpty()) {
JsonString destinationString = config.getJsonString("destination");
if ((destinationString == null) || destinationString.getString().isEmpty()) {
return null;
}
return destinationString.split(",");
return destinationString.getString().split(",");
}

static String[] parseDestination(final JsonObject config, final List<Team> teams) {
String[] destination = teams.stream().flatMap(
team -> Stream.of(
Arrays.stream(config.getString("destination").split(",")).filter(Predicate.not(String::isEmpty)),
Optional.ofNullable(config.getJsonString("destination"))
.map(JsonString::getString)
.stream()
.flatMap(dest -> Arrays.stream(dest.split(",")))
.filter(Predicate.not(String::isEmpty)),
Optional.ofNullable(team.getManagedUsers()).orElseGet(Collections::emptyList).stream().map(ManagedUser::getEmail).filter(Objects::nonNull),
Optional.ofNullable(team.getLdapUsers()).orElseGet(Collections::emptyList).stream().map(LdapUser::getEmail).filter(Objects::nonNull),
Optional.ofNullable(team.getOidcUsers()).orElseGet(Collections::emptyList).stream().map(OidcUser::getEmail).filter(Objects::nonNull)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,16 +268,19 @@ public List<PolicyViolation> getAllPolicyViolations(final Project project) {
*/
@SuppressWarnings("unchecked")
public PaginatedResult getPolicyViolations(final Project project, boolean includeSuppressed) {
PaginatedResult result;
final String projectFilter = includeSuppressed ? "project.id == :pid" : "project.id == :pid && (analysis.suppressed == false || analysis.suppressed == null)";
final Query<PolicyViolation> query = pm.newQuery(PolicyViolation.class);
if (includeSuppressed) {
query.setFilter("project.id == :pid");
} else {
query.setFilter("project.id == :pid && (analysis.suppressed == false || analysis.suppressed == null)");
}
if (orderBy == null) {
query.setOrdering("timestamp desc, component.name, component.version");
}
final PaginatedResult result = execute(query, project.getId());
if (filter != null) {
query.setFilter(projectFilter + " && (policyCondition.policy.name.toLowerCase().matches(:filter) || component.name.toLowerCase().matches(:filter))");
final String filterString = ".*" + filter.toLowerCase() + ".*";
result = execute(query, project.getId(), filterString);
} else {
result = execute(query, project.getId());
}
for (final PolicyViolation violation: result.getList(PolicyViolation.class)) {
violation.getPolicyCondition().getPolicy(); // force policy to ne included since its not the default
violation.getComponent().getResolvedLicense(); // force resolved license to ne included since its not the default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ public Project updateProject(Project transientProject, boolean commitIndex) {
project.setCpe(transientProject.getCpe());
project.setPurl(transientProject.getPurl());
project.setSwidTagId(transientProject.getSwidTagId());
project.setExternalReferences(transientProject.getExternalReferences());

if (Boolean.TRUE.equals(project.isActive()) && !Boolean.TRUE.equals(transientProject.isActive()) && hasActiveChild(project)){
throw new IllegalArgumentException("Project cannot be set to inactive if active children are present.");
Expand Down
Loading

0 comments on commit ae2d9cb

Please sign in to comment.