mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-03-21 08:39:24 +01:00
Merge branch 'ruby_dependency' of git://github.com/biancajiang/DependencyCheck into biancajiang-ruby_dependency
This commit is contained in:
@@ -138,6 +138,14 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
|||||||
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
||||||
break; //since we merged into the next dependency - skip forward to the next in mainIterator
|
break; //since we merged into the next dependency - skip forward to the next in mainIterator
|
||||||
}
|
}
|
||||||
|
} else if ( isSameRubyGem(dependency, nextDependency) ) {
|
||||||
|
Dependency main = getMainGemspecDependency(dependency, nextDependency);
|
||||||
|
if (main == dependency) {
|
||||||
|
mergeDependencies(dependency, nextDependency, dependenciesToRemove);
|
||||||
|
} else {
|
||||||
|
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
||||||
|
break; //since we merged into the next dependency - skip forward to the next in mainIterator
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,6 +310,44 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bundling Ruby gems that are identified from different .gemspec files but denote the same package path.
|
||||||
|
* This happens when Ruby bundler installs an app's dependencies by running "bundle install".
|
||||||
|
*/
|
||||||
|
private boolean isSameRubyGem(Dependency dependency1, Dependency dependency2) {
|
||||||
|
if (dependency1 == null || dependency2 == null ||
|
||||||
|
!dependency1.getFileName().endsWith(".gemspec") ||
|
||||||
|
!dependency2.getFileName().endsWith(".gemspec") ||
|
||||||
|
dependency1.getPackagePath() == null ||
|
||||||
|
dependency2.getPackagePath() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ruby gems installed by "bundle install" can have zero or more *.gemspec files, all of which have the same packagePath and should be grouped.
|
||||||
|
* If one of these gemspec is from <parent>/specifications/*.gemspec, because it is a stub with fully resolved gem meta-data
|
||||||
|
* created by Ruby bundler, this dependency should be the main one. Otherwise, use dependency2 as main.
|
||||||
|
*
|
||||||
|
* This method returns null if any dependency is not from *.gemspec, or the two do not have the same packagePath.
|
||||||
|
* In this case, they should not be grouped.
|
||||||
|
*/
|
||||||
|
private Dependency getMainGemspecDependency(Dependency dependency1, Dependency dependency2) {
|
||||||
|
if (isSameRubyGem(dependency1, dependency2)) {
|
||||||
|
final File lFile = dependency1.getActualFile();
|
||||||
|
File left = lFile.getParentFile();
|
||||||
|
if (left != null && left.getName().equalsIgnoreCase("specifications")) {
|
||||||
|
return dependency1;
|
||||||
|
}
|
||||||
|
return dependency2;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is likely a very broken attempt at determining if the 'left' dependency is the 'core' library in comparison to the
|
* This is likely a very broken attempt at determining if the 'left' dependency is the 'core' library in comparison to the
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
|
|||||||
dependency.getVersionEvidence().addEvidence("file", "name",
|
dependency.getVersionEvidence().addEvidence("file", "name",
|
||||||
version.toString(), Confidence.MEDIUM);
|
version.toString(), Confidence.MEDIUM);
|
||||||
} else {
|
} else {
|
||||||
dependency.getVersionEvidence().addEvidence("file", "name",
|
dependency.getVersionEvidence().addEvidence("file", "version",
|
||||||
version.toString(), Confidence.HIGHEST);
|
version.toString(), Confidence.HIGHEST);
|
||||||
}
|
}
|
||||||
dependency.getVersionEvidence().addEvidence("file", "name",
|
dependency.getVersionEvidence().addEvidence("file", "name",
|
||||||
@@ -106,12 +106,13 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//add as vendor and product evidence
|
//add as vendor and product evidence
|
||||||
if (fileName.contains("-")) {
|
// if (fileName.contains("-")) {
|
||||||
dependency.getProductEvidence().addEvidence("file", "name",
|
// dependency.getProductEvidence().addEvidence("file", "name",
|
||||||
fileName, Confidence.HIGHEST);
|
// fileName, Confidence.HIGHEST);
|
||||||
dependency.getVendorEvidence().addEvidence("file", "name",
|
// dependency.getVendorEvidence().addEvidence("file", "name",
|
||||||
fileName, Confidence.HIGHEST);
|
// fileName, Confidence.HIGHEST);
|
||||||
} else if (!IGNORED_FILES.accept(f)) {
|
// } else
|
||||||
|
if (!IGNORED_FILES.accept(f)) {
|
||||||
dependency.getProductEvidence().addEvidence("file", "name",
|
dependency.getProductEvidence().addEvidence("file", "name",
|
||||||
fileName, Confidence.HIGH);
|
fileName, Confidence.HIGH);
|
||||||
dependency.getVendorEvidence().addEvidence("file", "name",
|
dependency.getVendorEvidence().addEvidence("file", "name",
|
||||||
|
|||||||
@@ -565,6 +565,11 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
addMatchingValues(classes, trimmedDescription, dependency.getVendorEvidence());
|
addMatchingValues(classes, trimmedDescription, dependency.getVendorEvidence());
|
||||||
addMatchingValues(classes, trimmedDescription, dependency.getProductEvidence());
|
addMatchingValues(classes, trimmedDescription, dependency.getProductEvidence());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String projectURL = pom.getProjectURL();
|
||||||
|
if(projectURL != null && !projectURL.trim().isEmpty()) {
|
||||||
|
dependency.getVendorEvidence().addEvidence("pom", "url", projectURL, Confidence.HIGHEST);
|
||||||
|
}
|
||||||
|
|
||||||
extractLicense(pom, dependency);
|
extractLicense(pom, dependency);
|
||||||
return foundSomething;
|
return foundSomething;
|
||||||
|
|||||||
@@ -174,20 +174,22 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
final File file = dependency.getActualFile();
|
final File file = dependency.getActualFile();
|
||||||
final File parent = file.getParentFile();
|
final File parent = file.getParentFile();
|
||||||
final String parentName = parent.getName();
|
final String parentName = parent.getName();
|
||||||
boolean found = false;
|
|
||||||
if (INIT_PY_FILTER.accept(file)) {
|
if (INIT_PY_FILTER.accept(file)) {
|
||||||
|
//by definition, the containing folder of __init__.py is considered the package, even the file is empty:
|
||||||
|
//"The __init__.py files are required to make Python treat the directories as containing packages"
|
||||||
|
//see section "6.4 Packages" from https://docs.python.org/2/tutorial/modules.html;
|
||||||
|
dependency.setDisplayFileName(parentName + "/__init__.py");
|
||||||
|
dependency.getProductEvidence().addEvidence(file.getName(),
|
||||||
|
"PackageName", parentName, Confidence.HIGHEST);
|
||||||
|
|
||||||
final File[] fileList = parent.listFiles(PY_FILTER);
|
final File[] fileList = parent.listFiles(PY_FILTER);
|
||||||
if (fileList != null) {
|
if (fileList != null) {
|
||||||
for (final File sourceFile : fileList) {
|
for (final File sourceFile : fileList) {
|
||||||
found |= analyzeFileContents(dependency, sourceFile);
|
analyzeFileContents(dependency, sourceFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found) {
|
else {
|
||||||
dependency.setDisplayFileName(parentName + "/__init__.py");
|
|
||||||
dependency.getProductEvidence().addEvidence(file.getName(),
|
|
||||||
"PackageName", parentName, Confidence.HIGH);
|
|
||||||
} else {
|
|
||||||
// copy, alter and set in case some other thread is iterating over
|
// copy, alter and set in case some other thread is iterating over
|
||||||
final List<Dependency> dependencies = new ArrayList<Dependency>(
|
final List<Dependency> dependencies = new ArrayList<Dependency>(
|
||||||
engine.getDependencies());
|
engine.getDependencies());
|
||||||
|
|||||||
@@ -207,14 +207,18 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
boolean failed = true;
|
boolean failed = true;
|
||||||
final String className = RubyGemspecAnalyzer.class.getName();
|
final String className = RubyGemspecAnalyzer.class.getName();
|
||||||
for (FileTypeAnalyzer analyzer : engine.getFileTypeAnalyzers()) {
|
for (FileTypeAnalyzer analyzer : engine.getFileTypeAnalyzers()) {
|
||||||
if (analyzer instanceof RubyGemspecAnalyzer) {
|
if (analyzer instanceof RubyBundlerAnalyzer) {
|
||||||
|
((RubyBundlerAnalyzer) analyzer).setEnabled(false);
|
||||||
|
LOGGER.info("Disabled " + RubyBundlerAnalyzer.class.getName() + " to avoid noisy duplicate results.");
|
||||||
|
}
|
||||||
|
else if (analyzer instanceof RubyGemspecAnalyzer) {
|
||||||
((RubyGemspecAnalyzer) analyzer).setEnabled(false);
|
((RubyGemspecAnalyzer) analyzer).setEnabled(false);
|
||||||
LOGGER.info("Disabled " + className + " to avoid noisy duplicate results.");
|
LOGGER.info("Disabled " + className + " to avoid noisy duplicate results.");
|
||||||
failed = false;
|
failed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (failed) {
|
if (failed) {
|
||||||
LOGGER.warn("Did not find" + className + '.');
|
LOGGER.warn("Did not find " + className + '.');
|
||||||
}
|
}
|
||||||
needToDisableGemspecAnalyzer = false;
|
needToDisableGemspecAnalyzer = false;
|
||||||
}
|
}
|
||||||
@@ -251,6 +255,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
private void processBundlerAuditOutput(Dependency original, Engine engine, BufferedReader rdr) throws IOException {
|
private void processBundlerAuditOutput(Dependency original, Engine engine, BufferedReader rdr) throws IOException {
|
||||||
final String parentName = original.getActualFile().getParentFile().getName();
|
final String parentName = original.getActualFile().getParentFile().getName();
|
||||||
final String fileName = original.getFileName();
|
final String fileName = original.getFileName();
|
||||||
|
final String filePath = original.getFilePath();
|
||||||
Dependency dependency = null;
|
Dependency dependency = null;
|
||||||
Vulnerability vulnerability = null;
|
Vulnerability vulnerability = null;
|
||||||
String gem = null;
|
String gem = null;
|
||||||
@@ -264,7 +269,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
appendToDescription = false;
|
appendToDescription = false;
|
||||||
gem = nextLine.substring(NAME.length());
|
gem = nextLine.substring(NAME.length());
|
||||||
if (!map.containsKey(gem)) {
|
if (!map.containsKey(gem)) {
|
||||||
map.put(gem, createDependencyForGem(engine, parentName, fileName, gem));
|
map.put(gem, createDependencyForGem(engine, parentName, fileName, filePath, gem));
|
||||||
}
|
}
|
||||||
dependency = map.get(gem);
|
dependency = map.get(gem);
|
||||||
LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine));
|
LOGGER.debug(String.format("bundle-audit (%s): %s", parentName, nextLine));
|
||||||
@@ -359,13 +364,17 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
return vulnerability;
|
return vulnerability;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String gem) throws IOException {
|
private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String filePath, String gem) throws IOException {
|
||||||
final File tempFile = File.createTempFile("Gemfile-" + gem, ".lock", Settings.getTempDirectory());
|
final File gemFile = new File(Settings.getTempDirectory(), gem + "_Gemfile.lock");
|
||||||
|
gemFile.createNewFile();
|
||||||
final String displayFileName = String.format("%s%c%s:%s", parentName, File.separatorChar, fileName, gem);
|
final String displayFileName = String.format("%s%c%s:%s", parentName, File.separatorChar, fileName, gem);
|
||||||
FileUtils.write(tempFile, displayFileName, Charset.defaultCharset()); // unique contents to avoid dependency bundling
|
|
||||||
final Dependency dependency = new Dependency(tempFile);
|
FileUtils.write(gemFile, displayFileName, Charset.defaultCharset()); // unique contents to avoid dependency bundling
|
||||||
|
final Dependency dependency = new Dependency(gemFile);
|
||||||
dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST);
|
dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST);
|
||||||
dependency.setDisplayFileName(displayFileName);
|
dependency.setDisplayFileName(displayFileName);
|
||||||
|
dependency.setFileName(fileName);
|
||||||
|
dependency.setFilePath(filePath);
|
||||||
engine.getDependencies().add(dependency);
|
engine.getDependencies().add(dependency);
|
||||||
return dependency;
|
return dependency;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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) 2016 Bianca Jiang. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
package org.owasp.dependencycheck.analyzer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
|
||||||
|
import org.owasp.dependencycheck.Engine;
|
||||||
|
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||||
|
import org.owasp.dependencycheck.dependency.Dependency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This analyzer accepts the fully resolved .gemspec created by the Ruby bundler (http://bundler.io)
|
||||||
|
* for better evidence results. It also tries to resolve the dependency packagePath
|
||||||
|
* to where the gem is actually installed. Then during {@link AnalysisPhase.PRE_FINDING_ANALYSIS}
|
||||||
|
* {@link DependencyBundlingAnalyzer} will merge two .gemspec dependencies together if
|
||||||
|
* <code>Dependency.getPackagePath()</code> are the same.
|
||||||
|
*
|
||||||
|
* Ruby bundler creates new .gemspec files under a folder called "specifications" at deploy time,
|
||||||
|
* in addition to the original .gemspec files from source. The bundler generated
|
||||||
|
* .gemspec files always contain fully resolved attributes thus provide more accurate
|
||||||
|
* evidences, whereas the original .gemspec from source often contain variables for attributes
|
||||||
|
* that can't be used for evidences.
|
||||||
|
*
|
||||||
|
* Note this analyzer share the same {@link Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED} as
|
||||||
|
* {@link RubyGemspecAnalyzer}, so it will enabled/disabled with {@link RubyGemspecAnalyzer}.
|
||||||
|
*
|
||||||
|
* @author Bianca Jiang (biancajiang@gmail.com)
|
||||||
|
*/
|
||||||
|
public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the analyzer.
|
||||||
|
*/
|
||||||
|
private static final String ANALYZER_NAME = "Ruby Bundler Analyzer";
|
||||||
|
|
||||||
|
//Folder name that contains .gemspec files created by "bundle install"
|
||||||
|
private static final String SPECIFICATIONS = "specifications";
|
||||||
|
|
||||||
|
//Folder name that contains the gems by "bundle install"
|
||||||
|
private static final String GEMS = "gems";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the analyzer.
|
||||||
|
*
|
||||||
|
* @return the name of the analyzer.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return ANALYZER_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only accept *.gemspec files generated by "bundle install --deployment" under "specifications" folder.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean accept(File pathname) {
|
||||||
|
|
||||||
|
boolean accepted = super.accept(pathname);
|
||||||
|
if(accepted == true) {
|
||||||
|
File parentDir = pathname.getParentFile();
|
||||||
|
accepted = parentDir != null && parentDir.getName().equals(SPECIFICATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||||
|
throws AnalysisException {
|
||||||
|
super.analyzeFileType(dependency, engine);
|
||||||
|
|
||||||
|
//find the corresponding gem folder for this .gemspec stub by "bundle install --deployment"
|
||||||
|
File gemspecFile = dependency.getActualFile();
|
||||||
|
String gemFileName = gemspecFile.getName();
|
||||||
|
final String gemName = gemFileName.substring(0, gemFileName.lastIndexOf(".gemspec"));
|
||||||
|
File specificationsDir = gemspecFile.getParentFile();
|
||||||
|
if(specificationsDir != null && specificationsDir.getName().equals(SPECIFICATIONS) && specificationsDir.exists()) {
|
||||||
|
File parentDir = specificationsDir.getParentFile();
|
||||||
|
if(parentDir != null && parentDir.exists()) {
|
||||||
|
File gemsDir = new File(parentDir, GEMS);
|
||||||
|
if(gemsDir != null && gemsDir.exists()) {
|
||||||
|
File[] matchingFiles = gemsDir.listFiles(new FilenameFilter() {
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return name.equals(gemName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(matchingFiles.length > 0) {
|
||||||
|
String gemPath = matchingFiles[0].getAbsolutePath();
|
||||||
|
if(dependency.getActualFilePath().equals(dependency.getFilePath())) {
|
||||||
|
if(gemPath != null)
|
||||||
|
dependency.setPackagePath(gemPath);
|
||||||
|
} else {
|
||||||
|
//.gemspec's actualFilePath and filePath are different when it's from a compressed file
|
||||||
|
//in which case actualFilePath is the temp directory used by decompression.
|
||||||
|
//packagePath should use the filePath of the identified gem file in "gems" folder
|
||||||
|
File gemspecStub = new File(dependency.getFilePath());
|
||||||
|
File specDir = gemspecStub.getParentFile();
|
||||||
|
if(specDir != null && specDir.getName().equals(SPECIFICATIONS)) {
|
||||||
|
File gemsDir2 = new File(specDir.getParentFile(), GEMS);
|
||||||
|
File packageDir = new File(gemsDir2, gemName);
|
||||||
|
dependency.setPackagePath(packageDir.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.owasp.dependencycheck.analyzer;
|
package org.owasp.dependencycheck.analyzer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.owasp.dependencycheck.Engine;
|
import org.owasp.dependencycheck.Engine;
|
||||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||||
@@ -26,12 +35,6 @@ import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
|||||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||||
import org.owasp.dependencycheck.utils.Settings;
|
import org.owasp.dependencycheck.utils.Settings;
|
||||||
|
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to analyze Ruby Gem specifications and collect information that can be used to determine the associated CPE. Regular
|
* Used to analyze Ruby Gem specifications and collect information that can be used to determine the associated CPE. Regular
|
||||||
* expressions are used to parse the well-defined Ruby syntax that forms the specification.
|
* expressions are used to parse the well-defined Ruby syntax that forms the specification.
|
||||||
@@ -53,12 +56,14 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
private static final String GEMSPEC = "gemspec";
|
private static final String GEMSPEC = "gemspec";
|
||||||
|
|
||||||
private static final FileFilter FILTER
|
private static final FileFilter FILTER
|
||||||
= FileFilterBuilder.newInstance().addExtensions(GEMSPEC).addFilenames("Rakefile").build();
|
= FileFilterBuilder.newInstance().addExtensions(GEMSPEC).build();
|
||||||
|
//TODO: support Rakefile
|
||||||
|
//= FileFilterBuilder.newInstance().addExtensions(GEMSPEC).addFilenames("Rakefile").build();
|
||||||
|
|
||||||
private static final String EMAIL = "email";
|
private static final String VERSION_FILE_NAME = "VERSION";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a filter that accepts files named Rakefile or matching the glob pattern, *.gemspec
|
* @return a filter that accepts files matching the glob pattern, *.gemspec
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected FileFilter getFileFilter() {
|
protected FileFilter getFileFilter() {
|
||||||
@@ -120,43 +125,84 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
contents = contents.substring(matcher.end());
|
contents = contents.substring(matcher.end());
|
||||||
final String blockVariable = matcher.group(1);
|
final String blockVariable = matcher.group(1);
|
||||||
|
|
||||||
final EvidenceCollection vendor = dependency.getVendorEvidence();
|
final EvidenceCollection vendor = dependency.getVendorEvidence();
|
||||||
addStringEvidence(vendor, contents, blockVariable, "author", Confidence.HIGHEST);
|
|
||||||
addListEvidence(vendor, contents, blockVariable, "authors", Confidence.HIGHEST);
|
|
||||||
final String email = addStringEvidence(vendor, contents, blockVariable, EMAIL, Confidence.MEDIUM);
|
|
||||||
if (email.isEmpty()) {
|
|
||||||
addListEvidence(vendor, contents, blockVariable, EMAIL, Confidence.MEDIUM);
|
|
||||||
}
|
|
||||||
addStringEvidence(vendor, contents, blockVariable, "homepage", Confidence.MEDIUM);
|
|
||||||
final EvidenceCollection product = dependency.getProductEvidence();
|
final EvidenceCollection product = dependency.getProductEvidence();
|
||||||
final String name = addStringEvidence(product, contents, blockVariable, "name", Confidence.HIGHEST);
|
final String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST);
|
||||||
if (!name.isEmpty()) {
|
if (!name.isEmpty()) {
|
||||||
vendor.addEvidence(GEMSPEC, "name_project", name + "_project", Confidence.LOW);
|
vendor.addEvidence(GEMSPEC, "name_project", name + "_project", Confidence.LOW);
|
||||||
}
|
}
|
||||||
addStringEvidence(product, contents, blockVariable, "summary", Confidence.LOW);
|
addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.LOW);
|
||||||
addStringEvidence(dependency.getVersionEvidence(), contents, blockVariable, "version", Confidence.HIGHEST);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addListEvidence(EvidenceCollection evidences, String contents,
|
addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST);
|
||||||
String blockVariable, String field, Confidence confidence) {
|
addStringEvidence(vendor, contents, blockVariable, "email", "emails?", Confidence.MEDIUM);
|
||||||
final Matcher matcher = Pattern.compile(
|
addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST);
|
||||||
String.format("\\s+?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, field)).matcher(contents);
|
addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST);
|
||||||
if (matcher.find()) {
|
|
||||||
final String value = matcher.group(1).replaceAll("['\"]", " ").trim();
|
String value = addStringEvidence(dependency.getVersionEvidence(), contents, blockVariable, "version", "version", Confidence.HIGHEST);
|
||||||
evidences.addEvidence(GEMSPEC, field, value, confidence);
|
if(value.length() < 1)
|
||||||
|
addEvidenceFromVersionFile(dependency.getActualFile(), dependency.getVersionEvidence());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPackagePath(dependency);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String addStringEvidence(EvidenceCollection evidences, String contents,
|
private String addStringEvidence(EvidenceCollection evidences, String contents,
|
||||||
String blockVariable, String field, Confidence confidence) {
|
String blockVariable, String field, String fieldPattern, Confidence confidence) {
|
||||||
final Matcher matcher = Pattern.compile(
|
|
||||||
String.format("\\s+?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, field)).matcher(contents);
|
|
||||||
String value = "";
|
String value = "";
|
||||||
if (matcher.find()) {
|
|
||||||
value = matcher.group(2);
|
//capture array value between [ ]
|
||||||
evidences.addEvidence(GEMSPEC, field, value, confidence);
|
final Matcher arrayMatcher = Pattern.compile(
|
||||||
}
|
String.format("\\s*?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents);
|
||||||
|
if(arrayMatcher.find()) {
|
||||||
|
String arrayValue = arrayMatcher.group(1);
|
||||||
|
value = arrayValue.replaceAll("['\"]", "").trim(); //strip quotes
|
||||||
|
}
|
||||||
|
//capture single value between quotes
|
||||||
|
else {
|
||||||
|
final Matcher matcher = Pattern.compile(
|
||||||
|
String.format("\\s*?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents);
|
||||||
|
if (matcher.find()) {
|
||||||
|
value = matcher.group(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(value.length() > 0)
|
||||||
|
evidences.addEvidence(GEMSPEC, field, value, confidence);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String addEvidenceFromVersionFile(File dependencyFile, EvidenceCollection versionEvidences) {
|
||||||
|
String value = null;
|
||||||
|
File parentDir = dependencyFile.getParentFile();
|
||||||
|
if(parentDir != null) {
|
||||||
|
File[] matchingFiles = parentDir.listFiles(new FilenameFilter() {
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return name.contains(VERSION_FILE_NAME);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for(int i = 0; i < matchingFiles.length; i++) {
|
||||||
|
try {
|
||||||
|
List<String> lines = FileUtils.readLines(matchingFiles[i]);
|
||||||
|
if(lines.size() == 1) { //TODO other checking?
|
||||||
|
value = lines.get(0).trim();
|
||||||
|
versionEvidences.addEvidence(GEMSPEC, "version", value, Confidence.HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPackagePath(Dependency dep) {
|
||||||
|
File file = new File(dep.getFilePath());
|
||||||
|
String parent = file.getParent();
|
||||||
|
if(parent != null)
|
||||||
|
dep.setPackagePath(parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,17 @@ public class Dependency implements Serializable, Comparable<Dependency> {
|
|||||||
* The file name of the dependency.
|
* The file name of the dependency.
|
||||||
*/
|
*/
|
||||||
private String fileName;
|
private String fileName;
|
||||||
/**
|
|
||||||
|
private String packagePath;
|
||||||
|
public String getPackagePath() {
|
||||||
|
return packagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPackagePath(String packagePath) {
|
||||||
|
this.packagePath = packagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* The md5 hash of the dependency.
|
* The md5 hash of the dependency.
|
||||||
*/
|
*/
|
||||||
private String md5sum;
|
private String md5sum;
|
||||||
@@ -120,6 +130,7 @@ public class Dependency implements Serializable, Comparable<Dependency> {
|
|||||||
this.actualFilePath = file.getAbsolutePath();
|
this.actualFilePath = file.getAbsolutePath();
|
||||||
this.filePath = this.actualFilePath;
|
this.filePath = this.actualFilePath;
|
||||||
this.fileName = file.getName();
|
this.fileName = file.getName();
|
||||||
|
this.packagePath = filePath;
|
||||||
determineHashes(file);
|
determineHashes(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +199,9 @@ public class Dependency implements Serializable, Comparable<Dependency> {
|
|||||||
* @param filePath the file path of the dependency
|
* @param filePath the file path of the dependency
|
||||||
*/
|
*/
|
||||||
public void setFilePath(String filePath) {
|
public void setFilePath(String filePath) {
|
||||||
|
if(this.packagePath == null || this.packagePath.equals(this.filePath))
|
||||||
|
this.packagePath = filePath;
|
||||||
|
|
||||||
this.filePath = filePath;
|
this.filePath = filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -719,6 +733,7 @@ public class Dependency implements Serializable, Comparable<Dependency> {
|
|||||||
.append(this.actualFilePath, other.actualFilePath)
|
.append(this.actualFilePath, other.actualFilePath)
|
||||||
.append(this.filePath, other.filePath)
|
.append(this.filePath, other.filePath)
|
||||||
.append(this.fileName, other.fileName)
|
.append(this.fileName, other.fileName)
|
||||||
|
.append(this.packagePath, other.packagePath)
|
||||||
.append(this.md5sum, other.md5sum)
|
.append(this.md5sum, other.md5sum)
|
||||||
.append(this.sha1sum, other.sha1sum)
|
.append(this.sha1sum, other.sha1sum)
|
||||||
.append(this.identifiers, other.identifiers)
|
.append(this.identifiers, other.identifiers)
|
||||||
@@ -767,6 +782,6 @@ public class Dependency implements Serializable, Comparable<Dependency> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Dependency{ fileName='" + fileName + "', actualFilePath='" + actualFilePath + "', filePath='" + filePath + "'}";
|
return "Dependency{ fileName='" + fileName + "', actualFilePath='" + actualFilePath + "', filePath='" + filePath + "', packagePath='" + packagePath + "'}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,6 +260,29 @@ public class Model {
|
|||||||
public void addLicense(License license) {
|
public void addLicense(License license) {
|
||||||
licenses.add(license);
|
licenses.add(license);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The project URL.
|
||||||
|
*/
|
||||||
|
private String projectURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of projectURL.
|
||||||
|
*
|
||||||
|
* @return the value of projectURL
|
||||||
|
*/
|
||||||
|
public String getProjectURL() {
|
||||||
|
return projectURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of projectURL.
|
||||||
|
*
|
||||||
|
* @param parentVersion new value of projectURL
|
||||||
|
*/
|
||||||
|
public void setProjectURL(String projectURL) {
|
||||||
|
this.projectURL = projectURL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the Maven properties file and interpolate all properties.
|
* Process the Maven properties file and interpolate all properties.
|
||||||
@@ -276,11 +299,11 @@ public class Model {
|
|||||||
l.setUrl(interpolateString(l.getUrl(), properties));
|
l.setUrl(interpolateString(l.getUrl(), properties));
|
||||||
}
|
}
|
||||||
this.name = interpolateString(this.name, properties);
|
this.name = interpolateString(this.name, properties);
|
||||||
|
this.projectURL = interpolateString(this.projectURL, properties);
|
||||||
this.organization = interpolateString(this.organization, properties);
|
this.organization = interpolateString(this.organization, properties);
|
||||||
this.parentGroupId = interpolateString(this.parentGroupId, properties);
|
this.parentGroupId = interpolateString(this.parentGroupId, properties);
|
||||||
this.parentArtifactId = interpolateString(this.parentArtifactId, properties);
|
this.parentArtifactId = interpolateString(this.parentArtifactId, properties);
|
||||||
this.parentVersion = interpolateString(this.parentVersion, properties);
|
this.parentVersion = interpolateString(this.parentVersion, properties);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -317,7 +340,7 @@ public class Model {
|
|||||||
return substitutor.replace(text);
|
return substitutor.replace(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class that can provide values from a Properties object to a StrSubstitutor.
|
* Utility class that can provide values from a Properties object to a StrSubstitutor.
|
||||||
*/
|
*/
|
||||||
private static class PropertyLookup extends StrLookup {
|
private static class PropertyLookup extends StrLookup {
|
||||||
|
|||||||
@@ -145,6 +145,8 @@ public class PomHandler extends DefaultHandler {
|
|||||||
model.setOrganization(currentText.toString());
|
model.setOrganization(currentText.toString());
|
||||||
} else if (DESCRIPTION.equals(qName)) {
|
} else if (DESCRIPTION.equals(qName)) {
|
||||||
model.setDescription(currentText.toString());
|
model.setDescription(currentText.toString());
|
||||||
|
} else if (URL.equals(qName)) {
|
||||||
|
model.setProjectURL(currentText.toString());
|
||||||
}
|
}
|
||||||
} else if (PARENT.equals(parentNode)) {
|
} else if (PARENT.equals(parentNode)) {
|
||||||
if (GROUPID.equals(qName)) {
|
if (GROUPID.equals(qName)) {
|
||||||
|
|||||||
@@ -19,5 +19,6 @@ org.owasp.dependencycheck.analyzer.OpenSSLAnalyzer
|
|||||||
org.owasp.dependencycheck.analyzer.CMakeAnalyzer
|
org.owasp.dependencycheck.analyzer.CMakeAnalyzer
|
||||||
org.owasp.dependencycheck.analyzer.NodePackageAnalyzer
|
org.owasp.dependencycheck.analyzer.NodePackageAnalyzer
|
||||||
org.owasp.dependencycheck.analyzer.RubyGemspecAnalyzer
|
org.owasp.dependencycheck.analyzer.RubyGemspecAnalyzer
|
||||||
|
org.owasp.dependencycheck.analyzer.RubyBundlerAnalyzer
|
||||||
org.owasp.dependencycheck.analyzer.RubyBundleAuditAnalyzer
|
org.owasp.dependencycheck.analyzer.RubyBundleAuditAnalyzer
|
||||||
org.owasp.dependencycheck.analyzer.ComposerLockAnalyzer
|
org.owasp.dependencycheck.analyzer.ComposerLockAnalyzer
|
||||||
|
|||||||
@@ -17,23 +17,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.owasp.dependencycheck.analyzer;
|
package org.owasp.dependencycheck.analyzer;
|
||||||
|
|
||||||
import org.junit.Test;
|
import static org.junit.Assert.assertEquals;
|
||||||
import org.owasp.dependencycheck.BaseTest;
|
import static org.junit.Assert.assertTrue;
|
||||||
import org.owasp.dependencycheck.dependency.Dependency;
|
|
||||||
import org.owasp.dependencycheck.dependency.Evidence;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import org.junit.Test;
|
||||||
import static org.junit.Assert.assertTrue;
|
import org.owasp.dependencycheck.BaseTest;
|
||||||
|
import org.owasp.dependencycheck.dependency.Dependency;
|
||||||
|
import org.owasp.dependencycheck.dependency.Evidence;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Jeremy Long
|
* @author Jeremy Long
|
||||||
*/
|
*/
|
||||||
public class JarAnalyzerTest extends BaseTest {
|
public class JarAnalyzerTest extends BaseTest {
|
||||||
|
|
||||||
|
// private static final Logger LOGGER = LoggerFactory.getLogger(JarAnalyzerTest.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test of inspect method, of class JarAnalyzer.
|
* Test of inspect method, of class JarAnalyzer.
|
||||||
*
|
*
|
||||||
@@ -48,12 +50,25 @@ public class JarAnalyzerTest extends BaseTest {
|
|||||||
instance.analyze(result, null);
|
instance.analyze(result, null);
|
||||||
assertTrue(result.getVendorEvidence().toString().toLowerCase().contains("apache"));
|
assertTrue(result.getVendorEvidence().toString().toLowerCase().contains("apache"));
|
||||||
assertTrue(result.getVendorEvidence().getWeighting().contains("apache"));
|
assertTrue(result.getVendorEvidence().getWeighting().contains("apache"));
|
||||||
|
|
||||||
|
file = BaseTest.getResourceAsFile(this, "dwr.jar");
|
||||||
|
result = new Dependency(file);
|
||||||
|
instance.analyze(result, null);
|
||||||
|
boolean found = false;
|
||||||
|
for (Evidence e : result.getVendorEvidence()) {
|
||||||
|
if (e.getName().equals("url")) {
|
||||||
|
assertEquals("Project url was not as expected in dwr.jar", e.getValue(), "http://getahead.ltd.uk/dwr");
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue("Project url was not found in dwr.jar", found);
|
||||||
|
|
||||||
//file = new File(this.getClass().getClassLoader().getResource("org.mortbay.jetty.jar").getPath());
|
//file = new File(this.getClass().getClassLoader().getResource("org.mortbay.jetty.jar").getPath());
|
||||||
file = BaseTest.getResourceAsFile(this, "org.mortbay.jetty.jar");
|
file = BaseTest.getResourceAsFile(this, "org.mortbay.jetty.jar");
|
||||||
result = new Dependency(file);
|
result = new Dependency(file);
|
||||||
instance.analyze(result, null);
|
instance.analyze(result, null);
|
||||||
boolean found = false;
|
found = false;
|
||||||
for (Evidence e : result.getProductEvidence()) {
|
for (Evidence e : result.getProductEvidence()) {
|
||||||
if (e.getName().equalsIgnoreCase("package-title")
|
if (e.getName().equalsIgnoreCase("package-title")
|
||||||
&& e.getValue().equalsIgnoreCase("org.mortbay.http")) {
|
&& e.getValue().equalsIgnoreCase("org.mortbay.http")) {
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ import static org.junit.Assert.assertThat;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assume;
|
import org.junit.Assume;
|
||||||
@@ -33,6 +36,8 @@ import org.owasp.dependencycheck.Engine;
|
|||||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||||
import org.owasp.dependencycheck.dependency.Dependency;
|
import org.owasp.dependencycheck.dependency.Dependency;
|
||||||
|
import org.owasp.dependencycheck.dependency.Evidence;
|
||||||
|
import org.owasp.dependencycheck.dependency.Identifier;
|
||||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||||
import org.owasp.dependencycheck.utils.Settings;
|
import org.owasp.dependencycheck.utils.Settings;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -101,19 +106,19 @@ public class RubyBundleAuditAnalyzerTest extends BaseTest {
|
|||||||
public void testAnalysis() throws AnalysisException, DatabaseException {
|
public void testAnalysis() throws AnalysisException, DatabaseException {
|
||||||
try {
|
try {
|
||||||
analyzer.initialize();
|
analyzer.initialize();
|
||||||
|
final String resource = "ruby/vulnerable/gems/rails-4.1.15/Gemfile.lock";
|
||||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
|
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, resource));
|
||||||
"ruby/vulnerable/gems/rails-4.1.15/Gemfile.lock"));
|
|
||||||
final Engine engine = new Engine();
|
final Engine engine = new Engine();
|
||||||
analyzer.analyze(result, engine);
|
analyzer.analyze(result, engine);
|
||||||
int size = engine.getDependencies().size();
|
int size = engine.getDependencies().size();
|
||||||
assertThat(size, is(1));
|
|
||||||
|
|
||||||
|
assertTrue(size >= 1);
|
||||||
|
|
||||||
Dependency dependency = engine.getDependencies().get(0);
|
Dependency dependency = engine.getDependencies().get(0);
|
||||||
assertTrue(dependency.getProductEvidence().toString().toLowerCase().contains("redcarpet"));
|
assertTrue(dependency.getProductEvidence().toString().toLowerCase().contains("redcarpet"));
|
||||||
assertTrue(dependency.getVersionEvidence().toString().toLowerCase().contains("2.2.2"));
|
assertTrue(dependency.getVersionEvidence().toString().toLowerCase().contains("2.2.2"));
|
||||||
|
assertTrue(dependency.getFilePath().endsWith(resource));
|
||||||
|
assertTrue(dependency.getFileName().equals("Gemfile.lock"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.warn("Exception setting up RubyBundleAuditAnalyzer. Make sure Ruby gem bundle-audit is installed. You may also need to set property \"analyzer.bundle.audit.path\".", e);
|
LOGGER.warn("Exception setting up RubyBundleAuditAnalyzer. Make sure Ruby gem bundle-audit is installed. You may also need to set property \"analyzer.bundle.audit.path\".", e);
|
||||||
Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", e);
|
Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", e);
|
||||||
@@ -166,5 +171,52 @@ public class RubyBundleAuditAnalyzerTest extends BaseTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Ruby dependencies and their paths.
|
||||||
|
*
|
||||||
|
* @throws AnalysisException is thrown when an exception occurs.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDependenciesPath() throws AnalysisException, DatabaseException {
|
||||||
|
|
||||||
|
final Engine engine = new Engine();
|
||||||
|
engine.scan(BaseTest.getResourceAsFile(this,
|
||||||
|
"ruby/vulnerable/gems/rails-4.1.15/"));
|
||||||
|
engine.analyzeDependencies();
|
||||||
|
|
||||||
|
List<Dependency> dependencies = engine.getDependencies();
|
||||||
|
LOGGER.info(dependencies.size() + " dependencies found.");
|
||||||
|
Iterator<Dependency> dIterator = dependencies.iterator();
|
||||||
|
while(dIterator.hasNext()) {
|
||||||
|
Dependency dept = dIterator.next();
|
||||||
|
LOGGER.info("dept path: " + dept.getActualFilePath());
|
||||||
|
|
||||||
|
Set<Identifier> identifiers = dept.getIdentifiers();
|
||||||
|
Iterator<Identifier> idIterator = identifiers.iterator();
|
||||||
|
while(idIterator.hasNext()) {
|
||||||
|
Identifier id = idIterator.next();
|
||||||
|
LOGGER.info(" Identifier: " + id.getValue() + ", type=" + id.getType() + ", url=" + id.getUrl() + ", conf="+ id.getConfidence());
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Evidence> prodEv = dept.getProductEvidence().getEvidence();
|
||||||
|
Iterator<Evidence> it = prodEv.iterator();
|
||||||
|
while(it.hasNext()) {
|
||||||
|
Evidence e = it.next();
|
||||||
|
LOGGER.info(" prod: name=" + e.getName() + ", value=" + e.getValue() + ", source=" + e.getSource() + ", confidence=" + e.getConfidence());
|
||||||
|
}
|
||||||
|
Set<Evidence> versionEv = dept.getVersionEvidence().getEvidence();
|
||||||
|
Iterator<Evidence> vIt = versionEv.iterator();
|
||||||
|
while(vIt.hasNext()) {
|
||||||
|
Evidence e = vIt.next();
|
||||||
|
LOGGER.info(" version: name=" + e.getName() + ", value=" + e.getValue() + ", source=" + e.getSource() + ", confidence=" + e.getConfidence());
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Evidence> vendorEv = dept.getVendorEvidence().getEvidence();
|
||||||
|
Iterator<Evidence> vendorIt = vendorEv.iterator();
|
||||||
|
while(vendorIt.hasNext()) {
|
||||||
|
Evidence e = vendorIt.next();
|
||||||
|
LOGGER.info(" vendor: name=" + e.getName() + ", value=" + e.getValue() + ", source=" + e.getSource() + ", confidence=" + e.getConfidence());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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) 2016 Bianca Jiang. 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.analyzer.exception.AnalysisException;
|
||||||
|
import org.owasp.dependencycheck.dependency.Dependency;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link RubyBundlerAnalyzer}.
|
||||||
|
*
|
||||||
|
* @author Bianca Jiang
|
||||||
|
*/
|
||||||
|
public class RubyBundlerAnalyzerTest extends BaseTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The analyzer to test.
|
||||||
|
*/
|
||||||
|
RubyBundlerAnalyzer analyzer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correctly setup the analyzer for testing.
|
||||||
|
*
|
||||||
|
* @throws Exception thrown if there is a problem
|
||||||
|
*/
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
analyzer = new RubyBundlerAnalyzer();
|
||||||
|
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 Analyzer name.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetName() {
|
||||||
|
assertThat(analyzer.getName(), is("Ruby Bundler Analyzer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Ruby Gemspec file support.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSupportsFiles() {
|
||||||
|
assertThat(analyzer.accept(new File("test.gemspec")), is(false));
|
||||||
|
assertThat(analyzer.accept(new File("specifications" + File.separator + "test.gemspec")), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Ruby Bundler created gemspec analysis.
|
||||||
|
*
|
||||||
|
* @throws AnalysisException is thrown when an exception occurs.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAnalyzeGemspec() throws AnalysisException {
|
||||||
|
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
|
||||||
|
"ruby/vulnerable/gems/rails-4.1.15/vendor/bundle/ruby/2.2.0/specifications/dalli-2.7.5.gemspec"));
|
||||||
|
analyzer.analyze(result, null);
|
||||||
|
|
||||||
|
final String vendorString = result.getVendorEvidence().toString();
|
||||||
|
assertThat(vendorString, containsString("Peter M. Goldstein"));
|
||||||
|
assertThat(vendorString, containsString("Mike Perham"));
|
||||||
|
assertThat(vendorString, containsString("peter.m.goldstein@gmail.com"));
|
||||||
|
assertThat(vendorString, containsString("https://github.com/petergoldstein/dalli"));
|
||||||
|
assertThat(vendorString, containsString("MIT"));
|
||||||
|
assertThat(result.getProductEvidence().toString(), containsString("dalli"));
|
||||||
|
assertThat(result.getProductEvidence().toString(), containsString("High performance memcached client for Ruby"));
|
||||||
|
assertThat(result.getVersionEvidence().toString(), containsString("2.7.5"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,7 +79,7 @@ public class RubyGemspecAnalyzerTest extends BaseTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testSupportsFiles() {
|
public void testSupportsFiles() {
|
||||||
assertThat(analyzer.accept(new File("test.gemspec")), is(true));
|
assertThat(analyzer.accept(new File("test.gemspec")), is(true));
|
||||||
assertThat(analyzer.accept(new File("Rakefile")), is(true));
|
// assertThat(analyzer.accept(new File("Rakefile")), is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,4 +100,17 @@ public class RubyGemspecAnalyzerTest extends BaseTest {
|
|||||||
assertThat(result.getProductEvidence().toString(), containsString("rest-client"));
|
assertThat(result.getProductEvidence().toString(), containsString("rest-client"));
|
||||||
assertThat(result.getVersionEvidence().toString(), containsString("1.7.2"));
|
assertThat(result.getVersionEvidence().toString(), containsString("1.7.2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Rakefile analysis.
|
||||||
|
*
|
||||||
|
* @throws AnalysisException is thrown when an exception occurs.
|
||||||
|
*/
|
||||||
|
//@Test TODO: place holder to test Rakefile support
|
||||||
|
public void testAnalyzeRakefile() throws AnalysisException {
|
||||||
|
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
|
||||||
|
"ruby/vulnerable/gems/rails-4.1.15/vendor/bundle/ruby/2.2.0/gems/pg-0.18.4/Rakefile"));
|
||||||
|
analyzer.analyze(result, null);
|
||||||
|
//TODO add verification
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,4 +101,4 @@ analyzer.nexus.enabled=false
|
|||||||
analyzer.nexus.proxy=true
|
analyzer.nexus.proxy=true
|
||||||
|
|
||||||
#Use your own bundle-audit install directory.
|
#Use your own bundle-audit install directory.
|
||||||
#analyzer.bundle.audit.path=/usr/local/bin/bundle-audit
|
analyzer.bundle.audit.path=/usr/local/bin/bundle-audit
|
||||||
|
|||||||
BIN
dependency-check-core/src/test/resources/dwr.jar
Normal file
BIN
dependency-check-core/src/test/resources/dwr.jar
Normal file
Binary file not shown.
@@ -0,0 +1,218 @@
|
|||||||
|
#!/usr/bin/env rake
|
||||||
|
|
||||||
|
require 'rbconfig'
|
||||||
|
require 'pathname'
|
||||||
|
require 'tmpdir'
|
||||||
|
|
||||||
|
begin
|
||||||
|
require 'rake/extensiontask'
|
||||||
|
rescue LoadError
|
||||||
|
abort "This Rakefile requires rake-compiler (gem install rake-compiler)"
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
require 'hoe'
|
||||||
|
rescue LoadError
|
||||||
|
abort "This Rakefile requires hoe (gem install hoe)"
|
||||||
|
end
|
||||||
|
|
||||||
|
require 'rake/clean'
|
||||||
|
|
||||||
|
# Build directory constants
|
||||||
|
BASEDIR = Pathname( __FILE__ ).dirname
|
||||||
|
SPECDIR = BASEDIR + 'spec'
|
||||||
|
LIBDIR = BASEDIR + 'lib'
|
||||||
|
EXTDIR = BASEDIR + 'ext'
|
||||||
|
PKGDIR = BASEDIR + 'pkg'
|
||||||
|
TMPDIR = BASEDIR + 'tmp'
|
||||||
|
|
||||||
|
DLEXT = RbConfig::CONFIG['DLEXT']
|
||||||
|
EXT = LIBDIR + "pg_ext.#{DLEXT}"
|
||||||
|
|
||||||
|
GEMSPEC = 'pg.gemspec'
|
||||||
|
|
||||||
|
TEST_DIRECTORY = BASEDIR + "tmp_test_specs"
|
||||||
|
|
||||||
|
CLOBBER.include( TEST_DIRECTORY.to_s )
|
||||||
|
CLEAN.include( PKGDIR.to_s, TMPDIR.to_s )
|
||||||
|
|
||||||
|
# Set up Hoe plugins
|
||||||
|
Hoe.plugin :mercurial
|
||||||
|
Hoe.plugin :signing
|
||||||
|
Hoe.plugin :deveiate
|
||||||
|
Hoe.plugin :bundler
|
||||||
|
|
||||||
|
Hoe.plugins.delete :rubyforge
|
||||||
|
Hoe.plugins.delete :compiler
|
||||||
|
|
||||||
|
load 'Rakefile.cross'
|
||||||
|
|
||||||
|
|
||||||
|
# Hoe specification
|
||||||
|
$hoespec = Hoe.spec 'pg' do
|
||||||
|
self.readme_file = 'README.rdoc'
|
||||||
|
self.history_file = 'History.rdoc'
|
||||||
|
self.extra_rdoc_files = Rake::FileList[ '*.rdoc' ]
|
||||||
|
self.extra_rdoc_files.include( 'POSTGRES', 'LICENSE' )
|
||||||
|
self.extra_rdoc_files.include( 'ext/*.c' )
|
||||||
|
self.license :BSD
|
||||||
|
|
||||||
|
self.developer 'Michael Granger', 'ged@FaerieMUD.org'
|
||||||
|
self.developer 'Lars Kanis', 'lars@greiz-reinsdorf.de'
|
||||||
|
|
||||||
|
self.dependency 'rake-compiler', '~> 0.9', :developer
|
||||||
|
self.dependency 'rake-compiler-dock', '~> 0.3', :developer
|
||||||
|
self.dependency 'hoe', '~> 3.12', :developer
|
||||||
|
self.dependency 'hoe-deveiate', '~> 0.6', :developer
|
||||||
|
self.dependency 'hoe-bundler', '~> 1.0', :developer
|
||||||
|
self.dependency 'rspec', '~> 3.0', :developer
|
||||||
|
|
||||||
|
self.spec_extras[:licenses] = ['BSD', 'Ruby', 'GPL']
|
||||||
|
self.spec_extras[:extensions] = [ 'ext/extconf.rb' ]
|
||||||
|
|
||||||
|
self.require_ruby_version( '>= 1.9.3' )
|
||||||
|
|
||||||
|
self.hg_sign_tags = true if self.respond_to?( :hg_sign_tags= )
|
||||||
|
self.check_history_on_release = true if self.respond_to?( :check_history_on_release= )
|
||||||
|
|
||||||
|
self.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
|
||||||
|
end
|
||||||
|
|
||||||
|
ENV['VERSION'] ||= $hoespec.spec.version.to_s
|
||||||
|
|
||||||
|
# Tests should pass before checking in
|
||||||
|
task 'hg:precheckin' => [ :check_history, :check_manifest, :spec ]
|
||||||
|
|
||||||
|
# Support for 'rvm specs'
|
||||||
|
task :specs => :spec
|
||||||
|
|
||||||
|
# Compile before testing
|
||||||
|
task :spec => :compile
|
||||||
|
|
||||||
|
# gem-testers support
|
||||||
|
task :test do
|
||||||
|
# rake-compiler always wants to copy the compiled extension into lib/, but
|
||||||
|
# we don't want testers to have to re-compile, especially since that
|
||||||
|
# often fails because they can't (and shouldn't have to) write to tmp/ in
|
||||||
|
# the installed gem dir. So we clear the task rake-compiler set up
|
||||||
|
# to break the dependency between :spec and :compile when running under
|
||||||
|
# rubygems-test, and then run :spec.
|
||||||
|
Rake::Task[ EXT.to_s ].clear
|
||||||
|
Rake::Task[ :spec ].execute
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Turn on warnings and debugging in the build."
|
||||||
|
task :maint do
|
||||||
|
ENV['MAINTAINER_MODE'] = 'yes'
|
||||||
|
end
|
||||||
|
|
||||||
|
ENV['RUBY_CC_VERSION'] ||= '1.8.7:1.9.2:2.0.0'
|
||||||
|
|
||||||
|
# Rake-compiler task
|
||||||
|
Rake::ExtensionTask.new do |ext|
|
||||||
|
ext.name = 'pg_ext'
|
||||||
|
ext.gem_spec = $hoespec.spec
|
||||||
|
ext.ext_dir = 'ext'
|
||||||
|
ext.lib_dir = 'lib'
|
||||||
|
ext.source_pattern = "*.{c,h}"
|
||||||
|
ext.cross_compile = true
|
||||||
|
ext.cross_platform = CrossLibraries.map &:for_platform
|
||||||
|
|
||||||
|
ext.cross_config_options += CrossLibraries.map do |lib|
|
||||||
|
{
|
||||||
|
lib.for_platform => [
|
||||||
|
"--enable-windows-cross",
|
||||||
|
"--with-pg-include=#{lib.static_postgresql_incdir}",
|
||||||
|
"--with-pg-lib=#{lib.static_postgresql_libdir}",
|
||||||
|
# libpq-fe.h resides in src/interfaces/libpq/ before make install
|
||||||
|
"--with-opt-include=#{lib.static_postgresql_libdir}",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add libpq.dll to windows binary gemspec
|
||||||
|
ext.cross_compiling do |spec|
|
||||||
|
# mingw32-platform strings differ (RUBY_PLATFORM=i386-mingw32 vs. x86-mingw32 for rubygems)
|
||||||
|
spec.files << "lib/#{spec.platform.to_s.gsub(/^x86-/, "i386-")}/libpq.dll"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Use the fivefish formatter for docs generated from development checkout
|
||||||
|
if File.directory?( '.hg' )
|
||||||
|
require 'rdoc/task'
|
||||||
|
|
||||||
|
Rake::Task[ 'docs' ].clear
|
||||||
|
RDoc::Task.new( 'docs' ) do |rdoc|
|
||||||
|
rdoc.main = "README.rdoc"
|
||||||
|
rdoc.rdoc_files.include( "*.rdoc", "ChangeLog", "lib/**/*.rb", 'ext/**/*.{c,h}' )
|
||||||
|
rdoc.generator = :fivefish
|
||||||
|
rdoc.title = "PG: The Ruby PostgreSQL Driver"
|
||||||
|
rdoc.rdoc_dir = 'doc'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Make the ChangeLog update if the repo has changed since it was last built
|
||||||
|
file '.hg/branch' do
|
||||||
|
warn "WARNING: You need the Mercurial repo to update the ChangeLog"
|
||||||
|
end
|
||||||
|
file 'ChangeLog' do |task|
|
||||||
|
if File.exist?('.hg/branch')
|
||||||
|
$stderr.puts "Updating the changelog..."
|
||||||
|
begin
|
||||||
|
include Hoe::MercurialHelpers
|
||||||
|
content = make_changelog()
|
||||||
|
rescue NameError
|
||||||
|
abort "Packaging tasks require the hoe-mercurial plugin (gem install hoe-mercurial)"
|
||||||
|
end
|
||||||
|
File.open( task.name, 'w', 0644 ) do |fh|
|
||||||
|
fh.print( content )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
touch 'ChangeLog'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Rebuild the ChangeLog immediately before release
|
||||||
|
task :prerelease => 'ChangeLog'
|
||||||
|
|
||||||
|
|
||||||
|
desc "Stop any Postmaster instances that remain after testing."
|
||||||
|
task :cleanup_testing_dbs do
|
||||||
|
require 'spec/lib/helpers'
|
||||||
|
PgTestingHelpers.stop_existing_postmasters()
|
||||||
|
Rake::Task[:clean].invoke
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Update list of server error codes"
|
||||||
|
task :update_error_codes do
|
||||||
|
URL_ERRORCODES_TXT = "http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;f=src/backend/utils/errcodes.txt;hb=HEAD"
|
||||||
|
|
||||||
|
ERRORCODES_TXT = "ext/errorcodes.txt"
|
||||||
|
sh "wget #{URL_ERRORCODES_TXT.inspect} -O #{ERRORCODES_TXT.inspect} || curl #{URL_ERRORCODES_TXT.inspect} -o #{ERRORCODES_TXT.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
file 'ext/errorcodes.def' => ['ext/errorcodes.rb', 'ext/errorcodes.txt'] do
|
||||||
|
ruby 'ext/errorcodes.rb', 'ext/errorcodes.txt', 'ext/errorcodes.def'
|
||||||
|
end
|
||||||
|
|
||||||
|
file 'ext/pg_errors.c' => ['ext/errorcodes.def'] do
|
||||||
|
# trigger compilation of changed errorcodes.def
|
||||||
|
touch 'ext/pg_errors.c'
|
||||||
|
end
|
||||||
|
|
||||||
|
task :gemspec => GEMSPEC
|
||||||
|
file GEMSPEC => __FILE__
|
||||||
|
task GEMSPEC do |task|
|
||||||
|
spec = $hoespec.spec
|
||||||
|
spec.files.delete( '.gemtest' )
|
||||||
|
spec.version = "#{spec.version}.pre#{Time.now.strftime("%Y%m%d%H%M%S")}"
|
||||||
|
File.open( task.name, 'w' ) do |fh|
|
||||||
|
fh.write( spec.to_ruby )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
CLOBBER.include( GEMSPEC.to_s )
|
||||||
|
task :default => :gemspec
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
# stub: dalli 2.7.5 ruby lib
|
||||||
|
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "dalli"
|
||||||
|
s.version = "2.7.5"
|
||||||
|
|
||||||
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||||
|
s.require_paths = ["lib"]
|
||||||
|
s.authors = ["Peter M. Goldstein", "Mike Perham"]
|
||||||
|
s.date = "2015-12-16"
|
||||||
|
s.description = "High performance memcached client for Ruby"
|
||||||
|
s.email = ["peter.m.goldstein@gmail.com", "mperham@gmail.com"]
|
||||||
|
s.homepage = "https://github.com/petergoldstein/dalli"
|
||||||
|
s.licenses = ["MIT"]
|
||||||
|
s.rdoc_options = ["--charset=UTF-8"]
|
||||||
|
s.rubygems_version = "2.5.0"
|
||||||
|
s.summary = "High performance memcached client for Ruby"
|
||||||
|
|
||||||
|
s.installed_by_version = "2.5.0" 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<minitest>, [">= 4.2.0"])
|
||||||
|
s.add_development_dependency(%q<mocha>, [">= 0"])
|
||||||
|
s.add_development_dependency(%q<rails>, ["~> 4"])
|
||||||
|
s.add_development_dependency(%q<rake>, [">= 0"])
|
||||||
|
s.add_development_dependency(%q<appraisal>, [">= 0"])
|
||||||
|
s.add_development_dependency(%q<connection_pool>, [">= 0"])
|
||||||
|
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
||||||
|
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
||||||
|
else
|
||||||
|
s.add_dependency(%q<minitest>, [">= 4.2.0"])
|
||||||
|
s.add_dependency(%q<mocha>, [">= 0"])
|
||||||
|
s.add_dependency(%q<rails>, ["~> 4"])
|
||||||
|
s.add_dependency(%q<rake>, [">= 0"])
|
||||||
|
s.add_dependency(%q<appraisal>, [">= 0"])
|
||||||
|
s.add_dependency(%q<connection_pool>, [">= 0"])
|
||||||
|
s.add_dependency(%q<rdoc>, [">= 0"])
|
||||||
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
s.add_dependency(%q<minitest>, [">= 4.2.0"])
|
||||||
|
s.add_dependency(%q<mocha>, [">= 0"])
|
||||||
|
s.add_dependency(%q<rails>, ["~> 4"])
|
||||||
|
s.add_dependency(%q<rake>, [">= 0"])
|
||||||
|
s.add_dependency(%q<appraisal>, [">= 0"])
|
||||||
|
s.add_dependency(%q<connection_pool>, [">= 0"])
|
||||||
|
s.add_dependency(%q<rdoc>, [">= 0"])
|
||||||
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user