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.FileFilterBuilder;
32 import org.owasp.dependencycheck.utils.Settings;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 import javax.annotation.concurrent.ThreadSafe;
37 import java.io.File;
38 import java.io.FileFilter;
39 import java.io.IOException;
40 import java.nio.charset.StandardCharsets;
41 import java.nio.file.Files;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44
45
46
47
48
49
50
51
52 @Experimental
53 @ThreadSafe
54 public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer {
55
56
57
58
59 private static final Logger LOGGER = LoggerFactory.getLogger(SwiftPackageManagerAnalyzer.class);
60
61
62
63
64
65 public static final String DEPENDENCY_ECOSYSTEM = Ecosystem.IOS;
66
67
68
69
70 private static final String ANALYZER_NAME = "SWIFT Package Manager Analyzer";
71
72
73
74
75 private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
76
77
78
79
80 public static final String SPM_FILE_NAME = "Package.swift";
81
82
83
84
85 private static final FileFilter SPM_FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(SPM_FILE_NAME).build();
86
87
88
89
90
91 private static final Pattern SPM_BLOCK_PATTERN = Pattern.compile("let[^=]+=\\s*Package\\s*\\(\\s*([^)]*)\\s*\\)", Pattern.DOTALL);
92
93
94
95
96
97
98 @Override
99 protected FileFilter getFileFilter() {
100 return SPM_FILE_FILTER;
101 }
102
103 @Override
104 protected void prepareFileTypeAnalyzer(Engine engine) {
105
106 }
107
108
109
110
111
112
113 @Override
114 public String getName() {
115 return ANALYZER_NAME;
116 }
117
118
119
120
121
122
123 @Override
124 public AnalysisPhase getAnalysisPhase() {
125 return ANALYSIS_PHASE;
126 }
127
128
129
130
131
132
133
134 @Override
135 protected String getAnalyzerEnabledSettingKey() {
136 return Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED;
137 }
138
139 @Override
140 protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
141 try {
142 analyzeSpmFileDependency(dependency);
143
144 } catch (IOException ex) {
145 throw new AnalysisException(
146 "Problem occurred while reading dependency file: " + dependency.getActualFilePath(), ex);
147 }
148 }
149
150
151
152
153
154
155
156
157 private void analyzeSpmFileDependency(Dependency dependency)
158 throws AnalysisException, IOException {
159 dependency.setEcosystem(DEPENDENCY_ECOSYSTEM);
160
161 final String contents = new String(Files.readAllBytes(dependency.getActualFile().toPath()), StandardCharsets.UTF_8);
162
163 final Matcher matcher = SPM_BLOCK_PATTERN.matcher(contents);
164 if (matcher.find()) {
165 final String packageDescription = matcher.group(1);
166 if (packageDescription.isEmpty()) {
167 return;
168 }
169
170
171
172
173
174 final String name = addStringEvidence(dependency, EvidenceType.PRODUCT, packageDescription, "name", "name", Confidence.HIGHEST);
175 if (name != null && !name.isEmpty()) {
176 dependency.addEvidence(EvidenceType.VENDOR, SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST);
177 dependency.setName(name);
178 } else {
179
180
181 dependency.setName(dependency.getActualFile().getParentFile().getName());
182 }
183 if (dependency.getVersion() != null && !dependency.getVersion().isEmpty()) {
184 dependency.setDisplayFileName(String.format("%s:%s", dependency.getName(), dependency.getVersion()));
185 } else {
186 dependency.setDisplayFileName(dependency.getName());
187 }
188
189 try {
190 final PackageURLBuilder builder = PackageURLBuilder.aPackageURL().withType("swift").withName(dependency.getName());
191 if (dependency.getVersion() != null) {
192 builder.withVersion(dependency.getVersion());
193 }
194 final PackageURL purl = builder.build();
195 dependency.addSoftwareIdentifier(new PurlIdentifier(purl, Confidence.HIGHEST));
196 } catch (MalformedPackageURLException ex) {
197 LOGGER.debug("Unable to build package url for swift", ex);
198 final GenericIdentifier id;
199 if (dependency.getVersion() != null) {
200 id = new GenericIdentifier("swift:" + dependency.getName() + "@" + dependency.getVersion(), Confidence.HIGHEST);
201 } else {
202 id = new GenericIdentifier("swift:" + dependency.getName(), Confidence.HIGHEST);
203 }
204 dependency.addSoftwareIdentifier(id);
205 }
206 }
207 setPackagePath(dependency);
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221
222 private String addStringEvidence(Dependency dependency, EvidenceType type,
223 String packageDescription, String field, String fieldPattern, Confidence confidence) {
224 String value = "";
225
226 final Matcher matcher = Pattern.compile(
227 String.format("%s *:\\s*\"([^\"]*)", fieldPattern), Pattern.DOTALL).matcher(packageDescription);
228 if (matcher.find()) {
229 value = matcher.group(1);
230 }
231
232 if (value != null) {
233 value = value.trim();
234 if (value.length() > 0) {
235 dependency.addEvidence(type, SPM_FILE_NAME, field, value, confidence);
236 }
237 }
238 return value;
239 }
240
241
242
243
244
245
246 private void setPackagePath(Dependency dep) {
247 final File file = new File(dep.getFilePath());
248 final String parent = file.getParent();
249 if (parent != null) {
250 dep.setPackagePath(parent);
251 }
252 }
253 }