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.PurlIdentifier;
30 import org.owasp.dependencycheck.exception.InitializationException;
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.JsonException;
41 import javax.json.JsonObject;
42 import javax.json.JsonReader;
43 import java.io.File;
44 import java.io.FileFilter;
45 import java.io.IOException;
46 import java.nio.file.Files;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
49
50
51
52
53
54
55 @ThreadSafe
56 public class LibmanAnalyzer extends AbstractFileTypeAnalyzer {
57
58
59
60
61
62 private static final String DEPENDENCY_ECOSYSTEM = Ecosystem.NODEJS;
63
64
65
66
67 private static final Logger LOGGER = LoggerFactory.getLogger(LibmanAnalyzer.class);
68
69
70
71
72 private static final String ANALYZER_NAME = "Libman Analyzer";
73
74
75
76
77 private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
78
79
80
81
82 private static final String FILE_NAME = "libman.json";
83
84
85
86
87 private static final FileFilter FILTER = FileFilterBuilder.newInstance().addFilenames(FILE_NAME).build();
88
89
90
91
92 private static final Pattern LIBRARY_REGEX = Pattern
93 .compile("(\\@(?<package>[a-zA-Z]+)\\/)?(?<name>.+)\\@(?<version>.+)", Pattern.CASE_INSENSITIVE);
94
95
96
97
98
99
100
101 @Override
102 public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException {
103
104 }
105
106
107
108
109
110
111 @Override
112 public String getName() {
113 return ANALYZER_NAME;
114 }
115
116
117
118
119
120
121
122 @Override
123 protected String getAnalyzerEnabledSettingKey() {
124 return Settings.KEYS.ANALYZER_LIBMAN_ENABLED;
125 }
126
127
128
129
130
131
132 @Override
133 public AnalysisPhase getAnalysisPhase() {
134 return ANALYSIS_PHASE;
135 }
136
137
138
139
140
141
142 @Override
143 protected FileFilter getFileFilter() {
144 return FILTER;
145 }
146
147
148
149
150
151
152
153
154 @Override
155 public void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
156 LOGGER.debug("Checking file {}", dependency.getActualFilePath());
157
158 if (FILE_NAME.equals(dependency.getFileName()) && !dependency.isVirtual()) {
159 engine.removeDependency(dependency);
160 }
161
162 final File dependencyFile = dependency.getActualFile();
163
164 if (!dependencyFile.isFile() || dependencyFile.length() == 0) {
165 return;
166 }
167
168 try (JsonReader jsonReader = Json.createReader(Files.newInputStream(dependencyFile.toPath()))) {
169 final JsonObject json = jsonReader.readObject();
170
171 final String libmanVersion = json.getString("version");
172
173 if (!"1.0".equals(libmanVersion)) {
174 LOGGER.warn("The Libman analyzer currently only supports Libman version 1.0");
175 return;
176 }
177
178 final String defaultProvider = json.getString("defaultProvider");
179 final JsonArray libraries = json.getJsonArray("libraries");
180
181 libraries.forEach(e -> {
182 final JsonObject reference = (JsonObject) e;
183
184 final String provider = reference.getString("provider", defaultProvider);
185 final String library = reference.getString("library");
186
187 if ("filesystem".equals(provider)) {
188 LOGGER.warn("Unable to determine name and version for filesystem package: {}", library);
189 return;
190 }
191
192 final Matcher matcher = LIBRARY_REGEX.matcher(library);
193
194 if (!matcher.find()) {
195 LOGGER.warn("Unable to parse library, unknown format: {}", library);
196 return;
197 }
198
199 final String vendor = matcher.group("package");
200 final String name = matcher.group("name");
201 final String version = matcher.group("version");
202
203 LOGGER.debug("Found Libman package: vendor {}, name {}, version {}", vendor, name, version);
204
205 final Dependency child = new Dependency(dependency.getActualFile(), true);
206
207 child.setEcosystem(DEPENDENCY_ECOSYSTEM);
208 child.setName(name);
209 child.setVersion(version);
210
211 if (vendor != null) {
212 child.addEvidence(EvidenceType.VENDOR, FILE_NAME, "vendor", vendor, Confidence.HIGHEST);
213 }
214 child.addEvidence(EvidenceType.VENDOR, FILE_NAME, "name", name, Confidence.HIGH);
215 child.addEvidence(EvidenceType.PRODUCT, FILE_NAME, "name", name, Confidence.HIGHEST);
216 child.addEvidence(EvidenceType.VERSION, FILE_NAME, "version", version, Confidence.HIGHEST);
217
218 final String packagePath = String.format("%s:%s", name, version);
219
220 child.setSha1sum(Checksum.getSHA1Checksum(packagePath));
221 child.setSha256sum(Checksum.getSHA256Checksum(packagePath));
222 child.setMd5sum(Checksum.getMD5Checksum(packagePath));
223 child.setPackagePath(packagePath);
224
225 try {
226 final PackageURL purl = PackageURLBuilder.aPackageURL()
227 .withType("libman")
228 .withName(name)
229 .withVersion(version)
230 .build();
231 final PurlIdentifier id = new PurlIdentifier(purl, Confidence.HIGHEST);
232 child.addSoftwareIdentifier(id);
233 } catch (MalformedPackageURLException ex) {
234 LOGGER.warn("Unable to build package url for {}", ex.toString());
235 }
236
237 engine.addDependency(child);
238 });
239 } catch (JsonException e) {
240 LOGGER.warn(String.format("Failed to parse %s file", FILE_NAME), e);
241 } catch (IOException e) {
242 throw new AnalysisException("Problem occurred while reading dependency file", e);
243 }
244 }
245 }