Skip to content

Commit

Permalink
Merge pull request #1 from nutshelllabs/ef/add-maven-install-v2-support
Browse files Browse the repository at this point in the history
Add maven_install.json v2 support
  • Loading branch information
efabens committed Mar 18, 2024
2 parents 08a595b + d9ba0da commit a6a8f21
Show file tree
Hide file tree
Showing 4 changed files with 2,481 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.github.packageurl.MalformedPackageURLException;
import com.github.packageurl.PackageURL;
import com.github.packageurl.PackageURLBuilder;
import java.util.Map;
import java.util.stream.Collectors;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.data.nvd.ecosystem.Ecosystem;
Expand Down Expand Up @@ -118,26 +121,48 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An
}

final DependencyTree tree;
List<MavenDependency> deps;
try {
final InstallFile installFile = INSTALL_FILE_READER.readValue(dependencyFile);
tree = installFile.getDependencyTree();
} catch (IOException e) {
return;
}
JsonNode jsonNode = MAPPER.readTree(dependencyFile);
JsonNode v2Version = jsonNode.path("version");
JsonNode v010Version = jsonNode.path("dependency_tree").path("version");

if (v2Version.isTextual()) {
final InstallFileV2 installFile = INSTALL_FILE_V2_READER.readValue(dependencyFile);
if (!Objects.equals(installFile.getAutogeneratedSentinel(), "THERE_IS_NO_DATA_ONLY_ZUUL")) {
return;
}
if (!Objects.equals(installFile.getVersion(), "2")) {
LOGGER.warn("Unsupported pinned maven_install.json version {}. Continuing optimistically.", installFile.getVersion());
}
deps = installFile.getArtifacts().entrySet().stream().map(entry -> new MavenDependency(
entry.getKey() + ":" + entry.getValue().getVersion()
)).collect(Collectors.toList());
} else if (v010Version.isTextual()) {
final InstallFile installFile = INSTALL_FILE_READER.readValue(dependencyFile);
tree = installFile.getDependencyTree();
if (tree == null) {
return;
} else if (!Objects.equals(tree.getAutogeneratedSentinel(), "THERE_IS_NO_DATA_ONLY_ZUUL")) {
return;
}
if (!Objects.equals(tree.getVersion(), "0.1.0")) {
LOGGER.warn("Unsupported pinned maven_install.json version {}. Continuing optimistically.", tree.getVersion());
}
deps = tree.getDependencies();
} else {
LOGGER.warn("No pinned maven_install.json version found. Cannot Parse");
return;
}

if (tree == null) {
return;
} else if (!Objects.equals(tree.getAutogeneratedSentinel(), "THERE_IS_NO_DATA_ONLY_ZUUL")) {

} catch (IOException e) {
System.out.println("e");
return;
}

engine.removeDependency(dependency);

if (!Objects.equals(tree.getVersion(), "0.1.0")) {
LOGGER.warn("Unsupported pinned maven_install.json version {}. Continuing optimistically.", tree.getVersion());
}

