Critical JSONata RCE: upgrade to 2.2.2 (or 1.8.9) now

The patches shipped back in May and July — but the three critical JSONata RCE CVEs only went public on Aug 21, so plenty of teams have been quietly exposed without knowing. Which versions are affected, how to find them in your lockfile, and how to fix it.

JSONata RCE: upgrade to 2.2.2 now.

Short answer: If your project depends on jsonata below 1.8.8 or on 2.0.0 – 2.2.0 (CVE-2026-77413 affects everything below 2.2.0; CVE-2026-77414 and CVE-2026-77415 everything below 2.2.1) and it ever evaluates an expression you did not write yourself, an attacker can run arbitrary code on that host. Upgrade to 2.2.2 (or 1.8.9 on the 1.x line), then check whether untrusted expressions ever reached the old version.

On August 21, 2026 the GitHub Advisory Database published three critical advisories for JSONata, the JSON query and transformation library with roughly 1.65 million weekly downloads. It sits inside Node-RED, IBM App Connect, Stedi, a long tail of workflow engines, low-code platforms, and “let the user write a little transform” features. That last category is the problem: JSONata is usually exposed precisely so that someone other than you can supply the expression.

This is a vulnerability disclosure, not a package compromise. Nobody hijacked the jsonata package; the fixed versions are the maintainer's own. But the exploit is a one-liner, public proofs of concept are in the advisories, and the bug class (sandbox escape to child_process) means the blast radius is whatever the Node process can reach.

What are the three JSONata CVEs?

AdvisoryCVERoot causeAffectedFixed in
GHSA-8gq3-vp5j-2grpCVE-2026-77413Missing hasOwnProperty check in the lookup function — an expression can walk the prototype chain and reach constructor<= 1.8.7, 2.0.0 – 2.1.x1.8.8, 2.2.0
GHSA-2943-5xfg-gq5fCVE-2026-77414Bypassable hasOwnProperty check in environment.lookup — shadow the check with a user-defined variable, then reach the Function constructor< 1.8.8, 2.0.0 – 2.2.01.8.8, 2.2.1
GHSA-66mm-25pp-rfffCVE-2026-77415Overwriting $clone lets transforms mutate objects; combined with destructuring built-in lambdas and a non-standard forEach in applyProcedure, it chains to code execution< 1.8.8, 2.0.0 – 2.2.01.8.8, 2.2.1

All three share the same shape: JSONata's evaluator is meant to be a closed expression language, but object property lookup leaked into JavaScript's prototype chain. From constructor you get Function; from Function you get process.getBuiltinModule('child_process'); from there you get a shell. On Node releases older than 22.3 and 20.16, which lack getBuiltinModule, process.mainModule.require does the same job. The advisory PoC for CVE-2026-77414 is six lines.

Severity is rated critical. The fixes landed in the 2.2.0 and 2.2.1 releases (May 14 and May 19, 2026) and were back-ported to 1.8.8 on July 16. The maintainers published the repository advisories on July 13, but they were not reviewed into the GitHub Advisory Database — the feed behind npm audit, Dependabot and OSV.dev — until August 21. For five weeks the information existed and no scanner acted on it, which is why a lot of lockfiles are still on 2.1.x or 1.8.7 today. Current releases are 2.2.2 and 1.8.9. On the 1.x line prefer 1.8.9: 1.8.8 closes the three code-execution bugs but not a separate High-severity denial-of-service in $toMillis (CVE-2026-52746), which 1.8.9 and 2.2.0+ fix.

How do you check if you're exposed?

Two questions: is a vulnerable version in your tree, and does it ever evaluate an expression you don't control?

1. Find the installed version

# npm
npm ls jsonata --all

# pnpm
pnpm why jsonata

# yarn
yarn why jsonata

# Or just grep the lockfile (each format spells the entry differently)
grep -n '"jsonata"' package-lock.json 2>/dev/null
grep -n 'jsonata@' yarn.lock pnpm-lock.yaml 2>/dev/null

Anything below 1.8.8 or in 2.0.0 – 2.2.0 is affected. Remember transitive dependencies: Node-RED, @node-red/util, and assorted ETL and API-gateway packages pull jsonata in for you. npm audit now flags all three advisories, so a plain audit run in CI will light up too.

Or paste the repo into the free InstallSafe scan — it walks your lockfile against live OSV.dev advisory data and reports every vulnerable or known-malicious version in the tree, including transitive ones. No signup, nothing installed.

2. Figure out where expressions come from

The vulnerable call is jsonata(expression).evaluate(data). The data argument is not the issue; the expression string is. Grep for it:

grep -rn "jsonata(" --include='*.js' --include='*.ts' --include='*.mjs' . | grep -v node_modules

Then classify each call site:

  • Hard-coded expression in your source — low risk. Upgrade anyway.
  • Expression from config, database, a UI field, an API body, a webhook, a tenant's settings, a Node-RED flow file, an LLM's output — treat as exploitable. Anyone who can write that string gets code execution as your Node process.

That second bucket is more common than it sounds. “Custom mapping rule”, “transform template”, “output formatter”, and “let the agent write the query” are all the same thing from JSONata's point of view.

