From 1d4b021479038a100e5a1b56dff280aa754f32ec Mon Sep 17 00:00:00 2001 From: bd-spratikbharti Date: Fri, 19 Jun 2026 12:30:41 +0530 Subject: [PATCH 1/6] fix: read pnpm-lock.yaml as UTF-8 to handle emojis and non-ASCII chars --- .../pnpm/lockfile/process/PnpmLockYamlParserInitial.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java index dfaa5cba88..c32e92fbce 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java @@ -1,9 +1,11 @@ package com.blackduck.integration.detectable.detectables.pnpm.lockfile.process; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.List; import org.jetbrains.annotations.Nullable; @@ -74,11 +76,11 @@ private PnpmLockYamlBase parseYamlFile(File pnpmLockYamlFile) throws FileNotFoun // Try to read the lockfile into the current Yaml classes. It's more common and // should hopefully work more of the time. Yaml yaml = new Yaml(new Constructor(PnpmLockYaml.class, loaderOptions), representer); - return yaml.load(new FileReader(pnpmLockYamlFile)); + return yaml.load(new InputStreamReader(new FileInputStream(pnpmLockYamlFile), StandardCharsets.UTF_8)); } catch (ConstructorException e) { // If the reading fails try to read a v5 Yaml. Yaml yaml = new Yaml(new Constructor(PnpmLockYamlv5.class, loaderOptions), representer); - return yaml.load(new FileReader(pnpmLockYamlFile)); + return yaml.load(new InputStreamReader(new FileInputStream(pnpmLockYamlFile), StandardCharsets.UTF_8)); } } } From 149daddbc73848c441509cfcb09eb644fa4e1aa3 Mon Sep 17 00:00:00 2001 From: blackduck-serv-builder Date: Fri, 19 Jun 2026 03:51:45 -0400 Subject: [PATCH 2/6] Release 12.0.0-SIGQA3-IDETECT-5134 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6e46016799..87c33651df 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { group = 'com.blackduck.integration' -version = '12.0.0-SIGQA3-SNAPSHOT' +version = '12.0.0-SIGQA3-IDETECT-5134' apply plugin: 'com.blackduck.integration.solution' apply plugin: 'org.springframework.boot' From 2162d4c4878e1e249d32dc306f3386562a21a2cd Mon Sep 17 00:00:00 2001 From: blackduck-serv-builder Date: Fri, 19 Jun 2026 04:02:52 -0400 Subject: [PATCH 3/6] Using the next snapshot post release 12.0.0-SIGQA4-IDETECT-5134-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 87c33651df..1c3b2bb1e2 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { group = 'com.blackduck.integration' -version = '12.0.0-SIGQA3-IDETECT-5134' +version = '12.0.0-SIGQA4-IDETECT-5134-SNAPSHOT' apply plugin: 'com.blackduck.integration.solution' apply plugin: 'org.springframework.boot' From afadcd0d9889782b52fe2cc618df9047cb973719 Mon Sep 17 00:00:00 2001 From: bd-spratikbharti Date: Fri, 19 Jun 2026 14:09:20 +0530 Subject: [PATCH 4/6] Use try-with-resources to gurantee proper close() and prevent file descriptor leaks --- .../lockfile/process/PnpmLockYamlParserInitial.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java index c32e92fbce..5981cfe27a 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java @@ -2,7 +2,6 @@ import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; @@ -64,9 +63,9 @@ public List parse(File pnpmLockYamlFile, @Nullable NameVersion pro * * @param pnpmLockYamlFile the File path to the pnpm-lock.yaml file * @return a memory representation of the lock file. - * @throws FileNotFoundException + * @throws IOException if the file cannot be read or closed */ - private PnpmLockYamlBase parseYamlFile(File pnpmLockYamlFile) throws FileNotFoundException { + private PnpmLockYamlBase parseYamlFile(File pnpmLockYamlFile) throws IOException { DumperOptions dumperOptions = new DumperOptions(); Representer representer = new Representer(dumperOptions); representer.getPropertyUtils().setSkipMissingProperties(true); @@ -76,11 +75,15 @@ private PnpmLockYamlBase parseYamlFile(File pnpmLockYamlFile) throws FileNotFoun // Try to read the lockfile into the current Yaml classes. It's more common and // should hopefully work more of the time. Yaml yaml = new Yaml(new Constructor(PnpmLockYaml.class, loaderOptions), representer); - return yaml.load(new InputStreamReader(new FileInputStream(pnpmLockYamlFile), StandardCharsets.UTF_8)); + try (InputStreamReader reader = new InputStreamReader(new FileInputStream(pnpmLockYamlFile), StandardCharsets.UTF_8)) { + return yaml.load(reader); + } } catch (ConstructorException e) { // If the reading fails try to read a v5 Yaml. Yaml yaml = new Yaml(new Constructor(PnpmLockYamlv5.class, loaderOptions), representer); - return yaml.load(new InputStreamReader(new FileInputStream(pnpmLockYamlFile), StandardCharsets.UTF_8)); + try (InputStreamReader reader = new InputStreamReader(new FileInputStream(pnpmLockYamlFile), StandardCharsets.UTF_8)) { + return yaml.load(reader); + } } } } From 083a547d29e807d58af0e355d91bfc12aa5563cb Mon Sep 17 00:00:00 2001 From: bd-spratikbharti Date: Thu, 2 Jul 2026 16:31:42 +0530 Subject: [PATCH 5/6] Added unit tests for emoji and non-ASCII characters in pnpm --- .../pnpm/unit/PnpmLockYamlParserUtf8Test.java | 65 +++++++++++++++++++ .../pnpm/unicode/v5-accented/pnpm-lock.yaml | 25 +++++++ .../unicode/v9-emoji-comments/pnpm-lock.yaml | 28 ++++++++ 3 files changed, 118 insertions(+) create mode 100644 detectable/src/test/java/com/blackduck/integration/detectable/detectables/pnpm/unit/PnpmLockYamlParserUtf8Test.java create mode 100644 detectable/src/test/resources/detectables/functional/pnpm/unicode/v5-accented/pnpm-lock.yaml create mode 100644 detectable/src/test/resources/detectables/functional/pnpm/unicode/v9-emoji-comments/pnpm-lock.yaml diff --git a/detectable/src/test/java/com/blackduck/integration/detectable/detectables/pnpm/unit/PnpmLockYamlParserUtf8Test.java b/detectable/src/test/java/com/blackduck/integration/detectable/detectables/pnpm/unit/PnpmLockYamlParserUtf8Test.java new file mode 100644 index 0000000000..bf6e8515ee --- /dev/null +++ b/detectable/src/test/java/com/blackduck/integration/detectable/detectables/pnpm/unit/PnpmLockYamlParserUtf8Test.java @@ -0,0 +1,65 @@ +package com.blackduck.integration.detectable.detectables.pnpm.unit; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.google.gson.Gson; +import com.blackduck.integration.detectable.detectable.codelocation.CodeLocation; +import com.blackduck.integration.detectable.detectable.util.EnumListFilter; +import com.blackduck.integration.detectable.detectables.pnpm.lockfile.PnpmLockOptions; +import com.blackduck.integration.detectable.detectables.pnpm.lockfile.model.PnpmDependencyType; +import com.blackduck.integration.detectable.detectables.pnpm.lockfile.process.PnpmLinkedPackageResolver; +import com.blackduck.integration.detectable.detectables.pnpm.lockfile.process.PnpmLockYamlParserInitial; +import com.blackduck.integration.detectable.detectables.yarn.packagejson.PackageJsonFiles; +import com.blackduck.integration.detectable.detectables.yarn.packagejson.PackageJsonReader; +import com.blackduck.integration.detectable.util.FunctionalTestFiles; +import com.blackduck.integration.exception.IntegrationException; +import com.blackduck.integration.util.NameVersion; + +/** + * Tests that pnpm-lock.yaml files containing emojis and non-ASCII content + * are parsed correctly when read with explicit UTF-8 encoding + * (InputStreamReader + StandardCharsets.UTF_8). + */ +public class PnpmLockYamlParserUtf8Test { + + private List parseLockFile(String resourcePath) throws IOException, IntegrationException { + File pnpmLockYaml = FunctionalTestFiles.asFile(resourcePath); + + EnumListFilter dependencyTypeFilter = EnumListFilter.excludeNone(); + PnpmLockOptions pnpmLockOptions = new PnpmLockOptions(dependencyTypeFilter, Collections.emptyList(), Collections.emptyList()); + + PnpmLockYamlParserInitial parser = new PnpmLockYamlParserInitial(pnpmLockOptions); + PnpmLinkedPackageResolver linkedPackageResolver = new PnpmLinkedPackageResolver( + pnpmLockYaml.getParentFile(), + new PackageJsonFiles(new PackageJsonReader(new Gson())) + ); + + return parser.parse(pnpmLockYaml, new NameVersion("project", "1.0.0"), linkedPackageResolver); + } + + @Test + public void testParseV9WithEmojiComments() throws IOException, IntegrationException { + // YAML contains emoji characters (🚀🎉✅❌🔥💡) in comments + List codeLocations = parseLockFile("/pnpm/unicode/v9-emoji-comments/pnpm-lock.yaml"); + + Assertions.assertNotNull(codeLocations, "Code locations should not be null when parsing YAML with emoji comments"); + Assertions.assertFalse(codeLocations.isEmpty(), "Should produce at least one code location"); + } + + @Test + public void testParseV5WithAccentedComments() throws IOException, IntegrationException { + // YAML contains accented (Ünïcödé, Ñoño, résumé, naïveté) and Greek characters in comments + List codeLocations = parseLockFile("/pnpm/unicode/v5-accented/pnpm-lock.yaml"); + + Assertions.assertNotNull(codeLocations, "Code locations should not be null when parsing YAML with accented comments"); + Assertions.assertFalse(codeLocations.isEmpty(), "Should produce at least one code location"); + } + +} + diff --git a/detectable/src/test/resources/detectables/functional/pnpm/unicode/v5-accented/pnpm-lock.yaml b/detectable/src/test/resources/detectables/functional/pnpm/unicode/v5-accented/pnpm-lock.yaml new file mode 100644 index 0000000000..0bf88c7b18 --- /dev/null +++ b/detectable/src/test/resources/detectables/functional/pnpm/unicode/v5-accented/pnpm-lock.yaml @@ -0,0 +1,25 @@ +# Lockfile mit Ünïcödé Zeichën — Ñoño, résumé, naïveté +lockfileVersion: 5.3 + +importers: + + .: + specifiers: + react: ^17.0.2 + devDependencies: + '@babel/code-frame': 7.10.4 + +# Ελληνικά σχόλια — Greek comments +packages: + + /@babel/code-frame/7.10.4: + resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} + dependencies: + '@babel/highlight': 7.14.5 + dev: true + + /@babel/highlight/7.14.5: + resolution: {integrity: sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==} + engines: {node: '>=6.9.0'} + dev: true + diff --git a/detectable/src/test/resources/detectables/functional/pnpm/unicode/v9-emoji-comments/pnpm-lock.yaml b/detectable/src/test/resources/detectables/functional/pnpm/unicode/v9-emoji-comments/pnpm-lock.yaml new file mode 100644 index 0000000000..8a2a88c584 --- /dev/null +++ b/detectable/src/test/resources/detectables/functional/pnpm/unicode/v9-emoji-comments/pnpm-lock.yaml @@ -0,0 +1,28 @@ +# 🚀 pnpm lockfile with emoji characters in comments +# This file tests UTF-8 handling: 🎉✅❌🔥💡 +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +# 📦 root project dependencies +importers: + + .: + dependencies: + express: + specifier: ^4.18.2 + version: 4.19.2 + +packages: + + express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+UGORIhRhO2+ke4V9GnMEYTw9ay5wSfIQ==} + engines: {node: '>= 0.10.0'} + +# 🔧 snapshots section +snapshots: + + express@4.19.2: {} + From 889e90ac0f0a83df977f7b1539fb850c7d306745 Mon Sep 17 00:00:00 2001 From: bd-spratikbharti Date: Fri, 3 Jul 2026 19:52:50 +0530 Subject: [PATCH 6/6] Fixed merge conflict --- .../process/PnpmLockYamlParserInitial.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java index 2c56899317..71bd6bafc4 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/pnpm/lockfile/process/PnpmLockYamlParserInitial.java @@ -116,7 +116,7 @@ public List parse(File pnpmLockYamlFile, @Nullable NameVersion pro * * @param pnpmLockYamlFile the File path to the pnpm-lock.yaml file * @return a memory representation of the lock file, or null if the file is empty - * @throws FileNotFoundException if the file does not exist + * @throws IOException if the file cannot be read */ private PnpmLockYamlBase parseYamlFile(File pnpmLockYamlFile) throws IOException { DumperOptions dumperOptions = new DumperOptions(); @@ -129,16 +129,10 @@ private PnpmLockYamlBase parseYamlFile(File pnpmLockYamlFile) throws IOException // Step 1: Try to read the lockfile into the v6/v9 Yaml classes first (more common). logger.debug("Attempting to parse '{}' as v6/v9 format.", pnpmLockYamlFile.getName()); Yaml yaml = new Yaml(new Constructor(PnpmLockYaml.class, loaderOptions), representer); + PnpmLockYamlBase result; try (InputStreamReader reader = new InputStreamReader(new FileInputStream(pnpmLockYamlFile), StandardCharsets.UTF_8)) { - return yaml.load(reader); + result = yaml.load(reader); } - } catch (ConstructorException e) { - // If the reading fails try to read a v5 Yaml. - Yaml yaml = new Yaml(new Constructor(PnpmLockYamlv5.class, loaderOptions), representer); - try (InputStreamReader reader = new InputStreamReader(new FileInputStream(pnpmLockYamlFile), StandardCharsets.UTF_8)) { - return yaml.load(reader); - } - PnpmLockYamlBase result = yaml.load(new FileReader(pnpmLockYamlFile)); if (result == null) { // Step 1a: File was empty or contained only comments — SnakeYAML returns null. @@ -166,7 +160,9 @@ private PnpmLockYamlBase parseYamlFile(File pnpmLockYamlFile) throws IOException // Step 2: Re-parse as v5. logger.debug("Attempting to parse '{}' as v5 format.", pnpmLockYamlFile.getName()); Yaml yaml = new Yaml(new Constructor(PnpmLockYamlv5.class, loaderOptions), representer); - return yaml.load(new FileReader(pnpmLockYamlFile)); + try (InputStreamReader reader = new InputStreamReader(new FileInputStream(pnpmLockYamlFile), StandardCharsets.UTF_8)) { + return yaml.load(reader); + } } /**