Skip to content

Commit 1adf3bb

Browse files
committed
test(cmd): smoke-test git auth never leaks token into .git/config
Two end-to-end tests guard the http.extraheader migration: - setupGitConfig leaves origin URL credential-free - commitAndPushReadme against a local bare repo neither persists the token in .git/config nor leaves an extraheader entry behind, while still delivering the commit upstream
1 parent a4f744a commit 1adf3bb

1 file changed

Lines changed: 121 additions & 0 deletions

File tree

cmd/git_smoke_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
"path/filepath"
7+
"strings"
8+
"testing"
9+
)
10+
11+
// runGit is a tiny helper for shelling out to git during smoke tests.
12+
func runGit(t *testing.T, dir string, args ...string) {
13+
t.Helper()
14+
cmd := exec.Command("git", args...)
15+
cmd.Dir = dir
16+
if out, err := cmd.CombinedOutput(); err != nil {
17+
t.Fatalf("git %v in %s: %v\n%s", args, dir, err, out)
18+
}
19+
}
20+
21+
// TestSetupGitConfig_NoTokenInRemoteURL locks in the security guarantee:
22+
// setupGitConfig must set `origin` to a token-free URL so a leaked
23+
// .git/config cannot expose GITHUB_TOKEN. Auth is supplied per-push via
24+
// http.extraheader injected through GIT_CONFIG_* env vars.
25+
func TestSetupGitConfig_NoTokenInRemoteURL(t *testing.T) {
26+
if _, err := exec.LookPath("git"); err != nil {
27+
t.Skip("git not available on PATH")
28+
}
29+
30+
tmp := t.TempDir()
31+
work := filepath.Join(tmp, "work")
32+
if err := os.Mkdir(work, 0o755); err != nil {
33+
t.Fatal(err)
34+
}
35+
36+
runGit(t, work, "init", "-b", "main")
37+
runGit(t, work, "remote", "add", "origin", "https://example.com/placeholder.git")
38+
39+
// Sandbox `git config --global` writes so we never touch the user's real
40+
// ~/.gitconfig.
41+
t.Setenv("HOME", tmp)
42+
t.Setenv("XDG_CONFIG_HOME", filepath.Join(tmp, "xdg"))
43+
t.Chdir(work)
44+
45+
if err := setupGitConfig("smokeuser", "smoke", "smoke@example.com", false); err != nil {
46+
t.Fatalf("setupGitConfig: %v", err)
47+
}
48+
49+
cfg, err := os.ReadFile(filepath.Join(work, ".git", "config"))
50+
if err != nil {
51+
t.Fatal(err)
52+
}
53+
s := string(cfg)
54+
55+
if !strings.Contains(s, "https://github.com/smokeuser/smokeuser.git") {
56+
t.Errorf("expected clean github URL in .git/config, got:\n%s", s)
57+
}
58+
if strings.Contains(s, "@github.com") {
59+
t.Errorf(".git/config still embeds credentials in the URL:\n%s", s)
60+
}
61+
}
62+
63+
// TestCommitAndPushReadme_NoTokenLeak exercises the full commit+push flow
64+
// against a local bare repo with a fake token. It asserts:
65+
// 1. the push succeeds (the http.extraheader env vars don't break push)
66+
// 2. the fake token never lands in .git/config
67+
// 3. no `extraheader` config key persists across the push
68+
//
69+
// We deliberately do NOT call setupGitConfig here — it would overwrite
70+
// `origin` with a hard-coded github.com URL.
71+
func TestCommitAndPushReadme_NoTokenLeak(t *testing.T) {
72+
if _, err := exec.LookPath("git"); err != nil {
73+
t.Skip("git not available on PATH")
74+
}
75+
76+
tmp := t.TempDir()
77+
bare := filepath.Join(tmp, "remote.git")
78+
work := filepath.Join(tmp, "work")
79+
if err := os.Mkdir(work, 0o755); err != nil {
80+
t.Fatal(err)
81+
}
82+
83+
runGit(t, tmp, "init", "--bare", "-b", "main", bare)
84+
runGit(t, work, "init", "-b", "main")
85+
runGit(t, work, "config", "user.name", "smoke")
86+
runGit(t, work, "config", "user.email", "smoke@example.com")
87+
runGit(t, work, "remote", "add", "origin", bare)
88+
89+
if err := os.WriteFile(filepath.Join(work, "README.md"), []byte("hello\n"), 0o644); err != nil {
90+
t.Fatal(err)
91+
}
92+
93+
t.Chdir(work)
94+
95+
const fakeToken = "ghp_smoketest_AAAAAAAAAAAAAAAAAAAAAAAAAAA"
96+
97+
if err := commitAndPushReadme(fakeToken, "smoke test", "main", true); err != nil {
98+
t.Fatalf("commitAndPushReadme: %v", err)
99+
}
100+
101+
cfg, err := os.ReadFile(filepath.Join(work, ".git", "config"))
102+
if err != nil {
103+
t.Fatal(err)
104+
}
105+
s := string(cfg)
106+
107+
if strings.Contains(s, fakeToken) {
108+
t.Errorf(".git/config contains token after push:\n%s", s)
109+
}
110+
if strings.Contains(strings.ToLower(s), "extraheader") {
111+
t.Errorf(".git/config persists extraheader after push:\n%s", s)
112+
}
113+
114+
out, err := exec.Command("git", "--git-dir="+bare, "log", "--oneline", "main").Output()
115+
if err != nil {
116+
t.Fatalf("bare git log: %v", err)
117+
}
118+
if !strings.Contains(string(out), "smoke test") {
119+
t.Errorf("expected 'smoke test' commit in bare repo, got:\n%s", out)
120+
}
121+
}

0 commit comments

Comments
 (0)