Supply Chain Risk: Vetting Skills and Dependencies

Learning Objectives

Pre-Reading Check — The Agent Supply Chain

1. What is the defining characteristic of a supply chain attack?

The attacker directly exploits a bug in your own application code.
The attacker compromises something you depend on, so their code inherits your trust.
The attacker floods your service with traffic until it fails.
The attacker guesses a weak password on your admin account.

2. Why do transitive dependencies represent so much of an agent's attack surface?

They are always larger in file size than direct dependencies.
They are written in less secure programming languages.
They arrive unexamined as dependencies-of-dependencies you never explicitly chose.
They run with root privileges by default.

3. What makes third-party skills and MCP servers a sharper marketplace risk than mature package registries?

Their hubs are new and lightly moderated, often with no robust vetting process.
They can only be installed manually one at a time.
They are always more expensive than traditional packages.
They cannot be pinned to a specific version.

4. In the event-stream (2018) incident, how did the malicious payload reach most victims?

Victims were phished into downloading a fake installer.
Most projects pulled it in as a deep transitive dependency they had never heard of.
Victims deliberately installed a known-malicious package.
The attacker brute-forced npm's servers.

5. Why is a registry or marketplace considered a "chokepoint" for attackers?

It rate-limits downloads, slowing legitimate installs.
Compromising the distribution channel lets the attacker reach everyone downstream.
It is the only place developers can report security bugs.
It stores plaintext copies of every user's credentials.

The Agent Supply Chain

Key Points

When you build a personal AI assistant, almost none of the code that runs is code you wrote. You wrote a thin layer of glue; everything underneath — the HTTP client, JSON parser, Markdown renderer, model SDK, and the dozens of packages those depend on — was written and published by strangers. Layer agent "skills" and Model Context Protocol (MCP) servers on top, and your assistant becomes a machine that eagerly loads and executes other people's instructions with your credentials.

Think of the supply chain as an airport: you screen the passengers you invited (your own code), but you also accept connecting flights (dependencies), catering trucks (skills and plugins), and fuel deliveries (model and prompt assets) — trusting that each was screened somewhere upstream. A single unscreened item anywhere can walk straight onto your aircraft.

Figure 9.1: The agent supply chain — layers of code and instructions you trust

