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 com.github.packageurl.MalformedPackageURLException;
21 import com.github.packageurl.PackageURL;
22 import com.github.packageurl.PackageURLBuilder;
23 import org.owasp.dependencycheck.Engine;
24 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
25 import org.owasp.dependencycheck.data.nvd.ecosystem.Ecosystem;
26 import org.owasp.dependencycheck.dependency.Confidence;
27 import org.owasp.dependencycheck.dependency.Dependency;
28 import org.owasp.dependencycheck.dependency.EvidenceType;
29 import org.owasp.dependencycheck.dependency.naming.GenericIdentifier;
30 import org.owasp.dependencycheck.dependency.naming.PurlIdentifier;
31 import org.owasp.dependencycheck.utils.Checksum;
32 import org.owasp.dependencycheck.utils.FileFilterBuilder;
33 import org.owasp.dependencycheck.utils.Settings;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import javax.annotation.concurrent.ThreadSafe;
38 import java.io.File;
39 import java.io.FileFilter;
40 import java.io.IOException;
41 import java.nio.charset.StandardCharsets;
42 import java.nio.file.Files;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45
46
47
48
49
50
51
52
53
54
55 @Experimental
56 @ThreadSafe
57 public class CarthageAnalyzer extends AbstractFileTypeAnalyzer {
58
59
60
61
62
63 public static final String DEPENDENCY_ECOSYSTEM = Ecosystem.IOS;
64
65
66
67
68 private static final Logger LOGGER = LoggerFactory.getLogger(CarthageAnalyzer.class);
69
70
71
72 private static final String ANALYZER_NAME = "Carthage Package Analyzer";
73
74
75
76
77 private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
78
79
80
81
82 public static final String CARTFILE_RESOLVED = "Cartfile.resolved";
83
84
85
86 private static final FileFilter CARTHAGE_FILTER = FileFilterBuilder.newInstance().addFilenames(CARTFILE_RESOLVED).build();
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 private static final Pattern CARTFILE_RESOLVED_DEPENDENCY_PATTERN = Pattern.compile("(github|git|binary) \"([^\"]+)\" \"([^\"]+)\"");
104
105
106
107
108 private static final Pattern CARTFILE_VERSION_PATTERN = Pattern.compile("^v?(\\d+(\\.\\d+){0,4})$");
109
110
111
112
113
114
115
116
117
118
119 private static final Pattern CARTFILE_RESOLVED_GITHUB_DEPENDENCY = Pattern.compile("[a-zA-Z0-9-_]+/([a-zA-Z0-9\\-_\\.]+)");
120
121
122
123
124 private static final Pattern CARTFILE_RESOLVED_GIT_DEPENDENCY = Pattern.compile(".*?/([a-zA-Z0-9\\-_\\.]+).git");
125
126
127
128
129
130
131
132
133
134
135
136
137 private static final Pattern CARTFILE_RESOLVED_BINARY_DEPENDENCY = Pattern.compile("([a-zA-Z0-9\\-_\\.]+).json");
138
139
140
141
142
143
144 @Override
145 protected FileFilter getFileFilter() {
146 return CARTHAGE_FILTER;
147 }
148
149 @Override
150 protected void prepareFileTypeAnalyzer(Engine engine) {
151
152 }
153
154
155
156
157
158
159 @Override
160 public String getName() {
161 return ANALYZER_NAME;
162 }
163
164
165
166
167
168
169 @Override
170 public AnalysisPhase getAnalysisPhase() {
171 return ANALYSIS_PHASE;
172 }
173
174
175
176
177
178
179
180 @Override
181 protected String getAnalyzerEnabledSettingKey() {
182 return Settings.KEYS.ANALYZER_CARTHAGE_ENABLED;
183 }
184
185 @Override
186 protected void analyzeDependency(Dependency dependency, Engine engine)
187 throws AnalysisException {
188 if (CARTFILE_RESOLVED.equals(dependency.getFileName())) {
189 analyzeCartfileResolvedDependency(dependency, engine);
190 }
191 }
192
193
194
195
196
197
198
199
200
201 private void analyzeCartfileResolvedDependency(Dependency cartfileResolved, Engine engine)
202 throws AnalysisException {
203 engine.removeDependency(cartfileResolved);
204
205 final String contents;
206 try {
207 contents = new String(Files.readAllBytes(cartfileResolved.getActualFile().toPath()), StandardCharsets.UTF_8);
208 } catch (IOException e) {
209 throw new AnalysisException(
210 "Problem occurred while reading dependency file.", e);
211 }
212
213 final Matcher matcher = CARTFILE_RESOLVED_DEPENDENCY_PATTERN.matcher(contents);
214 while (matcher.find()) {
215 final String type = matcher.group(1);
216 String name = matcher.group(2);
217 String version = matcher.group(3);
218
219 final Matcher versionMatcher = CARTFILE_VERSION_PATTERN.matcher(version);
220 if (versionMatcher.find()) {
221 version = versionMatcher.group(1);
222 } else {
223
224
225
226 version = "0.0.0";
227 }
228
229 if (type.contentEquals("git")) {
230 final Matcher nameMatcher = CARTFILE_RESOLVED_GIT_DEPENDENCY.matcher(name);
231 if (!nameMatcher.find()) {
232 continue;
233 }
234 name = nameMatcher.group(1);
235 } else if (type.contentEquals("github")) {
236 final Matcher nameMatcher = CARTFILE_RESOLVED_GITHUB_DEPENDENCY.matcher(name);
237 if (!nameMatcher.find()) {
238 continue;
239 }
240 name = nameMatcher.group(1);
241 } else if (type.contentEquals("binary")) {
242 final Matcher nameMatcher = CARTFILE_RESOLVED_BINARY_DEPENDENCY.matcher(name);
243 if (!nameMatcher.find()) {
244 continue;
245 }
246 name = nameMatcher.group(1);
247 }
248
249 final Dependency dependency = new Dependency(cartfileResolved.getActualFile(), true);
250 dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
251 dependency.setName(name);
252 dependency.setVersion(version);
253
254 try {
255 final PackageURLBuilder builder = PackageURLBuilder.aPackageURL().withType("carthage").withName(dependency.getName());
256 if (dependency.getVersion() != null) {
257 builder.withVersion(dependency.getVersion());
258 }
259 final PackageURL purl = builder.build();
260 dependency.addSoftwareIdentifier(new PurlIdentifier(purl, Confidence.HIGHEST));
261 } catch (MalformedPackageURLException ex) {
262 LOGGER.debug("Unable to build package url for carthage", ex);
263 final GenericIdentifier id;
264 if (dependency.getVersion() != null) {
265 id = new GenericIdentifier("carthage:" + dependency.getName() + "@" + dependency.getVersion(), Confidence.HIGHEST);
266 } else {
267 id = new GenericIdentifier("carthage:" + dependency.getName(), Confidence.HIGHEST);
268 }
269 dependency.addSoftwareIdentifier(id);
270 }
271
272 final String packagePath = String.format("%s:%s", name, version);
273 dependency.setPackagePath(packagePath);
274 dependency.setDisplayFileName(packagePath);
275 dependency.setSha1sum(Checksum.getSHA1Checksum(packagePath));
276 dependency.setSha256sum(Checksum.getSHA256Checksum(packagePath));
277 dependency.setMd5sum(Checksum.getMD5Checksum(packagePath));
278 dependency.addEvidence(EvidenceType.VENDOR, CARTFILE_RESOLVED, "name", name, Confidence.HIGHEST);
279 dependency.addEvidence(EvidenceType.PRODUCT, CARTFILE_RESOLVED, "name", name, Confidence.HIGHEST);
280 dependency.addEvidence(EvidenceType.VERSION, CARTFILE_RESOLVED, "version", version, Confidence.HIGHEST);
281 engine.addDependency(dependency);
282 }
283 }
284
285
286
287
288
289
290 private void setPackagePath(Dependency dep) {
291 final File file = new File(dep.getFilePath());
292 final String parent = file.getParent();
293 if (parent != null) {
294 dep.setPackagePath(parent);
295 }
296 }
297 }