Skip to content

Commit 48fe9c4

Browse files
fix(settings): validate info.relay_url is a usable absolute URL (#825)
1 parent 242a300 commit 48fe9c4

3 files changed

Lines changed: 81 additions & 0 deletions

File tree

‎.changeset/validate-relay-url.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"nostream": patch
3+
---
4+
5+
fix(settings): validate `info.relay_url` is a usable absolute URL
6+
7+
`validateSettings()` now rejects a `relay_url` that is not a valid absolute URL, parses with an empty host (scheme-only strings like `localhost:8008`), or uses a protocol other than `ws`/`wss`/`http`/`https`. Previously an invalid value started cleanly and then broke every HTTP request with `500 TypeError: Invalid URL`, including the Admin Console, leaving the bad setting fixable only by editing the file by hand. Found in #824.

‎src/utils/settings-config.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,21 @@ export const validateSettings = (settings: Settings): ValidationIssue[] => {
594594

595595
if (!settings.info?.relay_url) {
596596
issues.push({ path: 'info.relay_url', message: 'relay_url is required' })
597+
} else if (typeof settings.info.relay_url === 'string') {
598+
try {
599+
const parsed = new URL(settings.info.relay_url)
600+
601+
if (!parsed.host) {
602+
issues.push({ path: 'info.relay_url', message: 'relay_url must include a valid host' })
603+
} else if (!['ws:', 'wss:', 'http:', 'https:'].includes(parsed.protocol)) {
604+
issues.push({
605+
path: 'info.relay_url',
606+
message: 'relay_url must use ws, wss, http, or https protocol',
607+
})
608+
}
609+
} catch {
610+
issues.push({ path: 'info.relay_url', message: 'relay_url must be a valid absolute URL' })
611+
}
597612
}
598613

599614
if (!settings.info?.name) {

‎test/unit/utils/settings-config.spec.ts‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,65 @@ describe('settings-config', () => {
9898
expect(issues.some((issue) => issue.path === 'network')).to.equal(true)
9999
})
100100

101+
it('accepts a relay_url with a valid ws/wss/http/https absolute URL', () => {
102+
for (const relay_url of [
103+
'ws://nostream.example.com:8008',
104+
'wss://nostream.example.com',
105+
'http://localhost:8008',
106+
'http://nostream.example.com',
107+
'https://nostream.example.com',
108+
'https://nostream.example.com:8443',
109+
'ws://[::1]:8008',
110+
'wss://127.0.0.1',
111+
]) {
112+
const issues = validateSettings({ info: { relay_url, name: 'test' } } as any)
113+
114+
expect(
115+
issues.filter((issue) => issue.path === 'info.relay_url'),
116+
`expected ${relay_url} to pass`,
117+
).to.be.empty
118+
}
119+
})
120+
121+
it('rejects a relay_url that is not a valid absolute URL', () => {
122+
const issues = validateSettings({ info: { relay_url: 'nostream.example.com', name: 'test' } } as any)
123+
124+
expect(
125+
issues.some(
126+
(issue) => issue.path === 'info.relay_url' && issue.message === 'relay_url must be a valid absolute URL',
127+
),
128+
).to.equal(true)
129+
})
130+
131+
it('rejects a scheme-only relay_url with no host', () => {
132+
const issues = validateSettings({ info: { relay_url: 'localhost:8008', name: 'test' } } as any)
133+
134+
expect(
135+
issues.some(
136+
(issue) => issue.path === 'info.relay_url' && issue.message === 'relay_url must include a valid host',
137+
),
138+
).to.equal(true)
139+
})
140+
141+
it('rejects a relay_url whose protocol is not ws/wss/http/https', () => {
142+
for (const relay_url of [
143+
'ftp://nostream.example.com',
144+
'gopher://nostream.example.com:70',
145+
'whttp://nostream.example.com',
146+
]) {
147+
const issues = validateSettings({ info: { relay_url, name: 'test' } } as any)
148+
149+
expect(
150+
issues.some(
151+
(issue) =>
152+
issue.path === 'info.relay_url' &&
153+
issue.message === 'relay_url must use ws, wss, http, or https protocol',
154+
),
155+
`expected ${relay_url} to be rejected for protocol`,
156+
).to.equal(true)
157+
}
158+
})
159+
101160
describe('rate limit period validation', () => {
102161
const baseSettings = () =>
103162
({

0 commit comments

Comments
 (0)