graph TD A["Personal AI Assistant
(your glue code)"] --> B["Direct code
dependencies"] A --> C["Third-party skills"] A --> D["MCP servers"] A --> E["Model & prompt assets"] B --> F["Transitive dependencies
(dependencies of dependencies)"] F --> G["Deeply nested packages
you never chose"] C --> H["Marketplaces & hubs
(lightly vetted)"] D --> H E --> I["System prompts,
tool descriptions,
model weights"] H --> J["Lookalike or
malicious payloads"] G --> K["Runs with YOUR
credentials & authority"] J --> K I --> K style A fill:#cde4ff,stroke:#333 style K fill:#ffd6d6,stroke:#333

Visual animation — coming soon

Transitive dependencies matter enormously because most of your attack surface lives in code you never chose. The canonical event-stream (2018) incident — roughly 1.5 million weekly downloads — hit hard precisely because most downstream projects pulled it in as a deep transitive dependency after an attacker social-engineered the maintainer into handing over publishing rights, then inserted a module targeting the Copay Bitcoin wallet.

A registry or hub is a chokepoint: compromise the channel and you reach everyone. In ua-parser-js (2021), an attacker took over the maintainer's npm account and published malicious versions of a package with over 7 million weekly downloads, dropping credential stealers and cryptominers. Nothing about the victims' own code was wrong — they simply trusted the registry to serve them the package they asked for. For AI agents the risk is sharper still, because skill hubs are new and lightly moderated.

Post-Reading Check — The Agent Supply Chain

1. What is the defining characteristic of a supply chain attack?

The attacker directly exploits a bug in your own application code.
The attacker compromises something you depend on, so their code inherits your trust.
The attacker floods your service with traffic until it fails.
The attacker guesses a weak password on your admin account.

2. Why do transitive dependencies represent so much of an agent's attack surface?

They are always larger in file size than direct dependencies.
They are written in less secure programming languages.
They arrive unexamined as dependencies-of-dependencies you never explicitly chose.
They run with root privileges by default.

3. What makes third-party skills and MCP servers a sharper marketplace risk than mature package registries?

Their hubs are new and lightly moderated, often with no robust vetting process.
They can only be installed manually one at a time.
They are always more expensive than traditional packages.
They cannot be pinned to a specific version.

4. In the event-stream (2018) incident, how did the malicious payload reach most victims?

Victims were phished into downloading a fake installer.
Most projects pulled it in as a deep transitive dependency they had never heard of.
Victims deliberately installed a known-malicious package.
The attacker brute-forced npm's servers.

5. Why is a registry or marketplace considered a "chokepoint" for attackers?

It rate-limits downloads, slowing legitimate installs.
Compromising the distribution channel lets the attacker reach everyone downstream.
It is the only place developers can report security bugs.
It stores plaintext copies of every user's credentials.
Pre-Reading Check — Attacks Through Dependencies

1. What human weakness does typosquatting primarily exploit?

Reluctance to update dependencies.
Typos, misremembered names, and copy-paste errors when naming packages.
Weak passwords on developer accounts.
Fear of running security scanners.

2. Why did Alex Birsan's dependency-confusion technique succeed without any phishing?

He bribed employees at each target company.
Resolvers checking both a private index and the public registry preferred his higher public version number.
He exploited an unpatched zero-day in npm itself.
He guessed the companies' internal build passwords.

3. What root cause do the event-stream and ua-parser-js incidents share that name-checking cannot catch?

Both packages had obvious typos in their names.
A legitimate package became malicious via a hijacked or socially-engineered maintainer.
Both were published at version 9.9.9 to win version resolution.
Both required victims to disable their firewalls.

4. In Snyk's ToxicSkills audit, what was notable about the confirmed malicious skills?

91% combined traditional malware patterns with prompt-injection techniques.
All of them were caught before any user installed them.
None of them requested any permissions.
They only affected skills written in Python.

5. Why are malicious skills uniquely hard to observe compared to ordinary packages?

They are always encrypted end-to-end.
Their instruction payload executes inside the model's reasoning, where observability tools cannot follow.
They run only on weekends when logging is off.
They delete their own source code after installation.

Attacks Through Dependencies

Key Points

Typosquatting publishes a malicious package whose name closely resembles a popular one — colourama for colorama, loadsh for lodash — betting a developer will mistype or copy a bad name, then embedding credential stealers or cryptominers that run at install time.

Dependency confusion is subtler. In 2021, Alex Birsan executed his code inside Microsoft, Apple, Uber, and 30-plus other companies with no phishing at all. He mined leaked package.json files for the names of private, internal packages, then published packages with those exact names at very high versions like 9.9.9 to the public registries. Build systems checking both a private index and the public registry preferred the higher version number and pulled the attacker's package. The attack earned Birsan over $130,000 in bug bounties.

Figure 9.2: Dependency confusion attack flow (Alex Birsan, 2021)

sequenceDiagram participant A as Attacker participant GH as Public GitHub / npm errors participant PUB as Public Registry
(npm / PyPI) participant B as Company Build System participant PRIV as Private Index A->>GH: Mine leaked package.json
for internal package names GH-->>A: Learn private package name A->>PUB: Publish same name at
version "9.9.9" B->>PRIV: Request internal package B->>PUB: Also check public registry PUB-->>B: Offer "9.9.9" (higher version wins) B->>B: Resolver installs malicious
public package B->>A: Payload phones home
with system info

The three most common dependency-side attacks contrast as follows:

AttackHow it worksWhat it exploitsPrimary defense
TyposquattingMalicious package named like a popular one (loadsh vs lodash)Human typos and copy-paste errorsBehavioral scanning at install; careful review of names
Dependency confusionPublic package matches a private internal name at a higher versionResolvers preferring the highest version across indexesName reservation; single explicit index; version pinning
Malicious updateA trusted package ships a poisoned new versionAuto-updating and blind trust in maintainersLockfiles; version pinning; --ignore-scripts

Both event-stream and ua-parser-js share a root cause no name-checking would catch: the legitimate package became malicious via a hijacked or socially-engineered maintainer. This is why the CERT Coordination Center stresses that the consistency of how a maintainer responds matters more than a raw vulnerability count. For agents, the "code" that can be poisoned even includes system prompts, tool descriptions, and model weights.

Agent skills are the fastest-growing and least-understood part of the supply chain, and the data is sobering. Snyk's February 2026 ToxicSkills audit of 3,984 skills found 36.82% had at least one flaw, 13.4% had a critical issue, and 76 carried confirmed malicious payloads — and 91% of malicious skills fused malware with prompt injection. Roughly one in four MCP servers opens the agent to code-execution risk. What makes skills different is where they execute: a skill loads a textual instruction set directly into the model's reasoning, so "what happens next plays out inside the model's reasoning where observability tools cannot follow."

Visual animation — coming soon

Post-Reading Check — Attacks Through Dependencies

1. What human weakness does typosquatting primarily exploit?

Reluctance to update dependencies.
Typos, misremembered names, and copy-paste errors when naming packages.
Weak passwords on developer accounts.
Fear of running security scanners.

2. Why did Alex Birsan's dependency-confusion technique succeed without any phishing?

He bribed employees at each target company.
Resolvers checking both a private index and the public registry preferred his higher public version number.
He exploited an unpatched zero-day in npm itself.
He guessed the companies' internal build passwords.

3. What root cause do the event-stream and ua-parser-js incidents share that name-checking cannot catch?

Both packages had obvious typos in their names.
A legitimate package became malicious via a hijacked or socially-engineered maintainer.
Both were published at version 9.9.9 to win version resolution.
Both required victims to disable their firewalls.

4. In Snyk's ToxicSkills audit, what was notable about the confirmed malicious skills?

91% combined traditional malware patterns with prompt-injection techniques.
All of them were caught before any user installed them.
None of them requested any permissions.
They only affected skills written in Python.

5. Why are malicious skills uniquely hard to observe compared to ordinary packages?

They are always encrypted end-to-end.
Their instruction payload executes inside the model's reasoning, where observability tools cannot follow.
They run only on weekends when logging is off.
They delete their own source code after installation.
Pre-Reading Check — Vetting and Controls

1. How should a low OpenSSF Scorecard for a package you are about to pin be treated?

As a definitive verdict to permanently ban the package.
As a signal to look closer, not a verdict on its own.
As irrelevant, since Scorecard only measures popularity.
As proof the package contains malware.

2. What does version pinning directly defeat in a dependency-confusion attack?

Nothing — pinning only affects licensing.
An attacker's 9.9.9 cannot silently win against your pinned lower version.
It encrypts the network traffic during install.
It prevents the package from being downloaded at all.

3. What is the primary payoff of maintaining an SBOM?

It automatically rewrites vulnerable code for you.
When a component is disclosed as compromised, you can trace your exposure in minutes instead of hours.
It blocks all transitive dependencies from installing.
It guarantees no vulnerability will ever be introduced.

4. Why does isolation remain necessary even after careful vetting?

Vetting is illegal in most jurisdictions.
Vetting lowers the probability of a bad component; isolation caps the impact if one slips through.
Isolation makes installs faster.
Vetting only works for skills, never for code dependencies.

5. In the worked "read-my-calendar" example, why run the server with a dedicated read-only calendar token in an egress-restricted sandbox?

To make the calendar sync faster.
So that even a malicious server discovered after install cannot reach your files, other tokens, or an attacker's server.
Because calendar APIs refuse primary account tokens.
To avoid paying for a second API subscription.

Vetting and Controls

Key Points

The cheapest moment to stop a malicious dependency is before it runs with your agent's authority. The OpenSSF Scorecard automates much of the initial judgment: roughly 18–19 automated checks scored 0–10, covering branch protection, required code review, pinned dependencies, CI/CD security, fuzzing, and disclosure practices. A low score is a signal to look closer, not a verdict. Layer human signals on top: a root SECURITY.md, an active advisories history, and a maintainer who credits reporters.

Skills and MCP servers need a more deliberate checklist because their payload hides in text. Review the publisher, permissions, instructions, scripts, dependencies, network behavior, and update path. MCP Server Scanning inspects tool descriptions and endpoints for hidden instructions and exfiltration patterns; Agent Skill Scanning analyzes skills statically — without executing them — for command injection, obfuscation, privilege escalation, and supply-chain indicators.

Figure 9.3: Vetting decision flow for a skill or MCP server before install

flowchart TD A["New skill / MCP server
requested"] --> B{"Reputable publisher?
OpenSSF Scorecard,
SECURITY.md, advisories"} B -->|No| R["Reject or
investigate further"] B -->|Yes| C{"Static scan clean?
No hidden instructions,
obfuscation, exfil URLs"} C -->|No| R C -->|Yes| D{"Permissions minimal?
Only what it needs"} D -->|No| R D -->|Yes| E["Pin version, record
checksum, add to allowlist,
commit lockfile"] E --> F["Generate SBOM
at build time"] F --> G["Run in ephemeral sandbox:
egress allowlist, non-root,
dedicated low-value token"] G --> H["Skill runs with
bounded blast radius"] style R fill:#ffd6d6,stroke:#333 style H fill:#d6ffd6,stroke:#333

Three composable controls turn a chaotic dependency tree into an auditable one. Version pinning locks a dependency to an exact version, so an attacker's 9.9.9 cannot silently win against a pinned 2.3.1. A lockfile (package-lock.json, poetry.lock, Cargo.lock) records the exact version and an integrity hash of every direct and transitive dependency; in CI the single most effective npm defense is npm ci --ignore-scripts against a committed lockfile. An SBOM is a structured inventory of every component that lets you trace exposure "in minutes instead of hours" — via SPDX (license-oriented) or CycloneDX (security-oriented).

For the largest picture, the SLSA framework describes graduated maturity for build integrity and provenance — verifiable metadata about where a component came from and how it was built.

Figure 9.4: SLSA Build track — graduated levels of build integrity

graph TD L0["L0: No guarantees
No provenance"] --> L1["L1: Basic provenance
Source repo, commit hash,
dependencies, output digest"] L1 --> L2["L2: Hosted build +
digital signatures
Prevents tampering"] L2 --> L3["L3: Hardened, isolated
build platform
Authenticated, signed provenance"] style L0 fill:#ffd6d6,stroke:#333 style L1 fill:#fff0c2,stroke:#333 style L2 fill:#e0f0c2,stroke:#333 style L3 fill:#d6ffd6,stroke:#333

Vetting reduces the probability of a bad component; isolation reduces its impact. A reviewed-but-not-fully-trusted skill should run in an ephemeral, sandboxed environment — no host mounts, a scoped working directory, an egress allowlist, and a non-root, low-privilege identity carrying only dedicated, low-value credentials. Maintain an allowlist of approved MCP servers and skills, pin versions, and verify checksums so only components you deliberately blessed can load.

Worked example — vetting a "read-my-calendar" MCP server: (1) run OpenSSF Scorecard and confirm SECURITY.md; (2) static-scan tool definitions for hidden instructions or exfil URLs; (3) confirm it requests calendar read only; (4) pin, record checksum, add to allowlist, commit the lockfile; (5) generate a CycloneDX SBOM at build time; (6) run it in an ephemeral sandbox with egress limited to the calendar endpoint, as a non-root user, using a dedicated read-only token — never your primary account. With step 6 in place, even a malicious server discovered after install cannot reach your files, your other tokens, or an attacker's server.

Visual animation — coming soon

Post-Reading Check — Vetting and Controls

1. How should a low OpenSSF Scorecard for a package you are about to pin be treated?

As a definitive verdict to permanently ban the package.
As a signal to look closer, not a verdict on its own.
As irrelevant, since Scorecard only measures popularity.
As proof the package contains malware.

2. What does version pinning directly defeat in a dependency-confusion attack?

Nothing — pinning only affects licensing.
An attacker's 9.9.9 cannot silently win against your pinned lower version.
It encrypts the network traffic during install.
It prevents the package from being downloaded at all.

3. What is the primary payoff of maintaining an SBOM?

It automatically rewrites vulnerable code for you.
When a component is disclosed as compromised, you can trace your exposure in minutes instead of hours.
It blocks all transitive dependencies from installing.
It guarantees no vulnerability will ever be introduced.

4. Why does isolation remain necessary even after careful vetting?

Vetting is illegal in most jurisdictions.
Vetting lowers the probability of a bad component; isolation caps the impact if one slips through.
Isolation makes installs faster.
Vetting only works for skills, never for code dependencies.

5. In the worked "read-my-calendar" example, why run the server with a dedicated read-only calendar token in an egress-restricted sandbox?

To make the calendar sync faster.
So that even a malicious server discovered after install cannot reach your files, other tokens, or an attacker's server.
Because calendar APIs refuse primary account tokens.
To avoid paying for a second API subscription.

Your Progress

Answer Explanations