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.lang3.StringUtils;
22 import org.owasp.dependencycheck.Engine;
23 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
24 import org.owasp.dependencycheck.dependency.Confidence;
25 import org.owasp.dependencycheck.dependency.Dependency;
26 import org.owasp.dependencycheck.utils.Checksum;
27 import org.owasp.dependencycheck.utils.FileFilterBuilder;
28 import org.owasp.dependencycheck.utils.Settings;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import java.io.File;
33 import java.io.FileFilter;
34 import java.io.IOException;
35 import java.io.UnsupportedEncodingException;
36 import java.security.MessageDigest;
37 import java.security.NoSuchAlgorithmException;
38 import java.util.logging.Level;
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
55
56
57
58
59 private static final Logger LOGGER = LoggerFactory.getLogger(CMakeAnalyzer.class);
60
61
62
63
64 private static final int REGEX_OPTIONS = Pattern.DOTALL
65 | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE;
66
67
68
69
70 private static final Pattern PROJECT = Pattern.compile(
71 "^ *project *\\([ \\n]*(\\w+)[ \\n]*.*?\\)", REGEX_OPTIONS);
72
73
74
75
76
77
78
79
80 private static final Pattern SET_VERSION = Pattern
81 .compile(
82 "^ *set\\s*\\(\\s*(\\w+)_version\\s+\"?(\\d+(?:\\.\\d+)+)[\\s\"]?\\)",
83 REGEX_OPTIONS);
84
85
86
87
88 private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(".cmake")
89 .addFilenames("CMakeLists.txt").build();
90
91
92
93
94 private static MessageDigest sha1 = null;
95
96 static {
97 try {
98 sha1 = MessageDigest.getInstance("SHA1");
99 } catch (NoSuchAlgorithmException e) {
100 LOGGER.error(e.getMessage());
101 }
102 }
103
104
105
106
107
108
109
110 @Override
111 public String getName() {
112 return "CMake Analyzer";
113 }
114
115
116
117
118
119
120 @Override
121 public AnalysisPhase getAnalysisPhase() {
122 return AnalysisPhase.INFORMATION_COLLECTION;
123 }
124
125
126
127
128
129
130 @Override
131 protected FileFilter getFileFilter() {
132 return FILTER;
133 }
134
135
136
137
138
139
140 @Override
141 protected void initializeFileTypeAnalyzer() throws Exception {
142
143 }
144
145
146
147
148
149
150
151
152 @Override
153 protected void analyzeFileType(Dependency dependency, Engine engine)
154 throws AnalysisException {
155 final File file = dependency.getActualFile();
156 final String parentName = file.getParentFile().getName();
157 final String name = file.getName();
158 dependency.setDisplayFileName(String.format("%s%c%s", parentName, File.separatorChar, name));
159 String contents;
160 try {
161 contents = FileUtils.readFileToString(file).trim();
162 } catch (IOException e) {
163 throw new AnalysisException(
164 "Problem occurred while reading dependency file.", e);
165 }
166
167 if (StringUtils.isNotBlank(contents)) {
168 final Matcher m = PROJECT.matcher(contents);
169 int count = 0;
170 while (m.find()) {
171 count++;
172 LOGGER.debug(String.format(
173 "Found project command match with %d groups: %s",
174 m.groupCount(), m.group(0)));
175 final String group = m.group(1);
176 LOGGER.debug("Group 1: " + group);
177 dependency.getProductEvidence().addEvidence(name, "Project",
178 group, Confidence.HIGH);
179 }
180 LOGGER.debug("Found {} matches.", count);
181 analyzeSetVersionCommand(dependency, engine, contents);
182 }
183 }
184
185
186
187
188
189
190
191
192
193 private void analyzeSetVersionCommand(Dependency dependency, Engine engine, String contents) {
194 Dependency currentDep = dependency;
195
196 final Matcher m = SET_VERSION.matcher(contents);
197 int count = 0;
198 while (m.find()) {
199 count++;
200 LOGGER.debug("Found project command match with {} groups: {}",
201 m.groupCount(), m.group(0));
202 String product = m.group(1);
203 final String version = m.group(2);
204 LOGGER.debug("Group 1: " + product);
205 LOGGER.debug("Group 2: " + version);
206 final String aliasPrefix = "ALIASOF_";
207 if (product.startsWith(aliasPrefix)) {
208 product = product.replaceFirst(aliasPrefix, "");
209 }
210 if (count > 1) {
211
212 currentDep = new Dependency(dependency.getActualFile());
213 currentDep.setDisplayFileName(String.format("%s:%s", dependency.getDisplayFileName(), product));
214 final String filePath = String.format("%s:%s", dependency.getFilePath(), product);
215 currentDep.setFilePath(filePath);
216
217 byte[] path;
218 try {
219 path = filePath.getBytes("UTF-8");
220 } catch (UnsupportedEncodingException ex) {
221 path = filePath.getBytes();
222 }
223 currentDep.setSha1sum(Checksum.getHex(sha1.digest(path)));
224 engine.getDependencies().add(currentDep);
225 }
226 final String source = currentDep.getDisplayFileName();
227 currentDep.getProductEvidence().addEvidence(source, "Product",
228 product, Confidence.MEDIUM);
229 currentDep.getVersionEvidence().addEvidence(source, "Version",
230 version, Confidence.MEDIUM);
231 }
232 LOGGER.debug(String.format("Found %d matches.", count));
233 }
234
235 @Override
236 protected String getAnalyzerEnabledSettingKey() {
237 return Settings.KEYS.ANALYZER_CMAKE_ENABLED;
238 }
239 }