TestConfigFilePermissionsAreOwnerOnly is the regression for a world-readable hamrpass key: /hamrpass stores the bearer token in plaintext, so any local user could cat it. Fresh-bootstrap and post-Save paths must both write 0o600.
(t *testing.T)
| 306 | // hamrpass key: /hamrpass stores the bearer token in plaintext, so any local |
| 307 | // user could cat it. Fresh-bootstrap and post-Save paths must both write 0o600. |
| 308 | func TestConfigFilePermissionsAreOwnerOnly(t *testing.T) { |
| 309 | dir := t.TempDir() |
| 310 | cfg, _, err := Bootstrap(dir) |
| 311 | if err != nil { |
| 312 | t.Fatal(err) |
| 313 | } |
| 314 | cfgPath := filepath.Join(dir, DirName, "config.yaml") |
| 315 | st, err := os.Stat(cfgPath) |
| 316 | if err != nil { |
| 317 | t.Fatal(err) |
| 318 | } |
| 319 | if got := st.Mode().Perm(); got != 0o600 { |
| 320 | t.Fatalf("fresh config.yaml perms = %v, want 0o600 (key may leak to other local users)", got) |
| 321 | } |
| 322 | |
| 323 | // Save() must keep 0o600; otherwise a /hamrpass write would widen perms |
| 324 | // right after the user pasted a key. |
| 325 | cfg.Models["hamrpass"].Key = "hp-secret-12345678" |
| 326 | if err := cfg.Save(); err != nil { |
| 327 | t.Fatal(err) |
| 328 | } |
| 329 | st2, err := os.Stat(cfgPath) |
| 330 | if err != nil { |
| 331 | t.Fatal(err) |
| 332 | } |
| 333 | if got := st2.Mode().Perm(); got != 0o600 { |
| 334 | t.Fatalf("Save() widened config.yaml perms to %v (must stay 0o600)", got) |
| 335 | } |
| 336 | |
| 337 | // The .codehamr/ dir mustn't be world-listable either: even with a 0o600 |
| 338 | // config.yaml, a listable parent leaks the key's existence and invites probing. |
| 339 | parentSt, err := os.Stat(filepath.Join(dir, DirName)) |
| 340 | if err != nil { |
| 341 | t.Fatal(err) |
| 342 | } |
| 343 | if got := parentSt.Mode().Perm(); got&0o077 != 0 { |
| 344 | t.Fatalf(".codehamr/ dir perms = %v - must not grant any other-user bits", got) |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | // TestBootstrapTightensLooseDirPerms: a .codehamr/ created loose (older |
| 349 | // release, or by hand at 0o755) must be tightened on the next Bootstrap, the |