Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Security News
Sarah Gooding
September 13, 2024
You’ve seen it before: a casual glance at a project’s package.json
file reveals a slew of asterisks (*) next to dependency names. While it might seem convenient to use wildcards to automatically update dependencies, the risks far outweigh the benefits.
{
"name": "my-project",
"version": "1.0.0",
"dependencies": {
"react": "*",
"react-dom": "*",
"axios": "*"
}
}
This practice can disrupt production environments, compromise security, and lead to unpredictable behavior in applications. In this post, we'll dive deeper into the risks of floating dependency ranges and explore how to mitigate them for safer npm package management.
npm uses semantic versioning (semver), for communicating what kind of changes are in a release. This helps users understand the level of effort involved in updating to the latest version by specifying major, minor, and patch versions (e.g., 1.2.3).
However, npm also offers more flexibility when specifying dependencies in your package.json
file. There are a whole host of different specifiers identified in node-semver, the semver parser for node that npm uses, but wildcards and floating dependency ranges are particularly common.
Floating dependencies can pull in unverified code, introduce breaking changes, or even expose your project to malicious packages. In an ecosystem as fast-paced as npm, this flexibility can quickly turn into a vulnerability, leaving your code open to security flaws and instability.
Some of the potential issues with floating dependency ranges:
Wildcard dependencies can land you in some weird situations where you get unexpected behavior during installation, resulting in disruptive conflicts and compatibility issues.
Nine months ago, someone posted a controversial hot take to reddit, advocating for using an asterisk/wildcard for each dependency in package.json in private projects. This was presumably to avoid having to update as often, but the post was subsequently deleted by its author after many developers weighed in to explain why this is a bad idea. They highlighted difficulty in debugging as another potential issue with wildcards.
Like touching a hot stove, you only have to get burned by this practice once to understand why it's a bad idea.
For one, some of your dependencies may have mutually conflicting dependencies, so periodically you will have to go in and pin some of your packages, anyway. But at least you have a chance to detect that possibility early when a build fails. Then all you have to do is find a good package-lock.json to revert to.
The other problem is when no two of your colleagues (nor build systems) is working on the same versions because actually updating packages all the time is not something that is typically done so subtle, undiscovered bugs are likely to slip through the cracks because nobody tested that specific set of dependencies. And by the time the issue is discovered, you don't know what dependency versions the working version of that feature was built on because they've been wiped out by an update.
Using wildcards for dependencies may seem like a convenient way to avoid frequent manual updates, but it can create a chaotic and unstable development environment. For example, if a package moves from version 1.5 to 2.0 and introduces changes that are not backward-compatible, your code that relies on the old API may break. If you have breaking changes with multiple packages, you will quickly have a huge mess on your hands, the kind of complex dependency issues that will bring you to your wit’s end.
While some developers may allow for flexibility with minor versions, most prefer to lock dependencies to specific versions and manually upgrade them to ensure compatibility and thorough testing. Here are a few best practices to follow wherever possible:
1. Use Exact Versions
2. Avoid Wildcards and Loose Versioning
3. Be Cautious with Caret (^) and Tilde (~) Ranges
4. Use package-lock.json
5. Semantic Versioning (SemVer)
Socket has a Wildcard Dependency alert that flags any package that has a dependency with a floating version range. It’s a medium-severity alert, because the behavior isn’t malicious but does introduce some security concerns. It flags any packages with dependencies that match the following:
In essence, these patterns are flagged because they allow for installing any version without strict control. Because wildcards can lead to unpredictable updates, they're often discouraged in critical systems. In these cases, you may want to update your security policy to monitor or warn.
The practice of using floating version ranges is especially common in early-stage projects or fast-evolving environments, where developers aim to stay up-to-date with the latest features or bug fixes.
However, many well-maintained projects avoid floating dependencies due to the associated risks. The popularity of floating dependencies can vary by ecosystem and the maturity of the project. It’s common to see them in open source projects, prototyping or rapid development, automation and CI/CD pipelines, and non-mission-critical projects.
Wildcard dependencies can be a ticking time bomb in your project. They might seem convenient, allowing your code to automatically adopt the latest version of a dependency, but this flexibility comes at a huge cost. By letting any version fit the criteria, you're inviting untested, unstable, or even malicious code into your project without warning. If you’re using dependencies with this alert, make sure you’re ready to monitor them and understand the risks. Otherwise, it’s a good idea to lock down your dependencies and ensure every version bump is deliberate and vetted.
Subscribe to our newsletter
Get notified when we publish new security blog posts!
Try it now
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.