Back to Blog
critical SEVERITY7 min read

How Wildcard Dependency Constraints Happen in Node.js and how to fix them

A critical supply chain vulnerability was discovered in the `package.json` of the `bpmn-js-task-resize` library, where wildcard (`*`) version constraints for `bpmn-js` and `diagram-js` allowed any version of those packages to be installed — including a maliciously compromised one. By pinning these dependencies to specific semver ranges (`^4.0.4` and `^4.0.3` respectively), the attack surface is dramatically reduced. This fix protects downstream consumers of the library from unknowingly executing

O
By Orbis AppSec
Published September 3, 2026Reviewed September 3, 2026

Answer Summary

This vulnerability is a supply chain attack risk (CWE-1104: Use of Unmaintained Third-Party Components / CWE-829: Inclusion of Functionality from Untrusted Control Sphere) in the Node.js `package.json` manifest of `bpmn-js-task-resize`. Wildcard (`*`) version constraints for `bpmn-js` and `diagram-js` meant that `npm install` could resolve to any published version, including a future malicious release. The fix pins these dependencies to `^4.0.4` and `^4.0.3` respectively, ensuring only known-good minor/patch updates are accepted and eliminating the unbounded version resolution window exploited in supply chain attacks.

Vulnerability at a Glance

cweCWE-829 (Inclusion of Functionality from Untrusted Control Sphere)
fixPinned both dependencies to specific semver ranges (`bpmn-js@^4.0.4`, `diagram-js@^4.0.3`)
riskMalicious npm package version automatically installed and executed in downstream consumers' browsers
languageJavaScript / Node.js
root causeWildcard (`*`) version constraints in `package.json` for `bpmn-js` and `diagram-js` impose no upper or lower bound on resolved package versions
vulnerabilityUnpinned/Wildcard npm Dependency (Supply Chain Risk)

The Risk Hidden in a Single Character: * in Your package.json

The package.json file is the beating heart of any Node.js project — it declares what your project is, how to build it, and critically, what it depends on. In the bpmn-js-task-resize library, two lines in this file contained a single character that quietly opened a supply chain attack vector affecting every downstream consumer of the package:

"bpmn-js": "*",
"diagram-js": "*"

That * is a wildcard version constraint. It tells npm: "I'll take any version of this package." And in a world where npm package hijacking and dependency confusion attacks are increasingly common, that's an open invitation for trouble.


The Vulnerability Explained

What Does * Actually Mean?

When npm resolves a * version constraint, it fetches the latest published version of the package at the time of installation. There is no floor and no ceiling — no minimum version required, no maximum version blocked. Every npm install is a fresh roll of the dice.

Here's the vulnerable section of package.json before the fix:

"dependencies": {
  "bpmn-js": "*",
  "diagram-js": "*"
}

For a library like bpmn-js-task-resize — which is itself a published npm package consumed by other projects — this is especially dangerous. When a downstream developer adds bpmn-js-task-resize to their own project and runs npm install, npm will also resolve bpmn-js and diagram-js using the * constraint. Their lockfile is generated from scratch based on what's currently published on the npm registry.

The Attack Scenario

Consider this realistic attack chain:

  1. An attacker gains publish access to the bpmn-js or diagram-js npm package — either by compromising maintainer credentials, exploiting a typosquatting opportunity, or through a dependency confusion attack targeting private package namespaces.

  2. The attacker publishes bpmn-js@99.0.0 containing malicious code — a cryptominer, a credential stealer, or a script that exfiltrates diagram data (which may contain sensitive business process information) to an external server.

  3. A developer working on a downstream project runs npm install bpmn-js-task-resize. Because the constraint is *, npm happily resolves bpmn-js to 99.0.0 — the latest available version.

  4. The malicious code is now bundled into the downstream application and executes in end-user browsers during BPMN diagram rendering or editing. BPMN diagrams often model sensitive business workflows, making this data particularly valuable.

  5. The downstream developer has no idea anything is wrong. Their package.json only references bpmn-js-task-resize, not bpmn-js directly.

