GHSA-CXV7-GMMP-228P
Vulnerability from github – Published: 2026-06-05 15:59 – Updated: 2026-06-05 15:59Summary
An authenticated user with columnAdd permission on a Postgres-backed base can inject arbitrary SQL into the formula engine via the optional direction argument of ARRAYSORT(...). The value is unrestricted by formula validation and embedded into a knex.raw ORDER BY clause, executing during column creation and on every subsequent record read of the formula column.
Details
The vulnerability is specific to the Postgres mapping for ARRAYSORT in packages/nocodb/src/db/functionMappings/pg.ts. Two factors combine:
ARRAYSORTdeclares only argument count, notvalidation.args.type, sovalidate-extract-tree.tsdoes not enforce an allowlist on the second argument.- The Postgres mapping then passes the attacker-controlled value through
sanitize(knex.raw(...))into a raw SQL fragment:
const direction = pt.arguments[1]
? sanitize(
knex.raw(pt.arguments[1]?.value ?? (await fn(pt.arguments[1])).builder),
)
: knex.raw('asc');
return {
builder: knex.raw(`ARRAY(SELECT UNNEST(??) ORDER BY 1 ??)`, [source, direction]),
};
sanitize() in sqlSanitize.ts only escapes ? placeholder characters; it does not validate SQL syntax. A payload such as "desc, (SELECT COUNT(*) FROM generate_series(1,30000000))" is accepted, persisted, and re-executed on every read of the formula column.
Impact
- Authenticated SQL injection against Postgres-backed bases.
- Requires
columnAddpermission (creator/owner-level). - Proven impact: attacker-controlled heavy SQL causing multi-second query stalls (DoS).
- Potentially extendable to broader SQL injection outcomes depending on database permissions and deployment hardening.
- Limited to Postgres backends.
Credit
This issue was reported by @leduckhuong.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "nocodb"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2026.04.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-47375"
],
"database_specific": {
"cwe_ids": [
"CWE-89"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-05T15:59:28Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\n\nAn authenticated user with `columnAdd` permission on a Postgres-backed base can inject arbitrary SQL into the formula engine via the optional `direction` argument of `ARRAYSORT(...)`. The value is unrestricted by formula validation and embedded into a `knex.raw` `ORDER BY` clause, executing during column creation and on every subsequent record read of the formula column.\n\n### Details\n\nThe vulnerability is specific to the Postgres mapping for `ARRAYSORT` in `packages/nocodb/src/db/functionMappings/pg.ts`. Two factors combine:\n\n1. `ARRAYSORT` declares only argument count, not `validation.args.type`, so `validate-extract-tree.ts` does not enforce an allowlist on the second argument.\n2. The Postgres mapping then passes the attacker-controlled value through `sanitize(knex.raw(...))` into a raw SQL fragment:\n\n```ts\nconst direction = pt.arguments[1]\n ? sanitize(\n knex.raw(pt.arguments[1]?.value ?? (await fn(pt.arguments[1])).builder),\n )\n : knex.raw(\u0027asc\u0027);\n\nreturn {\n builder: knex.raw(`ARRAY(SELECT UNNEST(??) ORDER BY 1 ??)`, [source, direction]),\n};\n```\n\n`sanitize()` in `sqlSanitize.ts` only escapes `?` placeholder characters; it does not validate SQL syntax. A payload such as `\"desc, (SELECT COUNT(*) FROM generate_series(1,30000000))\"` is accepted, persisted, and re-executed on every read of the formula column.\n\n### Impact\n\n- Authenticated SQL injection against Postgres-backed bases.\n- Requires `columnAdd` permission (creator/owner-level).\n- Proven impact: attacker-controlled heavy SQL causing multi-second query stalls (DoS).\n- Potentially extendable to broader SQL injection outcomes depending on database permissions and deployment hardening.\n- Limited to Postgres backends.\n\n### Credit\n\nThis issue was reported by [@leduckhuong](https://github.com/leduckhuong).",
"id": "GHSA-cxv7-gmmp-228p",
"modified": "2026-06-05T15:59:28Z",
"published": "2026-06-05T15:59:28Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/nocodb/nocodb/security/advisories/GHSA-cxv7-gmmp-228p"
},
{
"type": "PACKAGE",
"url": "https://github.com/nocodb/nocodb"
},
{
"type": "WEB",
"url": "https://github.com/nocodb/nocodb/releases/tag/2026.04.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:H",
"type": "CVSS_V3"
}
],
"summary": "NocoDB: Postgres SQL Injection in Formula `ARRAYSORT`"
}
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.