What Happened
Between late 2024 and early 2025, attackers published malicious Node.js packages to npm that used invisible Unicode characters and package manager configuration files to execute hidden code on developer machines. The packages appeared legitimate during code reviews because the malicious logic was concealed using the Unicode character U+115F (HANGUL FILLER), which renders as whitespace in most editors but executes as valid JavaScript.
The attack had two stages. First, the packages included configuration files (.yarnrc or .npmrc) that specified custom binaries to execute during package installation. Second, the malicious code was hidden using invisible characters, making it appear as blank lines or comments to human reviewers while remaining executable by the JavaScript interpreter.
Timeline
Discovery Phase: Security researchers identified packages exploiting the yarnPath option in .yarnrc files and the git, shell, and script-shell options in .npmrc files. These options allow packages to specify which binary should execute during installation or script execution.
Attack Mechanism: The packages used the HANGUL FILLER character (U+115F) to hide malicious code within what appeared to be empty lines or whitespace. Standard code review processes missed these characters because they render invisibly in most development environments.
Current State: The specific packages have been removed from npm, but the technique remains viable. No comprehensive tooling exists in standard CI/CD pipelines to detect invisible character abuse or validate package manager configuration files.
Which Controls Failed or Were Missing
Dependency Validation: Your team likely reviews package.json for new dependencies, but configuration files like .yarnrc and .npmrc rarely receive the same scrutiny. These files can execute arbitrary binaries during installation—before your code ever runs.
Character Set Validation: Most static analysis tools and code review processes don't flag invisible Unicode characters. The HANGUL FILLER character is valid JavaScript, so syntax checkers pass it without warning.
Installation-Time Security: Package managers execute configuration-specified binaries with the same privileges as the user running npm install or yarn install. There's no sandbox, no permission prompt, and no audit log of what executed.
Supply Chain Verification: Even if you verify package signatures or checksums, those validations don't extend to the configuration files that packages can include. A signed package can still contain a malicious .npmrc file.
What the Relevant Standards Require
PCI DSS v4.0.1 Requirement 6.3.2 mandates that your organization maintain an inventory of bespoke and custom software and third-party software components. This includes understanding what executes during your build process—not just what deploys to production. Configuration files that specify custom binaries are part of your software inventory.
OWASP ASVS v4.0.3 Section 14.2.3 requires verification that package managers are configured to only use approved repositories and that packages are verified using checksums or signatures. The standard doesn't explicitly address configuration files within packages, but the principle applies: verify what executes, not just what installs.
NIST 800-53 Rev 5 Control SA-10 (Developer Configuration Management) requires organizations to track configuration items throughout the system development life cycle. Package manager configuration files are configuration items. If a .yarnrc file changes which binary executes, that's a configuration change that affects your security posture.
ISO 27001 Control 8.31 (Separation of Development, Test, and Production Environments) implies that code running in development should be controlled with similar rigor to production code. Malicious packages that compromise developer machines can access production credentials stored in environment variables or configuration files.
Lessons and Action Items for Your Team
Audit your package manager configuration files now. Run find . -name ".npmrc" -o -name ".yarnrc*" in your repositories. Review every configuration file for the yarnPath, git, shell, and script-shell options. If you're not explicitly using these features, remove them or add them to your repository's banned-patterns list.
Add invisible character detection to your pre-commit hooks. Write a script that scans for Unicode characters outside the printable ASCII range (0x20-0x7E) and common extended Latin sets. Flag any invisible or zero-width characters in .js, .ts, .json, .npmrc, and .yarnrc files. This won't catch every encoding attack, but it stops the HANGUL FILLER technique specifically.
Restrict package manager configurations at the system level. Configure npm's global config (npm config set ignore-scripts true) to prevent packages from running install scripts by default. Use npm install --ignore-scripts in CI/CD and explicitly allowlist packages that need post-install scripts. This breaks some legitimate packages—that's the point. You should know which packages execute code during installation.
Implement configuration file validation in CI. Add a build step that parses .npmrc and .yarnrc files and rejects any that specify custom binaries. The valid use cases for yarnPath are rare enough that you can require explicit approval for any package that includes it.
Use a private registry with package scanning. Tools like Sonatype Nexus, JFrog Artifactory, or GitHub Packages can proxy npm and scan packages before they reach your developers. Configure your scanner to flag packages containing .npmrc or .yarnrc files with executable configurations.
Train your team to review configuration files as code. Your code review checklist should explicitly include: "Does this PR add or modify .npmrc or .yarnrc files? If yes, what binaries do they reference?" Configuration files are code. Treat them accordingly.
The invisible character technique isn't new—it's been used in homograph attacks and source code obfuscation for years. What's new is the combination with package manager configuration files to create a two-stage attack that bypasses both human review and automated scanning. Your dependency scanning tools are looking at package contents. They're not looking at what those packages tell your package manager to execute.



