Published: |
Supply chain security in coding agents
Autonomous coding agents can download third-party dependencies, run installations, and inspect around your system with almost no human supervision. Malicious actors are now utilising this convenience to accelerate their attacks and campaigns.
If you don’t take a layered defence approach to security, your coding agent might be downloading compromised malware to your system, secretly reporting credentials to a remote server, or installing backdoors for dead-man’s switch.
On the package registry side, we are seeing an explosion of malicious pacakges being published to public registries. Sonatype, a security company, logged 464,650 malicious open source packages in just the Q2 of 2026. Out of them, the NPM registry accounts for 96.6% of the numbers. That means, as of today, ~3 malicious packages are being logged on NPM per minute!
Attackers are leveraging the power of AI to expand the speed, coverage, and strategies. Here are few examples where supply-chain attacks have evolved when comparing to few years ago:
- Typosquatting or Slopsquatting: attackers survey common LLM model hallucination patterns, register hallucinated package names on the supply-chain, and waits for agents to install them. The agent didn’t choose a bad package, it invented a package that the attack then supplied.
- Multi-Ecosystems: attackers use agents to generate malicious libraries in bulk and in multiple programming languages, use software factories to publish and maintain across several registries. Before AI, this would have taken a significant amount of man power, but today it can be entirely automated.
- Greyware: Code you cannot cleanly label as malware, but cannot be trusted either. Examples: troll packages that dump ASCII arts into logs, libraries that are either abandoned by maintainers or published from newly registered users, low quality code that contain poorly optimised code or undectected memory leaks. Greyware are on the rise because human - both authors and users - have stopped reviewing code.
- Social Engineering: attackers don’t just target the code, they are actively targeting the humans too. By using different agents with personas, they can pretend to be legitimate actors who’s trying to raise issues, submit pull requests, or ask for helps in emails. Maintainers are flooded with requests, and it’s getting impossible to separate authentic users or bots. One mistake, they could accidentally grant permissions to an attacker, and compromise the ecosystem. The blame should never be on the maintainers, but on the lack of support and solutions from the ecosystem and platforms.
In this blog, I’ll be using the coding agents and the npm registry as examples, but the same
defensive principles can be applied to other types of agentic architectures and package registries
too (e.g., pypi, crates, rubygems, etc).
The recommended tools below are mostly free and open-source. Once you understand the framework, you’ll then free to either build your own or evalaute other premium solutions.
The Model Layer
Starting with the model, they are the brain of the AI, improving this core component will reduce chances of hallucinations or executing dangerous commands. Or, for security sensitive operations, it is worth considering using “Cyber-enhanced” models such as “GPT-5.6 Cyber”.
Cyber models have additional capabilities of autonomous vulnerability discovery, automated patching, and can even chain multiple vulnerabilities and bugs together to form an end-to-end report. Cyber models can also be split into “Red Team” or “Blue Team” variants, where the model acts as either the attacker or the defender. Few well-knwon cyber models that are available today are:
- GPT-5.6 Cyber, Daybreak Blue, Daybreak Red
- Gemini 3.8 Cyber, Fairwind Program
- Claude Mythos 5 or 5.1
- GLM-5.3
The Harness Layer
Coding agents consists of the model and the harness. The harness controls how the model interacts with tools, files, and the network. Therefore, we need to build safer defaults and guardrails into the harness too. Here, I’ve split the harness into three distinct stages:
- Best practices
- Planning
- Installation
Harness: Best Practices
For an agent, best practices means sensible environment defaults that you should enable for the agent’s runtime. For example, the package manager in the NPM ecosystem should:
- ignore lifecycle scripts
- enable dependency cooldowns
- generate provenance statements
- more…
You can define them in a global or project based .npmrc:
ignore-scripts = truemin-release-age = 3 # daysprovenance = trueYou should enable or tweak other configurations to protect against more low-hanging attack vectors. For a more extensive guide on NPM security, visit https://github.com/bodadotsh/npm-security-best-practices or install it as a SKILL:
npx skills add bodadotsh/npm-security-best-practicesHarness: Planning
Best practices help to prevent common malware attack patterns, e.g., the postinstall lifecycle,
but they do not stop the agents from selecting greyware. As I mentioned in the introduction,
greyware are software that are not exactly malware yet but we should still be cautious about using them.
This is where the “Planning” stage comes in. Within this stage, the agent should evalaute dependencies, query relevant health metrics, for example: download history, maintainer activities, code quality, licenses, etc.
There are scorer tools that aggregate these metrics for us, boils them down to a single number, say 0 - 100. From there, we can set a baseline policy - say, nothing below 80 - and if a dependency fails to meet this criteria, we won’t consider using it.
Here are few free and well-known dependency scorers on the market:
- Socket MCP Server
- deps.dev
- OpenSSF Scorecard
Here I’ll use Socket MCP Server as an example. When queried, the Socket MCP Server returns a
depscore tool that returns supply chain, maintainance, vulnerability, and license scores for
packages across different ecosystems.
With Cluade Code, it can be added like so:
claude mcp add --transport http socket-mcp https://mcp.socket.dev/Or for Cursor / most MCP clients:
{ "mcpServers": { "socket-mcp": { "type": "http", "url": "https://mcp.socket.dev/" } }}Here’s an example of an NPM package being scored:
See https://socket.dev/alerts on how Socket breaks down each category
From there, customise your policy, either hard-code it or inside the AGENTS.md:
Before adding or updating any dependencies, call the Socket MCP server on theexact package name and version. If supply chain, quality, or vulnerabilityscores are below 80, do not install it. Propose alternatives.Harness: Installation
The “Planning” stage can be useful in avoiding greyware, but in the case of a popular and well-knwon
package (such as react or express) gets compromised, we need one more safety check right before
the installation.
We should scan the dependency with a real-time intelligence database for known vulnerabilities. Here are few free and trustworthy scanners on the market:
- Socket Firewall CLI
osv-scannerby OSV.dev- Aikido Safe Chain
I will use the Socket Firewall CLI sfw as example. It needs no API key, intercepts the network
fetch, checks against Socket’s real-time threat intelligence, and blocks confirmed malware before the
tarball lands on your system. The sfw CLI also supports other languages such as pip, uv, cargo, etc.
There are many ways we can enable sfw within an agent, for example:
- Wrapper script in
$PATH - Prompt through SKILL or within
AGENTS.md - Enable as agent lifecycle hook
Wrapper script in $PATH
Create a smaller wrapper executable in a directory that comes before the real package manager in your system $PATH (e.g., ~/.local/bin or ~/bin).
- First, install
sfwglobally
npm i -g sfw- Check
sfwis working correctly

