{"slug": "a-scanner-flagged-our-supply-chain-package-it-was-right", "title": "A Scanner Flagged Our Supply-Chain Package. It Was Right.", "summary": "Socket, a supply-chain security scanner, flagged the npm package ai2rules-harness with a Supply Chain Security score of 64%, prompting its developer to discover that the package's postinstall script fetched a binary from the internet and executed it, a pattern indistinguishable from a supply-chain attack. The developer fixed the issue by shipping the binary as per-platform optional dependencies with no install scripts, moving publishing to CI with signed attestation, and rescanned to find the score only rose to 69%, with remaining alerts being 'Unpopular package' and 'URL strings' pointing to its own source repository.", "body_md": "# A Scanner Flagged Our Supply-Chain Package. It Was Right.\n\nWe publish a tool that governs what AI coding agents are allowed to do on your\nmachine. Shortly after it went on npm, [Socket](https://socket.dev) scored it:\n\n**Supply Chain Security: 64%.**\n\nThe instinct is to argue. We know what our code does. The scanner doesn’t.\n\nThe instinct is wrong. Here is what installing our package actually did:\n\n- run a\n`postinstall`\n\nscript `fetch()`\n\na binary from the internet- unpack it by shelling out to\n`tar`\n\n`chmod 0o755`\n\nit- leave it sitting there, executable\n\nRead that as a stranger. That is not *similar to* a supply-chain attack; it is a\nstep-by-step description of one. No scanner can tell our download from a hostile\none, and a scanner that tried to would be worse at its job, not better.\n\n## The part that was worse than the score\n\nThe score is a number. The thing underneath it was a real weakness, and we’d have kept missing it if we’d spent the afternoon disputing the number instead.\n\nWe verified the download. `install.js`\n\nfetched a `.sha256`\n\nalongside the binary and\nrefused to install on a mismatch. That felt responsible.\n\nIt fetched the checksum **from the same host, at the same moment, as the artifact\nit was verifying.** Anyone able to replace the binary could replace the digest\nsitting next to it. What that check actually proves is that the file arrived\nwithout being corrupted in transit.\n\nThat’s **integrity**. It is not **provenance**. And the difference between those two\nis a distinction this project spends its entire existence drawing everywhere else —\nwe’d written thousands of words on why a claim needs to be traceable to who made it,\nthen shipped an install path that couldn’t tell you who made the binary.\n\n## What we changed\n\nThe fix isn’t a better checksum. It’s not having the problem.\n\nThe binary now ships as a **per-platform package** — `ai2rules-harness-linux-x64`\n\n,\n`-darwin-arm64`\n\n, and so on — listed in `optionalDependencies`\n\n. npm resolves exactly\none by `os`\n\n/`cpu`\n\nand skips the rest. The main package has **no scripts block at\nall.**\n\n``` bash\n$ npm install ai2rules-harness\nadded 2 packages in 852ms\n```\n\nTwo packages. Under a second. **Nothing ran.** No network request, no shell, no\n`chmod`\n\n, no script of ours executing on your machine at install time.\n\nThree things follow, and only one of them is about the score:\n\n**The binary is covered by the integrity hash npm writes into** Not a digest we fetched — one your package manager recorded and will check forever. That’s the provenance we didn’t have.*your*lockfile.**Publishing moved into CI with signed attestation**, so each tarball is bound to the workflow and commit that produced it.** Installs became reproducible, offline-cacheable, and usable behind a corporate proxy or a mirrored registry**— none of which was true when installing meant reaching out to GitHub.\n\nThat third one is the giveaway that this was a real improvement rather than a\ncosmetic one. Nobody’s install got *worse* to make a scanner happier.\n\n## The anticlimax\n\nWe rescanned. **64 → 69.**\n\nFive points. For deleting the entire install-time attack surface.\n\nThe temptation here is to go hunting for the remaining thirty-one, and that temptation is exactly what we spend our time telling other people not to indulge. So we read the alert list instead. There are two:\n\n**“Unpopular package.”** Zero weekly downloads, two days old.**“URL strings.”** The package contains a URL. The URL is`https://github.com/sv-pro/ai2rules`\n\n— our own source repository.\n\nThat’s the list. Neither is removable. The first is time and adoption; the second would require hiding where our code lives, which is worse on every axis a security scanner is supposed to care about.\n\n**The absences are the actual result.** No install-script alert. No network alert.\nNo shell alert. No filesystem alert. Every signal the restructure targeted is\ngone. What holds the number at 69 is a popularity metric and a link to our own\nsource.\n\nSocket’s docs explain the rest: the final score is raised to a power scaled by the size and popularity of a project, compressing penalties for large, established packages. That scaling helps npm’s giants and does nothing for something published on Tuesday. A two-day-old package with no users being treated as unknown is a scanner behaving correctly, and we’re the wrong people to complain about a system that declines to assume good faith on no evidence.\n\n## Three things worth stealing\n\n**Take the flag seriously before you take it personally.** The number was a summary\nof behaviour we had chosen. Arguing with it would have preserved the behaviour and\nthe weakness underneath, which the number never mentioned.\n\n**“We verify the download” deserves one more question: verify against what, fetched\nfrom where?** A checksum served from the host serving the artifact is a\ntransit-corruption check wearing a security check’s clothes. Ours had been sitting\nthere looking reassuring for two releases.\n\n**Removing a capability beats documenting it.** We could have written a paragraph\nexplaining that our `postinstall`\n\nwas trustworthy. Every compromised package in\nhistory could have written that paragraph. The install script is gone instead,\nwhich is checkable by anyone in about four seconds:\n\n```\nnpm view ai2rules-harness scripts\n# undefined\n```\n\nThat’s a claim you can verify without believing anything we say — which is, roughly, the whole point of the project the package belongs to.\n\n[ ai2rules-harness](https://www.npmjs.com/package/ai2rules-harness) —\n\n`npm install -g ai2rules-harness && harness init`\n\n. The packaging reasoning,\nincluding the four alternatives we rejected, is in\n[D58.](https://github.com/sv-pro/ai2rules/blob/main/DECISIONS.md)\n\n`DECISIONS.md`", "url": "https://wpnews.pro/news/a-scanner-flagged-our-supply-chain-package-it-was-right", "canonical_source": "https://ai2rules.dev/blog/a-scanner-flagged-our-security-package/", "published_at": "2026-08-12 00:00:00+00:00", "updated_at": "2026-08-15 07:42:31.332228+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "ai-safety", "developer-tools"], "entities": ["Socket", "ai2rules-harness", "npm", "GitHub", "sv-pro"], "alternates": {"html": "https://wpnews.pro/news/a-scanner-flagged-our-supply-chain-package-it-was-right", "markdown": "https://wpnews.pro/news/a-scanner-flagged-our-supply-chain-package-it-was-right.md", "text": "https://wpnews.pro/news/a-scanner-flagged-our-supply-chain-package-it-was-right.txt", "jsonld": "https://wpnews.pro/news/a-scanner-flagged-our-supply-chain-package-it-was-right.jsonld"}}