also looking at this
fix: validate site domain before it reaches nginx server_name
#129
5 files
+143
-5
| @@ -40,7 +40,7 @@ import { | ||
| 40 | 40 | setServerlessSecret, |
| 41 | 41 | updateFunctionConfig, |
| 42 | 42 | } from './serverless-operations' |
| 43 | import { addSiteToCloudConfig, removeSiteFromCloudConfig, renderAliasesValue, renderEnvValue, renderRedirectsValue, renderSslValue, renderStringValue, setSitePropertyInCloudConfig } from './site-config-editor' | |
| 43 | import { addSiteToCloudConfig, isValidHostname, removeSiteFromCloudConfig, renderAliasesValue, renderEnvValue, renderRedirectsValue, renderSslValue, renderStringValue, setSitePropertyInCloudConfig } from './site-config-editor' | |
| 44 | 44 | import { addSshKeyToCloudConfig, describeSshKeys, removeSshKeyFromCloudConfig } from './ssh-config-editor' |
| 45 | 45 | import { createTerminalSession } from './terminal-session' |
| 46 | 46 | |
| @@ -860,6 +860,15 @@ export async function startLocalDashboardServer(options: LocalDashboardServerOpt | ||
| 860 | 860 | if (body.port !== undefined && body.port !== null && body.port !== '' && (!Number.isInteger(Number(body.port)) || Number(body.port) < 1 || Number(body.port) > 65_535)) |
| 861 | 861 | return json({ ok: false, error: 'Port must be a number between 1 and 65535.' }, 422) |
| 862 | 862 | |
| 863 | // `domain` lands in the generated nginx `server_name`, so it must be a | |
| 864 | // hostname and nothing else — an unvalidated value can close the | |
| 865 | // server block and open an attacker-controlled one. `aliases` (same | |
| 866 | // destination) has always been checked; this closes the gap for the | |
| 867 | // primary domain. Validated before any write so a bad value can't | |
| 868 | // leave the config half-edited. | |
| 869 | if (typeof body.domain === 'string' && body.domain.trim() && !isValidHostname(body.domain.trim())) | |
| 870 | return json({ ok: false, error: `Domain '${body.domain.trim()}' is not a valid hostname.` }, 422) | |
| 871 | ||
| 863 | 872 | let text = await readFile(configPath, 'utf8') |
| 864 | 873 | const set = (key: string, valueText: string): void => { |
| 865 | 874 | text = setSitePropertyInCloudConfig({ configText: text, siteName: name, key, valueText }) |