Why This Is Especially Risky for a Library Package

If this were a standalone application with a committed package-lock.json, the lockfile would pin the resolved version and partially mitigate the risk (though the lockfile itself could be tampered with). But bpmn-js-task-resize is a library — a package consumed by others. Downstream consumers generate their own lockfiles. The * constraint in this library's package.json propagates directly into those consumers' dependency resolution, with no protection from the library's own lockfile.

This is precisely why npm's own documentation and security best practices explicitly warn against wildcard constraints in published packages.


The Fix

The fix is surgical and precise — two lines changed in package.json:

 "dependencies": {
-  "bpmn-js": "*",
-  "diagram-js": "*"
+  "bpmn-js": "^4.0.4",
+  "diagram-js": "^4.0.3"
 }

What This Change Does

By replacing * with ^4.0.4 and ^4.0.3, the fix establishes a semver floor and a bounded ceiling:

  • ^4.0.4 means: accept bpmn-js versions >=4.0.4 and <5.0.0. This allows bug fixes and non-breaking feature additions, but blocks major version jumps that could introduce breaking or malicious changes.
  • ^4.0.3 means: accept diagram-js versions >=4.0.3 and <5.0.0. Same protection applies.

This is the npm community's recommended constraint style for library dependencies — it balances security (bounded range) with maintainability (automatic patch/minor updates).

Before vs. After: Security Impact

Before After
Accepted versions Any version ever published >=4.0.4 <5.0.0 / >=4.0.3 <5.0.0
Malicious v99.0.0 accepted? ✅ Yes ❌ No
Patch updates accepted? ✅ Yes ✅ Yes
Breaking major version accepted? ✅ Yes ❌ No
Downstream consumers protected? ❌ No ✅ Yes

The fix also implicitly documents the known-good version of these dependencies — 4.0.4 and 4.0.3 — giving future maintainers a clear baseline to reason about compatibility.


Prevention & Best Practices

1. Never Use * or latest in Published Package Dependencies

For any package you publish to npm, treat * and latest as red flags in your dependencies and peerDependencies. Use explicit semver ranges. Tools like npm-check and depcheck can help audit your constraints.

2. Commit and Verify Lockfiles

For applications (not libraries), commit your package-lock.json or yarn.lock. In CI, use npm ci instead of npm install — it installs exclusively from the lockfile and fails if the lockfile doesn't match package.json.

3. Enable npm audit in CI Pipelines

Add npm audit --audit-level=high as a CI step. This catches known vulnerabilities in resolved dependencies before they reach production.

4. Consider Subresource Integrity and Package Provenance

For high-security contexts, explore npm's provenance attestations and tools like Socket.dev that analyze package behavior — not just known CVEs — to detect suspicious new versions of dependencies.

5. Use Dependabot or Renovate for Automated Dependency Updates

Automated tools like GitHub Dependabot or Renovate Bot will open PRs when new versions of your dependencies are released, keeping you on known-good versions without requiring manual monitoring.

Relevant Standards

  • OWASP A06:2021 – Vulnerable and Outdated Components: Directly addresses the risk of uncontrolled third-party dependency versions.
  • CWE-829: Inclusion of Functionality from Untrusted Control Sphere — the CWE that most precisely describes this vulnerability pattern.
  • SLSA (Supply-chain Levels for Software Artifacts): A framework for improving supply chain integrity, including dependency pinning practices.

