Skip to content

Commit 5093181

Browse files
committed
fix(security): escape font CDN URL and tighten WS origin check
- Escape font.cdnUrl with escapeAttr() in build.ts HTML injection - Reject missing Origin header on non-loopback hosts in WS validation
1 parent 9e4a3a6 commit 5093181

3 files changed

Lines changed: 12 additions & 4 deletions

File tree

build.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
22
import { dirname, resolve } from 'node:path'
3-
import { generatePwaHtml } from './src/pwa/meta-tags'
3+
import { escapeAttr, generatePwaHtml } from './src/pwa/meta-tags'
44
import type { RemobiConfig } from './src/types'
55
import { readStdin, sleep, spawnProcess } from './src/util/node-compat'
66

@@ -113,7 +113,7 @@ const WS_INTERCEPTOR =
113113

114114
/** Inject remobi overlay into ttyd's HTML */
115115
export function injectOverlay(html: string, js: string, css: string, config: RemobiConfig): string {
116-
const fontLink = `<link rel="preload" href="${config.font.cdnUrl}" as="style" onload="this.rel='stylesheet'">`
116+
const fontLink = `<link rel="preload" href="${escapeAttr(config.font.cdnUrl)}" as="style" onload="this.rel='stylesheet'">`
117117
const viewport =
118118
'<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, viewport-fit=cover">'
119119
const styleTag = `<style>${css}</style>`

src/serve.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ export function isAllowedWebSocketOrigin(
6767
hostHeader: string | undefined,
6868
): boolean {
6969
if (originHeader === undefined) {
70-
return true
70+
const hostname = (hostHeader ?? '').replace(/:\d+$/, '')
71+
return isLoopbackHost(hostname)
7172
}
7273
if (hostHeader === undefined) {
7374
return false

tests/serve.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,15 @@ describe('isAllowedWebSocketOrigin', () => {
9393
expect(isAllowedWebSocketOrigin('https://term.example.ts.net', undefined)).toBe(false)
9494
})
9595

96-
test('allows requests without an origin header', () => {
96+
test('allows requests without an origin header on loopback', () => {
9797
expect(isAllowedWebSocketOrigin(undefined, 'localhost:7681')).toBe(true)
98+
expect(isAllowedWebSocketOrigin(undefined, '127.0.0.1:7681')).toBe(true)
99+
})
100+
101+
test('rejects requests without an origin header on non-loopback', () => {
102+
expect(isAllowedWebSocketOrigin(undefined, 'term.example.ts.net')).toBe(false)
103+
expect(isAllowedWebSocketOrigin(undefined, '0.0.0.0:7681')).toBe(false)
104+
expect(isAllowedWebSocketOrigin(undefined, undefined)).toBe(false)
98105
})
99106
})
100107

0 commit comments

Comments
 (0)