SIGN IN SIGN UP
appsmithorg / appsmith UNCLAIMED

Platform to build admin panels, internal tools, and dashboards. Integrates with 25+ databases and any API.

0 0 68 TypeScript

fix: prevent stored XSS via SQL autocomplete (GHSA-vjfq-fvfc-3vjw) (#41760)

## Description

Fixes a stored cross-site scripting vulnerability in the SQL
autocomplete dropdown (GHSA-vjfq-fvfc-3vjw, CVSS 8.7 / High). An
authenticated workspace Developer who can run DDL against a shared
datasource could create a table or column whose name contains an HTML
payload; when any other workspace member opened the SQL query editor and
triggered autocomplete, the raw identifier was written to `innerHTML`
and the payload executed in the victim's session.

**TL;DR —** the SQL hint renderer now writes completion text via
`textContent` instead of `innerHTML`. Matching `innerHTML` sinks in the
tern and JS-keyword autocomplete renderers are consolidated into a
shared `renderKeywordHint` helper so the pattern is eliminated from the
autocomplete subsystem. Pure client-side change, no
backend/API/migration impact, no visual regression.

### What changed
- `app/client/src/components/editorComponents/CodeEditor/hintHelpers.ts`
— SQL hint renderer switched from `innerHTML = text` to `textContent =
text` (primary fix for the actively-exploitable sink).
- `app/client/src/utils/autocomplete/keywordHintRenderer.ts` (new) —
single `renderKeywordHint(element, label, description?)` helper that
sets `textContent` for the visible label and mirrors the optional
description onto the `keyword` attribute (used by the existing `content:
attr(keyword)` CSS rule to render the "For Loop" / "While Statement"
suffix).
- `app/client/src/utils/autocomplete/CodemirrorTernService.ts` — tern
keyword completion render routed through the helper (was `innerHTML =
data.displayText`).
- `app/client/src/utils/autocomplete/keywordCompletion.ts` — 13
duplicate inline renderers collapsed into calls to the helper.
Keyword-to-description mapping (`for` → "For Loop", `try` → "Try-catch
Statement", …) is preserved 1:1; zero visual change.

### Why render-layer, not data-layer
Database identifiers must stay raw through the backend, plugin structure
cache, Redux state, and the `getAllDatasourceTableKeys` selector,
because users legitimately need the exact name to write queries, refresh
schemas, and bind widgets. The bug is missing output encoding, not
over-permissive input. DDL filtering is deliberately **not** added —
Appsmith's query editor is intentionally a full database console and
restricting DDL would break schema-design apps, migration tools, and
admin dashboards.

### Test plan — TDD driven

All three fixes were developed test-first. Jest coverage was written to
**fail** on the vulnerable code path and then verified to pass after the
fix.

| File | Tests added | Asserts |
|---|---|---|
| `hintHelpers.test.ts` | 3 | Table name, SVG table name, and
`table.column` composite key with `<img src=x onerror=…>` payloads
render as text, no `<img>`/`<svg>` child created, `window.__xssFired`
stays `undefined`. **These tests fail on the unpatched code** (verified
in the TDD red step). |
| `keywordHintRenderer.test.ts` | 6 | Helper writes `textContent` not
`innerHTML`; HTML and SVG payloads do not parse; empty label does not
create empty children; optional description populates the `keyword`
attribute correctly. |
| `keywordCompletion.test.ts` | 20 | Each of the 10 JS keyword cases
produces text-only hints (`children.length === 0`); descriptive labels
("For Loop", "While Statement", …) are preserved byte-for-byte after the
refactor; spoofed outer completion with HTML payload never parses
markup. |

**Verification commands:**
```
yarn g:jest --testPathPattern="(hintHelpers|keywordHintRenderer|keywordCompletion)"  # 34 passed
yarn run check-types   # clean
npx eslint --cache <modified files>  # 0 errors (2 pre-existing Object.keys warnings untouched)
grep -rn '\.innerHTML\s*=' app/client/src/components/editorComponents/CodeEditor app/client/src/utils/autocomplete  # no write-sinks outside the new safe helper
```

Also manually reproduced the advisory PoC (`CREATE TABLE "<svg
onload=alert(1)>" (id serial primary key);` in Postgres) in a local
instance — alert fires on `release`, does not fire after this patch.

Fixes
https://linear.app/appsmith/issue/APP-15167/security-high-stored-xss-via-database-tablecolumn-names-in-sql
Ref:
[GHSA-vjfq-fvfc-3vjw](https://github.com/appsmithorg/appsmith/security/advisories/GHSA-vjfq-fvfc-3vjw)

> [!WARNING]
> This is a security-advisory fix. Linear ticket APP-15167 tracks the
vulnerability; the GitHub advisory link is attached to the Linear issue.
Coordinate advisory publication + CVE assignment with the security team
before merge.

## Automation

/ok-to-test tags="@tag.All"

### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/24854712195>
> Commit: 7a42f7263bd9b40d0a4c5eff3fc362c09a128588
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=24854712195&attempt=3"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Fri, 24 Apr 2026 06:35:05 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [x] Yes
- [ ] No

(Coordinate via the security advisory disclosure.)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed XSS vulnerabilities in SQL and keyword autocomplete completion
rendering. Database table names, column names, and keywords are now
safely displayed as text without interpreting HTML content.

* **Tests**
* Added comprehensive XSS regression test suites for autocomplete
security.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
S
subratadeypappu committed
99d69180919981ed9bc5484050d809a5bec68acc
Parent: a0d0c2f
Committed by GitHub <noreply@github.com> on 4/24/2026, 12:21:27 PM