The CVE archive

Old bugs.
Still worth knowing.

We run Lachesis against vulnerable historical code and show the evidence path it reconstructs. No claim of novel discovery. Just a clear record of what the tool found.

59 case files2012 earliest case2-3 added each week
All case filesNewest first
CVE-2026-70456RsyncProject/rsync

rsync: Off-by-one out-of-bounds heap write

The argv pointer array is allocated with `maxargs` elements. argc is grown through the loop -- by direct argv[argc++] stores and by the interprocedural glob_expand paths -- while the only in-loop realloc guard fires on `argc == maxargs-1`. After the loop, `argv[argc] = NULL;` writes the trailing terminator; when argc has reached maxargs, that store lands one 8-byte pointer past the allocation, corrupting adjacent heap.

Lachesis case file2026Open case ↗
CVE-2026-5295wolfSSL/wolfssl

wolfssl: Stack-based buffer overflow

An attacker-sized ASN.1 OID length is copied straight into a small fixed stack buffer with no size check, overflowing the stack.

Lachesis case file2026Open case ↗
CVE-2026-44680mikro-orm

mikro-orm: Query injection in mikro-orm QueryBuilderHelper

QueryBuilderHelper builds WHERE/identifier clauses from operator/field input; a crafted key or operator escapes the intended parameterization and is concatenated into the SQL, letting request-derived content alter the query -- SQL injection. The blind enumerator built the scope cleanly (25397 nodes / 76329 edges, 3978 sinks); class-matched candidates fired 3 time(s) (census {'injection.query.escaping': 3}) but all in sibling files, 0 in QueryBuilderHelper.ts. Sites: injection.query.escaping SchemaHelper.ts:219; injection.query.escaping MySqlSchemaHelper.ts:262; injection.query.escaping SchemaHelper.ts:217 The real defect in QueryBuilderHelper (condition/identifier processing) (QueryBuilderHelper.ts) was not surfaced. Upstream fix: the fixed release validates/parameterizes the identifier and operator handling so a crafted key cannot inject SQL.

Lachesis case file2026Open case ↗
CVE-2026-41690i18next/i18next-http-middleware

i18next-http-middleware: Prototype pollution via an unguarded dynamic property write

`setPath` is a generic `object[path] = value` walker: it splits the path into a key stack (lib/utils.js:3-4) and, for each key, does `if (!object[key]) object[key] = {}` then descends (9-10), finishing with `object[key] = newValue` (15). No step checks the key against `__proto__`/`constructor`/`prototype`, so an attacker-chosen segment writes straight through Object.prototype. The blind enumerator surfaced the prototype-key family precisely on that final dynamic write; 3.9.3 fixes it by blocking those keys in setPath and validating the lng/namespace identifiers.

Lachesis case file2026Open case ↗
CVE-2026-3308mupdf

mupdf: Integer overflow in pdf_load_image_imp stride/size computati

pdf_load_image_imp validates the image width/height/bits-per-component against SIZE_MAX, but the row stride and total buffer size are computed with int (not size_t) arithmetic, so a crafted image whose dimensions pass the guard still overflows the stride/size computation; the undersized buffer is then filled by fz_unpack_stream, which writes past its end -- a heap out-of-bounds write. Lachesis, scoped to source/pdf + source/fitz + include and enumerating blind, surfaced the class-matched obligation inside the vulnerable file (1 candidate(s) at pdf-image.c:724); the missing bound is exactly what the fix restores.

Lachesis case file2026Open case ↗
CVE-2026-25062outline/outline

outline: Arbitrary file read via path traversal in JSON import

The attachment key from the imported JSON flows straight into path.join(rootPath, key) and is read by fs.readFile with no containment check, so ../ reads arbitrary files that are then imported.

Lachesis case file2026Open case ↗
CVE-2025-64718nodeca/js-yaml

js-yaml: the merge key that reached the prototype

A YAML merge key could choose the property name written by the loader. The existing check did not stop __proto__, so the write reached the shared prototype chain.

Lachesis case file2025Open case ↗
CVE-2025-6021GNOME/libxml2

xmlBuildQName: the length math that wrapped

Two attacker-sized lengths were added in a 32-bit integer. When the total wrapped, libxml2 allocated a small buffer and copied the full data into it.

