From 1c304f7d1f51804bb4f6605765dcb963c693902b Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Thu, 2 May 2024 00:09:48 +0100 Subject: [PATCH 1/5] Set component evidence for 1.5 spec Signed-off-by: Prabhu Subramanian --- .../maven/DefaultModelConverter.java | 69 ++++++++++++++++++- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java index 2d28c5fb..80f60f53 100644 --- a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java +++ b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java @@ -34,11 +34,15 @@ import org.apache.maven.repository.RepositorySystem; import org.cyclonedx.CycloneDxSchema; import org.cyclonedx.model.Component; +import org.cyclonedx.model.Evidence; import org.cyclonedx.model.ExternalReference; +import org.cyclonedx.model.Hash; import org.cyclonedx.model.License; import org.cyclonedx.model.LicenseChoice; import org.cyclonedx.model.Metadata; import org.cyclonedx.model.Tool; +import org.cyclonedx.model.component.evidence.Identity; +import org.cyclonedx.model.component.evidence.Method; import org.cyclonedx.util.BomUtils; import org.cyclonedx.util.LicenseResolver; import org.eclipse.aether.artifact.ArtifactProperties; @@ -52,12 +56,18 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.Properties; import java.util.TreeMap; import java.util.stream.Collectors; + @Singleton @Named public class DefaultModelConverter implements ModelConverter { @@ -122,7 +132,7 @@ public String generateClassifierlessPackageUrl(final org.eclipse.aether.artifact } private boolean isEmpty(final String value) { - return (value == null) || (value.length() == 0); + return (value == null) || (value.isEmpty()); } private String generatePackageUrl(final org.eclipse.aether.artifact.Artifact artifact, final boolean includeVersion, final boolean includeClassifier) { @@ -184,10 +194,63 @@ public Component convertMavenDependency(Artifact artifact, CycloneDxSchema.Versi logger.warn("Unable to create Maven project for " + artifact.getId() + " from repository."); } } + // Set component evidence for cyclonedx 1.5 + if (CycloneDxSchema.Version.VERSION_15 == schemaVersion) { + addComponentEvidence(component, artifact); + } return component; } + /** + * Adds component identity evidence for the filename and manifest file + * + * @param component Component for which the evidence needs to be attached + * @param artifact Maven artifact + */ + private static void addComponentEvidence(final Component component, final Artifact artifact) { + if (artifact.getFile() != null) { + final Optional sha1Hash = component.getHashes().stream().filter(hash -> hash.getAlgorithm().equals("SHA-1")).findFirst(); + final Evidence evidence = new Evidence(); + final Identity identity = new Identity(); + final List methods = new ArrayList<>(); + final Method fileMethod = new Method(); + final String jarPath = artifact.getFile().getAbsolutePath(); + final String sha1Path = jarPath.replace(".jar", ".jar.sha1"); + if (jarPath.contains(".m2")) { + fileMethod.setValue(Paths.get(".m2", jarPath.split(".m2")[1]).toString()); + } else { + fileMethod.setValue(jarPath); + } + fileMethod.setConfidence(0.1); + fileMethod.setTechnique(Method.Technique.FILENAME); + final Path sha1FilePath = Paths.get(sha1Path); + if (sha1Hash.isPresent() && Files.exists(sha1FilePath)) { + try { + final Optional sha1Content = Files.readAllLines(sha1FilePath).stream().findFirst(); + if (sha1Content.isPresent()) { + String sha1Value = sha1Content.get().split(" ")[0].trim(); + if (sha1Value.equals(sha1Hash.get().getValue())) { + final Method hashMethod = new Method(); + hashMethod.setConfidence(0.9); + hashMethod.setTechnique(Method.Technique.HASH_COMPARISON); + hashMethod.setValue(sha1Value); + methods.add(hashMethod); + } + } + } catch (IOException e) { + // Invalid hash + } + } + identity.setField(Identity.Field.PURL); + identity.setConfidence(1.0); + methods.add(fileMethod); + identity.setMethods(methods); + evidence.setIdentity(identity); + component.setEvidence(evidence); + } + } + private static void setExternalReferences(Component component, ExternalReference[] externalReferences) { if (externalReferences == null || externalReferences.length == 0) { return; @@ -240,7 +303,7 @@ private void extractComponentMetadata(MavenProject project, Component component, if (project.getIssueManagement() != null) { addExternalReference(ExternalReference.Type.ISSUE_TRACKER, project.getIssueManagement().getUrl(), component); } - if (project.getMailingLists() != null && project.getMailingLists().size() > 0) { + if (project.getMailingLists() != null && !project.getMailingLists().isEmpty()) { for (MailingList list : project.getMailingLists()) { String url = list.getArchive(); if (url == null) { @@ -401,6 +464,6 @@ private Component.Type resolveProjectType(String projectType) { } private static boolean isURLBlank(String url) { - return url == null || url.isEmpty() || url.trim().length() == 0; + return url == null || url.isEmpty() || url.trim().isEmpty(); } } From 4124e2fd2d8a01480d57ea9a489a57fc3ef8e80e Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Thu, 2 May 2024 00:13:17 +0100 Subject: [PATCH 2/5] Added comment Signed-off-by: Prabhu Subramanian --- src/main/java/org/cyclonedx/maven/DefaultModelConverter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java index 80f60f53..1eec7d95 100644 --- a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java +++ b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java @@ -218,6 +218,7 @@ private static void addComponentEvidence(final Component component, final Artifa final String jarPath = artifact.getFile().getAbsolutePath(); final String sha1Path = jarPath.replace(".jar", ".jar.sha1"); if (jarPath.contains(".m2")) { + // This removes the home directory which might be sensitive fileMethod.setValue(Paths.get(".m2", jarPath.split(".m2")[1]).toString()); } else { fileMethod.setValue(jarPath); From 1d0eb683af1eb98e5d89727d8e687ab844c8a9b0 Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Fri, 3 May 2024 00:06:26 +0100 Subject: [PATCH 3/5] Track repository url in purl Signed-off-by: Prabhu Subramanian --- .../cyclonedx/maven/BaseCycloneDxMojo.java | 26 ++++++++++++++++++- .../maven/CycloneDxAggregateMojo.java | 2 +- .../org/cyclonedx/maven/CycloneDxMojo.java | 2 +- .../cyclonedx/maven/CycloneDxPackageMojo.java | 2 +- .../DefaultProjectDependenciesConverter.java | 8 +++--- .../maven/DelegatingRepositorySystem.java | 19 ++++++++++++-- .../maven/ProjectDependenciesConverter.java | 8 +++++- 7 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java index 78e6dd83..3fee343d 100644 --- a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java +++ b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java @@ -46,10 +46,15 @@ import org.cyclonedx.parsers.JsonParser; import org.cyclonedx.parsers.Parser; import org.cyclonedx.parsers.XmlParser; +import org.eclipse.aether.repository.ArtifactRepository; +import org.eclipse.aether.repository.RemoteRepository; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; @@ -511,17 +516,36 @@ protected void logParameters() { } } - protected void populateComponents(final Set topLevelComponents, final Map components, final Map artifacts, final ProjectDependencyAnalysis dependencyAnalysis) { + protected void populateComponents(final Set topLevelComponents, final Map components, final Map artifacts, final Map artifactRemoteRepositories, final ProjectDependencyAnalysis dependencyAnalysis) { for (Map.Entry entry: artifacts.entrySet()) { final String purl = entry.getKey(); final Artifact artifact = entry.getValue(); final Component.Scope artifactScope = getComponentScope(artifact, dependencyAnalysis); final Component component = components.get(purl); + final ArtifactRepository repository = artifactRemoteRepositories.get(purl); + String repository_url = ""; + // We are only interested in non-central repository urls + if (repository instanceof RemoteRepository && !repository.getId().equals("central")) { + try { + String url = ((RemoteRepository) repository).getUrl(); + if (url != null) { + repository_url = "&repository_url=" + URLEncoder.encode(url, "UTF-8"); + } + } catch (UnsupportedEncodingException e) { + // ignore + } + } if (component == null) { final Component newComponent = convertMavenDependency(artifact); newComponent.setScope(artifactScope); + if (!repository_url.isEmpty()) { + newComponent.setPurl(newComponent.getPurl() + repository_url); + } components.put(purl, newComponent); } else if (!topLevelComponents.contains(purl)) { + if (!repository_url.isEmpty()) { + component.setPurl(component.getPurl() + repository_url); + } component.setScope(mergeScopes(component.getScope(), artifactScope)); } } diff --git a/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java b/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java index 416596ad..08da1314 100644 --- a/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java +++ b/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java @@ -130,7 +130,7 @@ protected String extractComponentsAndDependencies(final Set topLevelComp components.put(projectBomComponent.getPurl(), projectBomComponent); topLevelComponents.add(projectBomComponent.getPurl()); - populateComponents(topLevelComponents, components, bomDependencies.getArtifacts(), doProjectDependencyAnalysis(mavenProject, bomDependencies)); + populateComponents(topLevelComponents, components, bomDependencies.getArtifacts(), bomDependencies.getArtifactRemoteRepositories(), doProjectDependencyAnalysis(mavenProject, bomDependencies)); projectDependencies.forEach(dependencies::putIfAbsent); } diff --git a/src/main/java/org/cyclonedx/maven/CycloneDxMojo.java b/src/main/java/org/cyclonedx/maven/CycloneDxMojo.java index 2ca6e15a..b22a8526 100644 --- a/src/main/java/org/cyclonedx/maven/CycloneDxMojo.java +++ b/src/main/java/org/cyclonedx/maven/CycloneDxMojo.java @@ -127,7 +127,7 @@ protected String extractComponentsAndDependencies(final Set topLevelComp components.put(projectBomComponent.getPurl(), projectBomComponent); topLevelComponents.add(projectBomComponent.getPurl()); - populateComponents(topLevelComponents, components, bomDependencies.getArtifacts(), doProjectDependencyAnalysis(getProject(), bomDependencies)); + populateComponents(topLevelComponents, components, bomDependencies.getArtifacts(), bomDependencies.getArtifactRemoteRepositories(), doProjectDependencyAnalysis(getProject(), bomDependencies)); projectDependencies.forEach(dependencies::putIfAbsent); diff --git a/src/main/java/org/cyclonedx/maven/CycloneDxPackageMojo.java b/src/main/java/org/cyclonedx/maven/CycloneDxPackageMojo.java index 828d1037..0424b2ab 100644 --- a/src/main/java/org/cyclonedx/maven/CycloneDxPackageMojo.java +++ b/src/main/java/org/cyclonedx/maven/CycloneDxPackageMojo.java @@ -70,7 +70,7 @@ protected String extractComponentsAndDependencies(Set topLevelComponents components.put(projectBomComponent.getPurl(), projectBomComponent); topLevelComponents.add(projectBomComponent.getPurl()); - populateComponents(topLevelComponents, components, bomDependencies.getArtifacts(), null); + populateComponents(topLevelComponents, components, bomDependencies.getArtifacts(), bomDependencies.getArtifactRemoteRepositories(), null); projectDependencies.forEach(dependencies::putIfAbsent); } diff --git a/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java b/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java index 3b00edaf..c3988bda 100644 --- a/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java +++ b/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java @@ -35,6 +35,7 @@ import org.eclipse.aether.artifact.ArtifactProperties; import org.eclipse.aether.collection.CollectResult; import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.util.graph.transformer.ConflictResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,6 +67,8 @@ public class DefaultProjectDependenciesConverter implements ProjectDependenciesC private Set excludeTypesSet; private MavenDependencyScopes include; + final Map artifactRemoteRepositories = new LinkedHashMap<>(); + @Override public BomDependencies extractBOMDependencies(MavenProject mavenProject, MavenDependencyScopes include, String[] excludeTypes) throws MojoExecutionException { this.include = include; @@ -77,9 +80,8 @@ public BomDependencies extractBOMDependencies(MavenProject mavenProject, MavenDe final Map mavenArtifacts = new LinkedHashMap<>(); final Map mavenDependencyArtifacts = new LinkedHashMap<>(); try { - final DelegatingRepositorySystem delegateRepositorySystem = new DelegatingRepositorySystem(aetherRepositorySystem); + final DelegatingRepositorySystem delegateRepositorySystem = new DelegatingRepositorySystem(aetherRepositorySystem, modelConverter, artifactRemoteRepositories); final DependencyCollectorBuilder dependencyCollectorBuilder = new DefaultDependencyCollectorBuilder(delegateRepositorySystem); - final org.apache.maven.shared.dependency.graph.DependencyNode mavenRoot = dependencyCollectorBuilder.collectDependencyGraph(buildingRequest, null); populateArtifactMap(mavenArtifacts, mavenDependencyArtifacts, mavenRoot, 0); @@ -98,7 +100,7 @@ public BomDependencies extractBOMDependencies(MavenProject mavenProject, MavenDe // rather than throwing an exception https://github.com/CycloneDX/cyclonedx-maven-plugin/issues/55 logger.warn("An error occurred building dependency graph: " + e.getMessage()); } - return new BomDependencies(dependencies, mavenArtifacts, mavenDependencyArtifacts); + return new BomDependencies(dependencies, mavenArtifacts, mavenDependencyArtifacts, artifactRemoteRepositories); } private void populateArtifactMap(final Map artifactMap, final Map dependencyArtifactMap, final org.apache.maven.shared.dependency.graph.DependencyNode node, final int level) { diff --git a/src/main/java/org/cyclonedx/maven/DelegatingRepositorySystem.java b/src/main/java/org/cyclonedx/maven/DelegatingRepositorySystem.java index 6f99f104..f3ec351d 100644 --- a/src/main/java/org/cyclonedx/maven/DelegatingRepositorySystem.java +++ b/src/main/java/org/cyclonedx/maven/DelegatingRepositorySystem.java @@ -2,10 +2,12 @@ import java.util.Collection; import java.util.List; +import java.util.Map; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.SyncContext; +import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.collection.CollectRequest; import org.eclipse.aether.collection.CollectResult; import org.eclipse.aether.collection.DependencyCollectionException; @@ -17,6 +19,7 @@ import org.eclipse.aether.installation.InstallRequest; import org.eclipse.aether.installation.InstallResult; import org.eclipse.aether.installation.InstallationException; +import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.RemoteRepository; @@ -49,8 +52,18 @@ class DelegatingRepositorySystem implements RepositorySystem { private final RepositorySystem delegate; private CollectResult collectResult; - public DelegatingRepositorySystem(final RepositorySystem repositorySystem) { + private final ModelConverter modelConverter; + + private final Map artifactRemoteRepositories; + + public Map getArtifactRemoteRepositories() { + return artifactRemoteRepositories; + } + + public DelegatingRepositorySystem(final RepositorySystem repositorySystem, final ModelConverter modelConverter, final Map artifactRemoteRepositories) { this.delegate = repositorySystem; + this.modelConverter = modelConverter; + this.artifactRemoteRepositories = artifactRemoteRepositories; } public CollectResult getCollectResult() { @@ -69,7 +82,9 @@ public boolean visitEnter(final DependencyNode node) if (root != node) { try { final ArtifactResult resolveArtifact = resolveArtifact(session, new ArtifactRequest(node)); - node.setArtifact(resolveArtifact.getArtifact()); + final Artifact artifact = resolveArtifact.getArtifact(); + node.setArtifact(artifact); + artifactRemoteRepositories.put(modelConverter.generatePackageUrl(artifact), resolveArtifact.getRepository()); } catch (ArtifactResolutionException e) {} // ignored } return true; diff --git a/src/main/java/org/cyclonedx/maven/ProjectDependenciesConverter.java b/src/main/java/org/cyclonedx/maven/ProjectDependenciesConverter.java index 00d9d0ef..54f6142c 100644 --- a/src/main/java/org/cyclonedx/maven/ProjectDependenciesConverter.java +++ b/src/main/java/org/cyclonedx/maven/ProjectDependenciesConverter.java @@ -24,6 +24,7 @@ import org.cyclonedx.model.Component; import org.cyclonedx.model.Dependency; import org.cyclonedx.model.Metadata; +import org.eclipse.aether.repository.ArtifactRepository; import java.util.Map; @@ -66,10 +67,13 @@ public static class BomDependencies { private final Map artifacts; private final Map dependencyArtifacts; - public BomDependencies(final Map dependencies, final Map artifacts, final Map dependencyArtifacts) { + private final Map artifactRemoteRepositories; + + public BomDependencies(final Map dependencies, final Map artifacts, final Map dependencyArtifacts, final Map artifactRemoteRepositories) { this.dependencies = dependencies; this.artifacts = artifacts; this.dependencyArtifacts = dependencyArtifacts; + this.artifactRemoteRepositories = artifactRemoteRepositories; } public final Map getDependencies() { @@ -83,5 +87,7 @@ public final Map getDependencyArtifacts() { public final Map getArtifacts() { return artifacts; } + + public final Map getArtifactRemoteRepositories() { return artifactRemoteRepositories; } } } From eccaff19551c985ca55ace2442a548dd31736a43 Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Fri, 3 May 2024 00:26:49 +0100 Subject: [PATCH 4/5] Track repository url in purl Signed-off-by: Prabhu Subramanian --- .../java/org/cyclonedx/maven/BaseCycloneDxMojo.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java index 3fee343d..28c2afca 100644 --- a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java +++ b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java @@ -524,11 +524,11 @@ protected void populateComponents(final Set topLevelComponents, final Ma final Component component = components.get(purl); final ArtifactRepository repository = artifactRemoteRepositories.get(purl); String repository_url = ""; - // We are only interested in non-central repository urls - if (repository instanceof RemoteRepository && !repository.getId().equals("central")) { + if (repository instanceof RemoteRepository) { try { String url = ((RemoteRepository) repository).getUrl(); - if (url != null) { + // As per purl spec, only repo.maven.apache.org/maven2 is considered the default + if (url != null && !url.startsWith("https://repo.maven.apache.org/maven2")) { repository_url = "&repository_url=" + URLEncoder.encode(url, "UTF-8"); } } catch (UnsupportedEncodingException e) { @@ -543,8 +543,9 @@ protected void populateComponents(final Set topLevelComponents, final Ma } components.put(purl, newComponent); } else if (!topLevelComponents.contains(purl)) { - if (!repository_url.isEmpty()) { - component.setPurl(component.getPurl() + repository_url); + String currentPurl = component.getPurl(); + if (!repository_url.isEmpty() && !currentPurl.contains("repository_url")) { + component.setPurl(currentPurl + repository_url); } component.setScope(mergeScopes(component.getScope(), artifactScope)); } From d3d321d72b3cd89ed05a2f6a321e21d285e6289b Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Fri, 3 May 2024 11:20:43 +0100 Subject: [PATCH 5/5] Handles case where only .md5 hash is available. Uses gpg file to bump up confidence Signed-off-by: Prabhu Subramanian --- .../maven/DefaultModelConverter.java | 78 +++++++++++++------ 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java index 1eec7d95..6c6bd663 100644 --- a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java +++ b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java @@ -71,6 +71,11 @@ @Singleton @Named public class DefaultModelConverter implements ModelConverter { + // Confidence value for filename based identity detection + public static final double FILENAME_CONFIDENCE = 0.1; + + // Confidence value for hash comparison based identity detection + public static final double HASH_COMPARISON_CONFIDENCE = 0.8; private final Logger logger = LoggerFactory.getLogger(DefaultModelConverter.class); @Inject @@ -203,49 +208,72 @@ public Component convertMavenDependency(Artifact artifact, CycloneDxSchema.Versi } /** - * Adds component identity evidence for the filename and manifest file + * Adds component identity evidence for the component using the filename, hash and gpg key files * * @param component Component for which the evidence needs to be attached * @param artifact Maven artifact */ private static void addComponentEvidence(final Component component, final Artifact artifact) { if (artifact.getFile() != null) { - final Optional sha1Hash = component.getHashes().stream().filter(hash -> hash.getAlgorithm().equals("SHA-1")).findFirst(); + final List sha1OrMd5Hashes = component.getHashes().stream().filter(hash -> hash.getAlgorithm().equals("SHA-1") || hash.getAlgorithm().equals("MD5")).collect(Collectors.toList()); final Evidence evidence = new Evidence(); final Identity identity = new Identity(); final List methods = new ArrayList<>(); - final Method fileMethod = new Method(); + final Method jarFileMethod = new Method(); + final Method ascFileMethod = new Method(); + double overallConfidence = 0.0; final String jarPath = artifact.getFile().getAbsolutePath(); final String sha1Path = jarPath.replace(".jar", ".jar.sha1"); - if (jarPath.contains(".m2")) { - // This removes the home directory which might be sensitive - fileMethod.setValue(Paths.get(".m2", jarPath.split(".m2")[1]).toString()); - } else { - fileMethod.setValue(jarPath); + final String ascPath = jarPath.replace(".jar", ".jar.asc"); + final String md5Path = jarPath.replace(".jar", ".jar.md5"); + jarFileMethod.setValue(".m2/" + jarPath.replaceFirst("^(.*).m2/", "")); + jarFileMethod.setConfidence(FILENAME_CONFIDENCE); + jarFileMethod.setTechnique(Method.Technique.FILENAME); + methods.add(jarFileMethod); + overallConfidence += FILENAME_CONFIDENCE; + // For gpg signed artifacts, we can bump up the confidence a bit more + // In the future, there could be a setting to explicitly verify the gpg keys + if (Files.exists(Paths.get(ascPath))) { + ascFileMethod.setValue(".m2/" + ascPath.replaceFirst("^(.*).m2/", "")); + ascFileMethod.setConfidence(FILENAME_CONFIDENCE); + ascFileMethod.setTechnique(Method.Technique.FILENAME); + methods.add(ascFileMethod); + overallConfidence += FILENAME_CONFIDENCE; } - fileMethod.setConfidence(0.1); - fileMethod.setTechnique(Method.Technique.FILENAME); final Path sha1FilePath = Paths.get(sha1Path); - if (sha1Hash.isPresent() && Files.exists(sha1FilePath)) { - try { - final Optional sha1Content = Files.readAllLines(sha1FilePath).stream().findFirst(); - if (sha1Content.isPresent()) { - String sha1Value = sha1Content.get().split(" ")[0].trim(); - if (sha1Value.equals(sha1Hash.get().getValue())) { - final Method hashMethod = new Method(); - hashMethod.setConfidence(0.9); - hashMethod.setTechnique(Method.Technique.HASH_COMPARISON); - hashMethod.setValue(sha1Value); - methods.add(hashMethod); + final Path md5FilePath = Paths.get(md5Path); + if (!sha1OrMd5Hashes.isEmpty()) { + Path hashFileToUse = null; + // Prefer the .sha1 file followed by .md5 + if (Files.exists(sha1FilePath)) { + hashFileToUse = sha1FilePath; + } else if (Files.exists(md5FilePath)) { + hashFileToUse = md5FilePath; + } + if (hashFileToUse != null) { + try { + final Optional hashFileContent = Files.readAllLines(hashFileToUse).stream().findFirst(); + if (hashFileContent.isPresent()) { + String storedHashValue = hashFileContent.get().split(" ")[0].trim(); + for (Hash sha1OrMd5Hash : sha1OrMd5Hashes) { + if (storedHashValue.equals(sha1OrMd5Hash.getValue())) { + final Method hashMethod = new Method(); + hashMethod.setConfidence(HASH_COMPARISON_CONFIDENCE); + overallConfidence += HASH_COMPARISON_CONFIDENCE; + hashMethod.setTechnique(Method.Technique.HASH_COMPARISON); + hashMethod.setValue(storedHashValue); + methods.add(hashMethod); + break; + } + } } + } catch (IOException e) { + // Invalid hash } - } catch (IOException e) { - // Invalid hash } } identity.setField(Identity.Field.PURL); - identity.setConfidence(1.0); - methods.add(fileMethod); + identity.setConfidence(overallConfidence); identity.setMethods(methods); evidence.setIdentity(identity); component.setEvidence(evidence);