List<MavenDependency> deps = tree.getDependencies();
if (deps == null) {
deps = Collections.emptyList();
}
Expand Down Expand Up @@ -300,7 +325,12 @@ public String getVersion() {
* {@code .dependency_tree.dependencies}.
*/
private static class MavenDependency {
public MavenDependency(String coord) {
this.coord = coord;
}

public MavenDependency() {
}
/**
* The standard Maven coordinate string
* {@code group:artifact[:optional classifier][:optional packaging]:version}.
Expand All @@ -322,10 +352,98 @@ public String getCoord() {
* A reusable reader for {@link InstallFile}.
*/
private static final ObjectReader INSTALL_FILE_READER;
private static final ObjectReader INSTALL_FILE_V2_READER;
private static final ObjectMapper MAPPER;

static {
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
INSTALL_FILE_READER = mapper.readerFor(InstallFile.class);
MAPPER = new ObjectMapper();
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
INSTALL_FILE_READER = MAPPER.readerFor(InstallFile.class);
INSTALL_FILE_V2_READER = MAPPER.readerFor(InstallFileV2.class);
}

/**
* Represents the entire pinned Maven dependency set in an install.json
* file.
*
* <p>
* At the time of writing, the latest version is 2, and the dependencies
* are stored in {@code .artifacts}.
*
* <p>
* The top-level keys we care about are {@code .artifacts}. {@code .version}.
*/
private static class InstallFileV2 {

/**
* The file format version.
*/
@JsonProperty("version")
private String version;

/**
* A list of Maven dependencies made available. Note that this map is transitively closed and
* pinned to a specific version of each artifact.
* <p>
* The key is the Maven coordinate string, less the version
* {@code group:artifact[:optional classifier][:optional packaging]}.
* <p>
* The value contains the version of the artifact.
*/
@JsonProperty("artifacts")
private Map<String, Artifactv2> artifacts;

/**
* A sentinel value placed in the file to indicate that it is an auto-generated pinned maven
* install file.
*/
@JsonProperty("__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY")
private String autogeneratedSentinel;

/**
* Returns artifacts.
*
* @return artifacts
*/
public Map<String, Artifactv2> getArtifacts() {
return artifacts;
}

/**
* Returns version.
*
* @return version
*/
public String getVersion() {
return version;
}

/**
* Returns autogeneratedSentinel.
*
* @return autogeneratedSentinel
*/
public String getAutogeneratedSentinel() {
return autogeneratedSentinel;
}
}
private static class Artifactv2 {

/**
* The version of the artifact.
*/
@JsonProperty("version")
private String version;

/**
* Returns the value of version.
*
* @return the value of version
*/
public String getVersion() {
return version;
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ public void testGetName() {
public void testSupportsFiles() {
assertTrue(analyzer.accept(new File("install_maven.json")));
assertTrue(analyzer.accept(new File("maven_install.json")));
assertTrue(analyzer.accept(new File("maven_install_v010.json")));
assertTrue(analyzer.accept(new File("maven_install_v2.json")));
assertTrue(analyzer.accept(new File("rules_jvm_external_install.json")));
assertTrue(analyzer.accept(new File("pinned_install_gplonly.json")));
assertFalse("should not accept Cloudflare install.json", analyzer.accept(new File("install.json")));
Expand All @@ -89,12 +91,12 @@ public void testSupportsFiles() {
}

/**
* Tests that the analyzer correctly pulls dependencies out of a pinned {@code maven_install.json}.
* Tests that the analyzer correctly pulls dependencies out of a pinned v0.1.0 {@code maven_install.json}.
*/
@Test
public void testAnalyzePinnedInstallJson() throws Exception {
public void testAnalyzePinnedInstallJsonV010() throws Exception {
try (Engine engine = new Engine(getSettings())) {
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "maven_install.json"));
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "maven_install_v010.json"));
engine.addDependency(result);
analyzer.analyze(result, engine);
assertFalse(ArrayUtils.contains(engine.getDependencies(), result));
Expand All @@ -111,6 +113,29 @@ public void testAnalyzePinnedInstallJson() throws Exception {
}
}

/**
* Tests that the analyzer correctly pulls dependencies out of a pinned v2 {@code maven_install.json}.
*/
@Test
public void testAnalyzePinnedInstallJsonV2() throws Exception {
try (Engine engine = new Engine(getSettings())) {
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "maven_install_v2.json"));
engine.addDependency(result);
analyzer.analyze(result, engine);
assertFalse(ArrayUtils.contains(engine.getDependencies(), result));
assertEquals(113, engine.getDependencies().length);
boolean found = false;
for (Dependency d : engine.getDependencies()) {
if ("io.grpc:grpc-protobuf".equals(d.getName())) {
found = true;
assertEquals("1.48.1", d.getVersion());
assertEquals(Ecosystem.JAVA, d.getEcosystem());
}
}
assertTrue("Expected to find com.google.errorprone:error_prone_annotations:2.3.4", found);
}
}

/**
* Tests that the analyzer ignores a Cloudflare-style {@code install.json}.
*/
Expand Down
File renamed without changes.
Loading

0 comments on commit a6a8f21

Please sign in to comment.