Patching a MEAN Stack: Node.js, Express and Angular SSR Security Releases in 2026

The main 2026 security releases for Node.js, Express middleware and Angular SSR in one place, with affected and fixed versions. Plus how to check what you actually run, a patch routine for a production app with staging and rollback, and what to monitor afterwards.

Cover for the article Patching a MEAN Stack: Node.js, Express and Angular SSR Security Releases in 2026

If you run a MEAN stack in production on 5 November 2026, these are the minimum versions. Node.js: 26.12.1, 24.22.1 or 22.23.4. The 21 October release fixed CVE-2026-111497, a Brotli decoder bug that allows remote code execution. Express middleware: proxy-addr 2.0.8, body-parser 2.4.0 (or 1.20.9 on Express 4), compression 1.8.2 and multer 2.5.0. Angular SSR: framework packages on 22.2.1 or later, @angular/ssr on 22.1.7 or later and @angular/build on 22.2.5 or later. Node.js has also scheduled another security release for 26.x, 24.x and 22.x, on or shortly after 11 November 2026, with the highest severity rated High. Below you will find the advisories that matter most in one place, how to check what you actually run, and a patch routine that keeps rollback easy.

The 2026 advisories, component by component

These tables are a selection, and every row was confirmed on the projects' official pages. The Node.js table covers the five security releases of 2026 so far. The Express table covers July to October 2026; the earlier Express posts (February to June) cover multer, path-to-regexp, multiparty and morgan, so read them too if you are far behind. The Angular table covers the SSR, router, HttpClient and build packages. Node.js posts list the patched release for each line, not per CVE, so "Fixed in" there means the security release for each line. Later releases on the same line include the fixes.

Node.js runtime

ComponentAdvisorySeverityAffectedFixed inSource
Node.jsOctober release: CVE-2026-111497 (Brotli RCE), CVE-2026-105482, CVE-2026-105483, CVE-2026-106069, plus 3 Medium and 3 LowHigh26.x, 24.x, 22.x26.12.1, 24.22.1, 22.23.4nodejs.org, 21 Oct
Node.jsJuly release: CVE-2026-56846, CVE-2026-56848 (HTTP/2), CVE-2026-58043 (Permission Model), plus 5 Medium and 3 LowHigh26.x, 24.x, 22.x26.5.1, 24.18.1, 22.23.2nodejs.org, 29 Jul
Node.jsJune release: CVE-2026-48933 (WebCrypto AES crash), CVE-2026-48618 (TLS wildcard bypass), plus 6 Medium and 4 LowHigh26.x, 24.x, 22.x26.3.1, 24.17.0, 22.23.0nodejs.org, 18 Jun
Node.jsMarch release: CVE-2026-21710 (__proto__ header crash), incomplete fix for CVE-2026-21637, plus 5 Medium and 2 LowHigh25.x, 24.x, 22.x, 20.x25.8.2, 24.14.1, 22.22.2, 20.20.2nodejs.org, 24 Mar
Node.jsJanuary release: CVE-2025-55131, CVE-2025-55130, CVE-2025-59465High25.x, 24.x, 22.x, 20.x25.3.0, 24.13.0, 22.22.0, 20.20.0nodejs.org, 13 Jan

Node 20 reached end of life on 30 April 2026, and Node 25 is end of life too. The Node.js security posts state that end-of-life versions are affected by security releases, so "we are on 20" means "we are unpatched". Which line to move to is covered in Node.js LTS versions for production.

Express and its middleware

ComponentAdvisorySeverityAffectedFixed inSource
proxy-addr (trust proxy)CVE-2026-90711: an IPv4-mapped IPv6 subnet matched every address, so req.ip could be spoofedCritical1.1.0 to before 2.0.82.0.8expressjs.com, 30 Sep
body-parserCVE-2026-111181 (GHSA-27r3-jxmh-p828): trailing bytes after a gzip body crash the processHigh2.0.0 to before 2.4.0; before 1.20.92.4.0, 1.20.9expressjs.com, 31 Oct
body-parserCVE-2026-12590: an invalid limit silently disabled the size checkLow2.0.0 to before 2.3.0; before 1.20.62.3.0, 1.20.6expressjs.com, 31 Jul
compressionCVE-2026-87776: zlib stream not destroyed on abort, native memory leakHighbefore 1.8.21.8.2expressjs.com, 30 Sep
multerCVE-2026-113872 (GHSA-q7qp-rjg7-q26m): crash when a request is torn down while an async fileFilter is pendingHigh2.0.0 to before 2.5.02.5.0expressjs.com, 31 Oct
multerCVE-2026-77037, CVE-2026-77078, CVE-2026-82333 (High), CVE-2026-77063 (Low)Highbefore 2.3.0 (CVE-2026-77037: 2.2.0 only)2.3.0expressjs.com, 31 Aug
multerCVE-2026-88932: aborted uploads left orphaned filesMedium2.2.0 to before 2.4.02.4.0expressjs.com, 30 Sep
morganCVE-2026-87859 (log injection); CVE-2026-15603 (log forging)Mediumbefore 1.12.1; before 1.12.01.12.1expressjs.com, 30 Sep / 31 Aug
cookiesCVE-2026-88038: domain/path written unescaped into Set-CookieMediumbefore 0.9.20.9.2expressjs.com, 30 Sep

