fix(web): enforce upstream allowlist in vite dev proxy

Mirrors the nginx allowlist (spec section D) so make dev-web matches prod: when
OPENCONCHO_UPSTREAM_ALLOWLIST is set, non-matching upstreams get 403 +
X-Honcho-Proxy-Reject; unset stays open.
This commit is contained in:
Offending Commit
2026-06-02 13:25:12 -05:00
parent a2854ab8ea
commit b4fac95f37

View File

@@ -18,6 +18,19 @@ const { version } = JSON.parse(
// so req.url is already the upstream path (e.g. /v3/workspaces/list).
function honchoApiProxy(): Plugin {
const HEADER = "x-honcho-upstream";
// Mirror nginx's allowlist (spec §D): unset/empty => open; otherwise only
// matching upstream hosts forward. Glob `*` -> any non-slash run, like nginx.
const raw = process.env.OPENCONCHO_UPSTREAM_ALLOWLIST?.trim();
const allowlist: RegExp[] | null = raw
? raw
.split(",")
.map((h) => h.trim())
.filter(Boolean)
.map((host) => {
const esc = host.replace(/[.]/g, "\\.").replace(/[*]/g, "[^/]*");
return new RegExp(`^https?://${esc}(:[0-9]+)?(/.*)?$`);
})
: null;
return {
name: "honcho-api-proxy",
configureServer(server) {
@@ -29,6 +42,12 @@ function honchoApiProxy(): Plugin {
res.end();
return;
}
if (allowlist && !allowlist.some((re) => re.test(upstream))) {
res.statusCode = 403;
res.setHeader("X-Honcho-Proxy-Reject", "allowlist");
res.end();
return;
}
const target = upstream.replace(/\/+$/, "") + (req.url ?? "");
const chunks: Buffer[] = [];
for await (const c of req) chunks.push(c as Buffer);