Lachesis case file2025Open case ↗
CVE-2025-68664langchain-core

langchain-core: Unsafe deserialization

The dumps() side did not escape user-controlled mapping keys beginning with 'lc', letting an attacker smuggle a crafted {"lc":1,"type":"constructor","id":[...],"kwargs":{...}} node that load()/loads() then revives -- importing a module by the attacker-influenced namespace path and instantiating the named class with attacker kwargs. Fixed 0.3.81/1.2.5 by escaping 'lc' keys on serialization and hardening the load defaults (secrets_from_env=False plus an explicit allowed-objects allowlist).

Lachesis case file2025Open case ↗
CVE-2025-68130trpc/trpc

trpc: Prototype pollution via an unguarded recursive computed-prop

`formDataToObject` builds a nested object from submitted FormData by calling `set(obj, key, value)` per entry. `set` splits the field name on `.`/`[`/`]` into a path (line 8) and, while more than one segment remains, does `obj[p] = obj[p] || {}` and recurses (lines 12-15), finishing with an `obj[p] = value` leaf write (line 20). No step checks the segment against `__proto__`/`constructor`/`prototype`, so an attacker-chosen field name writes straight through Object.prototype. The blind enumerator surfaced the prototype-key family precisely on those dynamic writes; 10.45.3 fixes it by rejecting the dangerous keys in `set`.

Lachesis case file2025Open case ↗
CVE-2025-64340PrefectHQ/fastmcp

fastmcp: OS command injection via server name in subprocess-backed in

The attacker-controlled server name is placed unvalidated into the subprocess command list and executed; on Windows the .cmd wrapper runs via cmd.exe, which interprets the metacharacters and executes injected commands.

Lachesis case file2025Open case ↗
CVE-2025-59682django

django: Partial directory traversal in archive.extract

A malicious tar/zip entry name resolves to an absolute path that shares a string prefix with the extraction target but lives outside it; target_filename's startswith check passes, and extract() writes the file into the sibling directory.

Lachesis case file2025Open case ↗
CVE-2025-59681django

django: SQL injection via column alias

A crafted column-alias key passed via **kwargs to annotate()/alias()/aggregate()/extra() flows through add_annotation into the SELECT clause; check_alias runs but its regex omits '#', which on MySQL/MariaDB opens an inline comment, injecting SQL.

Lachesis case file2025Open case ↗
CVE-2025-3277sqlite/sqlite

sqlite: Integer overflow

The separator length flows from the SQL argument into a 32-bit size multiplication that overflows, undersizing the result buffer; the copy loop then writes the true byte count and runs off the heap allocation.

Lachesis case file2025Open case ↗
CVE-2025-32434pytorch/pytorch

pytorch: Unsafe deserialization

torch.load's modern loader builds UnpicklerWrapper(data_file) -- a pickle_module.Unpickler subclass -- and calls `result = unpickler.load()` (serialization.py:1826/1848). weights_only=True was supposed to route through a restricted unpickler, but <2.6.0 allowed a bypass, so a crafted checkpoint runs arbitrary code during unpickling (GHSA-53q9-r3pm-6pq6). The blind enumerator ran the whole taxonomy over the serialization.py import closure and object-integrity.deserialize.trust DID fire -- correct family -- but its one candidate is a sibling: torch.load(io.BytesIO(b), weights_only=False) in _load_from_bytes (storage.py:520). The advisory sink, unpickler.load() on a pickle.Unpickler subclass, is not name-matched by the catalog (module-function keys only), so it went unsurfaced. 2.6.0 fixes the CVE by defaulting weights_only=True and hardening the weights_only unpickler.

Lachesis case file2025Open case ↗
CVE-2025-25285octokit/endpoint.js

endpoint.js: Regular-expression denial of service

The caller-supplied options.headers is copied into the local headers object at parse.ts:18, and on a preview /graphql request headers.accept is fed as the SUBJECT to a static regex .match at parse.ts:62. The tool's reverse value-flow cone confirms the match subject is headers.accept. The regex /[\w-]+(?=-preview)/g is unanchored on the left, so a long non-preview subject drives catastrophic backtracking -- the DoS.

Lachesis case file2025Open case ↗
CVE-2025-24928libxml2

