Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,23 @@ function corsHeaders(request) {
const requestedMethod = request?.headers.get("access-control-request-method")
const requestedPrivateNetwork = request?.headers.get("access-control-request-private-network")
const requestOrigin = request?.headers.get("origin") ?? ""
const allowOrigin = configuredOrigin === "*" ? "*" : (requestOrigin === configuredOrigin ? requestOrigin : configuredOrigin)
// When a specific origin is configured, only echo it back when the request
// origin matches exactly. On a mismatch we omit the header entirely so that
// browsers block the cross-origin request and the configured origin is not
// disclosed to untrusted callers.
const allowOrigin = configuredOrigin === "*" ? "*" : (requestOrigin === configuredOrigin ? requestOrigin : null)

const headers = {
vary: "origin, access-control-request-method, access-control-request-headers",
"access-control-allow-origin": allowOrigin,
"access-control-allow-headers": requestedHeaders ?? "authorization, content-type, x-opencode-provider",
"access-control-allow-methods": requestedMethod ?? "GET, POST, OPTIONS",
"access-control-max-age": "86400",
}

if (allowOrigin !== null) {
headers["access-control-allow-origin"] = allowOrigin
}

if (requestedPrivateNetwork === "true") {
headers["access-control-allow-private-network"] = "true"
}
Expand Down
19 changes: 10 additions & 9 deletions index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,14 @@ test("health response includes CORS headers", async () => {
assert.deepEqual(body, { healthy: true, service: "opencode-openai-proxy" })
})

test("configured origin is returned for normal requests", async () => {
test("configured origin is returned when request origin matches", async () => {
process.env.OPENCODE_LLM_PROXY_CORS_ORIGIN = "https://console.example.com"

try {
const handler = createProxyFetchHandler(createClient())
const request = new Request("http://127.0.0.1:4010/health", {
headers: {
Origin: "https://app.example.com",
Origin: "https://console.example.com",
},
})

Expand All @@ -181,7 +181,7 @@ test("configured origin is returned for normal requests", async () => {
}
})

test("disallowed origin does not receive its own origin back", async () => {
test("disallowed origin does not receive access-control-allow-origin header", async () => {
process.env.OPENCODE_LLM_PROXY_CORS_ORIGIN = "https://allowed.example.com"

try {
Expand All @@ -192,9 +192,9 @@ test("disallowed origin does not receive its own origin back", async () => {

const response = await handler(request)

// The header must be the configured origin, not the request's origin
assert.equal(response.headers.get("access-control-allow-origin"), "https://allowed.example.com")
assert.notEqual(response.headers.get("access-control-allow-origin"), "https://evil.example.com")
// On an origin mismatch the header must be absent so neither the
// configured origin is leaked nor the browser grants cross-origin access.
assert.equal(response.headers.get("access-control-allow-origin"), null)
} finally {
delete process.env.OPENCODE_LLM_PROXY_CORS_ORIGIN
}
Expand All @@ -211,7 +211,7 @@ test("request with no Origin header is handled gracefully", async () => {
assert.equal(response.headers.get("access-control-allow-origin"), "*")
})

test("OPTIONS preflight for disallowed origin returns configured origin, not request origin", async () => {
test("OPTIONS preflight for disallowed origin omits access-control-allow-origin header", async () => {
process.env.OPENCODE_LLM_PROXY_CORS_ORIGIN = "https://allowed.example.com"

try {
Expand All @@ -227,8 +227,9 @@ test("OPTIONS preflight for disallowed origin returns configured origin, not req
const response = await handler(request)

assert.equal(response.status, 204)
assert.equal(response.headers.get("access-control-allow-origin"), "https://allowed.example.com")
assert.notEqual(response.headers.get("access-control-allow-origin"), "https://evil.example.com")
// The configured origin must not be disclosed on a mismatch; the header
// should be absent so the preflight is rejected by the browser.
assert.equal(response.headers.get("access-control-allow-origin"), null)
} finally {
delete process.env.OPENCODE_LLM_PROXY_CORS_ORIGIN
}
Expand Down