Skip to content

Commit 006b5d2

Browse files
authored
Merge pull request #381 from DependencyTrack/port-pr3067
Vex resource test
2 parents 2ab3c63 + ba3f191 commit 006b5d2

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

src/test/java/org/dependencytrack/ResourceTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public abstract class ResourceTest extends JerseyTest {
7474
protected final String V1_SEARCH = "/v1/search";
7575
protected final String V1_TEAM = "/v1/team";
7676
protected final String V1_USER = "/v1/user";
77+
protected final String V1_VEX = "/v1/vex";
7778
protected final String V1_VIOLATION_ANALYSIS = "/v1/violation/analysis";
7879
protected final String V1_VULNERABILITY = "/v1/vulnerability";
7980
protected final String V1_WORKFLOW = "/v1/workflow";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/*
2+
* This file is part of Dependency-Track.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* Copyright (c) Steve Springett. All Rights Reserved.
18+
*/
19+
package org.dependencytrack.resources.v1;
20+
21+
import alpine.server.filters.ApiFilter;
22+
import alpine.server.filters.AuthenticationFilter;
23+
import org.dependencytrack.ResourceTest;
24+
import org.dependencytrack.model.AnalysisResponse;
25+
import org.dependencytrack.model.AnalysisState;
26+
import org.dependencytrack.model.AnalyzerIdentity;
27+
import org.dependencytrack.model.Classifier;
28+
import org.dependencytrack.model.Component;
29+
import org.dependencytrack.model.Project;
30+
import org.dependencytrack.model.Severity;
31+
import org.dependencytrack.model.Vulnerability;
32+
import org.glassfish.jersey.media.multipart.MultiPartFeature;
33+
import org.glassfish.jersey.server.ResourceConfig;
34+
import org.glassfish.jersey.servlet.ServletContainer;
35+
import org.glassfish.jersey.test.DeploymentContext;
36+
import org.glassfish.jersey.test.ServletDeploymentContext;
37+
import org.junit.Test;
38+
39+
import javax.ws.rs.core.Response;
40+
41+
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
42+
import static org.assertj.core.api.Assertions.assertThat;
43+
import static org.hamcrest.CoreMatchers.equalTo;
44+
45+
public class VexResourceTest extends ResourceTest {
46+
47+
@Override
48+
protected DeploymentContext configureDeployment() {
49+
return ServletDeploymentContext.forServlet(new ServletContainer(
50+
new ResourceConfig(VexResource.class)
51+
.register(ApiFilter.class)
52+
.register(AuthenticationFilter.class)
53+
.register(MultiPartFeature.class)))
54+
.build();
55+
}
56+
57+
@Test
58+
public void exportProjectAsCycloneDxTest() {
59+
var vulnA = new Vulnerability();
60+
vulnA.setVulnId("INT-001");
61+
vulnA.setSource(Vulnerability.Source.INTERNAL);
62+
vulnA.setSeverity(Severity.HIGH);
63+
vulnA = qm.createVulnerability(vulnA, false);
64+
65+
var vulnB = new Vulnerability();
66+
vulnB.setVulnId("INT-002");
67+
vulnB.setSource(Vulnerability.Source.INTERNAL);
68+
vulnB.setSeverity(Severity.LOW);
69+
vulnB = qm.createVulnerability(vulnB, false);
70+
71+
final var project = new Project();
72+
project.setName("acme-app");
73+
project.setVersion("1.0.0");
74+
project.setClassifier(Classifier.APPLICATION);
75+
qm.persist(project);
76+
77+
var componentWithoutVuln = new Component();
78+
componentWithoutVuln.setProject(project);
79+
componentWithoutVuln.setName("acme-lib-a");
80+
componentWithoutVuln.setVersion("1.0.0");
81+
componentWithoutVuln.setDirectDependencies("[]");
82+
componentWithoutVuln = qm.createComponent(componentWithoutVuln, false);
83+
84+
var componentWithVuln = new Component();
85+
componentWithVuln.setProject(project);
86+
componentWithVuln.setName("acme-lib-b");
87+
componentWithVuln.setVersion("1.0.0");
88+
componentWithVuln.setDirectDependencies("[]");
89+
componentWithVuln = qm.createComponent(componentWithVuln, false);
90+
qm.addVulnerability(vulnA, componentWithVuln, AnalyzerIdentity.INTERNAL_ANALYZER);
91+
92+
var componentWithVulnAndAnalysis = new Component();
93+
componentWithVulnAndAnalysis.setProject(project);
94+
componentWithVulnAndAnalysis.setName("acme-lib-c");
95+
componentWithVulnAndAnalysis.setVersion("1.0.0");
96+
componentWithVulnAndAnalysis.setDirectDependencies("[]");
97+
componentWithVulnAndAnalysis = qm.createComponent(componentWithVulnAndAnalysis, false);
98+
qm.addVulnerability(vulnB, componentWithVulnAndAnalysis, AnalyzerIdentity.INTERNAL_ANALYZER);
99+
qm.makeAnalysis(componentWithVulnAndAnalysis, vulnB, AnalysisState.RESOLVED, null, AnalysisResponse.UPDATE, null, true);
100+
101+
// Make componentWithoutVuln (acme-lib-a) depend on componentWithVuln (acme-lib-b)
102+
componentWithoutVuln.setDirectDependencies("""
103+
[
104+
{"uuid": "%s"}
105+
]
106+
""".formatted(componentWithVuln.getUuid()));
107+
108+
// Make project depend on componentWithoutVuln (acme-lib-a)
109+
// and componentWithVulnAndAnalysis (acme-lib-c)
110+
project.setDirectDependencies("""
111+
[
112+
{"uuid": "%s"},
113+
{"uuid": "%s"}
114+
]
115+
"""
116+
.formatted(
117+
componentWithoutVuln.getUuid(),
118+
componentWithVulnAndAnalysis.getUuid()
119+
));
120+
qm.persist(project);
121+
122+
final Response response = target("%s/cyclonedx/project/%s".formatted(V1_VEX, project.getUuid()))
123+
.request()
124+
.header(X_API_KEY, apiKey)
125+
.get(Response.class);
126+
assertThat(response.getStatus()).isEqualTo(200);
127+
assertThatJson(getPlainTextBody(response))
128+
.withMatcher("vulnAUuid", equalTo(vulnA.getUuid().toString()))
129+
.withMatcher("vulnBUuid", equalTo(vulnB.getUuid().toString()))
130+
.withMatcher("projectUuid", equalTo(project.getUuid().toString()))
131+
.isEqualTo("""
132+
{
133+
"bomFormat": "CycloneDX",
134+
"specVersion": "1.4",
135+
"serialNumber": "${json-unit.any-string}",
136+
"version": 1,
137+
"metadata": {
138+
"timestamp": "${json-unit.any-string}",
139+
"component": {
140+
"type": "application",
141+
"bom-ref": "${json-unit.matches:projectUuid}",
142+
"name": "acme-app",
143+
"version": "1.0.0"
144+
},
145+
"tools": [
146+
{
147+
"vendor": "OWASP",
148+
"name": "Dependency-Track",
149+
"version": "${json-unit.any-string}"
150+
}
151+
]
152+
},
153+
"vulnerabilities": [
154+
{
155+
"bom-ref": "${json-unit.matches:vulnAUuid}",
156+
"id": "INT-001",
157+
"source": {
158+
"name": "INTERNAL"
159+
},
160+
"ratings": [
161+
{
162+
"source": {
163+
"name": "INTERNAL"
164+
},
165+
"severity": "high",
166+
"method": "other"
167+
}
168+
],
169+
"affects": [
170+
{
171+
"ref": "${json-unit.matches:projectUuid}"
172+
}
173+
]
174+
},
175+
{
176+
"bom-ref": "${json-unit.matches:vulnBUuid}",
177+
"id": "INT-002",
178+
"source": {
179+
"name": "INTERNAL"
180+
},
181+
"ratings": [
182+
{
183+
"source": {
184+
"name": "INTERNAL"
185+
},
186+
"severity": "low",
187+
"method": "other"
188+
}
189+
],
190+
"analysis":{
191+
"state": "resolved",
192+
"response": [
193+
"update"
194+
]
195+
},
196+
"affects": [
197+
{
198+
"ref": "${json-unit.matches:projectUuid}"
199+
}
200+
]
201+
}
202+
]
203+
}
204+
""");
205+
}
206+
207+
}

0 commit comments

Comments
 (0)