libxml2: Stack buffer overflow via cumulative strcat with stale lengt

During validity-error reporting, attacker-controlled element/namespace names from the parsed document are strcat'd into a fixed 5000-byte stack buffer; the per-component bounds checks use a stale length that ignores the already-appended prefix, so prefix + ':' + name overflows the stack.

Lachesis case file2025Open case ↗
CVE-2025-12060Keras

Keras: Arbitrary file write via archive extraction

Keras extracts downloaded archives in extract_archive() via tarfile.extractall(), trusting a path-safety filter to keep members inside the destination. A crafted archive with a long symlink chain defeats that filter and writes outside the destination -- an arbitrary file write. Lachesis, scoping the build to keras/src/utils and enumerating blind, surfaced the path-containment obligations inside the vulnerable file; the missing robust containment is exactly what 3.12.0 restores with filter="data".

Lachesis case file2025Open case ↗
CVE-2025-1194huggingface/transformers

transformers: Regular-expression denial of service

All six tokenizer regexes are compiled in SubWordJapaneseTokenizer.__init__; the enumerator's top lead is content_repatter6 at line 233, `re.compile(r"((0|[1-9]\d*|[1-9]\d{0,2}(,\d{3})+)*億)*...")`. The tool's value-flow cone confirms the compiled Argument[0] is that literal. The three nested `(...)*` groups over overlapping alternations are the ReDoS: on a long comma/digit run with no matching currency suffix the matcher backtracks catastrophically. The official fix rewrote exactly this pattern.

Lachesis case file2025Open case ↗
CVE-2024-53900Automattic/mongoose

mongoose: NoSQL query-operator injection via unfiltered populate

getModelsMapForPopulate assembles the populate sub-query, reading the caller's `match` from options (line 181) and storing it verbatim as data.match (line 187; virtual branch at 447). In 8.8.1 nothing filters that object, so a `{ $where: '<js>' }` match executes attacker JavaScript in the MongoDB query. The blind enumerator module-scoped the build and ran the whole taxonomy, but injection.query.escaping models string escaping into a query call, not an unfiltered operator object, so it surfaced only schema-path traversal look-alikes in getSchemaTypes.js (8 candidates, e.g. line 203) and missed the real sink. 8.8.3 fixes it by throwing when match.$where is present (GHSA-m7xq-9374-9rvx).

Lachesis case file2024Open case ↗
CVE-2024-47606GStreamer/gstreamer

gstreamer: Integer underflow

qtdemux_parse_theora_extension subtracts 8 from an attacker-controlled size without first checking size >= 8, so the unsigned size underflows to a huge value used as a length, causing a heap OOB write. The blind enumerator built the scope cleanly (10912 nodes / 32883 edges, 115 sinks); class-matched candidates fired 8 time(s) (census {'memory.copy.capacity': 8, 'memory.alloc.size': 0, 'memory.index.capacity': 0}) but all in sibling files, 0 in qtdemux.c. Sites: memory.copy.capacity properties.c:63; memory.copy.capacity atoms.c:3683; memory.copy.capacity atoms.c:3611; memory.copy.capacity atoms.c:210; memory.copy.capacity qtdemux_dump.c:339; memory.copy.capacity qtdemux_lang.c:204; memory.copy.capacity atoms.c:5748; memory.copy.capacity descriptors.c:66 The real defect in qtdemux_parse_theora_extension (qtdemux.c) was not surfaced. Upstream fix: later GStreamer validates size >= 8 before the subtraction.

Lachesis case file2024Open case ↗
CVE-2024-42005django/django

django: SQL injection

Composed SQL is executed at sql/query.py:152 via cursor.execute(self.sql, params); the tool confirms self.sql flows straight into execute() with no escaping. The CVE's specific taint enters upstream where a JSONField key is used as an unescaped column alias, which then becomes part of self.sql.

Lachesis case file2024Open case ↗
CVE-2024-37052mlflow/mlflow

mlflow: Deserialization of untrusted data

MLflow deserializes scikit-learn model artifacts with pickle/cloudpickle and no trust boundary, so loading an attacker-supplied model executes arbitrary code. The SAST surfaced the correct unsafe-deserialization family across mlflow/models, but the exact sklearn pickle sink was outside the built scope; the anchored sink is a same-family open()/yaml.safe_load look-alike, and the real load path is adjudicated from the advisory.

