diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzer.java index cccfeb010..6852413f5 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzer.java @@ -103,20 +103,34 @@ 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()); + d.setDisplayFileName(String.format("%s:%s/%s/%s", dependency.getDisplayFileName(), dep.getGroup(), dep.getProject(), dep.getVersion())); + final String filePath = String.format("%s:%s/%s/%s", dependency.getFilePath(), dep.getGroup(), dep.getProject(), dep.getVersion()); + final MessageDigest sha1 = getSha1MessageDigest(); d.setFilePath(filePath); d.setSha1sum(Checksum.getHex(sha1.digest(filePath.getBytes(Charset.defaultCharset())))); d.getVendorEvidence().addEvidence(COMPOSER_LOCK, "vendor", dep.getGroup(), Confidence.HIGHEST); d.getProductEvidence().addEvidence(COMPOSER_LOCK, "product", dep.getProject(), Confidence.HIGHEST); d.getVersionEvidence().addEvidence(COMPOSER_LOCK, "version", dep.getVersion(), Confidence.HIGHEST); - LOGGER.info("Adding dependency {}", d); + LOGGER.debug("Adding dependency {}", d.getDisplayFileName()); engine.getDependencies().add(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.getDependencies().remove(dependency); + } } catch (IOException ex) { LOGGER.warn("Error opening dependency {}", dependency.getActualFilePath()); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerLockParser.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerLockParser.java index 0803276d1..648f4a7f0 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerLockParser.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerLockParser.java @@ -58,7 +58,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<>(); } @@ -67,7 +67,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")) { diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzerTest.java index 30c72b25a..d94acfe37 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzerTest.java @@ -36,6 +36,8 @@ import java.security.NoSuchAlgorithmException; 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. @@ -88,6 +90,25 @@ public class ComposerLockAnalyzerTest extends BaseDBTestCase { assertTrue(analyzer.accept(new File("composer.lock"))); } + /** + * Test of basic additions to the depdnency list by parsing the composer.lock file + * + * @throws AnalysisException is thrown when an exception occurs. + */ + @Test + public void testRemoveRedundantParent() throws Exception { + final Engine engine = new Engine(); + + 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.getDependencies().add(result); + analyzer.analyze(result, engine); + //make sure the composer.lock is not removed + assertTrue(engine.getDependencies().contains(result)); + } + /** * Test of inspect method, of class PythonDistributionAnalyzer. * @@ -96,11 +117,18 @@ public class ComposerLockAnalyzerTest extends BaseDBTestCase { @Test public void testAnalyzePackageJson() throws Exception { final Engine engine = new Engine(); + 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.getDependencies().add(result); analyzer.analyze(result, engine); + //make sure the redundant composer.lock is removed + assertFalse(engine.getDependencies().contains(result)); + assertEquals(30,engine.getDependencies().size()); + assertThat(engine.getDependencies().get(0).getDisplayFileName(),equalTo("composer.lock:classpreloader/classpreloader/2.0.0")); } - + @Test(expected = InitializationException.class) public void analyzerIsDisabledInCaseOfMissingMessageDigest() throws InitializationException {