Express 5.3.0, released in October, raised its own minimums: qs (for CVE-2026-2391, CVE-2026-82417 and CVE-2026-82562), proxy-addr ^2.0.8 and body-parser ^2.3.0. Its caret range lets npm update pull in body-parser 2.4.0. If you use hbs or multiparty, the same Express posts cover fixes in hbs 4.3.1 and multiparty 4.3.1.

Angular SSR, router, HttpClient and @angular/build

ComponentAdvisorySeverityAffectedFixed inSource
@angular/ssrCVE-2026-27739 (GHSA-x288-3778-4hhx): SSRF and header injection via Host/X-Forwarded-*Critical (9.2)21.x before 21.1.5; 20.x before 20.3.17; 19.x before 19.2.2121.1.5 (21.2.0-rc.1), 20.3.17, 19.2.21angular-cli, 23 Feb
@angular/ssrCVE-2026-27738, CVE-2026-33397, CVE-2026-44437: open redirects (X-Forwarded-Prefix and protocol-relative URL handling)Moderatesee each advisory21.2.9, 20.3.25 cover all threeangular-cli, 23 Feb / 19 Mar / 30 Apr
@angular/ssrCVE-2026-104871 (GHSA-7g7c-h8rr-7p6q): path traversal in CommonEngine on WindowsModerate22.x before 22.1.7; 21.x before 21.2.23; 20.x before 20.3.3622.1.7, 21.2.23, 20.3.36angular-cli, 2 Sep
@angular/platform-serverCVE-2026-88056 (SSRF, credential disclosure) and CVE-2026-88058 (SSR XSS)High22.x before 22.1.4; 21.x before 21.2.22; 20.x before 20.3.3022.1.4, 21.2.22, 20.3.30angular, 27 Aug
@angular/platform-serverCVE-2026-101895 (GHSA-f67j-2jqw-jpq7): infinite loop on a malformed DOCTYPEHigh22.x before 22.1.6; 21.x before 21.2.23; 20.x before 20.3.3122.1.6, 21.2.23, 20.3.31angular, 9 Sep
@angular/platform-serverCVE-2026-111754 (GHSA-w739-gvwx-grc3): open redirect via protocol-relative URLsModerate22.x before 22.2.1; 21.x before 21.2.25; 20.x before 20.3.3322.2.1, 21.2.25, 20.3.33angular, 30 Sep
@angular/routerCVE-2026-101896 (numeric matrix params), CVE-2026-111752 (empty-path outlets), CVE-2026-111751 (RouterLink query params): SSR denial of serviceHigh22.x before 22.2.1; 21.x before 21.2.25; 20.x before 20.3.33 (CVE-2026-111751: 22.x and 21.2.x only)22.2.1, 21.2.25, 20.3.33angular, 23 Sep to 1 Oct
@angular/commonCVE-2026-88059 (GHSA-p297-fm68-3q8c): transfer cache stores authenticated responses with withRequestsMadeViaParentModerate22.x before 22.1.1; 21.x before 21.2.20; 20.x before 20.3.2822.1.1, 21.2.20, 20.3.28angular, 20 Aug
@angular/commonCVE-2026-111753 (GHSA-jfgx-762w-99vr): XSRF token leak to cross-origin hostsHigh21.0.x before 21.0.3; 20.x before 20.3.34 (22.x not affected)21.0.3, 20.3.34angular, 16 Oct
@angular/buildGHSA-w34w-96hf-98hj (no CVE): code injection in generated server manifests; GHSA-jp2q-rh7r-4vjf (Low): XSS in prerendered redirect pagesHigh (7.7)22.x before 22.2.5; 21.x before 21.2.29; 20.x before 20.3.4222.2.5, 21.2.29, 20.3.42angular-cli, 28 Oct

I left out the template-sanitisation (XSS), transfer-cache and service-worker advisories of May to August; every one of them is fixed in 22.1.4 or earlier, so framework packages on 22.2.1 are covered. Version 19 is out of support, and the advisories from late July onwards list no fix for it. Angular 20's LTS also ends on 28 November 2026, so a v20 app has only a few weeks of patches left. The upgrade itself is covered in Upgrading to Angular 22.

Check what you actually run

package.json shows ranges, not versions. The lockfile and the running process are what count. These commands answer "am I affected?" for every row above:

# The runtime that actually serves traffic
node -v

# Every installed copy of the Express middleware, including nested ones
npm ls proxy-addr body-parser compression multer morgan cookies

# Angular framework and tooling (core and CLI packages have different patch numbers)
npm ls @angular/core @angular/platform-server @angular/router @angular/common @angular/ssr @angular/build

# Known advisories for production dependencies, then for everything
npm audit --omit=dev
npm audit

Run both audits. --omit=dev is useful for API dependencies, but @angular/build is a devDependency, and the code it generates runs in your production SSR server. A production-only audit will not report it.