Lachesis case file2024Open case ↗
CVE-2024-34065strapi

strapi: Open redirect in strapi admin auth redirect handling

Strapi's admin authentication flow uses a request-supplied URL as a redirect target without validating it against an allowlist, so an attacker can craft a link that sends an authenticated user to an external site -- an open redirect usable for phishing / token leakage. Lachesis, scoped to the strapi package's source subtree and enumerating blind, surfaced the class-matched obligation inside the vulnerable file (1 candidate(s) at auth.js:441); the missing bound is exactly what the fix restores.

Lachesis case file2024Open case ↗
CVE-2024-23334aio-libs/aiohttp

aiohttp: Path traversal

The URL path segment flows straight into a pathlib Path that is joined onto the served directory and opened, and the one containment check is skipped whenever follow_symlinks is enabled -- so ../ reads arbitrary files.

Lachesis case file2024Open case ↗
CVE-2023-6730huggingface/transformers

transformers: Deserialization of untrusted data

LegacyIndex._load_passages resolves passages_path from self.index_path (133), opens it (134), and calls pickle.load(passages_file) (135); the tool ties the untrusted file to that call. In v4.35.0 nothing gates the deserialization, so a malicious passages pickle runs arbitrary code on load. 4.36.0 fixes it by raising unless TRUST_REMOTE_CODE is set, right before the open/pickle.load.

Lachesis case file2023Open case ↗
CVE-2023-6293RobinBuschmann/sequelize-typescript

sequelize-typescript: Prototype pollution

assign() copies `_source[key]` onto `_target[key]` and, for nested objects, recurses through deepAssign. The final `_target[key] = targetValue` at object.ts:54 is a plain computed write; the tool confirms `key` comes verbatim from the parameter. A `__proto__` key therefore writes through to the prototype.

Lachesis case file2023Open case ↗
CVE-2023-51449gradio-app/gradio

gradio: Path traversal

The /file route hands the request path to utils.abspath, which builds a filesystem Path at utils.py:909 and returns an absolute/resolved path. Containment is supposed to be enforced by is_in_or_equal, but the 4.10.0 check is flawed, so the resolved path can escape the allowed roots.

Lachesis case file2023Open case ↗
CVE-2023-50447python-pillow/Pillow

Pillow: Code injection via eval

ImageMath.eval compiles `expression` at line 246, runs a name-allowlist scan(), then eval's the compiled code at line 259. The tool anchors the eval sink and ties it to the `expression` parameter. The scan() guard is insufficient because the caller also controls the namespace it checks against.

Lachesis case file2023Open case ↗
CVE-2023-46229langchain-ai/langchain

langchain: Server-side request forgery via unrestricted recursive URL c

RecursiveUrlLoader._get_child_links_recursive fetches `url` with requests.get (line 133), extracts the page's links, and recurses into each one (line 161). The tool ties the fetched url to the crawl parameter. In v0.0.316 there is no filter keeping those links within the start domain, so a link to an internal address is followed -- SSRF. 0.0.317 fixes it by adding prevent_outside=True and base_url filtering in extract_sub_links.

Lachesis case file2023Open case ↗
CVE-2023-38545curl

curl: Heap buffer overflow

curl's SOCKS5 handshake copies the target hostname into a fixed connection buffer to ask the proxy to resolve it. An over-long (>255-byte) name is supposed to force local resolution instead, but that decision lives in an earlier handshake state and is kept in a local variable that does not survive the non-blocking re-entry into do_SOCKS5; the memcpy that builds the remote-resolve request (socks.c:907) has no length check of its own. Lachesis surfaced that unbounded buffer-write obligation blind; the missing precondition hostname_len <= capacity is exactly what 8.4.0 enforces by erroring out.

Lachesis case file2023Open case ↗
CVE-2023-37903patriksimek/vm2

vm2: Sandbox escape to host RCE per GHSA-g644-9gfx-q4q4

The SAST reader fired the expected injection.exec (code-injection) family and anchored on new Script(...) in NodeVM.run -- vm2 compiling untrusted code, which is by design. The real CVE is one layer out: objects crossing from the sandbox back to the host inspect/error path are not fully decontextualized, so an attacker-supplied custom-inspect method executes in the host realm and reaches the host Function constructor. The tool located the code-exec primitive near the sandbox; it did not model the host<->sandbox realm boundary where the escape actually happens.

