prepared for CVE data by refactoring CPE code

This commit is contained in:
Jeremy Long
2012-09-17 22:22:11 -04:00
parent aadb29c668
commit a9cf6b595d
21 changed files with 1614 additions and 187 deletions

View File

@@ -0,0 +1,196 @@
package org.codesecure.dependencycheck.data;
/*
* This file is part of DependencyCheck.
*
* DependencyCheck is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DependencyCheck is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.search.IndexSearcher;
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.
*/
private Analyzer analyzer = 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();
analyzer = this.getAnalyzer(); //new StandardAnalyzer(Version.LUCENE_35);
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) {
try {
indexSearcher.close();
} catch (IOException ex) {
Logger.getLogger(AbstractIndex.class.getName()).log(Level.SEVERE, null, ex);
} finally {
indexSearcher = null;
}
}
if (analyzer != null) {
analyzer.close();
analyzer = 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_35, analyzer);
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;
}
public void openIndexReader() throws CorruptIndexException, IOException {
if (!isOpen()) {
open();
}
indexReader = IndexReader.open(directory, true);
}
public IndexSearcher getIndexSearcher() throws CorruptIndexException, IOException {
if (indexReader == null) {
openIndexReader();
}
if (indexSearcher == null) {
indexSearcher = new IndexSearcher(indexReader);
}
return indexSearcher;
}
public Analyzer getAnalyzer() {
if (analyzer == null) {
analyzer = createAnalyzer();
}
return analyzer;
}
/**
* 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 and searching the index.
* @return a Lucene Analyzer.
*/
public abstract Analyzer createAnalyzer();
}

View File

@@ -0,0 +1,45 @@
package org.codesecure.dependencycheck.data;
/*
* This file is part of DependencyCheck.
*
* DependencyCheck is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DependencyCheck is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import java.io.IOException;
import java.net.MalformedURLException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
/**
* 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 WebDataIndex {
/**
* 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 MalformedURLException is thrown if the URL for the CPE is malformed.
* @throws ParserConfigurationException is thrown if the parser is misconfigured.
* @throws SAXException is thrown if there is an error parsing the CPE XML.
* @throws IOException is thrown if a temporary file could not be created.
*/
public void updateIndexFromWeb() throws MalformedURLException, ParserConfigurationException, SAXException, IOException;
}

View File

