Skip to content

Commit 45982a2

Browse files
authored
Merge pull request DependencyTrack#4458 from nscuro/issue-4455
2 parents c777e79 + 306e68b commit 45982a2

File tree

3 files changed

+1248
-12
lines changed

3 files changed

+1248
-12
lines changed

src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,9 +654,19 @@ private String resolveDirectDependenciesJson(
654654
}
655655

656656
final var jsonDependencies = new JSONArray();
657+
final var directDependencyIdentitiesSeen = new HashSet<ComponentIdentity>();
657658
for (final String directDependencyBomRef : directDependencyBomRefs) {
658659
final ComponentIdentity directDependencyIdentity = identitiesByBomRef.get(directDependencyBomRef);
659660
if (directDependencyIdentity != null) {
661+
if (!directDependencyIdentitiesSeen.add(directDependencyIdentity)) {
662+
// It's possible that multiple direct dependencies of a project or component
663+
// fall victim to de-duplication. In that case, we can ironically end up with
664+
// duplicate component identities (i.e. duplicate BOM refs).
665+
LOGGER.debug("Omitting duplicate direct dependency %s for BOM ref %s"
666+
.formatted(directDependencyBomRef, dependencyBomRef));
667+
continue;
668+
}
669+
660670
jsonDependencies.put(directDependencyIdentity.toJSON());
661671
} else {
662672
LOGGER.warn("""

src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,21 @@
5050
import org.dependencytrack.parser.spdx.json.SpdxLicenseDetailParser;
5151
import org.dependencytrack.search.document.ComponentDocument;
5252
import org.junit.After;
53+
import org.junit.Assert;
5354
import org.junit.Before;
5455
import org.junit.Test;
5556

57+
import jakarta.json.Json;
58+
import jakarta.json.JsonArray;
59+
import jakarta.json.JsonObject;
60+
import jakarta.json.JsonReader;
5661
import javax.jdo.JDOObjectNotFoundException;
62+
import java.io.StringReader;
5763
import java.nio.charset.StandardCharsets;
5864
import java.time.Duration;
5965
import java.util.ArrayList;
6066
import java.util.Arrays;
67+
import java.util.HashSet;
6168
import java.util.List;
6269
import java.util.Objects;
6370
import java.util.Optional;
@@ -1440,23 +1447,74 @@ public void informIssue3981Test() {
14401447
}
14411448

14421449
@Test
1443-
public void informIssue3936Test() throws Exception{
1450+
public void informIssue3936Test() throws Exception {
14441451
final Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false);
14451452
List<String> boms = new ArrayList<>(Arrays.asList("/unit/bom-issue3936-authors.json", "/unit/bom-issue3936-author.json", "/unit/bom-issue3936-both.json"));
1446-
for(String bom : boms){
1447-
final var bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, project.getId()),
1448-
resourceToByteArray(bom));
1449-
new BomUploadProcessingTask().inform(bomUploadEvent);
1450-
awaitBomProcessedNotification(bomUploadEvent);
1451-
1452-
assertThat(qm.getAllComponents(project)).isNotEmpty();
1453-
Component component = qm.getAllComponents().getFirst();
1454-
assertThat(component.getAuthor()).isEqualTo("Joane Doe et al.");
1455-
assertThat(component.getAuthors().get(0).getName()).isEqualTo("Joane Doe et al.");
1456-
assertThat(component.getAuthors().size()).isEqualTo(1);
1453+
for (String bom : boms) {
1454+
final var bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, project.getId()),
1455+
resourceToByteArray(bom));
1456+
new BomUploadProcessingTask().inform(bomUploadEvent);
1457+
awaitBomProcessedNotification(bomUploadEvent);
1458+
1459+
assertThat(qm.getAllComponents(project)).isNotEmpty();
1460+
Component component = qm.getAllComponents().getFirst();
1461+
assertThat(component.getAuthor()).isEqualTo("Joane Doe et al.");
1462+
assertThat(component.getAuthors().get(0).getName()).isEqualTo("Joane Doe et al.");
1463+
assertThat(component.getAuthors().size()).isEqualTo(1);
14571464
}
14581465
}
14591466

1467+
@Test
1468+
public void informIssue4455Test() throws Exception {
1469+
final var project = new Project();
1470+
project.setName("acme-app");
1471+
project.setVersion("1.2.3");
1472+
qm.persist(project);
1473+
1474+
var bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, project.getId()),
1475+
resourceToByteArray("/unit/bom-issue4455.json"));
1476+
new BomUploadProcessingTask().inform(bomUploadEvent);
1477+
awaitBomProcessedNotification(bomUploadEvent);
1478+
1479+
qm.getPersistenceManager().refresh(project);
1480+
assertThat(project.getDirectDependencies()).satisfies(directDependenciesJson -> {
1481+
final JsonReader jsonReader = Json.createReader(
1482+
new StringReader(directDependenciesJson));
1483+
final JsonArray directDependenciesArray = jsonReader.readArray();
1484+
1485+
final var uuidsSeen = new HashSet<String>();
1486+
for (int i = 0; i < directDependenciesArray.size(); i++) {
1487+
final JsonObject directDependencyObject = directDependenciesArray.getJsonObject(i);
1488+
final String directDependencyUuid = directDependencyObject.getString("uuid");
1489+
if (!uuidsSeen.add(directDependencyUuid)) {
1490+
Assert.fail("Duplicate UUID %s in project's directDependencies: %s".formatted(
1491+
directDependencyUuid, directDependenciesJson));
1492+
}
1493+
}
1494+
});
1495+
1496+
final List<Component> components = qm.getAllComponents(project);
1497+
assertThat(components).allSatisfy(component -> {
1498+
if (component.getDirectDependencies() == null) {
1499+
return;
1500+
}
1501+
1502+
final JsonReader jsonReader = Json.createReader(
1503+
new StringReader(component.getDirectDependencies()));
1504+
final JsonArray directDependenciesArray = jsonReader.readArray();
1505+
1506+
final var uuidsSeen = new HashSet<String>();
1507+
for (int i = 0; i < directDependenciesArray.size(); i++) {
1508+
final JsonObject directDependencyObject = directDependenciesArray.getJsonObject(i);
1509+
final String directDependencyUuid = directDependencyObject.getString("uuid");
1510+
if (!uuidsSeen.add(directDependencyUuid)) {
1511+
Assert.fail("Duplicate UUID %s in component's directDependencies: %s".formatted(
1512+
directDependencyUuid, component.getDirectDependencies()));
1513+
}
1514+
}
1515+
});
1516+
}
1517+
14601518
private void awaitBomProcessedNotification(final BomUploadEvent bomUploadEvent) {
14611519
try {
14621520
await("BOM Processed Notification")

0 commit comments

Comments
 (0)