Lachesis case file2023Open case ↗
CVE-2023-36665protobufjs/protobuf.js

protobuf.js: Prototype pollution

Untrusted key segments from a parsed protobuf message flow into a property-writing helper that assigns them onto a target object without rejecting __proto__/constructor/prototype -- so an attacker-chosen key mutates Object.prototype (prototype pollution). Lachesis surfaced the object-integrity.prototype family blind (49 candidates) and, on the top candidate, witnessed the untrusted-object value flow and the absence of any prototype-key guard; the exact util.setProperty vector is adjudicated by hand.

Lachesis case file2023Open case ↗
CVE-2023-36258langchain-ai/langchain

langchain: Arbitrary code execution / code injection

The SAST reader fired the expected injection.exec (code-injection) family blind and even enumerated the real exec()/eval() sinks -- including PythonREPL.run's exec at utilities/python.py:19, the exact sink PALChain reaches. But its top-ranked, selected candidate is a string.Template constant in the Nebula graph adapter (a hardcoded nGQL query skeleton with an empty provenance cone), not the PALChain code-execution path. The real CVE is one construct over: PALChain._call takes Python source directly from the LLM (base.py:63) and runs it through repl.run -> exec (base.py:67 -> python.py:19) with no PALValidation, so a prompt-injected model output executes arbitrary Python on the host. The 0.0.236 fix adds an AST allow/deny-list and a process timeout.

Lachesis case file2023Open case ↗
CVE-2022-46175json5/json5

json5: Prototype pollution

The parsed property name flows straight from the lexer into a plain computed assignment parent[key] = value with no __proto__ guard -- so a __proto__ key mutates Object.prototype (prototype pollution).

Lachesis case file2022Open case ↗
CVE-2022-37434zlib

zlib: Heap buffer overflow

zlib's inflate() copies a gzip header's variable-length extra field into a caller-owned buffer (state->head->extra, capacity extra_max). The copy is clamped by `extra_max - len`, but len -- the running write offset -- is never required to stay below extra_max, and it accumulates across the multiple inflate() calls a chunked stream produces. Once len >= extra_max the unsigned subtraction wraps and the clamp becomes enormous, so the zmemcpy overruns the buffer. Lachesis surfaced the write obligation at inflate.c:769 blind; the missing precondition len < extra_max is exactly what 1.2.13 adds.

Lachesis case file2022Open case ↗
CVE-2022-3602OpenSSL

OpenSSL: Off-by-one stack buffer overflow in X.509 name-constraint pu

OpenSSL decodes punycode IDN labels while checking X.509 name constraints, writing the decoded label into a fixed-size buffer. The capacity bound on that write is off by one, so a crafted label overflows the buffer by one element during certificate verification. Lachesis, scoping the build to punycode.c and enumerating blind, surfaced the buffer-write obligations inside the vulnerable file (3 memory.copy.capacity candidates at 187/276/297); the missing sound capacity bound is exactly what 3.0.7 restores.

Lachesis case file2022Open case ↗
CVE-2022-33987sindresorhus/got

got: SSRF via redirect to a UNIX-domain socket

got rewrites an initial `unix:` URL into an http-over-socket form and, crucially, only does its scheme handling on the first request (source/core/index.ts:1673-1676). On a redirect it reads `Location` (2096), builds `redirectUrl` (2099) and sets it as `options.url` (2127) with no scheme re-check; the followed request lands in `_createCacheableRequest`, whose `url.hostname === 'unix'` branch connects to `socketPath` (2347). The blind enumerator surfaced the redirect-destination sink precisely on that socket assignment; 11.8.5 fixes it by rejecting redirects to UNIX-socket URLs.

Lachesis case file2022Open case ↗
CVE-2022-29078mde/ejs

ejs: Server-side template injection / code injection , ref.

An attacker-controlled template OPTION NAME is concatenated verbatim into the JavaScript source that ejs generates for the template, and that source is compiled and executed via the Function constructor -- so a non-identifier option name injects arbitrary JS that runs on the server at compile time. 3.1.6 validates none of these names; 3.1.7 requires each to be a bare JS identifier.

