From 40734fd326a956fe623921b2d26d016b3c381df3 Mon Sep 17 00:00:00 2001
From: Seb Dangerfield <1449113+sedan07@users.noreply.github.com>
Date: Fri, 3 Jan 2025 11:16:15 +0000
Subject: [PATCH] Fix onlyOutdated ungrouped component filtering
Fixes: #4510
Signed-off-by: Seb Dangerfield <1449113+sedan07@users.noreply.github.com>
---
.../persistence/ComponentQueryManager.java | 2 +-
.../resources/v1/ComponentResourceTest.java | 78 +++++++++++++++++++
2 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java
index fa700e321e..9d5b542a4e 100644
--- a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java
+++ b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java
@@ -166,7 +166,7 @@ public PaginatedResult getComponents(final Project project, final boolean includ
" && !("+
" SELECT FROM org.dependencytrack.model.RepositoryMetaComponent m " +
" WHERE m.name == this.name " +
- " && m.namespace == this.group " +
+ " && (m.namespace == this.group || (m.namespace == null && this.group == null)) " +
" && m.latestVersion != this.version " +
" && this.purl.matches('pkg:' + m.repositoryType.toString().toLowerCase() + '/%') " +
" ).isEmpty()";
diff --git a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java
index ab795d3fa9..7817b84f37 100644
--- a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java
+++ b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java
@@ -121,6 +121,50 @@ private Project prepareProject() throws MalformedPackageURLException {
return project;
}
+ /**
+ * Generate a project with ungrouped dependencies
+ * @return A project with 10 dependencies:
+ * - 7 outdated dependencies
+ * - 3 recent dependencies
+ * @throws MalformedPackageURLException
+ */
+ private Project prepareProjectUngroupedComponents() throws MalformedPackageURLException {
+ final Project project = qm.createProject("Ungrouped Application", null, null, null, null, null, true, false);
+ final List directDepencencies = new ArrayList<>();
+ // Generate 10 dependencies
+ for (int i = 0; i < 10; i++) {
+ Component component = new Component();
+ component.setProject(project);
+ component.setName("component-name-"+i);
+ component.setVersion(String.valueOf(i)+".0");
+ component.setPurl(new PackageURL(RepositoryType.PYPI.toString(), null, "component-name-"+i , String.valueOf(i)+".0", null, null));
+ component = qm.createComponent(component, false);
+ // direct depencencies
+ if (i < 4) {
+ // 4 direct depencencies, 6 transitive depencencies
+ directDepencencies.add("{\"uuid\":\"" + component.getUuid() + "\"}");
+ }
+ // Recent & Outdated
+ if ((i < 7)) {
+ final var metaComponent = new RepositoryMetaComponent();
+ metaComponent.setRepositoryType(RepositoryType.PYPI);
+ metaComponent.setName("component-name-"+i);
+ metaComponent.setLatestVersion(String.valueOf(i+1)+".0");
+ metaComponent.setLastCheck(new Date());
+ qm.persist(metaComponent);
+ } else {
+ final var metaComponent = new RepositoryMetaComponent();
+ metaComponent.setRepositoryType(RepositoryType.PYPI);
+ metaComponent.setName("component-name-"+i);
+ metaComponent.setLatestVersion(String.valueOf(i)+".0");
+ metaComponent.setLastCheck(new Date());
+ qm.persist(metaComponent);
+ }
+ }
+ project.setDirectDependencies("[" + String.join(",", directDepencencies.toArray(new String[0])) + "]");
+ return project;
+ }
+
@Test
public void getOutdatedComponentsTest() throws MalformedPackageURLException {
final Project project = prepareProject();
@@ -138,6 +182,23 @@ public void getOutdatedComponentsTest() throws MalformedPackageURLException {
assertThat(json).hasSize(100); // Default page size is 100
}
+ @Test
+ public void getUngroupedOutdatedComponentsTest() throws MalformedPackageURLException {
+ final Project project = prepareProjectUngroupedComponents();
+
+ final Response response = jersey.target(V1_COMPONENT + "/project/" + project.getUuid())
+ .queryParam("onlyOutdated", true)
+ .queryParam("onlyDirect", false)
+ .request()
+ .header(X_API_KEY, apiKey)
+ .get(Response.class);
+ assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);
+ assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("7"); // 7 outdated dependencies, direct and transitive
+
+ final JsonArray json = parseJsonArray(response);
+ assertThat(json).hasSize(7);
+ }
+
@Test
public void getOutdatedDirectComponentsTest() throws MalformedPackageURLException {
final Project project = prepareProject();
@@ -155,6 +216,23 @@ public void getOutdatedDirectComponentsTest() throws MalformedPackageURLExceptio
assertThat(json).hasSize(75);
}
+ @Test
+ public void getUngroupedOutdatedDirectComponentsTest() throws MalformedPackageURLException {
+ final Project project = prepareProjectUngroupedComponents();
+
+ final Response response = jersey.target(V1_COMPONENT + "/project/" + project.getUuid())
+ .queryParam("onlyOutdated", true)
+ .queryParam("onlyDirect", true)
+ .request()
+ .header(X_API_KEY, apiKey)
+ .get(Response.class);
+ assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);
+ assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("3"); // 3 outdated dependencies
+
+ final JsonArray json = parseJsonArray(response);
+ assertThat(json).hasSize(3);
+ }
+
@Test
public void getAllComponentsTest() throws MalformedPackageURLException {
final Project project = prepareProject();