GoModDependency.java
/*
* This file is part of dependency-check-core.
*
* 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.
*
* Copyright (c) 2019 Matthijs van den Bos. All Rights Reserved.
*/
package org.owasp.dependencycheck.data.golang;
import com.github.packageurl.MalformedPackageURLException;
import com.github.packageurl.PackageURLBuilder;
import org.apache.commons.lang3.StringUtils;
import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.EvidenceType;
import org.owasp.dependencycheck.dependency.naming.GenericIdentifier;
import org.owasp.dependencycheck.dependency.naming.Identifier;
import org.owasp.dependencycheck.dependency.naming.PurlIdentifier;
import org.owasp.dependencycheck.utils.Checksum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.owasp.dependencycheck.analyzer.GolangModAnalyzer.DEPENDENCY_ECOSYSTEM;
import static org.owasp.dependencycheck.analyzer.GolangModAnalyzer.GO_MOD;
/**
* Represents a Go module dependency.
*
* @author Matthijs van den Bos
*/
public class GoModDependency {
/**
* A reference to the logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(GoModDependency.class);
/**
* A list of license files we recognize.
*/
private static final Set<String> LICENSE_FILES = new HashSet<>(Arrays.asList("LICENSE", "LICENCE", "LICENSE.TXT",
"LICENSE.MD", "LICENCE.MD", "LICENSE.CODE", "LICENCE.CODE", "COPYING"));
/**
* The module path.
*/
private final String modulePath;
/**
* The version.
*/
private final String version;
/**
* A Package-URL builder.
*/
private final PackageURLBuilder packageURLBuilder;
/**
* The module Dir.
*/
private final String dir;
/**
* Constructs a new GoModDependency.
*
* @param modulePath the module path
* @param version the dependency version
* @param dir the directory that the module exists within
*/
GoModDependency(String modulePath, String version, String dir) {
this.modulePath = modulePath;
this.version = version;
this.dir = dir;
packageURLBuilder = PackageURLBuilder.aPackageURL().withType("golang");
}
/**
* Converts the GoModDependency into a Dependency object.
*
* @param parentDependency the parent dependency
* @return the resulting Dependency object
*/
public Dependency toDependency(Dependency parentDependency) {
return createDependency(parentDependency, modulePath, version);
}
/**
* Builds a dependency object based on the given data.
*
* @param parentDependency a reference to the parent dependency
* @param name the name of the dependency
* @param version the version of the dependency
* @return a new dependency object
*/
private Dependency createDependency(Dependency parentDependency, String name, String version) {
final Dependency dep = new Dependency(parentDependency.getActualFile(), true);
String namespace = null;
String vendor = null;
String moduleName = null;
String packageNamespace = null;
// separate the product from the vendor
final int lastSlash = name.lastIndexOf("/");
if (lastSlash > 0) {
packageNamespace = name.substring(0, lastSlash);
final int pos = packageNamespace.indexOf("/");
if (pos > 0) {
namespace = packageNamespace.substring(0, pos);
vendor = packageNamespace.substring(pos + 1);
}
moduleName = name.substring(lastSlash + 1);
} else {
moduleName = name;
}
final String filePath = String.format("%s:%s/%s/%s", parentDependency.getFilePath(), packageNamespace, moduleName, version);
//virtual dep need a sha1 for the html report
dep.setSha1sum(Checksum.getSHA1Checksum(filePath));
packageURLBuilder.withName(moduleName);
packageURLBuilder.withNamespace(packageNamespace);
if (StringUtils.isNotBlank(version)) {
packageURLBuilder.withVersion(version);
}
dep.setEcosystem(DEPENDENCY_ECOSYSTEM);
dep.setDisplayFileName(name + ":" + version);
dep.setName(moduleName);
if (StringUtils.isNotBlank(version)) {
dep.setVersion(version);
dep.setPackagePath(String.format("%s:%s", name, version));
}
dep.setFilePath(filePath);
if (vendor != null) {
dep.addEvidence(EvidenceType.VENDOR, GO_MOD, "vendor", vendor, Confidence.HIGHEST);
dep.addEvidence(EvidenceType.PRODUCT, GO_MOD, "vendor", vendor, Confidence.MEDIUM);
}
if (namespace != null && !"golang.org".equals(namespace)) {
dep.addEvidence(EvidenceType.VENDOR, GO_MOD, "namespace", namespace, Confidence.LOW);
}
dep.addEvidence(EvidenceType.PRODUCT, GO_MOD, "name", moduleName, Confidence.HIGHEST);
dep.addEvidence(EvidenceType.VENDOR, GO_MOD, "name", moduleName, Confidence.HIGH);
if (StringUtils.isNotBlank(version)) {
dep.addEvidence(EvidenceType.VERSION, GO_MOD, "version", version, Confidence.HIGHEST);
}
Identifier id;
try {
id = new PurlIdentifier(packageURLBuilder.build(), Confidence.HIGHEST);
} catch (MalformedPackageURLException ex) {
LOGGER.warn("Unable to create package-url identifier for `{}` in `{}` - reason: {}",
name, parentDependency.getFilePath(), ex.getMessage());
final StringBuilder value = new StringBuilder(name);
if (StringUtils.isNotBlank(version)) {
value.append("@").append(version);
}
id = new GenericIdentifier(value.toString(), Confidence.HIGH);
}
dep.addSoftwareIdentifier(id);
if (StringUtils.isNotBlank(dir)) {
final File file = new File(dir);
if (file.exists()) {
dep.setFilePath(file.getAbsolutePath());
dep.setActualFilePath(file.getAbsolutePath());
dep.setFileName(file.getName());
extractLicense(dep, file);
}
}
return dep;
}
/**
* Extracts the content of the license file into the dependency's license
* field.
*
* @param dependency the dependency being analyzed
* @param file the license file
*/
private void extractLicense(Dependency dependency, File file) {
final File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
if (LICENSE_FILES.contains(f.getName().toUpperCase())) {
try {
final String license = new String(Files.readAllBytes(f.toPath()), StandardCharsets.UTF_8);
dependency.setLicense(license);
break;
} catch (IOException ex) {
LOGGER.debug("Error reading license file `" + file.getAbsolutePath()
+ "`: " + ex.getMessage(), ex);
}
}
}
}
}
@Override
public String toString() {
return modulePath + ": " + version;
}
}