Skip to content

Commit fe9abae

Browse files
committed
fix(workflow-renderer): rewrite dropbox embed via parsed URL, tolerate scheme-less links
Derive the direct video URL from the parsed URL object (rewrite hostname to dl.dropboxusercontent.com for any dropbox.com/*.dropbox.com host) instead of a www-only string replace, and accept scheme-less links. Fixes broken embeds for m.dropbox.com / bare-host links flagged in review.
1 parent 43bd49f commit fe9abae

1 file changed

Lines changed: 28 additions & 16 deletions

File tree

packages/workflow-renderer/src/note/note-block-view.tsx

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,32 @@ function getTwitchParent(): string {
1717
return typeof window !== 'undefined' ? window.location.hostname : 'localhost'
1818
}
1919

20-
/** Parse a URL's lowercased hostname, returning null for non-absolute/invalid URLs. */
21-
function parseHostname(url: string): string | null {
22-
try {
23-
return new URL(url).hostname.toLowerCase()
24-
} catch {
25-
return null
20+
/** Parse a URL, tolerating scheme-less inputs (https is assumed). Returns null if unparseable. */
21+
function parseUrl(url: string): URL | null {
22+
for (const candidate of [url, `https://${url}`]) {
23+
try {
24+
return new URL(candidate)
25+
} catch {}
2626
}
27+
return null
28+
}
29+
30+
/**
31+
* Resolve a Dropbox share link to a direct, embeddable video URL. Accepts only URLs
32+
* whose host is `dropbox.com` or a `*.dropbox.com` subdomain (so attacker-controlled
33+
* hosts like `dropbox.com.evil.com` are rejected), then rewrites the host to
34+
* `dl.dropboxusercontent.com` so the file streams as media. Returns null for any
35+
* non-Dropbox host or non-video path.
36+
*/
37+
function getDropboxDirectVideoUrl(url: string): string | null {
38+
const parsed = parseUrl(url)
39+
if (!parsed) return null
40+
const host = parsed.hostname.toLowerCase()
41+
if (host !== 'dropbox.com' && !host.endsWith('.dropbox.com')) return null
42+
if (!/\.(mp4|mov|webm)$/i.test(parsed.pathname)) return null
43+
parsed.hostname = 'dl.dropboxusercontent.com'
44+
parsed.searchParams.delete('dl')
45+
return parsed.toString()
2746
}
2847

2948
/**
@@ -259,16 +278,9 @@ function getEmbedInfo(url: string): EmbedInfo | null {
259278
return { url: `https://drive.google.com/file/d/${googleDriveMatch[1]}/preview`, type: 'iframe' }
260279
}
261280

262-
const dropboxHost = parseHostname(url)
263-
if (
264-
dropboxHost &&
265-
(dropboxHost === 'dropbox.com' || dropboxHost.endsWith('.dropbox.com')) &&
266-
/\.(mp4|mov|webm)/.test(url)
267-
) {
268-
const directUrl = url
269-
.replace('www.dropbox.com', 'dl.dropboxusercontent.com')
270-
.replace('?dl=0', '')
271-
return { url: directUrl, type: 'video' }
281+
const dropboxDirectVideoUrl = getDropboxDirectVideoUrl(url)
282+
if (dropboxDirectVideoUrl) {
283+
return { url: dropboxDirectVideoUrl, type: 'video' }
272284
}
273285

274286
const tenorMatch = url.match(/tenor\.com\/view\/[^/]+-(\d+)/)

0 commit comments

Comments
 (0)