If you deploy containers, ask the image rather than the Dockerfile in your repo, because the tag that is running may not be the one you last committed:

# Node version inside the image that is running
docker run --rm my-api:current node -v

# The base image line, and the digest you can pin for rollback
grep -n "^FROM" Dockerfile
docker image inspect node:24.22.2-alpine --format "{{index .RepoDigests 0}}"

For an Angular SSR app, check both images, because the API and the SSR server often run on different Node versions.

A patch routine for a production app

1. Triage, then patch everything anyway

Read each advisory's conditions, because they tell you how urgent it is. The body-parser and multer bugs crash the process with one unauthenticated request. The proxy-addr bug only matters if your trust proxy value uses IPv4-mapped IPv6 notation. The @angular/build issue needs prerendering with server output. The conditions set the order you patch in, not whether you patch: an advisory that does not apply today can apply after next month's refactor.

2. One layer per deploy

I patch in three separate deploys, so a regression points to one cause:

  1. Node runtime. Bump the base image to an exact patch tag. This touches TLS, HTTP and zlib for everything in the process.
  2. Express middleware. A lockfile-only change, using the command from the Express security posts.
  3. Angular packages. Update within your major, rebuild, and deploy the new SSR bundle.
# Pin the exact patch release, not just "node:24"
FROM node:24.22.2-alpine
# Express middleware: stays inside your semver ranges, updates package-lock.json
npm update proxy-addr body-parser compression multer morgan cookies

# Angular: latest patch of your current major
ng update @angular/cli@^22 @angular/core@^22

If a nested copy will not move because its parent pins an old range, an npm overrides entry forces it. Remove the override once the parent has been updated:

{
  "overrides": {
    "proxy-addr": "^2.0.8"
  }
}

3. Staging with the same artefact

Deploy the exact image you will ship to production, then test the paths these advisories touch:

  • a gzip-compressed JSON POST to a route that uses express.json()
  • a file upload, and an upload cancelled halfway
  • req.ip in the logs behind your real proxy
  • a server-rendered page (200), an unknown URL (404) and a redirect
echo '{"name":"patch test"}' | gzip | curl -s -o /dev/null -w "%{http_code}\n" \
  -X POST https://staging.example.com/api/contact \
  -H "Content-Type: application/json" -H "Content-Encoding: gzip" --data-binary @-

4. Rollback is a tag, not a rebuild

Keep the previous image tag (or digest) deployable, and keep each layer's change in its own commit. A runtime regression is rolled back by redeploying the old image. A dependency regression is rolled back by reverting the lockfile commit and running npm ci. You should never have to choose between an old Node.js and a new body-parser.

Stopgaps when you cannot patch today

These come from the official advisories and only buy time:

  • Brotli RCE (Node.js): upgrading Node.js is the real fix. If you cannot upgrade yet, the Express team suggests rejecting request bodies whose Content-Encoding is not identity, gzip or deflate. Do it before any body parser runs, and only if you do not need Brotli request bodies.
  • body-parser gzip crash: express.json({ inflate: false }) rejects compressed bodies instead of crashing.
  • Angular SSR host headers: set security.allowedHosts in angular.json (or NG_ALLOWED_HOSTS), and only trust X-Forwarded-* headers behind a proxy you control.
  • @angular/build manifests: only allow letters, digits, hyphens and underscores in values returned from getPrerenderParams().
// From the Express October 2026 post: register before every body parser.
app.use((req, res, next) => {
  const encoding = (req.get('Content-Encoding') || 'identity').trim().toLowerCase();
  if (!['identity', 'gzip', 'deflate'].includes(encoding)) {
    return res.status(415).send('Use gzip, deflate, or an uncompressed request body.');
  }
  next();
});
app.use(express.json());

The 415 status tells clients the encoding is the problem. The HTTP status codes reference covers it and the 400 that Angular returns for an unknown host.

The Node.js release scheduled for 11 November 2026

On 3 November the Node.js project posted a pre-announcement: new 26.x, 24.x and 22.x releases are scheduled for "on or shortly after" Wednesday, 11 November 2026. The highest severity issue fixed is rated High. No CVE details are public yet; they come with the release. Plan the runtime deploy for that week now, and test it like the routine above. Then update this post's minimum versions in your own runbook.

What to monitor

  • Announcements: the nodejs-sec group and the Node.js vulnerability blog; the Express blog's security posts; and the security advisory pages of angular/angular and angular/angular-cli.
  • Dependencies: npm audit in CI on every pull request, including dev dependencies, plus Dependabot or a similar tool. Supply-chain risk is a separate problem from known CVEs; see protecting a Node project from npm supply-chain attacks.
  • Runtime signals: most advisories this year are denial-of-service bugs, so alert on process restarts and crash loops, memory growth, event-loop lag and 5xx rates. After you enable allowedHosts, watch for a jump in 400 responses. Watch for 302 redirects to external hosts from your SSR server too.

A small, regular routine beats a heroic catch-up. If you want a second pair of hands on a Node.js and Express API, see my Node.js and Express API development service.

Sources