All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.chains_project.maven_lockfile.graph.DependencyGraph Maven / Gradle / Ivy

Go to download

This plugin is a state-of-the-art solution that can be used to validate the integrity of a maven repository. It does this by generating a lock file that contains the checksums of all the artifacts in the repository. The lock file can then be used to validate the integrity of the repository. This guards the supply chain against malicious actors that might tamper with the artifacts in the repository.

There is a newer version: 5.3.5
Show newest version
package io.github.chains_project.maven_lockfile.graph;

import com.google.common.graph.Graph;
import com.google.common.graph.MutableGraph;
import io.github.chains_project.maven_lockfile.checksum.AbstractChecksumCalculator;
import io.github.chains_project.maven_lockfile.data.ArtifactId;
import io.github.chains_project.maven_lockfile.data.Classifier;
import io.github.chains_project.maven_lockfile.data.GroupId;
import io.github.chains_project.maven_lockfile.data.MavenScope;
import io.github.chains_project.maven_lockfile.data.VersionNumber;
import java.util.*;
import java.util.stream.Collectors;
import org.apache.maven.shared.dependency.graph.internal.SpyingDependencyNodeUtils;

public class DependencyGraph {

    private final Set graph;

    public Set getRoots() {
        return graph.stream()
                .filter(node -> node.getParent() == null)
                .collect(Collectors.toCollection(
                        () -> new TreeSet<>(Comparator.comparing(DependencyNode::getChecksum))));
    }

    private DependencyGraph(Set graph) {
        this.graph = graph == null ? Set.of() : graph;
    }

    /**
     * @return the graph
     */
    public Set getGraph() {
        return graph;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof DependencyGraph)) {
            return false;
        }
        DependencyGraph dependencyGraph = (DependencyGraph) o;
        return Objects.equals(graph, dependencyGraph.graph);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(graph);
    }

    public Optional getParentForNode(DependencyNode node) {
        return graph.stream().filter(n -> n.id.equals(node.getParent())).findFirst();
    }

    public static DependencyGraph of(
            MutableGraph graph,
            AbstractChecksumCalculator calc,
            boolean reduced) {
        var roots = graph.nodes().stream()
                .filter(it -> graph.predecessors(it).isEmpty())
                .collect(Collectors.toList());
        Set nodes = new TreeSet<>(Comparator.comparing(DependencyNode::getChecksum));
        for (var artifact : roots) {
            createDependencyNode(artifact, graph, calc, true, reduced).ifPresent(nodes::add);
        }
        // maven dependency tree contains the project itself as a root node. We remove it here.
        Set dependencyRoots = nodes.stream()
                .flatMap(v -> v.getChildren().stream())
                .collect(Collectors.toCollection(
                        () -> new TreeSet<>(Comparator.comparing(DependencyNode::getChecksum))));
        dependencyRoots.forEach(v -> v.setParent(null));
        return new DependencyGraph(dependencyRoots);
    }

    private static Optional createDependencyNode(
            org.apache.maven.shared.dependency.graph.DependencyNode node,
            Graph graph,
            AbstractChecksumCalculator calc,
            boolean isRoot,
            boolean reduce) {
        var groupId = GroupId.of(node.getArtifact().getGroupId());
        var artifactId = ArtifactId.of(node.getArtifact().getArtifactId());
        var version = VersionNumber.of(node.getArtifact().getVersion());
        var classifier = Classifier.of(node.getArtifact().getClassifier());
        var checksum = isRoot ? "" : calc.calculateChecksum(node.getArtifact());
        var scope = MavenScope.fromString(node.getArtifact().getScope());
        Optional winnerVersion = SpyingDependencyNodeUtils.getWinnerVersion(node);
        boolean included = winnerVersion.isEmpty();
        // if there is no conflict marker for this node, we use the version from the artifact
        String baseVersion = included ? node.getArtifact().getVersion() : winnerVersion.get();
        if (reduce && !included) {
            return Optional.empty();
        }
        DependencyNode value = new DependencyNode(
                artifactId, groupId, version, classifier, scope, calc.getChecksumAlgorithm(), checksum);
        value.setSelectedVersion(baseVersion);
        value.setIncluded(included);
        for (var artifact : graph.successors(node)) {
            createDependencyNode(artifact, graph, calc, false, reduce).ifPresent(value::addChild);
        }
        return Optional.of(value);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy