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