Unexpected Dependencies

The unexpected dependencies rule flags package versions that introduce new dependencies not present in the package's history. This catches dependency injection attacks where a compromised package pulls in a malicious sub-dependency.

What it detects

When a new version of a package adds a dependency that has never appeared in any previous version, this rule triggers. It specifically targets the event-stream attack pattern where an attacker gains control of a package and injects a malicious dependency deep in the tree.

How it works

  1. The rule sorts all versions of a package by publish time
  2. It builds a set of all dependencies ever seen across all versions except the most recent 3
  3. For the 3 most recent versions, it checks if any new dependency names appear that are not in the historical set
  4. Versions that introduce new dependencies are flagged

The "most recent 3" threshold ensures that the rule catches changes in the latest versions while ignoring historical dependency churn. The rule requires at least 2 versions to exist -- single-version packages are skipped.

What counts as a dependency

The rule checks the dependencies field in package.json. It does not check devDependencies, peerDependencies, or optionalDependencies, because those are not installed by default in production.

Configuration

SettingValue
Default level (Free)off
Default level (Pro)warn
Parametersnone

Real-world examples

This rule would have caught:

  • event-stream (2018) -- A trusted maintainer transferred ownership to a stranger who injected the flatmap-stream dependency, which contained a crypto-stealing backdoor targeting a Bitcoin wallet app. The flatmap-stream dependency had never been part of event-stream before.

Limitations

  • The rule requires at least 2 published versions to build a historical baseline. Brand-new packages with only 1 version are not checked.
  • Adding a new dependency is often legitimate (a package might refactor to use a shared utility). The rule is designed to flag anomalies, not block all changes. That is why the default is warn rather than block.
  • The rule checks the dependency list at the version level, not the resolved dependency tree. It cannot detect deeply nested transitive dependency changes.

Warn for most teams. Dependency changes are common and often legitimate, but logging them provides visibility into what is changing in your supply chain. Teams with strict security requirements can set this to block, which will cause nproxy to strip the affected versions from the packument.