- Ensure the directory is at the front of your
$PATH
# Add to ~/.bashrc, ~/.zshrc, or equivalentexport PATH="$HOME/.local/bin:$PATH"- Create wrapper script
#!/usr/bin/env bashexec sfw npm "$@"- Make it executable
chmod +x ~/.local/bin/npm- Within an agent, verify by asking the agent to run the following commands:
which npm: should return our wrapper location, e.g.,~/.local/bin/npminstead of the standard node/npm installation path (e.g.,/usr/local/bin/node/npm)npm install lodahs: should return Socket error, as it is a blocked npm package as reasoned here: https://socket.dev/npm/package/lodahs/alerts/0.0.1-security
Prompt through SKILL or within AGENTS.md
Below is an excerpt from https://github.com/bodadotsh/npm-security-best-practices/blob/main/skills/npm-security/SKILL.md and a similar shorter prompt can be placed inside AGENTS.md:
---name: npm-securitydescription: Prevent JavaScript/TypeScript projects from supply-chain attacks across package managers like npm, pnpm, yarn, bun, and deno. Use whenever planning, installing, updating packages or configuring package managers---
## Install stage / package scanner
When time to install third-party dependencies, we should validate them against a package scanner first. This reduces risk of compromises as the scanner will check against a real-time intelligence database.
A free scanner solution is the Socket Firewall Free cli `sfw`. Can use other package scanners if user configured explicitly.
The `sfw` cli can be downloaded first through `npm i -g sfw` or through `npx`: `npx sfw npm install <package>`
If any package got compromised, as soon as the Socket security updated their database, the `sfw` cli can reject the package installations in real-time even before the malicious tarball reaches the user.
## References
- https://docs.socket.dev/docs/socket-firewall-freeEnable as agent lifecycle hook
Here’s how sfw can be added as a PreToolUse hook:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "$HOME/.claude/hooks/sfw-wrap.py" } ] } ] }}The sfw-wrap.py script can start with the following:
#!/usr/bin/env python3import json, re, sys
payload = json.load(sys.stdin)tool_input = payload.get("tool_input") or {}cmd = tool_input.get("command") or ""
wrapped = re.sub( r"(?<!sfw )(?<![^\s;|&])(npm|pnpm|yarn|pip3?|uv|cargo)\b", r"sfw \1", cmd,)
if wrapped == cmd: sys.exit(0)
json.dump( { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "allow", "permissionDecisionReason": "Prefixed package manager with sfw", "updatedInput": {**tool_input, "command": wrapped}, } }, sys.stdout,)Make it executable:
chmod +x ~/.claude/hooks/sfw-wrap.pyThe script reads the proposed command on stdin. If it looks like a package manager, it
returns updatedInput with sfw prepended. This wrap is Claude Code specific, Cursor has
beforeShellExecution. Codex and OpenCode need their own equivalent.
This hook template have few caveats:
/usr/local/bin/npmandnpxdo not match this regex. Inspect the logs to see if your agent uses them.curlto the registry never goes throughsfw.- If two
PreToolUsehooks both returnupdatedInput, last writer wins. Don’t run two Bash rewriters.
It is recommended to customise this hook to fit into your exact workflows.
Are we relying on external intelligence services like Socket too much? Security has always been difficult. Individuals need to track a constantly changing landscape. AI-assisted attackers have accelerated that pace, making it unrealistic for an individual developer, or even teams, to keep their own threat intelligence up to date.
That is why we use these specialist security companies. This is not unique to supply chain packages: your operating system detects and blocks malware for you, your browser warns you about dangerous sites, and services such as Cloudflare defend websites from bots, abuse, and network attacks. Delegating part of the problem to experts is a normal layer in a defence-in-depth strategy. The trick is always have a backup plan, and the next layer we introduce is for “what if an intelligence platform fails to defend for us?”.
The Sandbox Layer
Even after layers of preventions, when it comes to security, we should always assume the worst case and ask: “what if both the model and harness fails?“. The next defence layer would be the “sandbox”, and the goal is to “minimise the blast radius”.
Sandbox is not a simple topic and it deserves its own series of blog. There’s a famous “lethal trifecta” blog post detailing three key risks:

