One API key sitting in Brevo's source code put malware in front of visitors to as many as 100,000 websites. Nobody touched those sites. Nobody touched Brevo's servers either.
The key had full account permissions and it was long-lived. With it, attackers created a Cloudflare Worker that rewrote responses at the CDN edge, so the malicious content was injected between Brevo's servers and the browser. Origin files stayed clean, and standard integrity checks saw nothing.
This is a walkthrough of what happened, why every server-side defense missed it, and the single line of root cause that a code reviewer reading for secrets flags the day it lands.
Why this is on a code-review company's blog: Brevo's own post-mortem names the cause as a Cloudflare API key hardcoded in application source code. That is one commit. CodeAnt AI reads every pull request for exactly that, and its secret scanning blocks a full-permission key before it merges, which is before any Worker can exist.
What Happened in the Brevo Supply Chain Attack?
The sequence, from Brevo's post-mortem and Sansec's forensic analysis, is short and precise.
On Monday, September 14, 2026, at around 16:05 UTC, a new Cloudflare Worker appeared on Brevo's account. For roughly four hours, until about 20:13 to 20:30 UTC, that Worker rewrote web responses at Cloudflare's edge. Visitors to affected properties were shown a fake "Cloudflare, verify you are human" page, a technique known as ClickFix, which then instructed them to run a command on their own machine.
The affected surface was not just Brevo's own sites. The Worker modified the Brevo forms script, the Brevo Conversations chat widget, and the Brevo SDK loader, all of which Brevo customers embed directly on their own websites.
That is how one Worker reached up to 100,000 sites. WordPress visitors who were logged in as an administrator got worse. The injected script checked for admin status and attempted to install a malicious plugin called "Web Media Optimizer," which functions as a persistent backdoor.
Item | Detail |
|---|---|
Date of attack | September 14, 2026 |
Exposure window | Roughly 16:05 to 20:13 UTC, about four hours |
Root cause | Long-lived Cloudflare API key with full account permissions, hardcoded in application source code |
Mechanism | Attacker-created Cloudflare Worker rewriting responses at the CDN edge |
Reach | Up to 100,000 websites embedding Brevo scripts |
Payload | ClickFix fake-verification lure, plus a WordPress admin backdoor plugin |
Servers touched | None. Origin files and infrastructure unmodified |
Key possibly compromised | As early as late August 2026 |
The Root Cause Was One Line of Source Code
Strip away the CDN mechanics and the origin of the whole incident is a single sentence in Brevo's write-up. A long-lived Cloudflare API key with full account permissions was stored in application source code.
Every property of that key is a separate failure, and they compound. It was hardcoded. The key lived in source rather than in a secret manager or an injected environment variable, which means it was present anywhere the code was, including version history, developer machines, and any system with repository access.
It had full account permissions. The key could do anything the Brevo Cloudflare account could do. It was not scoped to a single zone or a single capability, so possession of it was possession of the entire edge. It was long-lived. No short expiry meant a key compromised in late August was still valid and still powerful in mid-September.
Each of those is a well-understood anti-pattern with a well-understood fix. The reason this became a 100,000-site incident rather than a rotated-key footnote is that all three were true of the same credential.
Why Server-Side Security Defenses Missed the Brevo Attack
This is the part that makes the attack modern, and it is worth slowing down on. A traditional website compromise modifies files on the origin server. That is what most defenses are built to catch.
File integrity monitoring hashes your files and alerts on change. A web application firewall inspects requests hitting your origin. Server access logs record who logged in.
None of those fired, because the attacker never went near the origin. A Cloudflare Worker runs at the edge, in the path between your server and the visitor's browser. The Worker intercepted responses on their way out and rewrote them in transit.
Brevo's own words are that the content was altered in transit at the CDN edge, so origin servers and files remained unmodified and standard integrity checks did not detect the change. The Worker also stripped security headers, including Content-Security-Policy, on the responses it rewrote.
A correctly configured CSP is one of the few controls that could limit an injected script, and the attacker simply removed it at the edge before the browser ever saw it.
Defense | What it watches | Why it missed Brevo |
|---|---|---|
File integrity monitoring | Origin file hashes | Origin files never changed |
Web application firewall | Requests to the origin | Attack was in the response, at the edge |
Server access logs | Logins and server activity | No server was accessed |
Content-Security-Policy | Which scripts a browser runs | Stripped at the edge before delivery |
Origin-based scanning | The origin | The malice lived in front of the origin |
The lesson is uncomfortable. Your source code is no longer where the attack happens. It is where the attack starts. The damage is done downstream, in infrastructure the origin's defenses cannot see.
What is a Supply Chain Attack, in this Specific Shape
A supply chain attack compromises a trusted third party in order to reach that party's customers, rather than attacking each customer directly.
The Brevo case is a precise example, and its shape is worth naming because it is increasingly common. The customers did nothing wrong and changed nothing. They had embedded a Brevo script, which is the documented, intended way to use the product. When that script was compromised at Brevo's edge, the compromise flowed to every site embedding it.
This is the JavaScript supply chain, and it differs from the more familiar dependency supply chain. The dependency supply chain is the packages you install at build time. A poisoned npm package is the familiar case, and software composition analysis is built to watch it.
The JavaScript supply chain is the third-party scripts your pages load at runtime, directly in the visitor's browser. Analytics, chat widgets, form handlers, tag managers, payment scripts. Each is code you did not write, loaded from infrastructure you do not control, executing with full access to your page.
The historical name for attacks on this surface is Magecart, most associated with skimming payment-card data from checkout pages. Brevo is the same surface used for a different payload. The defense is the same either way, and it is client-side rather than server-side.
Client-side Defenses that Would Have Limited the Blast Radius
Server-side controls could not see this attack. Two client-side controls could have limited it, and both are worth adopting regardless of Brevo.
Subresource Integrity
An SRI hash on a script tag tells the browser to run the script only if its content matches a known hash. If the content changes, the browser refuses to execute it.
The honest caveat is that SRI works for versioned, static scripts. Many third-party widgets are designed to update themselves, which is incompatible with a fixed hash, and that tension is real. Where a script is stable, SRI is a hard guarantee the browser enforces.
How Subresource Integrity and CSP Can Limit Supply Chain Attack Damage
A content security policy (CSP) restricts which origins a page may load scripts from, and which it may connect to. A well-scoped connect-src would have constrained where an injected script could send stolen data.
The Brevo attacker stripped CSP at the edge, which is precisely why the policy has to be delivered and enforced in a way an edge rewrite cannot silently remove. And why CSP monitoring that alerts on policy changes matters as much as the policy itself. Neither control fixes the root cause. Both shrink the blast radius when a trusted script goes bad, which on this surface is the realistic goal.
Why Code Review Is the First Place to Catch Hardcoded Secrets
The client-side controls above are damage limitation. The root cause has an earlier and cleaner fix, and it is at the pull request.
The entire incident traces to a credential that should never have been in source. That is a property of a code change, visible at the moment the change is proposed, and detectable before it is ever merged. A reviewer that reads every diff for secrets sees a full-permission API key the day the commit lands. Not the week 100,000 sites serve malware.
The gap between those two moments is the entire cost of the incident, and it is closable at the first one. This is why the secret belongs in the same conversation as the review, not in a separate scanning pass that runs later. The cheapest, earliest, most context-rich moment to catch a hardcoded credential is the review of the change that introduces it.
How CodeAnt AI Detects Hardcoded API Keys in Pull Requests
Three capabilities map directly onto this incident.
Secret scanning at the pull request
CodeAnt's secret scanning inspects every pull request for hardcoded credentials, from cloud keys to API tokens, and flags them inline before they reach production or version history. It detects AWS, Azure, GCP, GitHub tokens, and more, including cases where the value is obfuscated or embedded in a dynamic string. Critically for the Brevo case, it does not stop at flagging.
It recommends moving the value to an environment variable or a secret manager, with the correct syntax for the language, which is the fix Brevo eventually applied after the incident rather than before it.
Full pull-request review, not just a pattern match
A Cloudflare key is one finding. AI code review reads the whole change with the context of the surrounding code, so a hardcoded credential is caught alongside the injection, configuration, and authorization issues that a secrets-only scanner would miss.
Scope and severity, not undifferentiated alerts. A full-permission, long-lived key is a materially worse finding than a scoped, short-lived one. The review distinguishes them, so the credential that could rewrite an entire edge is not buried in a flat list of low-risk matches. The track record is the relevant proof.
CodeAnt has found 150+ CVEs, including a CVSS 10.0 vulnerability in pac4j that went undetected for six years, across projects representing 2B+ monthly downloads protected. The discipline that finds a maximum-severity flaw in widely deployed open-source code is the same discipline that flags a full-permission key in a diff.
How AI Penetration Testing Finds Secrets Already Exposed in Production
Catching the key at review stops it from entering the code. Testing the running system finds the keys that already did, which is the other half of the problem.
The AI penetration testing pipeline already performs the exact reconnaissance an attacker would run against a JavaScript supply chain.
It pulls production JavaScript bundles and statically mines them for API keys, internal hostnames, OAuth client identifiers, and source-map artifacts that leak original paths. That is the Brevo class of exposure, found from the outside, on the live artifact, the same way the attacker found it.
A key exposed in a shipped bundle or reachable through the front end is exactly what this surfaces, and it runs continuously rather than once a quarter. The two halves compose. Review keeps new secrets out of the code.
Attack surface analysis finds the ones already shipped and reachable, and correlates them against the exposure that matters. That is the difference between hoping no key leaked and knowing.
What Every Team Embedding Third-party Scripts Should Do Now
Six actions, ordered by where they stop the damage.
Scan every pull request for secrets. A hardcoded credential is the root cause here and it is detectable at the diff. This is the single highest-leverage control, because it acts before the credential is ever live.
Scope and time-limit every key. No credential should carry full account permissions or an indefinite lifetime. A scoped, short-lived key limits what its compromise can reach, which would have capped this incident dramatically.
Rotate credentials on a schedule and on any suspicion. Brevo's key may have been compromised weeks before it was used. Regular rotation shortens that window whether or not you ever detect the compromise.
Inventory your third-party scripts. You cannot defend a surface you have not enumerated. List every external script your pages load and treat each as code you do not control.
Add Subresource Integrity where scripts are static, and a monitored CSP everywhere. These do not fix the root cause, but they limit what a compromised script can do and alert you when the policy protecting you is tampered with.
Test your live front end for exposed secrets. Mine your own shipped bundles the way an attacker would, so a leaked key is something you find first.
Conclusion: The Brevo Attack Started With a Secret in Source Code
The Brevo attack is being filed as a CDN story and a ClickFix story. Underneath both, it is a hardcoded-secret story. A full-permission, long-lived key in source code became a Cloudflare Worker, which became malware on 100,000 sites, without a single server being touched.
Every server-side defense missed it because the attack lived in front of the origin, and the root cause lived behind it, in one line of code. That line was reviewable the day it was written. The question for any team shipping code today is not whether their edge is secure. It is whether a credential like that one would survive their next pull request.
Sources
Brevo, official incident post-mortem, September 2026
Sansec, Brevo supply chain attack hits 100k+ sites with WordPress backdoors and ClickFix malware, September 16, 2026
BleepingComputer, Brevo supply-chain attack injected ClickFix scripts on customer sites, September 17, 2026
Security Affairs, Brevo supply chain attack infected over 100,000 websites, September 2026


