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.apache.commons.io.FileUtils;
21 import org.apache.commons.io.filefilter.NameFileFilter;
22 import org.apache.commons.io.filefilter.SuffixFileFilter;
23 import org.owasp.dependencycheck.Engine;
24 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
25 import org.owasp.dependencycheck.dependency.Confidence;
26 import org.owasp.dependencycheck.dependency.Dependency;
27 import org.owasp.dependencycheck.dependency.EvidenceCollection;
28 import org.owasp.dependencycheck.utils.FileFilterBuilder;
29 import org.owasp.dependencycheck.utils.Settings;
30 import org.owasp.dependencycheck.utils.UrlStringUtils;
31
32 import java.io.File;
33 import java.io.FileFilter;
34 import java.io.IOException;
35 import java.nio.charset.Charset;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40
41
42
43
44
45
46
47 @Experimental
48 public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
49
50
51
52
53 private static final int REGEX_OPTIONS = Pattern.DOTALL
54 | Pattern.CASE_INSENSITIVE;
55
56
57
58
59 private static final String EXTENSIONS = "py";
60
61
62
63
64 private static final Pattern MODULE_DOCSTRING = Pattern.compile(
65 "^(['\\\"]{3})(.*?)\\1", REGEX_OPTIONS);
66
67
68
69
70 private static final Pattern VERSION_PATTERN = Pattern.compile(
71 "\\b(__)?version(__)? *= *(['\"]+)(\\d+\\.\\d+.*?)\\3",
72 REGEX_OPTIONS);
73
74
75
76
77 private static final Pattern TITLE_PATTERN = compileAssignPattern("title");
78
79
80
81
82 private static final Pattern SUMMARY_PATTERN = compileAssignPattern("summary");
83
84
85
86
87 private static final Pattern URI_PATTERN = compileAssignPattern("ur[il]");
88
89
90
91
92 private static final Pattern HOMEPAGE_PATTERN = compileAssignPattern("home_?page");
93
94
95
96
97 private static final Pattern AUTHOR_PATTERN = compileAssignPattern("author");
98
99
100
101
102 private static final FileFilter INIT_PY_FILTER = new NameFileFilter("__init__.py");
103
104
105
106
107 private static final FileFilter PY_FILTER = new SuffixFileFilter(".py");
108
109
110
111
112
113
114 @Override
115 public String getName() {
116 return "Python Package Analyzer";
117 }
118
119
120
121
122
123
124 @Override
125 public AnalysisPhase getAnalysisPhase() {
126 return AnalysisPhase.INFORMATION_COLLECTION;
127 }
128
129
130
131
132 private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(EXTENSIONS).build();
133
134
135
136
137
138
139 @Override
140 protected FileFilter getFileFilter() {
141 return FILTER;
142 }
143
144
145
146
147
148
149 @Override
150 protected void initializeFileTypeAnalyzer() throws Exception {
151
152 }
153
154
155
156
157
158
159
160 private static Pattern compileAssignPattern(String name) {
161 return Pattern.compile(
162 String.format("\\b(__)?%s(__)?\\b *= *(['\"]+)(.*?)\\3", name),
163 REGEX_OPTIONS);
164 }
165
166
167
168
169
170
171
172
173
174 @Override
175 protected void analyzeFileType(Dependency dependency, Engine engine)
176 throws AnalysisException {
177 final File file = dependency.getActualFile();
178 final File parent = file.getParentFile();
179 final String parentName = parent.getName();
180 if (INIT_PY_FILTER.accept(file)) {
181
182
183
184 dependency.setDisplayFileName(parentName + "/__init__.py");
185 dependency.getProductEvidence().addEvidence(file.getName(),
186 "PackageName", parentName, Confidence.HIGHEST);
187
188 final File[] fileList = parent.listFiles(PY_FILTER);
189 if (fileList != null) {
190 for (final File sourceFile : fileList) {
191 analyzeFileContents(dependency, sourceFile);
192 }
193 }
194 } else {
195
196 final List<Dependency> dependencies = new ArrayList<Dependency>(
197 engine.getDependencies());
198 dependencies.remove(dependency);
199 engine.setDependencies(dependencies);
200 }
201 }
202
203
204
205
206
207
208
209
210
211
212
213 private boolean analyzeFileContents(Dependency dependency, File file)
214 throws AnalysisException {
215 String contents;
216 try {
217 contents = FileUtils.readFileToString(file, Charset.defaultCharset()).trim();
218 } catch (IOException e) {
219 throw new AnalysisException(
220 "Problem occurred while reading dependency file.", e);
221 }
222 boolean found = false;
223 if (!contents.isEmpty()) {
224 final String source = file.getName();
225 found = gatherEvidence(VERSION_PATTERN, contents, source,
226 dependency.getVersionEvidence(), "SourceVersion",
227 Confidence.MEDIUM);
228 found |= addSummaryInfo(dependency, SUMMARY_PATTERN, 4, contents,
229 source, "summary");
230 if (INIT_PY_FILTER.accept(file)) {
231 found |= addSummaryInfo(dependency, MODULE_DOCSTRING, 2,
232 contents, source, "docstring");
233 }
234 found |= gatherEvidence(TITLE_PATTERN, contents, source,
235 dependency.getProductEvidence(), "SourceTitle",
236 Confidence.LOW);
237 final EvidenceCollection vendorEvidence = dependency
238 .getVendorEvidence();
239 found |= gatherEvidence(AUTHOR_PATTERN, contents, source,
240 vendorEvidence, "SourceAuthor", Confidence.MEDIUM);
241 found |= gatherHomePageEvidence(URI_PATTERN, vendorEvidence,
242 source, "URL", contents);
243 found |= gatherHomePageEvidence(HOMEPAGE_PATTERN,
244 vendorEvidence, source, "HomePage", contents);
245 }
246 return found;
247 }
248
249
250
251
252
253
254
255
256
257
258
259
260 private boolean addSummaryInfo(Dependency dependency, Pattern pattern,
261 int group, String contents, String source, String key) {
262 final Matcher matcher = pattern.matcher(contents);
263 final boolean found = matcher.find();
264 if (found) {
265 JarAnalyzer.addDescription(dependency, matcher.group(group),
266 source, key);
267 }
268 return found;
269 }
270
271
272
273
274
275
276
277
278
279
280
281 private boolean gatherHomePageEvidence(Pattern pattern,
282 EvidenceCollection evidence, String source, String name,
283 String contents) {
284 final Matcher matcher = pattern.matcher(contents);
285 boolean found = false;
286 if (matcher.find()) {
287 final String url = matcher.group(4);
288 if (UrlStringUtils.isUrl(url)) {
289 found = true;
290 evidence.addEvidence(source, name, url, Confidence.MEDIUM);
291 }
292 }
293 return found;
294 }
295
296
297
298
299
300
301
302
303
304
305
306
307
308 private boolean gatherEvidence(Pattern pattern, String contents,
309 String source, EvidenceCollection evidence, String name,
310 Confidence confidence) {
311 final Matcher matcher = pattern.matcher(contents);
312 final boolean found = matcher.find();
313 if (found) {
314 evidence.addEvidence(source, name, matcher.group(4), confidence);
315 }
316 return found;
317 }
318
319 @Override
320 protected String getAnalyzerEnabledSettingKey() {
321 return Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED;
322 }
323 }