mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-08 09:37:47 +03:00
72 lines
3.3 KiB
TypeScript
72 lines
3.3 KiB
TypeScript
import fs from "node:fs/promises"
|
|
import path from "node:path"
|
|
import { expect } from "bun:test"
|
|
import { Effect } from "effect"
|
|
import { tmpdirScoped } from "../../core/test/fixture/tmpdir"
|
|
import { it } from "../../core/test/lib/effect"
|
|
import { startServer } from "./fixture/server"
|
|
|
|
it.live("rejects API navigations and calls from other sites while other clients pass", () =>
|
|
Effect.gen(function* () {
|
|
const tmp = yield* tmpdirScoped()
|
|
yield* Effect.promise(() => fs.writeFile(path.join(tmp.path, "page.html"), "<p>hi</p>"))
|
|
const server = yield* startServer(path.join(tmp.path, "config"))
|
|
const read = new URL("/api/fs/read/page.html", server.base)
|
|
read.searchParams.set("location[directory]", tmp.path)
|
|
const status = (url: URL | string, headers: Record<string, string>, method = "GET") =>
|
|
Effect.promise(async () => {
|
|
const response = await fetch(new URL(url, server.base), {
|
|
method,
|
|
headers: { ...server.headers, ...headers },
|
|
})
|
|
return response.status
|
|
})
|
|
const navigate = (site: string) => ({
|
|
"sec-fetch-site": site,
|
|
"sec-fetch-mode": "navigate",
|
|
"sec-fetch-dest": "document",
|
|
})
|
|
|
|
// Native clients, same-origin pages and typed-in URLs.
|
|
expect(yield* status(read, {})).toBe(200)
|
|
expect(yield* status(read, navigate("same-origin"))).toBe(200)
|
|
expect(yield* status(read, navigate("none"))).toBe(200)
|
|
expect(yield* status(read, { "sec-fetch-site": "same-origin", "sec-fetch-mode": "cors" })).toBe(200)
|
|
|
|
// Another site opening an API URL as a page.
|
|
expect(yield* status(read, navigate("cross-site"))).toBe(403)
|
|
expect(yield* status(read, navigate("same-site"))).toBe(403)
|
|
expect(yield* status("/api/path", navigate("cross-site"))).toBe(403)
|
|
expect(yield* status("/api/path", navigate("cross-site"), "POST")).toBe(403)
|
|
|
|
// Calls from other sites need an allowed origin; media elements send none.
|
|
const cors = (origin: string) => ({ "sec-fetch-site": "cross-site", "sec-fetch-mode": "cors", origin })
|
|
expect(yield* status(read, cors("https://app.opencode.ai"))).toBe(200)
|
|
expect(yield* status(read, cors("oc://renderer"))).toBe(200)
|
|
expect(yield* status(read, cors("http://localhost:3000"))).toBe(200)
|
|
expect(yield* status(read, cors("https://example.com"))).toBe(403)
|
|
expect(yield* status("/api/path", cors("null"), "POST")).toBe(403)
|
|
expect(
|
|
yield* status(
|
|
"/api/path",
|
|
{ "sec-fetch-site": "cross-site", "sec-fetch-mode": "no-cors", origin: "https://example.com" },
|
|
"POST",
|
|
),
|
|
).toBe(403)
|
|
expect(
|
|
yield* status(read, { "sec-fetch-site": "cross-site", "sec-fetch-mode": "no-cors", "sec-fetch-dest": "video" }),
|
|
).toBe(200)
|
|
|
|
// Pairing links and artifact pages are opened from elsewhere on purpose.
|
|
expect(yield* status("/auth/connect/expired", { ...navigate("cross-site"), accept: "text/html" })).toBe(401)
|
|
const artifact = new URL("/api/experimental/artifacts/art_missing/view/", server.base)
|
|
artifact.searchParams.set("location[directory]", tmp.path)
|
|
const opened = yield* Effect.promise(() =>
|
|
fetch(artifact, { headers: { ...server.headers, ...navigate("cross-site") } }).then((response) =>
|
|
response.json(),
|
|
),
|
|
)
|
|
expect(opened.message).not.toBe("Cross-site requests to the API are not allowed")
|
|
}),
|
|
)
|