GHSA-2MFG-CC43-9PCJ
Vulnerability from github – Published: 2026-06-17 18:39 – Updated: 2026-06-17 18:39Summary
The MariaDB and pgvector embedding stores build metadata-filter SQL by string-concatenating
filter keys (and, in MariaDB, string values) directly into the query without adequate
escaping. A crafted metadata key in EmbeddingSearchRequest.filter() can break out of its SQL
context and inject arbitrary SQL into the statements executed by the stores' search and
removeAll(Filter) operations.
Details
pgvector — JSON mode (default, COMBINED_JSON / COMBINED_JSONB). JSONFilterMapper
places the key inside a single-quoted SQL literal (the JSON key of the ->> operator) with no
escaping:
(metadata->>'<key>')::text
A key containing a single quote breaks out, e.g.
metadataKey("')::text IS NOT NULL OR pg_sleep(1) IS NOT NULL --") injects a live pg_sleep(1)
(observable as a delay; exploitable for blind data extraction).
pgvector — column mode (COLUMN_PER_KEY). ColumnFilterMapper used the key as a bare,
unquoted, unvalidated SQL identifier (<key>::<type>), so a key such as 1=1 OR true --
injects directly.
MariaDB — JSON mode (default). JSONFilterMapper placed the key inside the JSON path literal
'$.<key>' unescaped (same break-out mechanism). Additionally, MariaDbFilterMapper.formatValue()
escaped ' but not \; because MariaDB treats backslash as an escape character by default, a
string value ending in a backslash could also break out of its literal.
MariaDB — column mode (COLUMN_PER_KEY). ColumnFilterMapper fell back to the raw,
unescaped key when the driver could not quote it as an identifier (e.g. a
character).
The filter key is the runtime injection surface; both stores' search() (including pgvector's
HYBRID mode) and removeAll(Filter) are affected. Add/upsert operations a
parameterized and not affected.
Impact
Applications that allow attacker-influenced metadata filter keys (e.g. use LLM-generated filters) to reach these stores are exposed to SQL injection: blind data exfiltration, denial of service via sleep functions, and — through `remove deletion of arbitrary rows. Applications using only hard-coded, developer-defined filter keys are not reachable.
Patches
Fixed in langchain4j-mariadb and langchain4j-pgvector 1.16.3-beta26:
- JSON filter keys are escaped before being embedded in the SQL string lit
quotes doubled, correct for PostgreSQL standard_conforming_strings = on; MariaDB: backslash
and single quote).
- MariaDB string values escape both \ and '.
- Column-mode keys are validated/quoted as identifiers and rejected when u
concatenated as raw SQL.
Workarounds
- Do not pass untrusted input as metadata filter keys.
- Restrict filter keys to a known allow-list at the application layer.
References
- pgvector:
JSONFilterMapper,ColumnFilterMapper - MariaDB:
JSONFilterMapper,MariaDbFilterMapper,ColumnFilterMapper
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.2.0-beta8"
},
"package": {
"ecosystem": "Maven",
"name": "dev.langchain4j:langchain4j-mariadb"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.2.1-beta8"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.5.0-beta11"
},
"package": {
"ecosystem": "Maven",
"name": "dev.langchain4j:langchain4j-mariadb"
},
"ranges": [
{
"events": [
{
"introduced": "1.3.0-beta9"
},
{
"fixed": "1.5.1-beta11"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.11.7-beta19"
},
"package": {
"ecosystem": "Maven",
"name": "dev.langchain4j:langchain4j-mariadb"
},
"ranges": [
{
"events": [
{
"introduced": "1.6.0-beta12"
},
{
"fixed": "1.11.8-beta19"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.16.2-beta26"
},
"package": {
"ecosystem": "Maven",
"name": "dev.langchain4j:langchain4j-mariadb"
},
"ranges": [
{
"events": [
{
"introduced": "1.12.1-beta21"
},
{
"fixed": "1.16.3-beta26"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.2.0-beta8"
},
"package": {
"ecosystem": "Maven",
"name": "dev.langchain4j:langchain4j-pgvector"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.2.1-beta8"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.5.0-beta11"
},
"package": {
"ecosystem": "Maven",
"name": "dev.langchain4j:langchain4j-pgvector"
},
"ranges": [
{
"events": [
{
"introduced": "1.3.0-beta9"
},
{
"fixed": "1.5.1-beta11"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.11.7-beta19"
},
"package": {
"ecosystem": "Maven",
"name": "dev.langchain4j:langchain4j-pgvector"
},
"ranges": [
{
"events": [
{
"introduced": "1.6.0-beta12"
},
{
"fixed": "1.11.8-beta19"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.16.2-beta26"
},
"package": {
"ecosystem": "Maven",
"name": "dev.langchain4j:langchain4j-pgvector"
},
"ranges": [
{
"events": [
{
"introduced": "1.12.1-beta21"
},
{
"fixed": "1.16.3-beta26"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-55405"
],
"database_specific": {
"cwe_ids": [
"CWE-89"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-17T18:39:56Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\nThe MariaDB and pgvector embedding stores build metadata-filter SQL by string-concatenating\nfilter **keys** (and, in MariaDB, string **values**) directly into the query without adequate\nescaping. A crafted metadata key in `EmbeddingSearchRequest.filter()` can break out of its SQL\ncontext and inject arbitrary SQL into the statements executed by the stores\u0027 search and\n`removeAll(Filter)` operations.\n\n### Details\n**pgvector \u2014 JSON mode (default, `COMBINED_JSON` / `COMBINED_JSONB`).** `JSONFilterMapper`\nplaces the key inside a single-quoted SQL literal (the JSON key of the `-\u003e\u003e` operator) with no\nescaping:\n\n (metadata-\u003e\u003e\u0027\u003ckey\u003e\u0027)::text\n\nA key containing a single quote breaks out, e.g.\n`metadataKey(\"\u0027)::text IS NOT NULL OR pg_sleep(1) IS NOT NULL --\")` injects a live `pg_sleep(1)`\n(observable as a delay; exploitable for blind data extraction).\n\n**pgvector \u2014 column mode (`COLUMN_PER_KEY`).** `ColumnFilterMapper` used the key as a bare,\nunquoted, unvalidated SQL identifier (`\u003ckey\u003e::\u003ctype\u003e`), so a key such as `1=1 OR true --`\ninjects directly.\n\n**MariaDB \u2014 JSON mode (default).** `JSONFilterMapper` placed the key inside the JSON path literal\n`\u0027$.\u003ckey\u003e\u0027` unescaped (same break-out mechanism). Additionally, `MariaDbFilterMapper.formatValue()`\nescaped `\u0027` but not `\\`; because MariaDB treats backslash as an escape character by default, a\nstring value ending in a backslash could also break out of its literal.\n\n**MariaDB \u2014 column mode (`COLUMN_PER_KEY`).** `ColumnFilterMapper` fell back to the raw,\nunescaped key when the driver could not quote it as an identifier (e.g. a\ncharacter).\n\nThe filter key is the runtime injection surface; both stores\u0027 `search()` (including pgvector\u0027s\nHYBRID mode) and `removeAll(Filter)` are affected. Add/upsert operations a\nparameterized and not affected.\n\n### Impact\nApplications that allow attacker-influenced metadata filter keys (e.g. use\nLLM-generated filters) to reach these stores are exposed to SQL injection: blind data\nexfiltration, denial of service via sleep functions, and \u2014 through `remove\ndeletion of arbitrary rows. Applications using only hard-coded, developer-defined filter keys\nare not reachable.\n\n### Patches\nFixed in `langchain4j-mariadb` and `langchain4j-pgvector` 1.16.3-beta26:\n- JSON filter keys are escaped before being embedded in the SQL string lit\n quotes doubled, correct for PostgreSQL `standard_conforming_strings = on`; MariaDB: backslash\n and single quote).\n- MariaDB string values escape both `\\` and `\u0027`.\n- Column-mode keys are validated/quoted as identifiers and rejected when u\n concatenated as raw SQL.\n\n### Workarounds\n- Do not pass untrusted input as metadata filter keys.\n- Restrict filter keys to a known allow-list at the application layer.\n\n### References\n- pgvector: `JSONFilterMapper`, `ColumnFilterMapper`\n- MariaDB: `JSONFilterMapper`, `MariaDbFilterMapper`, `ColumnFilterMapper`",
"id": "GHSA-2mfg-cc43-9pcj",
"modified": "2026-06-17T18:39:56Z",
"published": "2026-06-17T18:39:56Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/langchain4j/langchain4j/security/advisories/GHSA-2mfg-cc43-9pcj"
},
{
"type": "PACKAGE",
"url": "https://github.com/langchain4j/langchain4j"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L",
"type": "CVSS_V3"
}
],
"summary": "LangChain4j: SQL injection via metadata filters in langchain4j-mariadb and langchain4j-pgvector"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.