diff --git a/dependency-check-cli/src/site/markdown/index.md.vm b/dependency-check-cli/src/site/markdown/index.md.vm
index 6e0cecaa9..671da940a 100644
--- a/dependency-check-cli/src/site/markdown/index.md.vm
+++ b/dependency-check-cli/src/site/markdown/index.md.vm
@@ -14,9 +14,16 @@ script executable:
$ chmod +777 dependency-check.sh
-To scan a folder on the system you can run:
#set( $H = '#' )
+$H$H$H Homebrew
+ $ brew install dependency-check
+
+This puts an executable `dependency-check` script in the `/bin` directory of
+your homebrew installation.
+
+To scan a folder on the system you can run:
+
$H$H$H Windows
dependency-check.bat --app "My App Name" --scan "c:\java\application\lib"
@@ -29,4 +36,4 @@ $H$H$H Windows
dependency-check.bat --help
$H$H$H *nix
- dependency-check.sh --help
\ No newline at end of file
+ dependency-check.sh --help
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java
index ab3b84ac1..f43e09240 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java
@@ -17,22 +17,6 @@
*/
package org.owasp.dependencycheck.analyzer;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
@@ -40,6 +24,8 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.compressors.CompressorInputStream;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.commons.compress.compressors.bzip2.BZip2Utils;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipUtils;
import org.owasp.dependencycheck.Engine;
@@ -52,6 +38,9 @@ import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.*;
+import java.util.*;
+
/**
*
* An analyzer that extracts files from archives and ensures any supported files contained within the archive are added to the
@@ -100,20 +89,21 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
*/
private static final Set ZIPPABLES = newHashSet("zip", "ear", "war", "jar", "sar", "apk", "nupkg");
/**
- * The set of file extensions supported by this analyzer. Note for developers, any additions to this list will need to be
- * explicitly handled in extractFiles().
+ * The set of file extensions supported by this analyzer. Note for developers, any additions to this list will need
+ * to be explicitly handled in {@link #extractFiles(File, File, Engine)}.
*/
- private static final Set EXTENSIONS = newHashSet("tar", "gz", "tgz");
+ private static final Set EXTENSIONS = newHashSet("tar", "gz", "tgz", "bz2", "tbz2");
/**
* Detects files with extensions to remove from the engine's collection of dependencies.
*/
- private static final FileFilter REMOVE_FROM_ANALYSIS = FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz").build();
+ private static final FileFilter REMOVE_FROM_ANALYSIS =
+ FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2").build();
static {
final String additionalZipExt = Settings.getString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS);
if (additionalZipExt != null) {
- final Set ext = new HashSet(Arrays.asList(additionalZipExt));
+ final Set ext = new HashSet(Collections.singletonList(additionalZipExt));
ZIPPABLES.addAll(ext);
}
EXTENSIONS.addAll(ZIPPABLES);
@@ -205,7 +195,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
* and added to the list of dependencies within the engine.
*
* @param dependency the dependency to analyze
- * @param engine the engine scanning
+ * @param engine the engine scanning
* @throws AnalysisException thrown if there is an analysis exception
*/
@Override
@@ -215,15 +205,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
extractFiles(f, tmpDir, engine);
//make a copy
- List dependencies = new ArrayList(engine.getDependencies());
- engine.scan(tmpDir);
- List newDependencies = engine.getDependencies();
- if (dependencies.size() != newDependencies.size()) {
- //get the new dependencies
- final Set dependencySet = new HashSet();
- dependencySet.addAll(newDependencies);
- dependencySet.removeAll(dependencies);
-
+ final Set dependencySet = findMoreDependencies(engine, tmpDir);
+ if (!dependencySet.isEmpty()) {
for (Dependency d : dependencySet) {
//fix the dependency's display name and path
final String displayPath = String.format("%s%s",
@@ -245,41 +228,66 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
}
}
if (REMOVE_FROM_ANALYSIS.accept(dependency.getActualFile())) {
- if (ZIP_FILTER.accept(dependency.getActualFile()) && isZipFileActuallyJarFile(dependency)) {
- final File tdir = getNextTempDirectory();
- final String fileName = dependency.getFileName();
-
- LOGGER.info(String.format("The zip file '%s' appears to be a JAR file, making a copy and analyzing it as a JAR.", fileName));
-
- final File tmpLoc = new File(tdir, fileName.substring(0, fileName.length() - 3) + "jar");
- try {
- org.apache.commons.io.FileUtils.copyFile(tdir, tmpLoc);
- dependencies = new ArrayList(engine.getDependencies());
- engine.scan(tmpLoc);
- newDependencies = engine.getDependencies();
- if (dependencies.size() != newDependencies.size()) {
- //get the new dependencies
- final Set dependencySet = new HashSet();
- dependencySet.addAll(newDependencies);
- dependencySet.removeAll(dependencies);
- if (dependencySet.size() != 1) {
- LOGGER.info("Deep copy of ZIP to JAR file resulted in more then one dependency?");
- }
- for (Dependency d : dependencySet) {
- //fix the dependency's display name and path
- d.setFilePath(dependency.getFilePath());
- d.setDisplayFileName(dependency.getFileName());
- }
- }
- } catch (IOException ex) {
- LOGGER.debug("Unable to perform deep copy on '{}'", dependency.getActualFile().getPath(), ex);
- }
- }
+ addDisguisedJarsToDependencies(dependency, engine);
engine.getDependencies().remove(dependency);
}
Collections.sort(engine.getDependencies());
}
+ private void addDisguisedJarsToDependencies(Dependency dependency, Engine engine) throws AnalysisException {
+ if (ZIP_FILTER.accept(dependency.getActualFile()) && isZipFileActuallyJarFile(dependency)) {
+ final File tdir = getNextTempDirectory();
+ final String fileName = dependency.getFileName();
+
+ LOGGER.info(String.format("The zip file '%s' appears to be a JAR file, making a copy and analyzing it as a JAR.", fileName));
+
+ final File tmpLoc = new File(tdir, fileName.substring(0, fileName.length() - 3) + "jar");
+ try {
+ org.apache.commons.io.FileUtils.copyFile(tdir, tmpLoc);
+ final Set dependencySet = findMoreDependencies(engine, tmpLoc);
+ if (!dependencySet.isEmpty()) {
+ if (dependencySet.size() != 1) {
+ LOGGER.info("Deep copy of ZIP to JAR file resulted in more than one dependency?");
+ }
+ for (Dependency d : dependencySet) {
+ //fix the dependency's display name and path
+ d.setFilePath(dependency.getFilePath());
+ d.setDisplayFileName(dependency.getFileName());
+ }
+ }
+ } catch (IOException ex) {
+ LOGGER.debug("Unable to perform deep copy on '{}'", dependency.getActualFile().getPath(), ex);
+ }
+ }
+ }
+
+ private static final Set EMPTY_DEPENDENCY_SET = Collections.emptySet();
+
+ /**
+ * Scan the given file/folder, and return any new dependencies found.
+ *
+ * @param engine used to scan
+ * @param file target of scanning
+ * @return any dependencies that weren't known to the engine before
+ */
+ private static Set findMoreDependencies(Engine engine, File file) {
+ List before = new ArrayList(engine.getDependencies());
+ engine.scan(file);
+ List after = engine.getDependencies();
+ final boolean sizeChanged = before.size() != after.size();
+ final Set newDependencies;
+ if (sizeChanged) {
+ //get the new dependencies
+ newDependencies = new HashSet();
+ newDependencies.addAll(after);
+ newDependencies.removeAll(before);
+ } else {
+ newDependencies = EMPTY_DEPENDENCY_SET;
+ }
+ return newDependencies;
+ }
+
+
/**
* Retrieves the next temporary directory to extract an archive too.
*
@@ -303,47 +311,47 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
/**
* Extracts the contents of an archive into the specified directory.
*
- * @param archive an archive file such as a WAR or EAR
+ * @param archive an archive file such as a WAR or EAR
* @param destination a directory to extract the contents to
- * @param engine the scanning engine
+ * @param engine the scanning engine
* @throws AnalysisException thrown if the archive is not found
*/
private void extractFiles(File archive, File destination, Engine engine) throws AnalysisException {
- if (archive == null || destination == null) {
- return;
- }
-
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(archive);
- } catch (FileNotFoundException ex) {
- LOGGER.debug("", ex);
- throw new AnalysisException("Archive file was not found.", ex);
- }
- final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase();
- try {
- if (ZIPPABLES.contains(archiveExt)) {
- extractArchive(new ZipArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
- } else if ("tar".equals(archiveExt)) {
- extractArchive(new TarArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
- } else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) {
- final String uncompressedName = GzipUtils.getUncompressedFilename(archive.getName());
- final File f = new File(destination, uncompressedName);
- if (engine.accept(f)) {
- decompressFile(new GzipCompressorInputStream(new BufferedInputStream(fis)), f);
- }
- }
- } catch (ArchiveExtractionException ex) {
- LOGGER.warn("Exception extracting archive '{}'.", archive.getName());
- LOGGER.debug("", ex);
- } catch (IOException ex) {
- LOGGER.warn("Exception reading archive '{}'.", archive.getName());
- LOGGER.debug("", ex);
- } finally {
+ if (archive != null && destination != null) {
+ FileInputStream fis;
try {
- fis.close();
- } catch (IOException ex) {
+ fis = new FileInputStream(archive);
+ } catch (FileNotFoundException ex) {
LOGGER.debug("", ex);
+ throw new AnalysisException("Archive file was not found.", ex);
+ }
+ final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase();
+ try {
+ if (ZIPPABLES.contains(archiveExt)) {
+ extractArchive(new ZipArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
+ } else if ("tar".equals(archiveExt)) {
+ extractArchive(new TarArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
+ } else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) {
+ final String uncompressedName = GzipUtils.getUncompressedFilename(archive.getName());
+ final File f = new File(destination, uncompressedName);
+ if (engine.accept(f)) {
+ decompressFile(new GzipCompressorInputStream(new BufferedInputStream(fis)), f);
+ }
+ } else if ("bz2".equals(archiveExt) || "tbz2".equals(archiveExt)) {
+ final String uncompressedName = BZip2Utils.getUncompressedFilename(archive.getName());
+ final File f = new File(destination, uncompressedName);
+ if (engine.accept(f)) {
+ decompressFile(new BZip2CompressorInputStream(new BufferedInputStream(fis)), f);
+ }
+ }
+ } catch (ArchiveExtractionException ex) {
+ LOGGER.warn("Exception extracting archive '{}'.", archive.getName());
+ LOGGER.debug("", ex);
+ } catch (IOException ex) {
+ LOGGER.warn("Exception reading archive '{}'.", archive.getName());
+ LOGGER.debug("", ex);
+ } finally {
+ close(fis);
}
}
}
@@ -351,84 +359,63 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
/**
* Extracts files from an archive.
*
- * @param input the archive to extract files from
+ * @param input the archive to extract files from
* @param destination the location to write the files too
- * @param engine the dependency-check engine
+ * @param engine the dependency-check engine
* @throws ArchiveExtractionException thrown if there is an exception extracting files from the archive
*/
private void extractArchive(ArchiveInputStream input, File destination, Engine engine) throws ArchiveExtractionException {
ArchiveEntry entry;
try {
while ((entry = input.getNextEntry()) != null) {
+ final File file = new File(destination, entry.getName());
if (entry.isDirectory()) {
- final File d = new File(destination, entry.getName());
- if (!d.exists()) {
- if (!d.mkdirs()) {
- final String msg = String.format("Unable to create directory '%s'.", d.getAbsolutePath());
- throw new AnalysisException(msg);
- }
- }
- } else {
- final File file = new File(destination, entry.getName());
- if (engine.accept(file)) {
- LOGGER.debug("Extracting '{}'", file.getPath());
- BufferedOutputStream bos = null;
- FileOutputStream fos = null;
- try {
- final File parent = file.getParentFile();
- if (!parent.isDirectory()) {
- if (!parent.mkdirs()) {
- final String msg = String.format("Unable to build directory '%s'.", parent.getAbsolutePath());
- throw new AnalysisException(msg);
- }
- }
- fos = new FileOutputStream(file);
- bos = new BufferedOutputStream(fos, BUFFER_SIZE);
- int count;
- final byte[] data = new byte[BUFFER_SIZE];
- while ((count = input.read(data, 0, BUFFER_SIZE)) != -1) {
- bos.write(data, 0, count);
- }
- bos.flush();
- } catch (FileNotFoundException ex) {
- LOGGER.debug("", ex);
- final String msg = String.format("Unable to find file '%s'.", file.getName());
- throw new AnalysisException(msg, ex);
- } catch (IOException ex) {
- LOGGER.debug("", ex);
- final String msg = String.format("IO Exception while parsing file '%s'.", file.getName());
- throw new AnalysisException(msg, ex);
- } finally {
- if (bos != null) {
- try {
- bos.close();
- } catch (IOException ex) {
- LOGGER.trace("", ex);
- }
- }
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException ex) {
- LOGGER.trace("", ex);
- }
- }
- }
+ if (!file.exists() && !file.mkdirs()) {
+ final String msg = String.format("Unable to create directory '%s'.", file.getAbsolutePath());
+ throw new AnalysisException(msg);
}
+ } else if (engine.accept(file)) {
+ extractAcceptedFile(input, file);
}
}
- } catch (IOException ex) {
- throw new ArchiveExtractionException(ex);
} catch (Throwable ex) {
throw new ArchiveExtractionException(ex);
} finally {
- if (input != null) {
- try {
- input.close();
- } catch (IOException ex) {
- LOGGER.trace("", ex);
+ close(input);
+ }
+ }
+
+ private static void extractAcceptedFile(ArchiveInputStream input, File file) throws AnalysisException {
+ LOGGER.debug("Extracting '{}'", file.getPath());
+ BufferedOutputStream bos = null;
+ FileOutputStream fos = null;
+ try {
+ final File parent = file.getParentFile();
+ if (!parent.isDirectory()) {
+ if (!parent.mkdirs()) {
+ final String msg = String.format("Unable to build directory '%s'.", parent.getAbsolutePath());
+ throw new AnalysisException(msg);
}
}
+ fos = new FileOutputStream(file);
+ bos = new BufferedOutputStream(fos, BUFFER_SIZE);
+ int count;
+ final byte[] data = new byte[BUFFER_SIZE];
+ while ((count = input.read(data, 0, BUFFER_SIZE)) != -1) {
+ bos.write(data, 0, count);
+ }
+ bos.flush();
+ } catch (FileNotFoundException ex) {
+ LOGGER.debug("", ex);
+ final String msg = String.format("Unable to find file '%s'.", file.getName());
+ throw new AnalysisException(msg, ex);
+ } catch (IOException ex) {
+ LOGGER.debug("", ex);
+ final String msg = String.format("IO Exception while parsing file '%s'.", file.getName());
+ throw new AnalysisException(msg, ex);
+ } finally {
+ close(bos);
+ close(fos);
}
}
@@ -436,7 +423,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
* Decompresses a file.
*
* @param inputStream the compressed file
- * @param outputFile the location to write the decompressed file
+ * @param outputFile the location to write the decompressed file
* @throws ArchiveExtractionException thrown if there is an exception decompressing the file
*/
private void decompressFile(CompressorInputStream inputStream, File outputFile) throws ArchiveExtractionException {
@@ -445,7 +432,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
try {
out = new FileOutputStream(outputFile);
final byte[] buffer = new byte[BUFFER_SIZE];
- int n = 0;
+ int n; // = 0
while (-1 != (n = inputStream.read(buffer))) {
out.write(buffer, 0, n);
}
@@ -456,12 +443,21 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
LOGGER.debug("", ex);
throw new ArchiveExtractionException(ex);
} finally {
- if (out != null) {
- try {
- out.close();
- } catch (IOException ex) {
- LOGGER.trace("", ex);
- }
+ close(out);
+ }
+ }
+
+ /**
+ * Close the given {@link Closeable} instance, ignoring nulls, and logging any thrown {@link IOException}.
+ *
+ * @param closeable to be closed
+ */
+ private static void close(Closeable closeable){
+ if (null != closeable) {
+ try {
+ closeable.close();
+ } catch (IOException ex) {
+ LOGGER.trace("", ex);
}
}
}
diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIntegrationTest.java
index abd94ee62..7a5bf74bf 100644
--- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIntegrationTest.java
+++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIntegrationTest.java
@@ -51,6 +51,8 @@ public class ArchiveAnalyzerIntegrationTest extends AbstractDatabaseTestCase {
expResult.add("tar");
expResult.add("gz");
expResult.add("tgz");
+ expResult.add("bz2");
+ expResult.add("tbz2");
for (String ext : expResult) {
assertTrue(ext, instance.accept(new File("test." + ext)));
}
@@ -197,28 +199,31 @@ public class ArchiveAnalyzerIntegrationTest extends AbstractDatabaseTestCase {
}
}
-// /**
-// * Test of analyze method, of class ArchiveAnalyzer.
-// */
-// @Test
-// public void testNestedZipFolder() throws Exception {
-// ArchiveAnalyzer instance = new ArchiveAnalyzer();
-// try {
-// instance.initialize();
-//
-// File file = new File(this.getClass().getClassLoader().getResource("nested.zip").getPath());
-// Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
-// Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false);
-// Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false);
-// Engine engine = new Engine();
-//
-// engine.scan(file);
-// engine.analyzeDependencies();
-//
-// } finally {
-// instance.close();
-// }
-// }
+ /**
+ * Test of analyze method, of class ArchiveAnalyzer.
+ */
+ @Test
+ public void testAnalyzeTarBz2() throws Exception {
+ ArchiveAnalyzer instance = new ArchiveAnalyzer();
+ instance.accept(new File("zip")); //ensure analyzer is "enabled"
+ try {
+ instance.initialize();
+ File file = BaseTest.getResourceAsFile(this, "file.tar.bz2");
+ Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
+ Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false);
+ Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false);
+ Engine engine = new Engine();
+ int initial_size = engine.getDependencies().size();
+ engine.scan(file);
+ engine.analyzeDependencies();
+ int ending_size = engine.getDependencies().size();
+ engine.cleanup();
+ assertTrue(initial_size < ending_size);
+ } finally {
+ instance.close();
+ }
+ }
+
/**
* Test of analyze method, of class ArchiveAnalyzer.
*/
@@ -248,6 +253,31 @@ public class ArchiveAnalyzerIntegrationTest extends AbstractDatabaseTestCase {
}
}
+ /**
+ * Test of analyze method, of class ArchiveAnalyzer.
+ */
+ @Test
+ public void testAnalyzeTbz2() throws Exception {
+ ArchiveAnalyzer instance = new ArchiveAnalyzer();
+ instance.accept(new File("zip")); //ensure analyzer is "enabled"
+ try {
+ instance.initialize();
+ File file = BaseTest.getResourceAsFile(this, "file.tbz2");
+ Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
+ Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false);
+ Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false);
+ Engine engine = new Engine();
+ int initial_size = engine.getDependencies().size();
+ engine.scan(file);
+ engine.analyzeDependencies();
+ int ending_size = engine.getDependencies().size();
+ engine.cleanup();
+ assertTrue(initial_size < ending_size);
+ } finally {
+ instance.close();
+ }
+ }
+
/**
* Test of analyze method, of class ArchiveAnalyzer.
*/
diff --git a/dependency-check-core/src/test/resources/file.tar.bz2 b/dependency-check-core/src/test/resources/file.tar.bz2
new file mode 100644
index 000000000..7f3ab7f3b
Binary files /dev/null and b/dependency-check-core/src/test/resources/file.tar.bz2 differ
diff --git a/dependency-check-core/src/test/resources/file.tbz2 b/dependency-check-core/src/test/resources/file.tbz2
new file mode 100644
index 000000000..7f3ab7f3b
Binary files /dev/null and b/dependency-check-core/src/test/resources/file.tbz2 differ
diff --git a/dependency-check-gradle/.gitignore b/dependency-check-gradle/.gitignore
index 2a132566c..487d7533f 100644
--- a/dependency-check-gradle/.gitignore
+++ b/dependency-check-gradle/.gitignore
@@ -1,5 +1,6 @@
.idea/
.gradle
+gradle/
*.iml
*.ipr
diff --git a/dependency-check-gradle/README.md b/dependency-check-gradle/README.md
index ead2a50af..7707db3ec 100644
--- a/dependency-check-gradle/README.md
+++ b/dependency-check-gradle/README.md
@@ -7,6 +7,8 @@ This is a DependencyCheck gradle plugin designed for project which use Gradle as
Dependency-Check is a utility that attempts to detect publicly disclosed vulnerabilities contained within project dependencies. It does this by determining if there is a Common Platform Enumeration (CPE) identifier for a given dependency. If found, it will generate a report linking to the associated CVE entries.
+Current latest version is `0.0.6`
+
=========
## Usage
@@ -15,7 +17,7 @@ Dependency-Check is a utility that attempts to detect publicly disclosed vulnera
Please refer to either one of the solution
-#### Solution 1,Install from Maven Central
+#### Solution 1,Install from Maven Central (Recommended)
```groovy
buildscript {
@@ -23,7 +25,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.thoughtworks.tools:dependency-check:0.0.5'
+ classpath 'com.thoughtworks.tools:dependency-check:0.0.6'
}
}
```
@@ -38,7 +40,7 @@ apply plugin: 'dependency.check'
```groovy
plugins {
- id "dependency.check" version "0.0.5"
+ id "dependency.check" version "0.0.6"
}
```
@@ -52,11 +54,11 @@ buildscript {
}
}
dependencies {
- classpath "gradle.plugin.com.tools.security:dependency-check:0.0.5"
+ classpath "gradle.plugin.com.tools.security:dependency-check:0.0.6"
}
}
-apply plugin: "dependency.check"
+apply plugin: "dependency-check"
```
#### Solution 3,Install from Bintray
@@ -73,7 +75,7 @@ buildscript {
}
dependencies {
classpath(
- 'com.tools.security:dependency-check:0.0.5'
+ 'com.tools.security:dependency-check:0.0.6'
)
}
}
@@ -115,6 +117,19 @@ dependencyCheck {
}
```
+In addition, if the proxy only allow HTTP `GET` or `POST` methods, you will find that the update process will always fail,
+ the root cause is that every time you run `dependencyCheck` task, it will try to query the latest timestamp to determine whether need to perform an update action,
+ and for performance reason the HTTP method it uses by default is `HEAD`, which probably is disabled or not supported by the proxy. To avoid this problem, you can simply change the HTTP method by below configuration:
+
+```groovy
+dependencyCheck {
+ proxyServer = "127.0.0.1" // required, the server name or IP address of the proxy
+ proxyPort = 3128 // required, the port number of the proxy
+
+ quickQueryTimestamp = false // when set to false, it means use HTTP GET method to query timestamp. (default value is true)
+}
+```
+
### What if my project includes multiple sub-project? How can I use this plugin for each of them including the root project?
Try put 'apply plugin: "dependency-check"' inside the 'allprojects' or 'subprojects' if you'd like to check all sub-projects only, see below:
@@ -127,7 +142,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath "gradle.plugin.com.tools.security:dependency-check:0.0.5"
+ classpath "gradle.plugin.com.tools.security:dependency-check:0.0.6"
}
}
@@ -144,7 +159,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath "gradle.plugin.com.tools.security:dependency-check:0.0.5"
+ classpath "gradle.plugin.com.tools.security:dependency-check:0.0.6"
}
}
diff --git a/dependency-check-gradle/build.gradle b/dependency-check-gradle/build.gradle
index 2c0d302a9..18f7454a7 100644
--- a/dependency-check-gradle/build.gradle
+++ b/dependency-check-gradle/build.gradle
@@ -46,23 +46,15 @@ dependencies {
compile(
localGroovy(),
gradleApi(),
- 'org.owasp:dependency-check-core:1.2.11',
- 'org.owasp:dependency-check-utils:1.2.11'
+ 'org.owasp:dependency-check-core:1.3.0',
+ 'org.owasp:dependency-check-utils:1.3.0'
)
- testCompile ('com.netflix.nebula:nebula-test:2.2.+'){
+ testCompile ('com.netflix.nebula:nebula-test:2.2.2'){
exclude group: 'org.codehaus.groovy'
}
}
-group = 'com.thoughtworks.tools'
-version = '0.0.5'
-
-apply from: 'conf/publish/local.gradle'
-//apply from: 'conf/publish/maven.gradle'
-apply from: 'conf/publish/gradlePluginsPortal.gradle'
-//apply from: 'conf/publish/bintray.gradle' // according to the documentation of plugindev, this line has to be placed and the very end of the build file
-
sourceSets {
integTest {
groovy.srcDir file('src/integTest/groovy')
@@ -78,4 +70,12 @@ task integTest(type: Test) {
classpath = sourceSets.integTest.runtimeClasspath
reports.html.destination = file("$buildDir/reports/integ")
jvmArgs '-XX:MaxPermSize=256m'
-}
\ No newline at end of file
+}
+
+group = 'com.thoughtworks.tools'
+version = '0.0.6'
+
+apply from: 'conf/publish/local.gradle'
+//apply from: 'conf/publish/maven.gradle'
+apply from: 'conf/publish/gradlePluginsPortal.gradle'
+//apply from: 'conf/publish/bintray.gradle' // according to the documentation of plugindev, this line has to be placed and the very end of the build file
\ No newline at end of file
diff --git a/dependency-check-gradle/conf/publish/maven.gradle b/dependency-check-gradle/conf/publish/maven.gradle
index 5f9787387..462ced0f7 100644
--- a/dependency-check-gradle/conf/publish/maven.gradle
+++ b/dependency-check-gradle/conf/publish/maven.gradle
@@ -66,6 +66,11 @@ task javadocJar(type: Jar) {
from javadoc
}
+task sourcesJar(type: Jar, dependsOn: classes) {
+ classifier = 'sources'
+ from sourceSets.main.allSource
+}
+
artifacts {
archives javadocJar, sourcesJar
}
diff --git a/dependency-check-gradle/src/main/groovy/com/tools/security/extension/DependencyCheckConfigurationExtension.groovy b/dependency-check-gradle/src/main/groovy/com/tools/security/extension/DependencyCheckConfigurationExtension.groovy
index 8e7a29ce3..e86f66e25 100644
--- a/dependency-check-gradle/src/main/groovy/com/tools/security/extension/DependencyCheckConfigurationExtension.groovy
+++ b/dependency-check-gradle/src/main/groovy/com/tools/security/extension/DependencyCheckConfigurationExtension.groovy
@@ -31,4 +31,6 @@ class DependencyCheckConfigurationExtension {
String cveUrl20Base = "https://nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml.gz"
String outputDirectory = "./reports"
+
+ Boolean quickQueryTimestamp = true;
}
diff --git a/dependency-check-gradle/src/main/groovy/com/tools/security/plugin/DependencyCheckGradlePlugin.groovy b/dependency-check-gradle/src/main/groovy/com/tools/security/plugin/DependencyCheckGradlePlugin.groovy
index 937226401..2274c9af4 100644
--- a/dependency-check-gradle/src/main/groovy/com/tools/security/plugin/DependencyCheckGradlePlugin.groovy
+++ b/dependency-check-gradle/src/main/groovy/com/tools/security/plugin/DependencyCheckGradlePlugin.groovy
@@ -49,6 +49,7 @@ class DependencyCheckGradlePlugin implements Plugin {
conventionMapping.cveUrl12Base = { extension.cveUrl12Base }
conventionMapping.cveUrl20Base = { extension.cveUrl20Base }
conventionMapping.outputDirectory = { extension.outputDirectory }
+ conventionMapping.quickQueryTimestamp = { extension.quickQueryTimestamp }
}
}
}
\ No newline at end of file
diff --git a/dependency-check-gradle/src/main/groovy/com/tools/security/tasks/DependencyCheckTask.groovy b/dependency-check-gradle/src/main/groovy/com/tools/security/tasks/DependencyCheckTask.groovy
index c76f1ab92..3e371ec81 100644
--- a/dependency-check-gradle/src/main/groovy/com/tools/security/tasks/DependencyCheckTask.groovy
+++ b/dependency-check-gradle/src/main/groovy/com/tools/security/tasks/DependencyCheckTask.groovy
@@ -28,6 +28,7 @@ import org.owasp.dependencycheck.dependency.Dependency
import org.owasp.dependencycheck.reporting.ReportGenerator
import org.owasp.dependencycheck.utils.Settings
+import static org.owasp.dependencycheck.utils.Settings.setBoolean
import static org.owasp.dependencycheck.utils.Settings.setString
class DependencyCheckTask extends DefaultTask {
@@ -47,6 +48,8 @@ class DependencyCheckTask extends DefaultTask {
String outputDirectory = "./reports"
+ Boolean quickQueryTimestamp = true;
+
DependencyCheckTask() {
group = 'Dependency Check'
description = 'Produce dependency security report.'
@@ -73,6 +76,7 @@ class DependencyCheckTask extends DefaultTask {
Settings.initialize()
overrideProxySetting()
overrideCveUrlSetting()
+ overrideDownloaderSetting()
}
def cleanup(engine) {
@@ -140,4 +144,8 @@ class DependencyCheckTask extends DefaultTask {
setString(Settings.KEYS.CVE_SCHEMA_2_0, getCveUrl20Base())
setString(Settings.KEYS.CVE_SCHEMA_1_2, getCveUrl12Base())
}
+
+ def overrideDownloaderSetting() {
+ setBoolean(Settings.KEYS.DOWNLOADER_QUICK_QUERY_TIMESTAMP, getQuickQueryTimestamp())
+ }
}
diff --git a/dependency-check-gradle/src/main/resources/META-INF/gradle-plugins/dependency.check.properties b/dependency-check-gradle/src/main/resources/META-INF/gradle-plugins/dependency.check.properties
new file mode 100644
index 000000000..877c70050
--- /dev/null
+++ b/dependency-check-gradle/src/main/resources/META-INF/gradle-plugins/dependency.check.properties
@@ -0,0 +1,19 @@
+#
+# This file is part of dependency-check-gradle.
+#
+# 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 Wei Ma. All Rights Reserved.
+#
+
+implementation-class=com.tools.security.plugin.DependencyCheckGradlePlugin
\ No newline at end of file
diff --git a/dependency-check-gradle/src/test/groovy/com/tools/security/plugin/DependencyCheckGradlePluginSpec.groovy b/dependency-check-gradle/src/test/groovy/com/tools/security/plugin/DependencyCheckGradlePluginSpec.groovy
index 892285197..6a9666240 100644
--- a/dependency-check-gradle/src/test/groovy/com/tools/security/plugin/DependencyCheckGradlePluginSpec.groovy
+++ b/dependency-check-gradle/src/test/groovy/com/tools/security/plugin/DependencyCheckGradlePluginSpec.groovy
@@ -58,6 +58,7 @@ class DependencyCheckGradlePluginSpec extends PluginProjectSpec {
task.cveUrl12Base == 'https://nvd.nist.gov/download/nvdcve-%d.xml.gz'
task.cveUrl20Base == 'https://nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml.gz'
task.outputDirectory == './reports'
+ task.quickQueryTimestamp == true
}
def 'tasks use correct values when extension is used'() {
@@ -73,6 +74,7 @@ class DependencyCheckGradlePluginSpec extends PluginProjectSpec {
cveUrl12Base = 'cveUrl12Base'
cveUrl20Base = 'cveUrl20Base'
outputDirectory = 'outputDirectory'
+ quickQueryTimestamp = false
}
then:
@@ -87,5 +89,6 @@ class DependencyCheckGradlePluginSpec extends PluginProjectSpec {
task.cveUrl12Base == 'cveUrl12Base'
task.cveUrl20Base == 'cveUrl20Base'
task.outputDirectory == 'outputDirectory'
+ task.quickQueryTimestamp == false
}
}
diff --git a/src/site/markdown/data/cachenvd.md b/src/site/markdown/data/cachenvd.md
new file mode 100644
index 000000000..9246cc9f3
--- /dev/null
+++ b/src/site/markdown/data/cachenvd.md
@@ -0,0 +1,93 @@
+Snapshotting the NVD
+====================
+
+The [Mirroring the NVD from NIST](./mirrornvd.html) topic describes briefly
+how to use the [Nist-Data-Mirror](https://github.com/stevespringett/nist-data-mirror/)
+project to cache the NVD locally and run Dependency Check (D-C) against the
+local cache.
+
+This topic goes into a bit more depth with the [cli](../dependency-check-cli/index.html)
+client, focusing on the following use case.
+
+1. You wish to have daily local snapshots of the NVD, so that
+2. in order to compare later runs of D-C with earlier runs, you can compare
+ "apples with apples".
+
+In other words: It is sometimes desirable to run a comparison D-C analysis
+against the same NVD snapshot that an earlier D-C report used.
+
+In the steps below, concrete examples will be given assuming an Ubuntu Linux
+system. Hopefully, enough explanation is provided that the steps can easily be
+translated to other systems.
+
+Build Nist-Data-Mirror
+----------------------
+
+1. Perform a "git clone" of [Nist-Data-Mirror](https://github.com/stevespringett/nist-data-mirror/)
+2. Install gradle, if necessary. See [here](http://gradle.org/gradle-download/)
+ or your Linux distributions package management system. (e.g.,
+ `sudo apt-get install gradle`).
+3. Follow the [build instructions](https://github.com/stevespringett/nist-data-mirror/blob/master/README.md#user-content-build).
+ You will be left with a build artifact called `nist-data-mirror-1.0.0.jar`.
+
+Set Up a Daily NVD Download Job
+-------------------------------
+
+On Linux, the way to do this using the [cron daemon](http://linux.die.net/man/8/cron).
+"Cron jobs" are configured by invoking [crontab](http://linux.die.net/man/5/crontab).
+For example, invoke `crontab -e` to add a line like the following to your
+crontab file:
+
+ 4 5 * * * ~/.local/bin/nvd_download.sh ~/NVD ~/.local/jars
+
+This would run a job on your system at 4:05 AM daily to run the
+[nvd_download.sh](general/nvd_download.sh) shell script with the two given
+arguments. The script is simple:
+
+```sh
+#!/bin/sh
+NVD_ROOT=$1/`date -I`
+JAR_PATH=$2/nist-data-mirror-1.0.0.jar
+java -jar $JAR_PATH $NVD_ROOT
+rm $NVD_ROOT/*.xml # D-C works directly with .gz files anyway.
+```
+
+Nist-Data-Mirror will automatically create the directory, download the
+.xml.gz files, and extract the .xml files alongside them. Given the parameters
+in the cron example above, the new directory will be `~/NVD/2015-08-03` if
+executed on August 3rd, 2015. The download for 2015-08-03 pulled 47
+MiB, and took up a total of 668 MiB after extracting from the compressed
+archive format. It turns out that D-C works directly with the .xml.gz files,
+so the above script preserves disk space by deleting the .xml files.
+
+Invoke the Command-Line Using a Specific Daily Snapshot
+-------------------------------------------------------
+
+An example script named [dep-check-date.sh](general/dep-check-date.sh) is
+shown below, which facilitates a D-C scan against an arbitrary NVD snapshot:
+
+```sh
+#!/bin/sh
+CLI_LOCATION=~/.local/dependency-check-1.2.11
+CLI_SCRIPT=$CLI_LOCATION/bin/dependency-check.sh
+NVD_PATH=$1/`date -I -d $2`
+NVD=file://$NVD_PATH
+shift 2 # We've used the first two params. The rest go to CLI_SCRIPT.
+$CLI_SCRIPT --cveUrl20Base $NVD/nvdcve-2.0-%d.xml.gz \
+ --cveUrl12Base $NVD/nvdcve-%d.xml.gz \
+ --cveUrl20Modified $NVD/nvdcve-2.0-Modified.xml.gz \
+ --cveUrl12Modified $NVD/nvdcve-Modified.xml.gz \
+ --data $NVD_PATH $@
+```
+
+The script takes advantage of the `date` command's ability to parse a variety
+of date formats. The following invokation would successfully point to the
+`~/NVD/2015-08-03` folder.
+
+ $ ./dep-check-date.sh ~/NVD "08/03/2015" -app Foo -scan /path/to/Foo --out ~/DCreports/FooFollowup/
+
+If today happened to be August 4th, 2015, `"yesterday"` also would have
+worked. Also notice the usage of the `--data` parameter. This places the H2
+database file directly in the folder alongside the .xml.gz files. This is
+critical, so that D-C doesn't run against another version of the database,
+like the usual default in `$CLI_LOCATION/data`.
\ No newline at end of file
diff --git a/src/site/resources/general/dep-check-date.sh b/src/site/resources/general/dep-check-date.sh
new file mode 100755
index 000000000..21130bf8d
--- /dev/null
+++ b/src/site/resources/general/dep-check-date.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+CLI_LOCATION=~/.local/dependency-check-1.2.11
+CLI_SCRIPT=$CLI_LOCATION/bin/dependency-check.sh
+NVD_PATH=$1/`date -I -d $2`
+NVD=file://$NVD_PATH
+shift 2 # We've used the first two params. The rest go to CLI_SCRIPT.
+$CLI_SCRIPT --cveUrl20Base $NVD/nvdcve-2.0-%d.xml.gz \
+ --cveUrl12Base $NVD/nvdcve-%d.xml.gz \
+ --cveUrl20Modified $NVD/nvdcve-2.0-Modified.xml.gz \
+ --cveUrl12Modified $NVD/nvdcve-Modified.xml.gz \
+ --data $NVD_PATH $@
\ No newline at end of file
diff --git a/src/site/resources/general/nvd_download.sh b/src/site/resources/general/nvd_download.sh
new file mode 100755
index 000000000..5af32b5b9
--- /dev/null
+++ b/src/site/resources/general/nvd_download.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+NVD_ROOT=$1/`date -I`
+JAR_PATH=$2/nist-data-mirror-1.0.0.jar
+java -jar $JAR_PATH $NVD_ROOT
+rm $NVD_ROOT/*.xml # D-C works directly with .gz files anyway.
\ No newline at end of file
diff --git a/src/site/site.xml b/src/site/site.xml
index 9f3df078a..6ca795342 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -87,6 +87,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
-
+
-