ReviewOS

also looking at this

stacks/ts-cloud

fix: validate site domain before it reaches nginx server_name

#129
Merged glennmichael123 wants to merge fix/nginx-server-name-injection into main
5 files +143 -5

Hardening for values that flow from the dashboard into generated server config.

domain → nginx server_name

A site's domain is member-editable and was written into the generated server_name with quote-escaping only, while its sibling aliases — same destination — was validated with isValidHostname. This closes that asymmetry:

  • validate domain on PATCH /api/sites before any write, so a rejected value can't leave cloud.config.ts half-edited
  • reject non-hostname tokens inside buildNginxVhost as defense in depth, so no other path can introduce one
  • refuse an empty server_name instead of emitting server_name ; (invalid nginx)

The generator's check is deliberately laxer than the API's: compute-deploy falls back to domain: site.domain || siteName, so an internal site legitimately arrives as a single label (main, docs). A test pins that so the stricter API rule can't leak into the generator and break those deploys.

escapeSingle now escapes \r / \n

These values land in single-quoted TS literals in the shared, box-wide cloud.config.ts. A raw newline terminates the literal and leaves the file syntactically broken for every tenant that loads it. Escaping also keeps line-oriented sinks downstream (heredoc delimiters in the deploy script) safe from multi-line values.

Dashboard password no longer logged

The generated Basic-auth password was written to the 0600 credentials file and logged in full on every successful deploy — and deploy output outlives the deploy in CI logs, scrollback and the journal. Now logs the path. The failure path still prints it (persisting failed, so it's the only copy) but says so and tells you to rotate.

Tests

Adds test/drivers/nginx-vhost-injection.test.ts (9 tests): injection payloads rejected in both domain and aliases, whitespace/newline/;/{/} rejected, empty server_name rejected, single-label fallback still accepted, and escaping of newlines/CR/quotes/backslashes.

Baseline on main is 249 pass / 15 fail; this branch is 258 pass / 15 fail — the same 15 pre-existing module-resolution failures (@stacksjs/ts-xml, bunfig aren't installed in the checkout), no regressions.

5 changed files on the files tab, with 0 review threads.