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 private static final Pattern CARTFILE_RESOLVED_DEPENDENCY_PATTERN = Pattern.compile("(github|git|binary) \"([^\"]+)\" \"([^\"]+)\"");
100
101
102
103
104 private static final Pattern CARTFILE_VERSION_PATTERN = Pattern.compile("^v?(\\d+(\\.\\d+){0,4})$");
105
106
107
108
109
110
111
112
113 private static final Pattern CARTFILE_RESOLVED_GITHUB_DEPENDENCY = Pattern.compile("[a-zA-Z0-9-_]+/([a-zA-Z0-9\\-_\\.]+)");
114
115
116
117
118 private static final Pattern CARTFILE_RESOLVED_GIT_DEPENDENCY = Pattern.compile(".*?/([a-zA-Z0-9\\-_\\.]+).git");
119
120
121
122
123
124
125
126
127
128
129 private static final Pattern CARTFILE_RESOLVED_BINARY_DEPENDENCY = Pattern.compile("([a-zA-Z0-9\\-_\\.]+).json");
130
131
132
133
134
135
136 @Override
137 protected FileFilter getFileFilter() {
138 return CARTHAGE_FILTER;
139 }
140
141 @Override
142 protected void prepareFileTypeAnalyzer(Engine engine) {
143
144 }
145
146
147
148
149
150
151 @Override
152 public String getName() {
153 return ANALYZER_NAME;
154 }
155
156
157
158
159
160
161 @Override
162 public AnalysisPhase getAnalysisPhase() {
163 return ANALYSIS_PHASE;
164 }
165
166
167
168
169
170
171
172 @Override
173 protected String getAnalyzerEnabledSettingKey() {
174 return Settings.KEYS.ANALYZER_CARTHAGE_ENABLED;
175 }
176
177 @Override
178 protected void analyzeDependency(Dependency dependency, Engine engine)
179 throws AnalysisException {
180 if (CARTFILE_RESOLVED.equals(dependency.getFileName())) {
181 analyzeCartfileResolvedDependency(dependency, engine);
182 }
183 }
184
185
186
187
188
189
190
191
192 private void analyzeCartfileResolvedDependency(Dependency cartfileResolved, Engine engine)
193 throws AnalysisException {
194 engine.removeDependency(cartfileResolved);
195
196 final String contents;
197 try {
198 contents = new String(Files.readAllBytes(cartfileResolved.getActualFile().toPath()), StandardCharsets.UTF_8);
199 } catch (IOException e) {
200 throw new AnalysisException(
201 "Problem occurred while reading dependency file.", e);
202 }
203
204 final Matcher matcher = CARTFILE_RESOLVED_DEPENDENCY_PATTERN.matcher(contents);
205 while (matcher.find()) {
206 final String type = matcher.group(1);
207 String name = matcher.group(2);
208 String version = matcher.group(3);
209
210 final Matcher versionMatcher = CARTFILE_VERSION_PATTERN.matcher(version);
211 if (versionMatcher.find()) {
212 version = versionMatcher.group(1);
213 }
214 else {
215
216
217
218 version = "0.0.0";
219 }
220
221 if (type.contentEquals("git")) {
222 final Matcher nameMatcher = CARTFILE_RESOLVED_GIT_DEPENDENCY.matcher(name);
223 if (!nameMatcher.find()) {
224 continue;
225 }
226 name = nameMatcher.group(1);
227 }
228 else if (type.contentEquals("github")) {
229 final Matcher nameMatcher = CARTFILE_RESOLVED_GITHUB_DEPENDENCY.matcher(name);
230 if (!nameMatcher.find()) {
231 continue;
232 }
233 name = nameMatcher.group(1);
234 }
235 else if (type.contentEquals("binary")) {
236 final Matcher nameMatcher = CARTFILE_RESOLVED_BINARY_DEPENDENCY.matcher(name);
237 if (!nameMatcher.find()) {
238 continue;
239 }
240 name = nameMatcher.group(1);
241 }
242
243 final Dependency dependency = new Dependency(cartfileResolved.getActualFile(), true);
244 dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
245 dependency.setName(name);
246 dependency.setVersion(version);
247
248 try {
249 final PackageURLBuilder builder = PackageURLBuilder.aPackageURL().withType("carthage").withName(dependency.getName());
250 if (dependency.getVersion() != null) {
251 builder.withVersion(dependency.getVersion());
252 }
253 final PackageURL purl = builder.build();
254 dependency.addSoftwareIdentifier(new PurlIdentifier(purl, Confidence.HIGHEST));
255 } catch (MalformedPackageURLException ex) {
256 LOGGER.debug("Unable to build package url for carthage", ex);
257 final GenericIdentifier id;
258 if (dependency.getVersion() != null) {
259 id = new GenericIdentifier("carthage:" + dependency.getName() + "@" + dependency.getVersion(), Confidence.HIGHEST);
260 } else {
261 id = new GenericIdentifier("carthage:" + dependency.getName(), Confidence.HIGHEST);
262 }
263 dependency.addSoftwareIdentifier(id);
264 }
265
266 final String packagePath = String.format("%s:%s", name, version);
267 dependency.setPackagePath(packagePath);
268 dependency.setDisplayFileName(packagePath);
269 dependency.setSha1sum(Checksum.getSHA1Checksum(packagePath));
270 dependency.setSha256sum(Checksum.getSHA256Checksum(packagePath));
271 dependency.setMd5sum(Checksum.getMD5Checksum(packagePath));
272 dependency.addEvidence(EvidenceType.VENDOR, CARTFILE_RESOLVED, "name", name, Confidence.HIGHEST);
273 dependency.addEvidence(EvidenceType.PRODUCT, CARTFILE_RESOLVED, "name", name, Confidence.HIGHEST);
274 dependency.addEvidence(EvidenceType.VERSION, CARTFILE_RESOLVED, "version", version, Confidence.HIGHEST);
275 engine.addDependency(dependency);
276 }
277 }
278
279
280
281
282
283
284 private void setPackagePath(Dependency dep) {
285 final File file = new File(dep.getFilePath());
286 final String parent = file.getParent();
287 if (parent != null) {
288 dep.setPackagePath(parent);
289 }
290 }
291 }