package safety import ( "os" "path/filepath" "sort" "testing" ) func TestScanCWDForSensitive_Matches(t *testing.T) { dir := t.TempDir() // Sensitive files we expect to flag. sensitive := []string{ ".env", ".env.local", "id_rsa", "private.pem", "aws_credentials", ".netrc", "vault.kdbx", } // Non-sensitive control files. control := []string{ ".envrc", // direnv config, not a credential "main.go", "README.md", "secret_handler.go", // source code, not data } for _, f := range sensitive { if err := os.WriteFile(filepath.Join(dir, f), []byte("x"), 0o600); err != nil { t.Fatal(err) } } for _, f := range control { if err := os.WriteFile(filepath.Join(dir, f), []byte("x"), 0o600); err != nil { t.Fatal(err) } } // Sensitive directory. if err := os.MkdirAll(filepath.Join(dir, ".ssh"), 0o700); err != nil { t.Fatal(err) } matches := ScanCWDForSensitive(dir) wantNames := append([]string{}, sensitive...) wantNames = append(wantNames, ".ssh") sort.Strings(wantNames) gotNames := make([]string, 0, len(matches)) for _, m := range matches { gotNames = append(gotNames, filepath.Base(m.Path)) } sort.Strings(gotNames) if len(gotNames) != len(wantNames) { t.Errorf("matched %d files (%v), want %d (%v)", len(gotNames), gotNames, len(wantNames), wantNames) } for i, n := range wantNames { if i >= len(gotNames) || gotNames[i] != n { t.Errorf("match[%d] = %q, want %q (got=%v want=%v)", i, gotNames[i], n, gotNames, wantNames) } } } func TestScanCWDForSensitive_EmptyDir(t *testing.T) { dir := t.TempDir() matches := ScanCWDForSensitive(dir) if len(matches) != 0 { t.Errorf("empty dir matched %v, want none", matches) } } func TestScanCWDForSensitive_PrecisionNoFalsePositives(t *testing.T) { dir := t.TempDir() // Files that look credential-y but conventionally hold no // secrets — must NOT be flagged. control := []string{ ".envrc", // direnv config "secret_handler.go", // source code ".env.example", // template ".env.sample", // template ".env.template", // template ".env.dist", // template ".env.default", // template "env.local.example", // template } for _, name := range control { if err := os.WriteFile(filepath.Join(dir, name), []byte("x"), 0o600); err != nil { t.Fatal(err) } } matches := ScanCWDForSensitive(dir) if len(matches) != 0 { names := make([]string, 0, len(matches)) for _, m := range matches { names = append(names, filepath.Base(m.Path)) } t.Errorf("precision regression: none of %v should flag, got %v", control, names) } } func TestScanCWDForSensitive_RealEnvFilesStillMatch(t *testing.T) { dir := t.TempDir() // Real env files (non-template) must still be flagged. real := []string{ ".env", ".env.local", ".env.production", ".env.staging", "env.local", "env.local.production", } for _, name := range real { if err := os.WriteFile(filepath.Join(dir, name), []byte("API_KEY=secret"), 0o600); err != nil { t.Fatal(err) } } matches := ScanCWDForSensitive(dir) if len(matches) != len(real) { got := make([]string, 0, len(matches)) for _, m := range matches { got = append(got, filepath.Base(m.Path)) } t.Errorf("expected %d real env files flagged, got %d (%v)", len(real), len(matches), got) } } func TestScanCWDForSensitive_BoundedScan(t *testing.T) { dir := t.TempDir() // Populate just over the scan limit. The function should not panic // or hang. Result count is at most scanLimit (matches may be 0 if // the entries beyond the cap happen to be sensitive — that's OK, // the bound is a safety knob, not a correctness one). for i := 0; i < scanLimit+10; i++ { if err := os.WriteFile(filepath.Join(dir, "file"+itoa(i)), []byte("x"), 0o600); err != nil { t.Fatal(err) } } _ = ScanCWDForSensitive(dir) // mustn't panic } // itoa avoids importing strconv just for one use. func itoa(n int) string { if n == 0 { return "0" } var buf [20]byte i := len(buf) for n > 0 { i-- buf[i] = byte('0' + n%10) n /= 10 } return string(buf[i:]) }