Merge branch 'brianf-dependencyNameImprovements'

This commit is contained in:
Jeremy Long
2017-10-02 06:48:29 -04:00
21 changed files with 293 additions and 53 deletions

View File

@@ -58,6 +58,12 @@ import org.owasp.dependencycheck.exception.InitializationException;
@Experimental
public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
/**
* A descriptor for the type of dependencies processed or added by this
* analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "CMAKE";
/**
* The logger.
*/
@@ -66,8 +72,7 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
/**
* Used when compiling file scanning regex patterns.
*/
private static final int REGEX_OPTIONS = Pattern.DOTALL
| Pattern.CASE_INSENSITIVE | Pattern.MULTILINE;
private static final int REGEX_OPTIONS = Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE;
/**
* Regex to extract the product information.
@@ -82,10 +87,8 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
*
* Group 2: Version
*/
private static final Pattern SET_VERSION = Pattern
.compile(
"^ *set\\s*\\(\\s*(\\w+)_version\\s+\"?(\\d+(?:\\.\\d+)+)[\\s\"]?\\)",
REGEX_OPTIONS);
private static final Pattern SET_VERSION = Pattern.compile(
"^ *set\\s*\\(\\s*(\\w+)_version\\s+\"?(\\d+(?:\\.\\d+)+)[\\s\"]?\\)", REGEX_OPTIONS);
/**
* Detects files that can be analyzed.
@@ -149,12 +152,10 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
* analyzing the dependency
*/
@Override
protected void analyzeDependency(Dependency dependency, Engine engine)
throws AnalysisException {
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
final File file = dependency.getActualFile();
final String parentName = file.getParentFile().getName();
final String name = file.getName();
dependency.setDisplayFileName(String.format("%s%c%s", parentName, File.separatorChar, name));
String contents;
try {
contents = FileUtils.readFileToString(file, Charset.defaultCharset()).trim();
@@ -162,7 +163,6 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
throw new AnalysisException(
"Problem occurred while reading dependency file.", e);
}
if (StringUtils.isNotBlank(contents)) {
final Matcher m = PROJECT.matcher(contents);
int count = 0;
@@ -175,6 +175,7 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
LOGGER.debug("Group 1: {}", group);
dependency.addEvidence(EvidenceType.PRODUCT, name, "Project", group, Confidence.HIGH);
dependency.addEvidence(EvidenceType.VENDOR, name, "Project", group, Confidence.HIGH);
dependency.setName(group);
}
LOGGER.debug("Found {} matches.", count);
analyzeSetVersionCommand(dependency, engine, contents);
@@ -213,7 +214,7 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
if (count > 1) {
//TODO - refactor so we do not assign to the parameter (checkstyle)
currentDep = new Dependency(dependency.getActualFile());
currentDep.setDisplayFileName(String.format("%s:%s", dependency.getDisplayFileName(), product));
currentDep.setEcosystem(DEPENDENCY_ECOSYSTEM);
final String filePath = String.format("%s:%s", dependency.getFilePath(), product);
currentDep.setFilePath(filePath);
@@ -227,10 +228,12 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
currentDep.setSha1sum(Checksum.getHex(sha1.digest(path)));
engine.addDependency(currentDep);
}
final String source = currentDep.getDisplayFileName();
final String source = currentDep.getFileName();
currentDep.addEvidence(EvidenceType.PRODUCT, source, "Product", product, Confidence.MEDIUM);
currentDep.addEvidence(EvidenceType.VENDOR, source, "Vendor", product, Confidence.MEDIUM);
currentDep.addEvidence(EvidenceType.VERSION, source, "Version", version, Confidence.MEDIUM);
currentDep.setName(product);
currentDep.setVersion(version);
}
LOGGER.debug("Found {} matches.", count);
}
@@ -241,9 +244,9 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
}
/**
* Returns the sha1 message digest.
* Returns the SHA1 message digest.
*
* @return the sha1 message digest
* @return the SHA1 message digest
*/
private MessageDigest getSha1MessageDigest() {
try {

View File

@@ -45,6 +45,12 @@ import org.owasp.dependencycheck.utils.Settings;
@ThreadSafe
public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer {
/**
* A descriptor for the type of dependencies processed or added by this
* analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "CocoaPod";
/**
* The logger.
*/
@@ -124,6 +130,7 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer {
protected void analyzeDependency(Dependency dependency, Engine engine)
throws AnalysisException {
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
String contents;
try {
contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
@@ -140,6 +147,7 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer {
if (!name.isEmpty()) {
dependency.addEvidence(EvidenceType.PRODUCT, PODSPEC, "name_project", name, Confidence.HIGHEST);
dependency.addEvidence(EvidenceType.VENDOR, PODSPEC, "name_project", name, Confidence.HIGHEST);
dependency.setName(name);
}
final String summary = determineEvidence(contents, blockVariable, "summary");
if (!summary.isEmpty()) {
@@ -156,14 +164,14 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer {
}
final String license = determineEvidence(contents, blockVariable, "licen[cs]es?");
if (!license.isEmpty()) {
dependency.addEvidence(EvidenceType.VENDOR, PODSPEC, "license", license, Confidence.HIGHEST);
dependency.setLicense(license);
}
final String version = determineEvidence(contents, blockVariable, "version");
if (!version.isEmpty()) {
dependency.addEvidence(EvidenceType.VERSION, PODSPEC, "version", version, Confidence.HIGHEST);
dependency.setVersion(version);
}
}
setPackagePath(dependency);

View File

@@ -47,6 +47,12 @@ import org.owasp.dependencycheck.dependency.EvidenceType;
@Experimental
public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer {
/**
* A descriptor for the type of dependencies processed or added by this
* analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "Composer";
/**
* The logger.
*/
@@ -105,20 +111,33 @@ public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer {
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
try (FileInputStream fis = new FileInputStream(dependency.getActualFile())) {
final ComposerLockParser clp = new ComposerLockParser(fis);
LOGGER.info("Checking composer.lock file {}", dependency.getActualFilePath());
LOGGER.debug("Checking composer.lock file {}", dependency.getActualFilePath());
clp.process();
//if dependencies are found in the lock, then there is always an empty shell dependency left behind for the
//composer.lock. The first pass through, reuse the top level dependency, and add new ones for the rest.
boolean processedAtLeastOneDep = false;
for (ComposerDependency dep : clp.getDependencies()) {
final Dependency d = new Dependency(dependency.getActualFile());
d.setDisplayFileName(String.format("%s:%s/%s", dependency.getDisplayFileName(), dep.getGroup(), dep.getProject()));
final String filePath = String.format("%s:%s/%s", dependency.getFilePath(), dep.getGroup(), dep.getProject());
final String filePath = String.format("%s:%s/%s/%s", dependency.getFilePath(), dep.getGroup(), dep.getProject(), dep.getVersion());
d.setName(dep.getProject());
d.setVersion(dep.getVersion());
d.setEcosystem(DEPENDENCY_ECOSYSTEM);
final MessageDigest sha1 = getSha1MessageDigest();
d.setFilePath(filePath);
d.setSha1sum(Checksum.getHex(sha1.digest(filePath.getBytes(Charset.defaultCharset()))));
d.addEvidence(EvidenceType.VENDOR, COMPOSER_LOCK, "vendor", dep.getGroup(), Confidence.HIGHEST);
d.addEvidence(EvidenceType.PRODUCT, COMPOSER_LOCK, "product", dep.getProject(), Confidence.HIGHEST);
d.addEvidence(EvidenceType.VERSION, COMPOSER_LOCK, "version", dep.getVersion(), Confidence.HIGHEST);
LOGGER.info("Adding dependency {}", d);
LOGGER.debug("Adding dependency {}", d.getDisplayFileName());
engine.addDependency(d);
//make sure we only remove the main dependency if we went through this loop at least once.
processedAtLeastOneDep = true;
}
// remove the dependency at the end because it's referenced in the loop itself.
// double check the name to be sure we only remove the generic entry.
if (processedAtLeastOneDep && dependency.getDisplayFileName().equalsIgnoreCase("composer.lock")) {
LOGGER.debug("Removing main redundant dependency {}", dependency.getDisplayFileName());
engine.removeDependency(dependency);
}
} catch (IOException ex) {
LOGGER.warn("Error opening dependency {}", dependency.getActualFilePath());

View File

@@ -73,6 +73,10 @@ import org.slf4j.LoggerFactory;
public class JarAnalyzer extends AbstractFileTypeAnalyzer {
//<editor-fold defaultstate="collapsed" desc="Constants and Member Variables">
/**
* A descriptor for the type of dependencies processed or added by this analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "Java";
/**
* The logger.
*/
@@ -258,6 +262,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
final boolean hasPOM = analyzePOM(dependency, classNames, engine);
final boolean addPackagesAsEvidence = !(hasManifest && hasPOM);
analyzePackageNames(classNames, dependency, addPackagesAsEvidence);
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
} catch (IOException ex) {
throw new AnalysisException("Exception occurred reading the JAR file (" + dependency.getFileName() + ").", ex);
}

View File

@@ -55,17 +55,19 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(NodePackageAnalyzer.class);
/**
* A descriptor for the type of dependencies processed or added by this
* analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "npm";
/**
* The name of the analyzer.
*/
private static final String ANALYZER_NAME = "Node.js Package Analyzer";
/**
* The phase that this analyzer is intended to run in.
*/
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
/**
* The file name to scan.
*/
@@ -124,6 +126,7 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
@Override
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
final File file = dependency.getActualFile();
if (!file.isFile() || file.length() == 0) {
return;
@@ -134,6 +137,7 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
final Object value = json.get("name");
if (value instanceof JsonString) {
final String valueString = ((JsonString) value).getString();
dependency.setName(valueString);
dependency.addEvidence(EvidenceType.PRODUCT, PACKAGE_JSON, "name", valueString, Confidence.HIGHEST);
dependency.addEvidence(EvidenceType.VENDOR, PACKAGE_JSON, "name_project",
String.format("%s_project", valueString), Confidence.LOW);
@@ -143,8 +147,8 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
}
addToEvidence(dependency, EvidenceType.PRODUCT, json, "description");
addToEvidence(dependency, EvidenceType.VENDOR, json, "author");
addToEvidence(dependency, EvidenceType.VERSION, json, "version");
dependency.setDisplayFileName(String.format("%s/%s", file.getParentFile().getName(), file.getName()));
final String version = addToEvidence(dependency, EvidenceType.VERSION, json, "version");
dependency.setVersion(version);
} catch (JsonException e) {
LOGGER.warn("Failed to parse package.json file.", e);
} catch (IOException e) {
@@ -159,23 +163,26 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
* @param dep the dependency to add the evidence
* @param t the type of evidence to add
* @param json information from node.js
* @return the actual string set into evidence
* @param key the key to obtain the data from the json information
*/
private void addToEvidence(Dependency dep, EvidenceType t, JsonObject json, String key) {
private String addToEvidence(Dependency dep, EvidenceType t, JsonObject json, String key) {
String evidenceStr = null;
if (json.containsKey(key)) {
final JsonValue value = json.get(key);
if (value instanceof JsonString) {
dep.addEvidence(t, PACKAGE_JSON, key, ((JsonString) value).getString(), Confidence.HIGHEST);
evidenceStr = ((JsonString) value).getString();
dep.addEvidence(t, PACKAGE_JSON, key, evidenceStr, Confidence.HIGHEST);
} else if (value instanceof JsonObject) {
final JsonObject jsonObject = (JsonObject) value;
for (final Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
final String property = entry.getKey();
final JsonValue subValue = entry.getValue();
if (subValue instanceof JsonString) {
evidenceStr = ((JsonString) subValue).getString();
dep.addEvidence(t, PACKAGE_JSON,
String.format("%s.%s", key, property),
((JsonString) subValue).getString(),
evidenceStr,
Confidence.HIGHEST);
} else {
LOGGER.warn("JSON sub-value not string as expected: {}", subValue);
@@ -185,5 +192,6 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
LOGGER.warn("JSON value not string or JSON object as expected: {}", value);
}
}
return evidenceStr;
}
}

View File

@@ -59,6 +59,12 @@ import org.owasp.dependencycheck.dependency.EvidenceType;
@ThreadSafe
public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
/**
* A descriptor for the type of dependencies processed or added by this
* analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "Python.Dist";
/**
* Name of egg metadata files to analyze.
*/
@@ -167,6 +173,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
@Override
protected void analyzeDependency(Dependency dependency, Engine engine)
throws AnalysisException {
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
final File actualFile = dependency.getActualFile();
if (WHL_FILTER.accept(actualFile)) {
collectMetadataFromArchiveFormat(dependency, DIST_INFO_FILTER,
@@ -180,7 +188,6 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
if (metadata || PKG_INFO.equals(name)) {
final File parent = actualFile.getParentFile();
final String parentName = parent.getName();
dependency.setDisplayFileName(parentName + "/" + name);
if (parent.isDirectory()
&& (metadata && parentName.endsWith(".dist-info")
|| parentName.endsWith(".egg-info") || "EGG-INFO"
@@ -281,6 +288,9 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
final InternetHeaders headers = getManifestProperties(file);
addPropertyToEvidence(dependency, EvidenceType.VERSION, Confidence.HIGHEST, headers, "Version");
addPropertyToEvidence(dependency, EvidenceType.PRODUCT, Confidence.HIGHEST, headers, "Name");
addPropertyToEvidence(dependency, EvidenceType.PRODUCT, Confidence.MEDIUM, headers, "Name");
dependency.setName(headers.getHeader("Name", null));
dependency.setVersion(headers.getHeader("Version", null));
final String url = headers.getHeader("Home-page", null);
if (StringUtils.isNotBlank(url)) {
if (UrlStringUtils.isUrl(url)) {

View File

@@ -48,6 +48,12 @@ import org.owasp.dependencycheck.exception.InitializationException;
@ThreadSafe
public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
/**
* A descriptor for the type of dependencies processed or added by this
* analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "Python.Pkg";
/**
* Used when compiling file scanning regex patterns.
*/
@@ -183,6 +189,7 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
@Override
protected void analyzeDependency(Dependency dependency, Engine engine)
throws AnalysisException {
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
final File file = dependency.getActualFile();
final File parent = file.getParentFile();
final String parentName = parent.getName();
@@ -190,8 +197,8 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
//by definition, the containing folder of __init__.py is considered the package, even the file is empty:
//"The __init__.py files are required to make Python treat the directories as containing packages"
//see section "6.4 Packages" from https://docs.python.org/2/tutorial/modules.html;
dependency.setDisplayFileName(parentName + "/__init__.py");
dependency.addEvidence(EvidenceType.PRODUCT, file.getName(), "PackageName", parentName, Confidence.HIGHEST);
dependency.setName(parentName);
final File[] fileList = parent.listFiles(PY_FILTER);
if (fileList != null) {
@@ -312,6 +319,9 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
final boolean found = matcher.find();
if (found) {
dependency.addEvidence(type, source, name, matcher.group(4), confidence);
if (type == EvidenceType.VERSION) {
dependency.setVersion(matcher.group(4));
}
}
return found;
}

View File

@@ -51,6 +51,11 @@ import org.owasp.dependencycheck.dependency.Dependency;
@ThreadSafe
public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer {
/**
* A descriptor for the type of dependencies processed or added by this analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "Ruby.Bundle";
/**
* The name of the analyzer.
*/
@@ -99,7 +104,7 @@ public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer {
protected void analyzeDependency(Dependency dependency, Engine engine)
throws AnalysisException {
super.analyzeDependency(dependency, engine);
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
//find the corresponding gem folder for this .gemspec stub by "bundle install --deployment"
final File gemspecFile = dependency.getActualFile();
final String gemFileName = gemspecFile.getName();

View File

@@ -50,6 +50,11 @@ import org.slf4j.LoggerFactory;
@ThreadSafe
public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
/**
* A descriptor for the type of dependencies processed or added by this
* analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "Ruby.Bundle";
/**
* The logger.
*/
@@ -58,17 +63,14 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
* The name of the analyzer.
*/
private static final String ANALYZER_NAME = "Ruby Gemspec Analyzer";
/**
* The phase that this analyzer is intended to run in.
*/
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
/**
* The gemspec file extension.
*/
private static final String GEMSPEC = "gemspec";
/**
* The file filter containing the list of file extensions that can be
* analyzed.
@@ -133,6 +135,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
@Override
protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
String contents;
try {
contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
@@ -148,6 +151,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
final String name = addStringEvidence(dependency, EvidenceType.PRODUCT, contents, blockVariable, "name", "name", Confidence.HIGHEST);
if (!name.isEmpty()) {
dependency.addEvidence(EvidenceType.VENDOR, GEMSPEC, "name_project", name + "_project", Confidence.LOW);
dependency.setName(name);
}
addStringEvidence(dependency, EvidenceType.PRODUCT, contents, blockVariable, "summary", "summary", Confidence.LOW);
@@ -160,9 +164,10 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
blockVariable, "version", "version", Confidence.HIGHEST);
if (value.length() < 1) {
addEvidenceFromVersionFile(dependency, EvidenceType.VERSION, dependency.getActualFile());
} else {
dependency.setVersion(value);
}
}
setPackagePath(dependency);
}

View File

@@ -45,6 +45,12 @@ import org.owasp.dependencycheck.utils.Settings;
@ThreadSafe
public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer {
/**
* A descriptor for the type of dependencies processed or added by this
* analyzer
*/
public static final String DEPENDENCY_ECOSYSTEM = "Swift.PM";
/**
* The name of the analyzer.
*/
@@ -121,6 +127,8 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer {
protected void analyzeDependency(Dependency dependency, Engine engine)
throws AnalysisException {
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
String contents;
try {
contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
@@ -135,11 +143,18 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer {
return;
}
//SPM is currently under development for SWIFT 3. Its current metadata includes package name and dependencies.
//Future interesting metadata: version, license, homepage, author, summary, etc.
// TODO SPM is currently under development for SWIFT 3. Its current metadata includes
// package name and dependencies.
// Future interesting metadata: version, license, homepage, author, summary,
// etc.
final String name = addStringEvidence(dependency, EvidenceType.PRODUCT, packageDescription, "name", "name", Confidence.HIGHEST);
if (name != null && !name.isEmpty()) {
dependency.addEvidence(EvidenceType.VENDOR, SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST);
dependency.setName(name);
} else {
// if we can't get the name from the meta, then assume the name is the name of
// the parent folder containing the package.swift file.
dependency.setName(dependency.getActualFile().getParentFile().getName());
}
}
setPackagePath(dependency);

View File

@@ -60,7 +60,7 @@ public class ComposerLockParser {
* @param inputStream the InputStream to parse
*/
public ComposerLockParser(InputStream inputStream) {
LOGGER.info("Creating a ComposerLockParser");
LOGGER.debug("Creating a ComposerLockParser");
this.jsonReader = Json.createReader(inputStream);
this.composerDependencies = new ArrayList<>();
}
@@ -69,7 +69,7 @@ public class ComposerLockParser {
* Process the input stream to create the list of dependencies.
*/
public void process() {
LOGGER.info("Beginning Composer lock processing");
LOGGER.debug("Beginning Composer lock processing");
try {
final JsonObject composer = jsonReader.readObject();
if (composer.containsKey("packages")) {

View File

@@ -126,6 +126,22 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
*/
private boolean isVirtual = false;
/**
* Defines the human-recognizable name for the dependency
*/
private String name;
/**
* Defines the human-recognizable version for the dependency
*/
private String version;
/**
* A descriptor for the type of dependency based on which analyzer added it
* or collected evidence about it
*/
private String ecosystem;
/**
* Returns the package path.
*
@@ -249,15 +265,22 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
/**
* Returns the file name to display in reports; if no display file name has
* been set it will default to the actual file name.
* been set it will default to constructing a name based on the name and
* version fields, otherwise it will return the actual file name.
*
* @return the file name to display
*/
public String getDisplayFileName() {
if (displayName == null) {
return this.fileName;
if (displayName != null) {
return displayName;
}
return this.displayName;
if (name == null) {
return fileName;
}
if (version == null) {
return name;
}
return name + ":" + version;
}
/**
@@ -487,6 +510,20 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
this.license = license;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* Get the unmodifiable sorted set of vulnerabilities.
*
@@ -729,4 +766,32 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
public synchronized void addSuppressedVulnerabilities(List<Vulnerability> vulns) {
this.suppressedVulnerabilities.addAll(vulns);
}
/**
* @return the version
*/
public String getVersion() {
return version;
}
/**
* @param version the version to set
*/
public void setVersion(String version) {
this.version = version;
}
/**
* @return the ecosystem
*/
public String getEcosystem() {
return ecosystem;
}
/**
* @param ecosystem the ecosystem to set
*/
public void setEcosystem(String ecosystem) {
this.ecosystem = ecosystem;
}
}

View File

@@ -75,6 +75,7 @@ public class CMakeAnalyzerTest extends BaseDBTestCase {
/**
* Cleanup any resources used.
*
* @throws Exception if there is a problem
*/
@After
@Override
@@ -135,6 +136,21 @@ public class CMakeAnalyzerTest extends BaseDBTestCase {
assertProductEvidence(result, product);
}
/**
* Test whether expected evidence is gathered from OpenCV's CVDetectPython.
*
* @throws AnalysisException is thrown when an exception occurs.
*/
@Test
public void testAnalyzeCMakeListsPython() throws AnalysisException {
final Dependency result = new Dependency(BaseTest.getResourceAsFile(
this, "cmake/opencv/cmake/OpenCVDetectPython.cmake"));
analyzer.analyze(result, null);
//this one finds nothing so it falls through to the filename. Can we do better?
assertEquals("OpenCVDetectPython.cmake", result.getDisplayFileName());
}
private void assertProductEvidence(Dependency result, String product) {
boolean found = false;
for (Evidence e : result.getEvidence(EvidenceType.PRODUCT)) {

View File

@@ -32,10 +32,13 @@ import org.owasp.dependencycheck.exception.InitializationException;
import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.lang3.ArrayUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
/**
* Unit tests for NodePackageAnalyzer.
@@ -92,6 +95,25 @@ public class ComposerLockAnalyzerTest extends BaseDBTestCase {
assertTrue(analyzer.accept(new File("composer.lock")));
}
/**
* Test of basic additions to the dependency list by parsing the
* composer.lock file
*
* @throws AnalysisException is thrown when an exception occurs.
*/
@Test
public void testRemoveRedundantParent() throws Exception {
try (Engine engine = new Engine(getSettings())) {
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "composer.lock"));
//test that we don't remove the parent if it's not redundant by name
result.setDisplayFileName("NotComposer.Lock");
engine.addDependency(result);
analyzer.analyze(result, engine);
//make sure the composer.lock is not removed
assertTrue(ArrayUtils.contains(engine.getDependencies(), result));
}
}
/**
* Test of inspect method, of class PythonDistributionAnalyzer.
*
@@ -102,7 +124,17 @@ public class ComposerLockAnalyzerTest extends BaseDBTestCase {
try (Engine engine = new Engine(getSettings())) {
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
"composer.lock"));
//simulate normal operation when the composer.lock is already added to the engine as a dependency
engine.addDependency(result);
analyzer.analyze(result, engine);
//make sure the redundant composer.lock is removed
assertFalse(ArrayUtils.contains(engine.getDependencies(), result));
assertEquals(30, engine.getDependencies().length);
Dependency d = engine.getDependencies()[0];
assertEquals("classpreloader", d.getName());
assertEquals("2.0.0", d.getVersion());
assertThat(d.getDisplayFileName(), equalTo("classpreloader:2.0.0"));
assertEquals(ComposerLockAnalyzer.DEPENDENCY_ECOSYSTEM, d.getEcosystem());
}
}

View File

@@ -60,6 +60,7 @@ public class JarAnalyzerTest extends BaseTest {
file = BaseTest.getResourceAsFile(this, "dwr.jar");
result = new Dependency(file);
instance.analyze(result, null);
assertEquals(JarAnalyzer.DEPENDENCY_ECOSYSTEM,result.getEcosystem());
boolean found = false;
for (Evidence e : result.getEvidence(EvidenceType.VENDOR)) {
if (e.getName().equals("url")) {

View File

@@ -101,5 +101,8 @@ public class NodePackageAnalyzerTest extends BaseTest {
assertThat(vendorString, containsString("dns-sync_project"));
assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("dns-sync"));
assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("0.1.0"));
assertEquals(NodePackageAnalyzer.DEPENDENCY_ECOSYSTEM, result.getEcosystem());
assertEquals("dns-sync", result.getName());
assertEquals("0.1.0", result.getVersion());
}
}

View File

@@ -120,8 +120,7 @@ public class PythonDistributionAnalyzerTest extends BaseTest {
final Dependency result = new Dependency(BaseTest.getResourceAsFile(
this, "python/site-packages/Django-1.7.2.dist-info/METADATA"));
djangoAssertions(result);
assertEquals("Django-1.7.2.dist-info/METADATA", result.getDisplayFileName());
}
}
private void djangoAssertions(final Dependency result)
throws AnalysisException {
@@ -136,6 +135,10 @@ public class PythonDistributionAnalyzerTest extends BaseTest {
}
}
assertTrue("Version 1.7.2 not found in Django dependency.", found);
assertEquals("1.7.2",result.getVersion());
assertEquals("Django",result.getName());
assertEquals("Django:1.7.2",result.getDisplayFileName());
assertEquals(PythonDistributionAnalyzer.DEPENDENCY_ECOSYSTEM,result.getEcosystem());
}
@Test
@@ -188,5 +191,9 @@ public class PythonDistributionAnalyzerTest extends BaseTest {
}
}
assertTrue("Version 0.0.1 not found in EggTest dependency.", found);
assertEquals("0.0.1",result.getVersion());
assertEquals("EggTest",result.getName());
assertEquals("EggTest:0.0.1",result.getDisplayFileName());
assertEquals(PythonDistributionAnalyzer.DEPENDENCY_ECOSYSTEM,result.getEcosystem());
}
}

View File

@@ -103,5 +103,9 @@ public class PythonPackageAnalyzerTest extends BaseTest {
}
}
assertTrue("Version 0.0.1 not found in EggTest dependency.", found);
assertEquals("0.0.1",result.getVersion());
assertEquals("eggtest",result.getName());
assertEquals("eggtest:0.0.1",result.getDisplayFileName());
assertEquals(PythonPackageAnalyzer.DEPENDENCY_ECOSYSTEM,result.getEcosystem());
}
}

View File

@@ -85,6 +85,7 @@ public class RubyBundlerAnalyzerTest extends BaseTest {
public void testSupportsFiles() {
assertThat(analyzer.accept(new File("test.gemspec")), is(false));
assertThat(analyzer.accept(new File("specifications" + File.separator + "test.gemspec")), is(true));
assertThat(analyzer.accept(new File("gemspec.lock")), is(false));
}
/**
@@ -97,7 +98,7 @@ public class RubyBundlerAnalyzerTest extends BaseTest {
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
"ruby/vulnerable/gems/rails-4.1.15/vendor/bundle/ruby/2.2.0/specifications/dalli-2.7.5.gemspec"));
analyzer.analyze(result, null);
final String vendorString = result.getEvidence(EvidenceType.VENDOR).toString();
assertThat(vendorString, containsString("Peter M. Goldstein"));
assertThat(vendorString, containsString("Mike Perham"));
@@ -107,5 +108,9 @@ public class RubyBundlerAnalyzerTest extends BaseTest {
assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("dalli"));
assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("High performance memcached client for Ruby"));
assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("2.7.5"));
assertEquals("dalli", result.getName());
assertEquals("2.7.5", result.getVersion());
assertEquals(RubyBundlerAnalyzer.DEPENDENCY_ECOSYSTEM, result.getEcosystem());
assertEquals("dalli:2.7.5", result.getDisplayFileName());
}
}

View File

@@ -84,6 +84,7 @@ public class RubyGemspecAnalyzerTest extends BaseTest {
@Test
public void testSupportsFiles() {
assertThat(analyzer.accept(new File("test.gemspec")), is(true));
assertThat(analyzer.accept(new File("gemspec.lock")), is(false));
// assertThat(analyzer.accept(new File("Rakefile")), is(true));
}
@@ -98,24 +99,33 @@ public class RubyGemspecAnalyzerTest extends BaseTest {
"ruby/vulnerable/gems/specifications/rest-client-1.7.2.gemspec"));
analyzer.analyze(result, null);
final String vendorString = result.getEvidence(EvidenceType.VENDOR).toString();
assertEquals(RubyGemspecAnalyzer.DEPENDENCY_ECOSYSTEM, result.getEcosystem());
assertThat(vendorString, containsString("REST Client Team"));
assertThat(vendorString, containsString("rest-client_project"));
assertThat(vendorString, containsString("rest.client@librelist.com"));
assertThat(vendorString, containsString("https://github.com/rest-client/rest-client"));
assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("rest-client"));
assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("1.7.2"));
assertEquals("rest-client", result.getName());
assertEquals("1.7.2", result.getVersion());
assertEquals("rest-client:1.7.2", result.getDisplayFileName());
}
/**
* Test Rakefile analysis.
*
* @throws AnalysisException is thrown when an exception occurs.
*/
//@Test TODO: place holder to test Rakefile support
//@Test
//TODO: place holder to test Rakefile support
public void testAnalyzeRakefile() throws AnalysisException {
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
"ruby/vulnerable/gems/rails-4.1.15/vendor/bundle/ruby/2.2.0/gems/pg-0.18.4/Rakefile"));
analyzer.analyze(result, null);
assertTrue(result.size()>0);
assertTrue(result.size() > 0);
assertEquals(RubyGemspecAnalyzer.DEPENDENCY_ECOSYSTEM, result.getEcosystem());
assertEquals("pg", result.getName());
assertEquals("0.18.4", result.getVersion());
assertEquals("pg:0.18.4", result.getDisplayFileName());
}
}

View File

@@ -10,6 +10,7 @@ import org.owasp.dependencycheck.dependency.Dependency;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
import java.io.File;
import org.owasp.dependencycheck.dependency.EvidenceType;
@@ -60,7 +61,7 @@ public class SwiftAnalyzersTest extends BaseTest {
spmAnalyzer.close();
spmAnalyzer = null;
super.tearDown();
}
@@ -110,9 +111,13 @@ public class SwiftAnalyzersTest extends BaseTest {
assertThat(vendorString, containsString("Carlos Vidal"));
assertThat(vendorString, containsString("https://github.com/nakiostudio/EasyPeasy"));
assertThat(vendorString, containsString("MIT"));
assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("EasyPeasy"));
assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("0.2.3"));
assertThat(result.getName(), equalTo("EasyPeasy"));
assertThat(result.getVersion(), equalTo("0.2.3"));
assertThat(result.getDisplayFileName(), equalTo("EasyPeasy:0.2.3"));
assertThat(result.getLicense(), containsString("MIT"));
assertThat(result.getEcosystem(), equalTo(CocoaPodsAnalyzer.DEPENDENCY_ECOSYSTEM));
}
/**
@@ -127,5 +132,9 @@ public class SwiftAnalyzersTest extends BaseTest {
spmAnalyzer.analyze(result, null);
assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("Gloss"));
assertThat(result.getName(), equalTo("Gloss"));
//TODO: when version processing is added, update the expected name.
assertThat(result.getDisplayFileName(), equalTo("Gloss"));
assertThat(result.getEcosystem(), equalTo(SwiftPackageManagerAnalyzer.DEPENDENCY_ECOSYSTEM));
}
}