3030import java .util .Set ;
3131import java .util .regex .Pattern ;
3232import javax .annotation .concurrent .ThreadSafe ;
33+
34+ import org .jetbrains .annotations .NotNull ;
3335import org .owasp .dependencycheck .Engine ;
3436import org .owasp .dependencycheck .analyzer .exception .AnalysisException ;
3537import org .owasp .dependencycheck .data .update .HostedSuppressionsDataSource ;
@@ -68,6 +70,10 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
6870 * The file name of the base suppression XML file.
6971 */
7072 private static final String BASE_SUPPRESSION_FILE = "dependencycheck-base-suppression.xml" ;
73+ /**
74+ * The file name of the snapshot of the hosted suppression XML file.
75+ */
76+ private static final String HOSTED_SUPPRESSION_SNAPSHOT_FILE = "dependencycheck-hosted-suppression-snapshot.xml" ;
7177 /**
7278 * The key used to store and retrieve the suppression files.
7379 */
@@ -188,32 +194,16 @@ private void loadSuppressionBaseData(final Engine engine) throws SuppressionPars
188194 }
189195
190196 /**
191- * Loads the base suppression rules packaged with the application.
197+ * Loads the suppression rules packaged with the application.
192198 *
193199 * @param parser The suppression parser to use
194200 * @param engine a reference the dependency-check engine
195201 * @throws SuppressionParseException thrown if the XML cannot be parsed.
196202 */
197203 private void loadPackagedSuppressionBaseData (final SuppressionParser parser , final Engine engine ) throws SuppressionParseException {
198204 List <SuppressionRule > ruleList = null ;
199- final URL jarLocation = AbstractSuppressionAnalyzer .class .getProtectionDomain ().getCodeSource ().getLocation ();
200- String suppressionFileLocation = jarLocation .getFile ();
201- if (suppressionFileLocation .endsWith (".jar" )) {
202- suppressionFileLocation = "jar:file:" + suppressionFileLocation + "!/" + BASE_SUPPRESSION_FILE ;
203- } else if (suppressionFileLocation .startsWith ("nested:" ) && suppressionFileLocation .endsWith (".jar!/" )) {
204- // suppressionFileLocation -> nested:/app/app.jar/!BOOT-INF/lib/dependency-check-core-<version>.jar!/
205- // goal-> jar:nested:/app/app.jar/!BOOT-INF/lib/dependency-check-core-<version>.jar!/dependencycheck-base-suppression.xml
206- suppressionFileLocation = "jar:" + suppressionFileLocation + BASE_SUPPRESSION_FILE ;
207- } else {
208- suppressionFileLocation = "file:" + suppressionFileLocation + BASE_SUPPRESSION_FILE ;
209- }
210- URL baseSuppresssionURL = null ;
211- try {
212- baseSuppresssionURL = new URL (suppressionFileLocation );
213- } catch (MalformedURLException e ) {
214- throw new SuppressionParseException ("Unable to load the base suppression data file" , e );
215- }
216- try (InputStream in = baseSuppresssionURL .openStream ()) {
205+ URL baseSuppressionURL = getPackagedFile (BASE_SUPPRESSION_FILE );
206+ try (InputStream in = baseSuppressionURL .openStream ()) {
217207 ruleList = parser .parseSuppressionRules (in );
218208 } catch (SAXException | IOException ex ) {
219209 throw new SuppressionParseException ("Unable to parse the base suppression data file" , ex );
@@ -229,6 +219,27 @@ private void loadPackagedSuppressionBaseData(final SuppressionParser parser, fin
229219 }
230220 }
231221
222+ private static @ NotNull URL getPackagedFile (String packagedFileName ) throws SuppressionParseException {
223+ final URL jarLocation = AbstractSuppressionAnalyzer .class .getProtectionDomain ().getCodeSource ().getLocation ();
224+ String suppressionFileLocation = jarLocation .getFile ();
225+ if (suppressionFileLocation .endsWith (".jar" )) {
226+ suppressionFileLocation = "jar:file:" + suppressionFileLocation + "!/" + packagedFileName ;
227+ } else if (suppressionFileLocation .startsWith ("nested:" ) && suppressionFileLocation .endsWith (".jar!/" )) {
228+ // suppressionFileLocation -> nested:/app/app.jar/!BOOT-INF/lib/dependency-check-core-<version>.jar!/
229+ // goal-> jar:nested:/app/app.jar/!BOOT-INF/lib/dependency-check-core-<version>.jar!/dependencycheck-base-suppression.xml
230+ suppressionFileLocation = "jar:" + suppressionFileLocation + packagedFileName ;
231+ } else {
232+ suppressionFileLocation = "file:" + suppressionFileLocation + packagedFileName ;
233+ }
234+ URL baseSuppressionURL = null ;
235+ try {
236+ baseSuppressionURL = new URL (suppressionFileLocation );
237+ } catch (MalformedURLException e ) {
238+ throw new SuppressionParseException ("Unable to load the packaged file: " + packagedFileName , e );
239+ }
240+ return baseSuppressionURL ;
241+ }
242+
232243 /**
233244 * Loads all the base suppression rules from the hosted suppression file
234245 * generated/updated automatically by the FP Suppression GitHub Action for
@@ -242,31 +253,28 @@ private void loadPackagedSuppressionBaseData(final SuppressionParser parser, fin
242253 * @param parser The suppression parser to use
243254 */
244255 private void loadHostedSuppressionBaseData (final SuppressionParser parser , final Engine engine ) {
245- final File repoFile ;
246- boolean repoEmpty = false ;
247256 final boolean enabled = getSettings ().getBoolean (Settings .KEYS .HOSTED_SUPPRESSIONS_ENABLED , true );
248257 if (!enabled ) {
249258 return ;
250259 }
251- final boolean autoupdate = getSettings ().getBoolean (Settings .KEYS .AUTO_UPDATE , true );
252- final boolean forceupdate = getSettings ().getBoolean (Settings .KEYS .HOSTED_SUPPRESSIONS_FORCEUPDATE , false );
253260
254261 try {
255262 final String configuredUrl = getSettings ().getString (Settings .KEYS .HOSTED_SUPPRESSIONS_URL ,
256263 HostedSuppressionsDataSource .DEFAULT_SUPPRESSIONS_URL );
257264 final URL url = new URL (configuredUrl );
258265 final String fileName = new File (url .getPath ()).getName ();
259- repoFile = new File (getSettings ().getDataDirectory (), fileName );
260- if (!repoFile .isFile () || repoFile .length () <= 1L ) {
261- repoEmpty = true ;
262- LOGGER .warn ("Hosted Suppressions file is empty or missing - attempting to force the update" );
263- getSettings ().setBoolean (Settings .KEYS .HOSTED_SUPPRESSIONS_FORCEUPDATE , true );
264- }
265- if ((!autoupdate && forceupdate ) || (autoupdate && repoEmpty )) {
266- if (engine == null ) {
267- LOGGER .warn ("Engine was null, this should only happen in tests - skipping forced update" );
268- } else {
269- repoEmpty = forceUpdateHostedSuppressions (engine , repoFile );
266+ final File repoFile = new File (getSettings ().getDataDirectory (), fileName );
267+ boolean repoEmpty = !repoFile .isFile () || repoFile .length () <= 1L ;
268+ if (repoEmpty ) {
269+ // utilize the snapshot hosted suppression file
270+ URL hostedSuppressionSnapshotURL = getPackagedFile (HOSTED_SUPPRESSION_SNAPSHOT_FILE );
271+ try (InputStream in = hostedSuppressionSnapshotURL .openStream ()) {
272+ Files .copy (in , repoFile .toPath (), StandardCopyOption .REPLACE_EXISTING );
273+ repoEmpty = false ;
274+ LOGGER .debug ("Copied hosted suppression snapshot file to {}" , repoFile .toPath ());
275+ } catch (IOException ex ) {
276+ LOGGER .warn ("Unable to copy the hosted suppression snapshot file to {}, results may contain false positives "
277+ + "already resolved by the DependencyCheck project" , repoFile .toPath (), ex );
270278 }
271279 }
272280 if (!repoEmpty ) {
0 commit comments