Fork Horse repository, listed by its owner. Publisher snapshot.
packages/forkhorse-runner/src/git.ts
Snapshot at 34e3175
1/** Validate an authority-selected HTTPS remote; credentials never belong in this URL. */
2export function remoteUrl(remote: string): URL {
3 const url = new URL(remote);
4 if (url.protocol !== "https:" || url.username || url.password || url.port ||
5 url.search || url.hash || url.pathname === "/" || url.pathname.endsWith("/") ||
6 url.href !== remote) throw new Error("Invalid fork remote");
7 return url;
8}
9
10/** Restrict egress to this fork's Git read protocol. Never follow a credential-bearing redirect. */
11export async function forwardGitRead(
12 request: Request, remote: string, forward: typeof fetch = fetch,
13): Promise<Response> {
14 const repo = remoteUrl(remote);
15 const url = new URL(request.url);
16 const allowed = url.origin === repo.origin && (
17 (request.method === "GET" && url.pathname === `${repo.pathname}/info/refs` &&
18 url.search === "?service=git-upload-pack") ||
19 (request.method === "POST" && url.pathname === `${repo.pathname}/git-upload-pack` && !url.search)
20 );
21 if (!allowed) return new Response("Forbidden", { status: 403 });
22 const headers = new Headers(request.headers);
23 headers.set("User-Agent", "git/2.47.0");
24 // Only Git protocol headers and the repo-scoped read authorization cross the boundary.
25 const safe = new Headers();
26 for (const name of ["authorization", "content-type", "accept", "git-protocol", "user-agent"]) {
27 const value = headers.get(name);
28 if (value) safe.set(name, value);
29 }
30 return forward(new Request(request, { headers: safe, redirect: "manual" }));
31}
32