Skip to content

Commit 3853cef

Browse files
Merge pull request #1345 from blackducksoftware/dev/zahidblackduck/IDETECT-4594
Handle Duplicate Keys in `package.json` and `pnpm-lock` Files
2 parents 5e40510 + 2d01c2b commit 3853cef

File tree

4 files changed

+122
-5
lines changed

4 files changed

+122
-5
lines changed

detectable/src/main/java/com/blackduck/integration/detectable/detectables/npm/packagejson/CombinedPackageJsonExtractor.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
import java.util.List;
1111
import java.util.Optional;
1212

13+
import com.blackduck.integration.detectable.util.JsonSanitizer;
14+
import com.google.gson.Gson;
1315
import org.apache.commons.io.FileUtils;
1416

15-
import com.google.gson.Gson;
1617
import com.blackduck.integration.detectable.detectables.npm.packagejson.model.PackageJson;
1718

1819
public class CombinedPackageJsonExtractor {
@@ -31,7 +32,7 @@ public CombinedPackageJson constructCombinedPackageJson(String rootJsonPath, Str
3132
return null;
3233
}
3334

34-
PackageJson packageJson = Optional.ofNullable(packageJsonText)
35+
PackageJson packageJson = Optional.ofNullable(JsonSanitizer.sanitize(packageJsonText))
3536
.map(content -> gson.fromJson(content, PackageJson.class))
3637
.orElse(null);
3738

@@ -87,7 +88,6 @@ public CombinedPackageJson constructCombinedPackageJson(String rootJsonPath, Str
8788
return combinedPackageJson;
8889
}
8990

90-
9191
/**
9292
* Takes an absolute path to a workspace and converts it to a relative one for
9393
* future comparisons with contents of the package-lock.json file.

detectable/src/main/java/com/blackduck/integration/detectable/detectables/yarn/packagejson/PackageJsonReader.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
import java.util.List;
55
import java.util.Map;
66

7+
import com.blackduck.integration.detectable.util.JsonSanitizer;
8+
import com.google.gson.Gson;
79
import org.slf4j.Logger;
810
import org.slf4j.LoggerFactory;
911

10-
import com.google.gson.Gson;
1112
import com.blackduck.integration.detectable.detectables.npm.packagejson.model.YarnPackageJson;
1213

1314
public class PackageJsonReader {
@@ -20,7 +21,7 @@ public PackageJsonReader(Gson gson) {
2021
}
2122

2223
public NullSafePackageJson read(String packageJsonText) {
23-
YarnPackageJson rawPackageJson = gson.fromJson(packageJsonText, YarnPackageJson.class);
24+
YarnPackageJson rawPackageJson = gson.fromJson(JsonSanitizer.sanitize(packageJsonText), YarnPackageJson.class);
2425
return new NullSafePackageJson(rawPackageJson);
2526
}
2627

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.blackduck.integration.detectable.util;
2+
3+
import com.google.gson.JsonObject;
4+
import com.google.gson.JsonParser;
5+
import com.google.gson.JsonSyntaxException;
6+
7+
public class JsonSanitizer {
8+
public static JsonObject sanitize(String json) throws JsonSyntaxException {
9+
return JsonParser.parseString(json).getAsJsonObject();
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.blackduck.integration.detectable.util;
2+
3+
import com.google.gson.JsonObject;
4+
import com.google.gson.JsonSyntaxException;
5+
import org.junit.jupiter.api.Test;
6+
import static org.junit.jupiter.api.Assertions.*;
7+
8+
class JsonSanitizerTest {
9+
@Test
10+
void testValidJson() {
11+
String json = "{ " +
12+
"\"name\": \"my-project\", " +
13+
"\"version\": \"1.0.0\", " +
14+
"\"private\": true, " +
15+
"\"scripts\": { " +
16+
"\"start\": \"node index.js\", " +
17+
"\"test\": \"jest\"" +
18+
"}, " +
19+
"\"dependencies\": { " +
20+
"\"express\": \"^4.17.1\", " +
21+
"\"lodash\": \"^4.17.21\"" +
22+
"}, " +
23+
"\"devDependencies\": { " +
24+
"\"jest\": \"^27.0.0\", " +
25+
"\"eslint\": \"^7.32.0\"" +
26+
"}" +
27+
"}";
28+
29+
JsonObject sanitized = JsonSanitizer.sanitize(json);
30+
31+
assertEquals("my-project", sanitized.get("name").getAsString());
32+
assertEquals("1.0.0", sanitized.get("version").getAsString());
33+
assertTrue(sanitized.get("private").getAsBoolean());
34+
assertEquals("node index.js", sanitized.getAsJsonObject("scripts").get("start").getAsString());
35+
assertEquals("^4.17.1", sanitized.getAsJsonObject("dependencies").get("express").getAsString());
36+
assertEquals("^27.0.0", sanitized.getAsJsonObject("devDependencies").get("jest").getAsString());
37+
}
38+
39+
@Test
40+
void testDuplicateKeysInDependencies() {
41+
String json = "{ " +
42+
"\"dependencies\": { " +
43+
"\"express\": \"^4.17.1\", " +
44+
"\"express\": \"^5.0.0\"" +
45+
"}" +
46+
"}";
47+
48+
JsonObject sanitized = JsonSanitizer.sanitize(json);
49+
50+
assertEquals("^5.0.0", sanitized.getAsJsonObject("dependencies").get("express").getAsString()); // Last value is kept
51+
}
52+
53+
@Test
54+
void testNestedScriptsAndDependencies() {
55+
String json = "{ " +
56+
"\"scripts\": { " +
57+
"\"build\": \"tsc\", " +
58+
"\"start\": \"node dist/index.js\"" +
59+
"}, " +
60+
"\"dependencies\": { " +
61+
"\"typescript\": \"^4.0.0\", " +
62+
"\"node-fetch\": \"^2.6.1\"" +
63+
"}" +
64+
"}";
65+
66+
JsonObject sanitized = JsonSanitizer.sanitize(json);
67+
68+
assertEquals("tsc", sanitized.getAsJsonObject("scripts").get("build").getAsString());
69+
assertEquals("^4.0.0", sanitized.getAsJsonObject("dependencies").get("typescript").getAsString());
70+
}
71+
72+
@Test
73+
void testInvalidJson() {
74+
String json = "{ " +
75+
"\"name\": \"my-project\", " +
76+
"\"version\": \"1.0.0\"";
77+
78+
assertThrows(JsonSyntaxException.class, () -> JsonSanitizer.sanitize(json));
79+
}
80+
81+
@Test
82+
void testEmptyPackageJson() {
83+
String json = "{}";
84+
85+
JsonObject sanitized = JsonSanitizer.sanitize(json);
86+
assertTrue(sanitized.entrySet().isEmpty());
87+
}
88+
89+
@Test
90+
void testJsonWithNullValues() {
91+
String json = "{ " +
92+
"\"name\": \"my-project\", " +
93+
"\"version\": null, " +
94+
"\"dependencies\": { " +
95+
"\"express\": \"^4.17.1\", " +
96+
"\"lodash\": null " +
97+
"} " +
98+
"}";
99+
100+
JsonObject sanitized = JsonSanitizer.sanitize(json);
101+
102+
assertTrue(sanitized.get("version").isJsonNull());
103+
assertTrue(sanitized.getAsJsonObject("dependencies").get("lodash").isJsonNull());
104+
}
105+
}

0 commit comments

Comments
 (0)