initial release

This commit is contained in:
Jeremy Long
2012-09-08 01:26:38 -04:00
parent d5caab764a
commit ed600f1759
20 changed files with 474 additions and 428 deletions

12
README
View File

@@ -1,14 +1,18 @@
About:
DependencyCheck is a simple utility that attempts to determine if there is a Common Product Enumeration (CPE) identifier for a given project dependency. If found, it will generate a report linking to the associated CVE entries.
DependencyCheck is a simple utility that attempts to determine if there is a
Common Product Enumeration (CPE) identifier for a given project dependency.
If found, it will generate a report linking to the associated CVE entries.
Usage:
Still under development: mvn package site
java -jar dependencycheck-0.1.jar -h
TODO:
Add CVE download/indexing and CPE lookup.
Finish report generation.
Fix/finish the CLI.
Finish report generation (xml).
Consider utilizing the OSVDB in addition to CPE/CVE.
Author: Jeremy Long (jeremy.long@gmail.com)
Copyright (c) 2012 Jeremy Long. All Rights Reserved.
Copyright (c) 2012 Jeremy Long. All Rights Reserved.

56
pom.xml
View File

@@ -16,13 +16,14 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DependencyCheck. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codesecure</groupId>
<artifactId>DependencyCheck</artifactId>
<version>1.0-SNAPSHOT</version>
<version>0.1</version>
<packaging>jar</packaging>
<name>DependencyCheck</name>
@@ -34,6 +35,11 @@ along with DependencyCheck. If not, see <http://www.gnu.org/licenses/>.
<email>jeremy.long@gmail.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git@github.com:jeremylong/DependencyCheck.git</connection>
<url>scm:git:git@github.com:jeremylong/DependencyCheck.git</url>
<developerConnection>scm:git:git@github.com:jeremylong/DependencyCheck.git</developerConnection>
</scm>
<licenses>
<license>
<name>GNU General Public License version 3</name>
@@ -44,6 +50,25 @@ along with DependencyCheck. If not, see <http://www.gnu.org/licenses/>.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.properties</exclude>
<exclude>**/*.gif</exclude>
<exclude>**/*.js</exclude>
</excludes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -54,7 +79,9 @@ along with DependencyCheck. If not, see <http://www.gnu.org/licenses/>.
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<phase>package</phase>
@@ -153,15 +180,15 @@ along with DependencyCheck. If not, see <http://www.gnu.org/licenses/>.
<workingDirectory>target</workingDirectory>
</property>
<property>
<name>index.cpe</name>
<value>${project.build.directory}/store/cpe</value>
<name>cve</name>
<value>${project.build.directory}/store/cve</value>
</property>
<property>
<name>index.osvdb</name>
<name>osvdb</name>
<value>${project.build.directory}/store/osvdb</value>
</property>
<property>
<name>index.cpe</name>
<name>cpe</name>
<value>${project.build.directory}/store/cpe</value>
</property>
</systemProperties>
@@ -392,5 +419,24 @@ along with DependencyCheck. If not, see <http://www.gnu.org/licenses/>.
<classifier>javadoc</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.1</version>
<classifier>javadoc</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.1</version>
<classifier>sources</classifier>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,14 +1,32 @@
package org.codesecure.dependencycheck;
/*
* This file is part of DependencyCheck.
*
* DependencyCheck is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DependencyCheck is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.cli.ParseException;
import org.codesecure.dependencycheck.data.cpe.CPEQuery;
import org.codesecure.dependencycheck.data.cpe.Index;
import org.codesecure.dependencycheck.data.cpe.xml.Importer;
import org.codesecure.dependencycheck.reporting.ReportGenerator;
import org.codesecure.dependencycheck.scanner.Dependency;
@@ -34,9 +52,8 @@ import org.xml.sax.SAXException;
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
/**
*
* The command line interface for the DependencyCheck application.
*
* @author Jeremy Long (jeremy.long@gmail.com)
*/
@@ -50,7 +67,12 @@ public class App {
App app = new App();
app.run(args);
}
/**
* main CLI entry-point into the application.
*
* @param args the command line arguments
*/
public void run(String[] args) {
CliParser cli = new CliParser();
try {
@@ -62,18 +84,32 @@ public class App {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
return;
}
if (cli.isGetVersion()) {
cli.printVersionInfo();
} else if (cli.isLoadCPE()) {
loadCPE(cli.getCpeFile());
} else if (cli.isRunScan()) {
if (cli.isAutoUpdate()) {
Index cpeI = new Index();
try {
cpeI.updateIndexFromWeb();
} catch (Exception ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
runScan(cli.getReportDirectory(), cli.getApplicationName(), cli.getScanFiles());
} else {
cli.printHelp();
}
}
/**
* Loads the specified CPE.XML file into Lucene Index.
* @param cpePath
*/
private void loadCPE(String cpePath) {
try {
Importer.importXML(cpePath);
@@ -85,6 +121,13 @@ public class App {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Scans the specified directories and writes the dependency reports to the reportDirectory.
* @param reportDirectory the path to the directory where the reports will be written.
* @param applicationName the application name for the report.
* @param files the files/directories to scan.
*/
private void runScan(String reportDirectory, String applicationName, String[] files) {
try {
Scanner scanner = new Scanner();
@@ -106,13 +149,11 @@ public class App {
} catch (Exception ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (IOException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
} catch (org.apache.lucene.queryParser.ParseException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

View File

@@ -61,7 +61,7 @@ public class CPEQuery {
* utilized within the CPE Names.
*/
static final String CLEANSE_CHARACTER_RX = "[^A-Za-z0-9 _-]";
/* A string representation of a regular expression used to remove all but
/* A string representation of a regular expression used to remove all but
* alpha characters.
*/
static final String CLEANSE_NONALPHA_RX = "[^A-Za-z]*";
@@ -191,15 +191,15 @@ public class CPEQuery {
do {
List<Entry> entries = searchCPE(vendors, titles, versions, dependency.getTitleEvidence().getWeighting(),
dependency.getVendorEvidence().getWeighting());
if (entries.size()>0) {
if (entries.size() > 0) {
List<String> verified = verifyEntries(entries, dependency);
if (verified.size() > 0) {
found = true;
dependency.setCPEs(verified);
}
}
if (!found) {
int round = cnt % 3;
if (round == 0) {
@@ -320,7 +320,7 @@ public class CPEQuery {
*/
protected String buildSearch(String vendor, String product, String version,
List<String> vendorWeighting, List<String> produdctWeightings) {
StringBuilder sb = new StringBuilder(vendor.length() + product.length()
+ version.length() + Fields.PRODUCT.length() + Fields.VERSION.length()
+ Fields.VENDOR.length() + STRING_BUILDER_BUFFER);
@@ -335,7 +335,7 @@ public class CPEQuery {
if (!appendWeightedSearch(sb, Fields.VENDOR, vendor.toLowerCase(), vendorWeighting)) {
return null;
}
sb.append(Fields.VERSION).append(":(");
if (sb.indexOf("^") > 0) {
//if we have a weighting on something else, reduce the weighting on the version a lot
@@ -369,7 +369,7 @@ public class CPEQuery {
sb.append(" ").append(field).append(":( ");
String cleanText = cleanseText(searchText);
if ("".equals(cleanText)) {
return false;
}
@@ -408,7 +408,7 @@ public class CPEQuery {
private String cleanseText(String text) {
return text.replaceAll(CLEANSE_CHARACTER_RX, " ");
}
/**
* Compares two strings after lower casing them and removing the non-alpha
* characters.

View File

@@ -19,10 +19,16 @@ package org.codesecure.dependencycheck.data.cpe;
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.lucene.analysis.Analyzer;
@@ -38,6 +44,8 @@ import org.apache.lucene.util.Version;
import org.codesecure.dependencycheck.utils.Downloader;
import org.codesecure.dependencycheck.utils.Settings;
import org.codesecure.dependencycheck.data.cpe.xml.Importer;
import org.joda.time.DateTime;
import org.joda.time.Days;
/**
* The Index class is used to utilize and maintain the CPE Index.
@@ -46,6 +54,14 @@ import org.codesecure.dependencycheck.data.cpe.xml.Importer;
*/
public class Index {
/**
* Te name of the properties file containing the timestamp of the last update.
*/
private static final String UPDATE_PROPERTIES_FILE = "lastupdated.prop";
/**
* The properties file key for the last updated field.
*/
private static final String LAST_UPDATED = "lastupdated";
/**
* The Lucene directory containing the index.
*/
@@ -124,10 +140,47 @@ public class Index {
outputPath = File.createTempFile("cpe", ".xml");
Downloader.fetchFile(url, outputPath);
Importer.importXML(outputPath.toString());
writeLastUpdatedPropertyFile();
} catch (Exception ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
} finally {
outputPath.delete();
boolean deleted = false;
try {
deleted = outputPath.delete();
} finally {
if (!deleted) {
outputPath.deleteOnExit();
}
}
}
}
}
private void writeLastUpdatedPropertyFile() {
DateTime now = new DateTime();
String dir = Settings.getString(Settings.KEYS.CPE_INDEX);
File cpeProp = new File(dir + File.separatorChar + UPDATE_PROPERTIES_FILE);
Properties prop = new Properties();
prop.put(this.LAST_UPDATED, String.valueOf(now.getMillis()));
OutputStream os = null;
try {
os = new FileOutputStream(cpeProp);
OutputStreamWriter out = new OutputStreamWriter(os);
prop.store(out, dir);
} catch (FileNotFoundException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
os.flush();
} catch (IOException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
}
try {
os.close();
} catch (IOException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@@ -138,6 +191,43 @@ public class Index {
* @return whether or not the CPE Index needs to be updated.
*/
public boolean updateNeeded() {
return true;
boolean needed = false;
String lastUpdated = null;
String dir = Settings.getString(Settings.KEYS.CPE_INDEX);
File f = new File(dir);
if (!f.exists()) {
needed = true;
} else {
File cpeProp = new File(dir + File.separatorChar + UPDATE_PROPERTIES_FILE);
if (!cpeProp.exists()) {
needed = true;
} else {
Properties prop = new Properties();
FileInputStream is = null;
try {
is = new FileInputStream(cpeProp);
prop.load(is);
lastUpdated = prop.getProperty(this.LAST_UPDATED);
} catch (FileNotFoundException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
}
try {
long lastupdate = Long.parseLong(lastUpdated);
DateTime last = new DateTime(lastupdate);
DateTime now = new DateTime();
Days d = Days.daysBetween(last, now);
int days = d.getDays();
int freq = Settings.getInt(Settings.KEYS.CPE_DOWNLOAD_FREQUENCY);
if (days >= freq) {
needed = true;
}
} catch (NumberFormatException ex) {
needed = true;
}
}
}
return needed;
}
}

View File

@@ -39,6 +39,25 @@ public class Importer {
private Importer() {
}
/**
* Imports the CPE XML File into the Lucene Index.
*
* @param file containing the path to the CPE XML file.
* @throws ParserConfigurationException is thrown if the parser is misconfigured.
* @throws SAXException is thrown when there is a SAXException.
* @throws IOException is thrown when there is an IOException.
*/
public static void importXML(File file) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
CPEHandler handler = new CPEHandler();
Indexer indexer = new Indexer();
indexer.open();
handler.registerSaveDelegate(indexer);
saxParser.parse(file, handler);
indexer.close();
}
/**
* Imports the CPE XML File into the Lucene Index.
*
@@ -48,14 +67,7 @@ public class Importer {
* @throws IOException is thrown when there is an IOException.
*/
public static void importXML(String path) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
CPEHandler handler = new CPEHandler();
Indexer indexer = new Indexer();
indexer.open();
handler.registerSaveDelegate(indexer);
File f = new File(path);
saxParser.parse(f, handler);
indexer.close();
Importer.importXML(f);
}
}

View File

@@ -27,7 +27,6 @@ import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import org.apache.velocity.runtime.RuntimeConstants;
@@ -42,20 +41,28 @@ import org.codesecure.dependencycheck.scanner.Dependency;
*/
public class ReportGenerator {
/**
* Generates the Dependency Reports for the identified dependencies.
*
* @param outputDir the path where the reports should be written.
* @param applicationName the name of the application that was scanned.
* @param dependencies a list of dependencies to include in the report.
* @throws IOException is thrown when the template file does not exist.
* @throws Exception is thrown if there is an error writting out the reports.
*/
public void generateReports(String outputDir, String applicationName, List<Dependency> dependencies) throws IOException, Exception {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("dependencies",dependencies);
properties.put("dependencies", dependencies);
properties.put("applicationName", applicationName);
String reportName = applicationName.replaceAll("[^a-zA-Z0-9-_ \\.]+", "");
String filename = outputDir + File.separatorChar + reportName;
generateReport("HtmlReport",filename+".html",properties);
//generateReport("XmlReport",filename+".xml",properties);
generateReport("HtmlReport", filename + ".html", properties);
//generateReport("XmlReport",filename + ".xml",properties);
}
/**
* much of this code is from http://stackoverflow.com/questions/2931516/loading-velocity-template-inside-a-jar-file
* @param templateName the name of the template to load.
@@ -64,23 +71,23 @@ public class ReportGenerator {
* @throws IOException is thrown when the template file does not exist.
* @throws Exception is thrown when an exception occurs.
*/
protected void generateReport(String templateName, String outFileName,
protected void generateReport(String templateName, String outFileName,
Map<String, Object> properties) throws IOException, Exception {
VelocityEngine ve = new VelocityEngine();
ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
ToolManager manager = new ToolManager();
ToolManager manager = new ToolManager();
Context context = manager.createContext();
EasyFactoryConfiguration config = new EasyFactoryConfiguration();
EasyFactoryConfiguration config = new EasyFactoryConfiguration();
config.addDefaultTools();
config.toolbox("application")
.tool("esc","org.apache.velocity.tools.generic.EscapeTool")
.tool("esc", "org.apache.velocity.tools.generic.EscapeTool")
.tool("org.apache.velocity.tools.generic.DateTool");
manager.configure(config);
ve.init();
final String templatePath = "templates/" + templateName + ".vsl";
@@ -91,19 +98,19 @@ public class ReportGenerator {
InputStreamReader reader = new InputStreamReader(input);
BufferedWriter writer = null;
//VelocityContext context = new VelocityContext();
//load the data into the context
if (properties != null) {
for (Map.Entry<String, Object> property : properties.entrySet()) {
context.put(property.getKey(), property.getValue());
}
}
}
try {
writer = new BufferedWriter(new FileWriter(new File(outFileName)));
if (!ve.evaluate(context, writer, templatePath, reader)) {
throw new Exception("Failed to convert the template into html.");
}
@@ -112,12 +119,12 @@ public class ReportGenerator {
try {
writer.close();
} catch (Exception ex) {
//ignore this error.
}
try {
reader.close();
} catch (Exception ex) {
//ignore this error.
}
}
}

View File

@@ -0,0 +1,12 @@
/**
* <html>
* <head>
* <title>org.codesecure.dependencycheck.reporting</title>
* </head>
* <body>
* Contains classes used to generate reports.
* </body>
* </html>
*/
package org.codesecure.dependencycheck.reporting;

View File

@@ -65,13 +65,16 @@ public class Dependency {
*/
protected EvidenceCollection versionEvidence = null;
/**
* Constructs a new Dependency object.
*/
public Dependency() {
vendorEvidence = new EvidenceCollection();
titleEvidence = new EvidenceCollection();
versionEvidence = new EvidenceCollection();
cpes = new ArrayList<String>();
}
/**
* Returns the file name of the JAR.
*
@@ -180,8 +183,8 @@ public class Dependency {
public EvidenceCollection getEvidence() {
return EvidenceCollection.mergeUsed(this.titleEvidence, this.vendorEvidence, this.versionEvidence);
}
/**
* Returns the evidence used to identify this dependency.
*

View File

@@ -234,5 +234,5 @@ public class EvidenceCollection implements Iterable<Evidence> {
}
return sb.toString();
}
}

View File

@@ -51,22 +51,23 @@ public class JarAnalyzer implements Analyzer {
private static final String BUNDLE_NAME = "Bundle-Name"; //: Struts 2 Core
private static final String BUNDLE_VENDOR = "Bundle-Vendor"; //: Apache Software Foundation
private enum STRING_STATE {
ALPHA,
NUMBER,
OTHER
}
private STRING_STATE determineState(char c) {
if (c>='0' && c<='9' || c=='.') {
if (c >= '0' && c <= '9' || c == '.') {
return STRING_STATE.NUMBER;
} else if (c>='a' && c<='z') {
} else if (c >= 'a' && c <= 'z') {
return STRING_STATE.ALPHA;
} else {
return STRING_STATE.OTHER;
}
}
/**
* Loads a specified JAR file and collects information from the manifest and
* checksums to identify the correct CPE information.
@@ -82,19 +83,17 @@ public class JarAnalyzer implements Analyzer {
String fileName = file.getName();
dependency.setFileName(fileName);
dependency.setFilePath(file.getCanonicalPath());
String fileNameEvidence = fileName.substring(0,fileName.length()-4)
.toLowerCase()
.replace('-', ' ')
.replace('_', ' ');
String fileNameEvidence = fileName.substring(0, fileName.length() - 4)
.toLowerCase().replace('-', ' ').replace('_', ' ');
StringBuilder sb = new StringBuilder(fileNameEvidence.length());
STRING_STATE state = determineState(fileNameEvidence.charAt(0));
for(int i=0;i<fileNameEvidence.length();i++) {
for (int i = 0; i < fileNameEvidence.length(); i++) {
char c = fileNameEvidence.charAt(i);
STRING_STATE new_state = determineState(c);
if (new_state != state) {
STRING_STATE newState = determineState(c);
if (newState != state) {
sb.append(' ');
state = new_state;
state = newState;
}
sb.append(c);
}
@@ -106,7 +105,7 @@ public class JarAnalyzer implements Analyzer {
fileNameEvidence, Evidence.Confidence.HIGH);
dependency.getVersionEvidence().addEvidence("jar", "file name",
fileNameEvidence, Evidence.Confidence.HIGH);
String md5 = null;
String sha1 = null;
try {

View File

@@ -87,7 +87,7 @@ public class Scanner {
break;
}
}
if (implmnts) {
this.analyzers.put(extension, (Analyzer) analyzer.newInstance());
} else {

View File

@@ -103,7 +103,7 @@ public final class CliParser {
//TODO - need a new exception type here, this isn't really a parseexception.
throw new ParseException("Scan cannot be run without specifying a directory to write the reports to via the 'out' argument.");
} else {
String p = line.getOptionValue(ArgumentName.OUT,"");
String p = line.getOptionValue(ArgumentName.OUT, "");
File f = new File(p);
if ("".equals(p) || !(f.exists() && f.isDirectory())) {
//TODO - need a new exception type here, this isn't really a parseexception.
@@ -151,22 +151,29 @@ public final class CliParser {
*/
@SuppressWarnings("static-access")
private Options createCommandLineOptions() {
Option help = new Option(ArgumentName.HELP_SHORT, ArgumentName.HELP, false, "print this message");
Option version = new Option(ArgumentName.VERSION_SHORT, ArgumentName.VERSION, false, "print the version information and exit");
Option appname = OptionBuilder.withArgName("name").hasArg().withLongOpt(ArgumentName.APPNAME).withDescription("the name of the application being scanned").create(ArgumentName.APPNAME_SHORT);
Option help = new Option(ArgumentName.HELP_SHORT, ArgumentName.HELP, false,
"print this message");
Option path = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.SCAN).withDescription("the path to scan").create(ArgumentName.SCAN_SHORT);
Option version = new Option(ArgumentName.VERSION_SHORT, ArgumentName.VERSION,
false, "print the version information and exit");
Option load = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.CPE).withDescription("load the CPE xml file").create(ArgumentName.CPE_SHORT);
Option noupdate = new Option(ArgumentName.DISABLE_AUTO_UPDATE_SHORT, ArgumentName.DISABLE_AUTO_UPDATE,
false, "disables the automatic updating of the CPE data.");
Option appname = OptionBuilder.withArgName("name").hasArg().withLongOpt(ArgumentName.APPNAME)
.withDescription("the name of the application being scanned").create(ArgumentName.APPNAME_SHORT);
Option path = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.SCAN)
.withDescription("the path to scan - this option can be specified multiple times.").create(ArgumentName.SCAN_SHORT);
Option load = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.CPE)
.withDescription("load the CPE xml file").create(ArgumentName.CPE_SHORT);
Option out = OptionBuilder.withArgName("folder").hasArg().withLongOpt(ArgumentName.OUT)
.withDescription("the folder to write reports to.").create(ArgumentName.OUT_SHORT);
Option out = OptionBuilder.withArgName("folder").hasArg().withLongOpt(ArgumentName.OUT).withDescription("the folder to write reports to.").create(ArgumentName.OUT_SHORT);
//TODO add the ability to load a properties file to override the defaults...
//TODO add the ability to load the CVE entries.
//TODO add a switch to auto-update CVE entries.
//TODO add a switch to auto-update CPE entries.
OptionGroup og = new OptionGroup();
og.addOption(path);
og.addOption(load);
@@ -177,6 +184,7 @@ public final class CliParser {
opts.addOption(appname);
opts.addOption(version);
opts.addOption(help);
opts.addOption(noupdate);
return opts;
}
@@ -222,10 +230,14 @@ public final class CliParser {
*/
public void printHelp() {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("DependencyCheck", options, true);
formatter.printHelp(Settings.getString("application.name", "DependencyCheck"),
"\n" + Settings.getString("application.name", "DependencyCheck") +
" can be used to identify if there are any known CVE vulnerabilities in libraries utillized by an application." +
" " + Settings.getString("application.name", "DependencyCheck") + " will automatically update required data from the Internet, such as the CVE and CPE data files from nvd.nist.gov.\n",
options, "", true);
}
/**
* Retrieves the file command line parameter(s) specified for the 'cpe' argument.
*
@@ -234,7 +246,7 @@ public final class CliParser {
public String getCpeFile() {
return line.getOptionValue(ArgumentName.CPE);
}
/**
* Retrieves the file command line parameter(s) specified for the 'scan' argument.
*
@@ -242,12 +254,21 @@ public final class CliParser {
*/
public String[] getScanFiles() {
return line.getOptionValues(ArgumentName.SCAN);
}
/**
* returns the directory to write the reports to specified on the command line.
* @return the path to the reports directory.
*/
public String getReportDirectory() {
return line.getOptionValue(ArgumentName.OUT);
}
/**
* Returns the application name specified on the command line.
* @return the applicatoin name.
*/
public String getApplicationName() {
return line.getOptionValue(ArgumentName.APPNAME);
}
@@ -257,49 +278,83 @@ public final class CliParser {
* <li>Implementation-Version: ${pom.version}</li></ul>
*/
public void printVersionInfo() {
String version = "DependencyCheck version unknown";
URLClassLoader cl = (URLClassLoader) this.getClass().getClassLoader();
InputStream is = null;
try {
URL url = cl.findResource("META-INF/MANIFEST.MF");
is = url.openStream();
Manifest manifest = new Manifest(is);
Attributes atts = manifest.getMainAttributes();
version = atts.getValue(Attributes.Name.IMPLEMENTATION_TITLE)
+ " version "
+ atts.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
} catch (IOException ex) {
Logger.getLogger(CliParser.class.getName()).log(Level.WARNING, null, ex);
} finally {
try {
is.close();
is = null;
} catch (Throwable ex) {
Logger.getLogger(CliParser.class.getName()).log(Level.FINEST, null, ex);
}
}
String version = String.format("%s version %s",
Settings.getString("application.name", "DependencyCheck"),
Settings.getString("application.version", "Unknown"));
System.out.println(version);
}
/**
* Checks if the auto update feature has been disabled. If it has been disabled
* via the command line this will return false.
*
* @return if auto-update is allowed.
*/
public boolean isAutoUpdate() {
return (line != null) ? !line.hasOption(ArgumentName.DISABLE_AUTO_UPDATE) : false;
}
/**
* A collection of static final strings that represent the possible command
* line arguments.
*/
public static class ArgumentName {
/**
* The long CLI argument name specifing the directory/file to scan
*/
public static final String SCAN = "scan";
public static final String CPE = "cpe";
public static final String OUT = "out";
public static final String APPNAME = "app";
public static final String VERSION = "version";
public static final String HELP = "help";
/**
* The short CLI argument name specifing the directory/file to scan
*/
public static final String SCAN_SHORT = "s";
/**
* The long CLI argument name specifing the path to the CPE.XML file to import
*/
public static final String CPE = "cpe";
/**
* The short CLI argument name specifing the path to the CPE.XML file to import
*/
public static final String CPE_SHORT = "c";
/**
* The long CLI argument name specifing that the CPE/CVE/etc. data should not be automatically updated.
*/
public static final String DISABLE_AUTO_UPDATE = "noupdate";
/**
* The short CLI argument name specifing that the CPE/CVE/etc. data should not be automatically updated.
*/
public static final String DISABLE_AUTO_UPDATE_SHORT = "n";
/**
* The long CLI argument name specifing the directory to write the reports to.
*/
public static final String OUT = "out";
/**
* The short CLI argument name specifing the directory to write the reports to.
*/
public static final String OUT_SHORT = "o";
public static final String VERSION_SHORT = "v";
public static final String HELP_SHORT = "h";
/**
* The long CLI argument name specifing the name of the application to be scanned.
*/
public static final String APPNAME = "app";
/**
* The short CLI argument name specifing the name of the application to be scanned.
*/
public static final String APPNAME_SHORT = "a";
/**
* The long CLI argument name asking for help.
*/
public static final String HELP = "help";
/**
* The short CLI argument name asking for help.
*/
public static final String HELP_SHORT = "h";
/**
* The long CLI argument name asking for the version.
*/
public static final String VERSION_SHORT = "v";
/**
* The short CLI argument name asking for the version.
*/
public static final String VERSION = "version";
}
}

View File

@@ -34,6 +34,7 @@ import java.util.logging.Logger;
*/
public class Settings {
/**
* The collection of keys used within the properties file.
*/
@@ -42,19 +43,23 @@ public class Settings {
/**
* The properties key for the path where the CPE Lucene Index will be stored.
*/
public static final String CPE_INDEX = "index.cpe";
public static final String CPE_INDEX = "cpe";
/**
* The properties key for the URL to the CPE.
*/
public static final String CPE_URL = "index.cpe.url";
public static final String CPE_URL = "cpe.url";
/**
* The properties key for the URL to the CPE.
*/
public static final String CPE_DOWNLOAD_FREQUENCY = "cpe.downloadfrequency";
/**
* The properties key for the path where the CCE Lucene Index will be stored.
*/
public static final String CVE_INDEX = "index.cve";
public static final String CVE_INDEX = "cve";
/**
* The properties key for the path where the OSVDB Lucene Index will be stored.
*/
public static final String OSVDB_INDEX = "index.osvdb";
public static final String OSVDB_INDEX = "osvdb";
/**
* The properties key prefix for the analyzer assocations.
*/
@@ -77,6 +82,24 @@ public class Settings {
}
}
/**
* Returns a value from the properties file. If the value was specified as a
* system property or passed in via the -Dprop=value argument - this method
* will return the value from the system properties before the values in
* the contained configuration file.
*
* @param key the key to lookup within the properties file.
* @param defaultValue the default value for the requested property.
* @return the property from the properties file.
*/
public static String getString(String key, String defaultValue) {
String str = System.getProperty(key, instance.props.getProperty(key));
if (str==null) {
str = defaultValue;
}
return str;
}
/**
* Returns a value from the properties file. If the value was specified as a
* system property or passed in via the -Dprop=value argument - this method
@@ -127,13 +150,28 @@ public class Settings {
}
return ret;
}
// public static boolean getBoolean(String key) {
// return Boolean.parseBoolean(instance.props.getProperty(key));
// }
// public static long getLong(String key) {
// return Long.parseLong(instance.props.getProperty(key));
// }
// public static int getInt(String key) {
// return Integer.parseInt(instance.props.getProperty(key));
// }
/**
* Returns a integer value from the properties file. If the value was specified as a
* system property or passed in via the -Dprop=value argument - this method
* will return the value from the system properties before the values in
* the contained configuration file.
*
* @param key the key to lookup within the properties file.
* @return the property from the properties file.
*/
public static int getInt(String key) {
return Integer.parseInt(Settings.getString(key));
}
/**
* Returns a boolean value from the properties file. If the value was specified as a
* system property or passed in via the -Dprop=value argument - this method
* will return the value from the system properties before the values in
* the contained configuration file.
*
* @param key the key to lookup within the properties file.
* @return the property from the properties file.
*/
public static boolean getBoolean(String key) {
return Boolean.parseBoolean(Settings.getString(key));
}
}

View File

@@ -1,9 +1,10 @@
# To change this template, choose Tools | Templates
# and open the template in the editor.
application.name=${pom.name}
application.version=${pom.version}
index.cpe=store/cpe
index.cpe.url=http://static.nvd.nist.gov/feeds/xml/cpe/dictionary/official-cpe-dictionary_v2.2.xml
index.cve=store/cve
index.osvdb=store/osvdb
cpe=store/cpe
cpe.url=http://static.nvd.nist.gov/feeds/xml/cpe/dictionary/official-cpe-dictionary_v2.2.xml
cpe.downloadfrequency=1
cve=store/cve
osvdb=store/osvdb
file.extension.analyzer.association.jar=org.codesecure.dependencycheck.scanner.JarAnalyzer

View File

@@ -20,7 +20,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
@version 1
*#
<!DOCTYPE html>
<html>
<head>
@@ -274,7 +273,9 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
#set($cnt=0)
#foreach($dependency in $dependencies)
<h3 class="subsectionheader standardsubsection"><a name="$esc.html($dependency.FilePath)"></a>$esc.html($dependency.FileName)</h3>
<div class="subsectioncontent">File&nbsp;Path:&nbsp;$esc.html($dependency.FilePath)
<div class="subsectioncontent">File&nbsp;Path:&nbsp;$esc.html($dependency.FilePath)<br/>
MD5:&nbsp;$esc.html($dependency.Md5sum)<br/>
SHA1:&nbsp;$esc.html($dependency.Sha1sum)
#set($cnt=$cnt+1)
<h4 id="header$cnt" class="subsectionheader expandablesubsection white">Evidence</h4>
<div id="content$cnt" class="subsectioncontent standardsubsection hidden">
@@ -293,8 +294,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
Information for specific CVE entries for the idenfied CPE can be found <a href="http://web.nvd.nist.gov/view/vuln/search-results?cpe=$esc.url($cpevalue)" target="blank">here</a>.
#* http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-0838
<a href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=" target="blank">cve://a:/blah1.blah</a><br/>
<a href="cve.mitre.org" target="blank">cve://a:/blah2.blah</a><br/>
<a href="cve.mitre.org" target="blank">cve://a:/blah3.blah</a><br/>*#
*#
</div>
#end
#if($dependency.getCPEs().size()>1)
@@ -310,7 +310,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
#end
#if($dependency.getCPEs().size()==0)
<h4>No CPE Identifies were found for this dependency.</h4>
<h4>No CPE Identifiers were found for this dependency.</h4>
#end
</div>
#end

File diff suppressed because one or more lines are too long

View File

@@ -31,7 +31,7 @@ public abstract class BaseIndexTestCase extends TestCase {
}
protected void ensureIndexExists() throws Exception {
String indexPath = Settings.getString("index.cpe");
String indexPath = Settings.getString("cpe");
java.io.File f = new File(indexPath);
if (!f.exists()) {
f.mkdirs();

View File

@@ -336,9 +336,8 @@ public class CliParserTest extends TestCase {
baos.flush();
String text = (new String(baos.toByteArray()));
String[] lines = text.split(System.getProperty("line.separator"));
assertEquals("usage: DependencyCheck [-a <name>] [-c <file> | -s <path>] [-h] [-o", lines[0]);
assertEquals(" <folder>] [-v]", lines[1]);
assertEquals(8, lines.length);
assertTrue(lines[0].startsWith("usage: "));
assertTrue((lines.length>2));
} catch (IOException ex) {
System.setOut(out);
fail("CliParser.printVersionInfo did not write anything to system.out.");

View File

@@ -36,15 +36,17 @@ public class DownloaderTest {
public void tearDown() {
}
/**
* Test of fetchFile method, of class Downloader.
* @throws Exception thrown when an excpetion occurs.
*/
@Test
public void testFetchFile_URL_String() throws Exception {
System.out.println("fetchFile");
URL url = new URL(Settings.getString(Settings.KEYS.CPE_URL));
String outputPath = "target\\downloaded_cpe.xml";
Downloader.fetchFile(url, outputPath);
}
//This test is being removed because it is a bit too slow.
// /**
// * Test of fetchFile method, of class Downloader.
// * @throws Exception thrown when an excpetion occurs.
// */
// @Test
// public void testFetchFile_URL_String() throws Exception {
// System.out.println("fetchFile");
// URL url = new URL(Settings.getString(Settings.KEYS.CPE_URL));
// String outputPath = "target\\downloaded_cpe.xml";
// Downloader.fetchFile(url, outputPath);
// }
}