Supply chain consumption tips
04 Jun 2026
I was just doing some developer education about how to safely use end-to-end testing SDKs such as Microsoft Playwright.
While, for authN and authZ, I still love my old “E2E and Synthetic Testing Considered Harmful” advice, I’d also like to add on some supply chain compromise tips.
I’m probably not the best at this, but here are some quick wins I think I’ve collected so far about how to import SDKs and other open-source libraries/packages/modules/software/etc. onto your computer / into your codebases, in this Sha1-Hulud / package-registry native worm era.
Intro
While many SDKs are widely respected, their authors are not infallible or immune to phishing (nor are the authors of the SDKs upon which those widely respected SDKs, in turn, “transitively” depend – which could compromise the contents of various versions of widely-respected SDKs through a “supply chain worm” in the style of Sha1-Hulud).
Patching codebases to use the latest and greatest version numbers of widely-respected SDKs is generally desirable, because new releases typically offer security patches and improved features.
However, any given brand new version number for an SDK could, despite its authors’ best efforts, theoretically have been compromised and also contain malware.
Following tips below can, hopefully, reduce the probability of developer laptops, developer VDIs, enterprise servers, or enterprise CI/CD runtimes becoming infected by such malware.
Bundle separation
If the programming language that a web developer is using offers the ability to import “during development only” SDKs separately from “necessary to make the web application run” SDKs, then SDKs should be imported using the “during development only” mechanism.
- For example, end-to-end testing libraries such as Playwright should be part of
devDependencies, notdependencies, in thepackage.jsonfile for a Node.js JavaScript codebase.
Exact version number pinning
Web developers should specify the exact intended version number (e.g. 1.2.3) of each SDK that they currently intend their source code to use, rather than specifying a range (e.g. 1.2.x or latest) of acceptable version numbers.
Many programming languages offer built-in helper tooling.
- For example, Node.js JavaScript’s
npm installandnpm updatecommands offer a--save-exactoption, both:- Inline, and also
- Through
.npmrcconfiguration files that can be checked into version control so that no developer ever forgets to use the option inline.
Important: Don’t forget that you probably have “auxiliary” dependencies (e.g. 3rd-party steps like actions/checkout that are found in your CI/CD pipeline’s scripting) that also need to start getting exact-version-number-pinned.
Frequent version number patching
The specific versions of the SDKs used within a given codebase should be kept up-to-date and patched.
- Automatically scanning source code with a static application security testing (“SAST”) or software composition analysis (“SCA”) tool, upon each check-in into version control, is an excellent way for web developers to discover when an SDK version has fallen out-of-date.
- Many programming languages also offer built-in tooling.
- For example, Node.js JavaScript offers an
npm auditcommand.
- For example, Node.js JavaScript offers an
Version number age cooldown
While it is crucial to keep SDK versions up-to-date, keeping them too up-to-date can be a problem in the era of widespread SDK supply chain compromises.
If their programming language allows it, web developers should strongly consider setting a “package cooldown” / “minimum release age” when patching SDK version numbers within their codebase.
As of mid-2026, recent “supply chain” compromises seem to be ending up removed from major package registries within a few hours to a few days, so while industry consensus might further change, a cool-off period of 7 days seems to be the generally recommended window before treating a recently-released SDK version number as “safe enough” to try installing.
Many programming languages offer built-in helper tooling.
- For example, Node.js JavaScript’s
npm installandnpm updatecommands offer a--min-release-age=SOME_NUMBERoption, both:- Inline, and also
- Through
.npmrcconfiguration files that can be checked into version control so that no developer ever forgets to use the option inline.
If your programming language doesn’t include package cooldown, please help by chiming in and complaining until it does:
Version number lockfiles
If their programming language allows it, web developers should generate, and check into source code version control history, “lock files” that specify exact version numbers not only for the web developers’ intended SDKs, but also exact version numbers for all of the “transitive dependencies” that those SDKs in turn depended upon, at the exact time that the developer generated such a “lock file.”
- For example, check Node.js JavaScript’s
package-lock.jsonfile into source code version control.
Installation from lockfiles
If their programming language allows it, web developers should almost always install SDKs onto a machine by using a mechanism that installs them from the above “lock file.”
For example, as seen in OWASP’s NPM security cheat sheet, use Node.js JavaScript’s npm ci command, rather than the npm install (a.k.a. npm i) command.
This is particularly important in the following contexts:
- The automation scripts comprising a CI/CD pipeline (always).
- After downloading a codebase from version control onto a developer laptop or VDI.
The only exception is when a web developer, on their laptop or VDI, is explicitly in the process of SDK version maintenance as mentioned in “frequent version number patching” above.
- For example, this is the only time when it would be appropriate for a Node.js JavaScript developer to run
npm installornpm update.- Even then, the developer would still want to leverage those commands’
--save-exactand--min-release-ageoptions. - The developer should also remember to check any successfully updated
package.jsonandpackage-lock.jsonfiles back into source code version control.
- Even then, the developer would still want to leverage those commands’
SBOMs
Perhaps especially if your programming language doesn’t offer lockfiles (but arguably even if it does, just to get various programming languages’ lockfiles standardized into a single machine-readable format), consider also generating, and storing somewhere organized, a software bill of materials (“SBOM”), for each version-control commit of your codebase.
Be sure to include both:
- Your codebase’s “primary” SDK dependencies (for example, the ones in
package.jsonandpackage-lock.jsonif you are a Node.js JavaScript developer), and - Any “auxiliary” dependencies (e.g. 3rd-party steps like
actions/checkoutthat are found in your CI/CD pipeline’s scripting).
It probably doesn’t help you, the developer, much.
But when central IT comes knocking, asking if you got hit by the latest worm, being able to send them all SBOMs from all commits during that timeframe can probably help ease their worries.
Installation without auxiliary scripts
If their programming language allows it, web developers should almost always install SDKs onto a machine using a mechanism that prevents the SDK installation process from running arbitrary scripts on that machine.
This is because execution of arbitrary scripts during SDK installation has been a major compromise vector in recent supply chain compromises such as Sha1-Hulud.
- For example, the Node.js JavaScript’s
npm ci,npm install, andnpm updatecommands all offer an--ignore-scriptsoption, both:- Inline, and also
- Through
.npmrcconfiguration files that can be checked into version control so that no developer ever forgets to use the option inline.
Clutter management
Executable binaries clutter
Specifically for build-time / test-time SDKs such as Playwright, clutter might accumulate that is undesirable.
The most common machine type in which an SDK such as Playwright would be installed and executed is a CI/CD pipeline. In the case of the built-in CI/CD runtimes that come with major cloud-based Git version control hosts, these machines are ephemeral and self-destruct the moment a CI/CD pipeline finishes executing, so no extra cleanup would be needed.
However, when a clutter-producing SDK such as Playwright is installed onto a long-lived machine such as a developer laptop, developer VDI, or enterprise-hosted CI/CD runtime, the following clutter can build up that would be wise (because, really, who wants extra .exe files sitting around ready for malware to latch onto and use against you?) to periodically delete altogether:
- Logs, screenshots, and recordings from past runs of the automated test suite.
- Copies of automation-friendly (“headless”) versions of web browsers that were needed by older test automation code, but that are no longer relevant.
- For example, the Playwright SDK offers a
npx playwright uninstall --all**command that cleans out copies of old web browser executables that previous Playwright runs had put into a Windows machine’s%LOCALAPPDATA%\ms-playwrightfolder (or a Linux machine’s~/.cache/ms-playwrightfolder).
- For example, the Playwright SDK offers a
Authentication clutter
Maybe run some CLI commands on your computer like aws logout or az logout, or something, before running any third-party SDKs (which, if you have VSCode extensions auto-updating, includes “before opening VSCode”).
That way, if you get infected, maybe you’ll notice that the malware is trying to query AWS for secrets and whatnot from your machine when it prompts you to log your CLI in or something.
OWASP’s GitHub Actions cheat sheet has a similar trick: explicitly set every single GitHub Actions YAML file you write to have permissions: {} (no permissions), rather than just leaving the permissions property out altogether, and see if it breaks, and then rebuild the permissions block’s values permission by permission (e.g. adding back content: "read" manually if your YAML file needs to run an actions/checkout step).
Burner compute
Tomas Listiak’s “How to safely approach a JavaScript interview test project” takes “reduce clutter” to the extreme and focuses a lot on following the sort of “burner phone” approaches to running strange code that I’ve seen security researchers adopt for years (I get the impression they’re constantly reimaging their laptops and possibly even changing out hard drives altogether).
- For individuals, I wonder if a brand new account on GitHub.com and using a few hours of free included GitHub Codespaces time as your “burner laptop/VDI” could be a quick-and-dirty version of Tomas’s tips.
- For enterprises, I think things get tricky and expensive fast, and I don’t have great answers, so if you’re an enterprise that doesn’t already have hyper-ephemeral, hyper-constrained developer “workstations,” I can’t say I’d run out and focus on that. I’d probably focus more on enterprise-wide developer education and governance about the tips above, for now.
Links
- This quote from Fernando Piñero Estrada is great:
The cloud industry has spent more than a decade optimizing for developer velocity. We made dependency installation fast. We made CI/CD pipelines automatic. We made SaaS build platforms beautifully simple. We taught ourselves to trust registries because the alternative was slow, manual, and socially unpopular.
Mini Shai-Hulud is not the end of that model. It is the invoice.
The convenience of
npm installis not free. It is a line of credit against your security posture, and the interest rate just went up.This does not mean we should retreat into caves and compile everything by candlelight, although some incident response teams have looked into it. It means we need to stop treating dependency installation as a harmless clerical step. It is code execution. It happens early. It happens often. It happens in places where secrets live.
- A lot of this post is based on tricks I learned from Phoenix Security’s post-Sha1-Hulud article.
- If you’re interested in bigger-picture philosophical questions about where package management should be heading, Andrew Nesbitt’s blog pretty much exclusively talks about that.