Skip to content

Commit 55f9850

Browse files
authored
Merge pull request #580 from DependencyTrack/use-proto-instead-of-json-string
Replace json string with proto in Version Distance Cel Policy Closes DependencyTrack/hyades#931
2 parents 417142f + 503a0aa commit 55f9850

File tree

7 files changed

+127
-43
lines changed

7 files changed

+127
-43
lines changed

src/main/java/org/dependencytrack/util/VersionDistance.java src/main/java/org/dependencytrack/model/VersionDistance.java

+8-15
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@
1616
* SPDX-License-Identifier: Apache-2.0
1717
* Copyright (c) Steve Springett. All Rights Reserved.
1818
*/
19-
package org.dependencytrack.util;
19+
package org.dependencytrack.model;
2020

2121
import alpine.common.logging.Logger;
2222
import org.apache.commons.lang3.StringUtils;
23-
import org.dependencytrack.model.PolicyCondition;
24-
import org.json.JSONObject;
2523

2624
import java.io.Serializable;
2725
import java.util.ArrayList;
@@ -190,7 +188,7 @@ public static List<VersionDistance> parse(String combinedDistances) throws Numbe
190188
result.add(new VersionDistance(0, 0, 0, patch));
191189
}
192190
} else {
193-
throw new NumberFormatException("Invallid version distance: " + combinedDistances);
191+
throw new NumberFormatException("Invalid version distance: " + combinedDistances);
194192
}
195193
return result;
196194
}
@@ -380,17 +378,15 @@ public static boolean matches(final PolicyCondition.Operator operator, final Ver
380378
* @param versionDistance the {@link VersionDistance} to evalue
381379
* @return true if the condition is true for the components versionDistance, false otherwise
382380
*/
383-
public static boolean evaluate(final String policyConditionValue, final String policyConditionOperator, final VersionDistance versionDistance) {
381+
public static boolean evaluate(final org.dependencytrack.proto.policy.v1.VersionDistance policyConditionValue, final String policyConditionOperator, final VersionDistance versionDistance) {
384382
final var operator = PolicyCondition.Operator.valueOf(policyConditionOperator);
385383
final var value = policyConditionValue;
386384

387-
if (!StringUtils.isEmpty(value)) {
388-
final var json = new JSONObject(value);
389-
final var epoch = json.optString("epoch", "0");
390-
final var major = json.optString("major", "?");
391-
final var minor = json.optString("minor", "?");
392-
final var patch = json.optString("patch", "?");
393-
385+
if (policyConditionValue != null) {
386+
var epoch = policyConditionValue.getEpoch().equals("") ? "0" : policyConditionValue.getEpoch();
387+
var major = policyConditionValue.getMajor().equals("") ? "?" : policyConditionValue.getMajor();
388+
var minor = policyConditionValue.getMinor().equals("") ? "?" : policyConditionValue.getMinor();
389+
var patch = policyConditionValue.getPatch().equals("") ? "?" : policyConditionValue.getPatch();
394390
final List<VersionDistance> versionDistanceList;
395391
try {
396392
versionDistanceList = VersionDistance.parse(epoch + ":" + major + "." + minor + "." + patch);
@@ -409,8 +405,5 @@ public static boolean evaluate(final String policyConditionValue, final String p
409405
}
410406
return false;
411407

412-
413408
}
414-
415-
416409
}

src/main/java/org/dependencytrack/policy/cel/CelCommonPolicyLibrary.java

+62-23
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
import org.dependencytrack.proto.policy.v1.Component;
1111
import org.dependencytrack.proto.policy.v1.License;
1212
import org.dependencytrack.proto.policy.v1.Project;
13+
import org.dependencytrack.proto.policy.v1.VersionDistance;
1314
import org.dependencytrack.proto.policy.v1.Vulnerability;
14-
import org.dependencytrack.util.VersionDistance;
1515
import org.jdbi.v3.core.Handle;
1616
import org.jdbi.v3.core.mapper.reflect.ConstructorMapper;
1717
import org.jdbi.v3.core.statement.Query;
@@ -46,6 +46,7 @@
4646
import static org.dependencytrack.persistence.jdbi.JdbiFactory.jdbi;
4747
import static org.dependencytrack.policy.cel.definition.CelPolicyTypes.TYPE_COMPONENT;
4848
import static org.dependencytrack.policy.cel.definition.CelPolicyTypes.TYPE_PROJECT;
49+
import static org.dependencytrack.policy.cel.definition.CelPolicyTypes.TYPE_VERSION_DISTANCE;
4950

5051
public class CelCommonPolicyLibrary implements Library {
5152

@@ -117,7 +118,7 @@ public List<EnvOption> getCompileOptions() {
117118
FUNC_COMPARE_VERSION_DISTANCE,
118119
Decls.newInstanceOverload(
119120
"matches_version_distance_bool",
120-
List.of(TYPE_COMPONENT, Decls.String, Decls.String),
121+
List.of(TYPE_COMPONENT, Decls.String, TYPE_VERSION_DISTANCE),
121122
Decls.Bool
122123
)
123124
)
@@ -129,7 +130,8 @@ public List<EnvOption> getCompileOptions() {
129130
Project.getDefaultInstance(),
130131
Project.Property.getDefaultInstance(),
131132
Vulnerability.getDefaultInstance(),
132-
Vulnerability.Alias.getDefaultInstance()
133+
Vulnerability.Alias.getDefaultInstance(),
134+
VersionDistance.getDefaultInstance()
133135
)
134136
);
135137
}
@@ -163,20 +165,27 @@ public List<ProgramOption> getProgramOptions() {
163165
}
164166

165167
private static Val matchesVersionDistanceFunc(Val... vals) {
166-
var basicCheckResult = basicCheck(vals);
167-
if ((basicCheckResult instanceof BoolT && basicCheckResult.value() == Types.boolOf(false)) || basicCheckResult instanceof Err) {
168-
return basicCheckResult;
169-
}
170-
var component = (Component) vals[0].value();
171-
var value = (String) vals[2].value();
172-
var comparator = (String) vals[1].value();
173-
if (!component.hasLatestVersion()) {
174-
return Err.newErr("Requested component does not have latest version information", component);
168+
try {
169+
var basicCheckResult = basicVersionDistanceCheck(vals);
170+
if ((basicCheckResult instanceof BoolT && basicCheckResult.value() == Types.boolOf(false)) || basicCheckResult instanceof Err) {
171+
return basicCheckResult;
172+
}
173+
var component = (Component) vals[0].value();
174+
var comparator = (String) vals[1].value();
175+
var value = (VersionDistance) vals[2].value();
176+
if (!component.hasLatestVersion()) {
177+
return Err.newErr("Requested component does not have latest version information", component);
178+
}
179+
return Types.boolOf(matchesVersionDistance(component, comparator, value));
180+
}catch (Exception ex) {
181+
LOGGER.warn("""
182+
%s: Was unable to parse dynamic message for version distance policy;
183+
Unable to resolve, returning false""".formatted(FUNC_COMPARE_VERSION_DISTANCE));
184+
return Types.boolOf(false);
175185
}
176-
return Types.boolOf(matchesVersionDistance(component, comparator, value));
177186
}
178187

179-
private static boolean matchesVersionDistance(Component component, String comparator, String value) {
188+
private static boolean matchesVersionDistance(Component component, String comparator, VersionDistance value) {
180189
String comparatorComputed = switch (comparator) {
181190
case "NUMERIC_GREATER_THAN", ">" -> "NUMERIC_GREATER_THAN";
182191
case "NUMERIC_GREATER_THAN_OR_EQUAL", ">=" -> "NUMERIC_GREATER_THAN_OR_EQUAL";
@@ -192,9 +201,9 @@ private static boolean matchesVersionDistance(Component component, String compar
192201
Unable to resolve, returning false""".formatted(FUNC_COMPARE_VERSION_DISTANCE, comparator));
193202
return false;
194203
}
195-
final VersionDistance versionDistance;
204+
final org.dependencytrack.model.VersionDistance versionDistance;
196205
try {
197-
versionDistance = VersionDistance.getVersionDistance(component.getVersion(), component.getLatestVersion());
206+
versionDistance = org.dependencytrack.model.VersionDistance.getVersionDistance(component.getVersion(), component.getLatestVersion());
198207
} catch (RuntimeException e) {
199208
LOGGER.warn("""
200209
%s: Failed to compute version distance for component %s (UUID: %s), \
@@ -207,7 +216,7 @@ private static boolean matchesVersionDistance(Component component, String compar
207216
final var celQm = new CelPolicyQueryManager(qm)) {
208217
isDirectDependency = celQm.isDirectDependency(component);
209218
}
210-
return isDirectDependency && VersionDistance.evaluate(value, comparatorComputed, versionDistance);
219+
return isDirectDependency && org.dependencytrack.model.VersionDistance.evaluate(value, comparatorComputed, versionDistance);
211220
}
212221

213222
private static Val dependsOnFunc(final Val lhs, final Val rhs) {
@@ -289,11 +298,28 @@ private static Val isComponentOldFunc(Val... vals) {
289298
return Types.boolOf(isComponentOld(component, comparator, dateValue));
290299
}
291300

292-
private static Val basicCheck(Val... vals) {
301+
private static Val basicVersionDistanceCheck(Val... vals) {
302+
293303
if (vals.length != 3) {
294304
return Types.boolOf(false);
295305
}
296-
if (vals[0].value() == null || vals[1].value() == null || vals[2].value() == null) {
306+
Val[] subVals = {vals[0], vals[1]};
307+
Val basicCheckResult = basicPartsCheck(subVals);
308+
if ((basicCheckResult instanceof BoolT && basicCheckResult.value().equals(Types.boolOf(false))) || basicCheckResult instanceof Err) {
309+
return basicCheckResult;
310+
}
311+
312+
if (!(vals[2].value() instanceof VersionDistance)) {
313+
return Err.maybeNoSuchOverloadErr(vals[2]);
314+
}
315+
return Types.boolOf(true);
316+
}
317+
318+
private static Val basicPartsCheck(Val... vals) {
319+
if (vals.length != 2) {
320+
return Types.boolOf(false);
321+
}
322+
if (vals[0].value() == null || vals[1].value() == null) {
297323
return Types.boolOf(false);
298324
}
299325

@@ -304,10 +330,6 @@ private static Val basicCheck(Val... vals) {
304330
return Err.maybeNoSuchOverloadErr(vals[1]);
305331
}
306332

307-
if (!(vals[2].value() instanceof String)) {
308-
return Err.maybeNoSuchOverloadErr(vals[2]);
309-
}
310-
311333
if (!component.hasPurl()) {
312334
return Err.newErr("Provided component does not have a purl", vals[0]);
313335
}
@@ -321,6 +343,23 @@ private static Val basicCheck(Val... vals) {
321343
return Types.boolOf(true);
322344
}
323345

346+
private static Val basicCheck(Val... vals) {
347+
if (vals.length != 3) {
348+
return Types.boolOf(false);
349+
}
350+
Val[] subVals = {vals[0], vals[1]};
351+
Val basicCheckResult = basicPartsCheck(subVals);
352+
if ((basicCheckResult instanceof BoolT && basicCheckResult.value().equals(Types.boolOf(false))) || basicCheckResult instanceof Err) {
353+
return basicCheckResult;
354+
}
355+
356+
if (!(vals[2].value() instanceof String)) {
357+
return Err.maybeNoSuchOverloadErr(vals[2]);
358+
}
359+
360+
return Types.boolOf(true);
361+
}
362+
324363
private static boolean dependsOn(final Project project, final Component component) {
325364
if (project.getUuid().isBlank()) {
326365
// Need a UUID for our starting point.
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,55 @@
11
package org.dependencytrack.policy.cel.compat;
22

3+
import alpine.common.logging.Logger;
4+
import com.google.protobuf.InvalidProtocolBufferException;
5+
import com.google.protobuf.util.JsonFormat;
6+
import org.apache.commons.lang3.StringUtils;
37
import org.dependencytrack.model.PolicyCondition;
8+
import org.dependencytrack.proto.policy.v1.VersionDistance;
49

510
public class VersionDistanceCelScriptBuilder implements CelPolicyScriptSourceBuilder {
11+
12+
private static final Logger LOGGER = Logger.getLogger(VersionDistanceCelScriptBuilder.class);
13+
614
@Override
715
public String apply(PolicyCondition policyCondition) {
816
return """
9-
component.version_distance("%s", "%s")
10-
""".formatted(policyCondition.getOperator(), CelPolicyScriptSourceBuilder.escapeQuotes(policyCondition.getValue()));
17+
component.version_distance("%s", %s)
18+
""".formatted(comparator(policyCondition.getOperator()), toProtoString(policyCondition.getValue()));
19+
}
20+
21+
22+
private String toProtoString(String conditionValue) {
23+
try {
24+
VersionDistance.Builder structBuilder = VersionDistance.newBuilder();
25+
JsonFormat.parser().ignoringUnknownFields().merge(conditionValue, structBuilder);
26+
return convertToString(structBuilder.build());
27+
} catch (InvalidProtocolBufferException e) {
28+
LOGGER.error("Invalid version distance proto " + e);
29+
return convertToString(VersionDistance.newBuilder().build());
30+
}
31+
}
32+
33+
private String convertToString(VersionDistance versionDistance) {
34+
StringBuilder sbf = new StringBuilder();
35+
if (!StringUtils.isEmpty(versionDistance.getEpoch())) {
36+
sbf.append("epoch:").append("\"").append(versionDistance.getEpoch()).append("\"").append(",");
37+
}
38+
sbf.append("major:").append("\"").append(versionDistance.getMajor()).append("\"").append(",");
39+
sbf.append("minor:").append("\"").append(versionDistance.getMinor()).append("\"").append(",");
40+
sbf.append("patch:").append("\"").append(versionDistance.getPatch()).append("\"");
41+
return "v1.VersionDistance{" + sbf + "}";
42+
}
43+
44+
private String comparator(PolicyCondition.Operator operator) {
45+
return switch (operator) {
46+
case NUMERIC_GREATER_THAN -> ">";
47+
case NUMERIC_GREATER_THAN_OR_EQUAL -> ">=";
48+
case NUMERIC_EQUAL -> "==";
49+
case NUMERIC_NOT_EQUAL -> "!=";
50+
case NUMERIC_LESSER_THAN_OR_EQUAL -> "<=";
51+
case NUMERIC_LESS_THAN -> "<";
52+
default -> "";
53+
};
1154
}
1255
}

src/main/java/org/dependencytrack/policy/cel/definition/CelPolicyTypes.java

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.dependencytrack.proto.policy.v1.License;
66
import org.dependencytrack.proto.policy.v1.Project;
77
import org.dependencytrack.proto.policy.v1.Vulnerability;
8+
import org.dependencytrack.proto.policy.v1.VersionDistance;
89
import org.projectnessie.cel.checker.Decls;
910

1011
public class CelPolicyTypes {
@@ -17,5 +18,6 @@ public class CelPolicyTypes {
1718
public static final Type TYPE_VULNERABILITY = Decls.newObjectType(Vulnerability.getDescriptor().getFullName());
1819
public static final Type TYPE_VULNERABILITIES = Decls.newListType(TYPE_VULNERABILITY);
1920
public static final Type TYPE_VULNERABILITY_ALIAS = Decls.newObjectType(Vulnerability.Alias.getDescriptor().getFullName());
21+
public static final Type TYPE_VERSION_DISTANCE = Decls.newObjectType(VersionDistance.getDescriptor().getFullName());
2022

2123
}

src/main/proto/org/dependencytrack/policy/v1/policy.proto

+7
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,10 @@ message Vulnerability {
136136
string source = 2;
137137
}
138138
}
139+
140+
message VersionDistance {
141+
optional string epoch = 1;
142+
optional string major = 2;
143+
optional string minor = 3;
144+
optional string patch = 4;
145+
}

src/test/java/org/dependencytrack/util/VersionDistanceTest.java src/test/java/org/dependencytrack/model/VersionDistanceTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
package org.dependencytrack.util;
2+
package org.dependencytrack.model;
33

44
import org.junit.Test;
55

src/test/java/org/dependencytrack/policy/cel/CelPolicyEngineTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ public void testEvaluateProjectWithPolicyOperatorForComponentAgeLessThan() throw
342342
public void testEvaluateProjectWithPolicyOperatorForVersionDistance() {
343343
final var policy = qm.createPolicy("policy", Policy.Operator.ANY, Policy.ViolationState.FAIL);
344344
qm.createPolicyCondition(policy, PolicyCondition.Subject.EXPRESSION, PolicyCondition.Operator.MATCHES, """
345-
component.version_distance("NUMERIC_GREATER_THAN_OR_EQUAL", "{ \\"major\\": \\"0\\", \\"minor\\": \\"1\\", \\"patch\\": \\"?\\" }")
345+
component.version_distance(">=", v1.VersionDistance{ major: \"0\", minor: \"1\", patch: \"?\" })
346346
""", PolicyViolation.Type.OPERATIONAL);
347347

348348
final var project = new Project();
@@ -371,7 +371,7 @@ public void testEvaluateProjectWithPolicyOperatorForVersionDistance() {
371371
new CelPolicyEngine().evaluateProject(project.getUuid());
372372
assertThat(qm.getAllPolicyViolations(component)).hasSize(1);
373373
assertThat(qm.getAllPolicyViolations(component).get(0).getPolicyCondition().getValue()).isEqualTo("""
374-
component.version_distance("NUMERIC_GREATER_THAN_OR_EQUAL", "{ \\"major\\": \\"0\\", \\"minor\\": \\"1\\", \\"patch\\": \\"?\\" }")
374+
component.version_distance(">=", v1.VersionDistance{ major: \"0\", minor: \"1\", patch: \"?\" })
375375
""");
376376
}
377377

0 commit comments

Comments
 (0)