1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.data.nodeaudit;
19
20 import org.owasp.dependencycheck.analyzer.NodePackageAnalyzer;
21
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.Optional;
25 import java.util.TreeMap;
26 import java.util.stream.Collectors;
27 import javax.json.Json;
28 import javax.json.JsonObject;
29 import javax.json.JsonObjectBuilder;
30 import javax.json.JsonString;
31 import javax.json.JsonValue;
32 import javax.annotation.concurrent.ThreadSafe;
33 import org.apache.commons.collections4.MultiValuedMap;
34
35
36
37
38
39
40
41 @ThreadSafe
42 public final class NpmPayloadBuilder {
43
44
45
46
47 private NpmPayloadBuilder() {
48
49 }
50
51
52
53
54
55
56
57
58
59
60
61 public static JsonObject build(JsonObject lockJson, JsonObject packageJson,
62 MultiValuedMap<String, String> dependencyMap, boolean skipDevDependencies) {
63 final JsonObjectBuilder payloadBuilder = Json.createObjectBuilder();
64 addProjectInfo(packageJson, payloadBuilder);
65
66
67
68 final JsonObjectBuilder requiresBuilder = Json.createObjectBuilder();
69
70 if (packageJson.containsKey("dependencies")) {
71 packageJson.getJsonObject("dependencies").entrySet()
72 .stream()
73 .collect(Collectors.toMap(
74 Map.Entry::getKey,
75 Map.Entry::getValue,
76 (oldValue, newValue) -> newValue, TreeMap::new))
77 .forEach((key, value) -> {
78 if (NodePackageAnalyzer.shouldSkipDependency(key, ((JsonString) value).getString())) {
79 return;
80 }
81 requiresBuilder.add(key, value);
82 dependencyMap.put(key, value.toString());
83 });
84 }
85
86 if (!skipDevDependencies && packageJson.containsKey("devDependencies")) {
87 packageJson.getJsonObject("devDependencies").entrySet()
88 .stream()
89 .collect(Collectors.toMap(
90 Map.Entry::getKey,
91 Map.Entry::getValue,
92 (oldValue, newValue) -> newValue, TreeMap::new))
93 .forEach((key, value) -> {
94 if (NodePackageAnalyzer.shouldSkipDependency(key, ((JsonString) value).getString())) {
95 return;
96 }
97 requiresBuilder.add(key, value);
98 dependencyMap.put(key, value.toString());
99 });
100 }
101
102 payloadBuilder.add("requires", requiresBuilder.build());
103
104 final JsonObjectBuilder dependenciesBuilder = Json.createObjectBuilder();
105 final int lockJsonVersion = lockJson.containsKey("lockfileVersion") ? lockJson.getInt("lockfileVersion") : 1;
106 JsonObject dependencies = lockJson.getJsonObject("dependencies");
107 if (lockJsonVersion >= 2 && dependencies == null) {
108 dependencies = lockJson.getJsonObject("packages");
109 }
110
111 if (dependencies != null) {
112 dependencies.forEach((k, value) -> {
113 String key = k;
114 final int indexOfNodeModule = key.lastIndexOf(NodePackageAnalyzer.NODE_MODULES_DIRNAME + "/");
115 if (indexOfNodeModule >= 0) {
116 key = key.substring(indexOfNodeModule + NodePackageAnalyzer.NODE_MODULES_DIRNAME.length() + 1);
117 }
118
119 JsonObject dep = ((JsonObject) value);
120
121
122 if (lockJsonVersion > 2 && dep.containsKey("dependencies") && dep.get("dependencies") instanceof JsonObject) {
123 final JsonObjectBuilder depBuilder = Json.createObjectBuilder(dep);
124 depBuilder.remove("dependencies");
125 depBuilder.add("requires", dep.get("dependencies"));
126 dep = depBuilder.build();
127 }
128
129 final String version = dep.getString("version", "");
130 final boolean isDev = dep.getBoolean("dev", false);
131 if (skipDevDependencies && isDev) {
132 return;
133 }
134 if (NodePackageAnalyzer.shouldSkipDependency(key, version)) {
135 return;
136 }
137 dependencyMap.put(key, version);
138 dependenciesBuilder.add(key, buildDependencies(dep, dependencyMap));
139 });
140 }
141 payloadBuilder.add("dependencies", dependenciesBuilder.build());
142
143 addConstantElements(payloadBuilder);
144 return payloadBuilder.build();
145 }
146
147
148
149
150
151
152
153
154
155
156
157 public static JsonObject build(JsonObject packageJson, MultiValuedMap<String, String> dependencyMap,
158 final boolean skipDevDependencies) {
159 final JsonObjectBuilder payloadBuilder = Json.createObjectBuilder();
160 addProjectInfo(packageJson, payloadBuilder);
161
162
163
164 final JsonObjectBuilder requiresBuilder = Json.createObjectBuilder();
165 final JsonObjectBuilder dependenciesBuilder = Json.createObjectBuilder();
166
167 final JsonObject dependencies = packageJson.getJsonObject("dependencies");
168 if (dependencies != null) {
169 dependencies.forEach((name, value) -> {
170 final String version;
171 if (value.getValueType() == JsonValue.ValueType.OBJECT) {
172 final JsonObject dep = ((JsonObject) value);
173 version = Optional.ofNullable(dep.getJsonString("version"))
174 .map(JsonString::getString)
175 .orElse(null);
176
177 final boolean isDev = dep.getBoolean("dev", false);
178 if (skipDevDependencies && isDev) {
179 return;
180 }
181 if (NodePackageAnalyzer.shouldSkipDependency(name, version)) {
182 return;
183 }
184 dependencyMap.put(name, version);
185 dependenciesBuilder.add(name, buildDependencies(dep, dependencyMap));
186 } else {
187
188
189 final String tmp = value.toString();
190 if (tmp.startsWith("\"")) {
191 version = tmp.substring(1, tmp.length() - 1);
192 } else {
193 version = tmp;
194 }
195 }
196 requiresBuilder.add(name, Objects.isNull(version) ? "*" : "^" + version);
197 });
198 }
199 payloadBuilder.add("requires", requiresBuilder.build());
200
201 payloadBuilder.add("dependencies", dependenciesBuilder.build());
202
203 addConstantElements(payloadBuilder);
204 return payloadBuilder.build();
205 }
206
207
208
209
210
211
212
213 private static void addProjectInfo(JsonObject packageJson, final JsonObjectBuilder payloadBuilder) {
214 final String projectName = packageJson.getString("name", "");
215 final String projectVersion = packageJson.getString("version", "");
216 if (!projectName.isEmpty()) {
217 payloadBuilder.add("name", projectName);
218 }
219 if (!projectVersion.isEmpty()) {
220 payloadBuilder.add("version", projectVersion);
221 }
222 }
223
224
225
226
227
228
229 private static void addConstantElements(final JsonObjectBuilder payloadBuilder) {
230 payloadBuilder.add("install", Json.createArrayBuilder().build());
231 payloadBuilder.add("remove", Json.createArrayBuilder().build());
232 payloadBuilder.add("metadata", Json.createObjectBuilder()
233 .add("npm_version", "6.9.0")
234 .add("node_version", "v10.5.0")
235 .add("platform", "linux")
236 );
237 }
238
239
240
241
242
243
244
245
246
247 private static JsonObject buildDependencies(JsonObject dep, MultiValuedMap<String, String> dependencyMap) {
248 final JsonObjectBuilder depBuilder = Json.createObjectBuilder();
249 Optional.ofNullable(dep.getJsonString("version"))
250 .map(JsonString::getString)
251 .ifPresent(version -> depBuilder.add("version", version));
252
253
254 if (dep.containsKey("integrity")) {
255 depBuilder.add("integrity", dep.getString("integrity"));
256 }
257 if (dep.containsKey("requires")) {
258 final JsonObjectBuilder requiresBuilder = Json.createObjectBuilder();
259 dep.getJsonObject("requires").forEach((key, value) -> {
260 if (NodePackageAnalyzer.shouldSkipDependency(key, ((JsonString) value).getString())) {
261 return;
262 }
263
264 requiresBuilder.add(key, value);
265 });
266 depBuilder.add("requires", requiresBuilder.build());
267 }
268 if (dep.containsKey("dependencies")) {
269 final JsonObjectBuilder dependeciesBuilder = Json.createObjectBuilder();
270 dep.getJsonObject("dependencies").forEach((key, value) -> {
271 if (value.getValueType() == JsonValue.ValueType.OBJECT) {
272 final JsonObject currentDep = (JsonObject) value;
273 final String v = currentDep.getString("version");
274 dependencyMap.put(key, v);
275 dependeciesBuilder.add(key, buildDependencies(currentDep, dependencyMap));
276 } else {
277 final String tmp = value.toString();
278 final String v;
279 if (tmp.startsWith("\"")) {
280 v = tmp.substring(1, tmp.length() - 1);
281 } else {
282 v = tmp;
283 }
284 dependencyMap.put(key, v);
285 dependeciesBuilder.add(key, v);
286 }
287 });
288 depBuilder.add("dependencies", dependeciesBuilder.build());
289 }
290 return depBuilder.build();
291 }
292 }