-
-
Notifications
You must be signed in to change notification settings - Fork 595
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
VersionDistancePolicyEvaluator for detecting outdated components
Signed-off-by: Walter de Boer <[email protected]>
- Loading branch information
Walter de Boer
committed
Feb 28, 2023
1 parent
256b939
commit 588931e
Showing
9 changed files
with
685 additions
and
17 deletions.
There are no files selected for viewing
26 changes: 26 additions & 0 deletions
26
src/main/java/org/dependencytrack/exception/PolicyException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* This file is part of Dependency-Track. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright (c) Steve Springett. All Rights Reserved. | ||
*/ | ||
package org.dependencytrack.exception; | ||
|
||
public class PolicyException extends RuntimeException { | ||
|
||
public PolicyException(String message) { | ||
super(message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,7 +75,8 @@ public enum Subject { | |
SWID_TAGID, | ||
VERSION, | ||
COMPONENT_HASH, | ||
CWE | ||
CWE, | ||
VERSION_DISTANCE | ||
} | ||
|
||
@PrimaryKey | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
src/main/java/org/dependencytrack/policy/VersionDistancePolicyEvaluator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* | ||
* This file is part of Dependency-Track. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright (c) Steve Springett. All Rights Reserved. | ||
*/ | ||
package org.dependencytrack.policy; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import org.dependencytrack.model.Component; | ||
import org.dependencytrack.model.Policy; | ||
import org.dependencytrack.model.PolicyCondition; | ||
import org.dependencytrack.model.RepositoryMetaComponent; | ||
import org.dependencytrack.model.RepositoryType; | ||
import org.dependencytrack.persistence.QueryManager; | ||
import org.dependencytrack.util.VersionDistance; | ||
import alpine.common.logging.Logger; | ||
|
||
/** | ||
* Evaluates the {@link VersionDistance} between a {@link Component}'s current and it's latest | ||
* version against a {@link Policy}. This makes it possible to add a policy for checking outdated | ||
* components. The policy "greater than 0:1.0.0" for example means, a difference of only one | ||
* between the curren version's major number and the latest version's major number is allowed. | ||
* | ||
* TODO: add a VersionMatcher to partly match VersionDistances, instead of using | ||
* {@link VersionDistance#compareTo(VersionDistance)}. This could also be userd in | ||
* the VersionPolicyEvaluator to partly match versions. This would enable aadvanced | ||
* policies like "no outdated versions (one major difference), but only if the minor | ||
* version is at least 1 and when it is older than 21 days. (3.0.0 is to early, but | ||
* 3.1.0 is considered safe, when it's older than three weeks without a patch release)" | ||
* | ||
* @since 4.9.0 | ||
*/ | ||
public class VersionDistancePolicyEvaluator extends AbstractPolicyEvaluator { | ||
|
||
private static final Logger LOGGER = Logger.getLogger(VersionDistancePolicyEvaluator.class); | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Override | ||
public PolicyCondition.Subject supportedSubject() { | ||
return PolicyCondition.Subject.VERSION_DISTANCE; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Override | ||
public List<PolicyConditionViolation> evaluate(final Policy policy, final Component component) { | ||
final var violations = new ArrayList<PolicyConditionViolation>(); | ||
if (component.getPurl() == null) { | ||
return violations; | ||
} | ||
|
||
final RepositoryType repoType = RepositoryType.resolve(component.getPurl()); | ||
if (RepositoryType.UNSUPPORTED == repoType) { | ||
return violations; | ||
} | ||
|
||
final RepositoryMetaComponent metaComponent; | ||
try (final var qm = new QueryManager()) { | ||
metaComponent = qm.getRepositoryMetaComponent(repoType, | ||
component.getPurl().getNamespace(), component.getPurl().getName()); | ||
qm.getPersistenceManager().detachCopy(metaComponent); | ||
} | ||
if (metaComponent == null || metaComponent.getLatestVersion() == null) { | ||
return violations; | ||
} | ||
|
||
final var versionDistance = VersionDistance.getVersionDistance(component.getVersion(),metaComponent.getLatestVersion()); | ||
|
||
for (final PolicyCondition condition : super.extractSupportedConditions(policy)) { | ||
if (evaluate(condition, versionDistance)) { | ||
violations.add(new PolicyConditionViolation(condition, component)); | ||
} | ||
} | ||
|
||
return violations; | ||
} | ||
|
||
private boolean evaluate(final PolicyCondition condition, final VersionDistance versionDistance) { | ||
final VersionDistance policyDistance; | ||
try { | ||
policyDistance = new VersionDistance(condition.getValue()); | ||
} catch (NumberFormatException e) { | ||
LOGGER.error("Invalid version distance format", e); | ||
return false; | ||
} | ||
|
||
return switch (condition.getOperator()) { | ||
case NUMERIC_GREATER_THAN -> versionDistance.compareTo(policyDistance) < 0; | ||
case NUMERIC_GREATER_THAN_OR_EQUAL -> versionDistance.compareTo(policyDistance) <= 0; | ||
case NUMERIC_EQUAL -> versionDistance.compareTo(policyDistance) == 0; | ||
case NUMERIC_NOT_EQUAL -> versionDistance.compareTo(policyDistance) != 0; | ||
case NUMERIC_LESSER_THAN_OR_EQUAL -> versionDistance.compareTo(policyDistance) >= 0; | ||
case NUMERIC_LESS_THAN -> versionDistance.compareTo(policyDistance) > 0; | ||
default -> { | ||
LOGGER.warn("Operator %s is not supported for component age conditions".formatted(condition.getOperator())); | ||
yield false; | ||
} | ||
}; | ||
} | ||
|
||
} |
Oops, something went wrong.