supply-chain-audit
Skill0 starsChanged 3 months agoMITView on GitHub
Decide whether a third-party npm package is safe to admit into a repository. Use when asked to review, vet, audit, or approve adding a dependency, or when asked whether a package is safe or trustworthy. Covers unpacking the tarball in the sandbox, reading what is actually inside it, weighing that against registry signals, and reaching a verdict backed by evidence.
9 other projects use a version of this.
Adopted by
Most recently taken up by
- HetCreep/CoalMinetheir own version, first seen Nov 8, 2026
- TheColliery/CoalMinetheir own version, first seen Nov 8, 2026
- mahumadad/quarrytheir own version, first seen Nov 8, 2026
- michaelalber/ai-toolkittheir own version, first seen Nov 1, 2026
- mtarcure/claude-vibe-squadtheir own version, first seen Oct 28, 2026
- ulises-jeremias/agent-toolkittheir own version, first seen Aug 31, 2026
Other versions9 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 9
- mtarcure/claude-vibe-squad★ 220same name
- crafter-station/skills★ 111same name
- ulises-jeremias/agent-toolkit★ 19same name
- HetCreep/CoalMine★ 15same name
- TheColliery/CoalMine★ 15same name
- Montimage/skills★ 9same name
- michaelalber/ai-toolkit★ 3same name
- IsaacGemal/claude-skills★ 2same name
- mahumadad/quarry★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
Show its rules and text
We split this skill into the rules it gives an agent
23 rules. 0 are common, 13 are distinctive, and 10 are very specific to this project.
0
Common
Most of the 8 edited versions we read keep these.
13
Distinctive
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 18
Do these in order. Later steps depend on what earlier ones return, and stopping early is often correct.
- Very specific: line 23
Call get_package_metadata.
- Distinctive: line 26
If the package does not exist, say so and stop.
- Very specific: line 31
Call find_impersonators before you read any code.
- Very specific: line 35
When that happens, say so first and plainly: *"Did you mean express?
- Very specific: line 42
Call get_download_stats and find_advisories.
- Very specific: line 60
Run the bundled inspector using Code Mode, importing it rather than shelling out.
- Distinctive: line 84
Read the findings carefully.
- Distinctive: line 103
Every finding the inspector produces is a *candidate*.
- Distinctive: line 111
Read the surrounding lines before you keep a finding.
- Distinctive: line 117
Commit to one of three, and say which:
- Very specific: line 135
Every name you put in root must be defined below it.
- Very specific: line 185
When several packages were audited, lead with one Table of package, version and verdict, then a Card per package underneath in the same …
- Distinctive: line 188
Keep the prose outside the block short.
- Distinctive: line 192
Only registered components render.
- Very specific: line 206
If you are unsure whether a component exists, use MarkDownRenderer rather than guessing.
- Distinctive: line 210
Prefer the plain ones. Reach for a chart only when there is genuinely a distribution worth seeing, which for a single package there is not.
- Distinctive: line 228
If one subagent could not complete -- a tarball that would not download, a repository that could not be read -- say so for that package …
- Very specific: line 234
Only after the user has seen the verdict, and only if they ask for it, add the dependency through the GitHub tools: a branch, a change to …
- Very specific: line 238
This write pauses for approval and that is deliberate: Before it does, show the user exactly what you intend: the branch name, the precise …
- Distinctive: line 243
If they deny it, do not look for another route to the same effect.
- Distinctive: line 248
Never report a finding you have not read the evidence for.
- Distinctive: line 249
Never execute the package under audit.
“Common” is measured: kept by at least 6 in 10 of the 8 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Auditing an npm package before it enters a repository
Your job is to answer one question: should this package be admitted? Not "is it popular", not "does it have vulnerabilities" — whether a careful engineer, shown what you found, would let it in.
Two things make that answer trustworthy. You read the code that will actually run, rather than the code the repository advertises. And every claim you make points at a file and a line.
The order of work
Do these in order. Later steps depend on what earlier ones return, and stopping early is often correct.
1. Establish what you are looking at
Call get_package_metadata. You need the resolved version, the tarball URL, the
linked source repository, and whether the version declares lifecycle scripts.
If the package does not exist, say so and stop. Do not guess at a near name — a mistyped package name is itself the thing you are guarding against.
2. Check whether the name is the attack
Call find_impersonators before you read any code. If the package sits one or
two edits from something with orders of magnitude more downloads, the most likely
explanation is that the user typed the wrong name.
When that happens, say so first and plainly: "Did you mean express? The package
you named, expres, is a different package with 5,000 weekly downloads against
express's 132 million." Ask before continuing. A user who typed a typo does not
want a security report; they want the typo pointed out.
3. Gather the registry signals
Call get_download_stats and find_advisories. Together with the metadata you
already have, weigh:
- Age and cadence. A package published days ago, or one that shipped a burst of releases in the last week after years of quiet, deserves more scrutiny. The second pattern is what a hijacked maintainer account looks like from outside.
- Downloads against reputation. A name that sounds official but has forty weekly downloads is a mismatch worth stating.
- Maintainer count. A single maintainer is not a finding on its own. It is context for everything else.
- Advisories. Note severity and whether a fixed version exists.
None of these condemn a package by themselves. They tell you how hard to look next.
4. Read what is actually in the tarball
This is the step nobody does by hand, and the reason you have a sandbox.
Run the bundled inspector using Code Mode, importing it rather than shelling
out. Do not use a shell command for this: the sandbox image is not guaranteed to
have bash, and a step that depends on one silently fails on some images. Python
is always present, because the harness runs its own client there.
import sys, json
sys.path.insert(0, "/opt/tf/skills/supply-chain-audit/scripts")
from inspect_package import audit
report = audit(
name="<package>",
version="<version>",
tarball_url="<tarball url from get_package_metadata>",
repo_url="<repository_url from get_package_metadata, or None>",
)
print(json.dumps(report, indent=2))It downloads the published tarball, unpacks it safely, and reports what is inside.
It never executes the package. It returns a dict rather than raising, so if
something could not be checked you get a limitations entry to report rather than
an error to work around.
Read the findings carefully; the checks that matter most are:
install_script— apreinstall,install, orpostinstallhook. This code runs on every machine that installs the package, including CI, before anyone has looked at anything. Always quote the command verbatim in your report. Most packages have no install hook at all; one that does needs a reason.tarball_only_source— a source file present in the published tarball that does not exist in the linked GitHub repository. This is the strongest signal in the whole audit. The repository is what humans review; the tarball is what runs. Code that exists only in the tarball was reviewed by nobody.obfuscation— long encoded literals,eval,new Function, dense hex escapes. Legitimate packages ship minified code indist/; the inspector already accounts for that, so a hit outside a build directory is meaningful.process_executionandnetwork_egress—child_process, raw sockets, or hardcoded hosts. In an install script these are close to disqualifying. In library code they may be the package's whole purpose. Judge by context.
5. Verify before you report
Every finding the inspector produces is a candidate. Before it reaches your report, look at the evidence yourself and drop what does not hold up:
- A base64 blob in a package whose job is encoding is not obfuscation.
- A
postinstallthat runsnode-gyp rebuildis a native module compiling, not an attack. - A test fixture containing a fake key is not a leaked credential.
Read the surrounding lines before you keep a finding. A false positive costs you more than a missed low-severity issue — an agent that cries wolf gets ignored, and then the one real finding is ignored too.
6. Reach a verdict
Commit to one of three, and say which:
- Admit — nothing found that a careful reviewer would object to.
- Admit with conditions — safe enough, but pin the version, or use
--ignore-scripts, or prefer a named alternative. State the condition. - Refuse — say exactly which finding drives the refusal. One sentence.
Render the report as Generative UI
Emit the report inside an openui fenced block, not as markdown tables. The chat
UI renders that block as real components -- cards, tables, callouts -- and a markdown
table is a flat wall of pipes by comparison. This is the part of the answer the
reader actually looks at, so it is worth the few extra tokens.
The person reading has to see three things at a glance: what you decided, what you looked at, and what you could not check. Use the same shape every run, so a reader learns it once. Fill in the values; keep the structure.
Every name you put in root must be defined below it. A reference to a component
you decided not to emit is a broken block, and a broken block renders as nothing at
all -- so build root from the cards you actually have:
root = Stack([verdict, signals, findings], "column", "m")and only when there are limitations to report:
root = Stack([verdict, signals, findings, gaps], "column", "m")verdict = Callout(<tone>, "<Verdict>: <package>@<version>", "<one sentence, the reason>")tone is "success" to admit, "warning" to admit with conditions, "danger" to
refuse. The title carries the decision and the exact version it applies to -- a
verdict without a version is not a verdict.
signals = Card([CardHeader("What I looked at", "Registry and tarball"), signalsTable])
signalsTable = Table([Col("Signal", names), Col("Value", values), Col("Source", sources)])One row each for age, weekly downloads, maintainers, advisories, install hooks, and
the tarball inspection (files scanned, whether the source comparison ran). The
Source column names the tool the number came from, so every figure on screen can
be traced back.
findings = Card([CardHeader("Findings", "<n> kept, <m> dropped as false positives"), findingsTable])
findingsTable = Table([Col("Severity", ...), Col("Finding", ...), Col("File", ...), Col("Disposition", ...)])Show the dropped candidates too, with why. A reader who sees only what survived cannot tell a careful audit from a shallow one, and the dropped rows are the evidence that you checked rather than repeated. If nothing was found at all, say so in one row rather than hiding the table.
gaps = Card([CardHeader("What I could not check", "..."), TextContent(...)])Include this only when there are limitations, and never omit it when there are.
A gap that is not on screen may as well not have been reported. Remember to leave
gaps out of root too when you leave it out here.
When several packages were audited, lead with one Table of package, version and
verdict, then a Card per package underneath in the same shape as above.
Keep the prose outside the block short. The card is the report; the text around it should not repeat it -- one line naming the verdict is enough, since the callout already carries it.
Only registered components render; anything else silently produces nothing. The ones worth knowing here:
| Purpose | Components |
| Layout | Stack, Separator, Tabs + TabItem, Accordion + AccordionItem, Steps + StepsItem |
| Content | Card, CardHeader, TextContent, MarkDownRenderer, CodeBlock, Callout, TextCallout, Tag + TagBlock |
| Data | Table + Col, ListBlock + ListItem |
| Charts | BarChart, HorizontalBarChart, LineChart, AreaChart, PieChart, RadialChart (each with Series) |
Note the exact spelling of MarkDownRenderer. The table above is what the shipped
renderer actually registers; List, Progress and Link are not among them, so
use ListBlock/ListItem and put links inside markdown text instead.
If you are unsure whether a component exists, use MarkDownRenderer rather than
guessing. An unregistered name is dropped silently, and a report that renders as
nothing is worse than a plain one.
Prefer the plain ones. Reach for a chart only when there is genuinely a distribution worth seeing, which for a single package there is not.
Auditing several packages at once
A reviewer adding a dependency is usually adding four. When you are handed more than
one, or handed a package.json to go through, fan out: one subagent per package,
each given the package name, the version, and the instruction to run the procedure
above and report its findings back.
The audits are genuinely independent -- what you learn about one package changes nothing about another -- so running them in sequence makes the reviewer wait on the slowest tarball for no reason at all. It also keeps each audit's raw tool output in its own context instead of piling every packument into yours.
Merge what comes back into one report ranked by severity, and give a verdict per package. Do not collapse them into a single answer for the set: admitting four and refusing the fifth is the normal result, and a reviewer needs to know which is which. If one subagent could not complete -- a tarball that would not download, a repository that could not be read -- say so for that package rather than letting a gap disappear into a summary.
Admitting the package
Only after the user has seen the verdict, and only if they ask for it, add the
dependency through the GitHub tools: a branch, a change to package.json, and a
pull request whose body is your audit report.
This write pauses for approval and that is deliberate. Before it does, show the
user exactly what you intend: the branch name, the precise package.json change,
and the PR title. When the pause comes, they should be confirming something they
have already read, not discovering it.
If they deny it, do not look for another route to the same effect. Denial is an answer. Acknowledge it and stop.
Rules
- Never report a finding you have not read the evidence for.
- Never execute the package under audit. Unpack and read; that is all.
- Quote install-script commands verbatim. Never paraphrase them.
- Say "I could not check X" rather than implying you did. If the repository has no matching tag, the tarball-versus-source diff did not happen — report that as a gap, not as a clean result.
- Distinguish what you observed from what you infer. "This package runs a postinstall script that pipes a remote URL into bash" is an observation. "This package is malware" is an inference, and you should say which one you are making.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

