// Préparation d'un environnement d'authentification git ÉPHÉMÈRE (P12). HTTPS (pat/app_password) : // les identifiants sont fournis via GIT_ASKPASS (script à permissions restreintes lisant deux variables // d'env), JAMAIS dans l'URL ni dans `.git/config`. GIT_TERMINAL_PROMPT=0 (pas d'invite bloquante). Le // script est supprimé en `finally` ; le secret ne transite que par l'env du process enfant (jamais loggé). import { mkdtemp, writeFile, rm, chmod } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import type { GitService } from '@arboretum/shared'; import type { GitAuth } from './git-clients/index.js'; // Identité HTTPS par défaut quand l'utilisateur n'a pas fourni de username (token-as-password). const SERVICE_DEFAULT_USER: Record = { github: 'x-access-token', gitlab: 'oauth2', gitea: 'oauth2' }; const ASKPASS_SH = "#!/bin/sh\ncase \"$1\" in\n Username*) printf '%s' \"$ARB_GIT_USER\" ;;\n *) printf '%s' \"$ARB_GIT_PASS\" ;;\nesac\n"; // Équivalent Windows : git appelle GIT_ASKPASS avec l'invite en argument. `echo` de cmd.exe ajoute un // saut de ligne que git tolère (il trime la réponse). `~1` = premier argument sans les guillemets. const ASKPASS_CMD = [ '@echo off', 'echo %~1 | findstr /b /i "Username" >nul', 'if %errorlevel%==0 (echo %ARB_GIT_USER%) else (echo %ARB_GIT_PASS%)', '', ].join('\r\n'); /** * Nom et contenu du script askpass selon la plateforme. Un `.sh` avec shebang n'est PAS exécutable sur * Windows : sans cette variante `.cmd`, tout clone/push HTTPS par jeton y échouait silencieusement * (git n'obtenait aucun identifiant et abandonnait, GIT_TERMINAL_PROMPT étant à 0). */ export function askpassScript(platform: NodeJS.Platform = process.platform): { name: string; content: string; mode: number } { return platform === 'win32' ? { name: 'askpass.cmd', content: ASKPASS_CMD, mode: 0o700 } : { name: 'askpass.sh', content: ASKPASS_SH, mode: 0o700 }; } export async function withGitAuth( service: GitService, auth: GitAuth, fn: (env: NodeJS.ProcessEnv) => Promise, ): Promise { const dir = await mkdtemp(join(tmpdir(), 'arb-gitauth-')); const script = askpassScript(); const askpass = join(dir, script.name); const user = auth.username || SERVICE_DEFAULT_USER[service]; await writeFile(askpass, script.content, { mode: script.mode }); // chmod best-effort : sans effet sur NTFS (comme ailleurs dans le code, cf. config.ts). await chmod(askpass, script.mode).catch(() => {}); const env: NodeJS.ProcessEnv = { ...process.env, GIT_ASKPASS: askpass, GIT_TERMINAL_PROMPT: '0', ARB_GIT_USER: user, ARB_GIT_PASS: auth.secret, }; try { return await fn(env); } finally { await rm(dir, { recursive: true, force: true }).catch(() => {}); } }