Lachesis case file2022Open case ↗
CVE-2022-25883npm/node-semver

node-semver: Regular-expression denial of service

Every semver token regex is compiled at re.js:16 via `new RegExp(value, ...)` inside createToken; `value` is the token's source string. The tool's value-flow cone confirms the compiled first argument is that source. The vulnerable token sources embed unbounded overlapping quantifiers, which is the ReDoS.

Lachesis case file2022Open case ↗
CVE-2022-24999ljharb/qs

qs: Prototype pollution

qs parses `a[__proto__][polluted]=1` into a key chain ['a','[__proto__]','[polluted]'], and parseObject() reverses over it building nested objects: for each segment it computes cleanRoot (stripping the surrounding brackets) and writes `obj[cleanRoot] = leaf`. With cleanRoot = '__proto__', the write lands on Object.prototype. The tool witnesses the `chain -> chain[i]/root -> cleanRoot -> obj[cleanRoot] = leaf` flow. In v6.10.2 no __proto__ filter guards this; 6.10.3 adds one.

Lachesis case file2022Open case ↗
CVE-2022-21797joblib/joblib

joblib: Code injection via eval

Parallel.__call__ computes the dispatch batch size by calling `eval(pre_dispatch)`; the tool confirms the eval argument is the pre_dispatch attribute carried from the constructor. eval on a caller-supplied string is direct code injection.

Lachesis case file2022Open case ↗
CVE-2022-0577scrapy/scrapy

scrapy: Exposure of sensitive information across domains on redirect

The SAST reader fired the navigation (request forgery & redirection) family blind and anchored on open(self.statefn) in the SpiderState extension -- a local pickle state-file read the Atropos URL-opener model mislabels as an SSRF sink. The real CVE is in a different component: RedirectMiddleware rebuilds the follow-up request with request.replace(...) that copies cookies and the Cookie header without comparing the source and redirect domains, so a cross-domain redirect leaks the victim's cookies to the new domain. The tool located an adjacent navigation-domain sink; it did not model the redirect request reconstruction or the cross-domain trust boundary where the leak actually happens, and the CVE's class (cross-domain sensitive-header exposure / incorrect authorization) has no sink family in the catalog.

Lachesis case file2022Open case ↗
CVE-2022-0235node-fetch/node-fetch

node-fetch: Sensitive-header leak / SSRF on cross-origin redirect

On a redirect, fetch() takes `location = headers.get('Location')` (line 109), resolves `locationURL` (line 112), builds `requestOpts` with `headers: new Headers(request.headers)` -- every original header, including Authorization and Cookie (line 148) -- and re-issues `fetch(new Request(locationURL, requestOpts))` (line 175). With no cross-origin check, a redirect to a foreign host leaks those credentials. The tool anchors the destination sink (fetch to an attacker-chosen URL); 2.6.7 fixes it by stripping sensitive headers on cross-origin redirects.

Lachesis case file2022Open case ↗
CVE-2021-3807chalk/ansi-regex

ansi-regex: Regular-expression denial of service

The constant regex pattern is assembled at index.js:4 and handed to `new RegExp(pattern,'g')` at index.js:9. The tool's reverse value-flow cone confirms the sink argument is exactly that pattern. The pattern's `(?:;[a-zA-Z\d]*)*` group is the ReDoS: on a long ';'-run with no closing byte the matcher backtracks catastrophically.

Lachesis case file2021Open case ↗
CVE-2021-3634libssh

libssh: SSH rekey digest size mismatch

libssh derives the session id / secret hash from the negotiated key-exchange digest. On a REKEY that negotiates a KEX with a different digest size than the original exchange, the code reuses length assumptions from the first exchange, so secret_hash and session_id lengths mismatch the actual digest -- copies keyed on the wrong length read or write past the allocated hash buffer. Lachesis, scoped to src + include and enumerating blind, surfaced the class-matched obligation inside the vulnerable file (2 candidate(s) at kex.c:732, kex.c:1198); the missing bound is exactly what the fix restores.

Lachesis case file2021Open case ↗
CVE-2021-23727celery/celery

celery: Stored OS command / code injection via untrusted backend res

