Skip to content

Commit

Permalink
Fix NPE when cloning projects with broken dependency graph
Browse files Browse the repository at this point in the history
Fixes #4413

Signed-off-by: nscuro <[email protected]>
  • Loading branch information
nscuro committed Nov 28, 2024
1 parent 10f43a6 commit e5f811f
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,19 @@ public Project clone(
String directDependencies = project.getDirectDependencies();
for (final UUID sourceComponentUuid : projectDirectDepsSourceComponentUuids) {
final UUID clonedComponentUuid = clonedComponentUuidBySourceComponentUuid.get(sourceComponentUuid);
directDependencies = directDependencies.replace(sourceComponentUuid.toString(), clonedComponentUuid.toString());
if (clonedComponentUuid != null) {
directDependencies = directDependencies.replace(
sourceComponentUuid.toString(), clonedComponentUuid.toString());
} else {
// NB: This may happen when the source project itself is a clone,
// and it was cloned before DT v4.12.0.
// https://github.com/DependencyTrack/dependency-track/pull/4171
LOGGER.warn("""
The source project's directDependencies refer to a component with UUID \
%s, which does not exist in the project. The cloned project's dependency graph \
may be broken as a result. A BOM upload will resolve the issue.\
""".formatted(sourceComponentUuid));
}
}

project.setDirectDependencies(directDependencies);
Expand All @@ -724,7 +736,16 @@ public Project clone(
String directDependencies = component.getDirectDependencies();
for (final UUID sourceComponentUuid : sourceComponentUuids) {
final UUID clonedComponentUuid = clonedComponentUuidBySourceComponentUuid.get(sourceComponentUuid);
directDependencies = directDependencies.replace(sourceComponentUuid.toString(), clonedComponentUuid.toString());
if (clonedComponentUuid != null) {
directDependencies = directDependencies.replace(
sourceComponentUuid.toString(), clonedComponentUuid.toString());
} else {
LOGGER.warn("""
The directDependencies of component %s refer to a component with UUID \
%s, which does not exist in the source project. The cloned project's dependency graph \
may be broken as a result. A BOM upload will resolve the issue.\
""".formatted(component, sourceComponentUuid));
}
}

component.setDirectDependencies(directDependencies);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1967,6 +1967,51 @@ public void cloneProjectAsLatestTest() {
});
}

@Test // https://github.com/DependencyTrack/dependency-track/issues/4413
public void cloneProjectWithBrokenDependencyGraphTest() {
EventService.getInstance().subscribe(CloneProjectEvent.class, CloneProjectTask.class);

final var project = new Project();
project.setName("acme-app");
project.setVersion("1.0.0");
project.setDirectDependencies("[{\"uuid\":\"d6b6f140-f547-4fe2-a98c-f4942ad51f86\"}]");
qm.persist(project);

final var component = new Component();
component.setProject(project);
component.setName("acme-lib");
component.setVersion("2.0.0");
component.setDirectDependencies("[{\"uuid\":\"61503628-d2a2-447b-b99c-701b9d492cbd\"}]");
qm.persist(component);

final Response response = jersey.target("%s/clone".formatted(V1_PROJECT)).request()
.header(X_API_KEY, apiKey)
.put(Entity.json(/* language=JSON */ """
{
"project": "%s",
"version": "1.1.0",
"includeComponents": true,
"includeServices": true
}
""".formatted(project.getUuid())));
assertThat(response.getStatus()).isEqualTo(200);

await("Cloning completion")
.atMost(Duration.ofSeconds(15))
.pollInterval(Duration.ofMillis(50))
.untilAsserted(() -> {
final Project clonedProject = qm.getProject("acme-app", "1.1.0");
assertThat(clonedProject).isNotNull();
});

final Project clonedProject = qm.getProject("acme-app", "1.1.0");
assertThat(clonedProject.getDirectDependencies()).isEqualTo(
"[{\"uuid\":\"d6b6f140-f547-4fe2-a98c-f4942ad51f86\"}]");

assertThat(qm.getAllComponents(clonedProject).getFirst().getDirectDependencies()).isEqualTo(
"[{\"uuid\":\"61503628-d2a2-447b-b99c-701b9d492cbd\"}]");
}

@Test // https://github.com/DependencyTrack/dependency-track/issues/3883
public void issue3883RegressionTest() {
Response response = jersey.target(V1_PROJECT)
Expand Down

0 comments on commit e5f811f

Please sign in to comment.