In 2021, a researcher demonstrated that major companies — including Microsoft, Apple, and Tesla — were vulnerable to a trivially simple supply-chain attack. He registered public packages with the same names as private internal packages used by these companies, and their build systems automatically downloaded and executed the malicious public packages instead of the internal ones.
The attack, called "dependency confusion," exploited no software vulnerabilities. It exploited an architectural assumption: that package registries are trustworthy by default and that name resolution is a sufficient basis for identity.
AI tool registries are inheriting this same assumption. And they are inheriting it at a moment when the consequences of a compromised tool are far worse than a compromised library — because an AI tool doesn't just run in a build pipeline. It runs with the agent's authority, on the user's behalf, with access to the user's data.
A Decade of Supply-Chain Attacks
The pattern is well-established. Let's review what each major ecosystem has taught us.
npm: The Ecosystem Scale Problem
npm is the world's largest software registry, with over 2.5 million packages. That scale has made it the primary target for supply-chain attacks:
event-stream (2018). A maintainer of a popular package (downloaded 2 million times per week) transferred ownership to an unknown individual who injected code targeting a specific Bitcoin wallet application. The malicious code was added incrementally over several months, making it difficult to detect in code review. The attack was discovered by accident, months after deployment.
ua-parser-js (2021). An attacker compromised the maintainer's npm account and published three malicious versions of a package downloaded 8 million times per week. The malicious versions installed a cryptominer and credential stealer. The attack window was four hours — long enough for thousands of build systems to pull the compromised version.
colors and faker (2022). A maintainer intentionally sabotaged their own widely-used packages to protest open-source economics, adding infinite loops that broke thousands of dependent applications.
Lessons:
- Account takeover gives attackers access to publish malicious versions
- Maintainer transfer can introduce untrusted parties
- Popularity is not a proxy for security
- Even a few hours of compromise can cause massive blast radius
PyPI: The Typosquatting Playground
PyPI's open-registration model and simple package names have made it the primary target for typosquatting attacks:
Typosquatting campaigns (2017–present). Researchers have repeatedly identified hundreds of malicious packages with names similar to popular ones: colourama instead of colorama, python-dateutil with a hyphen instead of python_dateutil with an underscore, requessts instead of requests. The malicious packages contain credential stealers, cryptominers, or reverse shells.
ctx and phpass (2022). An attacker re-registered abandoned package names and published new versions containing credential-harvesting code. Because existing requirements.txt files referenced these packages by name (not hash), pip install pulled the malicious versions.
W4SP stealer campaign (2023). Over 400 malicious packages were published to PyPI over several months, each containing a different variant of a credential stealer targeting Discord tokens, browser cookies, and cryptocurrency wallets.
Lessons:
- Name-based identity is insufficient — similar names are easy to exploit
- Abandoned packages create a re-registration vector
- Open registration with no verification enables industrial-scale attacks
- Automated detection catches many but not all malicious packages
Crates.io: The Governance Model
Rust's crates.io has had fewer major incidents, partly due to language-level safety guarantees and partly due to a more conservative governance model. But it's not immune:
rustdecimal (2022). A typosquatting package mimicking the popular rust_decimal crate contained code that exfiltrated environment variables (including CI/CD credentials) to an attacker-controlled server.
crate squatting (ongoing). Despite policies against name squatting, thousands of empty crates occupy the namespace, creating confusion and occasionally blocking legitimate packages.
Lessons:
- Language-level safety (memory safety, type safety) does not prevent supply-chain attacks
- Even curated ecosystems need active anti-squatting measures
- Environment variable exfiltration is a common payload because CI/CD environments are credential-rich
The Attack Vectors, Mapped
From these incidents, we can extract a taxonomy of supply-chain attack vectors:
| Attack Vector | Description | Example | |--------------|-------------|---------| | Account Takeover | Attacker gains access to a publisher's account and publishes malicious versions | ua-parser-js | | Maintainer Transfer | A new maintainer adds malicious code over time | event-stream | | Typosquatting | Attacker registers a similar name to a popular package | colourama, requessts | | Dependency Confusion | Public package name shadows a private internal package | dependency confusion (2021) | | Abandoned Re-registration | Attacker claims a formerly-popular abandoned name | ctx, phpass | | Intentional Sabotage | Maintainer deliberately breaks their own package | colors, faker | | Binary Substitution | Attacker replaces the published artifact without changing source | various CI/CD compromises | | Registry Compromise | Attacker gains access to the registry's storage and modifies packages | hypothetical (but studied) |
Now let's examine how each of these vectors applies to AI tool registries — and what MPP does about them.
How MPP Addresses Each Vector
Account Takeover → Publisher Key Separation
In npm and PyPI, your account is your publishing identity. If an attacker gets your password (or your session token, or your MFA recovery codes), they can publish as you.
In MPP, publishing identity is tied to a cryptographic key, not an account credential. Your Ed25519 private key signs your packages. The registry verifies your public key, but the registry cannot sign packages on your behalf — even if the registry itself is compromised. An attacker needs your private key, not your registry login.
The private key should live in a secure environment (HSM for production, secret manager for CI/CD) — not in a password database that can be phished.
Maintainer Transfer → Signature Verification
In the event-stream attack, the new maintainer had legitimate publish access. Their packages were valid npm packages. Nothing in npm's infrastructure could distinguish between a legitimate update and a backdoored one.
In MPP, every package is signed. If the maintainer changes, the key changes. The Gatekeeper flags this: the new version is signed by a different key than previous versions. Consumers that pin trust to a specific publisher key will automatically reject the new signer until they explicitly extend trust.
This doesn't prevent maintainer transfers — they're sometimes legitimate. But it makes them visible and creates a decision point where the consumer can evaluate the new signer before trusting their code.
Typosquatting → Reverse Domain Naming
PyPI's flat namespace makes typosquatting trivial: requests vs requessts vs requsts. One character difference, and a user might not notice.
MPP uses reverse-domain package naming: org.acme.database-tool, not database-tool. This has two effects:
- Namespace collision is harder. An attacker cannot register
org.acme.database-toolwithout proving control of theacme.orgdomain. (Registry policies can enforce domain verification.) - Typosquatting is more visible.
org.acme.database-toolvsorg.acmee.database-tool— the extra 'e' in the organisation component stands out more when the name has structural components rather than being a freeform string.
This is the approach used by Java (Maven Central), Android (Google Play), and Apple (App Store). It doesn't eliminate name-based attacks, but it raises the bar significantly.
Dependency Confusion → Namespace + Signing
Dependency confusion works because internal and external packages share a flat namespace, and the resolution algorithm doesn't distinguish between them.
MPP's reverse-domain naming creates natural namespace separation: org.mycompany.internal-tool vs io.github.someuser.similar-tool. Combined with publisher key pinning (the host trusts specific keys, not package names), a public package cannot shadow an internal one. Even if an attacker registers an identically-named package, it will be signed by a different key and rejected.
Abandoned Re-registration → Immutable Publisher History
When a package name is abandoned in PyPI, anyone can re-register it. MPP registries maintain publisher history: the mapping between package names and publisher keys is part of the registry's verifiable state. If a package was previously published by key A and a new version appears signed by key B, this is flagged as a publisher change — even if the new publisher legitimately registered the name.
Consumers that pin to publisher keys are protected automatically. Consumers that rely on name-based trust will see the publisher change in the audit log and can investigate.
Intentional Sabotage → Sandbox Containment
The colors/faker incident was unique: a maintainer destroyed their own packages. No amount of signing or verification can prevent an author from publishing harmful code under their own key.
MPP's defense is the sandbox. Even a deliberately destructive tool is contained:
- It cannot access the filesystem beyond its declared paths
- It cannot access the network beyond its declared domains
- It cannot read environment variables beyond its declared keys
- It cannot exceed its memory or time limits
A tool that enters an infinite loop will be killed by the timeout. A tool that tries to fill the filesystem will be stopped by the sandbox boundary. A tool that returns garbage output will be caught by the response schema validation.
The sandbox doesn't prevent sabotage — but it makes sabotage boring. The blast radius is limited to the tool's declared capabilities.
Binary Substitution → Content Hashing
If an attacker modifies a published artifact (by compromising CI/CD, intercepting the upload, or tampering with storage), the content hash embedded in the MPP signature will not match. The Gatekeeper will reject the package.
This is the most direct defense: the SHA-256 content hash, computed over every byte of every file in the package (sorted and deterministic), is bound to the Ed25519 signature. Changing one byte invalidates the signature.
Registry Compromise → Client-Side Verification
If an attacker compromises the registry itself — the database, the storage backend, the API servers — they can serve modified packages. In npm and PyPI, registry compromise is effectively game over: consumers download whatever the registry serves.
In MPP, verification happens on the client side. The Gatekeeper verifies signatures and content hashes before execution, regardless of where the package came from. A compromised registry can serve tampered packages, but every tampered package will fail verification on the consumer's machine.
This architectural decision — client-side verification rather than server-side trust — means that registry compromise does not automatically lead to consumer compromise. The registry is a distribution mechanism, not a trust anchor.
What AI Registries Get Wrong
Most AI tool registries today operate at approximately the security level of npm in 2015: open registration, name-based identity, no signing, no sandboxing, and trust-on-download.
Some are worse. MCP tool servers are typically hosted processes with full system access. There is no package format, no signature, and no sandbox. The "registry" is often a GitHub repository listing URLs.
The lessons from a decade of supply-chain attacks are available. The attack patterns are documented. The defences are known. The question is whether AI tool ecosystems will learn from software's mistakes or repeat them.
MPP was designed by engineers who watched the npm, PyPI, and crates.io attack timeline unfold and asked: what would a tool ecosystem look like if these defences were built in from day one?
The answer is:
- Cryptographic identity instead of account-based identity
- Client-side verification instead of server-side trust
- Capability-based sandboxing instead of ambient authority
- Privacy filtering instead of unbounded data access
- Hash-chained audit logs instead of mutable application logs
- Reverse-domain naming instead of flat namespaces
- Publisher key pinning instead of name-based resolution
None of these are novel ideas. They are proven techniques from operating system security, code signing, and supply-chain hardening. What's novel is applying all of them, together, as the foundation of an AI tool ecosystem rather than bolting them on after the first major incident.
The Cost of Waiting
Every major software ecosystem introduced security measures after a significant attack:
- npm added 2FA for popular packages after ua-parser-js
- PyPI added mandatory 2FA for critical projects after years of typosquatting campaigns
- crates.io tightened name-squatting policies after rustdecimal
In each case, the retroactive fix was more expensive and less effective than proactive design would have been. Existing packages could not be retroactively signed. Existing namespaces could not be retroactively restructured. Existing trust models could not be retroactively replaced without breaking compatibility.
AI tool ecosystems are at the beginning of this curve. The number of published tools is small. The namespace is not yet polluted. The trust models are still forming. This is the window for building security in — before the first major supply-chain attack on an AI tool registry makes the cost of waiting concrete.
For the cryptographic signing details that underpin MPP's supply-chain defenses, read Cryptographic Signing for AI Tools: What Ed25519 Gives You. For the full security model, see the Security Model documentation.