Key Takeaways

  • Wildcard * constraints in a published library's package.json are not just sloppy — they're a security vulnerability that affects every downstream consumer who runs npm install.
  • bpmn-js and diagram-js are rendering engines that execute in user browsers; a compromised version of either could silently exfiltrate sensitive BPMN diagram data or execute arbitrary JavaScript.
  • Lockfiles do not protect library consumers — only the constraints in package.json govern what versions downstream projects resolve.
  • Pinning to ^4.0.4 and ^4.0.3 is the minimal correct fix — it preserves the ability to receive safe patch updates while blocking the unbounded version window that enabled the attack.
  • One character (*) in a manifest file can create a supply chain risk affecting an entire ecosystem of downstream users — dependency constraints deserve the same security scrutiny as application code.

How Orbis AppSec Detected This

  • Source: The dependencies block in package.json, specifically the version constraint fields for bpmn-js and diagram-js.
  • Sink: The npm registry resolution process — any npm install invocation by a downstream consumer would resolve these fields against all publicly published package versions, including future malicious ones.
  • Missing control: No version floor or ceiling was specified. The * wildcard imposed zero constraints on which package version would be fetched and executed.
  • CWE: CWE-829 — Inclusion of Functionality from Untrusted Control Sphere.
  • Fix: Replaced "bpmn-js": "*" with "bpmn-js": "^4.0.4" and "diagram-js": "*" with "diagram-js": "^4.0.3", establishing a bounded semver range that prevents resolution of unknown future versions.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

Supply chain attacks via npm have become one of the most impactful and underappreciated attack vectors in modern software development. The bpmn-js-task-resize vulnerability is a textbook example of how a single permissive character — * — in a dependency manifest can expose an entire ecosystem of downstream users to arbitrary code execution in their users' browsers.

The fix is simple, non-breaking, and immediately effective: pin bpmn-js to ^4.0.4 and diagram-js to ^4.0.3. But the broader lesson is that dependency constraints in published packages are a security boundary, not just a compatibility hint. Treat them accordingly — audit them, pin them, and monitor them with the same rigor you'd apply to your application code.


References

Frequently Asked Questions

What is a wildcard npm dependency vulnerability?

A wildcard (`*`) version constraint in `package.json` tells npm to accept any published version of a package. If that package is ever compromised or a malicious version is published, it will be automatically installed by anyone running `npm install`, with no version gating.

How do you prevent supply chain attacks via npm in Node.js?

Pin dependencies to specific semver ranges (e.g., `^4.0.4`), use a lockfile (`package-lock.json` or `yarn.lock`), enable npm audit in CI, and consider tools like Socket.dev or Dependabot to monitor for suspicious package updates.

What CWE is a wildcard npm dependency vulnerability?

CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) most directly applies, as the application includes third-party code without sufficient version controls. CWE-1104 (Use of Unmaintained Third-Party Components) is also relevant.

Is a lockfile enough to prevent supply chain attacks from wildcard dependencies?

A lockfile helps in single-project contexts by recording resolved versions, but it does not protect downstream consumers of a published library — they generate their own lockfile from your `package.json` constraints. Wildcard constraints remain dangerous for published packages even with a lockfile present.

Can static analysis detect wildcard npm dependency vulnerabilities?

Yes. Tools like Semgrep, Snyk, Socket.dev, and npm audit can flag wildcard or overly permissive version constraints in `package.json`. Orbis AppSec's multi-agent AI scanner flagged this exact pattern in `package.json:1`.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #28

Related Articles

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How dependabot-missing-cooldown happens in GitHub Actions/Node.js and how to fix it

The repository's `.github/dependabot.yml` had no cooldown period configured, meaning Dependabot could immediately propose updates to newly published package versions with zero time for the community to flag malware or instability. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` ecosystems, forcing a 7-day waiting period before new releases are surfaced as update PRs.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.

critical

How Remote Code Execution Happens in Handlebars Template Compilation and How to Fix It

CVE-2026-33937 is a critical remote code execution vulnerability in Handlebars.js that allows attackers to execute arbitrary code by passing maliciously crafted Abstract Syntax Tree (AST) objects to the compile() function. The vulnerability was patched in version 4.7.9, and we've upgraded to protect against this threat vector.