# Config override using non-validated query parameter allows at least reflected XSS by injecting configuration into state

Grammarly's ?config= query param was JSON-parsed and schema-validated, but unlisted live config keys like api.redirect and account.subscription accepted javascript: URLs, yielding reflected XSS.

- Payout: hidden
- Severity: medium
- Program: Superhuman (formerly Grammarly)
- Found by: fransrosen
- Disclosed: 2021-03-01
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/1082847
- Library page: https://bugbountylibrary.com/r/h1%3A1082847
- Tags: area:web domain:web impact:ato kind:bounty platform:hackerone sev:medium tech:rest technique:js-analysis technique:source-review vuln:business-logic vuln:misconfiguration vuln:xss

**Target:** app.grammarly.com web app config loader
**Root cause:** The ?config= parameter is JSON-parsed and validated against a Partial<> TypeScript schema, but live config keys (api.redirect, account.subscription, crossPlatformOfficeAddin.infoURL) are absent from the schema, so they pass through unvalidated and are later used as navigation URLs.
**Steps:**
1. Load `https://app.grammarly.com/docs/new?config={%22account%22:{%22subscription%22:%22javascript:alert(document.domain)//%22},%22api%22:{%22redirect%22:%22javascript:alert(document.domain)//%22}}`
2. Config persists in session state; URL is rewritten on doc creation hiding the payload
3. Free user clicks Upgrade to Premium, or paid user clicks Subscription in menu
4. The unvalidated javascript: URL is navigated to, executing in app.grammarly.com origin
5. CSP does not block since unsafe-inline/unsafe-eval allowed
**How found:** Read the config parser code, noticed JSON.parse then schema validation, then compared the TypeScript schema against the live config JSON and found keys present in production but missing from the Partial schema, making them freely overwritable.
**Exploitation:** Attacker crafts a link with a config param setting api.redirect or account.subscription to a javascript: URL; victim loads it, config persists in session, and clicking the upgrade/subscription link executes attacker JS on the app origin, allowing API calls as the victim.
**Escalation:** Beyond XSS, unvalidated desktop.windows.installURL / desktop.mac.installURL / officeAddin.installURL could redirect app-download links to attacker-controlled installers without any XSS.
**Insight:** When a config/state object is validated by a Partial<> schema, diff the schema against the actual live config payload (grep the JS bundle or intercept the real config) to find keys that bypass validation and are still consumed by navigation or URL sinks.

