Skip to main content
Lightning Jar - Web Studio Lightning Jar Wordmark

Your Next Breach Won't Come Through the Contact Form

A watercolor illustration of a mailbox with its flag raised

For twenty years, "website security" meant guarding the front door. Sanitize the form inputs, escape the output, lock down the queries, set the security headers. That work still matters, and nothing in this post excuses you from it. But while everyone was watching the front door, the economics of attacking websites changed, and the attackers moved somewhere most site owners never look: upstream, into the packages, the accounts that publish them, and the machines that build your site.

The reason is arithmetic. Breaking one website through its contact form yields one website. Compromising one npm maintainer account yields every site that installs that maintainer's package, automatically, the next time anyone runs an install. Attackers go where one effort pays a thousand times over, and that arithmetic now lives in the supply chain.

What that looks like in practice

Last week's incident in the keyv and cacheable npm namespaces is a clean specimen, documented by Socket's research team. One compromised maintainer account shipped malicious releases of about ten popular packages. The malicious code ran at install time through a lifecycle hook, before the developer had executed a single line of their own program. It harvested cloud credentials, CI tokens, and npm tokens, then used the stolen npm tokens to republish other packages with the same hook. The attack propagated itself.

Two details deserve your attention even if you skip the rest. First, the payload planted persistence hooks in the configuration directories of code editors and AI coding assistants, which means the attack surface now includes the tools your developers think with. Second, the remediation was order-sensitive: rotating your credentials before removing the attacker's monitoring tipped them off. This is professional work, aimed at builders rather than sites.

The four layers that matter now

The package layer. A modern website's lockfile lists hundreds of packages, most of which you have never heard of and none of which you review. Each one represents a stranger with the effective ability to run code in your project. The dependency tree is a trust graph, and every node in it is someone's account.

The repository layer. The target is rarely the code; it is the account that publishes the code. Maintainer credentials, publish tokens, and CI workflow permissions are what the keyv attacker actually stole, because they convert one compromise into thousands. GitHub has been hardening exactly this layer: staged publishing, delayed automation, and, most consequentially, npm turning install scripts off by default.

The build layer. Your build is the single most dangerous moment in your infrastructure: it executes thousands of strangers' code with your secrets resolved into the environment. API keys, database credentials, deploy tokens, all present in memory while third-party code runs. A malicious dependency does not need an install hook if it can simply run during the build.

The agent layer. This one is new, and last week made it real rather than theoretical. Developer machines now carry configuration that instructs AI coding agents, and that configuration executes. Malware that plants a hook in an agent's config directory gets its code run by the most trusted process on the machine, one that developers deliberately grant broad permissions. If your team uses AI tooling, its configuration surface is part of your perimeter now.

What we actually do about it

We are an npm publisher, our stack runs on the package manager this attack used as a payload loader, and we build with AI agents daily, so this is not abstract for us. When the Socket report landed, we audited every lockfile in our workspace the same day. We were clean, and we think it is worth being honest about why: partly design, partly luck.

The design part. Our package manager, Bun, does not run install scripts unless a package is explicitly allowlisted, which neutralizes this attack's exact entry point; our allowlists hold between zero and four packages per project, each added deliberately. Our linting toolchain choice happened to exclude the transitive dependencies this attack rode in on. We habitually choose a direct API call over an SDK, which keeps the trust graph small. Our secrets live in a vault and resolve at build time rather than sitting in files. And our lockfiles are committed, so nothing new enters without a person deciding it should.

The luck part. We simply do not use the packages that were compromised. Next time we might. That is why we also added tripwires the same week: a test that fails our builds if any package from a known incident ever appears in a lockfile, a pin on the install-script allowlist so it can only grow deliberately, and a daily audit that alerts on any unexpected change to the agent and editor configuration directories this malware targeted.

The takeaway for site owners

Keep guarding the front door. Then ask your team, or your agency, four questions that did not used to be part of website security: Who can publish the packages we depend on, and what happens on our machines when we install them? What code runs during our builds, and what secrets can it see? How would we know if a dependency turned malicious this week? And if we use AI development tools, who is watching their configuration?

None of this is settled practice yet, and anyone selling you a complete answer is ahead of the evidence. But the direction is not in doubt. The attacks moved upstream because that is where one compromise multiplies, and the defenses have to follow them there.

headshot of Kevin Peckham
Kevin Peckham
Principal, Lightning Jar

Ask Eljay

Ask about the studio's work, research, packages, or writing. A few starters:

Answers come from this site's own content and link their sources. For anything that matters, email hello@lightningjar.com.