Skip to content

The patch isn't in HubSpot. It's next door.

HubSpot’s documentation for CMS Serverless Functions lists only one supported runtime: nodejs20.x. However, Node.js 20 reached its end of life on April 30, 2026.
  • October 23, 2026

Data Divers Dialogs · Analysis

A certificate authority, two runtimes and twelve CVEs in one week. The vendor made one statement, about the one thing it doesn't run. The most exposed machine in most HubSpot setups is the webhook receiver the customer forgot it owns.

By Christian Schappeit · Data Divers Dialogs · October 2026 · approx. 9 min read


Late on Wednesday night, at 11:31 p.m. Eastern time, HubSpot's status page closed an incident titled Update on PHP security vulnerability. The key sentence was short: "PHP isn't part of HubSpot's infrastructure, and we are not aware of any impact to our platform or customers."

It was true. As far as I can find, it was also the only thing HubSpot said all week.

And it was a week that could have used a few more statements. On Saturday the 17th, Mozilla and Google began blocking eight intermediate certificates from eMudhra, a publicly trusted certificate authority, whose private keys are compromised or derivable, depending on whether you ask the browsers or the CA. On Wednesday, Node.js shipped fixes for ten vulnerabilities and PHP for two more, each rated 9.2. Within days, the Brotli flaw in the Node release had acquired a nickname, Node2Shell, and a working exploit. On Tuesday the 27th, Chrome removes one of eMudhra's roots altogether.

In the Bundestag, when something cannot wait for the agenda, a parliamentary group can demand an Aktuelle Stunde: an hour on the matter of the moment, five minutes per speaker, no motion at the end. This week the internet held one. The CA spoke, the browsers spoke, the runtime projects spoke, the researchers spoke. The speaker most of my clients were waiting for spoke about PHP.

RIGHT ANSWER

The statement was correct. The question was a different one.

HubSpot is right about its own house. Its CMS doesn't run PHP; it templates in HubL, a language derived from Jinja. What does run on PHP is a great many of its customers' websites, because the HubSpot plugin for WordPress, the one that puts forms, pop-ups and live chat on the company homepage, runs on whatever PHP the site operator keeps underneath it.

The plugin's readme states its minimum: PHP 7.2. That's not a vulnerability. It's a description of the audience. A plugin that still accepts 7.2 has users on 7.4, 8.0 and 8.1, the versions for which PHP published no release this week. The security team pushed minimal fixes for those as git branches instead.

A git branch is not something a marketing manager installs. Those sites now depend entirely on whoever hosts them having done it for them.

So the status page answered "is HubSpot affected?" with complete accuracy. The question that would actually have helped somebody was another one.

Which parts of our HubSpot setup do we run ourselves?

THE INTEGRATION DIAGRAM

Every integration has a server in it. It isn't HubSpot's.

Ask a HubSpot customer whether they run Node.js and most will say no. They bought SaaS precisely so that they wouldn't have to.

Then look at what actually surrounds the portal. The webhook receiver: HubSpot delivers events by HTTPS POST to a URL the customer configures and operates. The OAuth backend: HubSpot's own documentation says, without ceremony, "You'll need to set up and host an OAuth backend service." The private app that syncs the ERP every night. The pipeline that deploys theme changes with HubSpot's CLI. The developer laptop running the same CLI.

Every integration I've built for a client has a Node process in it, and the client owns it. Not HubSpot. Not me, once it's handed over. The client.

"We bought SaaS so we wouldn't have to run servers. Then we built the integration."

THE FRONT DOOR

The webhook receiver opens the parcel before it checks the sender.

Node's own description of the Brotli flaw is plain: an attacker who can supply Brotli data that an application decompresses can corrupt process memory, "which can lead to a crash, disclosure of process memory, or remote code execution." Researchers at Imperva have since reported code execution against the official, unmodified Node.js 24.22.0 image, without a debugger. They are careful about it. It didn't work on every attempt, and they tested one platform. That is no longer a theoretical risk.

The more uncomfortable finding is about who gets to supply the data. Express 5, and the body-parser 2.x it depends on, decompress any request body that arrives with Content-Encoding: br, by default. A stock Express 5 app with nothing but express.json() hands an anonymous POST straight to the vulnerable decoder. Express 4 never did; it refused Brotli bodies. Anyone who upgraded their framework this year, as one is supposed to, widened the door.

Now picture a HubSpot webhook receiver. Its URL is public by design, because HubSpot has to reach it. It checks HubSpot's signature, as the documentation tells it to. But the body parser runs first, as middleware, before any line of the handler. By the time the code asks whether the request really came from HubSpot, the parcel has already been unpacked.

The signature check is still correct. It just arrives after the damage.

Nest.js 11 and 12 inherit this through their Express adapter, and so does Fastify where @fastify/compress is registered. Next.js does not decode inbound bodies at all. The point is not which framework is safe. It's that the receiver is the most exposed machine in many HubSpot setups, and the one least likely to have a named owner.

THE EDITABLE FIELD