They could be translated to the following requirements for a coding agent sandbox:
- Ingress sanitisation. The sandbox needs secrets or sensitive data to do its job, but I need to sanitise data going in, so the malware doesn’t see the real values inside the sandbox.
- Isolation. I define whichever workspace is visible to the agent, other places like
~/.sshor unrelated projects should be isolated to it. - Egress control. I can control which outbound network is allowed.
There’s an invisible 4th requirement: “don’t add too much frictions to the developer experience”. In many cases, if the security boundaries are constantly interrupting the flows, many developers won’t use it.
Doesn’t Claude Code/Codex/Cursor etc already provide sandbox features? Unfortunately, the sandboxes built into these coding agents are only partial or opt-in. By default, they fail to meet the “lethal trifecta” requirements. The reason is simple: these tools prioritise friction-less productivities - meaning execute privileged shell commands, gather any local files for debugging purposes, and make network calls for contextualisation. Therefore, securing the agent environment where it prevents the lethal trifecta is left as an exercise for the developer.
Luckily, the Docker Sandbox sbx is a free product on
the market that can prevent the lethal trifecta for us. It runs the agent inside a microVM,
keeps real credentials on the host, and proxies outbound auth.
# store the real key on the hostsbx secret set openai
# start from a restrictive network posture, then allow what you needsbx policy allow network registry.npmjs.orgsbx policy allow network api.openai.comDocker Sandbox also allows the user to spin up their preferred agent:
# create a sandbox called "codex-sbx" with `codex` built-insbx run --name codex-sbx codexsbx stop codex-sbxsbx rm codex-sbx
# or create custom agent sandbox with `kit`, for example `pi`sbx run --kit "git+https://github.com/docker/sbx-kits-contrib.git#dir=pi" piSee Docker Sandbox docs on how to get started.
Defence In Layers

The three layers we’ve covered in this blog is only the starting point in securing against supply-chain attacks, there are still many topics to cover. Moving to a secure-by-default foundation means “think coverage, not perfection”:
- Node and Deno permission systems can restrict filesystem, network, environment-variable, and subprocess access. They are not safe proof but can add another layer of friction to malicious attempts.
- Use AI-assisted code review, and review their suggestions carefully. They are useful when teams face overwhelming amount of pull requests, and reaches the review and understanding bottleneck.
- Pair that review with established application-security controls, such as SCA, SBOM, SAST, DAST to test the running application from an attacker’s perspective.
- Local protections do not help if GitHub Actions or other CI system can install anything and expose long-lived secrets. Pin actions and dependencies, minimise workflow permissions, isolate untrusted pull requests, prefer short-lived identity tokens, protect release jobs, and require review for workflow-file changes. Treat generated build artefacts as untrusted until they have passed policy and security checks.
- Enterprise registry proxies such as Cloudsmith, Sonatype, or JFrog can add another enforcement point. They can cache approved packages, quarantine suspicious versions, enforce licence and vulnerability policies, and prevent builds from reaching public registries directly.
- For production, use a distroless or similarly minimal container image with no shell, package manager, compiler, or other tools the application does not need. Build in a separate stage, copy only the runtime artefacts into the final image, run as a non-root user, and use a read-only filesystem where possible. This does not prevent a vulnerable application from being exploited, but it removes many tools and paths an attacker would otherwise use after gaining execution.
- Social engineering remains an difficult and unanswered problem. How do we separate good and bad actors? How do we avoid maintainer burnouts? How do we build systems where the weakest link is not us? Many of these questions are being explored right now, as the landscape and solutions are changing everyday.
None of these preventions are sufficient on their own. Established security projects such as the MITRE ATLAS or OWASP Gen AI Security are regularly updated and offers more comprehensive guidelines on adversarial techniques beyond this blog post.