View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2022 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.analyzer;
19  
20  import java.util.List;
21  import org.owasp.dependencycheck.Engine;
22  import static org.owasp.dependencycheck.analyzer.AbstractSuppressionAnalyzer.SUPPRESSION_OBJECT_KEY;
23  import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
24  import org.owasp.dependencycheck.dependency.Dependency;
25  import org.owasp.dependencycheck.utils.Settings;
26  import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  /**
31   * Log the unused suppression rules.
32   *
33   * @author Jeremy Long
34   */
35  public class UnusedSuppressionRuleAnalyzer extends AbstractAnalyzer {
36  
37      /**
38       * Exception message.
39       */
40      protected static final String EXCEPTION_MSG = "There are %d unused suppression rule(s): check logs.";
41  
42      /**
43       * The Logger for use throughout the class.
44       */
45      private static final Logger LOGGER = LoggerFactory.getLogger(UnusedSuppressionRuleAnalyzer.class);
46      /**
47       * A flag indicating whether or not the unused vulnerabilities have already
48       * been reported.
49       */
50      private boolean reported = false;
51      /**
52       * A flag indicating whether build should fail on unused suppression rule
53       */
54      private boolean shouldFailForUnusedSuppressionRule = false;
55      /**
56       * unused suppression rule count
57       */
58      private int unusedSuppressionRuleCount = 0;
59  
60      @Override
61      public synchronized void initialize(Settings settings) {
62          super.initialize(settings);
63          if (settings.getBoolean(Settings.KEYS.FAIL_ON_UNUSED_SUPPRESSION_RULE, false)) {
64              this.shouldFailForUnusedSuppressionRule = true;
65          }
66      }
67  
68      @Override
69      protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
70          if (!reported) {
71              checkUnusedRules(engine);
72              reported = true;
73              if (unusedSuppressionRuleCount > 0 && failsForUnusedSuppressionRule()) {
74                  final String message = String.format(EXCEPTION_MSG, unusedSuppressionRuleCount);
75                  LOGGER.error(message);
76                  throw new AnalysisException(message);
77              }
78          }
79      }
80  
81      /**
82       * check unused suppression RULES.
83       *
84       * @param engine a reference to the ODC engine
85       */
86      protected void checkUnusedRules(Engine engine) {
87          if (engine.hasObject(SUPPRESSION_OBJECT_KEY)) {
88              @SuppressWarnings("unchecked")
89              final List<SuppressionRule> rules = (List<SuppressionRule>) engine.getObject(SUPPRESSION_OBJECT_KEY);
90              rules.forEach((rule) -> {
91                  if (!rule.isMatched() && !rule.isBase()) {
92                      final String message = String.format("Suppression Rule had zero matches: %s", rule);
93                      if (failsForUnusedSuppressionRule()) {
94                          LOGGER.error(message);
95                      } else {
96                          LOGGER.info(message);
97                      }
98                      increaseUnusedSuppressionRuleCount();
99                  }
100             });
101         }
102     }
103 
104     @Override
105     protected String getAnalyzerEnabledSettingKey() {
106         //technically incorrect - but we will reuse the enabled key for this analyzer
107         return Settings.KEYS.ANALYZER_VULNERABILITY_SUPPRESSION_ENABLED;
108     }
109 
110     @Override
111     public String getName() {
112         return "Unused Suppression Rule Analyzer";
113     }
114 
115     @Override
116     public AnalysisPhase getAnalysisPhase() {
117         return AnalysisPhase.FINAL;
118     }
119 
120     @Override
121     public boolean supportsParallelProcessing() {
122         return false;
123     }
124 
125     /**
126      * increases the count of unused suppression rules.
127      */
128     public void increaseUnusedSuppressionRuleCount() {
129         unusedSuppressionRuleCount++;
130     }
131 
132     /**
133      * @return the count of unused suppression rules.
134      */
135     public int getUnusedSuppressionRuleCount() {
136         return unusedSuppressionRuleCount;
137     }
138 
139     /**
140      * @return whether the analyzer will fail for a unused suppression rule.
141      */
142     public boolean failsForUnusedSuppressionRule() {
143         return shouldFailForUnusedSuppressionRule;
144     }
145 }