Skip to content
File

Blob: src/worker/sandbox/git.ts

typescript90 lines
1import { encodeBase64 } from "@/worker/services/crypto";
2 
3export const GIT_AUTH_HEADER_ENV = "ANVIL_GIT_AUTH_HEADER";
4const textEncoder = new TextEncoder();
5 
6export interface GitCheckoutAuth {
7 sessionEnv: Record<string, string | undefined>;
8 redactionSecrets: string[];
9 hasAuthHeader: boolean;
10}
11 
12const parseCustomProviderUserInfo = (value: string): { username: string; password: string } => {
13 const separatorIndex = value.indexOf(":");
14 if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
15 throw new Error("Custom HTTPS repository credentials must use the format username:token.");
16 }
17 
18 return {
19 username: value.slice(0, separatorIndex),
20 password: value.slice(separatorIndex + 1),
21 };
22};
23 
24const buildBasicAuthHeader = (username: string, password: string): string =>
25 `Authorization: Basic ${encodeBase64(textEncoder.encode(`${username}:${password}`))}`;
26 
27export const buildGitCheckoutAuth = (repoUrl: string, token: string | null): GitCheckoutAuth => {
28 if (!token) {
29 return {
30 sessionEnv: {
31 GIT_TERMINAL_PROMPT: "0",
32 },
33 redactionSecrets: [],
34 hasAuthHeader: false,
35 };
36 }
37 
38 const hostname = new URL(repoUrl).hostname.toLowerCase();
39 
40 if (hostname === "github.com" || hostname.endsWith(".github.com")) {
41 const authHeader = buildBasicAuthHeader("x-access-token", token);
42 return {
43 sessionEnv: {
44 GIT_TERMINAL_PROMPT: "0",
45 [GIT_AUTH_HEADER_ENV]: authHeader,
46 },
47 redactionSecrets: [token, authHeader],
48 hasAuthHeader: true,
49 };
50 }
51 
52 if (hostname === "gitlab.com" || hostname.endsWith(".gitlab.com")) {
53 const authHeader = buildBasicAuthHeader("oauth2", token);
54 return {
55 sessionEnv: {
56 GIT_TERMINAL_PROMPT: "0",
57 [GIT_AUTH_HEADER_ENV]: authHeader,
58 },
59 redactionSecrets: [token, authHeader],
60 hasAuthHeader: true,
61 };
62 }
63 
64 const { username, password } = parseCustomProviderUserInfo(token);
65 const authHeader = buildBasicAuthHeader(username, password);
66 
67 return {
68 sessionEnv: {
69 GIT_TERMINAL_PROMPT: "0",
70 [GIT_AUTH_HEADER_ENV]: authHeader,
71 },
72 redactionSecrets: [token, password, authHeader],
73 hasAuthHeader: true,
74 };
75};
76 
77export const redactSecrets = (value: string, secrets: string[]): string => {
78 let output = value;
79 
80 for (const secret of secrets) {
81 if (!secret) {
82 continue;
83 }
84 
85 output = output.split(secret).join("[REDACTED]");
86 }
87 
88 return output;
89};