The tool's blind hunt fired the expected injection.exec family but anchored its top lead on the worker self-reload os.execv, which is not attacker-influenced (empty provenance cone). The real CVE is stored command injection via exception-class reconstruction in celery/backends/base.py: untrusted result-backend metadata is deserialized and the named exception class is imported and instantiated, so a poisoned backend yields code execution when a client reads the result. Fixed in 5.2.2 by restricting reconstruction to genuine Exception subclasses.

Lachesis case file2021Open case ↗
CVE-2021-23434mariocasciaro/object-path

object-path: Prototype pollution

objectPath.set(obj, path, value) normalizes a string `path` via path.split('.').map(getKey), then recurses: currentPath = path[0], and on the final component writes `obj[currentPath] = value`. In v0.11.0 there is no guard rejecting __proto__/constructor/prototype, so a path of '__proto__.polluted' resolves currentPath to '__proto__', walks into Object.prototype, and assigns a shared property. The tool witnesses the `path -> path[0] -> currentPath -> obj[currentPath] = value` flow. 0.11.6 adds the guard.

Lachesis case file2021Open case ↗
CVE-2021-23369handlebars-lang/handlebars.js

handlebars.js: Code injection / template-compilation RCE

The tool localised the injection family to the handlebars compiler and its lower-ranked leads sit on the exact compile pipeline, but the top anchor is a benign regex .exec in the whitespace pass, not the code-generation line. My reading of the source and the 4.7.7 fix places the real RCE in depthedLookup, where an unescaped property name is concatenated into a JS string literal in the generated template function.

Lachesis case file2021Open case ↗
CVE-2021-21315sebhildebrandt/systeminformation

systeminformation: Command injection

inetChecksite sanitizes `url` into `urlSanitized` via a per-character allowlist (util.sanitizeShellString), then builds `args = ' -I --connect-timeout 5 -m 5 ' + urlSanitized + ' 2>/dev/null | head -n 1 | cut -d " " -f2'`, `cmd = 'curl'`, and calls `exec(cmd + args)`. child_process.exec runs the string through a shell, so any shell token surviving the sanitizer is interpreted. The tool witnesses the full `url -> sanitizeShellString -> urlSanitized -> args -> cmd + args -> exec` value-flow. In 5.3.0 the allowlist admitted spaces; 5.3.1 removes them.

Lachesis case file2021Open case ↗
CVE-2020-16846saltstack/salt

salt: OS command injection

The request-body low-state flows straight into the salt-api client dispatch self.api.run(chunk) with no escaping, and downstream the salt-ssh client interpolates request-derived fields into a shell command run with shell=True -- so shell metacharacters in the request execute arbitrary OS commands. Lachesis anchored the injection.exec sink at the netapi dispatch (self.api.run) and witnessed the source (cherrypy.request.lowstate) and the missing escaping guard; the downstream shell=True Popen is in a module outside the built scope and is adjudicated.

Lachesis case file2020Open case ↗
CVE-2020-15999freetype/freetype

freetype: Integer truncation in Load_SBit_Png

Load_SBit_Png stores a PNG's 32-bit width/height into narrower fields used to compute the glyph bitmap size, so dimensions above 0xFFFF truncate and undersize the buffer; libpng then writes the full-size image into it -- a heap OOB write. The blind enumerator built the scope cleanly (1957 nodes / 15737 edges, 8 sinks); class-matched candidates fired 8 time(s) (census {'memory.copy.capacity': 8, 'memory.alloc.size': 0, 'memory.index.capacity': 0}) but all in sibling files, 0 in pngshim.c. Sites: memory.copy.capacity sfdriver.c:967; memory.copy.capacity sfdriver.c:935; memory.copy.capacity ttcolr.c:346; memory.copy.capacity ttcolr.c:303; memory.copy.capacity sfwoff.c:357; memory.copy.capacity sfwoff2.c:242; memory.copy.capacity ttbdf.c:69; memory.copy.capacity ttbdf.c:132 The real defect in Load_SBit_Png (pngshim.c) was not surfaced. Upstream fix: 2.10.4 checks the PNG dimensions against the expected/target metrics before allocation and rejects images whose width/height do not fit, closing the truncation.

Lachesis case file2020Open case ↗
CVE-2020-14343yaml/pyyaml

