1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.analyzer;
19
20 import org.owasp.dependencycheck.Engine;
21 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
22 import org.owasp.dependencycheck.dependency.Dependency;
23 import org.owasp.dependencycheck.dependency.Evidence;
24 import org.owasp.dependencycheck.dependency.EvidenceType;
25 import org.owasp.dependencycheck.exception.InitializationException;
26 import org.owasp.dependencycheck.utils.DownloadFailedException;
27 import org.owasp.dependencycheck.utils.Downloader;
28 import org.owasp.dependencycheck.utils.FileUtils;
29 import org.owasp.dependencycheck.utils.ResourceNotFoundException;
30 import org.owasp.dependencycheck.utils.Settings;
31 import org.owasp.dependencycheck.utils.TooManyRequestsException;
32 import org.owasp.dependencycheck.xml.hints.EvidenceMatcher;
33 import org.owasp.dependencycheck.xml.hints.HintParseException;
34 import org.owasp.dependencycheck.xml.hints.HintParser;
35 import org.owasp.dependencycheck.xml.hints.HintRule;
36 import org.owasp.dependencycheck.xml.hints.VendorDuplicatingHintRule;
37 import org.owasp.dependencycheck.xml.suppression.PropertyType;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40 import org.xml.sax.SAXException;
41
42 import javax.annotation.concurrent.ThreadSafe;
43 import java.io.File;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.net.MalformedURLException;
47 import java.net.URL;
48 import java.nio.file.Files;
49 import java.util.List;
50 import java.util.Set;
51 import java.util.regex.Pattern;
52
53
54
55
56
57
58
59 @ThreadSafe
60 public class HintAnalyzer extends AbstractAnalyzer {
61
62
63
64
65 private static final Logger LOGGER = LoggerFactory.getLogger(HintAnalyzer.class);
66
67
68
69 private static final String HINT_RULE_FILE_NAME = "dependencycheck-base-hint.xml";
70
71
72
73 private HintRule[] hints = null;
74
75
76
77 private VendorDuplicatingHintRule[] vendorHints;
78
79
80
81 private static final String ANALYZER_NAME = "Hint Analyzer";
82
83
84
85 private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.POST_INFORMATION_COLLECTION2;
86
87
88
89
90
91
92 @Override
93 public String getName() {
94 return ANALYZER_NAME;
95 }
96
97
98
99
100
101
102 @Override
103 public AnalysisPhase getAnalysisPhase() {
104 return ANALYSIS_PHASE;
105 }
106
107
108
109
110
111
112
113 @Override
114 protected String getAnalyzerEnabledSettingKey() {
115 return Settings.KEYS.ANALYZER_HINT_ENABLED;
116 }
117
118
119
120
121
122
123
124 @Override
125 public void prepareAnalyzer(Engine engine) throws InitializationException {
126 try {
127 loadHintRules();
128 } catch (HintParseException ex) {
129 LOGGER.debug("Unable to parse hint file", ex);
130 throw new InitializationException("Unable to parse the hint file", ex);
131 }
132 }
133
134
135
136
137
138
139
140
141
142
143 @Override
144 @SuppressWarnings("StringSplitter")
145 protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
146 for (HintRule hint : hints) {
147 boolean matchFound = false;
148 for (EvidenceMatcher given : hint.getGivenVendor()) {
149 if (hasMatchingEvidence(dependency.getEvidence(EvidenceType.VENDOR), given)) {
150 matchFound = true;
151 break;
152 }
153 }
154 if (!matchFound) {
155 for (EvidenceMatcher given : hint.getGivenProduct()) {
156 if (hasMatchingEvidence(dependency.getEvidence(EvidenceType.PRODUCT), given)) {
157 matchFound = true;
158 break;
159 }
160 }
161 }
162 if (!matchFound) {
163 for (EvidenceMatcher given : hint.getGivenVersion()) {
164 if (hasMatchingEvidence(dependency.getEvidence(EvidenceType.VERSION), given)) {
165 matchFound = true;
166 break;
167 }
168 }
169 }
170 if (!matchFound) {
171 for (PropertyType pt : hint.getFileNames()) {
172 if (pt.matches(dependency.getFileName())) {
173 matchFound = true;
174 break;
175 }
176 }
177 }
178 if (matchFound) {
179 hint.getAddVendor().forEach((e) -> {
180 dependency.addEvidence(EvidenceType.VENDOR, e);
181 for (String weighting : e.getValue().split(" ")) {
182 dependency.addVendorWeighting(weighting);
183 }
184 });
185 hint.getAddProduct().forEach((e) -> {
186 dependency.addEvidence(EvidenceType.PRODUCT, e);
187 for (String weighting : e.getValue().split(" ")) {
188 dependency.addProductWeighting(weighting);
189 }
190 });
191 hint.getAddVersion().forEach((e) -> dependency.addEvidence(EvidenceType.VERSION, e));
192
193 hint.getRemoveVendor().forEach((e) -> removeMatchingEvidences(dependency, EvidenceType.VENDOR, e));
194 hint.getRemoveProduct().forEach((e) -> removeMatchingEvidences(dependency, EvidenceType.PRODUCT, e));
195 hint.getRemoveVersion().forEach((e) -> removeMatchingEvidences(dependency, EvidenceType.VERSION, e));
196 }
197 }
198
199 for (Evidence e : dependency.getEvidence(EvidenceType.VENDOR)) {
200 for (VendorDuplicatingHintRule dhr : vendorHints) {
201 if (dhr.getValue().equalsIgnoreCase(e.getValue())) {
202 dependency.addEvidence(EvidenceType.VENDOR, new Evidence(e.getSource() + " (hint)",
203 e.getName(), dhr.getDuplicate(), e.getConfidence(), true));
204 }
205 }
206 }
207 }
208
209
210
211
212
213
214
215
216 private boolean hasMatchingEvidence(Set<Evidence> evidences, EvidenceMatcher criterion) {
217 for (Evidence evidence : evidences) {
218 if (criterion.matches(evidence)) {
219 return true;
220 }
221 }
222 return false;
223 }
224
225
226
227
228
229
230
231
232 private void removeMatchingEvidences(Dependency dependency, EvidenceType type, EvidenceMatcher e) {
233 for (Evidence evidence : dependency.getEvidence(type)) {
234 if (e.matches(evidence)) {
235 dependency.removeEvidence(type, evidence);
236 }
237 }
238 }
239
240
241
242
243
244
245 private void loadHintRules() throws HintParseException {
246 final List<HintRule> localHints;
247 final List<VendorDuplicatingHintRule> localVendorHints;
248 final HintParser parser = new HintParser();
249 File file = null;
250 try (InputStream in = FileUtils.getResourceAsStream(HINT_RULE_FILE_NAME)) {
251 if (in == null) {
252 throw new HintParseException("Hint rules `" + HINT_RULE_FILE_NAME + "` could not be found");
253 }
254 parser.parseHints(in);
255 } catch (SAXException | IOException ex) {
256 throw new HintParseException("Error parsing hints: " + ex.getMessage(), ex);
257 }
258 localHints = parser.getHintRules();
259 localVendorHints = parser.getVendorDuplicatingHintRules();
260
261 final String filePath = getSettings().getString(Settings.KEYS.HINTS_FILE);
262 if (filePath != null) {
263 boolean deleteTempFile = false;
264 try {
265 final Pattern uriRx = Pattern.compile("^(https?|file):.*", Pattern.CASE_INSENSITIVE);
266 if (uriRx.matcher(filePath).matches()) {
267 deleteTempFile = true;
268 file = getSettings().getTempFile("hint", "xml");
269 final URL url = new URL(filePath);
270 try {
271 Downloader.getInstance().fetchFile(url, file, false);
272 } catch (DownloadFailedException ex) {
273 try {
274 Thread.sleep(500);
275 Downloader.getInstance().fetchFile(url, file, true);
276 } catch (TooManyRequestsException ex1) {
277 throw new HintParseException("Unable to download hint file `" + file + "`; received 429 - too many requests", ex1);
278 } catch (ResourceNotFoundException ex1) {
279 throw new HintParseException("Unable to download hint file `" + file + "`; received 404 - resource not found", ex1);
280 } catch (InterruptedException ex1) {
281 Thread.currentThread().interrupt();
282 throw new HintParseException("Unable to download hint file `" + file + "`", ex1);
283 }
284 } catch (TooManyRequestsException ex) {
285 throw new HintParseException("Unable to download hint file `" + file + "`; received 429 - too many requests", ex);
286 } catch (ResourceNotFoundException ex) {
287 throw new HintParseException("Unable to download hint file `" + file + "`; received 404 - resource not found", ex);
288 }
289 } else {
290 file = new File(filePath);
291 if (!file.exists()) {
292 try (InputStream fromClasspath = FileUtils.getResourceAsStream(filePath)) {
293 deleteTempFile = true;
294 file = getSettings().getTempFile("hint", "xml");
295 Files.copy(fromClasspath, file.toPath());
296 } catch (IOException ex) {
297 throw new HintParseException("Unable to locate hints file in classpath", ex);
298 }
299 }
300 }
301
302 if (file == null) {
303 throw new HintParseException("Unable to locate hints file:" + filePath);
304 } else {
305 try {
306 parser.parseHints(file);
307 if (parser.getHintRules() != null && !parser.getHintRules().isEmpty()) {
308 localHints.addAll(parser.getHintRules());
309 }
310 if (parser.getVendorDuplicatingHintRules() != null && !parser.getVendorDuplicatingHintRules().isEmpty()) {
311 localVendorHints.addAll(parser.getVendorDuplicatingHintRules());
312 }
313 } catch (HintParseException ex) {
314 LOGGER.warn("Unable to parse hint rule xml file '{}'", file.getPath());
315 LOGGER.warn(ex.getMessage());
316 LOGGER.debug("", ex);
317 throw ex;
318 }
319 }
320 } catch (DownloadFailedException ex) {
321 throw new HintParseException("Unable to fetch the configured hint file", ex);
322 } catch (MalformedURLException ex) {
323 throw new HintParseException("Configured hint file has an invalid URL", ex);
324 } catch (IOException ex) {
325 throw new HintParseException("Unable to create temp file for hints", ex);
326 } finally {
327 if (deleteTempFile && file != null) {
328 FileUtils.delete(file);
329 }
330 }
331 }
332 hints = localHints.toArray(new HintRule[0]);
333 vendorHints = localVendorHints.toArray(new VendorDuplicatingHintRule[0]);
334 LOGGER.debug("{} hint rules were loaded.", hints.length);
335 LOGGER.debug("{} duplicating hint rules were loaded.", vendorHints.length);
336 }
337 }