In a CRM, the data is also the configuration.

The coverage of the Node release also led with three ways to slip past TLS hostname checks. Read the fix commits and that drama narrows. All three concern host strings that a lenient parser turns into an IP address: 0x7f.0.0.1, 2130706433, 127.1, digits typed in full width. An integration calling api.hubapi.com, a fixed string in the source code, is exposed by none of the three. That deserves saying plainly, because security advisories rarely do.

But CRMs have a habit that most software doesn't. They keep URLs in fields. A logo URL on the company record. A document link on the deal. A webhook target typed into a custom property. An enrichment workflow that calls whatever address the record happens to hold.

In each of those, the destination of an outbound connection is chosen by whoever can edit a field. In most portals that is a lot of people, some of whom work for a partner agency. And for the Brotli flaw, nobody needs to forge a certificate or sit in the middle. The person who chose the URL simply runs the server at the other end. fetch() and axios will decompress whatever it sends, and tell it in advance that they will.

THE GREEN DASHBOARD

Every check says fine. None of them is looking.

This is the part of the week I find most instructive, because nothing in it is anybody's fault.

Run npm audit and it will come back clean. It reads the npm advisory database, and this is a flaw in Node itself, not in a package. Check the bundled Brotli version and it hasn't changed either, because Node fixed the decoder in place without bumping the library. A patched and an unpatched Node report the same string.

Rebuild the container and you may get the old runtime again, because Docker reuses a cached base image unless told to --pull. Ask GitHub Actions for node-version: 22 and the hosted runner hands you the copy in its local cache, which is currently an unpatched 22.23.3. HubSpot's own CMS deploy action asks for 22.x and gets exactly that. Build FROM node:20 and you get a Node line that will never be fixed, frozen in April with the distrusted root still in its trust store.

