Skip to content

Commit

Permalink
Handle direct dependency relationships in is_exclusive_dependency_of
Browse files Browse the repository at this point in the history
Because the recursive query only operates on the `COMPONENT` table, it doesn't "see" occurrences of a component's UUID being referenced in `PROJECT.DIRECT_DEPENDENCIES`.

Added a separate query to check for this condition. It also acts as a short-circuiting mechanism that can avoid the expensive recursive query from being executed, as the existence of a direct dependency already false-ifies the "is exclusive dependency of another component" condition.

Signed-off-by: nscuro <[email protected]>
  • Loading branch information
nscuro committed Feb 20, 2024
1 parent f6ee77a commit 2ef5cef
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,13 @@ private static boolean isExclusiveDependencyOf(final Component leafComponent, fi

try (final var qm = new QueryManager();
final Handle jdbiHandle = jdbi(qm).open()) {
// If the component is a direct dependency of the project,
// it can no longer be a dependency exclusively introduced
// through another component.
if (isDirectDependency(jdbiHandle, leafComponent)) {
return false;
}

final Query query = jdbiHandle.createQuery("""
-- Determine the project the given leaf component is part of.
WITH RECURSIVE
Expand Down Expand Up @@ -691,10 +698,6 @@ private static boolean isExclusiveDependencyOf(final Component leafComponent, fi

final List<List<Long>> paths = reducePaths(nodes);

// TODO: TBD whether only direct dependency relationships should count.
// Direct only:
// return paths.stream().allMatch(path -> matchedNodeIds.contains(path.get(0)));
// Also transitive (arbitrary distance between matched node and leaf component):
return paths.stream().allMatch(path -> path.stream().anyMatch(matchedNodeIds::contains));
}
}
Expand Down Expand Up @@ -898,4 +901,24 @@ private static <T> boolean containsExactly(final List<T> lhs, final List<T> rhs)
return Objects.equals(lhs, rhs);
}

private static boolean isDirectDependency(final Handle jdbiHandle, final Component component) {
final Query query = jdbiHandle.createQuery("""
SELECT
1
FROM
"COMPONENT" AS "C"
INNER JOIN
"PROJECT" AS "P" ON "P"."ID" = "C"."PROJECT_ID"
WHERE
"C"."UUID" = :leafComponentUuid
AND "P"."DIRECT_DEPENDENCIES" LIKE ('%' || :leafComponentUuid || '%')
""");

return query
.bind("leafComponentUuid", component.getUuid())
.mapTo(Boolean.class)
.findOne()
.orElse(false);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,54 @@ public void testEvaluateProjectWithFuncComponentIsDependencyOfExclusiveComponent
assertThat(qm.getAllPolicyViolations(componentSpringCore)).hasSize(1);
}

@Test
public void testEvaluateProjectWithFuncComponentIsDependencyOfExclusiveComponentWithMultiplePaths5() {
final var project = new Project();
project.setName("acme-app");
qm.persist(project);

final var componentA = new Component();
componentA.setProject(project);
componentA.setName("acme-lib-a");
qm.persist(componentA);

final var componentB = new Component();
componentB.setProject(project);
componentB.setName("acme-lib-b");
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);

final var policyEngine = new CelPolicyEngine();
final var policy = qm.createPolicy("policy", Policy.Operator.ANY, Policy.ViolationState.FAIL);

// Is component introduced exclusively through acme-lib-a?
PolicyCondition condition = qm.createPolicyCondition(policy,
PolicyCondition.Subject.EXPRESSION, PolicyCondition.Operator.MATCHES, """
component.is_exclusive_dependency_of(v1.Component{name: "acme-lib-a"})
""", PolicyViolation.Type.OPERATIONAL);
policyEngine.evaluateProject(project.getUuid());
assertThat(qm.getAllPolicyViolations(componentA)).isEmpty();
assertThat(qm.getAllPolicyViolations(componentB)).isEmpty();

// Is component introduced exclusively through acme-lib-b?
condition.setValue("""
component.is_exclusive_dependency_of(v1.Component{name: "acme-lib-b"})
""");
policyEngine.evaluateProject(project.getUuid());
assertThat(qm.getAllPolicyViolations(componentA)).isEmpty();
assertThat(qm.getAllPolicyViolations(componentB)).isEmpty();
}

@Test
public void testEvaluateProjectWithFuncMatchesRange() {
final var policy = qm.createPolicy("policy", Policy.Operator.ANY, Policy.ViolationState.FAIL);
Expand Down

0 comments on commit 2ef5cef

Please sign in to comment.