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