Confused: A Surgical Strike Tool for Dependency Confusion Detection
Hook
In February 2021, a researcher made $130,000 in bug bounties by uploading packages named after private dependencies to public repositories. Your codebase might still be vulnerable to the same attack.
Context
Dependency confusion attacks exploit a fundamental ambiguity in how package managers resolve dependencies. When your application declares a dependency on “internal-auth-lib”, which repository should your package manager check first—your private corporate registry or the public npm/PyPI repository? If an attacker discovers the name of your private package and uploads a malicious version to the public repository with a higher version number, many package managers will happily download and execute the attacker’s code instead of your legitimate private package.
Alex Birsan’s research in February 2021 demonstrated this attack vector, compromising systems at major companies. The attack succeeded because organizations assumed their private package names were invisible to the outside world, never claiming those namespaces in public repositories. Microsoft responded with mitigation guidance, but the underlying vulnerability remains architectural—it’s baked into how dependency resolution works. The Confused tool emerged from Visma’s product security team as a pragmatic response: a way to audit whether your private package names have been left unclaimed in public repositories, creating an exploitable window for attackers.
Technical Insight
Confused operates as a single-binary CLI tool with a focused architecture. It performs static manifest parsing followed by queries against public package repository APIs to check for package existence. The tool supports five package ecosystems: Python (pip) with requirements.txt, JavaScript (npm) with package.json, PHP (composer) with composer.json, Maven (mvn) with pom.xml, and Ruby (rubygems) with Gemfile.lock.
The tool’s workflow follows three steps per package ecosystem: parse the dependency manifest file format, extract package names, and query the corresponding public repository API to check existence. For Python’s requirements.txt, it queries PyPI. For npm’s package.json, it checks the npm registry. Maven’s pom.xml queries Maven Central, and so on.
Here’s a practical example of running Confused against a Node.js project with mixed public and private dependencies:
# Basic scan of package.json
./confused -l npm package.json
Issues found, the following packages are not available in public package repositories:
[!] internal-auth-sdk
[!] @acmecorp/customer-api
[!] @acmecorp/logging-framework
This output immediately flags three packages that don’t exist in the public npm registry. The two scoped packages (@acmecorp/*) represent a known challenge in the npm ecosystem. The namespace filtering feature addresses this with wildcard support:
# Exclude known-secure scopes that you've claimed
./confused -l npm -s '@acmecorp/*,@yourorg/*' package.json
Issues found, the following packages are not available in public package repositories:
[!] internal-auth-sdk
Now only the unscoped private package appears, indicating a potential risk—anyone could upload a package called “internal-auth-sdk” to npm and potentially compromise your build pipeline.
The tool makes no network calls to your private registries and requires no authentication credentials. It’s a read-only security audit tool that operates entirely through public APIs. The tradeoff is that it cannot verify whether someone has already exploited the vulnerability—it only identifies the opportunity for exploitation.
The tool provides a verbose mode (-v flag) for additional output during scans. The -s flag accepts comma-separated lists of known-secure namespaces with wildcard support, helping filter out packages from scopes you control.
Gotcha
Confused’s most significant limitation stems from how package scopes and namespaces work in modern registries. The npm ecosystem’s scoped packages (@organization/package-name) create systematic false positives because private scopes aren’t publicly enumerable. As noted in the README: “Some packaging ecosystems like npm have a concept called ‘scopes’ that can be either private or public… The scopes are not inherently visible publicly, which means that confused cannot reliably detect if it has been claimed.” Even if your organization has properly claimed the @yourcompany scope in the public npm registry, Confused cannot verify this through API calls—it will flag every scoped package as potentially vulnerable unless you explicitly exclude them with the -s wildcard flag.
The tool also operates on a single manifest file at a time with no apparent built-in directory traversal. If your repository contains multiple microservices, each with their own package.json, requirements.txt, or pom.xml, you’ll need to handle iteration separately.
Finally, Confused identifies the vulnerability surface but provides no remediation guidance. As the README states: “This however doesn’t mean that an application isn’t already being actively exploited. If you know your software is using private package repositories, you should ensure that the namespaces for your private packages have been claimed by a trusted party.” You’re left to manually investigate each flagged package and implement fixes through other means.
Verdict
Use Confused if you’re conducting security audits on legacy codebases where dependency provenance is unclear, responding to potential dependency confusion incidents where you need quick triage across multiple package ecosystems, or establishing baseline vulnerability assessments before implementing private registry controls. It excels as a lightweight scanner that security teams can run during code reviews or security assessments. The single-binary distribution (available as prebuilt releases or via ‘go get’) makes it accessible for teams that need simple tooling. Skip Confused if you need ongoing supply chain security monitoring—it appears designed as a point-in-time diagnostic tool. Also be aware that if your codebase heavily uses scoped packages and you haven’t pre-claimed those namespaces, you’ll need to manually verify scope ownership and use the -s flag to filter known-secure namespaces. For production security pipelines, the README suggests ensuring “that the namespaces for your private packages have been claimed by a trusted party” and references Microsoft’s mitigation whitepaper for additional strategies beyond detection.