ANT-2026-REDYDMAE · supabase/supabase
xss medium
Severity Claude medium · Security research firm - · Maintainer -
Discovered by Claude Mythos Preview
Anthropic's analysis of this finding, sealed at approval.
ANT-2026-REDYDMAE: DOM-based XSS via javascript: scheme in URL hostname check
The isVercelUrl() helper checks only that new URL(url).hostname === 'vercel.com' and never inspects the protocol. Because non-special schemes like javascript: parse an authority after //, a URL such as javascript://vercel.com/%0a yields hostname 'vercel.com' and passes validation. The attacker-controlled next query parameter flows through this check to window.location.href in three Vercel integration pages (choose-project.tsx:85/142 and new-project.tsx:246). Assigning a javascript: URL to location.href executes the decoded body after the %0a newline in the Studio origin, and the app's CSP permits 'unsafe-inline' so it does not block the navigation. An attacker who lures an authenticated admin to a crafted link and gets one click on the 'Skip' button gains arbitrary JS execution in the victim's dashboard session.
Target
Project: supabase/supabase
Location: apps/studio/components/interfaces/Integrations/Vercel/VercelIntegration.utils.ts:3
Discovery: static analysis — not yet dynamically reproduced
Technical Details
Root cause is a protocol-blind URL allowlist: validating only new URL(url).hostname is insufficient because the WHATWG URL parser will populate hostname for non-special schemes when the input contains //authority. Thus new URL('javascript://vercel.com/%0aalert(1)').hostname === 'vercel.com' is true, the check passes, and the javascript: URL is later assigned to window.location.href, where the browser treats //vercel.com/ as a JS line comment and executes everything after the %0a newline.
Reproduction
- Craft a URL to /dashboard/integrations/vercel//marketplace/choose-project with next=javascript%3A%2F%2Fvercel.com%2F%250a
- Deliver the link to an authenticated Studio user
- Victim loads the page; isVercelUrl() approves the next value because hostname === 'vercel.com'
- Victim clicks 'Skip', triggering window.location.href = next
- Browser executes the javascript: body after the %0a newline in the Studio origin
[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]
Suggested Fix
URL validators that gate navigation must enforce a protocol allowlist (http/https) in addition to hostname checks; hostname-only validation of new URL() output is bypassable via non-special schemes like javascript:.
Acknowledgement
This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-REDYDMAE.
Reference: ANT-2026-REDYDMAE
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure
The change that resolved this finding.
diff --git a/apps/studio/components/interfaces/Integrations/Vercel/VercelIntegration.utils.ts b/apps/studio/components/interfaces/Integrations/Vercel/VercelIntegration.utils.ts
index cf4a7f0c93bf5..3c55db6f9e6aa 100644
--- a/apps/studio/components/interfaces/Integrations/Vercel/VercelIntegration.utils.ts
+++ b/apps/studio/components/interfaces/Integrations/Vercel/VercelIntegration.utils.ts
@@ -1,6 +1,7 @@
export function isVercelUrl(url: string): boolean {
try {
- return new URL(url).hostname === 'vercel.com'
+ const u = new URL(url)
+ return u.protocol === 'https:' && u.hostname === 'vercel.com'
} catch {
// If the URL is invalid, return false
return falsehttps://github.com/supabase/supabase/commit/1097fcaa2f4e7954f7636eb8bbdc04c8060affc0
Dates from discovery through public reveal.
- 2026-05-14 Reported to tracker
- 2026-05-14 Maintainer acknowledged
- 2026-05-15 Sent to maintainer
- 2026-05-20 Patch released
- 2026-08-18 Publicly revealed
SHA-3-512 hash:
eee969c5af0ed7459b1785ef07a4d522782ddf10821f03a6515b47586c9d3369bb3fa34dbdb84a165ff71a303e7a47b22386c6369e6e72b990cb54174b4672c9
Committed 2026-05-17 17:55 PT
Revealed 2026-08-18 07:11 PT
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-REDYDMAE",
"bug_class": "xss",
"claude_severity": "medium",
"commit_sha": null,
"created_at": "2026-05-14T22:03:38+00:00",
"description": "The isVercelUrl() helper checks only that new URL(url).hostname === 'vercel.com' and never inspects the protocol. Because non-special schemes like javascript: parse an authority after //, a URL such as javascript://vercel.com/%0a<payload> yields hostname 'vercel.com' and passes validation. The attacker-controlled `next` query parameter flows through this check to window.location.href in three Vercel integration pages (choose-project.tsx:85/142 and new-project.tsx:246). Assigning a javascript: URL to location.href executes the decoded body after the %0a newline in the Studio origin, and the app's CSP permits 'unsafe-inline' so it does not block the navigation. An attacker who lures an authenticated admin to a crafted link and gets one click on the 'Skip' button gains arbitrary JS execution in the victim's dashboard session.",
"discovered_at": "2026-05-10T00:00:00+00:00",
"location": "apps/studio/components/interfaces/Integrations/Vercel/VercelIntegration.utils.ts:3",
"poc_sha256": null,
"preimage_version": 1,
"project": "supabase/supabase",
"reproduction": [
"1. Craft a URL to /dashboard/integrations/vercel/<slug>/marketplace/choose-project with next=javascript%3A%2F%2Fvercel.com%2F%250a<js-payload>",
"2. Deliver the link to an authenticated Studio user",
"3. Victim loads the page; isVercelUrl() approves the next value because hostname === 'vercel.com'",
"4. Victim clicks 'Skip', triggering window.location.href = next",
"5. Browser executes the javascript: body after the %0a newline in the Studio origin"
],
"technical_details": "Root cause is a protocol-blind URL allowlist: validating only `new URL(url).hostname` is insufficient because the WHATWG URL parser will populate hostname for non-special schemes when the input contains `//authority`. Thus `new URL('javascript://vercel.com/%0aalert(1)').hostname === 'vercel.com'` is true, the check passes, and the javascript: URL is later assigned to window.location.href, where the browser treats `//vercel.com/` as a JS line comment and executes everything after the %0a newline.",
"title": "DOM-based XSS via javascript: scheme in URL hostname check",
"vendor_severity": null
}