mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-01-14 07:43:40 +01:00
initial fix for CveDB singleton
This commit is contained in:
@@ -941,13 +941,14 @@ public class Check extends Update {
|
||||
}
|
||||
}
|
||||
DatabaseProperties prop = null;
|
||||
CveDB cve;
|
||||
try {
|
||||
cve = CveDB.getInstance();
|
||||
final CveDB cve = CveDB.getInstance();
|
||||
prop = cve.getDatabaseProperties();
|
||||
} catch (DatabaseException ex) {
|
||||
//TODO shouldn't this be a fatal exception
|
||||
log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG);
|
||||
} finally {
|
||||
CveDB.close();
|
||||
}
|
||||
|
||||
final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop);
|
||||
|
||||
@@ -285,6 +285,7 @@ public class App {
|
||||
final CveDB cve = CveDB.getInstance();
|
||||
final DatabaseProperties prop = cve.getDatabaseProperties();
|
||||
final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop);
|
||||
CveDB.close();
|
||||
try {
|
||||
report.generateReports(reportDirectory, outputFormat);
|
||||
} catch (ReportException ex) {
|
||||
|
||||
@@ -126,11 +126,7 @@ public class Engine implements FileFilter {
|
||||
* Properly cleans up resources allocated during analysis.
|
||||
*/
|
||||
public void cleanup() {
|
||||
try {
|
||||
CveDB.getInstance().closeDatabase();
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.trace("Error closing the database", ex);
|
||||
}
|
||||
CveDB.close();
|
||||
ConnectionFactory.cleanup();
|
||||
}
|
||||
|
||||
@@ -754,6 +750,7 @@ public class Engine implements FileFilter {
|
||||
if (!cve.dataExists()) {
|
||||
throw new NoDataException("No documents exist");
|
||||
}
|
||||
CveDB.close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -850,6 +850,8 @@ public class DependencyCheckScanAgent {
|
||||
} catch (DatabaseException ex) {
|
||||
//TODO shouldn't this throw an exception or return?
|
||||
LOGGER.debug("Unable to retrieve DB Properties", ex);
|
||||
} finally {
|
||||
CveDB.close();
|
||||
}
|
||||
final ReportGenerator r = new ReportGenerator(this.applicationName, engine.getDependencies(), engine.getAnalyzers(), prop);
|
||||
try {
|
||||
|
||||
@@ -182,6 +182,10 @@ public class CPEAnalyzer extends AbstractAnalyzer {
|
||||
*/
|
||||
@Override
|
||||
public void closeAnalyzer() {
|
||||
if (cve != null) {
|
||||
CveDB.close();
|
||||
cve = null;
|
||||
}
|
||||
if (cpe != null) {
|
||||
cpe.close();
|
||||
cpe = null;
|
||||
|
||||
@@ -68,6 +68,7 @@ public class NvdCveAnalyzer extends AbstractAnalyzer {
|
||||
*/
|
||||
@Override
|
||||
public void closeAnalyzer() {
|
||||
CveDB.close();
|
||||
cveDB = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,14 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the data source.
|
||||
*/
|
||||
@Override
|
||||
public void closeAnalyzer() {
|
||||
CveDB.close();
|
||||
cvedb = null;
|
||||
}
|
||||
/**
|
||||
* Returns the name of the analyzer.
|
||||
*
|
||||
|
||||
@@ -64,6 +64,11 @@ public final class CveDB {
|
||||
* Singleton instance of the CveDB.
|
||||
*/
|
||||
private static CveDB instance = null;
|
||||
/**
|
||||
* Track the number of current users of the CveDB; so that if someone is
|
||||
* using database another user cannot close the connection on them.
|
||||
*/
|
||||
private int usageCount = 0;
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
@@ -75,7 +80,7 @@ public final class CveDB {
|
||||
/**
|
||||
* The bundle of statements used when accessing the database.
|
||||
*/
|
||||
private final ResourceBundle statementBundle;
|
||||
private ResourceBundle statementBundle;
|
||||
/**
|
||||
* Database properties object containing the 'properties' from the database
|
||||
* table.
|
||||
@@ -84,7 +89,7 @@ public final class CveDB {
|
||||
/**
|
||||
* The prepared statements.
|
||||
*/
|
||||
private final EnumMap<PreparedStatementCveDb, PreparedStatement> preparedStatements;
|
||||
private final EnumMap<PreparedStatementCveDb, PreparedStatement> preparedStatements = new EnumMap<>(PreparedStatementCveDb.class);
|
||||
|
||||
/**
|
||||
* The enum value names must match the keys of the statements in the
|
||||
@@ -191,6 +196,10 @@ public final class CveDB {
|
||||
if (instance == null) {
|
||||
instance = new CveDB();
|
||||
}
|
||||
if (!instance.isOpen()) {
|
||||
instance.open();
|
||||
}
|
||||
instance.usageCount += 1;
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -202,23 +211,17 @@ public final class CveDB {
|
||||
* database.
|
||||
*/
|
||||
private CveDB() throws DatabaseException {
|
||||
openDatabase();
|
||||
final String databaseProductName = determineDatabaseProductName();
|
||||
statementBundle = databaseProductName != null
|
||||
? ResourceBundle.getBundle("data/dbStatements", new Locale(databaseProductName))
|
||||
: ResourceBundle.getBundle("data/dbStatements");
|
||||
preparedStatements = prepareStatements();
|
||||
databaseProperties = new DatabaseProperties(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to determine the product name of the database.
|
||||
*
|
||||
* @param conn the database connection
|
||||
* @return the product name of the database if successful, {@code null} else
|
||||
*/
|
||||
private synchronized String determineDatabaseProductName() {
|
||||
private static String determineDatabaseProductName(Connection conn) {
|
||||
try {
|
||||
final String databaseProductName = connection.getMetaData().getDatabaseProductName();
|
||||
final String databaseProductName = conn.getMetaData().getDatabaseProductName();
|
||||
LOGGER.debug("Database product: {}", databaseProductName);
|
||||
return databaseProductName;
|
||||
} catch (SQLException se) {
|
||||
@@ -234,30 +237,42 @@ public final class CveDB {
|
||||
* @throws DatabaseException thrown if there is an error opening the
|
||||
* database connection
|
||||
*/
|
||||
public synchronized void openDatabase() throws DatabaseException {
|
||||
if (!isOpen()) {
|
||||
connection = ConnectionFactory.getConnection();
|
||||
private synchronized void open() throws DatabaseException {
|
||||
if (!instance.isOpen()) {
|
||||
instance.connection = ConnectionFactory.getConnection();
|
||||
final String databaseProductName = determineDatabaseProductName(instance.connection);
|
||||
instance.statementBundle = databaseProductName != null
|
||||
? ResourceBundle.getBundle("data/dbStatements", new Locale(databaseProductName))
|
||||
: ResourceBundle.getBundle("data/dbStatements");
|
||||
instance.prepareStatements();
|
||||
instance.databaseProperties = new DatabaseProperties(instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the DB4O database. Close should be called on this object when it
|
||||
* is done being used.
|
||||
* Closes the database connection. Close should be called on this object
|
||||
* when it is done being used.
|
||||
*/
|
||||
public synchronized void closeDatabase() {
|
||||
if (isOpen()) {
|
||||
closeStatements();
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.error("There was an error attempting to close the CveDB, see the log for more details.");
|
||||
LOGGER.debug("", ex);
|
||||
} catch (Throwable ex) {
|
||||
LOGGER.error("There was an exception attempting to close the CveDB, see the log for more details.");
|
||||
LOGGER.debug("", ex);
|
||||
public static synchronized void close() {
|
||||
if (instance != null) {
|
||||
instance.usageCount -= 1;
|
||||
if (instance.usageCount <= 0 && instance.isOpen()) {
|
||||
instance.usageCount = 0;
|
||||
instance.closeStatements();
|
||||
try {
|
||||
instance.connection.close();
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.error("There was an error attempting to close the CveDB, see the log for more details.");
|
||||
LOGGER.debug("", ex);
|
||||
} catch (Throwable ex) {
|
||||
LOGGER.error("There was an exception attempting to close the CveDB, see the log for more details.");
|
||||
LOGGER.debug("", ex);
|
||||
}
|
||||
instance.statementBundle = null;
|
||||
instance.preparedStatements.clear();
|
||||
instance.databaseProperties = null;
|
||||
instance.connection = null;
|
||||
}
|
||||
connection = null;
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,16 +286,13 @@ public final class CveDB {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares all statements to be used and returns them.
|
||||
* Prepares all statements to be used.
|
||||
*
|
||||
* @return the prepared statements
|
||||
* @throws DatabaseException thrown if there is an error preparing the
|
||||
* statements
|
||||
*/
|
||||
private EnumMap<PreparedStatementCveDb, PreparedStatement> prepareStatements()
|
||||
private void prepareStatements()
|
||||
throws DatabaseException {
|
||||
|
||||
final EnumMap<PreparedStatementCveDb, PreparedStatement> result = new EnumMap<>(PreparedStatementCveDb.class);
|
||||
for (PreparedStatementCveDb key : values()) {
|
||||
final String statementString = statementBundle.getString(key.name());
|
||||
final PreparedStatement preparedStatement;
|
||||
@@ -293,9 +305,8 @@ public final class CveDB {
|
||||
} catch (SQLException exception) {
|
||||
throw new DatabaseException(exception);
|
||||
}
|
||||
result.put(key, preparedStatement);
|
||||
preparedStatements.put(key, preparedStatement);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,7 +353,7 @@ public final class CveDB {
|
||||
@SuppressWarnings("FinalizeDeclaration")
|
||||
protected void finalize() throws Throwable {
|
||||
LOGGER.debug("Entering finalize");
|
||||
closeDatabase();
|
||||
close();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
@Override
|
||||
public void update() throws UpdateException {
|
||||
try {
|
||||
CveDB db = CveDB.getInstance();
|
||||
final boolean autoupdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true);
|
||||
final boolean enabled = Settings.getBoolean(Settings.KEYS.UPDATE_VERSION_CHECK_ENABLED, true);
|
||||
final String original = Settings.getString(Settings.KEYS.CVE_ORIGINAL_MODIFIED_20_URL);
|
||||
@@ -105,7 +106,9 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
*/
|
||||
if (enabled && autoupdate && original != null && original.equals(current)) {
|
||||
LOGGER.debug("Begin Engine Version Check");
|
||||
final DatabaseProperties properties = CveDB.getInstance().getDatabaseProperties();
|
||||
|
||||
final DatabaseProperties properties = db.getDatabaseProperties();
|
||||
|
||||
final long lastChecked = Long.parseLong(properties.getProperty(ENGINE_VERSION_CHECKED_ON, "0"));
|
||||
final long now = System.currentTimeMillis();
|
||||
updateToVersion = properties.getProperty(CURRENT_ENGINE_RELEASE, "");
|
||||
@@ -124,6 +127,8 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
throw new UpdateException("Error occurred updating database properties.");
|
||||
} catch (InvalidSettingException ex) {
|
||||
LOGGER.debug("Unable to determine if autoupdate is enabled", ex);
|
||||
} finally {
|
||||
CveDB.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ public class NvdCveUpdater implements CachedWebDataSource {
|
||||
throw new UpdateException("Database Exception, unable to update the data to use the most current data.", ex);
|
||||
} finally {
|
||||
shutdownExecutorServices();
|
||||
CveDB.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +207,8 @@ public class NvdCveUpdater implements CachedWebDataSource {
|
||||
return cve.dataExists();
|
||||
} catch (DatabaseException ex) {
|
||||
return false;
|
||||
} finally {
|
||||
CveDB.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,11 +53,6 @@ public abstract class BaseDBTestCase extends BaseTest {
|
||||
ensureDBExists();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
CveDB.getInstance().closeDatabase();
|
||||
}
|
||||
|
||||
public static void ensureDBExists() throws Exception {
|
||||
File f = new File("./target/data/dc.h2.db");
|
||||
if (f.exists() && f.isFile() && f.length() < 71680) {
|
||||
|
||||
@@ -73,6 +73,7 @@ public class EngineIntegrationTest extends BaseDBTestCase {
|
||||
}
|
||||
CveDB cveDB = CveDB.getInstance();
|
||||
DatabaseProperties dbProp = cveDB.getDatabaseProperties();
|
||||
CveDB.close();
|
||||
ReportGenerator rg = new ReportGenerator("DependencyCheck", instance.getDependencies(), instance.getAnalyzers(), dbProp);
|
||||
rg.generateReports("./target/", "ALL");
|
||||
instance.cleanup();
|
||||
|
||||
@@ -53,7 +53,7 @@ import org.owasp.dependencycheck.exception.InitializationException;
|
||||
* @author Dale Visser
|
||||
*/
|
||||
public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzerTest.class);
|
||||
|
||||
/**
|
||||
@@ -117,7 +117,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
analyzer.analyze(result, engine);
|
||||
int size = engine.getDependencies().size();
|
||||
assertTrue(size >= 1);
|
||||
|
||||
|
||||
Dependency dependency = engine.getDependencies().get(0);
|
||||
assertTrue(dependency.getProductEvidence().toString().toLowerCase().contains("redcarpet"));
|
||||
assertTrue(dependency.getVersionEvidence().toString().toLowerCase().contains("2.2.2"));
|
||||
@@ -136,16 +136,16 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
public void testAddCriticalityToVulnerability() throws AnalysisException, DatabaseException {
|
||||
try {
|
||||
analyzer.initialize();
|
||||
|
||||
|
||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
|
||||
"ruby/vulnerable/gems/sinatra/Gemfile.lock"));
|
||||
final Engine engine = new Engine();
|
||||
analyzer.analyze(result, engine);
|
||||
|
||||
|
||||
Dependency dependency = engine.getDependencies().get(0);
|
||||
Vulnerability vulnerability = dependency.getVulnerabilities().first();
|
||||
assertEquals(vulnerability.getCvssScore(), 5.0f, 0.0);
|
||||
|
||||
|
||||
} catch (InitializationException | DatabaseException | AnalysisException e) {
|
||||
LOGGER.warn("Exception setting up RubyBundleAuditAnalyzer. Make sure Ruby gem bundle-audit is installed. You may also need to set property \"analyzer.bundle.audit.path\".");
|
||||
Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", e);
|
||||
@@ -166,6 +166,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
analyzer.initialize();
|
||||
} catch (Exception e) {
|
||||
//expected, so ignore.
|
||||
LOGGER.error("Exception", e);
|
||||
} finally {
|
||||
assertThat(analyzer.isEnabled(), is(false));
|
||||
LOGGER.info("phantom-bundle-audit is not available. Ruby Bundle Audit Analyzer is disabled as expected.");
|
||||
@@ -197,14 +198,14 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
while (dIterator.hasNext()) {
|
||||
Dependency dept = dIterator.next();
|
||||
LOGGER.info("dept path: " + dept.getActualFilePath());
|
||||
|
||||
|
||||
Set<Identifier> identifiers = dept.getIdentifiers();
|
||||
Iterator<Identifier> idIterator = identifiers.iterator();
|
||||
while (idIterator.hasNext()) {
|
||||
Identifier id = idIterator.next();
|
||||
LOGGER.info(" Identifier: " + id.getValue() + ", type=" + id.getType() + ", url=" + id.getUrl() + ", conf=" + id.getConfidence());
|
||||
}
|
||||
|
||||
|
||||
Set<Evidence> prodEv = dept.getProductEvidence().getEvidence();
|
||||
Iterator<Evidence> it = prodEv.iterator();
|
||||
while (it.hasNext()) {
|
||||
@@ -217,7 +218,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
Evidence e = vIt.next();
|
||||
LOGGER.info(" version: name=" + e.getName() + ", value=" + e.getValue() + ", source=" + e.getSource() + ", confidence=" + e.getConfidence());
|
||||
}
|
||||
|
||||
|
||||
Set<Evidence> vendorEv = dept.getVendorEvidence().getEvidence();
|
||||
Iterator<Evidence> vendorIt = vendorEv.iterator();
|
||||
while (vendorIt.hasNext()) {
|
||||
|
||||
@@ -51,6 +51,8 @@ public class CveDBIntegrationTest extends BaseDBTestCase {
|
||||
instance.commit();
|
||||
} catch (DatabaseException | SQLException ex) {
|
||||
fail(ex.getMessage());
|
||||
} finally {
|
||||
CveDB.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +66,7 @@ public class CveDBIntegrationTest extends BaseDBTestCase {
|
||||
String product = "struts";
|
||||
Set<VulnerableSoftware> result = instance.getCPEs(vendor, product);
|
||||
assertTrue(result.size() > 5);
|
||||
CveDB.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,6 +77,7 @@ public class CveDBIntegrationTest extends BaseDBTestCase {
|
||||
CveDB instance = CveDB.getInstance();
|
||||
Vulnerability result = instance.getVulnerability("CVE-2014-0094");
|
||||
assertEquals("The ParametersInterceptor in Apache Struts before 2.3.16.1 allows remote attackers to \"manipulate\" the ClassLoader via the class parameter, which is passed to the getClass method.", result.getDescription());
|
||||
CveDB.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,6 +114,7 @@ public class CveDBIntegrationTest extends BaseDBTestCase {
|
||||
}
|
||||
}
|
||||
assertTrue("Expected " + expected + ", but was not identified", found);
|
||||
CveDB.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,5 +170,6 @@ public class CveDBIntegrationTest extends BaseDBTestCase {
|
||||
identifiedVersion = new DependencyVersion("1.6.3");
|
||||
results = instance.getMatchingSoftware(versions, "springsource", "spring_framework", identifiedVersion);
|
||||
assertNotNull(results);
|
||||
CveDB.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ public class CveDBMySQLTest extends BaseTest {
|
||||
} catch (DatabaseException ex) {
|
||||
System.out.println("Unable to connect to the My SQL database; verify that the db server is running and that the schema has been generated");
|
||||
fail(ex.getMessage());
|
||||
} finally {
|
||||
CveDB.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +63,8 @@ public class CveDBMySQLTest extends BaseTest {
|
||||
} catch (Exception ex) {
|
||||
System.out.println("Unable to access the My SQL database; verify that the db server is running and that the schema has been generated");
|
||||
throw ex;
|
||||
} finally {
|
||||
CveDB.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +81,8 @@ public class CveDBMySQLTest extends BaseTest {
|
||||
} catch (Exception ex) {
|
||||
System.out.println("Unable to access the My SQL database; verify that the db server is running and that the schema has been generated");
|
||||
throw ex;
|
||||
}
|
||||
} finally {
|
||||
CveDB.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ public class DatabasePropertiesIntegrationTest extends BaseDBTestCase {
|
||||
assertNotNull(instance);
|
||||
//no exception means the call worked... whether or not it is empty depends on if the db is new
|
||||
//assertEquals(expResult, result);
|
||||
CveDB.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,6 +60,7 @@ public class DatabasePropertiesIntegrationTest extends BaseDBTestCase {
|
||||
instance = cveDB.reloadProperties();
|
||||
long results = Long.parseLong(instance.getProperty("NVD CVE " + key));
|
||||
assertEquals(expected, results);
|
||||
CveDB.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,6 +75,7 @@ public class DatabasePropertiesIntegrationTest extends BaseDBTestCase {
|
||||
String expResult = "default";
|
||||
String result = instance.getProperty(key, defaultValue);
|
||||
assertEquals(expResult, result);
|
||||
CveDB.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,6 +90,7 @@ public class DatabasePropertiesIntegrationTest extends BaseDBTestCase {
|
||||
double version = Double.parseDouble(result);
|
||||
assertTrue(version >= 2.8);
|
||||
assertTrue(version <= 10);
|
||||
CveDB.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,5 +102,6 @@ public class DatabasePropertiesIntegrationTest extends BaseDBTestCase {
|
||||
DatabaseProperties instance = cveDB.getDatabaseProperties();
|
||||
Properties result = instance.getProperties();
|
||||
assertTrue(result.size() > 0);
|
||||
CveDB.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ public class ReportGeneratorIntegrationTest extends BaseDBTestCase {
|
||||
|
||||
ReportGenerator generator = new ReportGenerator("Test Report", engine.getDependencies(), engine.getAnalyzers(), dbProp);
|
||||
generator.generateReport(templateName, writeTo);
|
||||
CveDB.close();
|
||||
|
||||
engine.cleanup();
|
||||
|
||||
|
||||
@@ -1017,6 +1017,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
|
||||
}
|
||||
}
|
||||
final ReportGenerator r = new ReportGenerator(p.getName(), engine.getDependencies(), engine.getAnalyzers(), prop);
|
||||
CveDB.close();
|
||||
try {
|
||||
r.generateReports(outputDir.getAbsolutePath(), format);
|
||||
} catch (ReportException ex) {
|
||||
|
||||
Reference in New Issue
Block a user