How do you fix it?

  1. Upgrade. npm install jsonata@^2.2.2 (or jsonata@^1.8.9 if you're pinned to the 1.x API). Regenerate the lockfile and commit it.
  2. Force transitive copies too. If a dependency pins an old range, use overrides (npm), pnpm.overrides, or resolutions (yarn) to lift every copy in the tree to a fixed version, then re-run npm ls jsonata --all to confirm nothing below 2.2.1 survives.
  3. Upgrade the host products. Node-RED and other platforms that embed JSONata ship their own pinned copy; check their release notes and update the product, not just your package.json.
  4. Assume exposure if expressions were user-supplied. If a vulnerable version ever evaluated expressions from an untrusted source, review logs for odd expressions (look for constructor, __proto__, __lookupGetter__, __defineGetter__, $clone :=, getBuiltinModule, child_process) and rotate any secrets that process could read.
  5. Reduce what a successful escape can do. Run expression evaluation in a separate, least-privileged process or container with no cloud credentials mounted. JSONata has now had multiple escape bugs; design as if there will be another.

Why did this sit unpatched in so many lockfiles?

Because the fix shipped months before anything flagged it. A 2.2.1 release in May with no CVE attached looks like routine maintenance, and even the July repository advisories changed nothing until the Advisory Database picked them up in August; with ^2.1.0 in package.json and a committed lockfile, nothing pulls it in until someone deliberately bumps. That is normally the right behavior — lockfiles are what saved people from the keyv worm's poisoned patch bumps two weeks ago — but it means a quietly fixed vulnerability stays in production until the advisory lands and something actually enforces it.

The same lockfile discipline that protects you from malicious upgrades therefore needs a second control: something that refuses to install a version once it's flagged, regardless of what the lockfile says.

How does a registry firewall help here?

A registry firewall sits between your package manager and npmjs.org. Every npm install — on a laptop, a CI runner, a Docker build, or an AI coding agent's sandbox — resolves through it, and it refuses to serve versions that appear in advisory data. For this disclosure that means:

  • From the moment OSV.dev carries GHSA-8gq3-vp5j-2grp, GHSA-2943-5xfg-gq5f, and GHSA-66mm-25pp-rfff, a fresh install of jsonata@2.1.1 fails loudly instead of succeeding silently. The developer sees why and bumps.
  • A new service scaffolded next week from a stale template cannot reintroduce the vulnerable range.
  • An agent told to “add a JSON transform library” cannot land on a flagged version, even if its training data thinks 2.1.x is current.
  • It's a single point of policy, so you don't depend on every repo's npm audit step being configured and not ignored.

That's what InstallSafe is: a drop-in npm registry proxy that filters known-vulnerable and known-malicious versions using live OSV.dev data, serving byte-for-byte identical tarballs for everything else so your lockfile integrity hashes stay valid. Two config lines, works with npm, yarn, pnpm, and bun:

npm config set registry https://r.installsafe.io
npm config set //r.installsafe.io/:_authToken <your-token>

Be clear about the limit: a registry firewall is advisory-driven. It would not have blocked jsonata@2.1.1 in June, before anyone had filed the CVE. What it guarantees is that once a version is flagged, nothing in your org — human or agent, laptop or CI — can install it again, and that the already-installed copies show up in a scan. Behavioral tools like Socket or Aikido analyze package contents and can catch malicious code earlier; the two approaches are complementary, and for a logic bug like this one, advisory data is the only signal that exists at all.

Frequently asked questions

Is jsonata safe to use?

Yes on 2.2.2 or 1.8.9 and above. The three August 2026 advisories (CVE-2026-77413, -77414, -77415) are fixed in 2.2.1 / 1.8.8; earlier versions should not evaluate untrusted expressions.

Which jsonata versions are vulnerable?

CVE-2026-77413: every 1.x release up to 1.8.7 and 2.0.0 through 2.1.x. CVE-2026-77414 and CVE-2026-77415: below 1.8.8 and 2.0.0 through 2.2.0. Upgrade to 2.2.2 or 1.8.9 to cover all three.

Am I affected if I only evaluate my own hard-coded expressions?

Practically no — the attacker needs to control the expression string. Upgrade anyway, because “hard-coded” has a habit of becoming “configurable” later.

Does this affect Node-RED?

Node-RED bundles JSONata as the J: expression type in its Change, Switch and Inject nodes, and in any contributed node that uses a typed input. Anyone who can edit flows can already run code in Node-RED, so the trust model is similar, but you should still upgrade to a Node-RED release that ships a fixed jsonata and re-check with npm ls jsonata.

Was the jsonata npm package compromised?

No. These are logic vulnerabilities in legitimate releases, disclosed by the maintainers through GitHub's advisory process. The fixed versions are the authentic ones.

Will a registry firewall stop the next one?

It stops installs of any version once it's in advisory data, across every machine that resolves through it. It does not detect unknown bugs before disclosure; pair it with least-privilege execution for anything that evaluates user-supplied expressions.

Check your tree now with the free InstallSafe scan, or read how registry-level blocking compares with Socket.dev and Snyk.


Sources: GHSA-8gq3-vp5j-2grp, GHSA-2943-5xfg-gq5f, GHSA-66mm-25pp-rfff (GitHub Advisory Database, published 2026-08-21); OSV.dev; jsonata on npm; download counts from the npm downloads API for 2026-08-15 to 2026-08-21.