Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion src/utils/lang/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,53 @@ export function isHTML(maybeHTML: string): boolean {
return [...$div.childNodes].reverse().some(($child) => $child.nodeType === 1)
}

function getFetchResolverScript() {
return `<script data-static-preview-fetch-resolver>
;(() => {
const proxyPrefix = 'https://api.codetabs.com/v1/proxy/?quest='
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WASM resolver uses deprecated third-party proxy service

High Severity

The new fetch resolver script incorrectly routes WASM fetch calls through api.codetabs.com. This third-party proxy, explicitly replaced by our first-party /api/proxy endpoint, introduces privacy and reliability risks (e.g., rate limits) and bypasses our security controls. Our first-party proxy is already equipped to handle these requests.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3109a57. Configure here.

const nativeFetch = window.fetch.bind(window)

const shouldProxy = (url) => {
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
return false
}

if (url.href.startsWith(proxyPrefix)) {
return false
}

const baseUrl = new URL(document.baseURI)
return url.origin === baseUrl.origin
}

window.fetch = (input, init) => {
try {
const requestUrl =
typeof input === 'string'
? input
: input instanceof URL
? input.href
: input.url

const absoluteUrl = new URL(requestUrl, document.baseURI)

if (shouldProxy(absoluteUrl)) {
const proxiedUrl = \`\${proxyPrefix}\${encodeURIComponent(absoluteUrl.href)}\`

if (typeof input !== 'string' && !(input instanceof URL)) {
return nativeFetch(new Request(proxiedUrl, input), init)
}

return nativeFetch(proxiedUrl, init)
}
} catch {}

return nativeFetch(input, init)
}
})()
</script>`
}

function getTitle(html: string) {
// TODO: Regex might reasonably be faster here
const $div = document.createElement('div')
Expand All @@ -24,7 +71,10 @@ export interface HTMLPageData {
export function processHTML(html: string, url: string): HTMLPageData {
const processedHTML = html
// Add base tag to iframe
.replace(/<head([^>]*)>/i, `<head$1><base href="${url}">`)
.replace(
/<head([^>]*)>/i,
`<head$1><base href="${url}">${getFetchResolverScript()}`,
)
// Replace absolute paths with relative paths
.replace(/((src|href|content)=")\/(.*?")/gm, '$1$3')

Expand Down
50 changes: 50 additions & 0 deletions tests/html.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { processHTML } from '../src/utils/lang/html'

const HTML_URL =
'https://gitlab.com/sirajchokshi/static-preview-test/-/raw/main/wasm.html'

describe('[HTML] processHTML', () => {
it('injects base url and runtime fetch resolver', () => {
const html = `
<!DOCTYPE html>
<html>
<head>
<title>WASM | Static Preview Test</title>
</head>
<body>
<script src="/assets/wasm.js"></script>
</body>
</html>
`

const { processedHTML, title } = processHTML(html, HTML_URL)

expect(processedHTML).toContain(`<base href="${HTML_URL}">`)
expect(processedHTML).toContain('data-static-preview-fetch-resolver')
expect(processedHTML).toContain('window.fetch = (input, init) =>')
expect(title).toEqual('WASM | Static Preview Test')
})

it('rewrites leading slash paths to document-relative paths', () => {
const html = `
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/assets/main.css" />
<meta content="/assets/preview.png" />
</head>
<body>
<a href="/index.html">Home</a>
<script src="/assets/wasm.js"></script>
</body>
</html>
`

const { processedHTML } = processHTML(html, HTML_URL)

expect(processedHTML).toContain('href="assets/main.css"')
expect(processedHTML).toContain('content="assets/preview.png"')
expect(processedHTML).toContain('href="index.html"')
expect(processedHTML).toContain('src="assets/wasm.js"')
})
})
Loading