@@ -75,29 +75,17 @@ public class CPEQuery {
*/
static final int STRING_BUILDER_BUFFER = 20;
/**
* The Lucene IndexReader.
* The CPE Index.
*/
private IndexReader indexReader = null;
protected Index cpe = null;
/**
* The Lucene IndexSearcher.
*/
private IndexSearcher indexSearcher = null;
/**
* The Lucene directory.
*/
private Directory directory = null;
/**
* The Lucene Analyzer.
*/
private Analyzer analyzer = null;
/**
* The Lucene QueryParser.
*/
private QueryParser queryParser = null;
/**
* Indicates whether or not the Lucene Index is open.
*/
private boolean indexOpen = false;
/**
* Opens the data source.
@@ -105,41 +93,21 @@ public class CPEQuery {
* @throws IOException when the Lucene directory to be querried does not exist or is corrupt.
*/
public void open() throws IOException {
directory = Index.getDirectory();
indexReader = IndexReader.open(directory, true);
indexSearcher = new IndexSearcher(indexReader);
analyzer = Index.createAnalyzer(); //use the same analyzer as used when indexing
cpe = new Index();
cpe.open();
indexSearcher = cpe.getIndexSearcher();
Analyzer analyzer = cpe.getAnalyzer();
//TITLE is the default field because it contains venddor, product, and version all in one.
queryParser = new QueryParser(Version.LUCENE_35, Fields.TITLE, analyzer);
indexOpen = true;
}
/**
* Closes the data source.
*/
public void close() {
analyzer.close();
analyzer = null;
queryParser = null;
try {
indexSearcher.close();
} catch (IOException ex) {
Logger.getLogger(CPEQuery.class.getName()).log(Level.SEVERE, null, ex);
}
indexSearcher = null;
try {
indexReader.close();
} catch (IOException ex) {
Logger.getLogger(CPEQuery.class.getName()).log(Level.SEVERE, null, ex);
}
indexReader = null;
try {
directory.close();
} catch (IOException ex) {
Logger.getLogger(CPEQuery.class.getName()).log(Level.SEVERE, null, ex);
}
directory = null;
indexOpen = false;
cpe.close();
}
/**
@@ -147,7 +115,7 @@ public class CPEQuery {
* @return true or false.
*/
public boolean isOpen() {
return indexOpen;
return (cpe == null) ? false : cpe.isOpen();
}
/**
@@ -157,7 +125,7 @@ public class CPEQuery {
@Override
protected void finalize() throws Throwable {
super.finalize();
if (indexOpen) {
if (isOpen()) {
close();
}
}
@@ -174,7 +142,7 @@ public class CPEQuery {
*/
public void determineCPE(Dependency dependency) throws CorruptIndexException, IOException, ParseException {
Confidence vendorConf = Confidence.HIGH;
Confidence titleConf = Confidence.HIGH;
Confidence productConf = Confidence.HIGH;
Confidence versionConf = Confidence.HIGH;
String vendors = addEvidenceWithoutDuplicateTerms("", dependency.getVendorEvidence(), vendorConf);
@@ -182,10 +150,10 @@ public class CPEQuery {
// if ("".equals(vendors)) {
// vendors = STRING_THAT_WILL_NEVER_BE_IN_THE_INDEX;
// }
String titles = addEvidenceWithoutDuplicateTerms("", dependency.getTitleEvidence(), titleConf);
///dependency.getTitleEvidence().toString(titleConf);
// if ("".equals(titles)) {
// titles = STRING_THAT_WILL_NEVER_BE_IN_THE_INDEX;
String products = addEvidenceWithoutDuplicateTerms("", dependency.getProductEvidence(), productConf);
///dependency.getProductEvidence().toString(productConf);
// if ("".equals(products)) {
// products = STRING_THAT_WILL_NEVER_BE_IN_THE_INDEX;
// }
String versions = addEvidenceWithoutDuplicateTerms("", dependency.getVersionEvidence(), versionConf);
//dependency.getVersionEvidence().toString(versionConf);
@@ -196,7 +164,7 @@ public class CPEQuery {
boolean found = false;
int cnt = 0;
do {
List<Entry> entries = searchCPE(vendors, titles, versions, dependency.getTitleEvidence().getWeighting(),
List<Entry> entries = searchCPE(vendors, products, versions, dependency.getProductEvidence().getWeighting(),
dependency.getVendorEvidence().getWeighting());
if (entries.size() > 0) {
@@ -220,10 +188,10 @@ public class CPEQuery {
}
}
if (round == 1) {
titleConf = reduceConfidence(titleConf);
if (dependency.getTitleEvidence().contains(titleConf)) {
//titles += " " + dependency.getTitleEvidence().toString(titleConf);
titles = addEvidenceWithoutDuplicateTerms(titles, dependency.getTitleEvidence(), titleConf);
productConf = reduceConfidence(productConf);
if (dependency.getProductEvidence().contains(productConf)) {
//products += " " + dependency.getProductEvidence().toString(productConf);
products = addEvidenceWithoutDuplicateTerms(products, dependency.getProductEvidence(), productConf);
} else {
cnt += 1;
round += 1;
@@ -289,7 +257,7 @@ public class CPEQuery {
* with the supplied vendor, product, and version.
*
* @param vendor the text used to search the vendor field.
* @param product the text used to search the title field.
* @param product the text used to search the product field.
* @param version the text used to search the version field.
* @return a list of possible CPE values.
* @throws CorruptIndexException when the Lucene index is corrupt.
@@ -309,10 +277,10 @@ public class CPEQuery {
* 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 title 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 title search.
* @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.
@@ -347,11 +315,11 @@ public class CPEQuery {
* 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 title 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 produdctWeightings a list of strings to apply to the product/title
* @param produdctWeightings a list of strings to apply to the product
* to boost the terms weight.
* @return the Lucene query.
*/
@@ -473,7 +441,7 @@ public class CPEQuery {
/**
* Takes a list of entries and a dependency. If the entry has terms that were
* used (i.e. this CPE entry wasn't identified because the version matched
* but the product and title did not) then the CPE Entry is returned in a list
* but the product names did not) then the CPE Entry is returned in a list
* of possible CPE Entries.
*
* @param entries a list of CPE entries.
@@ -483,7 +451,7 @@ public class CPEQuery {
private List<String> verifyEntries(final List<Entry> entries, final Dependency dependency) {
List<String> verified = new ArrayList<String>();
for (Entry e : entries) {
if (dependency.getTitleEvidence().containsUsedString(e.getProduct())
if (dependency.getProductEvidence().containsUsedString(e.getProduct())
&& dependency.getVendorEvidence().containsUsedString(e.getVendor())) {
//TODO - determine if this is right? Should we be carrying too much about the
// version at this point? Likely need to implement the versionAnalyzer....

View File

@@ -44,6 +44,8 @@ import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.codesecure.dependencycheck.data.AbstractIndex;
import org.codesecure.dependencycheck.data.WebDataIndex;
import org.codesecure.dependencycheck.utils.Downloader;
import org.codesecure.dependencycheck.utils.Settings;
import org.codesecure.dependencycheck.data.cpe.xml.Importer;
@@ -55,48 +57,17 @@ import org.xml.sax.SAXException;
*
* @author Jeremy Long (jeremy.long@gmail.com)
*/
public class Index {
public class Index extends AbstractIndex implements WebDataIndex {
/**
* Te name of the properties file containing the timestamp of the last update.
* 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.
*/
private static final String LAST_UPDATED = "lastupdated";
/**
* The Lucene directory containing the index.
*/
protected Directory directory = null;
/**
* The IndexWriter for the Lucene index.
*/
protected IndexWriter indexWriter = null;
/**
* Opens the CPE Index.
* @throws IOException is thrown if an IOException occurs opening the index.
*/
public void open() throws IOException {
directory = Index.getDirectory();
Analyzer analyzer = Index.createAnalyzer(); //new StandardAnalyzer(Version.LUCENE_35);
IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_35, analyzer);
indexWriter = new IndexWriter(directory, conf);
}
/**
* Closes the CPE Index.
*
* @throws CorruptIndexException is thrown if the index is corrupt.
* @throws IOException is thrown if an IOException occurs closing the index.
*/
public void close() throws CorruptIndexException, IOException {
indexWriter.commit();
indexWriter.close(true);
directory.close();
}
/**
* Returns the directory that holds the CPE Index.
@@ -104,12 +75,12 @@ public class Index {
* @return the Directory containing the CPE Index.
* @throws IOException is thrown if an IOException occurs.
*/
public static Directory getDirectory() throws IOException {
public Directory getDirectory() throws IOException {
String fileName = Settings.getString(Settings.KEYS.CPE_INDEX);
File path = new File(fileName);
Directory directory = FSDirectory.open(path);
Directory dir = FSDirectory.open(path);
return directory;
return dir;
}
/**
@@ -117,11 +88,11 @@ public class Index {
*
* @return the CPE Analyzer.
*/
public static Analyzer createAnalyzer() {
@SuppressWarnings("unchecked")
public Analyzer createAnalyzer() {
Map fieldAnalyzers = new HashMap();
fieldAnalyzers.put(Fields.VERSION, new KeywordAnalyzer());
//new WhitespaceAnalyzer(Version.LUCENE_35)); //
PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(
new StandardAnalyzer(Version.LUCENE_35), fieldAnalyzers);

View File

@@ -25,6 +25,10 @@ import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.FieldInfo.IndexOptions;
import org.apache.lucene.index.Term;
import org.codesecure.dependencycheck.data.LuceneUtils;
import org.codesecure.dependencycheck.data.cpe.Entry;
import org.codesecure.dependencycheck.data.cpe.Fields;
import org.codesecure.dependencycheck.data.cpe.Index;
import org.codesecure.dependencycheck.data.cpe.xml.EntrySaveDelegate;
import org.codesecure.dependencycheck.data.cpe.xml.EntrySaveDelegate;
/**
@@ -97,4 +101,5 @@ public class Indexer extends Index implements EntrySaveDelegate {
return doc;
}
}

View File

@@ -18,12 +18,12 @@ package org.codesecure.dependencycheck.data.cpe.xml;
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import org.codesecure.dependencycheck.data.cpe.Indexer;
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.codesecure.dependencycheck.data.cpe.Indexer;
import org.xml.sax.SAXException;
/**
@@ -53,7 +53,7 @@ public class Importer {
SAXParser saxParser = factory.newSAXParser();
CPEHandler handler = new CPEHandler();
Indexer indexer = new Indexer();
indexer.open();
indexer.openIndexWriter();
handler.registerSaveDelegate(indexer);
saxParser.parse(file, handler);
indexer.close();

View File

@@ -0,0 +1,380 @@
package org.codesecure.dependencycheck.data.cve;
/*
* This file is part of DependencyCheck.
*
* DependencyCheck is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DependencyCheck is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.lucene.document.Document;
/**
* A single CVE entry from the cve.xml files downloaded from
* <a href="http://nvd.nist.gov/cpe.cfm">http://nvd.nist.gov/cpe.cfm</a>.
*
* @author Jeremy Long (jeremy.long@gmail.com)
*/
public class Entry {
/**
* 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.setName(doc.get(Fields.NAME));
entry.setTitle(doc.get(Fields.TITLE));
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(Entry.class.getName()).log(Level.SEVERE, null, ex);
entry.name = doc.get(Fields.NAME);
}
// entry.vendor = doc.get(Fields.VENDOR);
// entry.version = doc.get(Fields.VERSION);
// //entry.revision = doc.get(Fields.REVISION);
// entry.product = doc.get(Fields.TITLE);
// entry.nvdId = doc.get(Fields.NVDID);
return entry;
}
/**
* The title of the CPE
*/
protected String title;
/**
* Get the value of title
*
* @return the value of title
*/
public String getTitle() {
return title;
}
/**
* Set the value of title
*
* @param title new value of title
*/
public void setTitle(String title) {
this.title = title;
}
/**
* 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 and calls parseName to obtain the vendor:product:version:revision
*
* @param name new value of name
* @throws UnsupportedEncodingException should never be thrown...
*/
public void setName(String name) throws UnsupportedEncodingException {
this.name = name;
parseName();
}
/**
* The status of the CPE Entry.
*/
protected String status;
/**
* Get the value of status
*
* @return the value of status
*/
public String getStatus() {
return status;
}
/**
* Set the value of status
*
* @param status new value of status
*/
public void setStatus(String status) {
this.status = status;
}
/**
* The modification date of the CPE Entry.
*/
protected Date modificationDate;
/**
* Get the value of modificationDate
*
* @return the value of modificationDate
*/
public Date getModificationDate() {
return modificationDate;
}
/**
* Set the value of modificationDate
*
* @param modificationDate new value of modificationDate
*/
public void setModificationDate(Date modificationDate) {
this.modificationDate = modificationDate;
}
/**
* Set the value of modificationDate
*
* Expected format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*
* @param modificationDate new value of modificationDate
* @throws ParseException is thrown when a parse exception occurs.
*/
public void setModificationDate(String modificationDate) throws ParseException {
String formatStr = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
Date tempDate = null;
SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
sdf.setLenient(true);
tempDate = sdf.parse(modificationDate);
this.modificationDate = tempDate;
}
/**
* The nvdId.
*/
protected String nvdId;
/**
* Get the value of nvdId
*
* @return the value of nvdId
*/
public String getNvdId() {
return nvdId;
}
/**
* Set the value of nvdId
*
* @param nvdId new value of nvdId
*/
public void setNvdId(String nvdId) {
this.nvdId = nvdId;
}
/**
* 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;
}
/**
* If the CPE Entry is well known (i.e. based off a hash)
*/
protected boolean wellKnown = false;
/**
* Get the value of wellKnown
*
* @return the value of wellKnown
*/
public boolean isWellKnown() {
return wellKnown;
}
/**
* Set the value of wellKnown
*
* @param wellKnown new value of wellKnown
*/
public void setWellKnown(boolean wellKnown) {
this.wellKnown = wellKnown;
}
/**
* 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>&nbsp;&nbsp;&nbsp;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>
*
* @throws UnsupportedEncodingException should never be thrown...
*/
private void parseName() throws UnsupportedEncodingException {
if (name != null && name.length() > 7) {
String[] data = name.substring(7).split(":");
if (data.length >= 1) {
vendor = URLDecoder.decode(data[0], "UTF-8");
if (data.length >= 2) {
product = URLDecoder.decode(data[1], "UTF-8");
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;
}
}

View File

@@ -0,0 +1,53 @@
package org.codesecure.dependencycheck.data.cve;
/*
* 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.
*/
/**
* 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 version field.
*/
public static final String VERSION = "version";
//public static final String REVISION = "revision";
/**
* The key for the product field.
*/
public static final String PRODUCT = "product";
/**
* The key for the title field. This is a field combining vendor, product, and version.
*/
public static final String TITLE = "title";
/**
* The key for the nvdId field.
*/
public static final String NVDID = "nvdid";
}

View File

@@ -0,0 +1,255 @@
package org.codesecure.dependencycheck.data.cve;
/*
* This file is part of DependencyCheck.
*
* DependencyCheck is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DependencyCheck is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import java.io.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 java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.KeywordAnalyzer;
import org.apache.lucene.analysis.PerFieldAnalyzerWrapper;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.codesecure.dependencycheck.data.AbstractIndex;
import org.codesecure.dependencycheck.data.WebDataIndex;
import org.codesecure.dependencycheck.utils.Downloader;
import org.codesecure.dependencycheck.utils.Settings;
import org.codesecure.dependencycheck.data.cpe.xml.Importer;
import org.codesecure.dependencycheck.utils.DownloadFailedException;
import org.xml.sax.SAXException;
/**
* The Index class is used to utilize and maintain the CVE Index.
*
* @author Jeremy Long (jeremy.long@gmail.com)
*/
public class Index extends AbstractIndex implements WebDataIndex {
/**
* 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.
*/
private static final String LAST_UPDATED = "lastupdated";
/**
* 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 {
String fileName = Settings.getString(Settings.KEYS.CVE_INDEX);
File path = new File(fileName);
Directory dir = FSDirectory.open(path);
return dir;
}
/**
* Creates an Analyzer for the CPE Index.
*
* @return the CPE Analyzer.
*/
@SuppressWarnings("unchecked")
public Analyzer createAnalyzer() {
Map fieldAnalyzers = new HashMap();
fieldAnalyzers.put(Fields.VERSION, new KeywordAnalyzer());
PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(
new StandardAnalyzer(Version.LUCENE_35), fieldAnalyzers);
return wrapper;
}
/**
* Downloads the latest CPE XML file from the web and imports it into
* the current CPE Index.
*
* @throws MalformedURLException is thrown if the URL for the CPE is malformed.
* @throws ParserConfigurationException is thrown if the parser is misconfigured.
* @throws SAXException is thrown if there is an error parsing the CPE XML.
* @throws IOException is thrown if a temporary file could not be created.
*/
public void updateIndexFromWeb() throws MalformedURLException, ParserConfigurationException, SAXException, IOException {
long timeStamp = updateNeeded();
if (timeStamp > 0) {
URL url = new URL(Settings.getString(Settings.KEYS.CPE_URL));
File outputPath = null;
try {
outputPath = File.createTempFile("cve", ".xml");
Downloader.fetchFile(url, outputPath, true);
Importer.importXML(outputPath.toString());
writeLastUpdatedPropertyFile(timeStamp);
} catch (DownloadFailedException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
if (outputPath != null && outputPath.exists()) {
outputPath.delete();
}
} finally {
if (outputPath != null && outputPath.exists()) {
outputPath.deleteOnExit();
}
}
}
}
}
/**
* Writes a properties file containing the last updated date to the CPE directory.
* @param timeStamp the timestamp to write.
*/
private void writeLastUpdatedPropertyFile(long timeStamp) {
String dir = Settings.getString(Settings.KEYS.CPE_INDEX);
File cpeProp = new File(dir + File.separatorChar + UPDATE_PROPERTIES_FILE);
Properties prop = new Properties();
prop.put(Index.LAST_UPDATED, String.valueOf(timeStamp));
OutputStream os = null;
try {
os = new FileOutputStream(cpeProp);
OutputStreamWriter out = new OutputStreamWriter(os);
prop.store(out, dir);
} catch (FileNotFoundException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
os.flush();
} catch (IOException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
}
try {
os.close();
} catch (IOException ex) {
Logger.getLogger(Index.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
/**
* Determines if the index needs to be updated. This is done by fetching the
* cpe.meta data and checking the lastModifiedDate. If the CPE data needs to
* be refreshed this method will return the timestamp of the new CPE. If an
* update is not required this function will return 0.
*
* @return the timestamp of the currently published CPE.xml if the index needs to be updated, otherwise returns 0..
* @throws MalformedURLException is thrown if the URL for the CPE Meta data is incorrect.
* @throws DownloadFailedException is thrown if there is an error downloading the cpe.meta data file.
*/
public long updateNeeded() throws MalformedURLException, DownloadFailedException {
long retVal = 0;
long lastUpdated = 0;
long currentlyPublishedDate = retrieveCurrentCPETimestampFromWeb();
if (currentlyPublishedDate == 0) {
throw new DownloadFailedException("Unable to retrieve valid timestamp from cpe.meta file");
}
String dir = Settings.getString(Settings.KEYS.CPE_INDEX);
File f = new File(dir);
if (!f.exists()) {
retVal = currentlyPublishedDate;
} else {
File cpeProp = new File(dir + File.separatorChar + UPDATE_PROPERTIES_FILE);
if (!cpeProp.exists()) {
retVal = currentlyPublishedDate;
} else {
Properties prop = new Properties();
InputStream is = null;
try {
is = new FileInputStream(cpeProp);
prop.load(is);
lastUpdated = Long.parseLong(prop.getProperty(Index.LAST_UPDATED));
} catch (FileNotFoundException ex) {
Logger.getLogger(Index.class.getName()).log(Level.FINEST, null, ex);
} catch (IOException ex) {
Logger.getLogger(Index.class.getName()).log(Level.FINEST, null, ex);
} catch (NumberFormatException ex) {
Logger.getLogger(Index.class.getName()).log(Level.FINEST, null, ex);
}
if (currentlyPublishedDate > lastUpdated) {
retVal = currentlyPublishedDate;
}
}
}
return retVal;
}
/**
* Retrieves the timestamp from the CPE meta data file.
* @return the timestamp from the currently published cpe.meta.
* @throws MalformedURLException is thrown if the URL for the CPE Meta data is incorrect.
* @throws DownloadFailedException is thrown if there is an error downloading the cpe.meta data file.
*/
private long retrieveCurrentCPETimestampFromWeb() throws MalformedURLException, DownloadFailedException {
long timestamp = 0;
File tmp = null;
InputStream is = null;
try {
tmp = File.createTempFile("cpe", "meta");
URL url = new URL(Settings.getString(Settings.KEYS.CPE_META_URL));
Downloader.fetchFile(url, tmp);
Properties prop = new Properties();
is = new FileInputStream(tmp);
prop.load(is);
timestamp = Long.parseLong(prop.getProperty("lastModifiedDate"));
} catch (IOException ex) {
throw new DownloadFailedException("Unable to create temporary file for CPE Meta File download.", ex);
} finally {
try {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
Logger.getLogger(Index.class.getName()).log(Level.FINEST, null, ex);
}
}
if (tmp != null && tmp.exists()) {
tmp.delete();
}
} finally {
if (tmp != null && tmp.exists()) {
tmp.deleteOnExit();
}
}
}
return timestamp;
}
}

View File

@@ -0,0 +1,100 @@
package org.codesecure.dependencycheck.data.cve;
/*
* This file is part of DependencyCheck.
*
* DependencyCheck is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DependencyCheck is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import java.io.IOException;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.FieldInfo.IndexOptions;
import org.apache.lucene.index.Term;
import org.codesecure.dependencycheck.data.LuceneUtils;
import org.codesecure.dependencycheck.data.cve.xml.EntrySaveDelegate;
/**
* The Indexer is used to convert a CPE Entry, retrieved from the CPE XML file,
* into a Document that is stored in the Lucene index.
*
* @author Jeremy Long (jeremy.long@gmail.com)
*/
public class Indexer extends Index implements EntrySaveDelegate {
/**
* 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()));
indexWriter.updateDocument(term, doc);
}
/**
* Converst 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 Field(Fields.NAME, entry.getName(), Field.Store.YES, Field.Index.ANALYZED);
name.setIndexOptions(IndexOptions.DOCS_ONLY);
doc.add(name);
Field nvdId = new Field(Fields.NVDID, entry.getNvdId(), Field.Store.NO, Field.Index.ANALYZED);
nvdId.setIndexOptions(IndexOptions.DOCS_ONLY);
doc.add(nvdId);
Field vendor = new Field(Fields.VENDOR, entry.getVendor(), Field.Store.NO, Field.Index.ANALYZED);
vendor.setIndexOptions(IndexOptions.DOCS_ONLY);
vendor.setBoost(5.0F);
doc.add(vendor);
Field product = new Field(Fields.PRODUCT, entry.getProduct(), Field.Store.NO, Field.Index.ANALYZED);
product.setIndexOptions(IndexOptions.DOCS_ONLY);
product.setBoost(5.0F);
doc.add(product);
Field title = new Field(Fields.TITLE, entry.getTitle(), Field.Store.NO, Field.Index.ANALYZED);
title.setIndexOptions(IndexOptions.DOCS_ONLY);
//title.setBoost(1.0F);
doc.add(title);
//TODO revision should likely be its own field
if (entry.getVersion() != null) {
Field version = null;
if (entry.getRevision() != null) {
version = new Field(Fields.VERSION, entry.getVersion() + " "
+ entry.getRevision(), Field.Store.NO, Field.Index.ANALYZED);
} else {
version = new Field(Fields.VERSION, entry.getVersion(),
Field.Store.NO, Field.Index.ANALYZED);
}
version.setIndexOptions(IndexOptions.DOCS_ONLY);
version.setBoost(0.8F);
doc.add(version);
}
return doc;
}
}

View File

@@ -0,0 +1,12 @@
/**
* <html>
* <head>
* <title>org.codesecure.dependencycheck.data.cve.xml</title>
* </head>
* <body>
* Contains classes used to parse the CVE XML files.
* </body>
* </html>
*/
package org.codesecure.dependencycheck.data.cve;

View File

@@ -0,0 +1,350 @@
package org.codesecure.dependencycheck.data.cve.xml;
/*
* This file is part of DependencyCheck.
*
* DependencyCheck is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DependencyCheck is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import org.codesecure.dependencycheck.data.cve.Entry;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.lucene.index.CorruptIndexException;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* A SAX Handler that will parse the CVE XML Listing.
*
* @author Jeremy Long (jeremy.long@gmail.com)
*/
public class CVEHandler extends DefaultHandler {
private static final String CURRENT_SCHEMA_VERSION = "2.2";
EntrySaveDelegate saveDelegate = null;
Entry entry = null;
boolean languageIsUS = false;
StringBuilder nodeText = null;
boolean skip = false;
Element current = new Element();
/**
* Register a EntrySaveDelegate object. When the last node of an entry is
* reached if a save delegate has been regsitered the save method will be invoked.
*
* @param delegate the delegate used to save an entry
*/
public void registerSaveDelegate(EntrySaveDelegate delegate) {
this.saveDelegate = delegate;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
nodeText = null;
current.setNode(qName);
if (current.isCpeItemNode()) {
entry = new Entry();
String temp = attributes.getValue("deprecated");
String name = attributes.getValue("name");
skip = (temp != null && temp.equals("true"));
try {
if (!skip && name.startsWith("cpe:/a:")) {
entry.setName(name);
} else {
skip = true;
}
} catch (UnsupportedEncodingException ex) {
throw new SAXException(ex);
}
} else if (current.isTitleNode()) {
nodeText = new StringBuilder(100);
if ("en-US".equalsIgnoreCase(attributes.getValue("xml:lang"))) {
languageIsUS = true;
} else {
languageIsUS = false;
}
} else if (current.isMetaNode()) {
try {
entry.setModificationDate(attributes.getValue("modification-date"));
} catch (ParseException ex) {
Logger.getLogger(CVEHandler.class.getName()).log(Level.SEVERE, null, ex);
}
entry.setStatus(attributes.getValue("status"));
entry.setNvdId(attributes.getValue("nvd-id"));
} else if (current.isSchemaVersionNode()) {
nodeText = new StringBuilder(3);
} else if (current.isTimestampNode()) {
nodeText = new StringBuilder(24);
}
// } else if (current.isCpeListNode()) {
// //do nothing
// } else if (current.isNotesNode()) {
// //do nothing
// } else if (current.isNoteNode()) {
// //do nothing
// } else if (current.isCheckNode()) {
// //do nothing
// } else if (current.isGeneratorNode()) {
// //do nothing
// } else if (current.isProductNameNode()) {
// //do nothing
// } else if (current.isProductVersionNode()) {
// //do nothing
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//nodeText += new String(ch, start, length);
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.isCpeItemNode()) {
if (saveDelegate != null && !skip) {
try {
saveDelegate.saveEntry(entry);
} catch (CorruptIndexException ex) {
Logger.getLogger(CVEHandler.class.getName()).log(Level.SEVERE, null, ex);
throw new SAXException(ex);
} catch (IOException ex) {
Logger.getLogger(CVEHandler.class.getName()).log(Level.SEVERE, null, ex);
throw new SAXException(ex);
}
entry = null;
}
} else if (current.isTitleNode()) {
if (languageIsUS) {
entry.setTitle(nodeText.toString());
}
} else if (current.isSchemaVersionNode() && !CURRENT_SCHEMA_VERSION.equals(nodeText.toString())) {
throw new SAXException("ERROR: Invalid Schema Version, expected: "
+ CURRENT_SCHEMA_VERSION + ", file is: " + nodeText);
}
// } else if (current.isCpeListNode()) {
// //do nothing
// } else if (current.isMetaNode()) {
// //do nothing
// } else if (current.isNotesNode()) {
// //do nothing
// } else if (current.isNoteNode()) {
// //do nothing
// } else if (current.isCheckNode()) {
// //do nothing
// } else if (current.isGeneratorNode()) {
// //do nothing
// } else if (current.isProductNameNode()) {
// //do nothing
// } else if (current.isProductVersionNode()) {
// //do nothing
// else if (current.isTimestampNode()) {
// //do nothing
// } else {
// throw new SAXException("ERROR STATE: Unexpected qName '" + qName + "'");
// }
}
// <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 CPE XML.
*/
protected class Element {
/**
* A node type in the CPE Schema 2.2
*/
public static final String CPE_LIST = "cpe-list";
/**
* A node type in the CPE Schema 2.2
*/
public static final String CPE_ITEM = "cpe-item";
/**
* A node type in the CPE Schema 2.2
*/
public static final String TITLE = "title";
/**
* A node type in the CPE Schema 2.2
*/
public static final String NOTES = "notes";
/**
* A node type in the CPE Schema 2.2
*/
public static final String NOTE = "note";
/**
* A node type in the CPE Schema 2.2
*/
public static final String CHECK = "check";
/**
* A node type in the CPE Schema 2.2
*/
public static final String META = "meta:item-metadata";
/**
* A node type in the CPE Schema 2.2
*/
public static final String GENERATOR = "generator";
/**
* A node type in the CPE Schema 2.2
*/
public static final String PRODUCT_NAME = "product_name";
/**
* A node type in the CPE Schema 2.2
*/
public static final String PRODUCT_VERSION = "product_version";
/**
* A node type in the CPE Schema 2.2
*/
public static final String SCHEMA_VERSION = "schema_version";
/**
* A node type in the CPE Schema 2.2
*/
public static final String TIMESTAMP = "timestamp";
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 CPE_LIST node
*
* @return true or false
*/
public boolean isCpeListNode() {
return CPE_LIST.equals(node);
}
/**
* Checks if the handler is at the CPE_ITEM node
*
* @return true or false
*/
public boolean isCpeItemNode() {
return CPE_ITEM.equals(node);
}
/**
* Checks if the handler is at the TITLE node
*
* @return true or false
*/
public boolean isTitleNode() {
return TITLE.equals(node);
}
/**
* Checks if the handler is at the NOTES node
*
* @return true or false
*/
public boolean isNotesNode() {
return NOTES.equals(node);
}
/**
* Checks if the handler is at the NOTE node
*
* @return true or false
*/
public boolean isNoteNode() {
return NOTE.equals(node);
}
/**
* Checks if the handler is at the CHECK node
*
* @return true or false
*/
public boolean isCheckNode() {
return CHECK.equals(node);
}
/**
* Checks if the handler is at the META node
*
* @return true or false
*/
public boolean isMetaNode() {
return META.equals(node);
}
/**
* Checks if the handler is at the GENERATOR node
*
* @return true or false
*/
public boolean isGeneratorNode() {
return GENERATOR.equals(node);
}
/**
* Checks if the handler is at the PRODUCT_NAME node
*
* @return true or false
*/
public boolean isProductNameNode() {
return PRODUCT_NAME.equals(node);
}
/**
* Checks if the handler is at the PRODUCT_VERSION node
*
* @return true or false
*/
public boolean isProductVersionNode() {
return PRODUCT_VERSION.equals(node);
}
/**
* Checks if the handler is at the SCHEMA_VERSION node
*
* @return true or false
*/
public boolean isSchemaVersionNode() {
return SCHEMA_VERSION.equals(node);
}
/**
* Checks if the handler is at the TIMESTAMP node
*
* @return true or false
*/
public boolean isTimestampNode() {
return TIMESTAMP.equals(node);
}
}
// </editor-fold>
}

View File

@@ -0,0 +1,42 @@
package org.codesecure.dependencycheck.data.cve.xml;
/*
* This file is part of DependencyCheck.
*
* DependencyCheck is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DependencyCheck is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import org.codesecure.dependencycheck.data.cve.Entry;
import java.io.IOException;
import org.apache.lucene.index.CorruptIndexException;
/**
*
* An interface used to define the save function used when parsing the CVE XML
* file.
*
* @author Jeremy Long (jeremy.long@gmail.com)
*/
public interface EntrySaveDelegate {
/**
* Saves a CVE Entry into the Lucene index.
*
* @param entry a CVE entry.
* @throws CorruptIndexException is thrown if the index is corrupt.
* @throws IOException is thrown if an IOException occurs.
*/
void saveEntry(Entry entry) throws CorruptIndexException, IOException;
}

View File

@@ -0,0 +1,76 @@
package org.codesecure.dependencycheck.data.cve.xml;
/*
* This file is part of DependencyCheck.
*
* DependencyCheck is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DependencyCheck is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DependencyCheck. If not, see http://www.gnu.org/licenses/.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
import org.codesecure.dependencycheck.data.cve.Indexer;
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
/**
* Imports a CVE XML file into the Lucene CVE Index.
*
* @author Jeremy Long (jeremy.long@gmail.com)
*/
public class Importer {
/**
* Private constructor for utility class.
*/
private Importer() {
}
/**
* Imports the CPE XML File into the Lucene Index.
*
* @param file containing the path to the CPE XML file.
* @throws ParserConfigurationException is thrown if the parser is misconfigured.
* @throws SAXException is thrown when there is a SAXException.
* @throws IOException is thrown when there is an IOException.
*/
public static void importXML(File file) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
CVEHandler handler = new CVEHandler();
Indexer indexer = new Indexer();
indexer.open();
handler.registerSaveDelegate(indexer);
saxParser.parse(file, handler);
indexer.close();
}
/**
* Imports the CPE XML File into the Lucene Index.
*
* @param path the path to the CPE XML file.
* @throws ParserConfigurationException is thrown if the parser is misconfigured.
* @throws SAXException is thrown when there is a SAXException.
* @throws IOException is thrown when there is an IOException.
*/
public static void importXML(String path) throws ParserConfigurationException, SAXException, IOException {
File f = new File(path);
if (!f.exists()) {
f.mkdirs();
}
Importer.importXML(f);
}
}

View File

@@ -0,0 +1,12 @@
/**
* <html>
* <head>
* <title>org.codesecure.dependencycheck.data.cve.xml</title>
* </head>
* <body>
* Contains classes used to parse the CVE XML file.
* </body>
* </html>
*/
package org.codesecure.dependencycheck.data.cve.xml;

View File

@@ -57,9 +57,9 @@ public class Dependency {
*/
protected EvidenceCollection vendorEvidence = null;
/**
* A collection of title evidence.
* A collection of product evidence.
*/
protected EvidenceCollection titleEvidence = null;
protected EvidenceCollection productEvidence = null;
/**
* A collection of version evidence.
*/
@@ -70,7 +70,7 @@ public class Dependency {
*/
public Dependency() {
vendorEvidence = new EvidenceCollection();
titleEvidence = new EvidenceCollection();
productEvidence = new EvidenceCollection();
versionEvidence = new EvidenceCollection();
cpes = new ArrayList<String>();
}
@@ -181,7 +181,7 @@ public class Dependency {
* @return an EvidenceCollection.
*/
public EvidenceCollection getEvidence() {
return EvidenceCollection.mergeUsed(this.titleEvidence, this.vendorEvidence, this.versionEvidence);
return EvidenceCollection.mergeUsed(this.productEvidence, this.vendorEvidence, this.versionEvidence);
}
@@ -191,7 +191,7 @@ public class Dependency {
* @return an EvidenceCollection.
*/
public EvidenceCollection getEvidenceUsed() {
EvidenceCollection ec = EvidenceCollection.mergeUsed(this.titleEvidence, this.vendorEvidence, this.versionEvidence);
EvidenceCollection ec = EvidenceCollection.mergeUsed(this.productEvidence, this.vendorEvidence, this.versionEvidence);
return ec;
}
@@ -205,12 +205,12 @@ public class Dependency {
}
/**
* Gets the Title Evidence.
* Gets the Product Evidence.
*
* @return an EvidenceCollection.
*/
public EvidenceCollection getTitleEvidence() {
return this.titleEvidence;
public EvidenceCollection getProductEvidence() {
return this.productEvidence;
}
/**
@@ -244,7 +244,7 @@ public class Dependency {
if (vendorEvidence.containsUsedString(str)) {
return true;
}
if (titleEvidence.containsUsedString(str)) {
if (productEvidence.containsUsedString(str)) {
return true;
}
if (versionEvidence.containsUsedString(fnd)) {

View File

@@ -179,7 +179,7 @@ public class JarAnalyzer extends AbstractAnalyzer {
}
Pattern rx = Pattern.compile("\\s\\s+");
fileNameEvidence = rx.matcher(sb.toString()).replaceAll(" ");
dependency.getTitleEvidence().addEvidence("jar", "file name",
dependency.getProductEvidence().addEvidence("jar", "file name",
fileNameEvidence, Evidence.Confidence.HIGH);
dependency.getVendorEvidence().addEvidence("jar", "file name",
fileNameEvidence, Evidence.Confidence.HIGH);
@@ -276,14 +276,14 @@ public class JarAnalyzer extends AbstractAnalyzer {
return;
}
EvidenceCollection vendor = dependency.getVendorEvidence();
EvidenceCollection title = dependency.getTitleEvidence();
EvidenceCollection product = dependency.getProductEvidence();
for (String s : level0.keySet()) {
if (!"org".equals(s) && !"com".equals(s)) {
vendor.addWeighting(s);
title.addWeighting(s);
product.addWeighting(s);
vendor.addEvidence("jar", "package", s, Evidence.Confidence.LOW);
title.addEvidence("jar", "package", s, Evidence.Confidence.LOW);
product.addEvidence("jar", "package", s, Evidence.Confidence.LOW);
}
}
for (String s : level1.keySet()) {
@@ -296,9 +296,9 @@ public class JarAnalyzer extends AbstractAnalyzer {
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
} else {
vendor.addWeighting(parts[0]);
title.addWeighting(parts[1]);
product.addWeighting(parts[1]);
vendor.addEvidence("jar", "package", parts[0], Evidence.Confidence.LOW);
title.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
product.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
}
}
}
@@ -309,18 +309,18 @@ public class JarAnalyzer extends AbstractAnalyzer {
String[] parts = s.split("/");
if ("org".equals(parts[0]) || "com".equals(parts[0])) {
vendor.addWeighting(parts[1]);
title.addWeighting(parts[2]);
product.addWeighting(parts[2]);
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
title.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
product.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
} else {
vendor.addWeighting(parts[0]);
vendor.addWeighting(parts[1]);
title.addWeighting(parts[1]);
title.addWeighting(parts[2]);
product.addWeighting(parts[1]);
product.addWeighting(parts[2]);
vendor.addEvidence("jar", "package", parts[0], Evidence.Confidence.LOW);
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
title.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
title.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
product.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
product.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
}
}
}
@@ -332,26 +332,26 @@ public class JarAnalyzer extends AbstractAnalyzer {
if ("org".equals(parts[0]) || "com".equals(parts[0])) {
vendor.addWeighting(parts[1]);
vendor.addWeighting(parts[2]);
title.addWeighting(parts[2]);
title.addWeighting(parts[3]);
product.addWeighting(parts[2]);
product.addWeighting(parts[3]);
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
vendor.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
title.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
title.addEvidence("jar", "package", parts[3], Evidence.Confidence.LOW);
product.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
product.addEvidence("jar", "package", parts[3], Evidence.Confidence.LOW);
} else {
vendor.addWeighting(parts[0]);
vendor.addWeighting(parts[1]);
vendor.addWeighting(parts[2]);
title.addWeighting(parts[1]);
title.addWeighting(parts[2]);
title.addWeighting(parts[3]);
product.addWeighting(parts[1]);
product.addWeighting(parts[2]);
product.addWeighting(parts[3]);
vendor.addEvidence("jar", "package", parts[0], Evidence.Confidence.LOW);
vendor.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
vendor.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
title.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
title.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
title.addEvidence("jar", "package", parts[3], Evidence.Confidence.LOW);
product.addEvidence("jar", "package", parts[1], Evidence.Confidence.LOW);
product.addEvidence("jar", "package", parts[2], Evidence.Confidence.LOW);
product.addEvidence("jar", "package", parts[3], Evidence.Confidence.LOW);
}
}
}
@@ -380,7 +380,7 @@ public class JarAnalyzer extends AbstractAnalyzer {
Attributes atts = manifest.getMainAttributes();
EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
EvidenceCollection titleEvidence = dependency.getTitleEvidence();
EvidenceCollection productEvidence = dependency.getProductEvidence();
EvidenceCollection versionEvidence = dependency.getVersionEvidence();
String source = "Manifest";
@@ -389,7 +389,7 @@ public class JarAnalyzer extends AbstractAnalyzer {
String key = entry.getKey().toString();
String value = atts.getValue(key);
if (key.equals(Attributes.Name.IMPLEMENTATION_TITLE.toString())) {
titleEvidence.addEvidence(source, key, value, Evidence.Confidence.HIGH);
productEvidence.addEvidence(source, key, value, Evidence.Confidence.HIGH);
} else if (key.equals(Attributes.Name.IMPLEMENTATION_VERSION.toString())) {
versionEvidence.addEvidence(source, key, value, Evidence.Confidence.HIGH);
} else if (key.equals(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) {
@@ -397,15 +397,15 @@ public class JarAnalyzer extends AbstractAnalyzer {
} else if (key.equals(Attributes.Name.IMPLEMENTATION_VENDOR_ID.toString())) {
vendorEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
} else if (key.equals(BUNDLE_DESCRIPTION)) {
titleEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
productEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
} else if (key.equals(BUNDLE_NAME)) {
titleEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
productEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
} else if (key.equals(BUNDLE_VENDOR)) {
vendorEvidence.addEvidence(source, key, value, Evidence.Confidence.HIGH);
} else if (key.equals(BUNDLE_VERSION)) {
versionEvidence.addEvidence(source, key, value, Evidence.Confidence.HIGH);
} else if (key.equals(Attributes.Name.MAIN_CLASS.toString())) {
titleEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
productEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
vendorEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
} else {
key = key.toLowerCase();
@@ -414,14 +414,14 @@ public class JarAnalyzer extends AbstractAnalyzer {
if (key.contains("version")) {
versionEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
} else if (key.contains("title")) {
titleEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
productEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
} else if (key.contains("vendor")) {
vendorEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
} else if (key.contains("name")) {
titleEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
productEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
vendorEvidence.addEvidence(source, key, value, Evidence.Confidence.MEDIUM);
} else {
titleEvidence.addEvidence(source, key, value, Evidence.Confidence.LOW);
productEvidence.addEvidence(source, key, value, Evidence.Confidence.LOW);
vendorEvidence.addEvidence(source, key, value, Evidence.Confidence.LOW);
if (value.matches(".*\\d.*")) {
versionEvidence.addEvidence(source, key, value, Evidence.Confidence.LOW);

View File

@@ -56,13 +56,7 @@ public class IndexTest extends BaseIndexTestCase {
} catch (IOException ex) {
fail(ex.getMessage());
}
try {
instance.close();
} catch (CorruptIndexException ex) {
fail(ex.getMessage());
} catch (IOException ex) {
fail(ex.getMessage());
}
instance.close();
}
/**
@@ -71,7 +65,8 @@ public class IndexTest extends BaseIndexTestCase {
@Test
public void testGetDirectory() throws Exception {
System.out.println("getDirectory");
Directory result = Index.getDirectory();
Index index = new Index();
Directory result = index.getDirectory();
String exp = "\\target\\store\\cpe";
// TODO review the generated test code and remove the default call to fail.
assertTrue(result.toString().contains(exp));

View File

@@ -1,33 +0,0 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.codesecure.dependencycheck.data.cpe;
import org.codesecure.dependencycheck.data.BaseIndexTestCase;
/**
*
* @author jeremy
*/
public class IndexTestCase extends BaseIndexTestCase {
public IndexTestCase(String testName) {
super(testName);
}
@Override
protected void setUp() throws Exception {
super.setUp();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
public void testIgnoreThisClass() throws Exception {
assertTrue(true);
}
}

View File

@@ -68,10 +68,10 @@ public class ReportGeneratorTest extends BaseIndexTestCase {
d.addCPEentry("cpe://a:/some:cpe:1.0");
List<Dependency> dependencies = new ArrayList<Dependency>();
d.getTitleEvidence().addEvidence("jar","filename","<test>test", Confidence.HIGH);
d.getTitleEvidence().addEvidence("manifest","vendor","<test>test", Confidence.HIGH);
d.getProductEvidence().addEvidence("jar","filename","<test>test", Confidence.HIGH);
d.getProductEvidence().addEvidence("manifest","vendor","<test>test", Confidence.HIGH);
for (Evidence e : d.getTitleEvidence().iterator(Confidence.HIGH)) {
for (Evidence e : d.getProductEvidence().iterator(Confidence.HIGH)) {
String t = e.getValue();
}
dependencies.add(d);
@@ -82,10 +82,10 @@ public class ReportGeneratorTest extends BaseIndexTestCase {
d2.addCPEentry("cpe://a:/another:cpe:1.0");
d2.addCPEentry("cpe://a:/another:cpe:1.1");
d2.addCPEentry("cpe://a:/another:cpe:1.2");
d2.getTitleEvidence().addEvidence("jar","filename","another.jar", Confidence.HIGH);
d2.getTitleEvidence().addEvidence("manifest","vendor","Company A", Confidence.MEDIUM);
d2.getProductEvidence().addEvidence("jar","filename","another.jar", Confidence.HIGH);
d2.getProductEvidence().addEvidence("manifest","vendor","Company A", Confidence.MEDIUM);
for (Evidence e : d2.getTitleEvidence().iterator(Confidence.HIGH)) {
for (Evidence e : d2.getProductEvidence().iterator(Confidence.HIGH)) {
String t = e.getValue();
}
@@ -94,9 +94,9 @@ public class ReportGeneratorTest extends BaseIndexTestCase {
Dependency d3 = new Dependency();
d3.setFileName("Third.jar");
d3.setFilePath("lib/Third.jar");
d3.getTitleEvidence().addEvidence("jar","filename","third.jar", Confidence.HIGH);
d3.getProductEvidence().addEvidence("jar","filename","third.jar", Confidence.HIGH);
for (Evidence e : d3.getTitleEvidence().iterator(Confidence.HIGH)) {
for (Evidence e : d3.getProductEvidence().iterator(Confidence.HIGH)) {
String t = e.getValue();
}

View File

@@ -59,7 +59,7 @@ public class JarAnalyzerTest {
result = instance.insepct(file);
boolean found = false;
for (Evidence e : result.getTitleEvidence()) {
for (Evidence e : result.getProductEvidence()) {
if (e.getName().equals("package-title") && e.getValue().equals("org.mortbay.http")) {
found = true;
break;