mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-01-16 00:33:46 +01:00
change in namespace as this is now an OWASP project
Former-commit-id: dc00f98a142bef2560d90f3b851844f352fbf262
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data;
|
||||
|
||||
/**
|
||||
* Defines an Index who's data is retrieved from the Internet. This data can be
|
||||
* downloaded and the index updated.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public interface CachedWebDataSource {
|
||||
|
||||
/**
|
||||
* Determines if an update to the current index is needed, if it is the new
|
||||
* data is downloaded from the Internet and imported into the current Lucene
|
||||
* Index.
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an exception updating the
|
||||
* index.
|
||||
*/
|
||||
void update() throws UpdateException;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An exception used when an error occurs reading a setting.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class UpdateException extends IOException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new UpdateException.
|
||||
*/
|
||||
public UpdateException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UpdateException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
*/
|
||||
public UpdateException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UpdateException.
|
||||
*
|
||||
* @param ex the cause of the update exception.
|
||||
*/
|
||||
public UpdateException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UpdateException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
* @param ex the cause of the update exception.
|
||||
*/
|
||||
public UpdateException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class UpdateService {
|
||||
|
||||
private static UpdateService service;
|
||||
private final ServiceLoader<CachedWebDataSource> loader;
|
||||
|
||||
/**
|
||||
* Creates a new instance of UpdateService
|
||||
*/
|
||||
private UpdateService() {
|
||||
loader = ServiceLoader.load(CachedWebDataSource.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the singleton instance of UpdateService.
|
||||
*
|
||||
* @return a singleton UpdateService.
|
||||
*/
|
||||
public static synchronized UpdateService getInstance() {
|
||||
if (service == null) {
|
||||
service = new UpdateService();
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for all instances of the CachedWebDataSource
|
||||
* interface.
|
||||
*
|
||||
* @return an iterator of CachedWebDataSource.
|
||||
*/
|
||||
public Iterator<CachedWebDataSource> getDataSources() {
|
||||
return loader.iterator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.queryparser.classic.ParseException;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.AnalysisException;
|
||||
import org.owasp.dependencycheck.analyzer.AnalysisPhase;
|
||||
import org.owasp.dependencycheck.data.lucene.LuceneUtils;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Evidence;
|
||||
import org.owasp.dependencycheck.dependency.Evidence.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
||||
|
||||
/**
|
||||
* CPEAnalyzer is a utility class that takes a project dependency and attempts
|
||||
* to discern if there is an associated CPE. It uses the evidence contained
|
||||
* within the dependency to search the Lucene index.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class CPEAnalyzer implements org.owasp.dependencycheck.analyzer.Analyzer {
|
||||
|
||||
/**
|
||||
* The maximum number of query results to return.
|
||||
*/
|
||||
static final int MAX_QUERY_RESULTS = 10;
|
||||
/**
|
||||
* The weighting boost to give terms when constructing the Lucene query.
|
||||
*/
|
||||
static final String WEIGHTING_BOOST = "^5";
|
||||
/**
|
||||
* A string representation of a regular expression defining characters
|
||||
* 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
|
||||
* alpha characters.
|
||||
*/
|
||||
static final String CLEANSE_NONALPHA_RX = "[^A-Za-z]*";
|
||||
/**
|
||||
* The additional size to add to a new StringBuilder to account for extra
|
||||
* data that will be written into the string.
|
||||
*/
|
||||
static final int STRING_BUILDER_BUFFER = 20;
|
||||
/**
|
||||
* The CPE Index.
|
||||
*/
|
||||
protected Index cpe = null;
|
||||
|
||||
/**
|
||||
* Opens the data source.
|
||||
*
|
||||
* @throws IOException when the Lucene directory to be queried does not
|
||||
* exist or is corrupt.
|
||||
*/
|
||||
public void open() throws IOException {
|
||||
cpe = new Index();
|
||||
cpe.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the data source.
|
||||
*/
|
||||
public void close() {
|
||||
cpe.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the data source - is the index open.
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return (cpe != null) && cpe.isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the Lucene index is closed.
|
||||
*
|
||||
* @throws Throwable when a throwable is thrown.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
if (isOpen()) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the data store of CPE entries, trying to identify the CPE for
|
||||
* the given dependency based on the evidence contained within. The
|
||||
* dependency passed in is updated with any identified CPE values.
|
||||
*
|
||||
* @param dependency the dependency to search for CPE entries on.
|
||||
* @throws CorruptIndexException is thrown when the Lucene index is corrupt.
|
||||
* @throws IOException is thrown when an IOException occurs.
|
||||
* @throws ParseException is thrown when the Lucene query cannot be parsed.
|
||||
*/
|
||||
protected void determineCPE(Dependency dependency) throws CorruptIndexException, IOException, ParseException {
|
||||
Confidence vendorConf = Confidence.HIGH;
|
||||
Confidence productConf = Confidence.HIGH;
|
||||
Confidence versionConf = Confidence.HIGH;
|
||||
|
||||
String vendors = addEvidenceWithoutDuplicateTerms("", dependency.getVendorEvidence(), vendorConf);
|
||||
String products = addEvidenceWithoutDuplicateTerms("", dependency.getProductEvidence(), productConf);
|
||||
String versions = addEvidenceWithoutDuplicateTerms("", dependency.getVersionEvidence(), versionConf);
|
||||
|
||||
boolean found = false;
|
||||
int ctr = 0;
|
||||
do {
|
||||
List<Entry> entries = searchCPE(vendors, products, versions, dependency.getProductEvidence().getWeighting(),
|
||||
dependency.getVendorEvidence().getWeighting());
|
||||
|
||||
|
||||
for (Entry e : entries) {
|
||||
if (verifyEntry(e, dependency)) {
|
||||
found = true;
|
||||
dependency.addIdentifier(
|
||||
"cpe",
|
||||
e.getName(),
|
||||
"http://web.nvd.nist.gov/view/vuln/search?cpe="
|
||||
+ URLEncoder.encode(e.getName(), "UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
int round = ctr % 3;
|
||||
if (round == 0) {
|
||||
vendorConf = reduceConfidence(vendorConf);
|
||||
if (dependency.getVendorEvidence().contains(vendorConf)) {
|
||||
//vendors += " " + dependency.getVendorEvidence().toString(vendorConf);
|
||||
vendors = addEvidenceWithoutDuplicateTerms(vendors, dependency.getVendorEvidence(), vendorConf);
|
||||
} else {
|
||||
ctr += 1;
|
||||
round += 1;
|
||||
}
|
||||
}
|
||||
if (round == 1) {
|
||||
productConf = reduceConfidence(productConf);
|
||||
if (dependency.getProductEvidence().contains(productConf)) {
|
||||
//products += " " + dependency.getProductEvidence().toString(productConf);
|
||||
products = addEvidenceWithoutDuplicateTerms(products, dependency.getProductEvidence(), productConf);
|
||||
} else {
|
||||
ctr += 1;
|
||||
round += 1;
|
||||
}
|
||||
}
|
||||
if (round == 2) {
|
||||
versionConf = reduceConfidence(versionConf);
|
||||
if (dependency.getVersionEvidence().contains(versionConf)) {
|
||||
//versions += " " + dependency.getVersionEvidence().toString(versionConf);
|
||||
versions = addEvidenceWithoutDuplicateTerms(versions, dependency.getVersionEvidence(), versionConf);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!found && (++ctr) < 9);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text created by concatenating the text and the values from
|
||||
* the EvidenceCollection (filtered for a specific confidence). This
|
||||
* attempts to prevent duplicate terms from being added.<br/<br/> Note, if
|
||||
* the evidence is longer then 200 characters it will be truncated.
|
||||
*
|
||||
* @param text the base text.
|
||||
* @param ec an EvidenceCollection
|
||||
* @param confidenceFilter a Confidence level to filter the evidence by.
|
||||
* @return the new evidence text
|
||||
*/
|
||||
private String addEvidenceWithoutDuplicateTerms(final String text, final EvidenceCollection ec, Confidence confidenceFilter) {
|
||||
String txt = (text == null) ? "" : text;
|
||||
StringBuilder sb = new StringBuilder(txt.length() + (20 * ec.size()));
|
||||
sb.append(txt);
|
||||
for (Evidence e : ec.iterator(confidenceFilter)) {
|
||||
String value = e.getValue();
|
||||
|
||||
//hack to get around the fact that lucene does a really good job of recognizing domains and not
|
||||
// splitting them. TODO - put together a better lucene analyzer specific to the domain.
|
||||
if (value.startsWith("http://")) {
|
||||
value = value.substring(7).replaceAll("\\.", " ");
|
||||
}
|
||||
if (value.startsWith("https://")) {
|
||||
value = value.substring(8).replaceAll("\\.", " ");
|
||||
}
|
||||
if (sb.indexOf(value) < 0) {
|
||||
sb.append(value).append(' ');
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces the given confidence by one level. This returns LOW if the
|
||||
* confidence passed in is not HIGH.
|
||||
*
|
||||
* @param c the confidence to reduce.
|
||||
* @return One less then the confidence passed in.
|
||||
*/
|
||||
private Confidence reduceConfidence(final Confidence c) {
|
||||
if (c == Confidence.HIGH) {
|
||||
return Confidence.MEDIUM;
|
||||
} else {
|
||||
return Confidence.LOW;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Searches the Lucene CPE index to identify possible CPE entries
|
||||
* associated with the supplied vendor, product, and version.</p>
|
||||
*
|
||||
* <p>If either the vendorWeightings or productWeightings lists have been
|
||||
* populated this data is used to add weighting factors to the search.</p>
|
||||
*
|
||||
* @param vendor the text used to search the vendor field.
|
||||
* @param product the text used to search the product field.
|
||||
* @param version the text used to search the version field.
|
||||
* @param vendorWeightings a list of strings to use to add weighting factors
|
||||
* to the vendor field.
|
||||
* @param productWeightings Adds a list of strings that will be used to add
|
||||
* weighting factors to the product search.
|
||||
* @return a list of possible CPE values.
|
||||
* @throws CorruptIndexException when the Lucene index is corrupt.
|
||||
* @throws IOException when the Lucene index is not found.
|
||||
* @throws ParseException when the generated query is not valid.
|
||||
*/
|
||||
protected List<Entry> searchCPE(String vendor, String product, String version,
|
||||
Set<String> vendorWeightings, Set<String> productWeightings)
|
||||
throws CorruptIndexException, IOException, ParseException {
|
||||
ArrayList<Entry> ret = new ArrayList<Entry>(MAX_QUERY_RESULTS);
|
||||
|
||||
String searchString = buildSearch(vendor, product, version, vendorWeightings, productWeightings);
|
||||
if (searchString == null) {
|
||||
return ret;
|
||||
}
|
||||
TopDocs docs = cpe.search(searchString, MAX_QUERY_RESULTS);
|
||||
for (ScoreDoc d : docs.scoreDocs) {
|
||||
Document doc = cpe.getDocument(d.doc);
|
||||
Entry entry = Entry.parse(doc);
|
||||
entry.setSearchScore(d.score);
|
||||
if (!ret.contains(entry)) {
|
||||
ret.add(entry);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Builds a Lucene search string by properly escaping data and
|
||||
* constructing a valid search query.</p>
|
||||
*
|
||||
* <p>If either the possibleVendor or possibleProducts lists have been
|
||||
* populated this data is used to add weighting factors to the search string
|
||||
* generated.</p>
|
||||
*
|
||||
* @param vendor text to search the vendor field.
|
||||
* @param product text to search the product field.
|
||||
* @param version text to search the version field.
|
||||
* @param vendorWeighting a list of strings to apply to the vendor to boost
|
||||
* the terms weight.
|
||||
* @param productWeightings a list of strings to apply to the product to
|
||||
* boost the terms weight.
|
||||
* @return the Lucene query.
|
||||
*/
|
||||
protected String buildSearch(String vendor, String product, String version,
|
||||
Set<String> vendorWeighting, Set<String> productWeightings) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(vendor.length() + product.length()
|
||||
+ version.length() + Fields.PRODUCT.length() + Fields.VERSION.length()
|
||||
+ Fields.VENDOR.length() + STRING_BUILDER_BUFFER);
|
||||
|
||||
if ("".equals(version)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!appendWeightedSearch(sb, Fields.PRODUCT, product, productWeightings)) {
|
||||
return null;
|
||||
}
|
||||
sb.append(" AND ");
|
||||
if (!appendWeightedSearch(sb, Fields.VENDOR, vendor, vendorWeighting)) {
|
||||
return null;
|
||||
}
|
||||
sb.append(" AND ");
|
||||
|
||||
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
|
||||
for (String v : version.split(" ")) {
|
||||
LuceneUtils.appendEscapedLuceneQuery(sb, cleanseText(v));
|
||||
sb.append("^0.2 ");
|
||||
}
|
||||
} else {
|
||||
//LuceneUtils.appendEscapedLuceneQuery(sb, version);
|
||||
//if we have a weighting on something else, reduce the weighting on the version a lot
|
||||
for (String v : version.split(" ")) {
|
||||
LuceneUtils.appendEscapedLuceneQuery(sb, cleanseText(v));
|
||||
sb.append("^0.7 ");
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method constructs a Lucene query for a given field. The searchText
|
||||
* is split into separate words and if the word is within the list of
|
||||
* weighted words then an additional weighting is applied to the term as it
|
||||
* is appended into the query.
|
||||
*
|
||||
* @param sb a StringBuilder that the query text will be appended to.
|
||||
* @param field the field within the Lucene index that the query is
|
||||
* searching.
|
||||
* @param searchText text used to construct the query.
|
||||
* @param weightedText a list of terms that will be considered higher
|
||||
* importance when searching.
|
||||
* @return if the append was successful.
|
||||
*/
|
||||
private boolean appendWeightedSearch(StringBuilder sb, String field, String searchText, Set<String> weightedText) {
|
||||
//TODO add a mutator or special analyzer that combines words next to each other and adds them as a key.
|
||||
sb.append(" ").append(field).append(":( ");
|
||||
|
||||
String cleanText = cleanseText(searchText);
|
||||
|
||||
if ("".equals(cleanText)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (weightedText == null || weightedText.isEmpty()) {
|
||||
LuceneUtils.appendEscapedLuceneQuery(sb, cleanText);
|
||||
} else {
|
||||
StringTokenizer tokens = new StringTokenizer(cleanText);
|
||||
while (tokens.hasMoreElements()) {
|
||||
String word = tokens.nextToken();
|
||||
String temp = null;
|
||||
for (String weighted : weightedText) {
|
||||
String weightedStr = cleanseText(weighted);
|
||||
if (equalsIgnoreCaseAndNonAlpha(word, weightedStr)) {
|
||||
temp = LuceneUtils.escapeLuceneQuery(word) + WEIGHTING_BOOST;
|
||||
if (!word.equalsIgnoreCase(weightedStr)) {
|
||||
temp += " " + LuceneUtils.escapeLuceneQuery(weightedStr) + WEIGHTING_BOOST;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (temp == null) {
|
||||
temp = LuceneUtils.escapeLuceneQuery(word);
|
||||
}
|
||||
sb.append(" ").append(temp);
|
||||
}
|
||||
}
|
||||
sb.append(" ) ");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes characters from the input text that are not used within the CPE
|
||||
* index.
|
||||
*
|
||||
* @param text is the text to remove the characters from.
|
||||
* @return the text having removed some characters.
|
||||
*/
|
||||
private String cleanseText(String text) {
|
||||
return text.replaceAll(CLEANSE_CHARACTER_RX, " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two strings after lower casing them and removing the non-alpha
|
||||
* characters.
|
||||
*
|
||||
* @param l string one to compare.
|
||||
* @param r string two to compare.
|
||||
* @return whether or not the two strings are similar.
|
||||
*/
|
||||
private boolean equalsIgnoreCaseAndNonAlpha(String l, String r) {
|
||||
if (l == null || r == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String left = l.replaceAll(CLEANSE_NONALPHA_RX, "");
|
||||
String right = r.replaceAll(CLEANSE_NONALPHA_RX, "");
|
||||
return left.equalsIgnoreCase(right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the CPE Identified matches the dependency. This validates
|
||||
* that the product, vendor, and version information for the CPE are
|
||||
* contained within the dependencies evidence.
|
||||
*
|
||||
* @param entry a CPE entry.
|
||||
* @param dependency the dependency that the CPE entries could be for.
|
||||
* @return whether or not the entry is valid.
|
||||
*/
|
||||
private boolean verifyEntry(final Entry entry, final Dependency dependency) {
|
||||
boolean isValid = false;
|
||||
|
||||
if (collectionContainsStrings(dependency.getProductEvidence(), entry.getProduct())
|
||||
&& collectionContainsStrings(dependency.getVendorEvidence(), entry.getVendor())
|
||||
&& collectionContainsStrings(dependency.getVersionEvidence(), entry.getVersion())) {
|
||||
isValid = true;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private boolean collectionContainsStrings(EvidenceCollection ec, String text) {
|
||||
String[] words = text.split("[\\s_-]");
|
||||
boolean contains = true;
|
||||
for (String word : words) {
|
||||
contains &= ec.containsUsedString(word);
|
||||
}
|
||||
return contains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a dependency and attempts to determine if there are any CPE
|
||||
* identifiers for this dependency.
|
||||
*
|
||||
* @param dependency The Dependency to analyze.
|
||||
* @param engine The analysis engine
|
||||
* @throws AnalysisException is thrown if there is an issue analyzing the
|
||||
* dependency.
|
||||
*/
|
||||
public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
try {
|
||||
determineCPE(dependency);
|
||||
} catch (CorruptIndexException ex) {
|
||||
throw new AnalysisException("CPE Index is corrupt.", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new AnalysisException("Failure opening the CPE Index.", ex);
|
||||
} catch (ParseException ex) {
|
||||
throw new AnalysisException("Unable to parse the generated Lucene query for this dependency.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true because this analyzer supports all dependency types.
|
||||
*
|
||||
* @return true.
|
||||
*/
|
||||
public Set<String> getSupportedExtensions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this analyzer.
|
||||
*
|
||||
* @return the name of this analyzer.
|
||||
*/
|
||||
public String getName() {
|
||||
return "CPE Analyzer";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true because this analyzer supports all dependency types.
|
||||
*
|
||||
* @param extension the file extension of the dependency being analyzed.
|
||||
* @return true.
|
||||
*/
|
||||
public boolean supportsExtension(String extension) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the analysis phase that this analyzer should run in.
|
||||
*
|
||||
* @return the analysis phase that this analyzer should run in.
|
||||
*/
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return AnalysisPhase.IDENTIFIER_ANALYSIS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the CPE Lucene Index.
|
||||
*
|
||||
* @throws Exception is thrown if there is an issue opening the index.
|
||||
*/
|
||||
public void initialize() throws Exception {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
240
src/main/java/org/owasp/dependencycheck/data/cpe/Entry.java
Normal file
240
src/main/java/org/owasp/dependencycheck/data/cpe/Entry.java
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.document.Document;
|
||||
|
||||
/**
|
||||
* A CPE entry containing the name, vendor, product, and version.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Entry implements Serializable {
|
||||
|
||||
static final long serialVersionUID = 8011924485946326934L;
|
||||
|
||||
/**
|
||||
* This parse method does not fully convert a Lucene Document into a CPE
|
||||
* Entry; it only sets the Entry.Name.
|
||||
*
|
||||
* @param doc a Lucene Document.
|
||||
* @return a CPE Entry.
|
||||
*/
|
||||
public static Entry parse(Document doc) {
|
||||
Entry entry = new Entry();
|
||||
try {
|
||||
entry.parseName(doc.get(Fields.NAME));
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
Logger.getLogger(Entry.class.getName()).log(Level.SEVERE, null, ex);
|
||||
entry.name = doc.get(Fields.NAME);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
/**
|
||||
* The name of the CPE entry.
|
||||
*/
|
||||
protected String name;
|
||||
|
||||
/**
|
||||
* Get the value of name
|
||||
*
|
||||
* @return the value of name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of name
|
||||
*
|
||||
* @param name new value of name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
/**
|
||||
* The vendor name.
|
||||
*/
|
||||
protected String vendor;
|
||||
|
||||
/**
|
||||
* Get the value of vendor
|
||||
*
|
||||
* @return the value of vendor
|
||||
*/
|
||||
public String getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of vendor
|
||||
*
|
||||
* @param vendor new value of vendor
|
||||
*/
|
||||
public void setVendor(String vendor) {
|
||||
this.vendor = vendor;
|
||||
}
|
||||
/**
|
||||
* The product name.
|
||||
*/
|
||||
protected String product;
|
||||
|
||||
/**
|
||||
* Get the value of product
|
||||
*
|
||||
* @return the value of product
|
||||
*/
|
||||
public String getProduct() {
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of product
|
||||
*
|
||||
* @param product new value of product
|
||||
*/
|
||||
public void setProduct(String product) {
|
||||
this.product = product;
|
||||
}
|
||||
/**
|
||||
* The product version.
|
||||
*/
|
||||
protected String version;
|
||||
|
||||
/**
|
||||
* Get the value of version
|
||||
*
|
||||
* @return the value of version
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of version
|
||||
*
|
||||
* @param version new value of version
|
||||
*/
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
/**
|
||||
* The product revision.
|
||||
*/
|
||||
protected String revision;
|
||||
|
||||
/**
|
||||
* Get the value of revision
|
||||
*
|
||||
* @return the value of revision
|
||||
*/
|
||||
public String getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of revision
|
||||
*
|
||||
* @param revision new value of revision
|
||||
*/
|
||||
public void setRevision(String revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
/**
|
||||
* The search score.
|
||||
*/
|
||||
protected float searchScore;
|
||||
|
||||
/**
|
||||
* Get the value of searchScore
|
||||
*
|
||||
* @return the value of searchScore
|
||||
*/
|
||||
public float getSearchScore() {
|
||||
return searchScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of searchScore
|
||||
*
|
||||
* @param searchScore new value of searchScore
|
||||
*/
|
||||
public void setSearchScore(float searchScore) {
|
||||
this.searchScore = searchScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Parses a name attribute value, from the cpe.xml, into its
|
||||
* corresponding parts: vendor, product, version, revision.</p>
|
||||
* <p>Example:</p>
|
||||
* <code> cpe:/a:apache:struts:1.1:rc2</code>
|
||||
*
|
||||
* <p>Results in:</p> <ul> <li>Vendor: apache</li> <li>Product: struts</li>
|
||||
* <li>Version: 1.1</li> <li>Revision: rc2</li> </ul>
|
||||
*
|
||||
* @param cpeName the cpe name
|
||||
* @throws UnsupportedEncodingException should never be thrown...
|
||||
*/
|
||||
public void parseName(String cpeName) throws UnsupportedEncodingException {
|
||||
this.name = cpeName;
|
||||
if (cpeName != null && cpeName.length() > 7) {
|
||||
String[] data = cpeName.substring(7).split(":");
|
||||
if (data.length >= 1) {
|
||||
vendor = URLDecoder.decode(data[0], "UTF-8").replaceAll("[_-]", " ");
|
||||
if (data.length >= 2) {
|
||||
product = URLDecoder.decode(data[1], "UTF-8").replaceAll("[_-]", " ");
|
||||
if (data.length >= 3) {
|
||||
version = URLDecoder.decode(data[2], "UTF-8");
|
||||
if (data.length >= 4) {
|
||||
revision = URLDecoder.decode(data[3], "UTF-8");
|
||||
}
|
||||
//ignore edition and language fields.. don't really see them used in the a:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Entry other = (Entry) obj;
|
||||
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 83 * hash + (this.name != null ? this.name.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
46
src/main/java/org/owasp/dependencycheck/data/cpe/Fields.java
Normal file
46
src/main/java/org/owasp/dependencycheck/data/cpe/Fields.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
/**
|
||||
* Fields is a collection of field names used within the Lucene index for CPE
|
||||
* entries.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public abstract class Fields {
|
||||
|
||||
/**
|
||||
* The key for the name field.
|
||||
*/
|
||||
public static final String NAME = "name";
|
||||
/**
|
||||
* The key for the vendor field.
|
||||
*/
|
||||
public static final String VENDOR = "vendor";
|
||||
/**
|
||||
* The key for the product field.
|
||||
*/
|
||||
public static final String PRODUCT = "product";
|
||||
/**
|
||||
* The key for the version field.
|
||||
*/
|
||||
public static final String VERSION = "version";
|
||||
//public static final String REVISION = "revision";
|
||||
}
|
||||
206
src/main/java/org/owasp/dependencycheck/data/cpe/Index.java
Normal file
206
src/main/java/org/owasp/dependencycheck/data/cpe/Index.java
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.core.KeywordAnalyzer;
|
||||
import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queryparser.classic.QueryParser;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.FSDirectory;
|
||||
import org.apache.lucene.util.Version;
|
||||
import org.owasp.dependencycheck.data.lucene.AbstractIndex;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.data.lucene.FieldAnalyzer;
|
||||
import org.owasp.dependencycheck.data.lucene.SearchFieldAnalyzer;
|
||||
import org.owasp.dependencycheck.data.lucene.SearchVersionAnalyzer;
|
||||
import org.owasp.dependencycheck.data.lucene.VersionAnalyzer;
|
||||
|
||||
/**
|
||||
* The Index class is used to utilize and maintain the CPE Index.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class Index extends AbstractIndex {
|
||||
|
||||
/**
|
||||
* Returns the directory that holds the CPE Index.
|
||||
*
|
||||
* @return the Directory containing the CPE Index.
|
||||
* @throws IOException is thrown if an IOException occurs.
|
||||
*/
|
||||
public Directory getDirectory() throws IOException {
|
||||
File path = getDataDirectory();
|
||||
Directory dir = FSDirectory.open(path);
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the directory that the JAR file exists in so that
|
||||
* we can ensure we always use a common data directory.
|
||||
*
|
||||
* @return the data directory for this index.
|
||||
* @throws IOException is thrown if an IOException occurs of course...
|
||||
*/
|
||||
public File getDataDirectory() throws IOException {
|
||||
String fileName = Settings.getString(Settings.KEYS.CPE_INDEX);
|
||||
String filePath = Index.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
String decodedPath = URLDecoder.decode(filePath, "UTF-8");
|
||||
File exePath = new File(decodedPath);
|
||||
if (exePath.getName().toLowerCase().endsWith(".jar")) {
|
||||
exePath = exePath.getParentFile();
|
||||
} else {
|
||||
exePath = new File(".");
|
||||
}
|
||||
File path = new File(exePath.getCanonicalFile() + File.separator + fileName);
|
||||
path = new File(path.getCanonicalPath());
|
||||
if (!path.exists()) {
|
||||
if (!path.mkdirs()) {
|
||||
throw new IOException("Unable to create CPE Data directory");
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Analyzer for the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Analyzer createIndexingAnalyzer() {
|
||||
Map fieldAnalyzers = new HashMap();
|
||||
|
||||
//fieldAnalyzers.put(Fields.VERSION, new KeywordAnalyzer());
|
||||
fieldAnalyzers.put(Fields.VERSION, new VersionAnalyzer(Version.LUCENE_40));
|
||||
fieldAnalyzers.put(Fields.NAME, new KeywordAnalyzer());
|
||||
|
||||
PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(
|
||||
new FieldAnalyzer(Version.LUCENE_40), fieldAnalyzers);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
private SearchFieldAnalyzer productSearchFieldAnalyzer = null;
|
||||
private SearchFieldAnalyzer vendorSearchFieldAnalyzer = null;
|
||||
|
||||
/**
|
||||
* Creates an Analyzer for searching the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Analyzer createSearchingAnalyzer() {
|
||||
Map fieldAnalyzers = new HashMap();
|
||||
|
||||
fieldAnalyzers.put(Fields.NAME, new KeywordAnalyzer());
|
||||
//fieldAnalyzers.put(Fields.VERSION, new KeywordAnalyzer());
|
||||
fieldAnalyzers.put(Fields.VERSION, new SearchVersionAnalyzer(Version.LUCENE_40));
|
||||
productSearchFieldAnalyzer = new SearchFieldAnalyzer(Version.LUCENE_40);
|
||||
vendorSearchFieldAnalyzer = new SearchFieldAnalyzer(Version.LUCENE_40);
|
||||
fieldAnalyzers.put(Fields.PRODUCT, productSearchFieldAnalyzer);
|
||||
fieldAnalyzers.put(Fields.VENDOR, vendorSearchFieldAnalyzer);
|
||||
|
||||
PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(
|
||||
new FieldAnalyzer(Version.LUCENE_40), fieldAnalyzers);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Lucene QueryParser used when querying the index
|
||||
* @return a QueryParser.
|
||||
*/
|
||||
public QueryParser createQueryParser() {
|
||||
return new QueryParser(Version.LUCENE_40, Fields.NAME, getSearchingAnalyzer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the searching analyzers
|
||||
*/
|
||||
protected void resetSearchingAnalyzer() {
|
||||
if (productSearchFieldAnalyzer != null) {
|
||||
productSearchFieldAnalyzer.clear();
|
||||
}
|
||||
if (vendorSearchFieldAnalyzer != null) {
|
||||
vendorSearchFieldAnalyzer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a CPE Entry into the Lucene index.
|
||||
*
|
||||
* @param entry a CPE entry.
|
||||
* @throws CorruptIndexException is thrown if the index is corrupt.
|
||||
* @throws IOException is thrown if an IOException occurs.
|
||||
*/
|
||||
public void saveEntry(Entry entry) throws CorruptIndexException, IOException {
|
||||
Document doc = convertEntryToDoc(entry);
|
||||
//Term term = new Term(Fields.NVDID, LuceneUtils.escapeLuceneQuery(entry.getNvdId()));
|
||||
Term term = new Term(Fields.NAME, entry.getName());
|
||||
indexWriter.updateDocument(term, doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a CPE entry into a Lucene Document.
|
||||
*
|
||||
* @param entry a CPE Entry.
|
||||
* @return a Lucene Document containing a CPE Entry.
|
||||
*/
|
||||
protected Document convertEntryToDoc(Entry entry) {
|
||||
Document doc = new Document();
|
||||
|
||||
Field name = new StoredField(Fields.NAME, entry.getName());
|
||||
doc.add(name);
|
||||
|
||||
Field vendor = new TextField(Fields.VENDOR, entry.getVendor(), Field.Store.NO);
|
||||
vendor.setBoost(5.0F);
|
||||
doc.add(vendor);
|
||||
|
||||
Field product = new TextField(Fields.PRODUCT, entry.getProduct(), Field.Store.NO);
|
||||
product.setBoost(5.0F);
|
||||
doc.add(product);
|
||||
|
||||
//TODO revision should likely be its own field
|
||||
if (entry.getVersion() != null) {
|
||||
Field version = null;
|
||||
if (entry.getRevision() != null) {
|
||||
version = new TextField(Fields.VERSION, entry.getVersion() + " "
|
||||
+ entry.getRevision(), Field.Store.NO);
|
||||
} else {
|
||||
version = new TextField(Fields.VERSION, entry.getVersion(),
|
||||
Field.Store.NO);
|
||||
}
|
||||
version.setBoost(0.8F);
|
||||
doc.add(version);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.cpe</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains classes for working with the CPE Lucene Index.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
75
src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java
Normal file
75
src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cwe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class CweDB {
|
||||
|
||||
private CweDB() {
|
||||
//empty constructor for utility class
|
||||
}
|
||||
private static final HashMap<String, String> CWE = loadData();
|
||||
|
||||
private static HashMap<String, String> loadData() {
|
||||
ObjectInputStream oin = null;
|
||||
try {
|
||||
String filePath = "data/cwe.hashmap.serialized";
|
||||
InputStream input = CweDB.class.getClassLoader().getResourceAsStream(filePath);
|
||||
oin = new ObjectInputStream(input);
|
||||
@SuppressWarnings("unchecked")
|
||||
HashMap<String, String> data = (HashMap<String, String>) oin.readObject();
|
||||
return data;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
Logger.getLogger(CweDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CweDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
if (oin != null) {
|
||||
try {
|
||||
oin.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CweDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the full CWE name from the CWE ID.</p>
|
||||
* @param cweId te CWE ID
|
||||
* @return the full name of the CWE
|
||||
*/
|
||||
public static String getCweName(String cweId) {
|
||||
if (cweId != null) {
|
||||
return CWE.get(cweId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cwe;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
/**
|
||||
* A SAX Handler that will parse the CWE XML.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class CweHandler extends DefaultHandler {
|
||||
|
||||
private HashMap<String, String> cwe = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Returns the HashMap of CWE entries (CWE-ID, Full CWE Name).
|
||||
* @return a HashMap of CWE entries <String, String>
|
||||
*/
|
||||
public HashMap<String, String> getCwe() {
|
||||
return cwe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
|
||||
if ("Weakness".equals(qName) || "Category".equals(qName)) {
|
||||
String id = "CWE-" + attributes.getValue("ID");
|
||||
String name = attributes.getValue("Name");
|
||||
cwe.put(id, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.cwe</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains classes for working with the CWE Database.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.owasp.dependencycheck.data.cwe;
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.queryparser.classic.ParseException;
|
||||
import org.apache.lucene.queryparser.classic.QueryParser;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.LockObtainFailedException;
|
||||
import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* The base Index for other index objects. Implements the open and close
|
||||
* methods.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public abstract class AbstractIndex {
|
||||
|
||||
/**
|
||||
* The Lucene directory containing the index.
|
||||
*/
|
||||
protected Directory directory = null;
|
||||
/**
|
||||
* The IndexWriter for the Lucene index.
|
||||
*/
|
||||
protected IndexWriter indexWriter = null;
|
||||
/**
|
||||
* The Lucene IndexReader.
|
||||
*/
|
||||
private IndexReader indexReader = null;
|
||||
/**
|
||||
* The Lucene IndexSearcher.
|
||||
*/
|
||||
private IndexSearcher indexSearcher = null;
|
||||
/**
|
||||
* The Lucene Analyzer used for Indexing.
|
||||
*/
|
||||
private Analyzer indexingAnalyzer = null;
|
||||
/**
|
||||
* The Lucene Analyzer used for Searching
|
||||
*/
|
||||
private Analyzer searchingAnalyzer = null;
|
||||
/**
|
||||
* The Lucene QueryParser used for Searching
|
||||
*/
|
||||
private QueryParser queryParser = null;
|
||||
/**
|
||||
* Indicates whether or not the Lucene Index is open.
|
||||
*/
|
||||
private boolean indexOpen = false;
|
||||
|
||||
/**
|
||||
* Opens the CPE Index.
|
||||
*
|
||||
* @throws IOException is thrown if an IOException occurs opening the index.
|
||||
*/
|
||||
public void open() throws IOException {
|
||||
directory = this.getDirectory();
|
||||
indexingAnalyzer = this.getIndexingAnalyzer();
|
||||
searchingAnalyzer = this.getSearchingAnalyzer();
|
||||
indexOpen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the CPE Index.
|
||||
*/
|
||||
public void close() {
|
||||
if (indexWriter != null) {
|
||||
try {
|
||||
indexWriter.commit();
|
||||
} catch (CorruptIndexException ex) {
|
||||
Logger.getLogger(AbstractIndex.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(AbstractIndex.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
try {
|
||||
indexWriter.close(true);
|
||||
} catch (CorruptIndexException ex) {
|
||||
Logger.getLogger(AbstractIndex.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(AbstractIndex.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
indexWriter = null;
|
||||
}
|
||||
}
|
||||
if (indexSearcher != null) {
|
||||
indexSearcher = null;
|
||||
}
|
||||
|
||||
if (indexingAnalyzer != null) {
|
||||
indexingAnalyzer.close();
|
||||
indexingAnalyzer = null;
|
||||
}
|
||||
|
||||
if (searchingAnalyzer != null) {
|
||||
searchingAnalyzer.close();
|
||||
searchingAnalyzer = null;
|
||||
}
|
||||
|
||||
try {
|
||||
directory.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(AbstractIndex.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
directory = null;
|
||||
}
|
||||
indexOpen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the data source - is the index open.
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return indexOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the Lucene Index Writer.
|
||||
*
|
||||
* @throws CorruptIndexException is thrown if the Lucene index is corrupt.
|
||||
* @throws IOException is thrown if an IOException occurs opening the index.
|
||||
*/
|
||||
public void openIndexWriter() throws CorruptIndexException, IOException {
|
||||
if (!isOpen()) {
|
||||
open();
|
||||
}
|
||||
IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_40, indexingAnalyzer);
|
||||
indexWriter = new IndexWriter(directory, conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the IndexWriter for the Lucene Index.
|
||||
*
|
||||
* @return an IndexWriter.
|
||||
* @throws CorruptIndexException is thrown if the Lucene Index is corrupt.
|
||||
* @throws LockObtainFailedException is thrown if there is an exception
|
||||
* obtaining a lock on the Lucene index.
|
||||
* @throws IOException is thrown if an IOException occurs opening the index.
|
||||
*/
|
||||
public IndexWriter getIndexWriter() throws CorruptIndexException, LockObtainFailedException, IOException {
|
||||
if (indexWriter == null) {
|
||||
openIndexWriter();
|
||||
}
|
||||
return indexWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the Lucene Index for reading.
|
||||
*
|
||||
* @throws CorruptIndexException is thrown if the index is corrupt.
|
||||
* @throws IOException is thrown if there is an exception reading the index.
|
||||
*/
|
||||
public void openIndexReader() throws CorruptIndexException, IOException {
|
||||
if (!isOpen()) {
|
||||
open();
|
||||
}
|
||||
//indexReader = IndexReader.open(directory, true);
|
||||
indexReader = DirectoryReader.open(directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an IndexSearcher for the Lucene Index.
|
||||
*
|
||||
* @return an IndexSearcher.
|
||||
* @throws CorruptIndexException is thrown if the index is corrupt.
|
||||
* @throws IOException is thrown if there is an exception reading the index.
|
||||
*/
|
||||
protected IndexSearcher getIndexSearcher() throws CorruptIndexException, IOException {
|
||||
if (indexReader == null) {
|
||||
openIndexReader();
|
||||
}
|
||||
if (indexSearcher == null) {
|
||||
indexSearcher = new IndexSearcher(indexReader);
|
||||
}
|
||||
return indexSearcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Analyzer to be used when indexing.
|
||||
*
|
||||
* @return an Analyzer.
|
||||
*/
|
||||
public Analyzer getIndexingAnalyzer() {
|
||||
if (indexingAnalyzer == null) {
|
||||
indexingAnalyzer = createIndexingAnalyzer();
|
||||
}
|
||||
return indexingAnalyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an analyzer used for searching the index
|
||||
* @return a lucene analyzer
|
||||
*/
|
||||
protected Analyzer getSearchingAnalyzer() {
|
||||
if (searchingAnalyzer == null) {
|
||||
searchingAnalyzer = createSearchingAnalyzer();
|
||||
}
|
||||
return searchingAnalyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a query parser
|
||||
* @return a query parser
|
||||
*/
|
||||
protected QueryParser getQueryParser() {
|
||||
if (queryParser == null) {
|
||||
queryParser = createQueryParser();
|
||||
}
|
||||
return queryParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the index using the given search string
|
||||
* @param searchString the query text
|
||||
* @param maxQueryResults the maximum number of documents to return
|
||||
* @return the TopDocs found by the search
|
||||
* @throws ParseException thrown when the searchString is invalid
|
||||
* @throws IOException is thrown if there is an issue with the underlying Index
|
||||
*/
|
||||
public TopDocs search(String searchString, int maxQueryResults) throws ParseException, IOException {
|
||||
|
||||
QueryParser parser = getQueryParser();
|
||||
|
||||
Query query = parser.parse(searchString);
|
||||
|
||||
resetSearchingAnalyzer();
|
||||
|
||||
IndexSearcher is = getIndexSearcher();
|
||||
|
||||
TopDocs docs = is.search(query, maxQueryResults);
|
||||
|
||||
return docs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the index using the given query
|
||||
* @param query the query used to search the index
|
||||
* @param maxQueryResults the max number of results to return
|
||||
* @return the TopDocs found be the query
|
||||
* @throws CorruptIndexException thrown if the Index is corrupt
|
||||
* @throws IOException thrown if there is an IOException
|
||||
*/
|
||||
public TopDocs search(Query query, int maxQueryResults) throws CorruptIndexException, IOException {
|
||||
IndexSearcher is = getIndexSearcher();
|
||||
return is.search(query, maxQueryResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a document from the Index
|
||||
* @param documentId the id of the document to retrieve
|
||||
* @return the Document
|
||||
* @throws IOException thrown if there is an IOException
|
||||
*/
|
||||
public Document getDocument(int documentId) throws IOException {
|
||||
IndexSearcher is = getIndexSearcher();
|
||||
return is.doc(documentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the directory that contains the Lucene Index
|
||||
*
|
||||
* @return a Lucene Directory
|
||||
* @throws IOException is thrown when an IOException occurs
|
||||
*/
|
||||
public abstract Directory getDirectory() throws IOException;
|
||||
|
||||
/**
|
||||
* Creates the Lucene Analyzer used when indexing
|
||||
*
|
||||
* @return a Lucene Analyzer
|
||||
*/
|
||||
public abstract Analyzer createIndexingAnalyzer();
|
||||
|
||||
/**
|
||||
* Creates the Lucene Analyzer used when querying the index
|
||||
*
|
||||
* @return a Lucene Analyzer
|
||||
*/
|
||||
public abstract Analyzer createSearchingAnalyzer();
|
||||
|
||||
/**
|
||||
* Creates the Lucene QueryParser used when querying the index
|
||||
* @return a QueryParser
|
||||
*/
|
||||
public abstract QueryParser createQueryParser();
|
||||
|
||||
/**
|
||||
* Resets the searching analyzers
|
||||
*/
|
||||
protected abstract void resetSearchingAnalyzer();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
import org.apache.lucene.search.similarities.DefaultSimilarity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class DependencySimilarity extends DefaultSimilarity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* <p>Override the default idf implementation so that frequency within all
|
||||
* document is ignored.</p>
|
||||
*
|
||||
* See <a
|
||||
* href="http://www.lucenetutorial.com/advanced-topics/scoring.html">this
|
||||
* article</a> for more details.
|
||||
*
|
||||
* @param docFreq - the number of documents which contain the term
|
||||
* @param numDocs - the total number of documents in the collection
|
||||
* @return 1
|
||||
*/
|
||||
@Override
|
||||
public float idf(long docFreq, long numDocs) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
import java.io.Reader;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.core.WhitespaceTokenizer;
|
||||
import org.apache.lucene.analysis.core.LowerCaseFilter;
|
||||
import org.apache.lucene.analysis.core.StopAnalyzer;
|
||||
import org.apache.lucene.analysis.core.StopFilter;
|
||||
import org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter;
|
||||
import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* <p>A Lucene Analyzer that utilizes the WhitespaceTokenizer, WordDelimiterFilter,
|
||||
* LowerCaseFilter, and StopFilter. The intended purpose of this Analyzer is
|
||||
* to index the CPE fields vendor and product.</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class FieldAnalyzer extends Analyzer {
|
||||
|
||||
/**
|
||||
* The Lucene Version used
|
||||
*/
|
||||
private Version version = null;
|
||||
|
||||
/**
|
||||
* Creates a new FieldAnalyzer
|
||||
* @param version the Lucene version
|
||||
*/
|
||||
public FieldAnalyzer(Version version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the TokenStreamComponents
|
||||
*
|
||||
* @param fieldName the field name being analyzed
|
||||
* @param reader the reader containing the input
|
||||
* @return the TokenStreamComponents
|
||||
*/
|
||||
@Override
|
||||
protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
|
||||
Tokenizer source = new WhitespaceTokenizer(version, reader);
|
||||
|
||||
TokenStream stream = source;
|
||||
|
||||
stream = new WordDelimiterFilter(stream,
|
||||
WordDelimiterFilter.CATENATE_WORDS
|
||||
| WordDelimiterFilter.GENERATE_WORD_PARTS
|
||||
| WordDelimiterFilter.GENERATE_NUMBER_PARTS
|
||||
| WordDelimiterFilter.PRESERVE_ORIGINAL
|
||||
| WordDelimiterFilter.SPLIT_ON_CASE_CHANGE
|
||||
| WordDelimiterFilter.SPLIT_ON_NUMERICS
|
||||
| WordDelimiterFilter.STEM_ENGLISH_POSSESSIVE, null);
|
||||
|
||||
stream = new LowerCaseFilter(version, stream);
|
||||
stream = new StopFilter(version, stream, StopAnalyzer.ENGLISH_STOP_WORDS_SET);
|
||||
|
||||
return new TokenStreamComponents(source, stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
/**
|
||||
* <p>Lucene utils is a set of utilize written to make constructing Lucene
|
||||
* queries simpler.</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public final class LuceneUtils {
|
||||
|
||||
/**
|
||||
* Private constructor as this is a utility class.
|
||||
*/
|
||||
private LuceneUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the text to the supplied StringBuilder escaping Lucene control
|
||||
* characters in the process.
|
||||
*
|
||||
* @param buf a StringBuilder to append the escaped text to
|
||||
* @param text the data to be escaped
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
public static void appendEscapedLuceneQuery(StringBuilder buf,
|
||||
final CharSequence text) {
|
||||
|
||||
if (text == null || buf == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
switch (c) {
|
||||
case '+':
|
||||
case '-':
|
||||
case '&':
|
||||
case '|':
|
||||
case '!':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '[':
|
||||
case ']':
|
||||
case '^':
|
||||
case '"':
|
||||
case '~':
|
||||
case '*':
|
||||
case '?':
|
||||
case ':':
|
||||
case '\\': //it is supposed to fall through here
|
||||
buf.append('\\');
|
||||
default:
|
||||
buf.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the text passed in so that it is treated as data instead of
|
||||
* control characters.
|
||||
*
|
||||
* @param text data to be escaped
|
||||
* @return the escaped text.
|
||||
*/
|
||||
public static String escapeLuceneQuery(final CharSequence text) {
|
||||
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int size = text.length();
|
||||
size = size >> 1;
|
||||
StringBuilder buf = new StringBuilder(size);
|
||||
|
||||
appendEscapedLuceneQuery(buf, text);
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
import java.io.Reader;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.core.WhitespaceTokenizer;
|
||||
import org.apache.lucene.analysis.core.LowerCaseFilter;
|
||||
import org.apache.lucene.analysis.core.StopAnalyzer;
|
||||
import org.apache.lucene.analysis.core.StopFilter;
|
||||
import org.apache.lucene.analysis.miscellaneous.WordDelimiterFilter;
|
||||
import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* A Lucene field analyzer used to analyzer queries against the CPE data.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class SearchFieldAnalyzer extends Analyzer {
|
||||
|
||||
/**
|
||||
* The Lucene Version used
|
||||
*/
|
||||
private Version version = null;
|
||||
/**
|
||||
* A local reference to the TokenPairConcatenatingFilter so that we
|
||||
* can clear any left over state if this analyzer is re-used.
|
||||
*/
|
||||
private TokenPairConcatenatingFilter concatenatingFilter = null;
|
||||
|
||||
/**
|
||||
* Constructs a new SearchFieldAnalyzer
|
||||
* @param version the Lucene version
|
||||
*/
|
||||
public SearchFieldAnalyzer(Version version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a the TokenStreamComponents used to analyze the stream.
|
||||
* @param fieldName the field that this lucene analyzer will process
|
||||
* @param reader a reader containing the tokens
|
||||
* @return the token stream filter chain
|
||||
*/
|
||||
@Override
|
||||
protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
|
||||
Tokenizer source = new WhitespaceTokenizer(version, reader);
|
||||
|
||||
TokenStream stream = source;
|
||||
|
||||
stream = new WordDelimiterFilter(stream,
|
||||
WordDelimiterFilter.GENERATE_WORD_PARTS
|
||||
| WordDelimiterFilter.GENERATE_NUMBER_PARTS
|
||||
| WordDelimiterFilter.PRESERVE_ORIGINAL
|
||||
| WordDelimiterFilter.SPLIT_ON_CASE_CHANGE
|
||||
| WordDelimiterFilter.SPLIT_ON_NUMERICS
|
||||
| WordDelimiterFilter.STEM_ENGLISH_POSSESSIVE, null);
|
||||
|
||||
stream = new LowerCaseFilter(version, stream);
|
||||
concatenatingFilter = new TokenPairConcatenatingFilter(stream);
|
||||
stream = concatenatingFilter;
|
||||
stream = new StopFilter(version, stream, StopAnalyzer.ENGLISH_STOP_WORDS_SET);
|
||||
|
||||
return new TokenStreamComponents(source, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Resets the analyzer and clears any internal state data that may
|
||||
* have been left-over from previous uses of the analyzer.</p>
|
||||
* <p><b>If this analyzer is re-used this method must be called between uses.</b></p>
|
||||
*/
|
||||
public void clear() {
|
||||
concatenatingFilter.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
import java.io.Reader;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.core.LowerCaseFilter;
|
||||
import org.apache.lucene.analysis.core.WhitespaceTokenizer;
|
||||
import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* SearchVersionAnalyzer is a Lucene Analyzer used to analyze version information.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class SearchVersionAnalyzer extends Analyzer {
|
||||
//TODO consider implementing payloads/custom attributes...
|
||||
// use custom attributes for major, minor, x, x, x, rcx
|
||||
// these can then be used to weight the score for searches on the version.
|
||||
// see http://lucene.apache.org/core/3_6_1/api/core/org/apache/lucene/analysis/package-summary.html#package_description
|
||||
// look at this article to implement
|
||||
// http://www.codewrecks.com/blog/index.php/2012/08/25/index-your-blog-using-tags-and-lucene-net/
|
||||
|
||||
/**
|
||||
* The Lucene Version used
|
||||
*/
|
||||
private Version version = null;
|
||||
|
||||
/**
|
||||
* Creates a new SearchVersionAnalyzer
|
||||
* @param version the Lucene version
|
||||
*/
|
||||
public SearchVersionAnalyzer(Version version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the TokenStreamComponents
|
||||
*
|
||||
* @param fieldName the field name being analyzed
|
||||
* @param reader the reader containing the input
|
||||
* @return the TokenStreamComponents
|
||||
*/
|
||||
@Override
|
||||
protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
|
||||
Tokenizer source = new WhitespaceTokenizer(version, reader);
|
||||
TokenStream stream = source;
|
||||
stream = new LowerCaseFilter(version, stream);
|
||||
stream = new VersionTokenizingFilter(stream);
|
||||
return new TokenStreamComponents(source, stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import org.apache.lucene.analysis.TokenFilter;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
|
||||
|
||||
/**
|
||||
* <p>Takes a TokenStream and adds additional tokens by concatenating pairs of words.</p>
|
||||
* <p><b>Example:</b> "Spring Framework Core" -> "Spring SpringFramework Framework FrameworkCore Core".</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public final class TokenPairConcatenatingFilter extends TokenFilter {
|
||||
|
||||
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
|
||||
private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class);
|
||||
private String previousWord = null;
|
||||
private LinkedList<String> words = null;
|
||||
|
||||
/**
|
||||
* Constructs a new TokenPairConcatenatingFilter
|
||||
* @param stream the TokenStream that this filter will process
|
||||
*/
|
||||
public TokenPairConcatenatingFilter(TokenStream stream) {
|
||||
super(stream);
|
||||
words = new LinkedList<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the underlying TokenStream and sets CharTermAttributes to
|
||||
* construct an expanded set of tokens by concatenating tokens with the
|
||||
* previous token.
|
||||
*
|
||||
* @return whether or not we have hit the end of the TokenStream
|
||||
* @throws IOException is thrown when an IOException occurs
|
||||
*/
|
||||
@Override
|
||||
public boolean incrementToken() throws IOException {
|
||||
|
||||
//collect all the terms into the words collection
|
||||
while (input.incrementToken()) {
|
||||
String word = new String(termAtt.buffer(), 0, termAtt.length());
|
||||
words.add(word);
|
||||
}
|
||||
|
||||
//if we have a previousTerm - write it out as its own token concatenated
|
||||
// with the current word (if one is available).
|
||||
if (previousWord != null && words.size() > 0) {
|
||||
String word = words.getFirst();
|
||||
clearAttributes();
|
||||
termAtt.append(previousWord).append(word);
|
||||
posIncAtt.setPositionIncrement(0);
|
||||
previousWord = null;
|
||||
return true;
|
||||
}
|
||||
//if we have words, write it out as a single token
|
||||
if (words.size() > 0) {
|
||||
String word = words.removeFirst();
|
||||
clearAttributes();
|
||||
termAtt.append(word);
|
||||
previousWord = word;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Resets the Filter and clears any internal state data that may
|
||||
* have been left-over from previous uses of the Filter.</p>
|
||||
* <p><b>If this Filter is re-used this method must be called between uses.</b></p>
|
||||
*/
|
||||
public void clear() {
|
||||
previousWord = null;
|
||||
words.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
import java.io.Reader;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.core.LowerCaseFilter;
|
||||
import org.apache.lucene.analysis.core.WhitespaceTokenizer;
|
||||
import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* VersionAnalyzer is a Lucene Analyzer used to analyze version information.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class VersionAnalyzer extends Analyzer {
|
||||
//TODO consider implementing payloads/custom attributes...
|
||||
// use custom attributes for major, minor, x, x, x, rcx
|
||||
// these can then be used to weight the score for searches on the version.
|
||||
// see http://lucene.apache.org/core/3_6_1/api/core/org/apache/lucene/analysis/package-summary.html#package_description
|
||||
// look at this article to implement
|
||||
// http://www.codewrecks.com/blog/index.php/2012/08/25/index-your-blog-using-tags-and-lucene-net/
|
||||
|
||||
/**
|
||||
* The Lucene Version used
|
||||
*/
|
||||
private Version version = null;
|
||||
|
||||
/**
|
||||
* Creates a new VersionAnalyzer
|
||||
* @param version the Lucene version
|
||||
*/
|
||||
public VersionAnalyzer(Version version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the TokenStreamComponents
|
||||
*
|
||||
* @param fieldName the field name being analyzed
|
||||
* @param reader the reader containing the input
|
||||
* @return the TokenStreamComponents
|
||||
*/
|
||||
@Override
|
||||
protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
|
||||
Tokenizer source = new WhitespaceTokenizer(version, reader);
|
||||
TokenStream stream = source;
|
||||
stream = new LowerCaseFilter(version, stream);
|
||||
return new TokenStreamComponents(source, stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import org.apache.lucene.analysis.TokenFilter;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
|
||||
/**
|
||||
* <p>Takes a TokenStream and splits or adds tokens to correctly index version numbers.</p>
|
||||
* <p><b>Example:</b> "3.0.0.RELEASE" -> "3 3.0 3.0.0 RELEASE 3.0.0.RELEASE".</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public final class VersionTokenizingFilter extends TokenFilter {
|
||||
|
||||
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
|
||||
/**
|
||||
* A collection of tokens to add to the stream.
|
||||
*/
|
||||
protected LinkedList<String> tokens = null;
|
||||
|
||||
/**
|
||||
* Constructs a new VersionTokenizingFilter
|
||||
* @param stream the TokenStream that this filter will process
|
||||
*/
|
||||
public VersionTokenizingFilter(TokenStream stream) {
|
||||
super(stream);
|
||||
tokens = new LinkedList<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the underlying TokenStream and sets CharTermAttributes to
|
||||
* construct an expanded set of tokens by concatenating tokens with the
|
||||
* previous token.
|
||||
*
|
||||
* @return whether or not we have hit the end of the TokenStream
|
||||
* @throws IOException is thrown when an IOException occurs
|
||||
*/
|
||||
@Override
|
||||
public boolean incrementToken() throws IOException {
|
||||
if (tokens.size() == 0 && input.incrementToken()) {
|
||||
String version = new String(termAtt.buffer(), 0, termAtt.length());
|
||||
analyzeVersion(version);
|
||||
}
|
||||
return addTerm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a term, if one exists, from the tokens collection.
|
||||
* @return whether or not a new term was added
|
||||
*/
|
||||
private boolean addTerm() {
|
||||
boolean termAdded = tokens.size() > 0;
|
||||
if (termAdded) {
|
||||
String version = tokens.pop();
|
||||
clearAttributes();
|
||||
termAtt.append(version);
|
||||
}
|
||||
return termAdded;
|
||||
}
|
||||
|
||||
//major.minor[.maintenance[.build]]
|
||||
private void analyzeVersion(String version) {
|
||||
//todo should we also be splitting on dash or underscore? we would need
|
||||
// to incorporate the dash or underscore back in...
|
||||
String[] versionParts = version.split("\\.");
|
||||
String dottedVersion = null;
|
||||
for (String current : versionParts) {
|
||||
if (!current.matches("^/d+$")) {
|
||||
tokens.add(current);
|
||||
}
|
||||
if (dottedVersion == null) {
|
||||
dottedVersion = current;
|
||||
} else {
|
||||
dottedVersion = dottedVersion + "." + current;
|
||||
}
|
||||
tokens.add(dottedVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.lucene</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains classes used to work with the Lucene Indexes.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
/**
|
||||
* An exception used to indicate the db4o database is corrupt.
|
||||
* This could be due to invalid data or a complete failure of the db.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
class CorruptDatabaseException extends DatabaseException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an CorruptDatabaseException
|
||||
*
|
||||
* @param msg the exception message
|
||||
*/
|
||||
public CorruptDatabaseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an CorruptDatabaseException
|
||||
*
|
||||
* @param msg the exception message
|
||||
* @param ex the cause of the exception
|
||||
*/
|
||||
public CorruptDatabaseException(String msg, Exception ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
472
src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java
Normal file
472
src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java
Normal file
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.data.cpe.Entry;
|
||||
import org.owasp.dependencycheck.data.cwe.CweDB;
|
||||
import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* The database holding information about the NVD CVE data.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class CveDB {
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="Constants to create, maintain, and retrieve data from the CVE Database">
|
||||
/**
|
||||
* SQL Statement to create an index on the reference table
|
||||
*/
|
||||
public static final String CREATE_INDEX_IDXREFERENCE = "CREATE INDEX IF NOT EXISTS idxReference ON reference(cveid)";
|
||||
/**
|
||||
* SQL Statement to create an index on the software for finding CVE entries based on CPE data
|
||||
*/
|
||||
public static final String CREATE_INDEX_IDXSOFTWARE = "CREATE INDEX IF NOT EXISTS idxSoftware ON software(product, vendor, version)";
|
||||
/**
|
||||
* SQL Statement to create an index for retrieving software by CVEID
|
||||
*/
|
||||
public static final String CREATE_INDEX_IDXSOFTWARECVE = "CREATE INDEX IF NOT EXISTS idxSoftwareCve ON software(cveid)";
|
||||
/**
|
||||
* SQL Statement to create an index on the vulnerability table
|
||||
*/
|
||||
public static final String CREATE_INDEX_IDXVULNERABILITY = "CREATE INDEX IF NOT EXISTS idxVulnerability ON vulnerability(cveid)";
|
||||
/**
|
||||
* SQL Statement to create the reference table
|
||||
*/
|
||||
public static final String CREATE_TABLE_REFERENCE = "CREATE TABLE IF NOT EXISTS reference (cveid CHAR(13), "
|
||||
+ "name varchar(1000), url varchar(1000), source varchar(255))";
|
||||
/**
|
||||
* SQL Statement to create the software table
|
||||
*/
|
||||
public static final String CREATE_TABLE_SOFTWARE = "CREATE TABLE IF NOT EXISTS software (cveid CHAR(13), cpe varchar(500), "
|
||||
+ "vendor varchar(255), product varchar(255), version varchar(50), previousVersion varchar(50))";
|
||||
/**
|
||||
* SQL Statement to create the vulnerability table
|
||||
*/
|
||||
public static final String CREATE_TABLE_VULNERABILITY = "CREATE TABLE IF NOT EXISTS vulnerability (cveid CHAR(13) PRIMARY KEY, "
|
||||
+ "description varchar(8000), cwe varchar(10), cvssScore DECIMAL(3,1), cvssAccessVector varchar(20), "
|
||||
+ "cvssAccessComplexity varchar(20), cvssAuthentication varchar(20), cvssConfidentialityImpact varchar(20), "
|
||||
+ "cvssIntegrityImpact varchar(20), cvssAvailabilityImpact varchar(20))";
|
||||
/**
|
||||
* SQL Statement to delete references by CVEID
|
||||
*/
|
||||
public static final String DELETE_REFERENCE = "DELETE FROM reference WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to delete software by CVEID
|
||||
*/
|
||||
public static final String DELETE_SOFTWARE = "DELETE FROM software WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to delete a vulnerability by CVEID
|
||||
*/
|
||||
public static final String DELETE_VULNERABILITY = "DELETE FROM vulnerability WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to insert a new reference
|
||||
*/
|
||||
public static final String INSERT_REFERENCE = "INSERT INTO reference (cveid, name, url, source) VALUES (?, ?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to insert a new software
|
||||
*/
|
||||
public static final String INSERT_SOFTWARE = "INSERT INTO software (cveid, cpe, vendor, product, version, previousVersion) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to insert a new vulnerability
|
||||
*/
|
||||
public static final String INSERT_VULNERABILITY = "INSERT INTO vulnerability (cveid, description, cwe, cvssScore, cvssAccessVector, "
|
||||
+ "cvssAccessComplexity, cvssAuthentication, cvssConfidentialityImpact, cvssIntegrityImpact, cvssAvailabilityImpact) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to find CVE entries based on CPE data
|
||||
*/
|
||||
public static final String SELECT_CVE_FROM_SOFTWARE = "SELECT cveid FROM software WHERE Vendor = ? AND Product = ? AND "
|
||||
+ "(version = '-' OR previousVersion IS NOT NULL OR version=?)";
|
||||
/**
|
||||
* SQL Statement to select references by CVEID
|
||||
*/
|
||||
public static final String SELECT_REFERENCE = "SELECT source, name, url FROM reference WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to select software by CVEID
|
||||
*/
|
||||
public static final String SELECT_SOFTWARE = "SELECT cpe, previousVersion FROM software WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to select a vulnerability by CVEID
|
||||
*/
|
||||
public static final String SELECT_VULNERABILITY = "SELECT cveid, description, cwe, cvssScore, cvssAccessVector, cvssAccessComplexity, "
|
||||
+ "cvssAuthentication, cvssConfidentialityImpact, cvssIntegrityImpact, cvssAvailabilityImpact FROM vulnerability WHERE cveid = ?";
|
||||
//</editor-fold>
|
||||
//<editor-fold defaultstate="collapsed" desc="Collection of CallableStatements to work with the DB">
|
||||
/**
|
||||
* delete reference - parameters (cveid)
|
||||
*/
|
||||
private CallableStatement deleteReferences = null;
|
||||
/**
|
||||
* delete software - parameters (cveid)
|
||||
*/
|
||||
private CallableStatement deleteSoftware = null;
|
||||
/**
|
||||
* delete vulnerability - parameters (cveid)
|
||||
*/
|
||||
private CallableStatement deleteVulnerabilities = null;
|
||||
/**
|
||||
* insert reference - parameters (cveid, name, url, source)
|
||||
*/
|
||||
private CallableStatement insertReference = null;
|
||||
/**
|
||||
* insert software - parameters (cveid, cpe, vendor, product, version, previousVersion)
|
||||
*/
|
||||
private CallableStatement insertSoftware = null;
|
||||
/**
|
||||
* insert vulnerability - parameters (cveid, description, cwe, cvssScore, cvssAccessVector,
|
||||
* cvssAccessComplexity, cvssAuthentication, cvssConfidentialityImpact, cvssIntegrityImpact, cvssAvailabilityImpact)
|
||||
*/
|
||||
private CallableStatement insertVulnerability = null;
|
||||
/**
|
||||
* select cve from software - parameters (vendor, product, version)
|
||||
*/
|
||||
private CallableStatement selectCveFromSoftware = null;
|
||||
/**
|
||||
* select vulnerability - parameters (cveid)
|
||||
*/
|
||||
private CallableStatement selectVulnerability = null;
|
||||
/**
|
||||
* select reference - parameters (cveid)
|
||||
*/
|
||||
private CallableStatement selectReferences = null;
|
||||
/**
|
||||
* select software - parameters (cveid)
|
||||
*/
|
||||
private CallableStatement selectSoftware = null;
|
||||
//</editor-fold>
|
||||
/**
|
||||
* Database connection
|
||||
*/
|
||||
protected Connection conn = null;
|
||||
|
||||
/**
|
||||
* Opens the database connection. If the database does not exist, it will
|
||||
* create a new one.
|
||||
*
|
||||
* @throws IOException thrown if there is an IO Exception
|
||||
* @throws SQLException thrown if there is a SQL Exception
|
||||
* @throws DatabaseException thrown if there is an error initializing a new database
|
||||
*/
|
||||
public void open() throws IOException, SQLException, DatabaseException {
|
||||
String fileName = CveDB.getDataDirectory().getCanonicalPath()
|
||||
+ File.separator
|
||||
+ "cve";
|
||||
File f = new File(fileName);
|
||||
boolean createTables = !f.exists();
|
||||
String connStr = "jdbc:h2:file:" + fileName;
|
||||
conn = DriverManager.getConnection(connStr, "sa", "");
|
||||
if (createTables) {
|
||||
createTables();
|
||||
}
|
||||
buildStatements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the object and ensures that "close" has been called.
|
||||
* @throws Throwable thrown if there is a problem
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
close();
|
||||
super.finalize(); //not necessary if extending Object.
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the DB4O database. Close should be called on
|
||||
* this object when it is done being used.
|
||||
*/
|
||||
public void close() {
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
conn = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the vulnerabilities associated with the specified CPE cpe.
|
||||
*
|
||||
* @param cpeStr the CPE cpe name
|
||||
* @return a list of Vulnerabilities
|
||||
* @throws DatabaseException thrown if there is an exception retrieving data
|
||||
*/
|
||||
public List<Vulnerability> getVulnerabilities(String cpeStr) throws DatabaseException {
|
||||
ResultSet rs = null;
|
||||
final Entry cpe = new Entry();
|
||||
try {
|
||||
cpe.parseName(cpeStr);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
List<Vulnerability> vulnerabilities = new ArrayList<Vulnerability>();
|
||||
|
||||
try {
|
||||
selectCveFromSoftware.setString(1, cpe.getVendor());
|
||||
selectCveFromSoftware.setString(2, cpe.getProduct());
|
||||
selectCveFromSoftware.setString(3, cpe.getVersion());
|
||||
rs = selectCveFromSoftware.executeQuery();
|
||||
while (rs.next()) {
|
||||
Vulnerability v = getVulnerability(rs.getString("cveid"));
|
||||
vulnerabilities.add(v);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Exception retrieving vulnerability for " + cpeStr, ex);
|
||||
} finally {
|
||||
if (rs != null) {
|
||||
try {
|
||||
rs.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return vulnerabilities;
|
||||
}
|
||||
|
||||
private Vulnerability getVulnerability(String cve) throws DatabaseException {
|
||||
ResultSet rsV = null;
|
||||
ResultSet rsR = null;
|
||||
ResultSet rsS = null;
|
||||
Vulnerability vuln = null;
|
||||
try {
|
||||
selectVulnerability.setString(1, cve);
|
||||
rsV = selectVulnerability.executeQuery();
|
||||
if (rsV.next()) {
|
||||
vuln = new Vulnerability();
|
||||
vuln.setName(cve);
|
||||
vuln.setDescription(rsV.getString(2));
|
||||
String cwe = rsV.getString(3);
|
||||
if (cwe != null) {
|
||||
String name = CweDB.getCweName(cwe);
|
||||
if (name != null) {
|
||||
cwe += " " + name;
|
||||
}
|
||||
}
|
||||
vuln.setCwe(cwe);
|
||||
vuln.setCvssScore(rsV.getFloat(4));
|
||||
vuln.setCvssAccessVector(rsV.getString(5));
|
||||
vuln.setCvssAccessComplexity(rsV.getString(6));
|
||||
vuln.setCvssAuthentication(rsV.getString(7));
|
||||
vuln.setCvssConfidentialityImpact(rsV.getString(8));
|
||||
vuln.setCvssIntegrityImpact(rsV.getString(9));
|
||||
vuln.setCvssAvailabilityImpact(rsV.getString(10));
|
||||
|
||||
selectReferences.setString(1, cve);
|
||||
rsR = selectReferences.executeQuery();
|
||||
while (rsR.next()) {
|
||||
vuln.addReference(rsR.getString(1), rsR.getString(2), rsR.getString(3));
|
||||
}
|
||||
selectSoftware.setString(1, cve);
|
||||
rsS = selectSoftware.executeQuery();
|
||||
while (rsS.next()) {
|
||||
String cpe = rsS.getString(1);
|
||||
String prevVers = rsS.getString(2);
|
||||
if (prevVers == null) {
|
||||
vuln.addVulnerableSoftware(cpe);
|
||||
} else {
|
||||
vuln.addVulnerableSoftware(cpe, prevVers);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Error retrieving " + cve, ex);
|
||||
} finally {
|
||||
if (rsV != null) {
|
||||
try {
|
||||
rsV.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
if (rsR != null) {
|
||||
try {
|
||||
rsR.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
if (rsS != null) {
|
||||
try {
|
||||
rsS.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return vuln;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the vulnerability within the database. If the vulnerability does not
|
||||
* exist it will be added.
|
||||
*
|
||||
* @param vuln the vulnerability to add to the database
|
||||
* @throws DatabaseException is thrown if the database
|
||||
*/
|
||||
public void updateVulnerability(Vulnerability vuln) throws DatabaseException {
|
||||
try {
|
||||
// first delete any existing vulnerability info.
|
||||
deleteReferences.setString(1, vuln.getName());
|
||||
deleteReferences.execute();
|
||||
deleteSoftware.setString(1, vuln.getName());
|
||||
deleteSoftware.execute();
|
||||
deleteVulnerabilities.setString(1, vuln.getName());
|
||||
deleteVulnerabilities.execute();
|
||||
|
||||
insertVulnerability.setString(1, vuln.getName());
|
||||
insertVulnerability.setString(2, vuln.getDescription());
|
||||
insertVulnerability.setString(3, vuln.getCwe());
|
||||
insertVulnerability.setFloat(4, vuln.getCvssScore());
|
||||
insertVulnerability.setString(5, vuln.getCvssAccessVector());
|
||||
insertVulnerability.setString(6, vuln.getCvssAccessComplexity());
|
||||
insertVulnerability.setString(7, vuln.getCvssAuthentication());
|
||||
insertVulnerability.setString(8, vuln.getCvssConfidentialityImpact());
|
||||
insertVulnerability.setString(9, vuln.getCvssIntegrityImpact());
|
||||
insertVulnerability.setString(10, vuln.getCvssAvailabilityImpact());
|
||||
insertVulnerability.execute();
|
||||
|
||||
insertReference.setString(1, vuln.getName());
|
||||
for (Reference r : vuln.getReferences()) {
|
||||
insertReference.setString(2, r.getName());
|
||||
insertReference.setString(3, r.getUrl());
|
||||
insertReference.setString(4, r.getSource());
|
||||
insertReference.execute();
|
||||
}
|
||||
insertSoftware.setString(1, vuln.getName());
|
||||
for (VulnerableSoftware s : vuln.getVulnerableSoftware()) {
|
||||
//cveid, cpe, vendor, product, version, previousVersion
|
||||
insertSoftware.setString(2, s.getName());
|
||||
insertSoftware.setString(3, s.getVendor());
|
||||
insertSoftware.setString(4, s.getProduct());
|
||||
insertSoftware.setString(5, s.getVersion());
|
||||
if (s.hasPreviousVersion()) {
|
||||
insertSoftware.setString(6, s.getPreviousVersion());
|
||||
} else {
|
||||
insertSoftware.setNull(6, java.sql.Types.VARCHAR);
|
||||
}
|
||||
insertSoftware.execute();
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new DatabaseException("Error updating '" + vuln.getName() + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the directory that the JAR file exists in so that
|
||||
* we can ensure we always use a common data directory.
|
||||
*
|
||||
* @return the data directory for this index.
|
||||
* @throws IOException is thrown if an IOException occurs of course...
|
||||
*/
|
||||
public static File getDataDirectory() throws IOException {
|
||||
String fileName = Settings.getString(Settings.KEYS.CVE_INDEX);
|
||||
String filePath = CveDB.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
String decodedPath = URLDecoder.decode(filePath, "UTF-8");
|
||||
File exePath = new File(decodedPath);
|
||||
|
||||
if (exePath.getName().toLowerCase().endsWith(".jar")) {
|
||||
exePath = exePath.getParentFile();
|
||||
} else {
|
||||
exePath = new File(".");
|
||||
}
|
||||
File path = new File(exePath.getCanonicalFile() + File.separator + fileName);
|
||||
path = new File(path.getCanonicalPath());
|
||||
|
||||
if (!path.exists()) {
|
||||
if (!path.mkdirs()) {
|
||||
throw new IOException("Unable to create NVD CVE Data directory");
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database structure (tables and indexes) to store the CVE data
|
||||
*
|
||||
* @throws SQLException thrown if there is a sql exception
|
||||
* @throws DatabaseException thrown if there is a database exception
|
||||
*/
|
||||
protected void createTables() throws SQLException, DatabaseException {
|
||||
Statement statement = null;
|
||||
try {
|
||||
statement = conn.createStatement();
|
||||
statement.execute(CREATE_TABLE_VULNERABILITY);
|
||||
statement.execute(CREATE_TABLE_REFERENCE);
|
||||
statement.execute(CREATE_TABLE_SOFTWARE);
|
||||
statement.execute(CREATE_INDEX_IDXSOFTWARE);
|
||||
statement.execute(CREATE_INDEX_IDXREFERENCE);
|
||||
statement.execute(CREATE_INDEX_IDXVULNERABILITY);
|
||||
statement.execute(CREATE_INDEX_IDXSOFTWARECVE);
|
||||
} finally {
|
||||
if (statement != null) {
|
||||
try {
|
||||
statement.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the CallableStatements used by the application.
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
private void buildStatements() throws DatabaseException {
|
||||
try {
|
||||
deleteReferences = conn.prepareCall(DELETE_REFERENCE);
|
||||
deleteSoftware = conn.prepareCall(DELETE_SOFTWARE);
|
||||
deleteVulnerabilities = conn.prepareCall(DELETE_VULNERABILITY);
|
||||
insertReference = conn.prepareCall(INSERT_REFERENCE);
|
||||
insertSoftware = conn.prepareCall(INSERT_SOFTWARE);
|
||||
insertVulnerability = conn.prepareCall(INSERT_VULNERABILITY);
|
||||
selectCveFromSoftware = conn.prepareCall(SELECT_CVE_FROM_SOFTWARE);
|
||||
selectVulnerability = conn.prepareCall(SELECT_VULNERABILITY);
|
||||
selectReferences = conn.prepareCall(SELECT_REFERENCE);
|
||||
selectSoftware = conn.prepareCall(SELECT_SOFTWARE);
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Unable to prepare statements", ex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
/**
|
||||
* An exception thrown if an operation against the database fails.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class DatabaseException extends Exception {
|
||||
|
||||
/**
|
||||
* Creates an DatabaseException
|
||||
*
|
||||
* @param msg the exception message
|
||||
*/
|
||||
public DatabaseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an DatabaseException
|
||||
*
|
||||
* @param msg the exception message
|
||||
* @param ex the cause of the exception
|
||||
*/
|
||||
public DatabaseException(String msg, Exception ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.AnalysisException;
|
||||
import org.owasp.dependencycheck.analyzer.AnalysisPhase;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
|
||||
/**
|
||||
* NvdCveAnalyzer is a utility class that takes a project dependency and
|
||||
* attempts to discern if there is an associated CVEs. It uses the the
|
||||
* identifiers found by other analyzers to lookup the CVE data.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class NvdCveAnalyzer implements org.owasp.dependencycheck.analyzer.Analyzer {
|
||||
|
||||
/**
|
||||
* The maximum number of query results to return.
|
||||
*/
|
||||
static final int MAX_QUERY_RESULTS = 100;
|
||||
/**
|
||||
* The CVE Index.
|
||||
*/
|
||||
protected CveDB cveDB = null;
|
||||
|
||||
/**
|
||||
* Opens the data source.
|
||||
*
|
||||
* @throws SQLException thrown when there is a SQL Exception
|
||||
* @throws IOException thrown when there is an IO Exception
|
||||
* @throws DatabaseException thrown when there is a database exceptions
|
||||
*/
|
||||
public void open() throws SQLException, IOException, DatabaseException {
|
||||
cveDB = new CveDB();
|
||||
cveDB.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the data source.
|
||||
*/
|
||||
public void close() {
|
||||
cveDB.close();
|
||||
cveDB = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the data source - is the database open.
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return (cveDB != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the CVE Database is closed.
|
||||
*
|
||||
* @throws Throwable when a throwable is thrown.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
if (isOpen()) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a dependency and attempts to determine if there are any CPE
|
||||
* identifiers for this dependency.
|
||||
*
|
||||
* @param dependency The Dependency to analyze
|
||||
* @param engine The analysis engine
|
||||
* @throws AnalysisException is thrown if there is an issue analyzing the
|
||||
* dependency
|
||||
*/
|
||||
public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
for (Identifier id : dependency.getIdentifiers()) {
|
||||
if ("cpe".equals(id.getType())) {
|
||||
try {
|
||||
String value = id.getValue();
|
||||
List<Vulnerability> vulns = cveDB.getVulnerabilities(value);
|
||||
for (Vulnerability v : vulns) {
|
||||
dependency.addVulnerability(v);
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
throw new AnalysisException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true because this analyzer supports all dependency types.
|
||||
*
|
||||
* @return true.
|
||||
*/
|
||||
public Set<String> getSupportedExtensions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this analyzer.
|
||||
*
|
||||
* @return the name of this analyzer.
|
||||
*/
|
||||
public String getName() {
|
||||
return "NVD CVE Analyzer";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true because this analyzer supports all dependency types.
|
||||
*
|
||||
* @param extension the file extension of the dependency being analyzed.
|
||||
* @return true.
|
||||
*/
|
||||
public boolean supportsExtension(String extension) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the analysis phase that this analyzer should run in.
|
||||
*
|
||||
* @return the analysis phase that this analyzer should run in.
|
||||
*/
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return AnalysisPhase.FINDING_ANALYSIS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the NVD CVE Lucene Index.
|
||||
*
|
||||
* @throws Exception is thrown if there is an issue opening the index.
|
||||
*/
|
||||
public void initialize() throws Exception {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.nvdcve</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains classes used to work with the NVD CVE data.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve.xml;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.owasp.dependencycheck.data.CachedWebDataSource;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.data.cpe.Index;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class DatabaseUpdater implements CachedWebDataSource {
|
||||
|
||||
/**
|
||||
* The 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 - used to store the
|
||||
* last updated time of the Modified NVD CVE xml file.
|
||||
*/
|
||||
private static final String LAST_UPDATED_MODIFIED = "lastupdated.modified";
|
||||
/**
|
||||
* Stores the last updated time for each of the NVD CVE files. These
|
||||
* timestamps should be updated if we process the modified file within 7
|
||||
* days of the last update.
|
||||
*/
|
||||
private static final String LAST_UPDATED_BASE = "lastupdated.";
|
||||
/**
|
||||
* The current version of the database
|
||||
*/
|
||||
public static final String DATABASE_VERSION = "2.2";
|
||||
|
||||
/**
|
||||
* <p>Downloads the latest NVD CVE XML file from the web and imports it into
|
||||
* the current CVE Database.</p>
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an error updating the database
|
||||
*/
|
||||
public void update() throws UpdateException {
|
||||
try {
|
||||
Map<String, NvdCveUrl> update = updateNeeded();
|
||||
int maxUpdates = 0;
|
||||
for (NvdCveUrl cve : update.values()) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
maxUpdates += 1;
|
||||
}
|
||||
}
|
||||
if (maxUpdates > 3) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING,
|
||||
"NVD CVE requires several updates; this could take a couple of minutes.");
|
||||
}
|
||||
int count = 0;
|
||||
for (NvdCveUrl cve : update.values()) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
count += 1;
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING,
|
||||
"Updating NVD CVE ({0} of {1})", new Object[]{count, maxUpdates});
|
||||
URL url = new URL(cve.getUrl());
|
||||
File outputPath = null;
|
||||
File outputPath12 = null;
|
||||
try {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING,
|
||||
"Downloading {0}", cve.getUrl());
|
||||
|
||||
outputPath = File.createTempFile("cve" + cve.getId() + "_", ".xml");
|
||||
Downloader.fetchFile(url, outputPath, false);
|
||||
|
||||
url = new URL(cve.getOldSchemaVersionUrl());
|
||||
outputPath12 = File.createTempFile("cve_1_2_" + cve.getId() + "_", ".xml");
|
||||
Downloader.fetchFile(url, outputPath12, false);
|
||||
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING,
|
||||
"Processing {0}", cve.getUrl());
|
||||
importXML(outputPath, outputPath12);
|
||||
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING,
|
||||
"Completed updated {0} of {1}", new Object[]{count, maxUpdates});
|
||||
} catch (FileNotFoundException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (ParserConfigurationException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (SAXException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (IOException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (SQLException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (DatabaseException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} finally {
|
||||
try {
|
||||
if (outputPath != null && outputPath.exists()) {
|
||||
outputPath.delete();
|
||||
}
|
||||
} finally {
|
||||
if (outputPath != null && outputPath.exists()) {
|
||||
outputPath.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxUpdates >= 1) {
|
||||
writeLastUpdatedPropertyFile(update);
|
||||
}
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (DownloadFailedException ex) {
|
||||
throw new UpdateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the NVD CVE XML File into the Lucene Index.
|
||||
*
|
||||
* @param file the file containing the NVD CVE XML
|
||||
* @param oldVersion contains the file containing the NVD CVE XML 1.2
|
||||
*/
|
||||
private void importXML(File file, File oldVersion)
|
||||
throws ParserConfigurationException, SAXException, IOException, SQLException, DatabaseException {
|
||||
CveDB cveDB = null;
|
||||
Index cpeIndex = null;
|
||||
|
||||
try {
|
||||
cveDB = new CveDB();
|
||||
cveDB.open();
|
||||
|
||||
cpeIndex = new Index();
|
||||
cpeIndex.openIndexWriter();
|
||||
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
SAXParser saxParser = factory.newSAXParser();
|
||||
|
||||
NvdCve12Handler cve12Handler = new NvdCve12Handler();
|
||||
saxParser.parse(oldVersion, cve12Handler);
|
||||
Map<String, List<VulnerableSoftware>> prevVersionVulnMap = cve12Handler.getVulnerabilities();
|
||||
cve12Handler = null;
|
||||
|
||||
NvdCve20Handler cve20Handler = new NvdCve20Handler();
|
||||
cve20Handler.setCveDB(cveDB);
|
||||
cve20Handler.setPrevVersionVulnMap(prevVersionVulnMap);
|
||||
cve20Handler.setCpeIndex(cpeIndex);
|
||||
saxParser.parse(file, cve20Handler);
|
||||
cve20Handler = null;
|
||||
} finally {
|
||||
if (cpeIndex != null) {
|
||||
cpeIndex.close();
|
||||
cpeIndex = null;
|
||||
}
|
||||
if (cveDB != null) {
|
||||
cveDB.close();
|
||||
cveDB = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="Code to read/write properties files regarding the last update dates">
|
||||
|
||||
/**
|
||||
* Writes a properties file containing the last updated date to the
|
||||
* VULNERABLE_CPE directory.
|
||||
*
|
||||
* @param updated a map of the updated nvdcve.
|
||||
*/
|
||||
private void writeLastUpdatedPropertyFile(Map<String, NvdCveUrl> updated) throws UpdateException {
|
||||
String dir;
|
||||
try {
|
||||
|
||||
dir = CveDB.getDataDirectory().getCanonicalPath();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new UpdateException("Unable to locate last updated properties file.", ex);
|
||||
}
|
||||
File cveProp = new File(dir + File.separatorChar + UPDATE_PROPERTIES_FILE);
|
||||
Properties prop = new Properties();
|
||||
prop.put("version", DATABASE_VERSION);
|
||||
for (NvdCveUrl cve : updated.values()) {
|
||||
prop.put(LAST_UPDATED_BASE + cve.id, String.valueOf(cve.getTimestamp()));
|
||||
}
|
||||
|
||||
OutputStream os = null;
|
||||
OutputStreamWriter out = null;
|
||||
try {
|
||||
os = new FileOutputStream(cveProp);
|
||||
out = new OutputStreamWriter(os, "UTF-8");
|
||||
prop.store(out, dir);
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new UpdateException("Unable to find last updated properties file.", ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new UpdateException("Unable to update last updated properties file.", ex);
|
||||
} finally {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the index needs to be updated. This is done by fetching the
|
||||
* nvd cve meta data and checking the last update date. If the data needs to
|
||||
* be refreshed this method will return the NvdCveUrl for the files that
|
||||
* need to be updated.
|
||||
*
|
||||
* @return the NvdCveUrl of the files that need to be updated.
|
||||
* @throws MalformedURLException is thrown if the URL for the NVD CVE Meta
|
||||
* data is incorrect.
|
||||
* @throws DownloadFailedException is thrown if there is an error.
|
||||
* downloading the nvd cve download data file.
|
||||
* @throws UpdateException Is thrown if there is an issue with the last updated properties file.
|
||||
*/
|
||||
public Map<String, NvdCveUrl> updateNeeded() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
|
||||
Map<String, NvdCveUrl> currentlyPublished;
|
||||
try {
|
||||
currentlyPublished = retrieveCurrentTimestampsFromWeb();
|
||||
} catch (InvalidDataException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new DownloadFailedException("Unable to retrieve valid timestamp from nvd cve downloads page", ex);
|
||||
|
||||
} catch (InvalidSettingException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new DownloadFailedException("Invalid settings", ex);
|
||||
}
|
||||
|
||||
if (currentlyPublished == null) {
|
||||
throw new DownloadFailedException("Unable to retrieve valid timestamp from nvd cve downloads page");
|
||||
}
|
||||
String dir;
|
||||
try {
|
||||
dir = CveDB.getDataDirectory().getCanonicalPath();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new UpdateException("Unable to locate last updated properties file.", ex);
|
||||
}
|
||||
|
||||
File f = new File(dir);
|
||||
if (f.exists()) {
|
||||
File cveProp = new File(dir + File.separatorChar + UPDATE_PROPERTIES_FILE);
|
||||
if (cveProp.exists()) {
|
||||
Properties prop = new Properties();
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(cveProp);
|
||||
prop.load(is);
|
||||
|
||||
boolean deleteAndRecreate = false;
|
||||
float version = 0;
|
||||
|
||||
if (prop.getProperty("version") == null) {
|
||||
deleteAndRecreate = true;
|
||||
} else {
|
||||
try {
|
||||
version = Float.parseFloat(prop.getProperty("version"));
|
||||
float currentVersion = Float.parseFloat(DATABASE_VERSION);
|
||||
if (currentVersion > version) {
|
||||
deleteAndRecreate = true;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
deleteAndRecreate = true;
|
||||
}
|
||||
}
|
||||
if (deleteAndRecreate) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING, "Index version is old. Rebuilding the index.");
|
||||
is.close();
|
||||
//this is an old version of the lucene index - just delete it
|
||||
FileUtils.delete(f);
|
||||
|
||||
//this importer also updates the CPE index and it is also using an old version
|
||||
org.owasp.dependencycheck.data.cpe.Index cpeid = new org.owasp.dependencycheck.data.cpe.Index();
|
||||
File cpeDir = cpeid.getDataDirectory();
|
||||
FileUtils.delete(cpeDir);
|
||||
return currentlyPublished;
|
||||
}
|
||||
|
||||
long lastUpdated = Long.parseLong(prop.getProperty(LAST_UPDATED_MODIFIED));
|
||||
Date now = new Date();
|
||||
int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS);
|
||||
int maxEntries = Settings.getInt(Settings.KEYS.CVE_URL_COUNT);
|
||||
if (lastUpdated == currentlyPublished.get("modified").timestamp) {
|
||||
currentlyPublished.clear(); //we don't need to update anything.
|
||||
} else if (withinRange(lastUpdated, now.getTime(), days)) {
|
||||
currentlyPublished.get("modified").setNeedsUpdate(true);
|
||||
for (int i = 1; i <= maxEntries; i++) {
|
||||
currentlyPublished.get(String.valueOf(i)).setNeedsUpdate(false);
|
||||
}
|
||||
} else { //we figure out which of the several XML files need to be downloaded.
|
||||
currentlyPublished.get("modified").setNeedsUpdate(false);
|
||||
for (int i = 1; i <= maxEntries; i++) {
|
||||
NvdCveUrl cve = currentlyPublished.get(String.valueOf(i));
|
||||
long currentTimestamp = 0;
|
||||
try {
|
||||
currentTimestamp = Long.parseLong(prop.getProperty(LAST_UPDATED_BASE + String.valueOf(i), "0"));
|
||||
} catch (NumberFormatException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.FINEST, "Error parsing " + LAST_UPDATED_BASE
|
||||
+ String.valueOf(i) + " from nvdcve.lastupdated", ex);
|
||||
}
|
||||
if (currentTimestamp == cve.getTimestamp()) {
|
||||
cve.setNeedsUpdate(false); //they default to true.
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.FINEST, null, ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.FINEST, null, ex);
|
||||
} catch (NumberFormatException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.FINEST, null, ex);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentlyPublished;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the epoch date is within the range specified of the
|
||||
* compareTo epoch time. This takes the (compareTo-date)/1000/60/60/24 to
|
||||
* get the number of days. If the calculated days is less then the range the
|
||||
* date is considered valid.
|
||||
*
|
||||
* @param date the date to be checked.
|
||||
* @param compareTo the date to compare to.
|
||||
* @param range the range in days to be considered valid.
|
||||
* @return whether or not the date is within the range.
|
||||
*/
|
||||
private boolean withinRange(long date, long compareTo, int range) {
|
||||
double differenceInDays = (compareTo - date) / 1000.0 / 60.0 / 60.0 / 24.0;
|
||||
return differenceInDays < range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the timestamps from the NVD CVE meta data file.
|
||||
*
|
||||
* @return the timestamp from the currently published nvdcve downloads page
|
||||
* @throws MalformedURLException thrown if the URL for the NVD CCE Meta
|
||||
* data is incorrect.
|
||||
* @throws DownloadFailedException thrown if there is an error
|
||||
* downloading the nvd cve meta data file
|
||||
* @throws InvalidDataException thrown if there is an exception parsing
|
||||
* the timestamps
|
||||
* @throws InvalidSettingException thrown if the settings are invalid
|
||||
*/
|
||||
protected Map<String, NvdCveUrl> retrieveCurrentTimestampsFromWeb()
|
||||
throws MalformedURLException, DownloadFailedException, InvalidDataException, InvalidSettingException {
|
||||
|
||||
Map<String, NvdCveUrl> map = new HashMap<String, NvdCveUrl>();
|
||||
String retrieveUrl = Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL);
|
||||
|
||||
NvdCveUrl item = new NvdCveUrl();
|
||||
item.setNeedsUpdate(false); //the others default to true, to make life easier later this should default to false.
|
||||
item.setId("modified");
|
||||
item.setUrl(retrieveUrl);
|
||||
item.setOldSchemaVersionUrl(Settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL));
|
||||
|
||||
item.timestamp = Downloader.getLastModified(new URL(retrieveUrl));
|
||||
map.put("modified", item);
|
||||
|
||||
int max = Settings.getInt(Settings.KEYS.CVE_URL_COUNT);
|
||||
for (int i = 1; i <= max; i++) {
|
||||
retrieveUrl = Settings.getString(Settings.KEYS.CVE_BASE_URL + Settings.KEYS.CVE_SCHEMA_2_0 + i);
|
||||
item = new NvdCveUrl();
|
||||
item.setId(Integer.toString(i));
|
||||
item.setUrl(retrieveUrl);
|
||||
item.setOldSchemaVersionUrl(Settings.getString(Settings.KEYS.CVE_BASE_URL + Settings.KEYS.CVE_SCHEMA_1_2 + i));
|
||||
item.setTimestamp(Downloader.getLastModified(new URL(retrieveUrl)));
|
||||
map.put(item.id, item);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* A pojo that contains the Url and timestamp of the current NvdCve XML
|
||||
* files.
|
||||
*/
|
||||
protected static class NvdCveUrl {
|
||||
|
||||
/**
|
||||
* an id.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Get the value of id
|
||||
*
|
||||
* @return the value of id
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of id
|
||||
*
|
||||
* @param id new value of id
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
/**
|
||||
* a url.
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* Get the value of url
|
||||
*
|
||||
* @return the value of url
|
||||
*/
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of url
|
||||
*
|
||||
* @param url new value of url
|
||||
*/
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
/**
|
||||
* The 1.2 schema URL
|
||||
*/
|
||||
protected String oldSchemaVersionUrl;
|
||||
|
||||
/**
|
||||
* Get the value of oldSchemaVersionUrl
|
||||
*
|
||||
* @return the value of oldSchemaVersionUrl
|
||||
*/
|
||||
public String getOldSchemaVersionUrl() {
|
||||
return oldSchemaVersionUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of oldSchemaVersionUrl
|
||||
*
|
||||
* @param oldSchemaVersionUrl new value of oldSchemaVersionUrl
|
||||
*/
|
||||
public void setOldSchemaVersionUrl(String oldSchemaVersionUrl) {
|
||||
this.oldSchemaVersionUrl = oldSchemaVersionUrl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* a timestamp - epoch time.
|
||||
*/
|
||||
private long timestamp;
|
||||
|
||||
/**
|
||||
* Get the value of timestamp - epoch time
|
||||
*
|
||||
* @return the value of timestamp - epoch time
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of timestamp - epoch time
|
||||
*
|
||||
* @param timestamp new value of timestamp - epoch time
|
||||
*/
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
/**
|
||||
* indicates whether or not this item should be updated.
|
||||
*/
|
||||
private boolean needsUpdate = true;
|
||||
|
||||
/**
|
||||
* Get the value of needsUpdate
|
||||
*
|
||||
* @return the value of needsUpdate
|
||||
*/
|
||||
public boolean getNeedsUpdate() {
|
||||
return needsUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of needsUpdate
|
||||
*
|
||||
* @param needsUpdate new value of needsUpdate
|
||||
*/
|
||||
public void setNeedsUpdate(boolean needsUpdate) {
|
||||
this.needsUpdate = needsUpdate;
|
||||
}
|
||||
}
|
||||
//</editor-fold>
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve.xml;
|
||||
|
||||
/**
|
||||
* An InvalidDataDataException is a generic exception used when trying to load
|
||||
* the nvd cve meta data.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class InvalidDataException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an InvalidDataException
|
||||
*
|
||||
* @param msg the exception message
|
||||
*/
|
||||
public InvalidDataException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an InvalidDataException
|
||||
*
|
||||
* @param msg the exception message
|
||||
* @param ex the cause of the exception
|
||||
*/
|
||||
public InvalidDataException(String msg, Exception ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve.xml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXNotSupportedException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
/**
|
||||
* A SAX Handler that will parse the NVD CVE XML (schema version 1.2). This
|
||||
* parses the xml and retrieves a listing of CPEs that have previous versions
|
||||
* specified. The previous version information is not in the 2.0 version of the
|
||||
* schema and is useful to ensure accurate identification (or at least complete).
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class NvdCve12Handler extends DefaultHandler {
|
||||
|
||||
private static final String CURRENT_SCHEMA_VERSION = "1.2";
|
||||
private String vulnerability = null;
|
||||
private List<VulnerableSoftware> software = null;
|
||||
private String vendor = null;
|
||||
private String product = null;
|
||||
private boolean skip = false;
|
||||
private boolean hasPreviousVersion = false;
|
||||
private Element current = new Element();
|
||||
private Map<String, List<VulnerableSoftware>> vulnerabilities = null;
|
||||
|
||||
/**
|
||||
* Get the value of vulnerabilities
|
||||
*
|
||||
* @return the value of vulnerabilities
|
||||
*/
|
||||
public Map<String, List<VulnerableSoftware>> getVulnerabilities() {
|
||||
return vulnerabilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
current.setNode(qName);
|
||||
if (current.isEntryNode()) {
|
||||
vendor = null;
|
||||
product = null;
|
||||
hasPreviousVersion = false;
|
||||
String reject = attributes.getValue("reject");
|
||||
skip = (reject != null && reject.equals("1"));
|
||||
if (!skip) {
|
||||
vulnerability = attributes.getValue("name");
|
||||
software = new ArrayList<VulnerableSoftware>();
|
||||
} else {
|
||||
vulnerability = null;
|
||||
software = null;
|
||||
}
|
||||
} else if (!skip && current.isProdNode()) {
|
||||
|
||||
vendor = attributes.getValue("vendor");
|
||||
product = attributes.getValue("name");
|
||||
} else if (!skip && current.isVersNode()) {
|
||||
String prev = attributes.getValue("prev");
|
||||
if (prev != null && "1".equals(prev)) {
|
||||
hasPreviousVersion = true;
|
||||
String edition = attributes.getValue("edition");
|
||||
String num = attributes.getValue("num");
|
||||
|
||||
/*yes yes, this may not actually be an "a" - it could be an OS, etc. but for our
|
||||
purposes this is good enough as we won't use this if we don't find a corresponding "a"
|
||||
in the nvd cve 2.0. */
|
||||
String cpe = "cpe:/a:" + vendor + ":" + product;
|
||||
if (num != null) {
|
||||
cpe += ":" + num;
|
||||
}
|
||||
if (edition != null) {
|
||||
cpe += ":" + edition;
|
||||
}
|
||||
VulnerableSoftware vs = new VulnerableSoftware();
|
||||
vs.setCpe(cpe);
|
||||
vs.setPreviousVersion(prev);
|
||||
software.add(vs);
|
||||
}
|
||||
} else if (current.isNVDNode()) {
|
||||
String nvdVer = attributes.getValue("nvd_xml_version");
|
||||
if (!CURRENT_SCHEMA_VERSION.equals(nvdVer)) {
|
||||
throw new SAXNotSupportedException("Schema version " + nvdVer + " is not supported");
|
||||
}
|
||||
vulnerabilities = new HashMap<String, List<VulnerableSoftware>>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
current.setNode(qName);
|
||||
if (current.isEntryNode()) {
|
||||
if (!skip && hasPreviousVersion) {
|
||||
vulnerabilities.put(vulnerability, software);
|
||||
}
|
||||
vulnerability = null;
|
||||
software = null;
|
||||
}
|
||||
}
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
|
||||
/**
|
||||
* A simple class to maintain information about the current element while
|
||||
* parsing the NVD CVE XML.
|
||||
*/
|
||||
protected static class Element {
|
||||
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 1.2
|
||||
*/
|
||||
public static final String NVD = "nvd";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 1.2
|
||||
*/
|
||||
public static final String ENTRY = "entry";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 1.2
|
||||
*/
|
||||
public static final String VULN_SOFTWARE = "vuln_soft";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 1.2
|
||||
*/
|
||||
public static final String PROD = "prod";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 1.2
|
||||
*/
|
||||
public static final String VERS = "vers";
|
||||
private String node = null;
|
||||
|
||||
/**
|
||||
* Gets the value of node
|
||||
*
|
||||
* @return the value of node
|
||||
*/
|
||||
public String getNode() {
|
||||
return this.node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of node
|
||||
*
|
||||
* @param node new value of node
|
||||
*/
|
||||
public void setNode(String node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the NVD node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isNVDNode() {
|
||||
return NVD.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the ENTRY node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isEntryNode() {
|
||||
return ENTRY.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the VULN_SOFTWARE node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isVulnSoftwareNode() {
|
||||
return VULN_SOFTWARE.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the PROD node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isProdNode() {
|
||||
return PROD.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the VERS node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isVersNode() {
|
||||
return VERS.equals(node);
|
||||
}
|
||||
}
|
||||
// </editor-fold>
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve.xml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.owasp.dependencycheck.data.cpe.Index;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXNotSupportedException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
/**
|
||||
* A SAX Handler that will parse the NVD CVE XML (schema version 2.0).
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@gmail.com)
|
||||
*/
|
||||
public class NvdCve20Handler extends DefaultHandler {
|
||||
|
||||
private static final String CURRENT_SCHEMA_VERSION = "2.0";
|
||||
private Element current = new Element();
|
||||
StringBuilder nodeText = null;
|
||||
Vulnerability vulnerability = null;
|
||||
Reference reference = null;
|
||||
boolean hasApplicationCpe = false;
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
current.setNode(qName);
|
||||
if (current.isEntryNode()) {
|
||||
hasApplicationCpe = false;
|
||||
vulnerability = new Vulnerability();
|
||||
vulnerability.setName(attributes.getValue("id"));
|
||||
} else if (current.isVulnProductNode()) {
|
||||
nodeText = new StringBuilder(100);
|
||||
} else if (current.isVulnReferencesNode()) {
|
||||
String lang = attributes.getValue("xml:lang");
|
||||
if ("en".equals(lang)) {
|
||||
reference = new Reference();
|
||||
} else {
|
||||
reference = null;
|
||||
}
|
||||
} else if (reference != null && current.isVulnReferenceNode()) {
|
||||
reference.setUrl(attributes.getValue("href"));
|
||||
nodeText = new StringBuilder(130);
|
||||
} else if (reference != null && current.isVulnSourceNode()) {
|
||||
nodeText = new StringBuilder(30);
|
||||
} else if (current.isVulnSummaryNode()) {
|
||||
nodeText = new StringBuilder(500);
|
||||
} else if (current.isNVDNode()) {
|
||||
String nvdVer = attributes.getValue("nvd_xml_version");
|
||||
if (!CURRENT_SCHEMA_VERSION.equals(nvdVer)) {
|
||||
throw new SAXNotSupportedException("Schema version " + nvdVer + " is not supported");
|
||||
}
|
||||
} else if (current.isVulnCWENode()) {
|
||||
vulnerability.setCwe(attributes.getValue("id"));
|
||||
} else if (current.isCVSSScoreNode()) {
|
||||
nodeText = new StringBuilder(5);
|
||||
} else if (current.isCVSSAccessVectorNode()) {
|
||||
nodeText = new StringBuilder(20);
|
||||
} else if (current.isCVSSAccessComplexityNode()) {
|
||||
nodeText = new StringBuilder(20);
|
||||
} else if (current.isCVSSAuthenticationNode()) {
|
||||
nodeText = new StringBuilder(20);
|
||||
} else if (current.isCVSSAvailabilityImpactNode()) {
|
||||
nodeText = new StringBuilder(20);
|
||||
} else if (current.isCVSSConfidentialityImpactNode()) {
|
||||
nodeText = new StringBuilder(20);
|
||||
} else if (current.isCVSSIntegrityImpactNode()) {
|
||||
nodeText = new StringBuilder(20);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
if (nodeText != null) {
|
||||
nodeText.append(ch, start, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
current.setNode(qName);
|
||||
if (current.isEntryNode()) {
|
||||
if (hasApplicationCpe) {
|
||||
try {
|
||||
saveEntry(vulnerability);
|
||||
} catch (DatabaseException ex) {
|
||||
throw new SAXException(ex);
|
||||
} catch (CorruptIndexException ex) {
|
||||
throw new SAXException(ex);
|
||||
} catch (IOException ex) {
|
||||
throw new SAXException(ex);
|
||||
}
|
||||
}
|
||||
vulnerability = null;
|
||||
} else if (current.isCVSSScoreNode()) {
|
||||
try {
|
||||
float score = Float.parseFloat(nodeText.toString());
|
||||
vulnerability.setCvssScore(score);
|
||||
} catch (NumberFormatException ex) {
|
||||
Logger.getLogger(NvdCve20Handler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
nodeText = null;
|
||||
} else if (current.isCVSSAccessVectorNode()) {
|
||||
vulnerability.setCvssAccessVector(nodeText.toString());
|
||||
nodeText = null;
|
||||
} else if (current.isCVSSAccessComplexityNode()) {
|
||||
vulnerability.setCvssAccessComplexity(nodeText.toString());
|
||||
nodeText = null;
|
||||
} else if (current.isCVSSAuthenticationNode()) {
|
||||
vulnerability.setCvssAuthentication(nodeText.toString());
|
||||
nodeText = null;
|
||||
} else if (current.isCVSSAvailabilityImpactNode()) {
|
||||
vulnerability.setCvssAvailabilityImpact(nodeText.toString());
|
||||
nodeText = null;
|
||||
} else if (current.isCVSSConfidentialityImpactNode()) {
|
||||
vulnerability.setCvssConfidentialityImpact(nodeText.toString());
|
||||
nodeText = null;
|
||||
} else if (current.isCVSSIntegrityImpactNode()) {
|
||||
vulnerability.setCvssIntegrityImpact(nodeText.toString());
|
||||
nodeText = null;
|
||||
} else if (current.isVulnProductNode()) {
|
||||
String cpe = nodeText.toString();
|
||||
if (cpe.startsWith("cpe:/a:")) {
|
||||
hasApplicationCpe = true;
|
||||
vulnerability.addVulnerableSoftware(cpe);
|
||||
}
|
||||
nodeText = null;
|
||||
} else if (reference != null && current.isVulnReferencesNode()) {
|
||||
vulnerability.addReference(reference);
|
||||
reference = null;
|
||||
} else if (reference != null && current.isVulnReferenceNode()) {
|
||||
reference.setName(nodeText.toString());
|
||||
nodeText = null;
|
||||
} else if (reference != null && current.isVulnSourceNode()) {
|
||||
reference.setSource(nodeText.toString());
|
||||
nodeText = null;
|
||||
} else if (current.isVulnSummaryNode()) {
|
||||
vulnerability.setDescription(nodeText.toString());
|
||||
nodeText = null;
|
||||
}
|
||||
}
|
||||
private CveDB cveDB = null;
|
||||
|
||||
/**
|
||||
* Sets the cveDB
|
||||
* @param db a reference to the CveDB
|
||||
*/
|
||||
public void setCveDB(CveDB db) {
|
||||
cveDB = db;
|
||||
}
|
||||
/**
|
||||
* A list of CVE entries and associated VulnerableSoftware entries that contain
|
||||
* previous entries.
|
||||
*/
|
||||
private Map<String, List<VulnerableSoftware>> prevVersionVulnMap = null;
|
||||
|
||||
/**
|
||||
* Sets the prevVersionVulnMap.
|
||||
* @param map the map of vulnerable software with previous versions being vulnerable
|
||||
*/
|
||||
public void setPrevVersionVulnMap(Map<String, List<VulnerableSoftware>> map) {
|
||||
prevVersionVulnMap = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a vulnerability to the CVE Database. This is a callback method
|
||||
* called by the Sax Parser Handler {@link org.owasp.dependencycheck.data.nvdcve.xml.NvdCve20Handler}.
|
||||
*
|
||||
* @param vuln the vulnerability to store in the database
|
||||
* @throws DatabaseException thrown if there is an error writing to the database
|
||||
* @throws CorruptIndexException is thrown if the CPE Index is corrupt
|
||||
* @throws IOException thrown if there is an IOException with the CPE Index
|
||||
*/
|
||||
public void saveEntry(Vulnerability vuln) throws DatabaseException, CorruptIndexException, IOException {
|
||||
if (cveDB == null) {
|
||||
return;
|
||||
}
|
||||
String cveName = vuln.getName();
|
||||
if (prevVersionVulnMap.containsKey(cveName)) {
|
||||
List<VulnerableSoftware> vulnSoftware = prevVersionVulnMap.get(cveName);
|
||||
for (VulnerableSoftware vs : vulnSoftware) {
|
||||
vuln.updateVulnerableSoftware(vs);
|
||||
}
|
||||
}
|
||||
for (VulnerableSoftware vs : vuln.getVulnerableSoftware()) {
|
||||
if (cpeIndex != null) {
|
||||
cpeIndex.saveEntry(vs);
|
||||
}
|
||||
}
|
||||
cveDB.updateVulnerability(vuln);
|
||||
}
|
||||
private Index cpeIndex = null;
|
||||
|
||||
/**
|
||||
* Sets the cpe index
|
||||
* @param index the CPE Lucene Index
|
||||
*/
|
||||
void setCpeIndex(Index index) {
|
||||
cpeIndex = index;
|
||||
}
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
|
||||
/**
|
||||
* A simple class to maintain information about the current element while
|
||||
* parsing the NVD CVE XML.
|
||||
*/
|
||||
protected static class Element {
|
||||
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String NVD = "nvd";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String ENTRY = "entry";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String VULN_PRODUCT = "vuln:product";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String VULN_REFERENCES = "vuln:references";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String VULN_SOURCE = "vuln:source";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String VULN_REFERENCE = "vuln:reference";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String VULN_SUMMARY = "vuln:summary";
|
||||
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String VULN_CWE = "vuln:cwe";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String CVSS_SCORE = "cvss:score";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String CVSS_ACCESS_VECTOR = "cvss:access-vector";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String CVSS_ACCESS_COMPLEXITY = "cvss:access-complexity";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String CVSS_AUTHENTICATION = "cvss:authentication";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String CVSS_CONFIDENTIALITY_IMPACT = "cvss:confidentiality-impact";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String CVSS_INTEGRITY_IMPACT = "cvss:integrity-impact";
|
||||
/**
|
||||
* A node type in the NVD CVE Schema 2.0
|
||||
*/
|
||||
public static final String CVSS_AVAILABILITY_IMPACT = "cvss:availability-impact";
|
||||
|
||||
private String node = null;
|
||||
|
||||
/**
|
||||
* Gets the value of node
|
||||
*
|
||||
* @return the value of node
|
||||
*/
|
||||
public String getNode() {
|
||||
return this.node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of node
|
||||
*
|
||||
* @param node new value of node
|
||||
*/
|
||||
public void setNode(String node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the NVD node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isNVDNode() {
|
||||
return NVD.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the ENTRY node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isEntryNode() {
|
||||
return ENTRY.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the VULN_PRODUCT node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isVulnProductNode() {
|
||||
return VULN_PRODUCT.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the REFERENCES node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isVulnReferencesNode() {
|
||||
return VULN_REFERENCES.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the REFERENCE node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isVulnReferenceNode() {
|
||||
return VULN_REFERENCE.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the VULN_SOURCE node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isVulnSourceNode() {
|
||||
return VULN_SOURCE.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the VULN_SUMMARY node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isVulnSummaryNode() {
|
||||
return VULN_SUMMARY.equals(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the handler is at the VULN_CWE node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isVulnCWENode() {
|
||||
return VULN_CWE.equals(node);
|
||||
}
|
||||
/**
|
||||
* Checks if the handler is at the CVSS_SCORE node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCVSSScoreNode() {
|
||||
return CVSS_SCORE.equals(node);
|
||||
}
|
||||
/**
|
||||
* Checks if the handler is at the CVSS_ACCESS_VECTOR node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCVSSAccessVectorNode() {
|
||||
return CVSS_ACCESS_VECTOR.equals(node);
|
||||
}
|
||||
/**
|
||||
* Checks if the handler is at the CVSS_ACCESS_COMPLEXITY node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCVSSAccessComplexityNode() {
|
||||
return CVSS_ACCESS_COMPLEXITY.equals(node);
|
||||
}
|
||||
/**
|
||||
* Checks if the handler is at the CVSS_AUTHENTICATION node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCVSSAuthenticationNode() {
|
||||
return CVSS_AUTHENTICATION.equals(node);
|
||||
}
|
||||
/**
|
||||
* Checks if the handler is at the CVSS_CONFIDENTIALITY_IMPACT node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCVSSConfidentialityImpactNode() {
|
||||
return CVSS_CONFIDENTIALITY_IMPACT.equals(node);
|
||||
}
|
||||
/**
|
||||
* Checks if the handler is at the CVSS_INTEGRITY_IMPACT node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCVSSIntegrityImpactNode() {
|
||||
return CVSS_INTEGRITY_IMPACT.equals(node);
|
||||
}
|
||||
/**
|
||||
* Checks if the handler is at the CVSS_AVAILABILITY_IMPACT node
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isCVSSAvailabilityImpactNode() {
|
||||
return CVSS_AVAILABILITY_IMPACT.equals(node);
|
||||
}
|
||||
|
||||
}
|
||||
// </editor-fold>
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.nvdcve.xml</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* <p>Contains classes used to parse the NVD CVE XML file.</p>
|
||||
* <p>The basic use is that the Importer is called to import
|
||||
* an NVD CVE file. The Importer instantiates an Indexer object
|
||||
* (which extends Index). The Indexer creates a partial-unmarshalling
|
||||
* SAX parser (implemented in the NvdCveXmlFilter) that extracts
|
||||
* VulnerabilityTypes (aka Entry) from the NVD CVE data file and
|
||||
* stores these into a Lucene Index.</p>
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.owasp.dependencycheck.data.nvdcve.xml;
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains classes used to work with the data sources.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.owasp.dependencycheck.data;
|
||||
Reference in New Issue
Block a user