From 5c32ecd8e1215be298a81c6c136cb625e81ac59f Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Tue, 18 Aug 2015 16:59:39 -0400 Subject: [PATCH 01/19] Ruby Analyzer: Added bundle-audit analyzer. So far just launches if available and logs the output. --- .../java/org/owasp/dependencycheck/App.java | 5 + .../org/owasp/dependencycheck/CliParser.java | 20 +- .../analyzer/RubyBundleAuditAnalyzer.java | 187 ++++++++++++++++++ .../analyzer/RubyGemspecAnalyzer.java | 5 +- ...rg.owasp.dependencycheck.analyzer.Analyzer | 3 +- .../owasp/dependencycheck/utils/Settings.java | 4 + 6 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java index 648c32cd3..5d33a9b66 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java @@ -27,6 +27,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.cli.ParseException; +import org.apache.commons.lang.StringUtils; import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; @@ -354,6 +355,10 @@ public class App { if (pathToMono != null && !pathToMono.isEmpty()) { Settings.setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono); } + String pathToBundleAudit = cli.getPathToBundleAudit(); + if (!StringUtils.isEmpty(pathToBundleAudit)){ + Settings.setString(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, pathToBundleAudit); + } if (cveBase12 != null && !cveBase12.isEmpty()) { Settings.setString(Settings.KEYS.CVE_SCHEMA_1_2, cveBase12); Settings.setString(Settings.KEYS.CVE_SCHEMA_2_0, cveBase20); diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java index 7101fa389..dbd48215f 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java @@ -328,6 +328,10 @@ public final class CliParser { .withDescription("The path to Mono for .NET Assembly analysis on non-windows systems.") .create(); + final Option pathToBundleAudit = OptionBuilder.withArgName("path").hasArg() + .withLongOpt(ARGUMENT.PATH_TO_BUNDLE_AUDIT) + .withDescription("The path to bundle-audit for Gem bundle analysis.").create(); + final Option connectionTimeout = OptionBuilder.withArgName("timeout").hasArg().withLongOpt(ARGUMENT.CONNECTION_TIMEOUT) .withDescription("The connection timeout (in milliseconds) to use when downloading resources.") .create(ARGUMENT.CONNECTION_TIMEOUT_SHORT); @@ -426,7 +430,8 @@ public final class CliParser { .addOption(nexusUrl) .addOption(nexusUsesProxy) .addOption(additionalZipExtensions) - .addOption(pathToMono); + .addOption(pathToMono) + .addOption(pathToBundleAudit); } /** @@ -690,6 +695,15 @@ public final class CliParser { return line.getOptionValue(ARGUMENT.PATH_TO_MONO); } + /** + * Returns the path to bundle-audit for Ruby bundle analysis. + * + * @return the path to Mono + */ + public String getPathToBundleAudit() { + return line.getOptionValue(ARGUMENT.PATH_TO_BUNDLE_AUDIT); + } + /** * Returns the output format specified on the command line. Defaults to HTML if no format was specified. * @@ -1160,5 +1174,9 @@ public final class CliParser { * Exclude path argument. */ public static final String EXCLUDE = "exclude"; + /** + * The CLI argument name for setting the path to bundle-audit for Ruby bundle analysis. + */ + public static final String PATH_TO_BUNDLE_AUDIT = "bundleAudit"; } } 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 new file mode 100644 index 000000000..10e0dcce1 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java @@ -0,0 +1,187 @@ +/* + * 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.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.utils.FileFilterBuilder; +import org.owasp.dependencycheck.utils.Settings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Used to analyze Ruby Bundler Gemspec.lock files utilizing the 3rd party bundle-audit tool. + * + * @author Dale Visser + */ +public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { + + private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzer.class); + + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "Ruby Bundle Audit Analyzer"; + + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; + + private static final FileFilter FILTER = + FileFilterBuilder.newInstance().addFilenames("Gemfile.lock").build(); + + /** + * @return a filter that accepts files named Rakefile or matching the glob pattern, *.gemspec + */ + @Override + protected FileFilter getFileFilter() { + return FILTER; + } + + /** + * Launch bundle-audit. + * + * @return a handle to the process + */ + private Process launchBundleAudit(File folder) throws AnalysisException { + if (!folder.isDirectory()){ + throw new AnalysisException(String.format("%s should have been a directory.", folder.getAbsolutePath())); + } + final List args = new ArrayList(); + final String bundleAuditPath = Settings.getString(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH); + args.add(null == bundleAuditPath ? "bundle-audit" : bundleAuditPath); + args.add("check"); + final ProcessBuilder builder = new ProcessBuilder(args); + builder.directory(folder); + try { + return builder.start(); + } catch (IOException ioe) { + throw new AnalysisException("bundle-audit failure", ioe); + } + } + + /** + * Initialize the analyzer. In this case, extract GrokAssembly.exe to a temporary location. + * + * @throws Exception if anything goes wrong + */ + @Override + public void initializeFileTypeAnalyzer() throws Exception { + // Now, need to see if bundle-audit actually runs from this location. + try { + Process process = launchBundleAudit(Settings.getTempDirectory()); + int exitValue = process.waitFor(); + if (0 == exitValue) { + LOGGER.warn("Unexpected exit code from bundle-audit process. Disabling %s: %d", ANALYZER_NAME, exitValue); + setEnabled(false); + } else { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8")); + if (!reader.ready()) { + LOGGER.warn("Bundle-audit error stream unexpectedly not ready. Disabling " + ANALYZER_NAME); + setEnabled(false); + } else { + final String line = reader.readLine(); + if (!line.contains("Errno::ENOENT")) { + LOGGER.warn("Unexpected bundle-audit output. Disabling %s: %s", ANALYZER_NAME, line); + setEnabled(false); + } + } + } finally { + if (null != reader) { + reader.close(); + } + } + } + } catch (AnalysisException ae) { + LOGGER.warn("Exception while trying to launch bundle-audit. Disabling " + ANALYZER_NAME, ae.getCause()); + setEnabled(false); + } + } + + /** + * Returns the name of the analyzer. + * + * @return the name of the analyzer. + */ + @Override + public String getName() { + return ANALYZER_NAME; + } + + /** + * Returns the phase that the analyzer is intended to run in. + * + * @return the phase that the analyzer is intended to run in. + */ + @Override + public AnalysisPhase getAnalysisPhase() { + return ANALYSIS_PHASE; + } + + /** + * Returns the key used in the properties file to reference the analyzer's enabled property. + * + * @return the analyzer's enabled property setting key + */ + @Override + protected String getAnalyzerEnabledSettingKey() { + return Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED; + } + + @Override + protected void analyzeFileType(Dependency dependency, Engine engine) + throws AnalysisException { + final File parentFile = dependency.getActualFile().getParentFile(); + final Process process = launchBundleAudit(parentFile); + try { + process.waitFor(); + } catch (InterruptedException ie) { + throw new AnalysisException("bundle-audit process interrupted", ie); + } + 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)); + } + } catch (IOException ioe) { + LOGGER.warn("bundle-audit failure", ioe); + } finally { + if (null != rdr) { + try { + rdr.close(); + } catch (IOException ioe) { + LOGGER.warn("bundle-audit close failure", ioe); + } + } + } + + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzer.java index 5112912e7..6f29e7bb2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzer.java @@ -49,11 +49,12 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { */ private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; + private static final String GEMSPEC = "gemspec"; + private static final FileFilter FILTER = - FileFilterBuilder.newInstance().addExtensions("gemspec").addFilenames("Rakefile").build(); + FileFilterBuilder.newInstance().addExtensions(GEMSPEC).addFilenames("Rakefile").build(); private static final String EMAIL = "email"; - private static final String GEMSPEC = "gemspec"; /** * @return a filter that accepts files named Rakefile or matching the glob pattern, *.gemspec diff --git a/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer b/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer index 659c104b5..a4d0f78c8 100644 --- a/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer +++ b/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer @@ -17,4 +17,5 @@ org.owasp.dependencycheck.analyzer.PythonPackageAnalyzer org.owasp.dependencycheck.analyzer.AutoconfAnalyzer org.owasp.dependencycheck.analyzer.OpenSSLAnalyzer org.owasp.dependencycheck.analyzer.CMakeAnalyzer -org.owasp.dependencycheck.analyzer.RubyGemspecAnalyzer \ No newline at end of file +org.owasp.dependencycheck.analyzer.RubyGemspecAnalyzer +org.owasp.dependencycheck.analyzer.RubyBundleAuditAnalyzer \ No newline at end of file diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java index 482e19753..ec99710d5 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java @@ -246,6 +246,10 @@ public final class Settings { * The path to mono, if available. */ public static final String ANALYZER_ASSEMBLY_MONO_PATH = "analyzer.assembly.mono.path"; + /** + * The path to bundle-audit, if available. + */ + public static final String ANALYZER_BUNDLE_AUDIT_PATH = "analyzer.bundle.audit.path"; /** * The additional configured zip file extensions, if available. */ From 4493f895c62de5fc3e45a7db8fb9e234da357277 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Wed, 19 Aug 2015 15:26:17 -0400 Subject: [PATCH 02/19] Added test resources to cover interesting bundle-audit cases. --- .../analyzer/RubyGemspecAnalyzerTest.java | 8 +-- .../specifications/mime-types-2.6.1.gemspec | 72 ------------------- ...cord-oracle_enhanced-adapter-1.1.7.gemspec | 24 +++++++ .../gems/specifications/i18n-0.7.0.gemspec | 22 ++++++ .../gems/specifications/mail-2.4.3.gemspec | 39 ++++++++++ .../specifications/mime-types-1.25.1.gemspec | 67 +++++++++++++++++ .../gems/specifications/netrc-0.10.3.gemspec | 0 .../specifications/polyglot-0.3.5.gemspec | 22 ++++++ .../specifications/rest-client-1.7.2.gemspec | 0 .../specifications/treetop-1.4.15.gemspec | 56 +++++++++++++++ 10 files changed, 234 insertions(+), 76 deletions(-) delete mode 100644 dependency-check-core/src/test/resources/ruby/gems/specifications/mime-types-2.6.1.gemspec create mode 100644 dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/activerecord-oracle_enhanced-adapter-1.1.7.gemspec create mode 100644 dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/i18n-0.7.0.gemspec create mode 100644 dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/mail-2.4.3.gemspec create mode 100644 dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/mime-types-1.25.1.gemspec rename dependency-check-core/src/test/resources/ruby/{ => vulnerable}/gems/specifications/netrc-0.10.3.gemspec (100%) create mode 100644 dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/polyglot-0.3.5.gemspec rename dependency-check-core/src/test/resources/ruby/{ => vulnerable}/gems/specifications/rest-client-1.7.2.gemspec (100%) create mode 100644 dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/treetop-1.4.15.gemspec diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzerTest.java index dd749f193..e861f97d5 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzerTest.java @@ -66,7 +66,7 @@ public class RubyGemspecAnalyzerTest extends BaseTest { } /** - * Test of getName method, of class PythonDistributionAnalyzer. + * Test Ruby Gemspec name. */ @Test public void testGetName() { @@ -74,7 +74,7 @@ public class RubyGemspecAnalyzerTest extends BaseTest { } /** - * Test of supportsExtension method, of class PythonDistributionAnalyzer. + * Test Ruby Gemspec file support. */ @Test public void testSupportsFiles() { @@ -83,14 +83,14 @@ public class RubyGemspecAnalyzerTest extends BaseTest { } /** - * Test of inspect method, of class PythonDistributionAnalyzer. + * Test Ruby Gemspec analysis. * * @throws AnalysisException is thrown when an exception occurs. */ @Test public void testAnalyzePackageJson() throws AnalysisException { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, - "ruby/gems/specifications/rest-client-1.7.2.gemspec")); + "ruby/vulnerable/gems/specifications/rest-client-1.7.2.gemspec")); analyzer.analyze(result, null); final String vendorString = result.getVendorEvidence().toString(); assertThat(vendorString, containsString("REST Client Team")); diff --git a/dependency-check-core/src/test/resources/ruby/gems/specifications/mime-types-2.6.1.gemspec b/dependency-check-core/src/test/resources/ruby/gems/specifications/mime-types-2.6.1.gemspec deleted file mode 100644 index 1bea93f2f..000000000 --- a/dependency-check-core/src/test/resources/ruby/gems/specifications/mime-types-2.6.1.gemspec +++ /dev/null @@ -1,72 +0,0 @@ -# -*- encoding: utf-8 -*- -# stub: mime-types 2.6.1 ruby lib - -Gem::Specification.new do |s| - s.name = "mime-types" - s.version = "2.6.1" - - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.require_paths = ["lib"] - s.authors = ["Austin Ziegler"] - s.date = "2015-05-25" - s.description = "The mime-types library provides a library and registry for information about\nMIME content type definitions. It can be used to determine defined filename\nextensions for MIME types, or to use filename extensions to look up the likely\nMIME type definitions.\n\nMIME content types are used in MIME-compliant communications, as in e-mail or\nHTTP traffic, to indicate the type of content which is transmitted. The\nmime-types library provides the ability for detailed information about MIME\nentities (provided as an enumerable collection of MIME::Type objects) to be\ndetermined and used. There are many types defined by RFCs and vendors, so the\nlist is long but by definition incomplete; don't hesitate to add additional\ntype definitions. MIME type definitions found in mime-types are from RFCs, W3C\nrecommendations, the {IANA Media Types\nregistry}[https://www.iana.org/assignments/media-types/media-types.xhtml], and\nuser contributions. It conforms to RFCs 2045 and 2231.\n\nThis is release 2.6 with two new experimental features. The first new feature\nis a new default registry storage format that greatly reduces the initial\nmemory use of the mime-types library. This feature is enabled by requiring\n+mime/types/columnar+ instead of +mime/types+ with a small performance cost and\nno change in *total* memory use if certain methods are called (see {Columnar\nStore}[#columnar-store] for more details). The second new feature is a logger\ninterface that conforms to the expectations of an ActiveSupport::Logger so that\nwarnings can be written to an application's log rather than the default\nlocation for +warn+. This interface may be used for other logging purposes in\nthe future.\n\nmime-types 2.6 is the last planned version of mime-types 2.x, so deprecation\nwarnings are no longer cached but provided every time the method is called.\nmime-types 2.6 supports Ruby 1.9.2 or later." - s.email = ["halostatue@gmail.com"] - s.extra_rdoc_files = ["Contributing.rdoc", "History-Types.rdoc", "History.rdoc", "Licence.rdoc", "Manifest.txt", "README.rdoc", "docs/COPYING.txt", "docs/artistic.txt"] - s.files = ["Contributing.rdoc", "History-Types.rdoc", "History.rdoc", "Licence.rdoc", "Manifest.txt", "README.rdoc", "docs/COPYING.txt", "docs/artistic.txt"] - s.homepage = "https://github.com/mime-types/ruby-mime-types/" - s.licenses = ["MIT", "Artistic 2.0", "GPL-2"] - s.rdoc_options = ["--main", "README.rdoc"] - s.required_ruby_version = Gem::Requirement.new(">= 1.9.2") - s.rubygems_version = "2.2.2" - s.summary = "The mime-types library provides a library and registry for information about MIME content type definitions" - - s.installed_by_version = "2.2.2" if s.respond_to? :installed_by_version - - if s.respond_to? :specification_version then - s.specification_version = 4 - - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_development_dependency(%q, ["~> 5.6"]) - s.add_development_dependency(%q, ["~> 4.0"]) - s.add_development_dependency(%q, ["~> 1.0"]) - s.add_development_dependency(%q, ["~> 1.1"]) - s.add_development_dependency(%q, ["~> 1.6"]) - s.add_development_dependency(%q, ["~> 1.0"]) - s.add_development_dependency(%q, ["~> 1.2"]) - s.add_development_dependency(%q, ["~> 1.0"]) - s.add_development_dependency(%q, ["~> 1.0"]) - s.add_development_dependency(%q, ["~> 10.0"]) - s.add_development_dependency(%q, ["~> 0.7"]) - s.add_development_dependency(%q, ["~> 0.8"]) - s.add_development_dependency(%q, ["~> 3.13"]) - else - s.add_dependency(%q, ["~> 5.6"]) - s.add_dependency(%q, ["~> 4.0"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 1.1"]) - s.add_dependency(%q, ["~> 1.6"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 1.2"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 10.0"]) - s.add_dependency(%q, ["~> 0.7"]) - s.add_dependency(%q, ["~> 0.8"]) - s.add_dependency(%q, ["~> 3.13"]) - end - else - s.add_dependency(%q, ["~> 5.6"]) - s.add_dependency(%q, ["~> 4.0"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 1.1"]) - s.add_dependency(%q, ["~> 1.6"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 1.2"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 10.0"]) - s.add_dependency(%q, ["~> 0.7"]) - s.add_dependency(%q, ["~> 0.8"]) - s.add_dependency(%q, ["~> 3.13"]) - end -end diff --git a/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/activerecord-oracle_enhanced-adapter-1.1.7.gemspec b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/activerecord-oracle_enhanced-adapter-1.1.7.gemspec new file mode 100644 index 000000000..3a0daf061 --- /dev/null +++ b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/activerecord-oracle_enhanced-adapter-1.1.7.gemspec @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +# stub: activerecord-oracle_enhanced-adapter 1.1.7 ruby lib + +Gem::Specification.new do |s| + s.name = "activerecord-oracle_enhanced-adapter" + s.version = "1.1.7" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] + s.authors = ["Raimonds Simanovskis"] + s.date = "2008-08-20" + s.description = "Oracle enhaced adapter for Active Record" + s.email = ["raymonds72@gmail.com"] + s.extra_rdoc_files = ["History.txt", "License.txt", "README.txt"] + s.files = ["History.txt", "License.txt", "README.txt"] + s.homepage = "http://oracle-enhanced.rubyforge.org" + s.post_install_message = "" + s.rdoc_options = ["--main", "README.txt"] + s.rubyforge_project = "oracle-enhanced" + s.rubygems_version = "2.2.2" + s.summary = "Oracle enhaced adapter for Active Record" + + s.installed_by_version = "2.2.2" if s.respond_to? :installed_by_version +end diff --git a/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/i18n-0.7.0.gemspec b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/i18n-0.7.0.gemspec new file mode 100644 index 000000000..ee33ff2f5 --- /dev/null +++ b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/i18n-0.7.0.gemspec @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +# stub: i18n 0.7.0 ruby lib + +Gem::Specification.new do |s| + s.name = "i18n" + s.version = "0.7.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] + s.authors = ["Sven Fuchs", "Joshua Harvey", "Matt Aimonetti", "Stephan Soller", "Saimon Moore"] + s.date = "2014-12-19" + s.description = "New wave Internationalization support for Ruby." + s.email = "rails-i18n@googlegroups.com" + s.homepage = "http://github.com/svenfuchs/i18n" + s.licenses = ["MIT"] + s.required_ruby_version = Gem::Requirement.new(">= 1.9.3") + s.rubyforge_project = "[none]" + s.rubygems_version = "2.2.2" + s.summary = "New wave Internationalization support for Ruby" + + s.installed_by_version = "2.2.2" if s.respond_to? :installed_by_version +end diff --git a/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/mail-2.4.3.gemspec b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/mail-2.4.3.gemspec new file mode 100644 index 000000000..07be6e8a7 --- /dev/null +++ b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/mail-2.4.3.gemspec @@ -0,0 +1,39 @@ +# -*- encoding: utf-8 -*- +# stub: mail 2.4.3 ruby lib + +Gem::Specification.new do |s| + s.name = "mail" + s.version = "2.4.3" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] + s.authors = ["Mikel Lindsaar"] + s.date = "2012-03-05" + s.description = "A really Ruby Mail handler." + s.email = "raasdnil@gmail.com" + s.extra_rdoc_files = ["README.md", "CONTRIBUTING.md", "CHANGELOG.rdoc", "TODO.rdoc"] + s.files = ["CHANGELOG.rdoc", "CONTRIBUTING.md", "README.md", "TODO.rdoc"] + s.homepage = "http://github.com/mikel/mail" + s.rubygems_version = "2.2.2" + s.summary = "Mail provides a nice Ruby DSL for making, sending and reading emails." + + s.installed_by_version = "2.2.2" if s.respond_to? :installed_by_version + + if s.respond_to? :specification_version then + s.specification_version = 3 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, ["~> 1.16"]) + s.add_runtime_dependency(%q, ["~> 1.4.8"]) + s.add_runtime_dependency(%q, [">= 0.4.0"]) + else + s.add_dependency(%q, ["~> 1.16"]) + s.add_dependency(%q, ["~> 1.4.8"]) + s.add_dependency(%q, [">= 0.4.0"]) + end + else + s.add_dependency(%q, ["~> 1.16"]) + s.add_dependency(%q, ["~> 1.4.8"]) + s.add_dependency(%q, [">= 0.4.0"]) + end +end diff --git a/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/mime-types-1.25.1.gemspec b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/mime-types-1.25.1.gemspec new file mode 100644 index 000000000..de36785ed --- /dev/null +++ b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/mime-types-1.25.1.gemspec @@ -0,0 +1,67 @@ +# -*- encoding: utf-8 -*- +# stub: mime-types 1.25.1 ruby lib + +Gem::Specification.new do |s| + s.name = "mime-types" + s.version = "1.25.1" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] + s.authors = ["Austin Ziegler"] + s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMQ8wDQYDVQQDDAZhdXN0\naW4xGTAXBgoJkiaJk/IsZAEZFglydWJ5Zm9yZ2UxEzARBgoJkiaJk/IsZAEZFgNv\ncmcwHhcNMTMwMjA0MDMzMzI3WhcNMTQwMjA0MDMzMzI3WjBBMQ8wDQYDVQQDDAZh\ndXN0aW4xGTAXBgoJkiaJk/IsZAEZFglydWJ5Zm9yZ2UxEzARBgoJkiaJk/IsZAEZ\nFgNvcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2mPNf4L37GhKI\nSPCYsvYWXA2/R9u5+pyUnbJ2R1o2CiRq2ZA/AIzY6N3hGnsgoWnh5RzvgTN1Lt08\nDNIrsIG2VDYk/JVt6f9J6zZ8EQHbznWa3cWYoCFaaICdk7jV1n/42hg70jEDYXl9\ngDOl0k6JmyF/rtfFu/OIkFGWeFYIuFHvRuLyUbw66+QDTOzKb3t8o55Ihgy1GVwT\ni6pkDs8LhZWXdOD+921l2Z1NZGZa9KNbJIg6vtgYKU98jQ5qr9iY3ikBAspHrFas\nK6USvGgAg8fCD5YiotBEvCBMYtfqmfrhpdU2p+gvTgeLW1Kaevwqd7ngQmFUrFG1\neUJSURv5AgMBAAGjOTA3MAkGA1UdEwQCMAAwHQYDVR0OBBYEFAtJKMp6YYNqlgR3\n9TiZLWqvLagSMAsGA1UdDwQEAwIEsDANBgkqhkiG9w0BAQUFAAOCAQEApTPkvDm8\n7gJlUT4FfumXPvtuqP67LxUtGE8syvR0A4As+0P/wylLJFUOsGTTdZYtThhxCSJG\n+7KG2FfIcH4Zz2d97arZGAzBoi8iPht2/UtSl1fCcUI5vmJa1MiXZT2oqdW7Wydq\nrAZcBPlrYYuiwtGI0yqIOgBfXSZCWWsJsuyTKELep6mCLgz0YZUfmvKr8W/Ab3ax\nDuLzH92LSRjZJyjyAUpw/Vc2rM4giiP5jtByrb1Y1dGnQhHTMHf1GfucWm7Nw/V9\ntwEPVw8+0f88JQucxOTmTF1NbLFpiRwQUZ1zoZbNg2e7mShc/eexnVLWKFKxRoP6\nKPj3WoD+spB8fA==\n-----END CERTIFICATE-----\n"] + s.date = "2013-11-24" + s.description = "This library allows for the identification of a file's likely MIME content\ntype. This is release 1.25.1, fixing an issue with priority comparison for\nmime-types 1.x. The current release is 2.0, which only supports Ruby 1.9 or\nlater.\n\nRelease 1.25.1 contains all features of 1.25, including the experimental\ncaching and lazy loading functionality. The caching and lazy loading features\nwere initially implemented by Greg Brockman (gdb). As these features are\nexperimental, they are disabled by default and must be enabled through the use\nof environment variables. The cache is invalidated on a per-version basis; the\ncache for version 1.25 will not be reused for any later version.\n\nTo use lazy loading, set the environment variable +RUBY_MIME_TYPES_LAZY_LOAD+\nto any value other than 'false'. When using lazy loading, the initial startup\nof MIME::Types is around 12\u{2013}25\u{d7} faster than normal startup (on my system,\nnormal startup is about 90 ms; lazy startup is about 4 ms). This isn't\ngenerally useful, however, as the MIME::Types database has not been loaded.\nLazy startup and load is just *slightly* faster\u{2014}around 1 ms. The real advantage\ncomes from using the cache.\n\nTo enable the cache, set the environment variable +RUBY_MIME_TYPES_CACHE+ to a\nfilename where MIME::Types will have read-write access. The first time a new\nversion of MIME::Types is run using this file, it will be created, taking a\nlittle longer than normal. Subsequent loads using the same cache file will be\napproximately 3\u{bd}\u{d7} faster (25 ms) than normal loads. This can be combined with\n+RUBY_MIME_TYPES_LAZY_LOAD+, but this is *not* recommended in a multithreaded\nor multiprocess environment where all threads or processes will be using the\nsame cache file.\n\nAs the caching interface is still experimental, the only values cached are the\ndefault MIME::Types database, not any custom MIME::Types added by users.\n\nMIME types are used in MIME-compliant communications, as in e-mail or HTTP\ntraffic, to indicate the type of content which is transmitted. MIME::Types\nprovides the ability for detailed information about MIME entities (provided as\na set of MIME::Type objects) to be determined and used programmatically. There\nare many types defined by RFCs and vendors, so the list is long but not\ncomplete; don't hesitate to ask to add additional information. This library\nfollows the IANA collection of MIME types (see below for reference).\n\nMIME::Types for Ruby was originally based on MIME::Types for Perl by Mark\nOvermeer, copyright 2001 - 2009.\n\nMIME::Types is built to conform to the MIME types of RFCs 2045 and 2231. It\ntracks the {IANA registry}[http://www.iana.org/assignments/media-types/]\n({ftp}[ftp://ftp.iana.org/assignments/media-types]) with some unofficial types\nadded from the {LTSW collection}[http://www.ltsw.se/knbase/internet/mime.htp]\nand added by the users of MIME::Types." + s.email = ["austin@rubyforge.org"] + s.extra_rdoc_files = ["Contributing.rdoc", "History.rdoc", "Licence.rdoc", "Manifest.txt", "README.rdoc", "docs/COPYING.txt", "docs/artistic.txt"] + s.files = ["Contributing.rdoc", "History.rdoc", "Licence.rdoc", "Manifest.txt", "README.rdoc", "docs/COPYING.txt", "docs/artistic.txt"] + s.homepage = "http://mime-types.rubyforge.org/" + s.licenses = ["MIT", "Artistic 2.0", "GPL-2"] + s.rdoc_options = ["--main", "README.rdoc"] + s.rubyforge_project = "mime-types" + s.rubygems_version = "2.2.2" + s.summary = "This library allows for the identification of a file's likely MIME content type" + + s.installed_by_version = "2.2.2" if s.respond_to? :installed_by_version + + if s.respond_to? :specification_version then + s.specification_version = 4 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_development_dependency(%q, [">= 2.0.4"]) + s.add_development_dependency(%q, ["~> 5.0"]) + s.add_development_dependency(%q, ["~> 4.0"]) + s.add_development_dependency(%q, ["~> 1.2"]) + s.add_development_dependency(%q, ["~> 1.0"]) + s.add_development_dependency(%q, ["~> 1.1"]) + s.add_development_dependency(%q, ["~> 1.5"]) + s.add_development_dependency(%q, ["~> 1.0"]) + s.add_development_dependency(%q, ["~> 1.2"]) + s.add_development_dependency(%q, ["~> 10.0"]) + s.add_development_dependency(%q, ["~> 3.7"]) + else + s.add_dependency(%q, [">= 2.0.4"]) + s.add_dependency(%q, ["~> 5.0"]) + s.add_dependency(%q, ["~> 4.0"]) + s.add_dependency(%q, ["~> 1.2"]) + s.add_dependency(%q, ["~> 1.0"]) + s.add_dependency(%q, ["~> 1.1"]) + s.add_dependency(%q, ["~> 1.5"]) + s.add_dependency(%q, ["~> 1.0"]) + s.add_dependency(%q, ["~> 1.2"]) + s.add_dependency(%q, ["~> 10.0"]) + s.add_dependency(%q, ["~> 3.7"]) + end + else + s.add_dependency(%q, [">= 2.0.4"]) + s.add_dependency(%q, ["~> 5.0"]) + s.add_dependency(%q, ["~> 4.0"]) + s.add_dependency(%q, ["~> 1.2"]) + s.add_dependency(%q, ["~> 1.0"]) + s.add_dependency(%q, ["~> 1.1"]) + s.add_dependency(%q, ["~> 1.5"]) + s.add_dependency(%q, ["~> 1.0"]) + s.add_dependency(%q, ["~> 1.2"]) + s.add_dependency(%q, ["~> 10.0"]) + s.add_dependency(%q, ["~> 3.7"]) + end +end diff --git a/dependency-check-core/src/test/resources/ruby/gems/specifications/netrc-0.10.3.gemspec b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/netrc-0.10.3.gemspec similarity index 100% rename from dependency-check-core/src/test/resources/ruby/gems/specifications/netrc-0.10.3.gemspec rename to dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/netrc-0.10.3.gemspec diff --git a/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/polyglot-0.3.5.gemspec b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/polyglot-0.3.5.gemspec new file mode 100644 index 000000000..4fa7a1080 --- /dev/null +++ b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/polyglot-0.3.5.gemspec @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +# stub: polyglot 0.3.5 ruby lib + +Gem::Specification.new do |s| + s.name = "polyglot" + s.version = "0.3.5" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] + s.authors = ["Clifford Heath"] + s.date = "2014-05-30" + s.description = "\nThe Polyglot library allows a Ruby module to register a loader\nfor the file type associated with a filename extension, and it\naugments 'require' to find and load matching files." + s.email = ["clifford.heath@gmail.com"] + s.extra_rdoc_files = ["README.txt"] + s.files = ["README.txt"] + s.homepage = "http://github.com/cjheath/polyglot" + s.licenses = ["MIT"] + s.rubygems_version = "2.2.2" + s.summary = "Augment 'require' to load non-Ruby file types" + + s.installed_by_version = "2.2.2" if s.respond_to? :installed_by_version +end diff --git a/dependency-check-core/src/test/resources/ruby/gems/specifications/rest-client-1.7.2.gemspec b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/rest-client-1.7.2.gemspec similarity index 100% rename from dependency-check-core/src/test/resources/ruby/gems/specifications/rest-client-1.7.2.gemspec rename to dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/rest-client-1.7.2.gemspec diff --git a/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/treetop-1.4.15.gemspec b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/treetop-1.4.15.gemspec new file mode 100644 index 000000000..70cae417f --- /dev/null +++ b/dependency-check-core/src/test/resources/ruby/vulnerable/gems/specifications/treetop-1.4.15.gemspec @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- +# stub: treetop 1.4.15 ruby lib + +Gem::Specification.new do |s| + s.name = "treetop" + s.version = "1.4.15" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] + s.authors = ["Nathan Sobo", "Clifford Heath"] + s.autorequire = "treetop" + s.date = "2013-08-17" + s.email = "cliffordheath@gmail.com" + s.executables = ["tt"] + s.extra_rdoc_files = ["LICENSE", "README.md"] + s.files = ["LICENSE", "README.md", "bin/tt"] + s.homepage = "https://github.com/cjheath/treetop" + s.licenses = ["MIT"] + s.rubygems_version = "2.2.2" + s.summary = "A Ruby-based text parsing and interpretation DSL" + + s.installed_by_version = "2.2.2" if s.respond_to? :installed_by_version + + if s.respond_to? :specification_version then + s.specification_version = 3 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, ["~> 0.5.0"]) + s.add_development_dependency(%q, ["~> 1.0"]) + s.add_development_dependency(%q, [">= 2.0.0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_runtime_dependency(%q, [">= 0.3.1"]) + else + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 0.5.0"]) + s.add_dependency(%q, ["~> 1.0"]) + s.add_dependency(%q, [">= 2.0.0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0.3.1"]) + end + else + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 0.5.0"]) + s.add_dependency(%q, ["~> 1.0"]) + s.add_dependency(%q, [">= 2.0.0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0.3.1"]) + end +end From 271016f0fa8d3308fab4e608f5b4d7f686003214 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Wed, 19 Aug 2015 15:33:50 -0400 Subject: [PATCH 03/19] Added verbose flag to get as much reportable info as possible. --- .../owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java | 1 + 1 file changed, 1 insertion(+) 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 10e0dcce1..6ab97b166 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 @@ -72,6 +72,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { final String bundleAuditPath = Settings.getString(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH); args.add(null == bundleAuditPath ? "bundle-audit" : bundleAuditPath); args.add("check"); + args.add("--verbose"); final ProcessBuilder builder = new ProcessBuilder(args); builder.directory(folder); try { From 95d3d17d83cbc998d168513d605568047943f099 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Fri, 28 Aug 2015 13:58:49 -0400 Subject: [PATCH 04/19] 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))); + } +} From 88535521610ff5139d36b1a2a146ce0767b63eeb Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Fri, 28 Aug 2015 19:56:35 -0400 Subject: [PATCH 05/19] Ruby Bundler: Successfully adding vulnerability into report, though all displayed info not looking great. --- .../analyzer/RubyBundleAuditAnalyzer.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) 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 53d013997..aa6e7b9ad 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 @@ -22,6 +22,7 @@ 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.dependency.Vulnerability; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; @@ -188,12 +189,14 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { final String parentName = original.getActualFile().getParentFile().getName(); final String fileName = original.getFileName(); Dependency dependency = null; + Vulnerability vulnerability= null; + String gem = 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()); + 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 @@ -201,16 +204,27 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { engine.getDependencies().add(dependency); dependency.setDisplayFileName(displayFileName); dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST); + vulnerability = new Vulnerability(); + vulnerability.setName(gem); + dependency.getVulnerabilities().add(vulnerability); LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith(VERSION)) { if (null != dependency) { + final String version = nextLine.substring(VERSION.length()); dependency.getVersionEvidence().addEvidence( "bundler-audit", "Version", - nextLine.substring(VERSION.length()), + version, Confidence.HIGHEST); + vulnerability.setMatchedCPE( + String.format("cpe:/a:%1$s_project:%1$s:%2$s::~~~ruby~~", gem, version), + null); } LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + } else if (nextLine.startsWith("Advisory: ")){ + final String advisory = nextLine.substring(("Advisory: ".length())); + vulnerability.setName(advisory); + LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } } } From 6f4ce348404ee569bcb832719847b06cd08ff400 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Fri, 28 Aug 2015 21:31:01 -0400 Subject: [PATCH 06/19] Ruby Bundler: Added CVSS score and a little hack to avoid dependency bundling. --- .../analyzer/RubyBundleAuditAnalyzer.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) 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 aa6e7b9ad..1077437a1 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 @@ -55,6 +55,8 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { FileFilterBuilder.newInstance().addFilenames("Gemfile.lock").build(); public static final String NAME = "Name: "; public static final String VERSION = "Version: "; + public static final String ADVISORY = "Advisory: "; + public static final String CRITICALITY = "Criticality: "; /** * @return a filter that accepts files named Gemfile.lock @@ -191,15 +193,17 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { Dependency dependency = null; Vulnerability vulnerability= null; String gem = null; + int i = 0; while (rdr.ready()) { final String nextLine = rdr.readLine(); + i++; if (null == nextLine) { break; } else if (nextLine.startsWith(NAME)) { 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 + FileUtils.write(tempFile, displayFileName + "\n" + i); // unique contents to avoid dependency bundling dependency = new Dependency(tempFile); engine.getDependencies().add(dependency); dependency.setDisplayFileName(displayFileName); @@ -221,10 +225,22 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { null); } LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); - } else if (nextLine.startsWith("Advisory: ")){ - final String advisory = nextLine.substring(("Advisory: ".length())); + } else if (nextLine.startsWith(ADVISORY)){ + final String advisory = nextLine.substring((ADVISORY.length())); vulnerability.setName(advisory); LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + } else if (nextLine.startsWith(CRITICALITY)) { + final String criticality = nextLine.substring(CRITICALITY.length()).trim(); + if ("High".equals(criticality)) { + vulnerability.setCvssScore(8.5f); + } else if ("Medium".equals(criticality)) { + vulnerability.setCvssScore(5.5f); + } else if ("Low".equals(criticality)) { + vulnerability.setCvssScore(2.0f); + } else { + vulnerability.setCvssScore(-1.0f); + } + LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } } } From 2eb6918fb36dc86158187da291e2c9f64d6b78c5 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sat, 29 Aug 2015 11:06:24 -0400 Subject: [PATCH 07/19] Ruby Bundler: Clean up report a little bit, and grouped vulnerabilities under dependencies, when appropriate. --- .../analyzer/RubyBundleAuditAnalyzer.java | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) 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 1077437a1..c04034997 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 @@ -30,7 +30,9 @@ import org.slf4j.LoggerFactory; import java.io.*; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Used to analyze Ruby Bundler Gemspec.lock files utilizing the 3rd party bundle-audit tool. @@ -191,8 +193,9 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { final String parentName = original.getActualFile().getParentFile().getName(); final String fileName = original.getFileName(); Dependency dependency = null; - Vulnerability vulnerability= null; + Vulnerability vulnerability = null; String gem = null; + final Map map = new HashMap(); int i = 0; while (rdr.ready()) { final String nextLine = rdr.readLine(); @@ -201,16 +204,18 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { break; } else if (nextLine.startsWith(NAME)) { 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 + "\n" + i); // 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); - vulnerability = new Vulnerability(); - vulnerability.setName(gem); - dependency.getVulnerabilities().add(vulnerability); + if (map.containsKey(gem)) { + dependency = map.get(gem); + } else { + 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 + "\n" + i); // unique contents to avoid dependency bundling + dependency = new Dependency(tempFile); + dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST); + dependency.setDisplayFileName(displayFileName); + engine.getDependencies().add(dependency); + map.put(gem, dependency); + } LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith(VERSION)) { if (null != dependency) { @@ -220,25 +225,37 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { "Version", version, Confidence.HIGHEST); + vulnerability = new Vulnerability(); // don't add to dependency until we have name set later vulnerability.setMatchedCPE( String.format("cpe:/a:%1$s_project:%1$s:%2$s::~~~ruby~~", gem, version), null); } LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); - } else if (nextLine.startsWith(ADVISORY)){ + } else if (nextLine.startsWith(ADVISORY)) { final String advisory = nextLine.substring((ADVISORY.length())); vulnerability.setName(advisory); + vulnerability.setCvssAccessVector("-"); + vulnerability.setCvssAccessComplexity("-"); + vulnerability.setCvssAuthentication("-"); + vulnerability.setCvssAvailabilityImpact("-"); + vulnerability.setCvssConfidentialityImpact("-"); + vulnerability.setCvssIntegrityImpact("-"); + if (null != dependency) { + dependency.getVulnerabilities().add(vulnerability); + } LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith(CRITICALITY)) { final String criticality = nextLine.substring(CRITICALITY.length()).trim(); - if ("High".equals(criticality)) { - vulnerability.setCvssScore(8.5f); - } else if ("Medium".equals(criticality)) { - vulnerability.setCvssScore(5.5f); - } else if ("Low".equals(criticality)) { - vulnerability.setCvssScore(2.0f); - } else { - vulnerability.setCvssScore(-1.0f); + if (null != vulnerability) { + if ("High".equals(criticality)) { + vulnerability.setCvssScore(8.5f); + } else if ("Medium".equals(criticality)) { + vulnerability.setCvssScore(5.5f); + } else if ("Low".equals(criticality)) { + vulnerability.setCvssScore(2.0f); + } else { + vulnerability.setCvssScore(-1.0f); + } } LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } From b473d8ab9c0e45bdfde4d2ed4c8ef1b51c25282e Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sat, 29 Aug 2015 11:28:38 -0400 Subject: [PATCH 08/19] Ruby Bundler: Added URL to report. --- .../analyzer/RubyBundleAuditAnalyzer.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) 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 c04034997..9f11f9d11 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 @@ -22,6 +22,7 @@ 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.dependency.Reference; import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; @@ -29,10 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Used to analyze Ruby Bundler Gemspec.lock files utilizing the 3rd party bundle-audit tool. @@ -245,8 +243,8 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith(CRITICALITY)) { - final String criticality = nextLine.substring(CRITICALITY.length()).trim(); if (null != vulnerability) { + final String criticality = nextLine.substring(CRITICALITY.length()).trim(); if ("High".equals(criticality)) { vulnerability.setCvssScore(8.5f); } else if ("Medium".equals(criticality)) { @@ -258,6 +256,15 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } } LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + } else if (nextLine.startsWith("URL: ")){ + final String url = nextLine.substring(("URL: ").length()); + if (null != vulnerability) { + Reference ref = new Reference(); + ref.setName(vulnerability.getName()); + ref.setSource("bundle-audit"); + ref.setUrl(url); + vulnerability.getReferences().add(ref); + } } } } From 782039810ed664bfe05bddcc2e057bbb9d28db5c Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sat, 29 Aug 2015 11:33:16 -0400 Subject: [PATCH 09/19] Ruby Bundler: Added URL to report. --- .../dependencycheck/analyzer/RubyBundleAuditAnalyzer.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 9f11f9d11..468a0c34b 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 @@ -198,9 +198,11 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { while (rdr.ready()) { final String nextLine = rdr.readLine(); i++; + boolean appendToDescription = false; if (null == nextLine) { break; } else if (nextLine.startsWith(NAME)) { + appendToDescription = false; gem = nextLine.substring(NAME.length()); if (map.containsKey(gem)) { dependency = map.get(gem); @@ -265,6 +267,12 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { ref.setUrl(url); vulnerability.getReferences().add(ref); } + LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + } else if (nextLine.startsWith("Description: ")) { + appendToDescription = true; + vulnerability.setDescription("Vulnerability obtained from bundle-audit. NVD links may not work.\n\n"); + } else if (appendToDescription) { + vulnerability.setDescription(vulnerability.getDescription() + nextLine + "\n"); } } } From 713e9658c52bb10217d1f1aac55a978bef9d2fe9 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sat, 29 Aug 2015 12:29:44 -0400 Subject: [PATCH 10/19] Ruby bundler: got description working. Added boilerplate text describing differences from standard D-C vulnerability report. --- .../dependencycheck/analyzer/RubyBundleAuditAnalyzer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 468a0c34b..65bca7c07 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 @@ -195,10 +195,10 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { String gem = null; final Map map = new HashMap(); int i = 0; + boolean appendToDescription = false; while (rdr.ready()) { final String nextLine = rdr.readLine(); i++; - boolean appendToDescription = false; if (null == nextLine) { break; } else if (nextLine.startsWith(NAME)) { @@ -268,9 +268,9 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { vulnerability.getReferences().add(ref); } LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); - } else if (nextLine.startsWith("Description: ")) { + } else if (nextLine.startsWith("Description:")) { appendToDescription = true; - vulnerability.setDescription("Vulnerability obtained from bundle-audit. NVD links may not work.\n\n"); + vulnerability.setDescription("*** Vulnerability obtained from bundle-audit verbose report. Title link may not work. CPE below is guessed. CVSS score is estimated (-1.0 indicates unknown). See link below for full details. *** "); } else if (appendToDescription) { vulnerability.setDescription(vulnerability.getDescription() + nextLine + "\n"); } From 036200350d7bf5fae6fd9c2b0be04b27ca7fb9e4 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sun, 30 Aug 2015 13:50:22 -0400 Subject: [PATCH 11/19] Ruby bundler: add needed null checks to avoid NPEs. --- .../analyzer/RubyBundleAuditAnalyzer.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) 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 65bca7c07..6dbdcb25e 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 @@ -233,13 +233,15 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith(ADVISORY)) { final String advisory = nextLine.substring((ADVISORY.length())); - vulnerability.setName(advisory); - vulnerability.setCvssAccessVector("-"); - vulnerability.setCvssAccessComplexity("-"); - vulnerability.setCvssAuthentication("-"); - vulnerability.setCvssAvailabilityImpact("-"); - vulnerability.setCvssConfidentialityImpact("-"); - vulnerability.setCvssIntegrityImpact("-"); + if (null != vulnerability) { + vulnerability.setName(advisory); + vulnerability.setCvssAccessVector("-"); + vulnerability.setCvssAccessComplexity("-"); + vulnerability.setCvssAuthentication("-"); + vulnerability.setCvssAvailabilityImpact("-"); + vulnerability.setCvssConfidentialityImpact("-"); + vulnerability.setCvssIntegrityImpact("-"); + } if (null != dependency) { dependency.getVulnerabilities().add(vulnerability); } @@ -270,9 +272,13 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith("Description:")) { appendToDescription = true; - vulnerability.setDescription("*** Vulnerability obtained from bundle-audit verbose report. Title link may not work. CPE below is guessed. CVSS score is estimated (-1.0 indicates unknown). See link below for full details. *** "); + if (null != vulnerability) { + vulnerability.setDescription("*** Vulnerability obtained from bundle-audit verbose report. Title link may not work. CPE below is guessed. CVSS score is estimated (-1.0 indicates unknown). See link below for full details. *** "); + } } else if (appendToDescription) { - vulnerability.setDescription(vulnerability.getDescription() + nextLine + "\n"); + if (null != vulnerability) { + vulnerability.setDescription(vulnerability.getDescription() + nextLine + "\n"); + } } } } From b3a55cc85db4e022425b8b88d8773343e27cd014 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sun, 30 Aug 2015 13:57:30 -0400 Subject: [PATCH 12/19] Ruby bundler: extracted method --- .../analyzer/RubyBundleAuditAnalyzer.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) 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 6dbdcb25e..689e8e8c4 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 @@ -204,18 +204,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } else if (nextLine.startsWith(NAME)) { appendToDescription = false; gem = nextLine.substring(NAME.length()); - if (map.containsKey(gem)) { - dependency = map.get(gem); - } else { - 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 + "\n" + i); // unique contents to avoid dependency bundling - dependency = new Dependency(tempFile); - dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST); - dependency.setDisplayFileName(displayFileName); - engine.getDependencies().add(dependency); - map.put(gem, dependency); - } + dependency = map.containsKey(gem) ? map.get(gem) : createDependencyForGem(engine, parentName, fileName, gem, map, i); LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith(VERSION)) { if (null != dependency) { @@ -282,4 +271,17 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } } } + + private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String gem, Map map, int i) throws IOException { + Dependency dependency; + 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 + "\n" + i); // unique contents to avoid dependency bundling + dependency = new Dependency(tempFile); + dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST); + dependency.setDisplayFileName(displayFileName); + engine.getDependencies().add(dependency); + map.put(gem, dependency); + return dependency; + } } From ea7bd1f70099c989a5907fdc7fe02f862d734a1b Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sun, 30 Aug 2015 14:06:47 -0400 Subject: [PATCH 13/19] Ruby bundler: tidied up how extracted method is used. --- .../analyzer/RubyBundleAuditAnalyzer.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) 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 689e8e8c4..c06d28c3e 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 @@ -194,17 +194,18 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { Vulnerability vulnerability = null; String gem = null; final Map map = new HashMap(); - int i = 0; boolean appendToDescription = false; while (rdr.ready()) { final String nextLine = rdr.readLine(); - i++; if (null == nextLine) { break; } else if (nextLine.startsWith(NAME)) { appendToDescription = false; gem = nextLine.substring(NAME.length()); - dependency = map.containsKey(gem) ? map.get(gem) : createDependencyForGem(engine, parentName, fileName, gem, map, i); + if (!map.containsKey(gem)){ + map.put(gem, createDependencyForGem(engine, parentName, fileName, gem)); + } + dependency = map.get(gem); LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith(VERSION)) { if (null != dependency) { @@ -272,16 +273,14 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } } - private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String gem, Map map, int i) throws IOException { - Dependency dependency; + private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String gem) throws IOException { 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 + "\n" + i); // unique contents to avoid dependency bundling - dependency = new Dependency(tempFile); + FileUtils.write(tempFile, displayFileName); // unique contents to avoid dependency bundling + final Dependency dependency = new Dependency(tempFile); dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST); dependency.setDisplayFileName(displayFileName); engine.getDependencies().add(dependency); - map.put(gem, dependency); return dependency; } } From 80c46661980335416d4b2e134777a8bc9b6cce04 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sun, 30 Aug 2015 14:16:32 -0400 Subject: [PATCH 14/19] Ruby bundler: More method extractions to eliminate monolithic method. --- .../analyzer/RubyBundleAuditAnalyzer.java | 115 ++++++++++-------- 1 file changed, 66 insertions(+), 49 deletions(-) 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 c06d28c3e..a4988f5a1 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 @@ -208,58 +208,13 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { dependency = map.get(gem); LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith(VERSION)) { - if (null != dependency) { - final String version = nextLine.substring(VERSION.length()); - dependency.getVersionEvidence().addEvidence( - "bundler-audit", - "Version", - version, - Confidence.HIGHEST); - vulnerability = new Vulnerability(); // don't add to dependency until we have name set later - vulnerability.setMatchedCPE( - String.format("cpe:/a:%1$s_project:%1$s:%2$s::~~~ruby~~", gem, version), - null); - } - LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + vulnerability = createVulnerability(parentName, dependency, vulnerability, gem, nextLine); } else if (nextLine.startsWith(ADVISORY)) { - final String advisory = nextLine.substring((ADVISORY.length())); - if (null != vulnerability) { - vulnerability.setName(advisory); - vulnerability.setCvssAccessVector("-"); - vulnerability.setCvssAccessComplexity("-"); - vulnerability.setCvssAuthentication("-"); - vulnerability.setCvssAvailabilityImpact("-"); - vulnerability.setCvssConfidentialityImpact("-"); - vulnerability.setCvssIntegrityImpact("-"); - } - if (null != dependency) { - dependency.getVulnerabilities().add(vulnerability); - } - LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + setVulnerabilityName(parentName, dependency, vulnerability, nextLine); } else if (nextLine.startsWith(CRITICALITY)) { - if (null != vulnerability) { - final String criticality = nextLine.substring(CRITICALITY.length()).trim(); - if ("High".equals(criticality)) { - vulnerability.setCvssScore(8.5f); - } else if ("Medium".equals(criticality)) { - vulnerability.setCvssScore(5.5f); - } else if ("Low".equals(criticality)) { - vulnerability.setCvssScore(2.0f); - } else { - vulnerability.setCvssScore(-1.0f); - } - } - LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + addCriticalityToVulnerability(parentName, vulnerability, nextLine); } else if (nextLine.startsWith("URL: ")){ - final String url = nextLine.substring(("URL: ").length()); - if (null != vulnerability) { - Reference ref = new Reference(); - ref.setName(vulnerability.getName()); - ref.setSource("bundle-audit"); - ref.setUrl(url); - vulnerability.getReferences().add(ref); - } - LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + addReferenceToVulnerability(parentName, vulnerability, nextLine); } else if (nextLine.startsWith("Description:")) { appendToDescription = true; if (null != vulnerability) { @@ -273,6 +228,68 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } } + private void setVulnerabilityName(String parentName, Dependency dependency, Vulnerability vulnerability, String nextLine) { + final String advisory = nextLine.substring((ADVISORY.length())); + if (null != vulnerability) { + vulnerability.setName(advisory); + } + if (null != dependency) { + dependency.getVulnerabilities().add(vulnerability); // needed to wait for vulnerability name to avoid NPE + } + LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + } + + private void addReferenceToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) { + final String url = nextLine.substring(("URL: ").length()); + if (null != vulnerability) { + Reference ref = new Reference(); + ref.setName(vulnerability.getName()); + ref.setSource("bundle-audit"); + ref.setUrl(url); + vulnerability.getReferences().add(ref); + } + LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + } + + private void addCriticalityToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) { + if (null != vulnerability) { + final String criticality = nextLine.substring(CRITICALITY.length()).trim(); + if ("High".equals(criticality)) { + vulnerability.setCvssScore(8.5f); + } else if ("Medium".equals(criticality)) { + vulnerability.setCvssScore(5.5f); + } else if ("Low".equals(criticality)) { + vulnerability.setCvssScore(2.0f); + } else { + vulnerability.setCvssScore(-1.0f); + } + } + LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + } + + private Vulnerability createVulnerability(String parentName, Dependency dependency, Vulnerability vulnerability, String gem, String nextLine) { + if (null != dependency) { + final String version = nextLine.substring(VERSION.length()); + dependency.getVersionEvidence().addEvidence( + "bundler-audit", + "Version", + version, + Confidence.HIGHEST); + vulnerability = new Vulnerability(); // don't add to dependency until we have name set later + vulnerability.setMatchedCPE( + String.format("cpe:/a:%1$s_project:%1$s:%2$s::~~~ruby~~", gem, version), + null); + vulnerability.setCvssAccessVector("-"); + vulnerability.setCvssAccessComplexity("-"); + vulnerability.setCvssAuthentication("-"); + vulnerability.setCvssAvailabilityImpact("-"); + vulnerability.setCvssConfidentialityImpact("-"); + vulnerability.setCvssIntegrityImpact("-"); + } + LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + return vulnerability; + } + private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String gem) throws IOException { final File tempFile = File.createTempFile("Gemfile-" + gem, ".lock", Settings.getTempDirectory()); final String displayFileName = String.format("%s%c%s:%s", parentName, File.separatorChar, fileName, gem); From c393e74160d4f6bf531b64dc3606655e62b07f84 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sun, 30 Aug 2015 14:31:58 -0400 Subject: [PATCH 15/19] Ruby bundler: Better message and logging when bundle-audit not found. --- .../dependencycheck/analyzer/RubyBundleAuditAnalyzer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 a4988f5a1..e3e9b43da 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 @@ -124,7 +124,9 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } } } catch (AnalysisException ae) { - LOGGER.warn("Exception while trying to launch bundle-audit. Disabling " + ANALYZER_NAME, ae.getCause()); + LOGGER.warn("Exception while trying to launch bundle-audit. Disabling " + + ANALYZER_NAME + ". See log file for more details."); + LOGGER.debug("Exception while trying to launch bundle-audit.", ae); setEnabled(false); } } From 73e0292a4b9b48d8969ccbd0f178ebe44770480e Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sun, 30 Aug 2015 14:52:55 -0400 Subject: [PATCH 16/19] Ruby Bundler: Added informative message about updating DB. Switched most log messages to debug level. --- .../src/site/markdown/arguments.md | 2 ++ .../analyzer/RubyBundleAuditAnalyzer.java | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/dependency-check-cli/src/site/markdown/arguments.md b/dependency-check-cli/src/site/markdown/arguments.md index ededf1f2d..f3193b41a 100644 --- a/dependency-check-cli/src/site/markdown/arguments.md +++ b/dependency-check-cli/src/site/markdown/arguments.md @@ -32,6 +32,7 @@ Short | Argument Name        | Paramete | \-\-disablePyPkg | | Sets whether the Python Package Analyzer will be used. | false | \-\-disableNodeJS | | Sets whehter the Node.js Package Analyzer will be used. | false | \-\-disableRubygems | | Sets whether the Ruby Gemspec Analyzer will be used. | false + | \-\-disableBundlerAudit | | Sets whether the Ruby Bundler Audit Analyzer will be used. | false | \-\-disableAutoconf | | Sets whether the Autoconf Analyzer will be used. | false | \-\-disableOpenSSL | | Sets whether the OpenSSL Analyzer will be used. | false | \-\-disableCmake | | Sets whether the Cmake Analyzer will be used. | false @@ -45,6 +46,7 @@ Short | Argument Name        | Paramete | \-\-disableNuspec | | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | false | \-\-disableAssembly | | Sets whether or not the .NET Assembly Analyzer should be used. | false | \-\-mono | \ | The path to Mono for .NET Assembly analysis on non-windows systems. |   + | \-\-bundleAudit | | The path to the bundle-audit executable. |   | \-\-proxyserver | \ | The proxy server to use when downloading resources. |   | \-\-proxyport | \ | The proxy port to use when downloading resources. |   | \-\-connectiontimeout | \ | The connection timeout (in milliseconds) to use when downloading resources. |   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 e3e9b43da..a21ce3b32 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 @@ -129,6 +129,10 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { LOGGER.debug("Exception while trying to launch bundle-audit.", ae); setEnabled(false); } + if (isEnabled()) { + LOGGER.info(ANALYZER_NAME + " is enabled. It is necessary to manually run \"bundle-audit update\" " + + "occasionally to keep its database up to date."); + } } /** @@ -208,7 +212,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { map.put(gem, createDependencyForGem(engine, parentName, fileName, gem)); } dependency = map.get(gem); - LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine)); } else if (nextLine.startsWith(VERSION)) { vulnerability = createVulnerability(parentName, dependency, vulnerability, gem, nextLine); } else if (nextLine.startsWith(ADVISORY)) { @@ -238,7 +242,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { if (null != dependency) { dependency.getVulnerabilities().add(vulnerability); // needed to wait for vulnerability name to avoid NPE } - LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine)); } private void addReferenceToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) { @@ -250,7 +254,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { ref.setUrl(url); vulnerability.getReferences().add(ref); } - LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine)); } private void addCriticalityToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) { @@ -266,7 +270,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { vulnerability.setCvssScore(-1.0f); } } - LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine)); } private Vulnerability createVulnerability(String parentName, Dependency dependency, Vulnerability vulnerability, String gem, String nextLine) { @@ -288,7 +292,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { vulnerability.setCvssConfidentialityImpact("-"); vulnerability.setCvssIntegrityImpact("-"); } - LOGGER.info(String.format("bundle-audit (%s): %s", parentName, nextLine)); + LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine)); return vulnerability; } From a0437bf933c33358ea2aa53efda8a4a305a92edb Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Sun, 30 Aug 2015 15:07:21 -0400 Subject: [PATCH 17/19] Ruby bunder: Code needed to disable the analyzer in the CLI if desired. --- .../main/java/org/owasp/dependencycheck/App.java | 2 ++ .../org/owasp/dependencycheck/CliParser.java | 16 ++++++++++++++++ .../src/site/markdown/arguments.md | 2 +- .../analyzer/RubyBundleAuditAnalyzer.java | 2 +- .../owasp/dependencycheck/utils/Settings.java | 4 ++++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java index 4a1d1084a..274f2fb55 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java @@ -252,6 +252,7 @@ public class App { final boolean cMakeDisabled = cli.isCmakeDisabled(); final boolean pyPkgDisabled = cli.isPythonPackageDisabled(); final boolean autoconfDisabled = cli.isAutoconfDisabled(); + final boolean bundleAuditDisabled = cli.isBundleAuditDisabled(); final boolean assemblyDisabled = cli.isAssemblyDisabled(); final boolean nuspecDisabled = cli.isNuspecDisabled(); final boolean centralDisabled = cli.isCentralDisabled(); @@ -325,6 +326,7 @@ public class App { Settings.setBoolean(Settings.KEYS.ANALYZER_CMAKE_ENABLED, !cMakeDisabled); Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !nuspecDisabled); Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !assemblyDisabled); + Settings.setBoolean(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED, !bundleAuditDisabled); Settings.setBoolean(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, !cli.isOpenSSLDisabled()); Settings.setBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, !cli.isNodeJsDisabled()); Settings.setBoolean(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, !cli.isRubyGemspecDisabled()); diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java index 2cd4cb00b..18d7454e8 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java @@ -417,6 +417,8 @@ public final class CliParser { .addOption(disableJarAnalyzer) .addOption(disableArchiveAnalyzer) .addOption(disableAssemblyAnalyzer) + .addOption(OptionBuilder.withLongOpt(ARGUMENT.DISABLE_BUNDLE_AUDIT) + .withDescription("Disable the Ruby Bundler Audit Analyzer.").create()) .addOption(disablePythonDistributionAnalyzer) .addOption(disableCmakeAnalyzer) .addOption(disablePythonPackageAnalyzer) @@ -534,6 +536,16 @@ public final class CliParser { return (line != null) && line.hasOption(ARGUMENT.DISABLE_ASSEMBLY); } + /** + * Returns true if the disableBundleAudit command line argument was specified. + * + * @return true if the disableBundleAudit command line argument was specified; otherwise false + */ + public boolean isBundleAuditDisabled() { + return (line != null) && line.hasOption(ARGUMENT.DISABLE_BUNDLE_AUDIT); + } + + /** * Returns true if the disablePyDist command line argument was specified. * @@ -1129,6 +1141,10 @@ public final class CliParser { * Disables the Assembly Analyzer. */ public static final String DISABLE_ASSEMBLY = "disableAssembly"; + /** + * Disables the Ruby Bundler Audit Analyzer. + */ + public static final String DISABLE_BUNDLE_AUDIT = "disableBundleAudit"; /** * Disables the Nuspec Analyzer. */ diff --git a/dependency-check-cli/src/site/markdown/arguments.md b/dependency-check-cli/src/site/markdown/arguments.md index f3193b41a..0953f85f3 100644 --- a/dependency-check-cli/src/site/markdown/arguments.md +++ b/dependency-check-cli/src/site/markdown/arguments.md @@ -32,7 +32,7 @@ Short | Argument Name        | Paramete | \-\-disablePyPkg | | Sets whether the Python Package Analyzer will be used. | false | \-\-disableNodeJS | | Sets whehter the Node.js Package Analyzer will be used. | false | \-\-disableRubygems | | Sets whether the Ruby Gemspec Analyzer will be used. | false - | \-\-disableBundlerAudit | | Sets whether the Ruby Bundler Audit Analyzer will be used. | false + | \-\-disableBundleAudit | | Sets whether the Ruby Bundler Audit Analyzer will be used. | false | \-\-disableAutoconf | | Sets whether the Autoconf Analyzer will be used. | false | \-\-disableOpenSSL | | Sets whether the OpenSSL Analyzer will be used. | false | \-\-disableCmake | | Sets whether the Cmake Analyzer will be used. | false 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 a21ce3b32..217de39b7 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 @@ -162,7 +162,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { */ @Override protected String getAnalyzerEnabledSettingKey() { - return Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED; + return Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED; } @Override diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java index 94600032e..63c5a0b19 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java @@ -206,6 +206,10 @@ public final class Settings { * The properties key for whether the CMake analyzer is enabled. */ public static final String ANALYZER_CMAKE_ENABLED = "analyzer.cmake.enabled"; + /** + * The properties key for whether the Ruby Bundler Audit analyzer is enabled. + */ + public static final String ANALYZER_BUNDLE_AUDIT_ENABLED = "analyzer.bundle.audit.enabled"; /** * The properties key for whether the .NET Assembly analyzer is enabled. */ From 877a584a26e08ed81bca325ea618fbb8c617ef5d Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Tue, 15 Sep 2015 12:27:26 -0400 Subject: [PATCH 18/19] Ruby Bundler: Disable Gemspec analysis if successful init. Moved to new analysis phase after init, before info collection. --- .../analyzer/AnalysisPhase.java | 4 ++++ .../analyzer/RubyBundleAuditAnalyzer.java | 23 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AnalysisPhase.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AnalysisPhase.java index 5e63d4a77..61d128a18 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AnalysisPhase.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AnalysisPhase.java @@ -28,6 +28,10 @@ public enum AnalysisPhase { * Initialization phase. */ INITIAL, + /** + * Pre information collection phase + */ + PRE_INFORMATION_COLLECTION, /** * Information collection phase. */ 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 217de39b7..1164efddc 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 @@ -49,7 +49,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { /** * The phase that this analyzer is intended to run in. */ - private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.PRE_INFORMATION_COLLECTION; private static final FileFilter FILTER = FileFilterBuilder.newInstance().addFilenames("Gemfile.lock").build(); @@ -165,9 +165,30 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { return Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED; } + /** + * If {@link #analyzeFileType(Dependency, Engine)} is called, then we have successfully initialized, and it will + * be necessary to disable {@link RubyGemspecAnalyzer}. + */ + private boolean needToDisableGemspecAnalyzer = true; + @Override protected void analyzeFileType(Dependency dependency, Engine engine) throws AnalysisException { + if (needToDisableGemspecAnalyzer) { + boolean failed = true; + final String className = RubyGemspecAnalyzer.class.getName(); + for (FileTypeAnalyzer analyzer : engine.getFileTypeAnalyzers()) { + if (analyzer instanceof RubyGemspecAnalyzer) { + ((RubyGemspecAnalyzer) analyzer).setEnabled(false); + LOGGER.info("Disabled " + className + " to avoid noisy duplicate results."); + failed = false; + } + } + if (failed) { + LOGGER.warn("Did not find" + className + '.'); + } + needToDisableGemspecAnalyzer = false; + } final File parentFile = dependency.getActualFile().getParentFile(); final Process process = launchBundleAudit(parentFile); try { From 0573d0083e7eea560fe13219631fbcf50a6916b1 Mon Sep 17 00:00:00 2001 From: Dale Visser Date: Tue, 22 Sep 2015 15:07:43 -0400 Subject: [PATCH 19/19] Ruby Bundler: Throw AnalysisException in initialize if can't run bundle-audit. --- .../analyzer/RubyBundleAuditAnalyzer.java | 54 +++++++++---------- .../analyzer/RubyBundleAuditAnalyzerTest.java | 16 ++++-- 2 files changed, 38 insertions(+), 32 deletions(-) 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 1164efddc..ffb988e03 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 @@ -97,37 +97,33 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { @Override public void initializeFileTypeAnalyzer() throws Exception { // Now, need to see if bundle-audit actually runs from this location. - try { - Process process = launchBundleAudit(Settings.getTempDirectory()); - int exitValue = process.waitFor(); - if (0 == exitValue) { - LOGGER.warn("Unexpected exit code from bundle-audit process. Disabling %s: %d", ANALYZER_NAME, exitValue); - setEnabled(false); - } else { - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8")); - if (!reader.ready()) { - LOGGER.warn("Bundle-audit error stream unexpectedly not ready. Disabling " + ANALYZER_NAME); + Process process = launchBundleAudit(Settings.getTempDirectory()); + int exitValue = process.waitFor(); + if (0 == exitValue) { + LOGGER.warn("Unexpected exit code from bundle-audit process. Disabling {}: {}", ANALYZER_NAME, exitValue); + setEnabled(false); + throw new AnalysisException("Unexpected exit code from bundle-audit process."); + } else { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8")); + if (!reader.ready()) { + LOGGER.warn("Bundle-audit error stream unexpectedly not ready. Disabling " + ANALYZER_NAME); + setEnabled(false); + throw new AnalysisException("Bundle-audit error stream unexpectedly not ready."); + } else { + final String line = reader.readLine(); + if (!line.contains("Errno::ENOENT")) { + LOGGER.warn("Unexpected bundle-audit output. Disabling {}: {}", ANALYZER_NAME, line); setEnabled(false); - } else { - final String line = reader.readLine(); - if (!line.contains("Errno::ENOENT")) { - LOGGER.warn("Unexpected bundle-audit output. Disabling %s: %s", ANALYZER_NAME, line); - setEnabled(false); - } - } - } finally { - if (null != reader) { - reader.close(); + throw new AnalysisException("Unexpected bundle-audit output."); } } + } finally { + if (null != reader) { + reader.close(); + } } - } catch (AnalysisException ae) { - LOGGER.warn("Exception while trying to launch bundle-audit. Disabling " + - ANALYZER_NAME + ". See log file for more details."); - LOGGER.debug("Exception while trying to launch bundle-audit.", ae); - setEnabled(false); } if (isEnabled()) { LOGGER.info(ANALYZER_NAME + " is enabled. It is necessary to manually run \"bundle-audit update\" " + @@ -229,7 +225,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } else if (nextLine.startsWith(NAME)) { appendToDescription = false; gem = nextLine.substring(NAME.length()); - if (!map.containsKey(gem)){ + if (!map.containsKey(gem)) { map.put(gem, createDependencyForGem(engine, parentName, fileName, gem)); } dependency = map.get(gem); @@ -240,7 +236,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { setVulnerabilityName(parentName, dependency, vulnerability, nextLine); } else if (nextLine.startsWith(CRITICALITY)) { addCriticalityToVulnerability(parentName, vulnerability, nextLine); - } else if (nextLine.startsWith("URL: ")){ + } else if (nextLine.startsWith("URL: ")) { addReferenceToVulnerability(parentName, vulnerability, nextLine); } else if (nextLine.startsWith("Description:")) { appendToDescription = true; 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 index 91a7743ea..09ad347b5 100644 --- 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 @@ -18,6 +18,7 @@ package org.owasp.dependencycheck.analyzer; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.owasp.dependencycheck.BaseTest; @@ -25,6 +26,8 @@ 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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; @@ -39,6 +42,8 @@ import static org.junit.Assert.assertThat; */ public class RubyBundleAuditAnalyzerTest extends BaseTest { + private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzerTest.class); + /** * The analyzer to test. */ @@ -51,9 +56,14 @@ public class RubyBundleAuditAnalyzerTest extends BaseTest { */ @Before public void setUp() throws Exception { - analyzer = new RubyBundleAuditAnalyzer(); - analyzer.setFilesMatched(true); - analyzer.initialize(); + try { + analyzer = new RubyBundleAuditAnalyzer(); + analyzer.setFilesMatched(true); + analyzer.initialize(); + } catch (Exception e) { + LOGGER.warn("Exception setting up RubyBundleAuditAnalyzer. Tests will be incomplete", e); + Assume.assumeNoException("Is bundle-audit installed? TESTS WILL BE INCOMPLETE", e); + } } /**