Skip to content
ReviewOS
Features
Review
Durable review threads
Comments that follow the line through a rebase instead of vanishing.
Stacked pull requests
Dependent changes that merge in order and retarget themselves.
Agentic review
Coding agents as first-class authors and reviewers, with the audit trail to match.
Diffs that stay fast
Rendered on the server, against the merge base, readable at a hundred files.
Merge strategies and rules
Merge, squash, or rebase, gated by approvals, threads, and checks.
Repositories
Plain git hosting
Ordinary bare repositories on your disk, driven by the git binary.
Issues that link up
One number sequence, closing keywords, and references that resolve.
Search that reads like a query
Qualifiers, quoting, and negation, parsed properly rather than by regex.
Automation
Checks from your own CI
A status API your existing pipeline reports into, enforced at merge.
Webhooks you can trust
Signed, retried with backoff, and blocked from reaching your own network.
Notifications worth reading
Every message says why it reached you, and a burst arrives as one.
Operations
Self-hosting without a cluster
One Postgres and one process. Scale the parts that need it, later.
Pages for every repository
A docs folder is a documentation site. Push, and the site is the branch.
Every feature, in one place.
All features
Explore
Discover
Docs
Sign in
Sign up
Reload
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
Conversation
Commits
Checks
Files changed
Review screen
File 4 of 5
previous
next
all files
packages/ts-cloud/src/drivers/shared/nginx-vhost.ts
modified
+18
-1
Changes to packages/ts-cloud/src/drivers/shared/nginx-vhost.ts
⋯
268 lines
Show the 268 lines above this hunk
@@ -269,7 +269,24 @@
function vhostBody(options: NginxVhostOptions): string[] {
269
269
*
otherwise
a
single
:
80
block
(
certbot
upgrades
it
for
Let
's Encrypt).
270
270
*
/
271
271
export
function
buildNginxVhost
(
options
:
NginxVhostOptions
)
:
string
{
272
-
const
serverNames
=
[
options
.
domain
,
...
(
options
.
aliases
||
[
]
)
]
.
filter
(
Boolean
)
.
join
(
'
'
)
272
+
const
hosts
=
[
options
.
domain
,
...
(
options
.
aliases
||
[
]
)
]
.
filter
(
Boolean
)
as
string
[
]
273
+
// Defense in depth: every token here is interpolated straight into a
274
+
// `server_name` directive, and nginx is whitespace-insensitive — so anything
275
+
// that isn't a bare hostname could close this block and open another. Callers
276
+
// validate too; refuse here as well so no future path can slip a directive in.
277
+
//
278
+
// Deliberately laxer than `isValidHostname` (which the dashboard API uses for
279
+
// user-supplied domains and which requires a dot): a single label is valid
280
+
// here because compute-deploy falls back to `site.domain || siteName`, so an
281
+
// internal site legitimately arrives as `main` or `docs`. What matters for
282
+
// safety is only that a token can't contain whitespace, `;`, `{` or `}`.
283
+
for
(
const
host
of
hosts
)
{
284
+
if
(
!
/
^
(
?
=
.
{
1
,
253
}
$
)
(
?
:
\
*
\
.
)
?
[
a
-
z0
-
9
]
(
?
:
[
a
-
z0
-
9
-
]
{
0
,
61
}
[
a
-
z0
-
9
]
)
?
(
?
:
\
.
[
a
-
z0
-
9
]
(
?
:
[
a
-
z0
-
9
-
]
{
0
,
61
}
[
a
-
z0
-
9
]
)
?
)
*
$
/
i
.
test
(
host
.
trim
(
)
)
)
285
+
throw
new
Error
(
`Refusing to build a vhost: '${host}' is not a valid hostname.`
)
286
+
}
287
+
if
(
!
hosts
.
length
)
288
+
throw
new
Error
(
'Refusing to build a vhost: no server_name (domain) was given.'
)
289
+
const
serverNames
=
hosts
.
join
(
' '
)
273
290
const
body
=
vhostBody
(
options
)
274
291
275
292
if
(
options
.
ssl
)
{