Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ properties:
maxLength: 255
internal:
type: boolean
direct:
type: boolean
copyright:
type: string
maxLength: 255
Expand Down
16 changes: 16 additions & 0 deletions apiserver/src/main/java/org/dependencytrack/model/Component.java
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,11 @@ public enum FetchGroup {
@NotNull
private UUID uuid;

@Persistent
@Column(name = "DIRECT", allowsNull = "true")
@JsonProperty("isDirect")
private Boolean direct;
Comment on lines +429 to +432
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we disambiguate the name? e.g. call it directDependency / isDirectDependency.


private transient String bomRef;
private transient List<org.cyclonedx.model.License> licenseCandidates;
private transient DependencyMetrics metrics;
Expand Down Expand Up @@ -955,6 +960,17 @@ public void setOccurrenceCount(final Long occurrenceCount) {
this.occurrenceCount = occurrenceCount;
}

public boolean isDirect() {
if (direct == null) {
return false;
}
return direct;
}

public void setDirect(boolean direct) {
this.direct = direct;
}

@Override
public String toString() {
if (getPurl() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public static org.dependencytrack.proto.policy.v1.Component mapToProto(final Com
maybeSet(component::getPurl, purl -> protoBuilder.setPurl(purl.canonicalize()));
maybeSet(component::getSwidTagId, protoBuilder::setSwidTagId);
maybeSet(component::isInternal, protoBuilder::setIsInternal);
maybeSet(component::isDirect, protoBuilder::setIsDirect);
maybeSet(component::getMd5, protoBuilder::setMd5);
maybeSet(component::getSha1, protoBuilder::setSha1);
maybeSet(component::getSha256, protoBuilder::setSha256);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ AND EXISTS (
if (onlyDirect) {
queryString +=
"""
AND "B0"."DIRECT_DEPENDENCIES" @> JSONB_BUILD_ARRAY(JSONB_BUILD_OBJECT('uuid', "A0"."UUID"))
AND "A0"."DIRECT" = TRUE
""";
}
if (orderBy == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,8 @@ default Page<Component> listProjectComponents(ListProjectComponentsQuery query)
}
if (Boolean.TRUE.equals(query.onlyDirect())) {
whereConditions.add(/* language=SQL */ """
EXISTS (
SELECT 1
FROM "PROJECT"
WHERE "PROJECT"."ID" = "C"."PROJECT_ID"
AND "PROJECT"."DIRECT_DEPENDENCIES" @> JSONB_BUILD_ARRAY(JSONB_BUILD_OBJECT('uuid', "C"."UUID"))
)""");
AND "C"."DIRECT" IS TRUE
""");
Comment on lines 124 to +126
}
if (query.searchText() != null && !query.searchText().isBlank()) {
whereConditions.add(/* language=SQL */ """
Expand Down Expand Up @@ -235,6 +231,7 @@ default Page<Component> listProjectComponents(ListProjectComponentsQuery query)
, "C"."PURL"
, "C"."GROUP"
, "C"."INTERNAL"
, "C"."DIRECT"
, "C"."LAST_RISKSCORE"
, "C"."LICENSE" AS "componentLicenseName"
, "C"."LICENSE_EXPRESSION" AS "licenseExpression"
Expand Down Expand Up @@ -630,4 +627,14 @@ public ListedComponent map(final ResultSet rs, final StatementContext ctx) throw
}
}

@SqlUpdate("""
UPDATE "COMPONENT"
SET "DIRECT" = ("UUID"::TEXT IN (
SELECT JSONB_ARRAY_ELEMENTS(
COALESCE("DIRECT_DEPENDENCIES", '[]'::jsonb)) ->> 'uuid'
FROM "PROJECT" WHERE "ID" = :projectId
))
WHERE "PROJECT_ID" = :projectId
""")
void setDirect(@Bind Long projectId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public Component map(final ResultSet rs, final StatementContext ctx) throws SQLE
maybeSet(rs, "purl", ResultSet::getString, builder::setPurl);
maybeSet(rs, "swid_tag_id", ResultSet::getString, builder::setSwidTagId);
maybeSet(rs, "is_internal", ResultSet::getBoolean, builder::setIsInternal);
maybeSet(rs, "is_direct", ResultSet::getBoolean, builder::setIsDirect);
maybeSet(rs, "md5", ResultSet::getString, builder::setMd5);
maybeSet(rs, "sha1", ResultSet::getString, builder::setSha1);
maybeSet(rs, "sha256", ResultSet::getString, builder::setSha256);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,14 +361,9 @@ AND EXISTS (
public boolean isDirectDependency(Component component) {
return jdbiHandle
.createQuery("""
SELECT EXISTS (
SELECT 1
FROM "COMPONENT" AS c
INNER JOIN "PROJECT" AS p
ON p."ID" = c."PROJECT_ID"
AND p."DIRECT_DEPENDENCIES" @> JSONB_BUILD_ARRAY(JSONB_BUILD_OBJECT('uuid', :uuid))
WHERE c."UUID" = CAST(:uuid AS UUID)
)
SELECT COALESCE(c."DIRECT", FALSE)
FROM "COMPONENT" c
WHERE c."UUID" = CAST(:uuid AS UUID)
Comment on lines +364 to +366
""")
.bind("uuid", component.getUuid())
.mapTo(Boolean.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public record FieldMapping(String protoFieldName, String sqlExpression) {
new FieldMapping("purl", "c.\"PURL\""),
new FieldMapping("swid_tag_id", "c.\"SWIDTAGID\""),
new FieldMapping("is_internal", "c.\"INTERNAL\""),
new FieldMapping("is_direct", "c.\"DIRECT\""),
new FieldMapping("md5", "c.\"MD5\""),
new FieldMapping("sha1", "c.\"SHA1\""),
new FieldMapping("sha256", "c.\"SHA_256\""),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ public Response listProjectComponents(
.cpe(componentRow.getCpe())
.group(componentRow.getGroup())
.internal(componentRow.isInternal())
.direct(componentRow.isDirect())
.lastInheritedRiskScore(componentRow.getLastInheritedRiskScore())
.license(componentRow.getLicense())
.licenseExpression(componentRow.getLicenseExpression())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@
import org.dependencytrack.notification.JdoNotificationEmitter;
import org.dependencytrack.notification.NotificationModelConverter;
import org.dependencytrack.persistence.QueryManager;
import org.dependencytrack.persistence.jdbi.ComponentDao;
import org.dependencytrack.pkgmetadata.ResolvePackageMetadataWorkflow;
import org.dependencytrack.proto.internal.workflow.v1.AnalyzeProjectWorkflowArg;
import org.dependencytrack.proto.internal.workflow.v1.ImportBomArg;
import org.dependencytrack.proto.internal.workflow.v1.VulnAnalysisWorkflowContext;
import org.dependencytrack.util.InternalComponentIdentifier;
import org.jdbi.v3.core.Handle;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
Expand Down Expand Up @@ -102,6 +104,7 @@
import static org.dependencytrack.parser.cyclonedx.util.ModelConverter.convertToProject;
import static org.dependencytrack.parser.cyclonedx.util.ModelConverter.convertToProjectMetadata;
import static org.dependencytrack.parser.cyclonedx.util.ModelConverter.flatten;
import static org.dependencytrack.persistence.jdbi.JdbiFactory.createLocalJdbi;
import static org.dependencytrack.proto.internal.workflow.v1.AnalysisTrigger.ANALYSIS_TRIGGER_BOM_UPLOAD;
import static org.dependencytrack.util.PersistenceUtil.applyIfChanged;
import static org.dependencytrack.util.PersistenceUtil.assertPersistent;
Expand Down Expand Up @@ -698,6 +701,10 @@ private void processDependencyGraph(
}

qm.getPersistenceManager().flush();

try (final Handle jdbiHandle = createLocalJdbi(qm).open()) {
jdbiHandle.attach(ComponentDao.class).setDirect(project.getId());
}
Comment on lines +705 to +707
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether a component is a direct dependency could be determined here as well:

private void processDependencyGraph(
final QueryManager qm,
final Project project,
final MultiValuedMap<String, String> dependencyGraph,
final Map<ComponentIdentity, Component> componentsByIdentity,
final Map<String, ComponentIdentity> identitiesByBomRef,
final MultiValuedMap<ComponentIdentity, String> bomRefsByIdentity
) {
assertPersistent(project, "Project must be persistent");
if (project.getBomRef() != null) {
final Collection<String> directDependencyBomRefs = dependencyGraph.get(project.getBomRef());
if (directDependencyBomRefs == null || directDependencyBomRefs.isEmpty()) {
LOGGER.warn("""
The dependency graph has %d entries, but the project (metadata.component node of the BOM) \
is not one of them; Graph will be incomplete because it is not possible to determine its root\
""".formatted(dependencyGraph.size()));
}
final String directDependenciesJson = resolveDependenciesJson(
List.of(project.getBomRef()),
sourceBomRef -> sourceBomRef.equals(project.getBomRef()) ? directDependencyBomRefs : null,
identitiesByBomRef);
if (!Objects.equals(directDependenciesJson, project.getDirectDependencies())) {
project.setDirectDependencies(directDependenciesJson);
qm.getPersistenceManager().flush();
}
} else {
// Make sure we don't retain stale data from previous BOM uploads.
if (project.getDirectDependencies() != null) {
project.setDirectDependencies(null);
qm.getPersistenceManager().flush();
}
}
for (final Component component : componentsByIdentity.values()) {
assertPersistent(component, "Component must be persistent");
final String mergedDirectDependenciesJson = resolveMergedDirectDependenciesJson(
component, dependencyGraph, identitiesByBomRef, bomRefsByIdentity);
if (!Objects.equals(mergedDirectDependenciesJson, component.getDirectDependencies())) {
component.setDirectDependencies(mergedDirectDependenciesJson);
}
}
qm.getPersistenceManager().flush();
}

}

private static void recordBomImport(final ProcessingContext ctx, final QueryManager qm, final Project project) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void testGetOutdatedComponents() throws Exception {
}

@Test
public void testGetDirectComponents() throws Exception {
public void testIsDirectComponents() throws Exception {

final Project project = prepareProject();
var components = qm.getComponents(project, false, false, true);
Expand Down Expand Up @@ -94,7 +94,6 @@ public void getUngroupedOutdatedDirectComponentsTest() throws Exception {

private Project prepareProject() throws Exception {
final Project project = qm.createProject("Acme Application", null, null, null, null, null, null, false);
final List<String> directDepencencies = new ArrayList<>();
final List<PackageMetadata> metadataList = new ArrayList<>();
final List<PackageArtifactMetadata> artifactMetadataList = new ArrayList<>();
// Generate 1000 dependencies
Expand All @@ -114,7 +113,7 @@ private Project prepareProject() throws Exception {
// direct depencencies
if (i < 100) {
// 100 direct depencencies, 900 transitive depencencies
directDepencencies.add("{\"uuid\":\"" + component.getUuid() + "\"}");
component.setDirect(true);
}
// Recent & Outdated
if ((i >= 25) && (i < 225)) {
Expand Down Expand Up @@ -151,7 +150,6 @@ private Project prepareProject() throws Exception {
new PackageMetadataDao(handle).upsertAll(metadataList);
new PackageArtifactMetadataDao(handle).upsertAll(artifactMetadataList);
});
project.setDirectDependencies("[" + String.join(",", directDepencencies.toArray(new String[0])) + "]");
return project;
}

Expand Down Expand Up @@ -270,7 +268,6 @@ public void testMappingComponentProjectionWithAllFields() throws Exception {
*/
private Project prepareProjectUngroupedComponents() throws Exception {
final Project project = qm.createProject("Ungrouped Application", null, null, null, null, null, null, false);
final List<String> directDepencencies = new ArrayList<>();
final List<PackageMetadata> metadataList = new ArrayList<>();
final List<PackageArtifactMetadata> artifactMetadataList = new ArrayList<>();
// Generate 10 dependencies
Expand All @@ -284,7 +281,7 @@ private Project prepareProjectUngroupedComponents() throws Exception {
// direct depencencies
if (i < 4) {
// 4 direct depencencies, 6 transitive depencencies
directDepencencies.add("{\"uuid\":\"" + component.getUuid() + "\"}");
component.setDirect(true);
}
// Recent & Outdated
if ((i < 7)) {
Expand Down Expand Up @@ -313,7 +310,6 @@ private Project prepareProjectUngroupedComponents() throws Exception {
new PackageMetadataDao(handle).upsertAll(metadataList);
new PackageArtifactMetadataDao(handle).upsertAll(artifactMetadataList);
});
project.setDirectDependencies("[" + String.join(",", directDepencencies.toArray(new String[0])) + "]");
return project;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,28 @@ public void testGetComponentId() {
qm.persist(component);
assertThat(componentDao.getComponentId(component.getUuid())).isEqualTo(component.getId());
}

@Test
public void testSettingIsDirectFlag() {
final var project = qm.createProject("acme-app", "Description 1", "1.0.0", null, null, null, null, false);
final var componentA = new Component();
componentA.setName("acme-lib-a");
componentA.setVersion("1.0.0");
componentA.setProject(project);
qm.persist(componentA);
final var componentB = new Component();
componentB.setName("acme-lib-b");
componentB.setVersion("2.0.0");
componentB.setProject(project);
qm.persist(componentB);
project.setDirectDependencies("[" + String.join(",", "{\"uuid\":\"" + componentB.getUuid() + "\"}]"));

componentDao.setDirect(project.getId());

qm.getPersistenceManager().refresh(componentA);
qm.getPersistenceManager().refresh(componentB);

assertThat(componentA.isDirect()).isFalse();
assertThat(componentB.isDirect()).isTrue();
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also add the new field to the "all fields" test:

/**
* (Regression-)Test for ensuring that all data available in the policy expression context
* can be accessed in the expression at runtime.
* <p>
* Data being available means:
* <ul>
* <li>Expression requirements were analyzed correctly</li>
* <li>Data was retrieved from the database correctly</li>
* <li>The mapping from DB data to CEL Protobuf models worked as expected</li>
* </ul>
*/
@Test
void testEvaluateProjectWithAllFields() throws Exception {
final var project = new Project();
project.setUuid(UUID.fromString("d7173786-60aa-4a4f-a950-c92fe6422307"));
project.setGroup("projectGroup");
project.setName("projectName");
project.setVersion("projectVersion");
project.setClassifier(Classifier.APPLICATION);
project.setInactiveSince(new java.util.Date(777));
project.setCpe("projectCpe");
project.setPurl("projectPurl");
project.setSwidTagId("projectSwidTagId");
project.setLastBomImport(new java.util.Date());
qm.persist(project);
final var bom = new Bom();
bom.setProject(project);
bom.setGenerated(new java.util.Date(999));
bom.setImported(new Date());
qm.persist(bom);
final var toolComponentLicense = new License();
toolComponentLicense.setUuid(UUID.randomUUID());
toolComponentLicense.setLicenseId("toolComponentLicenseId");
final var toolComponent = new Component();
toolComponent.setGroup("toolComponentGroup");
toolComponent.setName("toolComponentName");
toolComponent.setVersion("toolComponentVersion");
toolComponent.setClassifier(Classifier.APPLICATION);
toolComponent.setCpe("toolComponentCpe");
toolComponent.setPurl("pkg:maven/toolComponentGroup/toolComponentName@toolComponentVersion"); // NB: Must be valid PURL, otherwise it's being JSON serialized as null
toolComponent.setSwidTagId("toolComponentSwidTagId");
toolComponent.setInternal(true); // NB: Currently ignored for tool components.
toolComponent.setMd5("toolComponentMd5");
toolComponent.setSha1("toolComponentSha1");
toolComponent.setSha256("toolComponentSha256");
toolComponent.setSha384("toolComponentSha384");
toolComponent.setSha512("toolComponentSha512");
toolComponent.setSha3_256("toolComponentSha3_256");
toolComponent.setSha3_384("toolComponentSha3_384");
toolComponent.setSha3_512("toolComponentSha3_512");
toolComponent.setBlake2b_256("toolComponentBlake2b_256");
toolComponent.setBlake2b_384("toolComponentBlake2b_384");
toolComponent.setBlake2b_512("toolComponentBlake2b_512");
toolComponent.setBlake3("toolComponentBlake3");
// NB: License data is currently ignored for tool components.
// Including it in the test for documentation purposes.
toolComponent.setLicense("toolComponentLicense");
toolComponent.setLicenseExpression("toolComponentLicenseExpression");
toolComponent.setLicenseUrl("toolComponentLicenseUrl");
toolComponent.setResolvedLicense(toolComponentLicense);
final var projectMetadata = new ProjectMetadata();
projectMetadata.setProject(project);
projectMetadata.setTools(new Tools(List.of(toolComponent), null));
qm.persist(projectMetadata);
qm.createProjectProperty(project, "propertyGroup", "propertyName", "propertyValue", IConfigProperty.PropertyType.STRING, null);
qm.bind(project, List.of(
qm.createTag("projectTagA"),
qm.createTag("projectTagB")
));
final var licenseGroup = new LicenseGroup();
licenseGroup.setUuid(UUID.fromString("bbdb62f8-d854-4e43-a9ed-36481545c201"));
licenseGroup.setName("licenseGroupName");
qm.persist(licenseGroup);
final var license = new License();
license.setUuid(UUID.fromString("dc9876c2-0adc-422b-9f71-3ca78285f138"));
license.setLicenseId("resolvedLicenseId");
license.setName("resolvedLicenseName");
license.setOsiApproved(true);
license.setFsfLibre(true);
license.setDeprecatedLicenseId(true);
license.setCustomLicense(true);
license.setLicenseGroups(List.of(licenseGroup));
qm.persist(license);
final var component = new Component();
component.setProject(project);
component.setUuid(UUID.fromString("7e5f6465-d2f2-424f-b1a4-68d186fa2b46"));
component.setGroup("componentGroup");
component.setName("componentName");
component.setVersion("componentVersion");
component.setClassifier(Classifier.LIBRARY);
component.setCpe("componentCpe");
component.setPurl("pkg:maven/componentGroup/componentName@componentVersion");
component.setSwidTagId("componentSwidTagId");
component.setInternal(true);
component.setMd5("componentMd5");
component.setSha1("componentSha1");
component.setSha256("componentSha256");
component.setSha384("componentSha384");
component.setSha512("componentSha512");
component.setSha3_256("componentSha3_256");
component.setSha3_384("componentSha3_384");
component.setSha3_512("componentSha3_512");
component.setBlake2b_256("componentBlake2b_256");
component.setBlake2b_384("componentBlake2b_384");
component.setBlake2b_512("componentBlake2b_512");
component.setBlake3("componentBlake3");
component.setLicense("componentLicenseName");
component.setLicenseExpression("componentLicenseExpression");
component.setResolvedLicense(license);
qm.persist(component);
qm.createComponentProperty(
component,
"componentPropertyGroup",
"componentPropertyName",
"componentPropertyValue",
IConfigProperty.PropertyType.STRING,
null);
useJdbiHandle(handle -> {
new PackageMetadataDao(handle).upsertAll(List.of(
new PackageMetadata(
new PackageURL("pkg:maven/componentGroup/componentName"),
"1.0.0",
null,
Instant.now(),
null,
null)));
new PackageArtifactMetadataDao(handle).upsertAll(List.of(
new PackageArtifactMetadata(
new PackageURL("pkg:maven/componentGroup/componentName@componentVersion"),
new PackageURL("pkg:maven/componentGroup/componentName"),
null,
null,
null,
null,
new java.util.Date(222).toInstant(),
null,
null,
Instant.now())));
});
final var vuln = new Vulnerability();
vuln.setUuid(UUID.fromString("ffe9743f-b916-431e-8a68-9b3ac56db72c"));
vuln.setVulnId("CVE-001");
vuln.setSource(Vulnerability.Source.NVD);
vuln.setCwes(List.of(666, 777));
vuln.setCreated(new java.util.Date(666));
vuln.setPublished(new java.util.Date(777));
vuln.setUpdated(new java.util.Date(888));
vuln.setSeverity(Severity.INFO);
vuln.setCvssV2BaseScore(BigDecimal.valueOf(6.0));
vuln.setCvssV2ImpactSubScore(BigDecimal.valueOf(6.4));
vuln.setCvssV2ExploitabilitySubScore(BigDecimal.valueOf(6.8));
vuln.setCvssV2Vector("(AV:N/AC:M/Au:S/C:P/I:P/A:P)");
vuln.setCvssV3BaseScore(BigDecimal.valueOf(9.1));
vuln.setCvssV3ImpactSubScore(BigDecimal.valueOf(5.3));
vuln.setCvssV3ExploitabilitySubScore(BigDecimal.valueOf(3.1));
vuln.setCvssV3Vector("CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L");
vuln.setOwaspRRLikelihoodScore(BigDecimal.valueOf(4.5));
vuln.setOwaspRRTechnicalImpactScore(BigDecimal.valueOf(5.0));
vuln.setOwaspRRBusinessImpactScore(BigDecimal.valueOf(3.75));
vuln.setOwaspRRVector("(SL:5/M:5/O:2/S:9/ED:4/EE:2/A:7/ID:2/LC:2/LI:2/LAV:7/LAC:9/FD:3/RD:5/NC:0/PV:7)");
qm.persist(vuln);
qm.addVulnerability(vuln, component, "internal");
useJdbiTransaction(handle -> new VulnerabilityAliasDao(handle)
.syncAssertions(
"TEST",
new VulnerabilityKey("CVE-001", Vulnerability.Source.NVD),
Set.of(
new VulnerabilityKey("GHSA-001", Vulnerability.Source.GITHUB),
new VulnerabilityKey("INT-001", Vulnerability.Source.INTERNAL),
new VulnerabilityKey("OSV-001", Vulnerability.Source.OSV),
new VulnerabilityKey("SNYK-001", Vulnerability.Source.SNYK),
new VulnerabilityKey("SONATYPE-001", Vulnerability.Source.OSSINDEX),
new VulnerabilityKey("VULNDB-001", Vulnerability.Source.VULNDB))));
useJdbiHandle(handle -> handle.attach(EpssDao.class)
.createOrUpdateAll(List.of(new Epss(
"CVE-001", BigDecimal.valueOf(0.6), BigDecimal.valueOf(0.2)))));
final Policy policy = qm.createPolicy("policy", Policy.Operator.ALL, Policy.ViolationState.INFO);
qm.createPolicyCondition(policy, PolicyCondition.Subject.EXPRESSION, PolicyCondition.Operator.MATCHES, """
component.uuid == "__COMPONENT_UUID__"
&& component.group == "componentGroup"
&& component.name == "componentName"
&& component.version == "componentVersion"
&& component.classifier == "LIBRARY"
&& component.cpe == "componentCpe"
&& component.purl == "pkg:maven/componentGroup/componentName@componentVersion"
&& component.swid_tag_id == "componentSwidTagId"
&& component.is_internal
&& component.md5 == "componentmd5"
&& component.sha1 == "componentsha1"
&& component.sha256 == "componentsha256"
&& component.sha384 == "componentsha384"
&& component.sha512 == "componentsha512"
&& component.sha3_256 == "componentsha3_256"
&& component.sha3_384 == "componentsha3_384"
&& component.sha3_512 == "componentsha3_512"
&& component.blake2b_256 == "componentBlake2b_256"
&& component.blake2b_384 == "componentBlake2b_384"
&& component.blake2b_512 == "componentBlake2b_512"
&& component.blake3 == "componentBlake3"
&& component.license_name == "componentLicenseName"
&& component.license_expression == "componentLicenseExpression"
&& component.resolved_license.uuid == "__RESOLVED_LICENSE_UUID__"
&& component.resolved_license.id == "resolvedLicenseId"
&& component.resolved_license.name == "resolvedLicenseName"
&& component.resolved_license.is_osi_approved
&& component.resolved_license.is_fsf_libre
&& component.resolved_license.is_deprecated_id
&& component.resolved_license.is_custom
&& component.resolved_license.groups.all(licenseGroup,
licenseGroup.uuid == "__LICENSE_GROUP_UUID__"
&& licenseGroup.name == "licenseGroupName"
)
&& component.published_at == timestamp("1970-01-01T00:00:00.222Z")
&& component.properties.all(property,
property.group == "componentPropertyGroup"
&& property.name == "componentPropertyName"
&& property.value == "componentPropertyValue"
&& property.type == "STRING"
)
&& project.uuid == "__PROJECT_UUID__"
&& project.group == "projectGroup"
&& project.name == "projectName"
&& project.version == "projectVersion"
&& project.classifier == "APPLICATION"
&& !project.is_active
&& project.cpe == "projectCpe"
&& project.purl == "projectPurl"
&& project.swid_tag_id == "projectSwidTagId"
&& has(project.last_bom_import)
&& project.metadata.bom_generated == timestamp("1970-01-01T00:00:00.999Z")
&& project.metadata.tools.components.all(tool,
tool.group == "toolComponentGroup"
&& tool.name == "toolComponentName"
&& tool.version == "toolComponentVersion"
&& tool.classifier == "APPLICATION"
&& tool.cpe == "toolComponentCpe"
&& tool.purl == "pkg:maven/toolComponentGroup/toolComponentName@toolComponentVersion"
&& tool.swid_tag_id == "toolComponentSwidTagId"
&& !tool.is_internal
&& tool.md5 == "toolcomponentmd5"
&& tool.sha1 == "toolcomponentsha1"
&& tool.sha256 == "toolcomponentsha256"
&& tool.sha384 == "toolcomponentsha384"
&& tool.sha512 == "toolcomponentsha512"
&& tool.sha3_256 == "toolcomponentsha3_256"
&& tool.sha3_384 == "toolcomponentsha3_384"
&& tool.sha3_512 == "toolcomponentsha3_512"
&& tool.blake2b_256 == "toolComponentBlake2b_256"
&& tool.blake2b_384 == "toolComponentBlake2b_384"
&& tool.blake2b_512 == "toolComponentBlake2b_512"
&& tool.blake3 == "toolComponentBlake3"
&& !has(tool.license_name)
&& !has(tool.license_expression)
&& !has(tool.resolved_license)
)
&& "projecttaga" in project.tags
&& project.properties.all(property,
property.group == "propertyGroup"
&& property.name == "propertyName"
&& property.value == "propertyValue"
&& property.type == "STRING"
)
&& vulns.all(vuln,
vuln.uuid == "__VULN_UUID__"
&& vuln.id == "CVE-001"
&& vuln.source == "NVD"
&& 666 in vuln.cwes
&& vuln.aliases
.map(alias, alias.source + ":" + alias.id)
.all(alias, alias in [
"NVD:CVE-001",
"GITHUB:GHSA-001",
"GSD:GSD-001",
"INTERNAL:INT-001",
"OSV:OSV-001",
"SNYK:SNYK-001",
"OSSINDEX:SONATYPE-001",
"VULNDB:VULNDB-001"
])
&& vuln.created == timestamp("1970-01-01T00:00:00.666Z")
&& vuln.published == timestamp("1970-01-01T00:00:00.777Z")
&& vuln.updated == timestamp("1970-01-01T00:00:00.888Z")
&& vuln.severity == "INFO"
&& vuln.cvssv2_base_score == 6.0
&& vuln.cvssv2_impact_subscore == 6.4
&& vuln.cvssv2_exploitability_subscore == 6.8
&& vuln.cvssv2_vector == "(AV:N/AC:M/Au:S/C:P/I:P/A:P)"
&& vuln.cvssv3_base_score == 9.1
&& vuln.cvssv3_impact_subscore == 5.3
&& vuln.cvssv3_exploitability_subscore == 3.1
&& vuln.cvssv3_vector == "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L"
&& vuln.owasp_rr_likelihood_score == 4.5
&& vuln.owasp_rr_technical_impact_score == 5.0
&& vuln.owasp_rr_business_impact_score == 3.75
&& vuln.owasp_rr_vector == "(SL:5/M:5/O:2/S:9/ED:4/EE:2/A:7/ID:2/LC:2/LI:2/LAV:7/LAC:9/FD:3/RD:5/NC:0/PV:7)"
&& vuln.epss_score == 0.6
&& vuln.epss_percentile == 0.2
)
"""
.replace("__COMPONENT_UUID__", component.getUuid().toString())
.replace("__PROJECT_UUID__", project.getUuid().toString())
.replace("__RESOLVED_LICENSE_UUID__", license.getUuid().toString())
.replace("__LICENSE_GROUP_UUID__", licenseGroup.getUuid().toString())
.replace("__VULN_UUID__", vuln.getUuid().toString()), PolicyViolation.Type.OPERATIONAL);
new CelPolicyEngine().evaluateProject(project.getUuid());
assertThat(qm.getAllPolicyViolations(project)).hasSize(1);
}

Original file line number Diff line number Diff line change
Expand Up @@ -500,9 +500,8 @@ void testEvaluateProjectWithPolicyOperatorForVersionDistance() throws Exception
component.setName("bar");
component.setPurl("pkg:maven/foo/bar@1.0.0");
component.setVersion("1.2.3");
component.setDirect(true);
qm.persist(component);

project.setDirectDependencies("[{\"uuid\":\"" + component.getUuid() + "\"}]");
qm.persist(project);

new CelPolicyEngine().evaluateProject(project.getUuid());
Expand Down Expand Up @@ -1277,11 +1276,13 @@ void testEvaluateProjectWithFuncComponentIsDependencyOfExclusiveComponentWithMul
final var componentA = new Component();
componentA.setProject(project);
componentA.setName("acme-lib-a");
componentA.setDirect(true);
qm.persist(componentA);

final var componentB = new Component();
componentB.setProject(project);
componentB.setName("acme-lib-b");
componentB.setDirect(true);
qm.persist(componentB);

final var componentC = new Component();
Expand All @@ -1297,10 +1298,6 @@ void testEvaluateProjectWithFuncComponentIsDependencyOfExclusiveComponentWithMul
// /-> A -------\
// * > C
// \-> B -> D --/
project.setDirectDependencies("[%s, %s]".formatted(
new ComponentIdentity(componentA).toJSON(),
new ComponentIdentity(componentB).toJSON())
);
componentA.setDirectDependencies("[%s]".formatted(new ComponentIdentity(componentC).toJSON()));
componentB.setDirectDependencies("[%s]".formatted(new ComponentIdentity(componentD).toJSON()));
componentD.setDirectDependencies("[%s]".formatted(new ComponentIdentity(componentC).toJSON()));
Expand Down Expand Up @@ -1710,20 +1707,18 @@ void testEvaluateProjectWithFuncComponentIsDependencyOfExclusiveComponentWithMul
final var componentA = new Component();
componentA.setProject(project);
componentA.setName("acme-lib-a");
componentA.setDirect(true);
qm.persist(componentA);

final var componentB = new Component();
componentB.setProject(project);
componentB.setName("acme-lib-b");
componentB.setDirect(true);
qm.persist(componentB);

// /-> A -> B
// * ^
// \-------/
project.setDirectDependencies("[%s, %s]".formatted(
new ComponentIdentity(componentA).toJSON(),
new ComponentIdentity(componentB).toJSON()
));
componentA.setDirectDependencies("[%s]".formatted(new ComponentIdentity(componentB).toJSON()));
qm.persist(project);
qm.persist(componentA);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,8 @@ public void evaluateTest(final String version, String latestVersion, Operator op
component.setName("bar");
component.setPurl(componentPurl);
component.setVersion(version);
component.setDirect(true);
qm.persist(component);

project.setDirectDependencies("[{\"uuid\":\"" + component.getUuid() + "\"}]");
qm.persist(project);

new CelPolicyEngine().evaluateProject(project.getUuid());
Expand All @@ -172,8 +171,8 @@ public void evaluateTest(final String version, String latestVersion, Operator op
}

// https://github.com/DependencyTrack/dependency-track/issues/3295
project.setDirectDependencies(null);
qm.persist(project);
component.setDirect(false);
qm.persist(component);
new CelPolicyEngine().evaluateProject(project.getUuid());
assertThat(qm.getAllPolicyViolations(component)).isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ public void testLoadRequiredFieldsForComponent() throws Exception {
"purl": "pkg:maven/componentGroup/componentName@componentVersion",
"swidTagId": "componentSwidTagId",
"isInternal": true,
"isDirect": false,
"md5": "componentmd5",
"sha1": "componentsha1",
"sha256": "componentsha256",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,7 @@ public void getAllComponentsTest() throws Exception {
},
"expandDependencyGraph": false,
"isInternal": false,
"isDirect": false,
"occurrenceCount": 0
}
""");
Expand Down Expand Up @@ -1510,6 +1511,7 @@ private Project prepareProject() throws Exception {
component.setPurl(new PackageURL(RepositoryType.MAVEN.toString(), "component-group", "component-name-" + i, i + ".0", null, null));
component = qm.createComponent(component, false);
if (i < 10) {
component.setDirect(true);
directDependencies.add("{\"uuid\":\"" + component.getUuid() + "\"}");
}
if ((i >= 5) && (i < 15)) {
Expand Down
Loading
Loading