CVE-2026-44635
Kysely: JSON-path traversal injection via unsanitized path-leg metacharacters in `JSONPathBuilder.key()` / `.at()`
Kysely is a type-safe TypeScript SQL query builder. From 0.26.0 to 0.28.16, DefaultQueryCompiler.visitJSONPathLeg does not escape JSON-path metacharacters (., [, ], *, **, ?). When attacker-controlled input flows into eb.ref(col, '->$').key(input) or .at(input) — including type-safe code where the JSON column is shaped like Record<string, T> so K extends string is the inferred type — every dot becomes a path-leg separator, letting an attacker traverse from the intended key into sibling and child fields the developer never meant to expose. The result is read access (and, in update statements, write access) to JSON sub-fields outside the intended scope across MySQL, PostgreSQL ->$/->>$, and SQLite. This vulnerability is fixed in 0.28.17.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.5
Kysely: JSON-path traversal injection via unsanitized path-leg metacharacters in `JSONPathBuilder.key()` / `.at()`
Kysely is a type-safe TypeScript SQL query builder. From 0.26.0 to 0.28.16, DefaultQueryCompiler.visitJSONPathLeg does not escape JSON-path metacharacters (., [, ], *, **, ?). When attacker-controlled input flows into eb.ref(col, '->$').key(input) or .at(input) — including type-safe code where the JSON column is shaped like Record<string, T> so K extends string is the inferred type — every dot becomes a path-leg separator, letting an attacker traverse from the intended key into sibling and child fields the developer never meant to expose. The result is read access (and, in update statements, write access) to JSON sub-fields outside the intended scope across MySQL, PostgreSQL ->$/->>$, and SQLite. This vulnerability is fixed in 0.28.17.
- Record
CVE-2026-44635- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22, CWE-89, CWE-915, CWE-1284
- Updated
- 2026-11-02
EUVDEUVD-2026-32623
7.5
Kysely is a type-safe TypeScript SQL query builder. From 0.26.0 to 0.28.16, DefaultQueryCompiler.visitJSONPathLeg does not escape JSON-path metacharacters (., [, ], *, **, ?). When attacker-controlled input flows into eb.ref(col, '->$').key(input) or .at(input) — including type-safe code where the JSON column is shaped like Record<string, T> so K extends string is the inferred type — every dot becomes a path-leg separator, letting an attacker traverse from the intended key into sibling and child fields the developer never meant to expose. The result is read access (and, in update statements, write access) to JSON sub-fields outside the intended scope across MySQL, PostgreSQL ->$/->>$, and SQLite. This vulnerability is fixed in 0.28.17.
- Record
EUVD-2026-32623- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-pv5w-4p9q-p3v2
7.5
Kysely: JSON-path traversal injection via unsanitized path-leg metacharacters in `JSONPathBuilder.key()` / `.at()`
Summary
Kysely 0.28.12 added a sanitizeStringLiteral() call inside DefaultQueryCompiler.visitJSONPathLeg (commit 0a602bf, PR #1727) to fix CVE-2026-32763 (GHSA-wmrf-hv6w-mr66). The fix only doubles single quotes (' → ''); it does not escape JSON-path metacharacters (., [, ], *, **, ?). When attacker-controlled input flows into eb.ref(col, '->$').key(input) or .at(input) — including type-safe code where the JSON column is shaped like Record<string, T> so K extends string is the inferred type — every dot becomes a path-leg separator, letting an attacker traverse from the intended key into sibling and child fields the developer never meant to expose. The result is read access (and, in update statements, write access) to JSON sub-fields outside the intended scope across MySQL, PostgreSQL ->$/->>$, and SQLite.
- Project: Kysely — TypeScript SQL query builder (npm
kysely); affects MySQL, PostgreSQL->$/->>$, and SQLite dialects. - Source reviewed:
kysely-org/kysely@master(73192e4, version0.28.16). - Deployed artefact validated:
[email protected]from npm. - Affected file(s):
src/query-compiler/default-query-compiler.ts(lines 1611–1639, 1821–1823)src/query-builder/json-path-builder.ts(lines 93–196)src/dialect/mysql/mysql-query-compiler.ts(overridessanitizeStringLiteralbut inherits the same behaviour for path legs — escapes\and', nothing else)
- CWE: CWE-89 — Improper Neutralization of Special Elements used in an SQL Command, with CWE-915 / CWE-1284 (improper validation of specified quantity in input) flavours for the JSON-path sub-language.
- OWASP 2021: A03:2021 — Injection.
Vulnerable code
src/query-compiler/default-query-compiler.ts:1625-1639:
protected override visitJSONPathLeg(node: JSONPathLegNode): void {
const isArrayLocation = node.type === 'ArrayLocation'
this.append(isArrayLocation ? '[' : '.') // (1)
this.append(
typeof node.value === 'string'
? this.sanitizeStringLiteral(node.value) // (2)
: String(node.value),
)
if (isArrayLocation) {
this.append(']')
}
}
src/query-compiler/default-query-compiler.ts:1821-1823:
protected sanitizeStringLiteral(value: string): string {
return value.replace(LIT_WRAP_REGEX, "''") // (3)
}
with LIT_WRAP_REGEX = /'/g.
src/query-builder/json-path-builder.ts:151-167:
key<
K extends any[] extends O
? never
: O extends object
? keyof NonNullable<O> & string
: never,
O2 = undefined extends O
? null | NonNullable<NonNullable<O>[K]>
: null extends O
? null | NonNullable<NonNullable<O>[K]>
: // when the object has non-specific keys, e.g. Record<string, T>, should infer `T | null`!
string extends keyof NonNullable<O>
? null | NonNullable<NonNullable<O>[K]>
: NonNullable<O>[K],
>(key: K): TraversedJSONPathBuilder<S, O2> {
return this.#createBuilderWithPathLeg('Member', key) // (4)
}
src/query-builder/json-path-builder.ts:169-196:
#createBuilderWithPathLeg(
legType: JSONPathLegType,
value: string | number, // (5)
): TraversedJSONPathBuilder<any, any> {
// ...
return new TraversedJSONPathBuilder(
JSONPathNode.cloneWithLeg(
this.#node,
JSONPathLegNode.create(legType, value), // (6)
),
)
}
At (1) the compiler emits the path-leg separator — . for member access or [ for array index. At (2) the user-supplied string is run through sanitizeStringLiteral, which at (3) only doubles single quotes ('). Dots, brackets, asterisks, double-asterisks and question marks — every reserved character of the SQL/JSON path mini-language — pass through unmodified.
At (4) .key(K) types K as keyof NonNullable<O> & string. When the JSON column is typed as Record<string, T> (a common shape for free-form metadata blobs) the inferred K is just…
- Record
GHSA-pv5w-4p9q-p3v2- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22, CWE-89, CWE-915, CWE-1284
- Updated
- 2026-11-02
NVD
7.5
Kysely is a type-safe TypeScript SQL query builder. From 0.26.0 to 0.28.16, DefaultQueryCompiler.visitJSONPathLeg does not escape JSON-path metacharacters (., [, ], *, **, ?). When attacker-controlled input flows into eb.ref(col, '->$').key(input) or .at(input) — including type-safe code where the JSON column is shaped like Record<string, T> so K extends string is the inferred type — every dot becomes a path-leg separator, letting an attacker traverse from the intended key into sibling and child fields the developer never meant to expose. The result is read access (and, in update statements, write access) to JSON sub-fields outside the intended scope across MySQL, PostgreSQL ->$/->>$, and SQLite. This vulnerability is fixed in 0.28.17.
- Record
CVE-2026-44635- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22, CWE-89, CWE-915, CWE-1284
- Updated
- 2026-11-02
OSV
7.5
Kysely: JSON-path traversal injection via unsanitized path-leg metacharacters in `JSONPathBuilder.key()` / `.at()`
Kysely is a type-safe TypeScript SQL query builder. From 0.26.0 to 0.28.16, DefaultQueryCompiler.visitJSONPathLeg does not escape JSON-path metacharacters (., [, ], *, **, ?). When attacker-controlled input flows into eb.ref(col, '->$').key(input) or .at(input) — including type-safe code where the JSON column is shaped like Record<string, T> so K extends string is the inferred type — every dot becomes a path-leg separator, letting an attacker traverse from the intended key into sibling and child fields the developer never meant to expose. The result is read access (and, in update statements, write access) to JSON sub-fields outside the intended scope across MySQL, PostgreSQL ->$/->>$, and SQLite. This vulnerability is fixed in 0.28.17.
- Record
CVE-2026-44635- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-1284, CWE-22, CWE-89, CWE-915
- Updated
- 2026-11-02
OSVGHSA-pv5w-4p9q-p3v2
7.5
Kysely: JSON-path traversal injection via unsanitized path-leg metacharacters in `JSONPathBuilder.key()` / `.at()`
Summary
Kysely 0.28.12 added a sanitizeStringLiteral() call inside DefaultQueryCompiler.visitJSONPathLeg (commit 0a602bf, PR #1727) to fix CVE-2026-32763 (GHSA-wmrf-hv6w-mr66). The fix only doubles single quotes (' → ''); it does not escape JSON-path metacharacters (., [, ], *, **, ?). When attacker-controlled input flows into eb.ref(col, '->$').key(input) or .at(input) — including type-safe code where the JSON column is shaped like Record<string, T> so K extends string is the inferred type — every dot becomes a path-leg separator, letting an attacker traverse from the intended key into sibling and child fields the developer never meant to expose. The result is read access (and, in update statements, write access) to JSON sub-fields outside the intended scope across MySQL, PostgreSQL ->$/->>$, and SQLite.
- Project: Kysely — TypeScript SQL query builder (npm
kysely); affects MySQL, PostgreSQL->$/->>$, and SQLite dialects. - Source reviewed:
kysely-org/kysely@master(73192e4, version0.28.16). - Deployed artefact validated:
[email protected]from npm. - Affected file(s):
src/query-compiler/default-query-compiler.ts(lines 1611–1639, 1821–1823)src/query-builder/json-path-builder.ts(lines 93–196)src/dialect/mysql/mysql-query-compiler.ts(overridessanitizeStringLiteralbut inherits the same behaviour for path legs — escapes\and', nothing else)
- CWE: CWE-89 — Improper Neutralization of Special Elements used in an SQL Command, with CWE-915 / CWE-1284 (improper validation of specified quantity in input) flavours for the JSON-path sub-language.
- OWASP 2021: A03:2021 — Injection.
Vulnerable code
src/query-compiler/default-query-compiler.ts:1625-1639:
protected override visitJSONPathLeg(node: JSONPathLegNode): void {
const isArrayLocation = node.type === 'ArrayLocation'
this.append(isArrayLocation ? '[' : '.') // (1)
this.append(
typeof node.value === 'string'
? this.sanitizeStringLiteral(node.value) // (2)
: String(node.value),
)
if (isArrayLocation) {
this.append(']')
}
}
src/query-compiler/default-query-compiler.ts:1821-1823:
protected sanitizeStringLiteral(value: string): string {
return value.replace(LIT_WRAP_REGEX, "''") // (3)
}
with LIT_WRAP_REGEX = /'/g.
src/query-builder/json-path-builder.ts:151-167:
key<
K extends any[] extends O
? never
: O extends object
? keyof NonNullable<O> & string
: never,
O2 = undefined extends O
? null | NonNullable<NonNullable<O>[K]>
: null extends O
? null | NonNullable<NonNullable<O>[K]>
: // when the object has non-specific keys, e.g. Record<string, T>, should infer `T | null`!
string extends keyof NonNullable<O>
? null | NonNullable<NonNullable<O>[K]>
: NonNullable<O>[K],
>(key: K): TraversedJSONPathBuilder<S, O2> {
return this.#createBuilderWithPathLeg('Member', key) // (4)
}
src/query-builder/json-path-builder.ts:169-196:
#createBuilderWithPathLeg(
legType: JSONPathLegType,
value: string | number, // (5)
): TraversedJSONPathBuilder<any, any> {
// ...
return new TraversedJSONPathBuilder(
JSONPathNode.cloneWithLeg(
this.#node,
JSONPathLegNode.create(legType, value), // (6)
),
)
}
At (1) the compiler emits the path-leg separator — . for member access or [ for array index. At (2) the user-supplied string is run through sanitizeStringLiteral, which at (3) only doubles single quotes ('). Dots, brackets, asterisks, double-asterisks and question marks — every reserved character of the SQL/JSON path mini-language — pass through unmodified.
At (4) .key(K) types K as keyof NonNullable<O> & string. When the JSON column is typed as Record<string, T> (a common shape for free-form metadata blobs) the inferred K is just…
- Record
GHSA-pv5w-4p9q-p3v2- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-1284, CWE-22, CWE-89, CWE-915
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
kyselynpm |
0.26.0 up to 0.28.17 | 0.28.17 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-44635 is a high vulnerability (CVSS 7.5) in Kysely-org Kysely, published May 11, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.36%, the 30th percentile. Fixed in kysely 0.28.17.
References
github.com/kysely-org/kysely/security/advisories/GHSA-pv5w-4p9q-p3v2
github.com/kysely-org/kysely/releases/tag/v0.28.17
nvd.nist.gov/vuln/detail/CVE-2026-44635
github.com/advisories/GHSA-pv5w-4p9q-p3v2
github.com/CVEProject/cvelistV5/tree/main/cves/2026/44xxx/CVE-2026-44635.json
github.com/kysely-org/kysely
Left out here: the rest of 2 source descriptions. The JSON record has them.