pyyaml: Deserialization of untrusted data

full_load delegates to `load(stream, FullLoader)`; the tool ties the untrusted `stream` argument to that call. FullLoader was assumed to strip unsafe tags, but in 5.3.1 it still constructs arbitrary objects via the python/object/* tags, so the deserialization is a code-execution sink.

Lachesis case file2020Open case ↗
CVE-2020-11652saltstack/salt

salt: Path traversal

A caller-controlled wheel argument (file_name) is concatenated onto a base directory and written with no canonicalisation or containment check, so ../ segments write arbitrary files on the salt-master. The SAST tool surfaced the same absent-containment defect blind on the os.makedirs destination in this exact function, in the exact file the CVE fix patched.

Lachesis case file2020Open case ↗
CVE-2019-7164sqlalchemy/sqlalchemy

sqlalchemy: SQL injection

The tool fired the SQL-injection family (injection.query.escaping, CWE-89) blind and reported an unguarded execute() sink, matching the CVE class. The precise CVE, however, is implicit string->text() coercion in the order_by/group_by compilation path: a bare string argument is emitted verbatim into the SQL and never validated as a real column label, so untrusted ordering text injects SQL. The fix (1.3.0b3, commit 30307c4) makes unresolved string coercion raise instead of rendering raw SQL.

Lachesis case file2019Open case ↗
CVE-2019-10748sequelize/sequelize

sequelize: SQL injection via unescaped JSON-path-key interpolation in t

jsonPathExtractionQuery turns a JSON column + a path (derived from the where-clause JSON key) into a dialect SQL fragment. In v5.8.10 the MySQL branch interpolates the joined path inside single quotes (`(col->>'${pathStr}')`, line 1057) and the MariaDB branch does the same with no quoting (`json_unquote(json_extract(col,'${pathStr}'))`, line 1061); neither SQL-escapes the key. An attacker-chosen JSON key thus injects into the SQL. The blind enumerator module-scoped the build and ran the whole taxonomy, but injection.query.escaping models query-execution call sites, not a helper that returns a SQL substring, so it surfaced only an unrelated Array.find look-alike (line 1884) and missed the real sink. The fix (#11089) wraps the path in this.escape on both branches.

Lachesis case file2019Open case ↗
CVE-2019-10744lodash/lodash

lodash: Prototype pollution

baseMergeDeep copies each source key onto the target and recurses for nested objects, reading the current value at each key through `safeGet`. In 4.17.11 safeGet rejects only the literal `__proto__`, so `safeGet(obj,'constructor')` returns the constructor function and the merge recurses into its `.prototype`; the terminal `object[key] = value` at baseAssignValue:2559 then writes onto Object.prototype. The tool confirms `key` at the write comes verbatim from the parameter (VALUE_FLOWS_TO 2550 -> 2559). 4.17.12 adds the missing `constructor` guard to safeGet.

Lachesis case file2019Open case ↗
CVE-2017-5941luin/serialize

serialize: Code injection via eval / unsafe deserialization

serialize.unserialize(str) does `obj = JSON.parse(str)` then walks the object; for each string value it checks `obj[key].indexOf('_$$ND_FUNC$$_') === 0` and, if so, runs `eval('(' + obj[key].substring(marker.length) + ')')`. A payload such as `{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('id')}()"}` therefore executes on unserialize. The tool witnesses the `obj[key].substring -> '('+...+')' -> eval` argument construction. No version fixes this.

Lachesis case file2017Open case ↗
CVE-2012-0809sudo

sudo: Format-string vulnerability in sudo_debug

sudo_debug() builds a diagnostic message and passes it, together with the program name derived from argv[0], to a printf-family call in a way that uses attacker-influenced text as the format string. Because sudo is setuid root and argv[0] is fully attacker-controlled, format specifiers in the program name are interpreted, corrupting memory or crashing the process. Lachesis, scoped to src + lib + include and enumerating blind, surfaced the class-matched obligation inside the vulnerable file (2 candidate(s) at sudo.c:1219, sudo.c:401); the missing bound is exactly what the fix restores.

Lachesis case file2012Open case ↗
Publishing standardOnly complete case files appear here.

Every published case includes the final Lachesis evidence and its historical sources.