ghsa-rfx3-98h7-v3xp
CVSS 9.1 osv_nugetSeveral code paths in Marten's LINQ provider and tenant-management internals interpolated a runtime, potentially attacker-influenced value into generated SQL as a single-quoted string literal without escaping or parameterization. A value containing a single quote (`'`) could break out of the literal and inject arbitrary SQL. The primary vector is a `Dictionary<,>` indexer **key** used in a `Where` filter — `Where(x => x.Attributes[key] == v)` — a common "filter by attribute name" / EAV pattern. It is confirmed with an executed proof-of-concept and yields filter / multi-tenant authorization bypass (returning other tenants' rows) and blind data exfiltration. Where the application permits `;`-batched statements (Npgsql default), data modification is also possible. ## Affected sinks - `Linq/Members/Dictionaries/DictionaryItemMember.cs` — dictionary indexer key (primary, confirmed PoC) - `Linq/Members/Dictionaries/DictionaryContainsKeyFilter.cs` — `Dictionary.ContainsKey(key)` (Newtonsoft serializer only; System.Text.Json escapes the quote) - `Linq/Parsing/SelectParser.cs` — a constant string projected through `Select(x => new { L = runtimeString })` - `Internal/Operations/DeleteAllForTenant.cs` — tenant id reaching per-tenant projection teardown via `IEventStore.DeleteProjectionProgressAsync` (an in-code comment wrongly claimed the value was validated) - `Schema/DatabaseScopedTenantPartitions.cs` — tenant id inlined into `FOR VALUES IN ('...')` partition DDL - `Events/Daemon/Internals/EventLoader.cs` (defense-in-depth) — per-tenant partition-pruning literal ## Proof of concept (primary vector) Benign: `x.Attributes["nonexistent-key"] == "v"` returns 0 rows. Attack: `x.Attributes["nonexistent' = '' or 1=1 --"] == "v"` returns all rows. Generated SQL: ``` select d.data from public.mt_doc_doc as d where d.data -> 'Attributes' ->> 'x' or 1=1 --' = :p0; ``` ## Fix Each sink now escapes embedded single quotes (`.Replace("'", "''")`, mirroring the existing `Ordering.BuildNgramRankExpression`) or binds the value as a parameter (`DeleteAllForTenant`). Where a literal is retained for partition pruning, escaping preserves plan-time pruning while closing the injection. Regression tests lock down each vector. ## Workarounds Until upgrading: do not pass untrusted input as a dictionary indexer key / `ContainsKey` argument / `Select` constant, nor as a tenant id into projection teardown or provisioning; and disable multi-statement command batching to limit blast radius. ## Credit Reported privately and responsibly by an external security researcher with an executed PoC. Additional sinks found during the follow-up LINQ-wide audit.
- Published
- unknown
- Last Modified
- unknown
CVSS details not available.
No product information available.
- https://github.com/JasperFx/marten/security/advisories/GHSA-rfx3-98h7-v3xp
- https://nvd.nist.gov/vuln/detail/CVE-2026-75513
- https://github.com/JasperFx/marten/pull/4911
- https://github.com/JasperFx/marten/commit/61882d0424854cb48703f08bdb246894ac576bed
- https://github.com/JasperFx/marten
- https://github.com/JasperFx/marten/releases/tag/9.13.0
No linked vulnerabilities found.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "<= 9.12.0",
"source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-rfx3-98h7-v3xp/GHSA-rfx3-98h7-v3xp.json"
},
"package": {
"ecosystem": "NuGet",
"name": "Marten",
"purl": "pkg:nuget/Marten"
},
"ranges": [
{
"events": [
{
"introduced": "7.0.0"
},
{
"fixed": "9.13.0"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"7.0.0",
"7.1.0",
"7.1.1",
"7.10.0",
"7.10.1",
"7.11.0",
"7.12.0",
"7.13.0",
"7.13.1",
"7.14.0",
"7.15.0",
"7.16.0",
"7.17.0",
"7.17.1",
"7.18.0",
"7.19.0",
"7.19.1",
"7.2.0",
"7.20.0",
"7.20.1",
"7.20.2",
"7.21.0",
"7.21.1",
"7.22.0",
"7.23.0",
"7.23.1",
"7.24.0",
"7.25.0",
"7.25.1",
"7.25.2",
"7.26.0",
"7.26.1",
"7.26.2",
"7.26.3",
"7.26.4",
"7.26.5",
"7.26.6",
"7.27.0",
"7.28.0",
"7.28.1",
"7.28.2",
"7.29.0",
"7.3.0",
"7.3.1",
"7.30.0",
"7.30.1",
"7.30.2",
"7.30.3",
"7.31.0",
"7.31.1",
"7.31.2",
"7.31.3",
"7.32.0",
"7.33.0",
"7.33.1",
"7.33.2",
"7.33.3",
"7.34.0",
"7.34.1",
"7.35.0",
"7.35.1",
"7.35.2",
"7.35.3",
"7.36.0",
"7.37.0",
"7.37.1",
"7.37.2",
"7.37.3",
"7.38.0",
"7.38.1",
"7.39.0",
"7.39.1",
"7.39.2",
"7.39.3",
"7.39.5",
"7.39.6",
"7.4.0",
"7.40.0",
"7.40.1",
"7.40.2",
"7.40.3",
"7.40.4",
"7.40.5",
"7.5.0",
"7.6.0",
"7.7.0",
"7.8.0",
"7.9.0",
"8.0.0",
"8.0.0-alpha-2",
"8.0.0-alpha-3",
"8.0.0-alpha-5",
"8.0.0-alpha-6-",
"8.0.0-beta-1",
"8.0.0-beta-2",
"8.0.0-rc-1",
"8.0.0-rc-2",
"8.0.1",
"8.1.0",
"8.1.2",
"8.10.0",
"8.10.1",
"8.11.0",
"8.12.0",
"8.13.0",
"8.13.1",
"8.13.2",
"8.13.3",
"8.14.0",
"8.15.0",
"8.15.1",
"8.15.2",
"8.15.3",
"8.15.4",
"8.16.0",
"8.16.1",
"8.16.2",
"8.16.3",
"8.16.4",
"8.17.0",
"8.18.0",
"8.18.1",
"8.18.2",
"8.18.3",
"8.19.0",
"8.2.0",
"8.2.1",
"8.20.0",
"8.21.0",
"8.22.0",
"8.22.1",
"8.22.2",
"8.23.0",
"8.24.0",
"8.25.0",
"8.26.0",
"8.26.1",
"8.26.2",
"8.27.0",
"8.28.0",
"8.29.0",
"8.29.1",
"8.29.2",
"8.29.3",
"8.3.0",
"8.3.1",
"8.3.2",
"8.3.3",
"8.30.0",
"8.30.1",
"8.31.0",
"8.32.0",
"8.32.1",
"8.33.0",
"8.34.0",
"8.34.1",
"8.34.2",
"8.35.0",
"8.36.0",
"8.37.0",
"8.37.1",
"8.37.2",
"8.37.3",
"8.37.4",
"8.38.0",
"8.38.1",
"8.4.0",
"8.5.0",
"8.6.0",
"8.7.0",
"8.8.0",
"8.8.1",
"8.8.2",
"8.9.0",
"8.9.0-channels-alpha-1",
"8.9.0-channels-alpha-2",
"9.0.0",
"9.0.1",
"9.0.2",
"9.10.0",
"9.11.0",
"9.12.0",
"9.2.0",
"9.2.1",
"9.3.0",
"9.3.1",
"9.3.2",
"9.3.4",
"9.3.5",
"9.4.0",
"9.5.0",
"9.5.1",
"9.5.2",
"9.5.3",
"9.6.0",
"9.7.0",
"9.7.1",
"9.7.2",
"9.7.3",
"9.7.4",
"9.7.5",
"9.7.6",
"9.8.0",
"9.8.1",
"9.8.2",
"9.9.0",
"9.9.1"
]
}
],
"aliases": [
"CVE-2026-75513"
],
"database_specific": {
"cwe_ids": [
"CWE-89"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-17T14:51:41Z",
"nvd_published_at": "2026-09-16T21:17:13Z",
"severity": "CRITICAL"
},
"details": "Several code paths in Marten's LINQ provider and tenant-management internals interpolated a runtime, potentially attacker-influenced value into generated SQL as a single-quoted string literal without escaping or parameterization. A value containing a single quote (`'`) could break out of the literal and inject arbitrary SQL.\n\nThe primary vector is a `Dictionary<,>` indexer **key** used in a `Where` filter — `Where(x => x.Attributes[key] == v)` — a common \"filter by attribute name\" / EAV pattern. It is confirmed with an executed proof-of-concept and yields filter / multi-tenant authorization bypass (returning other tenants' rows) and blind data exfiltration. Where the application permits `;`-batched statements (Npgsql default), data modification is also possible.\n\n## Affected sinks\n\n- `Linq/Members/Dictionaries/DictionaryItemMember.cs` — dictionary indexer key (primary, confirmed PoC)\n- `Linq/Members/Dictionaries/DictionaryContainsKeyFilter.cs` — `Dictionary.ContainsKey(key)` (Newtonsoft serializer only; System.Text.Json escapes the quote)\n- `Linq/Parsing/SelectParser.cs` — a constant string projected through `Select(x => new { L = runtimeString })`\n- `Internal/Operations/DeleteAllForTenant.cs` — tenant id reaching per-tenant projection teardown via `IEventStore.DeleteProjectionProgressAsync` (an in-code comment wrongly claimed the value was validated)\n- `Schema/DatabaseScopedTenantPartitions.cs` — tenant id inlined into `FOR VALUES IN ('...')` partition DDL\n- `Events/Daemon/Internals/EventLoader.cs` (defense-in-depth) — per-tenant partition-pruning literal\n\n## Proof of concept (primary vector)\n\nBenign: `x.Attributes[\"nonexistent-key\"] == \"v\"` returns 0 rows. Attack: `x.Attributes[\"nonexistent' = '' or 1=1 --\"] == \"v\"` returns all rows. Generated SQL:\n\n```\nselect d.data from public.mt_doc_doc as d where d.data -> 'Attributes' ->> 'x' or 1=1 --' = :p0;\n```\n\n## Fix\n\nEach sink now escapes embedded single quotes (`.Replace(\"'\", \"''\")`, mirroring the existing `Ordering.BuildNgramRankExpression`) or binds the value as a parameter (`DeleteAllForTenant`). Where a literal is retained for partition pruning, escaping preserves plan-time pruning while closing the injection. Regression tests lock down each vector.\n\n## Workarounds\n\nUntil upgrading: do not pass untrusted input as a dictionary indexer key / `ContainsKey` argument / `Select` constant, nor as a tenant id into projection teardown or provisioning; and disable multi-statement command batching to limit blast radius.\n\n## Credit\n\nReported privately and responsibly by an external security researcher with an executed PoC. Additional sinks found during the follow-up LINQ-wide audit.",
"id": "GHSA-rfx3-98h7-v3xp",
"modified": "2026-09-17T15:00:07.273360513Z",
"published": "2026-09-17T14:51:41Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/JasperFx/marten/security/advisories/GHSA-rfx3-98h7-v3xp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-75513"
},
{
"type": "WEB",
"url": "https://github.com/JasperFx/marten/pull/4911"
},
{
"type": "WEB",
"url": "https://github.com/JasperFx/marten/commit/61882d0424854cb48703f08bdb246894ac576bed"
},
{
"type": "PACKAGE",
"url": "https://github.com/JasperFx/marten"
},
{
"type": "WEB",
"url": "https://github.com/JasperFx/marten/releases/tag/9.13.0"
}
],
"schema_version": "1.9.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:L",
"type": "CVSS_V3"
}
],
"summary": "Marten's LINQ provider has SQL injection via unescaped string literals"
}