From 95d3d17d83cbc998d168513d605568047943f099 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Fri, 28 Aug 2015 13:58:49 -0400 Subject: [PATCH] Ruby Bundler: Now successfully creating temp files for dependency objects. --- .../analyzer/RubyBundleAuditAnalyzer.java | 47 +++++++-- .../analyzer/RubyBundleAuditAnalyzerTest.java | 99 +++++++++++++++++++ 2 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java index 6ab97b166..53d013997 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java @@ -17,8 +17,10 @@ */ package org.owasp.dependencycheck.analyzer; +import org.apache.commons.io.FileUtils; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; @@ -50,9 +52,11 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { private static final FileFilter FILTER = FileFilterBuilder.newInstance().addFilenames("Gemfile.lock").build(); + public static final String NAME = "Name: "; + public static final String VERSION = "Version: "; /** - * @return a filter that accepts files named Rakefile or matching the glob pattern, *.gemspec + * @return a filter that accepts files named Gemfile.lock */ @Override protected FileFilter getFileFilter() { @@ -65,7 +69,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { * @return a handle to the process */ private Process launchBundleAudit(File folder) throws AnalysisException { - if (!folder.isDirectory()){ + if (!folder.isDirectory()) { throw new AnalysisException(String.format("%s should have been a directory.", folder.getAbsolutePath())); } final List args = new ArrayList(); @@ -165,13 +169,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { BufferedReader rdr = null; try { rdr = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); - while (rdr.ready()) { - final String nextLine = rdr.readLine(); - if (null == nextLine) { - break; - } - LOGGER.info(String.format("bundle-audit (%s): %s", parentFile.getName(), nextLine)); - } + processBundlerAuditOutput(dependency, engine, rdr); } catch (IOException ioe) { LOGGER.warn("bundle-audit failure", ioe); } finally { @@ -185,4 +183,35 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } } + + private void processBundlerAuditOutput(Dependency original, Engine engine, BufferedReader rdr) throws IOException { + final String parentName = original.getActualFile().getParentFile().getName(); + final String fileName = original.getFileName(); + Dependency dependency = null; + while (rdr.ready()) { + final String nextLine = rdr.readLine(); + if (null == nextLine) { + break; + } else if (nextLine.startsWith(NAME)) { + final String gem = nextLine.substring(NAME.length()); + final File tempFile = File.createTempFile("Gemfile-" + gem, ".lock", Settings.getTempDirectory()); + final String displayFileName = String.format("%s%c%s:%s", parentName, File.separatorChar, fileName, gem); + FileUtils.write(tempFile, displayFileName); // unique contents to avoid dependency bundling + dependency = new Dependency(tempFile); + engine.getDependencies().add(dependency); + dependency.setDisplayFileName(displayFileName); + dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST); + LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + } else if (nextLine.startsWith(VERSION)) { + if (null != dependency) { + dependency.getVersionEvidence().addEvidence( + "bundler-audit", + "Version", + nextLine.substring(VERSION.length()), + Confidence.HIGHEST); + } + LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + } + } + } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java new file mode 100644 index 000000000..91a7743ea --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java @@ -0,0 +1,99 @@ +/* + * 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) 2015 Institute for Defense Analyses. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.data.nvdcve.DatabaseException; +import org.owasp.dependencycheck.dependency.Dependency; + +import java.io.File; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +/** + * Unit tests for {@link RubyBundleAuditAnalyzer}. + * + * @author Dale Visser + */ +public class RubyBundleAuditAnalyzerTest extends BaseTest { + + /** + * The analyzer to test. + */ + RubyBundleAuditAnalyzer analyzer; + + /** + * Correctly setup the analyzer for testing. + * + * @throws Exception thrown if there is a problem + */ + @Before + public void setUp() throws Exception { + analyzer = new RubyBundleAuditAnalyzer(); + analyzer.setFilesMatched(true); + analyzer.initialize(); + } + + /** + * Cleanup the analyzer's temp files, etc. + * + * @throws Exception thrown if there is a problem + */ + @After + public void tearDown() throws Exception { + analyzer.close(); + analyzer = null; + } + + /** + * Test Ruby Gemspec name. + */ + @Test + public void testGetName() { + assertThat(analyzer.getName(), is("Ruby Bundle Audit Analyzer")); + } + + /** + * Test Ruby Bundler Audit file support. + */ + @Test + public void testSupportsFiles() { + assertThat(analyzer.accept(new File("Gemfile.lock")), is(true)); + } + + /** + * Test Ruby BundlerAudit analysis. + * + * @throws AnalysisException is thrown when an exception occurs. + */ + @Test + public void testAnalysis() throws AnalysisException, DatabaseException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, + "ruby/vulnerable/Gemfile.lock")); + final Engine engine = new Engine(); + analyzer.analyze(result, engine); + assertThat(engine.getDependencies().size(), is(not(0))); + } +}