Secure Coding with AI - Supply Chain Protection for Node Projects and Guardrails for Agents
Practical security defaults for npm and pnpm so AI-assisted coding does not turn into a supply chain risk.
Short version: If you let AI or agents write code and install dependencies, you need safety rails. This guide gives you practical defaults, sensible workflows, and a least-privilege mindset for running server-side code.
Why this matters right now
In the JavaScript ecosystem, new packages and updates ship every day. That is great for innovation, but it also opens the door to supply chain attacks.
Recent example: compromised npm versions around the Axios ecosystem (malicious versions / remote access trojan). Background and analysis:
Typical attack paths:
- Maintainer account compromise
- Dependency tree / transitive dependencies
- Typosquatting (a package name that looks almost correct)
- A malicious update that initially looks normal
Goal: Even if something goes wrong, the blast radius should stay small.
1) 60 seconds: what npm actually is
- npm is both a package manager and a registry for Node and JavaScript.
- You install packages with
npm install <package>. - Dependencies are declared in
package.json. - Installed packages land in
node_modules. - A lockfile (
package-lock.json,pnpm-lock.yaml) pins exact versions.
Important: You are pulling in code from the outside world. That is the core supply chain risk.
2) The biggest risks during install
2.1 Malicious updates or new packages
- New versions can be compromised and are often only discovered later.
2.2 Lifecycle scripts during install
Packages can automatically run scripts during install:
preinstallinstallpostinstall
There can be valid reasons for this, such as native binaries or build steps, but it is also an attack surface.
3) The 2 most important immediate measures
3.1 Release cooldown: do not trust brand-new releases
Use a cooldown window, for example 7 to 14 days.
Important: This option currently belongs to pnpm, not the npm CLI.
pnpm-workspace.yaml:
minimumReleaseAge: 20160
20160 minutes equals 14 days.
Why this helps: If a compromised package was just published, you do not pick it up immediately.
Tip: For production systems, 7 to 14 days is often a good starting point. For very sensitive projects, consider 30 days.
3.2 Block install scripts
.npmrc:
ignore-scripts=true
What happens then? Dependencies do not run preinstall or postinstall scripts. That lowers risk, but some packages depend on those steps and may break.
4) Why postinstall can still be “necessary”
Some packages need install-time steps for:
- Downloading or setting up native binaries
- Compilation
- Platform-specific checks
If you enable ignore-scripts=true, this can cause errors.
A pragmatic approach:
- Default: scripts off
- Exception: temporarily allow scripts if you truly need that package
- Then switch back to scripts off
Important: never leave scripts globally enabled forever “just so everything works” if your actual goal is security.
5) AI and agents: the most important policy
When AI or agents work in your repo, the risk is not “AI is evil”. The real issue is: AI is fast.
5.1 Golden rule
Agents must not install dependencies without approval.
5.2 Minimal policy
If the AI wants to install a package, it must provide:
- Why is the package needed? (specific use case)
- Alternatives (native solution? already available in the project?)
- Risk check (repo or owner, downloads, maintenance, recent releases)
- Minimal scope (smallest possible addition, no unnecessary tool sprawl)
- Test plan (how do we verify nothing is broken or malicious?)
5.3 Recommended workflow
- AI only proposes commands
- You approve installs or updates
- Then run tests, lint, and build
6) Reproducible installs: lockfile plus CI
This is not flashy, but it is extremely effective.
6.1 Commit the lockfile
package-lock.jsonorpnpm-lock.yamlbelongs in the repo.
6.2 Strict CI installs
- npm:
npm ci - pnpm:
pnpm install --frozen-lockfile
Why: No surprise version jumps in CI or production builds.
7) Audits: useful, but do not trust them blindly
npm auditis a good starting point.- But not every audit finding is a real risk in your context.
Pragmatic approach:
- Block at least high/critical in CI, or warn and require manual review.
8) pnpm and Yarn: short comparison
If you are already considering pnpm:
- pnpm has strong supply chain features and is popular with many teams.
- Important highlight: pnpm can control build and lifecycle scripts via an allowlist, instead of using a strict all-or-nothing switch.
8.1 pnpm: allowlist for build scripts
pnpm v10 leans heavily into the idea that dependencies should not be allowed to run arbitrary install scripts automatically. Instead, you can explicitly allow which packages are permitted to run build scripts.
In practice, this looks like an allowlist or approval flow such as:
onlyBuiltDependencies(only these dependencies may run build scripts)- or a workflow using
pnpm approve-builds(interactively approve what is allowed to build)
Takeaway for a video or course: pnpm gives you a middle ground between “scripts completely off” and “scripts always on”.
9) Runtime security: least privilege for Node, Bun, and server code
Even if a malicious package gets in, you still want to prevent it from reading or writing everything.
9.1 Core principles
- Do not run as root
- Minimize filesystem access (only project paths or uploads, not your whole home directory)
- Isolate secrets (do not spread env vars everywhere)
- Minimize network access (only required hosts and ports)
9.2 Most practical option: containers
- Run the process as non-root
- Optionally use a read-only filesystem
- Only allow write access to defined volumes
- Drop capabilities
Advantage: easy to explain and very teachable.
9.3 Alternative: systemd sandboxing on Linux
- Run the service as a dedicated user
ProtectSystem=strictReadWritePaths=only for required pathsNoNewPrivileges=true
9.4 Alternative for strict runtime permissions: Deno
Deno has an explicit permission system for read, network, and environment access. That makes it easy to demonstrate:
- “Without
--allow-read, the process cannot access the filesystem.”
9.5 Bun
Bun is Node-compatible, but does not provide a comparable permission model like Deno. → In practice, you isolate it through containers, systemd, or the OS.
10) Copy/paste: secure project defaults
10.1 pnpm-workspace.yaml for release cooldown
# Cooldown for brand-new releases
minimumReleaseAge: 20160
10.2 .npmrc for install scripts
# Do not run lifecycle scripts from dependencies
ignore-scripts=true
10.3 Short team rule
- No installs without approval
- Always include: purpose, alternatives, scope, and a test plan
11) Frequently asked questions
”Doesn’t ignore-scripts break a lot of things?”
Yes, some packages depend on it. That is exactly why it is a security default. If you need scripts:
- allow them temporarily
- keep the exception minimal and deliberate
”What about vibe coding?”
Vibe coding is fine, but package installation is a security boundary. That is where you need an extra rule.
12) Next steps
- Add
.npmrcdefaults to your projects - Add the agent policy to your prompts or rules
- Use containers or systemd sandboxing for server runtimes
If you want, I can also publish:
- a small “AI coding security checklist” as a PDF
- a copy-paste template for agent rules
- a mini repo with a demo setup (cooldown + ignore-scripts + CI)