Each tool is doing precisely what it was designed to do. Together they produce a green dashboard over an unpatched estate. The reliable check is boring: the Node version, and whether require('zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH exists. It only exists in a patched decoder.

NODEJS20.X

The one runtime HubSpot does operate is now measurable.

There is one place where HubSpot does run the Node, and it deserves a careful look rather than a loud one.

HubSpot's reference for CMS serverless functions lists a single valid runtime: nodejs20.x. Node.js 20 reached end of life on 30 April 2026. Its last release bundles Brotli 1.1.0, inside the affected range, and it received nothing on Wednesday. The Node project is blunt about what that means: "End-of-Life versions are always affected when a security release occurs."

That still does not mean HubSpot runs vulnerable code. It may backport fixes into its own build, and a backport wouldn't change the version string. But the constant would. For the first time, the question has a test: deploy a small read-only function into a portal, call it once, record what the runtime says about itself, delete it.

I wrote that function this week, ran it, and got a crisp result: Windows, Node 24.14.1, unpatched. It had measured my own workstation. HubSpot's local development server runs functions on the developer's machine, which is a good joke and a better lesson. The nearest unpatched Node was the one I was typing on.

The measurement has another wrinkle. CMS serverless functions exist only in Content Hub Enterprise portals and in developer sandboxes. Many mid-market clients, on CMS Professional, have none, which means the question doesn't apply to them at all. For the Enterprise clients it does apply to, the result belongs in the supplier file, dated, alongside a written question to HubSpot. At the time of writing I haven't measured a HubSpot-hosted runtime. I would rather update that sentence than retract a guess.

TUESDAY, 27 OCTOBER

Only one item this week has a clock on it.

On Tuesday morning, Chrome removes emSign ECC Root CA – G3 from its root store. Certificates that chain to it stop working.

A direct connection to api.hubapi.com this week presented a certificate from Google Trust Services, served through Cloudflare. HubSpot's API does not depend on the eMudhra root. That covers one hostname at one moment. It says nothing about your own endpoints: the webhook receiver, the origin behind your CDN, the client certificate a partner issued three years ago and nobody has looked at since.

And somewhere on Tuesday, an integration will throw a certificate error that looks exactly like a broken integration. The fastest fix is one line of configuration that switches off verification. Cloudflare's guidance says it with the patience of someone who has watched it happen: "It's important to not resolve these errors by disabling verification."

"Under deadline pressure, a forged certificate looks exactly like a broken integration."

If you run Node.js next to HubSpot

Find every Node. Webhook receivers, the OAuth callback service, sync jobs, CI pipelines, every laptop with the HubSpot CLI. Patched means 22.23.4, 24.22.1 or 26.12.1, or later. Anything on 20 or 18 has no fix and needs to move.

Check the decoder, not the audit. npm audit cannot see this flaw. Run node -p "require('zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH": undefined means unpatched, -17 means patched.

Rebuild, don't restart. docker build --pull --no-cache, bump pinned digests, and move off node:20. On GitHub Actions, add check-latest: true or pin the exact version. If you use HubSpot's CMS deploy action, install the patched Node in a step before it.

Close the front door today. If a webhook receiver runs Express 5 or Nest 11–12, patch Node first. Until then, set inflate: false on body parsers for endpoints that never expect compressed bodies, or reject Content-Encoding: br at the proxy, and confirm that HubSpot's deliveries still arrive. That does nothing for outbound calls.

List the fields that hold URLs. Any integration that fetches an address from a contact, company or deal property: restrict who can edit that field, or allow-list the hosts it may call.

Check how Node was installed. Debian's own nodejs package links the system Brotli, which has no fix yet. A server built with apt install nodejs is a different risk from one running the official binary or Docker image.

If HubSpot forms reach you through WordPress

Look up your PHP version. In WordPress, Tools → Site Health → Info → Server. On 8.2 to 8.5, ask your host to confirm 8.5.12, 8.4.27, 8.3.36 or 8.2.35.

On 8.1 or older, ask in writing. There is no upstream release for those versions, only source backports. Your host has either applied one or it hasn't. If it hasn't, plan the upgrade now; the HubSpot plugin won't stand in the way, and the forms deserve a test afterwards.

Removing the plugin fixes nothing. The flaw is in PHP, and the site still runs PHP without the plugin.

If you can't upgrade this week, PHP's advisory lists partial measures, with one explicit warning: do not use RLIMIT_AS or RLIMIT_DATA. Ask your host whether the site runs on mod_php, where PHP rates the risk highest.

Three dates for the diary

27 October 2026 — Chrome drops the eMudhra root. Certificates chaining to emSign ECC Root CA – G3 must be replaced before the morning, including CDN origins, internal services and mTLS clients.

31 December 2026 — PHP 8.2 reaches end of life. This week's patch buys an 8.2 site ten weeks.

8 January 2027 — HubSpot's CLI requires Node 22. Node 20 has had no upstream patch since April. Workstations and CI runners still on it should move first.

NO MOTION

The debate is over. The change ticket isn't.

An Aktuelle Stunde, by the rules of the house, ends without a vote. The speakers say their piece, the chamber empties, and nothing has formally been decided.

That is roughly where the week leaves a HubSpot customer. The CA has spoken, the browsers have acted, the runtimes have shipped, the researchers have shown it works. The vendor has confirmed, correctly, that it doesn't run PHP.

Every decision still open sits in a system the customer owns and has quite often forgotten it owns: a webhook receiver nobody has touched since the agency handed it over, still answering HubSpot every time a contact changes, still unpacking every parcel before it reads the label.


Figures and sources. Node.js security release of 21 October 2026: ten CVEs, patched in v26.12.1, v24.22.1 and v22.23.4; the TLS hostname flaws are CVE-2026-105483, CVE-2026-106069 and CVE-2026-106072, the Brotli flaw ("Node2Shell") CVE-2026-111497, in upstream Google Brotli 1.0.3 to 1.2.0, scored CVSS v4 9.2 by the Google CNA. PHP release of 21 October 2026: CVE-2026-109618 and CVE-2026-109619, each CVSS v4.0 9.2, fixed in 8.5.12, 8.4.27, 8.3.36 and 8.2.35; 7.4, 8.0 and 8.1 received source backports as git branches, not releases. Node.js 20 end of life: 30 April 2026; its last release, 20.20.2, bundles Brotli 1.1.0. @hubspot/api-client 14.0.1 declares engines.node >=22.0.0. HubSpot CLI drops Node 20 on 8 January 2027. CMS serverless functions require Content Hub Enterprise or a CMS developer sandbox.

On confidence. CVE list, versions and dates come from the vendors' own pages and are established. The code-execution result is Imperva's, on the official Node.js 24.22.0 Linux arm64 image; Imperva states it did not succeed on every attempt and that not every build was tested. No exploitation in the wild has been reported. The Express 5 behaviour was read from the published body-parser 2.x source; that the parser runs before a handler's signature check follows from how Express middleware is ordered, and is an inference rather than a tested attack on a HubSpot receiver. The setup-node cache finding refers to the GitHub-hosted ubuntu-24.04 image 20261012. Whether HubSpot's hosted nodejs20.x runtime carries a backported fix is not established; HubSpot has published no statement on the Node.js release or the eMudhra incident, and that silence is read here as neither reassurance nor alarm. The certificate observation for api.hubapi.com is a single connection on 23 October. The WordPress plugin's "Requires PHP: 7.2" was read from a mirror of the wordpress.org readme. The name "Node2Shell" is in circulation; it is not Node's own name for the flaw.

Primary reporting: HubSpot status: Update on PHP security vulnerability · Node.js: October 2026 Security Releases · PHP: security advisory, 21 October 2026 · HubSpot: Getting started with serverless functions · eMudhra: Statement on Recent Certificate Security Incident · Mozilla Security Blog · Google: Protecting Chrome and Android users · Cloudflare: 8 eMudhra certificates are compromised.

Tags: HubSpot, Node2Shell, Express 5, eMudhra, PHP, Supplier Assurance, GxP

— End —

Related Posts

You may also like this

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique.

Free Guide

How to increase your Facebook reach by over 200% with this simple trick

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique.