1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//! Git PAT-injection command handler.
//!
//! Implements `tsafe git` — runs any git subcommand with the vault PAT injected
//! via `GIT_CONFIG_*` environment variables, avoiding URL mutation or temp files.
use anyhow::{Context, Result};
use colored::Colorize;
use tsafe_core::audit::AuditEntry;
use crate::helpers::*;
/// Run a git command with the vault PAT injected via http.extraHeader.
///
/// Looks for `ADO_PAT` in the vault (or the key named by `TSAFE_GIT_PAT_KEY`).
/// Encodes it as `Authorization: Basic base64(:PAT)` and injects it via git's
/// GIT_CONFIG_* env-var mechanism — no URL mutation, no temp files.
pub(crate) fn cmd_git(profile: &str, args: Vec<String>) -> Result<()> {
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use std::process::Command;
if args.is_empty() {
anyhow::bail!("no git subcommand given. Usage: tsafe git <subcommand> [args...]");
}
let vault = open_vault(profile)?;
// Determine which vault key holds the PAT (default: ADO_PAT).
let pat_key = std::env::var("TSAFE_GIT_PAT_KEY").unwrap_or_else(|_| "ADO_PAT".to_string());
let pat = vault.get(&pat_key).ok().map(|z| z.to_string());
// Drop vault (releases lock) before spawning — process::exit bypasses Drop.
drop(vault);
let mut cmd = Command::new("git");
cmd.args(&args);
if let Some(ref p) = pat {
// git accepts http.extraHeader via environment: GIT_CONFIG_COUNT + GIT_CONFIG_KEY_N + GIT_CONFIG_VALUE_N
let encoded = B64.encode(format!(":{p}"));
cmd.env("GIT_CONFIG_COUNT", "1");
cmd.env("GIT_CONFIG_KEY_0", "http.extraHeader");
cmd.env(
"GIT_CONFIG_VALUE_0",
format!("Authorization: Basic {encoded}"),
);
} else {
eprintln!(
"{} Vault key '{}' not found — running git without injected credentials.",
"i".blue(),
pat_key
);
}
audit(profile)
.append(&AuditEntry::success(profile, "git", None))
.ok();
let status = cmd.status().context("failed to spawn git")?;
std::process::exit(status.code().unwrap_or(1));
}