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 final JsonObject dep = ((JsonObject) value);
119 final String version = dep.getString("version", "");
120 final boolean isDev = dep.getBoolean("dev", false);
121 if (skipDevDependencies && isDev) {
122 return;
123 }
124 if (NodePackageAnalyzer.shouldSkipDependency(key, version)) {
125 return;
126 }
127 dependencyMap.put(key, version);
128 dependenciesBuilder.add(key, buildDependencies(dep, dependencyMap));
129 });
130 }
131 payloadBuilder.add("dependencies", dependenciesBuilder.build());
132
133 addConstantElements(payloadBuilder);
134 return payloadBuilder.build();
135 }
136
137
138
139
140
141
142
143
144
145
146
147 public static JsonObject build(JsonObject packageJson, MultiValuedMap<String, String> dependencyMap,
148 final boolean skipDevDependencies) {
149 final JsonObjectBuilder payloadBuilder = Json.createObjectBuilder();
150 addProjectInfo(packageJson, payloadBuilder);
151
152
153
154 final JsonObjectBuilder requiresBuilder = Json.createObjectBuilder();
155 final JsonObjectBuilder dependenciesBuilder = Json.createObjectBuilder();
156
157 final JsonObject dependencies = packageJson.getJsonObject("dependencies");
158 if (dependencies != null) {
159 dependencies.forEach((name, value) -> {
160 final String version;
161 if (value.getValueType() == JsonValue.ValueType.OBJECT) {
162 final JsonObject dep = ((JsonObject) value);
163 version = Optional.ofNullable(dep.getJsonString("version"))
164 .map(JsonString::getString)
165 .orElse(null);
166
167 final boolean isDev = dep.getBoolean("dev", false);
168 if (skipDevDependencies && isDev) {
169 return;
170 }
171 if (NodePackageAnalyzer.shouldSkipDependency(name, version)) {
172 return;
173 }
174 dependencyMap.put(name, version);
175 dependenciesBuilder.add(name, buildDependencies(dep, dependencyMap));
176 } else {
177
178
179 final String tmp = value.toString();
180 if (tmp.startsWith("\"")) {
181 version = tmp.substring(1, tmp.length() - 1);
182 } else {
183 version = tmp;
184 }
185 }
186 requiresBuilder.add(name, Objects.isNull(version) ? "*" : "^" + version);
187 });
188 }
189 payloadBuilder.add("requires", requiresBuilder.build());
190
191 payloadBuilder.add("dependencies", dependenciesBuilder.build());
192
193 addConstantElements(payloadBuilder);
194 return payloadBuilder.build();
195 }
196
197
198
199
200
201
202
203 private static void addProjectInfo(JsonObject packageJson, final JsonObjectBuilder payloadBuilder) {
204 final String projectName = packageJson.getString("name", "");
205 final String projectVersion = packageJson.getString("version", "");
206 if (!projectName.isEmpty()) {
207 payloadBuilder.add("name", projectName);
208 }
209 if (!projectVersion.isEmpty()) {
210 payloadBuilder.add("version", projectVersion);
211 }
212 }
213
214
215
216
217
218
219 private static void addConstantElements(final JsonObjectBuilder payloadBuilder) {
220 payloadBuilder.add("install", Json.createArrayBuilder().build());
221 payloadBuilder.add("remove", Json.createArrayBuilder().build());
222 payloadBuilder.add("metadata", Json.createObjectBuilder()
223 .add("npm_version", "6.9.0")
224 .add("node_version", "v10.5.0")
225 .add("platform", "linux")
226 );
227 }
228
229
230
231
232
233
234
235
236
237 private static JsonObject buildDependencies(JsonObject dep, MultiValuedMap<String, String> dependencyMap) {
238 final JsonObjectBuilder depBuilder = Json.createObjectBuilder();
239 Optional.ofNullable(dep.getJsonString("version"))
240 .map(JsonString::getString)
241 .ifPresent(version -> depBuilder.add("version", version));
242
243
244 if (dep.containsKey("integrity")) {
245 depBuilder.add("integrity", dep.getString("integrity"));
246 }
247 if (dep.containsKey("requires")) {
248 final JsonObjectBuilder requiresBuilder = Json.createObjectBuilder();
249 dep.getJsonObject("requires").forEach((key, value) -> {
250 if (NodePackageAnalyzer.shouldSkipDependency(key, ((JsonString) value).getString())) {
251 return;
252 }
253
254 requiresBuilder.add(key, value);
255 });
256 depBuilder.add("requires", requiresBuilder.build());
257 }
258 if (dep.containsKey("dependencies")) {
259 final JsonObjectBuilder dependeciesBuilder = Json.createObjectBuilder();
260 dep.getJsonObject("dependencies").forEach((key, value) -> {
261 if (value.getValueType() == JsonValue.ValueType.OBJECT) {
262 final JsonObject currentDep = (JsonObject) value;
263 final String v = currentDep.getString("version");
264 dependencyMap.put(key, v);
265 dependeciesBuilder.add(key, buildDependencies(currentDep, dependencyMap));
266 } else {
267 final String tmp = value.toString();
268 final String v;
269 if (tmp.startsWith("\"")) {
270 v = tmp.substring(1, tmp.length() - 1);
271 } else {
272 v = tmp;
273 }
274 dependencyMap.put(key, v);
275 dependeciesBuilder.add(key, v);
276 }
277 });
278 depBuilder.add("dependencies", dependeciesBuilder.build());
279 }
280 return depBuilder.build();
281 }
282 }