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 jakarta.json.Json;
39 import jakarta.json.JsonArray;
40 import jakarta.json.JsonObject;
41 import jakarta.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 case 3:
169 analyzeSpmResolvedDependenciesV2And3(spmResolved, engine, file);
170 break;
171 default:
172 return;
173 }
174 }
175 }
176
177
178
179
180
181
182
183
184
185 private void analyzeSpmResolvedDependenciesV1(Dependency spmResolved, Engine engine, JsonObject resolved) {
186 final JsonObject object = resolved.getJsonObject("object");
187 if (object == null) {
188 return;
189 }
190 final JsonArray pins = object.getJsonArray("pins");
191 if (pins == null) {
192 return;
193 }
194 pins.forEach(row -> {
195 final JsonObject pin = (JsonObject) row;
196 final String name = pin.getString("package");
197 final String repo = pin.getString("repositoryURL");
198 String version = null;
199 final JsonObject state = pin.getJsonObject("state");
200 if (state != null) {
201 if (!state.isNull("version")) {
202 version = state.getString("version");
203 } else if (!state.isNull("branch")) {
204 version = state.getString("branch");
205 }
206 }
207 final Dependency dependency = createDependency(spmResolved, SPM_RESOLVED_FILE_NAME, name, version, repo);
208 engine.addDependency(dependency);
209 });
210 }
211
212
213
214
215
216
217
218
219
220 private void analyzeSpmResolvedDependenciesV2And3(Dependency spmResolved, Engine engine, JsonObject resolved) {
221 final JsonArray pins = resolved.getJsonArray("pins");
222 if (pins == null) {
223 return;
224 }
225 pins.forEach(row -> {
226 final JsonObject pin = (JsonObject) row;
227 final String name = pin.getString("identity");
228 final String repo = pin.getString("location");
229 String version = null;
230 final JsonObject state = pin.getJsonObject("state");
231 if (state != null) {
232 if (state.containsKey("version")
233 && !state.isNull("version")
234 && !state.getString("version").isEmpty()) {
235 version = state.getString("version");
236 } else if (state.containsKey("branch") && !state.isNull("branch")) {
237 version = state.getString("branch");
238 }
239 }
240 final Dependency dependency = createDependency(spmResolved, SPM_RESOLVED_FILE_NAME, name, version, repo);
241 engine.addDependency(dependency);
242 });
243 }
244
245
246
247
248
249
250
251
252
253
254
255 private Dependency createDependency(Dependency parent, String source, final String name, String version, String repo) {
256 final Dependency dependency = new Dependency(parent.getActualFile(), true);
257 dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
258 dependency.setName(name);
259 dependency.setVersion(version);
260 final String packagePath = String.format("%s:%s", name, version);
261 dependency.setPackagePath(packagePath);
262 dependency.setDisplayFileName(packagePath);
263 dependency.setSha1sum(Checksum.getSHA1Checksum(packagePath));
264 dependency.setSha256sum(Checksum.getSHA256Checksum(packagePath));
265 dependency.setMd5sum(Checksum.getMD5Checksum(packagePath));
266 dependency.addEvidence(EvidenceType.VENDOR, source, "name", name, Confidence.HIGHEST);
267 dependency.addEvidence(EvidenceType.PRODUCT, source, "name", name, Confidence.HIGHEST);
268 dependency.addEvidence(EvidenceType.VENDOR, source, "repositoryUrl", repo, Confidence.HIGH);
269 dependency.addEvidence(EvidenceType.PRODUCT, source, "repositoryUrl", repo, Confidence.HIGH);
270 dependency.addEvidence(EvidenceType.VERSION, source, "version", version, Confidence.HIGHEST);
271 try {
272 final PackageURLBuilder builder = PackageURLBuilder.aPackageURL().withType("swift").withName(dependency.getName());
273 if (dependency.getVersion() != null) {
274 builder.withVersion(dependency.getVersion());
275 }
276 final PackageURL purl = builder.build();
277 dependency.addSoftwareIdentifier(new PurlIdentifier(purl, Confidence.HIGHEST));
278 } catch (MalformedPackageURLException ex) {
279 LOGGER.debug("Unable to build package url for swift dependency", ex);
280 final GenericIdentifier id;
281 if (dependency.getVersion() != null) {
282 id = new GenericIdentifier("swift:" + dependency.getName() + "@" + dependency.getVersion(), Confidence.HIGHEST);
283 } else {
284 id = new GenericIdentifier("swift:" + dependency.getName(), Confidence.HIGHEST);
285 }
286 dependency.addSoftwareIdentifier(id);
287 }
288 return dependency;
289 }
290 }