Files
code-server/src/node/proxy.ts
Asher 701566b895 Merge commit from fork
* Strip token from cookies before proxying

Since this functionality requires information placed onto the request by
code-server (req.args) and Express (req.cookies), move the standalone
tests into the integration tests as the proxy can no longer run
correctly on its own without that context.

We could strip the header elsewhere or refactor in some way (pass in a
callback function for the stripping or something) but this seems like
the simplest and safest place at the moment to ensure we catch all uses
of the proxy.

In any case, I think it does lend more confidence to know we are testing
the proxy the way it will be used in practice.  The downside is some
additional complexity when setting up tests, but at the moment I do not
think that exchange is overly burdensome.

* Properly stringify the cookie

Cookie values need to be encoded, and they come off the req.cookies
already decoded.
2026-06-15 16:37:07 -08:00

40 lines
1.6 KiB
TypeScript

import * as cookie from "cookie"
import type { Request } from "express"
import proxyServer from "http-proxy"
import { getCookieSessionName, HttpCode } from "../common/http"
export const proxy = proxyServer.createProxyServer({})
// The error handler catches when the proxy fails to connect (for example when
// there is nothing running on the target port).
proxy.on("error", (error, _, res) => {
// This could be for either a web socket or a regular request. Despite what
// the types say, writeHead() will not exist on web socket requests (nor will
// status() from Express). But writing out the code manually does not work
// for regular requests thus the branching behavior.
if (typeof res.writeHead !== "undefined") {
res.writeHead(HttpCode.ServerError)
res.end(error.message)
} else {
res.end(`HTTP/1.1 ${HttpCode.ServerError} ${error.message}\r\n\r\n`)
}
})
// Strip the code-server cookie if it exists to avoid transmitting the cookie
// to potentially malicious local ports.
proxy.on("proxyReq", (preq, req) => {
const cookieSessionName = getCookieSessionName((req as Request).args["cookie-suffix"])
preq.setHeader("Cookie", cookie.stringifyCookie({
...(req as Request).cookies,
[cookieSessionName]: undefined,
}))
})
// Intercept the response to rewrite absolute redirects against the base path.
// Is disabled when the request has no base path which means /absproxy is in use.
proxy.on("proxyRes", (res, req) => {
if (res.headers.location && res.headers.location.startsWith("/") && (req as any).base) {
res.headers.location = (req as any).base + res.headers.location
}
})