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 javax.json.Json;
39 import javax.json.JsonArray;
40 import javax.json.JsonObject;
41 import javax.json.JsonReader;
42 import java.io.FileFilter;
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.nio.file.Files;
46
47
48
49
50
51
52
53
54 @Experimental
55 @ThreadSafe
56 public class SwiftPackageResolvedAnalyzer extends AbstractFileTypeAnalyzer {
57
58
59
60
61 private static final Logger LOGGER = LoggerFactory.getLogger(SwiftPackageResolvedAnalyzer.class);
62
63
64
65
66
67 public static final String DEPENDENCY_ECOSYSTEM = Ecosystem.IOS;
68
69
70
71
72 private static final String ANALYZER_NAME = "SWIFT Package Resolved Analyzer";
73
74
75
76
77 private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
78
79
80
81
82 public static final String SPM_RESOLVED_FILE_NAME = "Package.resolved";
83
84
85
86
87 private static final FileFilter SPM_FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(SPM_RESOLVED_FILE_NAME).build();
88
89
90
91
92
93
94 @Override
95 protected FileFilter getFileFilter() {
96 return SPM_FILE_FILTER;
97 }
98
99 @Override
100 protected void prepareFileTypeAnalyzer(Engine engine) {
101
102 }
103
104
105
106
107
108
109 @Override
110 public String getName() {
111 return ANALYZER_NAME;
112 }
113
114
115
116
117
118
119 @Override
120 public AnalysisPhase getAnalysisPhase() {
121 return ANALYSIS_PHASE;
122 }
123
124
125
126
127
128
129
130 @Override
131 protected String getAnalyzerEnabledSettingKey() {
132 return Settings.KEYS.ANALYZER_SWIFT_PACKAGE_RESOLVED_ENABLED;
133 }
134
135 @Override
136 protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
137 try {
138 engine.removeDependency(dependency);
139 analyzeSpmResolvedDependencies(dependency, engine);
140 } catch (IOException ex) {
141 throw new AnalysisException(
142 "Problem occurred while reading dependency file: " + dependency.getActualFilePath(), ex);
143 }
144 }
145
146
147
148
149
150
151
152
153
154
155 private void analyzeSpmResolvedDependencies(Dependency spmResolved, Engine engine)
156 throws AnalysisException, IOException {
157
158 try (InputStream in = Files.newInputStream(spmResolved.getActualFile().toPath());
159 JsonReader resolved = Json.createReader(in)) {
160 final JsonObject file = resolved.readObject();
161 final int fileVersion = file.getInt("version");
162
163 switch (fileVersion) {
164 case 1:
165 analyzeSpmResolvedDependenciesV1(spmResolved, engine, file);
166 break;
167 case 2:
168 analyzeSpmResolvedDependenciesV2(spmResolved, engine, file);
169 break;
170 default:
171 return;
172 }
173 }
174 }
175
176
177
178
179
180
181
182
183
184 private void analyzeSpmResolvedDependenciesV1(Dependency spmResolved, Engine engine, JsonObject resolved) {
185 final JsonObject object = resolved.getJsonObject("object");
186 if (object == null) {
187 return;
188 }
189 final JsonArray pins = object.getJsonArray("pins");
190 if (pins == null) {
191 return;
192 }
193 pins.forEach(row -> {
194 final JsonObject pin = (JsonObject) row;
195 final String name = pin.getString("package");
196 final String repo = pin.getString("repositoryURL");
197 String version = null;
198 final JsonObject state = pin.getJsonObject("state");
199 if (state != null) {
200 if (!state.isNull("version")) {
201 version = state.getString("version");
202 } else if (!state.isNull("branch")) {
203 version = state.getString("branch");
204 }
205 }
206 final Dependency dependency = createDependency(spmResolved, SPM_RESOLVED_FILE_NAME, name, version, repo);
207 engine.addDependency(dependency);
208 });
209 }
210
211
212
213
214
215
216
217
218
219 private void analyzeSpmResolvedDependenciesV2(Dependency spmResolved, Engine engine, JsonObject resolved) {
220 final JsonArray pins = resolved.getJsonArray("pins");
221 if (pins == null) {
222 return;
223 }
224 pins.forEach(row -> {
225 final JsonObject pin = (JsonObject) row;
226 final String name = pin.getString("identity");
227 final String repo = pin.getString("location");
228 String version = null;
229 final JsonObject state = pin.getJsonObject("state");
230 if (state != null) {
231 if (state.containsKey("version")
232 && !state.isNull("version")
233 && !state.getString("version").isEmpty()) {
234 version = state.getString("version");
235 } else if (state.containsKey("branch") && !state.isNull("branch")) {
236 version = state.getString("branch");
237 }
238 }
239 final Dependency dependency = createDependency(spmResolved, SPM_RESOLVED_FILE_NAME, name, version, repo);
240 engine.addDependency(dependency);
241 });
242 }
243
244
245
246
247
248
249
250
251
252
253
254 private Dependency createDependency(Dependency parent, String source, final String name, String version, String repo) {
255 final Dependency dependency = new Dependency(parent.getActualFile(), true);
256 dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
257 dependency.setName(name);
258 dependency.setVersion(version);
259 final String packagePath = String.format("%s:%s", name, version);
260 dependency.setPackagePath(packagePath);
261 dependency.setDisplayFileName(packagePath);
262 dependency.setSha1sum(Checksum.getSHA1Checksum(packagePath));
263 dependency.setSha256sum(Checksum.getSHA256Checksum(packagePath));
264 dependency.setMd5sum(Checksum.getMD5Checksum(packagePath));
265 dependency.addEvidence(EvidenceType.VENDOR, source, "name", name, Confidence.HIGHEST);
266 dependency.addEvidence(EvidenceType.PRODUCT, source, "name", name, Confidence.HIGHEST);
267 dependency.addEvidence(EvidenceType.VENDOR, source, "repositoryUrl", repo, Confidence.HIGH);
268 dependency.addEvidence(EvidenceType.PRODUCT, source, "repositoryUrl", repo, Confidence.HIGH);
269 dependency.addEvidence(EvidenceType.VERSION, source, "version", version, Confidence.HIGHEST);
270 try {
271 final PackageURLBuilder builder = PackageURLBuilder.aPackageURL().withType("swift").withName(dependency.getName());
272 if (dependency.getVersion() != null) {
273 builder.withVersion(dependency.getVersion());
274 }
275 final PackageURL purl = builder.build();
276 dependency.addSoftwareIdentifier(new PurlIdentifier(purl, Confidence.HIGHEST));
277 } catch (MalformedPackageURLException ex) {
278 LOGGER.debug("Unable to build package url for swift dependency", ex);
279 final GenericIdentifier id;
280 if (dependency.getVersion() != null) {
281 id = new GenericIdentifier("swift:" + dependency.getName() + "@" + dependency.getVersion(), Confidence.HIGHEST);
282 } else {
283 id = new GenericIdentifier("swift:" + dependency.getName(), Confidence.HIGHEST);
284 }
285 dependency.addSoftwareIdentifier(id);
286 }
287 return dependency;
288 }
289 }