The archive/CVE-2022-46175
json5/json5Disclosed 2022

CVE-2022-46175:json5: Prototype pollution

A reconstruction of the flaw, and the evidence path Lachesis follows to make it visible.

Historical recordv2.2.1 (affected: < 1.0.2, and >= 2.0.0 < 2.2.2)
WeaknessCWE-1321 · Prototype pollution
ImpactA prototype pollution in json5, reachable from attacker-controlled input.
Read the reconstruction
01What happened

A JSON5 document whose property name is __proto__ (e.g. {"__proto__": {"polluted": true}}) passed to JSON5.parse

That is the shape of the failure. The rest of this case file follows the evidence behind it.

Separate the record from the run

What was known.
What Lachesis established.

01Historical record

Known before Lachesis ran

The CVE and vulnerable release were selected from public history. We are not claiming novel discovery.

CVE
CVE-2022-46175
Vulnerable target
v2.2.1 (affected: < 1.0.2, and >= 2.0.0 < 2.2.2) · vulnerable
Prior knowledge
CVE-2022-46175 · v2.2.1 (affected: < 1.0.2, and >= 2.0.0 < 2.2.2) is vulnerable
then
02Lachesis run
Verified captured run

Independent re-detection

The experiment graph was built around the vulnerable json5 package (release v2.2.1) and enriched with the full pass-2/pass-3 pipeline; we did not point the tool at the CVE or at a family. Enumeration ran over the whole candidate registry (31 constructors, every family) and the object-integrity prototype-pollution sink surfaced on its own; the computed write parent[key] = value in the parser's push() came out of that. Every field under sast_output is the enumerator's own emission (candidate capsule plus sources_of value-flow cone and read_body); everything under adjudication/trace is my reading of the source and the 2.2.2 fix and is labelled as such.

Seeded inputs
None
Run timestamp
2026-08-29T00:00:00Z
Evidence artifact
json5.kuzu (built from json5-2.2.1, enriched pass2+pass3)

The commands and outputs below come from this recorded Lachesis run.

The reconstruction, in one frame

What Lachesis reconstructed.

One historical repository. Four captured queries. A complete source-to-sink argument.

Lachesis Casefiles · Unbound ComputeVerified runCVE-2022-46175
A JSON5 document whose property name is __proto__ (e.g. {"__proto__": {"polluted": true}}) passed to JSON5.parse
01Sourcekey = token.value
02Sinkparent[key] = value
03Missing guardparent[key] = value
$ lachesis sources_of → reaches → guard_dominance
json5/json5 · v2.2.1 (affected: < 1.0.2, and >= 2.0.0 < 2.2.2) · vulnerableunboundcompute.com
This was known history. Your code is not.

Lachesis follows the same evidence path through unfamiliar repositories.

The Lachesis reconstruction

Here is how the path becomes visible.

These are the recorded questions Lachesis asked of the historical vulnerable code, followed by the raw result and source location each query returned.

Repositoryjson5/json5
Revisionv2.2.1 (affected: < 1.0.2, and >= 2.0.0 < 2.2.2) · vulnerable
EngineLachesis

Captured runThis is a real replay of Lachesis over the graph we built for this case. Every command below was run against the json5.kuzu graph and every result is the output captured on that drive (via the MCP server's own call_tool). We did not point the tool at a file or a family. The hunt listed the whole taxonomy first and the prototype-pollution sink came out of that. No payload was seeded; the graph was built from the source alone and the finding was rediscovered from graph structure, so the mode is independent-redetection.

01 · load_graph01 / 05
Lachesis ran
load_graph --path json5.kuzu --profile all && candidate_census

Load the graph and list every bug family

First we load the graph and ask the tool to list every bug family it knows, with nothing chosen ahead of time. It reports all 8 domains and 31 sink constructors and says the census is complete for what the graph can observe. Object integrity (prototype pollution) is just one row here, not something we aimed at.

Captured resultCaptured · load_graph
raw captured outputload_graph
[lachesis mcp] loaded 30329 nodes; overlay: 15671 derived edges; dataflow tier: present

CANDIDATE_CENSUS
move: candidate_census
taxonomy (8):
  domain=lifecycle         title=Resource lifecycle              enumerable=True
  domain=memory            title=Memory safety                   enumerable=True
  domain=injection         title=Injection                       enumerable=True
  domain=navigation        title=Request forgery & redirection   enumerable=True
  domain=object-integrity  title=Object integrity                enumerable=True
  domain=filesystem        title=Filesystem                      enumerable=True
  domain=crypto-config     title=Cryptography & transport config enumerable=True
  domain=resource          title=Resource exhaustion             enumerable=True
constructors (31): complete_for_observable_graph=True
lib/parse.jsline 0
Code location returned for this evidence step.
The repair

A small line with a large consequence.

no __proto__/own-key guard before the computed assignment; a __proto__ key walks the prototype setter instead of creating an own property.

Before
parent[key] = value
After the patch
// use Object.defineProperty (or a null-prototype object) so the write always creates an own property (fixed in 2.2.2, commit 7774c109).
Sources & further reading

The original record.

Read the historical advisory and vulnerability record behind this reconstruction.

Open the Lachesis quickstart