I spend my working days talking to development teams about shifting security left: catching problems in the IDE or pipeline, rather than in production. I was aware my own development process didn't really practise what I preach; I just pull packages knowing that increases risk and exposure. That kind of works when you're in control but when using Agents the build velocity increases by an order of magnitude; in turn, so does the dependency footprint, and I hadn't given supply-chain security any real thought.
The threat is well documented: typosquatting, dependency confusion, and outright compromised packages on public registries. A freshly published version of a popular library can be malicious for a few hours before anyone notices. Pulling packages straight from PyPI the moment they land is the risk I wanted to reduce.
Weighing the options
I used the tree of thought skill from my claude-toolkit to explore the repository approaches before committing to one. It suited the problem: an open question with several viable paths and no obvious winner. The branches were a transparent proxy with a delay, an AWS CodeArtifact quarantine gate that only publishes packages older than a threshold, and Google's Assured OSS as a curated, vetted mirror.
Assured OSS looked like the cleanest fit on paper. Google builds the packages, provides SLSA provenance, scans and signs them, and serves them from a secured Artifact Registry. Essentially the quarantine philosophy without having to build the delay logic myself, and the free tier covers over 1,600 Python packages including most of the heavy hitters.
Why Assured OSS didn't stick
It didn't survive contact with reality because the curated catalogue lagged behind upstream releases, so the versions Assured OSS served were often old; and old versions meant missing security patches. I was solving for "don't pull a freshly compromised package" but introducing "you're stuck on a release with a known CVE that's already fixed upstream". That's the wrong trade. For a curated security mirror to introduce vulnerabilities through staleness rather than prevent them was a bit of an irony.
So I switched approach for Python. Rather than a curated mirror, I now use uv's exclude-newer set to a rolling seven-day window. uv simply won't resolve any package version published in the last seven days. That gives the cooldown I actually wanted: enough time for a malicious or broken release to be flagged and pulled before it can land in my lockfile, while still tracking upstream properly once the window passes. No stale catalogue, no separate mirror to maintain, just a resolver constraint.
The audit layer
The cooldown reduces the chance of pulling something bad and auditing catches the things that are already known to be bad. The process now runs two dependency audits: uv's audit over the resolved lockfile, and pip-audit as a second pass against the advisory databases. Running both locally as a pre-commit step and again in CI means a vulnerable dependency gets flagged before it reaches a pull request, not after.
This is the part that maps most directly to the shift-left conversation I have at work: the cost of fixing a flagged dependency at commit/PR time is trivial compared to finding it in a deployed environment.
Wiz at the gate
The final layer is Wiz, running as a pre-PR scan. Where the audits focus on known dependency vulnerabilities, Wiz scans the wider picture: infrastructure-as-code misconfigurations, data, secrets, and code issues (e.g. SAST) across the repo. It sits as a gate before the PR opens, in the same spirit as the code-reviewer and test-generator agents in the toolkit. Given the day job, not using Wiz on my own projects would have been a slightly awkward omission.
Between the three layers there's a reasonable defence in depth: the cooldown reduces exposure to fresh attacks, the audits catch known vulnerabilities, and Wiz covers configuration and secrets.
Side note on Wiz scanning: if this was a customer I'd suggest they use the IDE plugin and WizCLI (and MCP) to harden the commit gate too. Given I do a chunk of this work on my own laptop I simply haven't set all that up locally - another one for the future :D
Reflection
This is a decent start but it's only half the job. Everything here addresses Python; I've done nothing for npm, container base images, or any other ecosystem my projects touch. The uv cooldown is Python-specific by definition.
What I really want, at some point, is a proper artifact repository sitting in front of every package source: one place that proxies, caches, and applies policy regardless of ecosystem, rather than a per-language patchwork. That's a bigger piece of work than a resolver flag and a couple of audit steps, so it's on the list rather than in the process. For now, the Python path is meaningfully more defensible than it was, and the framework is in place to evolve.
The security tooling lives across the claude-toolkit and the project repos that consume it.
Comments 0
Log in or register to comment.