diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java b/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java index 25423700b8d..9b9518c63fa 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java @@ -28,6 +28,7 @@ import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.data.update.exception.UpdateException; +import org.owasp.dependencycheck.utils.DateUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,6 +73,14 @@ public class DatabaseProperties { * The key for the last check time for the Known Exploited Vulnerabilities. */ public static final String KEV_LAST_CHECKED = "kev.checked"; + /** + * The key for the last check time for the Retire JS repository. + */ + public static final String RETIRE_LAST_CHECKED = "retirejs.checked"; + /** + * The key for the last check time for the hosted suppression file. + */ + public static final String HOSTED_SUPPRESSION_LAST_CHECKED = "hosted.suppression.checked"; /** * The key for the version the Known Exploited Vulnerabilities. */ @@ -214,5 +223,16 @@ public static ZonedDateTime getTimestamp(Properties properties, String key) { } return null; } + + /** + * Returns the database property value in seconds. + * + * @param key the key to the property + * @return the property value in seconds + */ + public long getPropertyInSeconds(String key) { + final String value = getProperty(key, "0"); + return DateUtil.getEpochValueInSeconds(value); + } } diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSource.java b/core/src/main/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSource.java index afa0630eb53..3d5aae940f4 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSource.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/HostedSuppressionsDataSource.java @@ -33,8 +33,9 @@ import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; +import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; -public class HostedSuppressionsDataSource implements CachedWebDataSource { +public class HostedSuppressionsDataSource implements CachedWebDataSource { /** * Static logger. @@ -45,7 +46,10 @@ public class HostedSuppressionsDataSource implements CachedWebDataSource { * The configured settings. */ private Settings settings; - + /** + * The properties obtained from the database. + */ + private DatabaseProperties dbProperties = null; /** * The default URL to the Hosted Suppressions file. */ @@ -55,12 +59,17 @@ public class HostedSuppressionsDataSource implements CachedWebDataSource { * Downloads the current Hosted suppressions file. * * @param engine a reference to the ODC Engine - * @return returns false as no updates are made to the database, just web resources cached locally + * @return returns false as no updates are made to the database, just web + * resources cached locally * @throws UpdateException thrown if the update encountered fatal errors */ @Override public boolean update(Engine engine) throws UpdateException { this.settings = engine.getSettings(); + if (engine.getMode() != Engine.Mode.EVIDENCE_COLLECTION) { + //note this conditional is only to support test cases. + this.dbProperties = engine.getDatabase().getDatabaseProperties(); + } final String configuredUrl = settings.getString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, DEFAULT_SUPPRESSIONS_URL); final boolean autoupdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true); final boolean forceupdate = settings.getBoolean(Settings.KEYS.HOSTED_SUPPRESSIONS_FORCEUPDATE, false); @@ -76,11 +85,14 @@ public boolean update(Engine engine) throws UpdateException { if (proceed) { LOGGER.debug("Begin Hosted Suppressions file update"); fetchHostedSuppressions(settings, url, repoFile); + if (dbProperties != null) { + dbProperties.save(DatabaseProperties.HOSTED_SUPPRESSION_LAST_CHECKED, Long.toString(System.currentTimeMillis() / 1000)); + } } } catch (UpdateException ex) { // only emit a warning, DependencyCheck will continue without taking the latest hosted suppressions into account. LOGGER.warn("Failed to update hosted suppressions file, results may contain false positives already resolved by the " - + "DependencyCheck project", ex); + + "DependencyCheck project", ex); } catch (MalformedURLException ex) { throw new UpdateException(String.format("Invalid URL for Hosted Suppressions file (%s)", configuredUrl), ex); } catch (IOException ex) { @@ -93,8 +105,8 @@ public boolean update(Engine engine) throws UpdateException { * Determines if the we should update the Hosted Suppressions file. * * @param repo the Hosted Suppressions file. - * @return true if an update to the Hosted Suppressions file should - * be performed; otherwise false + * @return true if an update to the Hosted Suppressions file + * should be performed; otherwise false * @throws NumberFormatException thrown if an invalid value is contained in * the database properties */ @@ -102,7 +114,14 @@ protected boolean shouldUpdate(File repo) throws NumberFormatException { boolean proceed = true; if (repo != null && repo.isFile()) { final int validForHours = settings.getInt(Settings.KEYS.HOSTED_SUPPRESSIONS_VALID_FOR_HOURS, 2); - final long lastUpdatedOn = repo.lastModified(); + long lastUpdatedOn = 0; + if (dbProperties != null) { + lastUpdatedOn = dbProperties.getPropertyInSeconds(DatabaseProperties.HOSTED_SUPPRESSION_LAST_CHECKED); + } + if (lastUpdatedOn <= 0) { + //fall back on conversion from file last modified to storing in the db. + lastUpdatedOn = repo.lastModified(); + } final long now = System.currentTimeMillis(); LOGGER.debug("Last updated: {}", lastUpdatedOn); LOGGER.debug("Now: {}", now); @@ -120,7 +139,8 @@ protected boolean shouldUpdate(File repo) throws NumberFormatException { * * @param settings a reference to the dependency-check settings * @param repoUrl the URL to the hosted suppressions file to use - * @param repoFile the local file where the hosted suppressions file is to be placed + * @param repoFile the local file where the hosted suppressions file is to + * be placed * @throws UpdateException thrown if there is an exception during * initialization */ @@ -144,7 +164,7 @@ public boolean purge(Engine engine) { boolean result = true; try { final URL repoUrl = new URL(settings.getString(Settings.KEYS.HOSTED_SUPPRESSIONS_URL, - DEFAULT_SUPPRESSIONS_URL)); + DEFAULT_SUPPRESSIONS_URL)); final String filename = new File(repoUrl.getPath()).getName(); final File repo = new File(settings.getDataDirectory(), filename); if (repo.exists()) { diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/KnownExploitedDataSource.java b/core/src/main/java/org/owasp/dependencycheck/data/update/KnownExploitedDataSource.java index dea02961c39..50fb6154c7d 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/update/KnownExploitedDataSource.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/KnownExploitedDataSource.java @@ -120,7 +120,7 @@ private boolean shouldUpdate() throws UpdateException { if (cveDB.dataExists() && 0 < validForHours) { // ms Valid = valid (hours) x 60 min/hour x 60 sec/min x 1000 ms/sec final long validForSeconds = validForHours * 60L * 60L; - final long lastChecked = getPropertyInSeconds(DatabaseProperties.KEV_LAST_CHECKED); + final long lastChecked = dbProperties.getPropertyInSeconds(DatabaseProperties.KEV_LAST_CHECKED); final long now = System.currentTimeMillis() / 1000; proceed = (now - lastChecked) > validForSeconds; if (!proceed) { @@ -130,15 +130,4 @@ private boolean shouldUpdate() throws UpdateException { return proceed; } - /** - * Returns the database property value in seconds. - * - * @param key the key to the property - * @return the property value in seconds - */ - private long getPropertyInSeconds(String key) { - final String value = dbProperties.getProperty(key, "0"); - return DateUtil.getEpochValueInSeconds(value); - } - } diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/RetireJSDataSource.java b/core/src/main/java/org/owasp/dependencycheck/data/update/RetireJSDataSource.java index 59f4b7a39fa..b4e2880ec90 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/update/RetireJSDataSource.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/RetireJSDataSource.java @@ -24,6 +24,7 @@ import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.exception.WriteLockException; import org.owasp.dependencycheck.utils.Downloader; @@ -54,6 +55,10 @@ public class RetireJSDataSource implements CachedWebDataSource { * The configured settings. */ private Settings settings; + /** + * The properties obtained from the database. + */ + private DatabaseProperties dbProperties = null; /** * The default URL to the RetireJS JavaScript repository. */ @@ -75,6 +80,7 @@ public RetireJSDataSource() { @Override public boolean update(Engine engine) throws UpdateException { this.settings = engine.getSettings(); + this.dbProperties = engine.getDatabase().getDatabaseProperties(); final String configuredUrl = settings.getString(Settings.KEYS.ANALYZER_RETIREJS_REPO_JS_URL, DEFAULT_JS_URL); final boolean autoupdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true); final boolean forceupdate = settings.getBoolean(Settings.KEYS.ANALYZER_RETIREJS_FORCEUPDATE, false); @@ -87,6 +93,7 @@ public boolean update(Engine engine) throws UpdateException { if (proceed) { LOGGER.debug("Begin RetireJS Update"); initializeRetireJsRepo(settings, url, repoFile); + dbProperties.save(DatabaseProperties.RETIRE_LAST_CHECKED, Long.toString(System.currentTimeMillis() / 1000)); } } catch (MalformedURLException ex) { throw new UpdateException(String.format("Invalid URL for RetireJS repository (%s)", configuredUrl), ex); @@ -109,7 +116,11 @@ protected boolean shouldUpdate(File repo) throws NumberFormatException { boolean proceed = true; if (repo != null && repo.isFile()) { final int validForHours = settings.getInt(Settings.KEYS.ANALYZER_RETIREJS_REPO_VALID_FOR_HOURS, 0); - final long lastUpdatedOn = repo.lastModified(); + long lastUpdatedOn = dbProperties.getPropertyInSeconds(DatabaseProperties.RETIRE_LAST_CHECKED); + if (lastUpdatedOn <= 0) { + //fall back on conversion from file last modified to storing in the db. + lastUpdatedOn = repo.lastModified(); + } final long now = System.currentTimeMillis(); LOGGER.debug("Last updated: {}", lastUpdatedOn); LOGGER.debug("Now: {}", now);