1.7M Installs, Zero Lines of Malicious Code
The payload was English, which is why lockfiles, dependency audits and pull-request review had nothing to inspect — and why a marketplace ranking did the distribution work.
The control and the attack sit on different layers
Every dependency control in production measures artifacts. A file, a hash, a diff, a version range in a lockfile. A skill or MCP manifest is not primarily an artifact. It is natural language plus tool declarations, resolved by name at runtime, interpreted by a model that then decides what to fetch. Nothing to pin in a lockfile, no dependency-audit scanner that parses it, no reviewer sitting between the manifest and the shell. So the cheapest attack against an agent is a sentence, and a sentence is the shape this campaign took.
| Governance dimension | PyPI / npm | Agent skills and MCP servers |
|---|---|---|
| How it resolves | Version range plus lockfile | By name, at runtime, by the model |
| Integrity check | Hashes and signatures | None by default |
| Audit tooling | Dependency scanners, advisory feeds | None in common use |
| Human review point | The pull request | None when installs are unattended |
| Revocation path | Yank plus advisory notification | Delisting from a marketplace |
Rank is an adversarially optimized statistic
The distribution mechanism is better treated as a measurement problem than a security one. These entries reached the marketplace's trending list, and trending rank is computed from installs, which is precisely the quantity an attacker can manufacture. Reading rank as evidence of safety means conditioning on popularity inside a population where popularity is purchasable. That is selection bias, and no threshold fixes it. The remedy is unglamorous: no popularity-based dependency selection for anything an agent can install, and resolution from a curated internal registry by digest instead.
An agent with unattended install rights is a code-review bypass you built yourself.
Turn "our agents might do this" into a number
Exposure here is measurable this sprint rather than arguable. Publish an internal canary skill whose name is a near-miss of a tool the agents already use. No payload, telemetry only. Run at least 100 task instances and record how often agents install it. Stratify the runs by configuration: autonomous versus approval-gated, and across the models actually served. The install rate with a confidence interval is the attack success rate. At 100 instances that interval is wide enough to separate zero from non-zero and too wide to rank configurations finely, which is the right level of precision for a first pass. Re-running after default-deny egress lands gives the effect size of the control instead of a vendor's claim about it. Two days of work converts a hardening debate into a metric a team can defend.
One caveat on scope: this account comes from a single reporting track, and marketplace install counts are self-reported. The thing those totals do not tell you is how many of the installs were real. The named typosquats are specific enough to grep for, which is the part worth acting on today; the exact totals are not load-bearing for the response.
What to do
Grep every agent config, MCP manifest and developer dotfile for Paperclip and Browser Use lookalikes this week, and treat any hit as confirmed credential compromise.
Put default-deny egress in front of every agent runtime and eval container this sprint, allowlisting your model endpoints and internal registry only, with alerts on fetches to any host outside that allowlist.
Run a benign internal typosquat canary across at least 100 task runs, stratified by autonomous versus approval-gated configs, before expanding agent autonomy next sprint.