From 6da5b249f90935e5d44331366ea8f1a57a39f23f Mon Sep 17 00:00:00 2001 From: "Brandon McGinty (chatgpt)" Date: Sun, 9 Aug 2026 22:35:38 -0400 Subject: [PATCH] Reject missing explicit configuration files --- config/user_config.go | 14 ++++++++++++++ config/user_config_test.go | 9 +++++++++ main.go | 13 ++++++++++--- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/config/user_config.go b/config/user_config.go index c280cd8..7e9aab7 100644 --- a/config/user_config.go +++ b/config/user_config.go @@ -356,6 +356,20 @@ func (c *Config) UpdateConfig(u *gumble.User) { j.LocallyMuted = u.LocallyMuted() // Save LocallyMuted state to config } +// RequireConfigFile verifies that an explicitly requested configuration file +// exists and is a regular file. The default configuration remains optional. +func RequireConfigFile(fn string) error { + path := resolvePath(fn) + info, err := os.Stat(path) + if err != nil { + return fmt.Errorf("config file %q: %w", path, err) + } + if info.IsDir() { + return fmt.Errorf("config file %q is a directory", path) + } + return nil +} + func NewConfig(fn *string) *Config { var c *Config c = &Config{} diff --git a/config/user_config_test.go b/config/user_config_test.go index 514df37..0f7e474 100644 --- a/config/user_config_test.go +++ b/config/user_config_test.go @@ -8,6 +8,15 @@ import ( "git.stormux.org/storm/barnard/uiterm" ) +// Regression: an explicit -config path silently fell back to in-memory +// defaults, then overwrote the intended file on exit. +func TestRequireConfigFileRejectsMissingExplicitPath(t *testing.T) { + missing := filepath.Join(t.TempDir(), "missing.toml") + if err := RequireConfigFile(missing); err == nil { + t.Fatal("missing explicit config was accepted") + } +} + func TestConfigBackfillsRecordingDefaults(t *testing.T) { dir := t.TempDir() configPath := filepath.Join(dir, "barnard.toml") diff --git a/main.go b/main.go index d13e6b2..95a925d 100644 --- a/main.go +++ b/main.go @@ -129,6 +129,8 @@ func main() { fifo := flag.String("fifo", "", "path of a FIFO from which to read commands") serverSet := false usernameSet := false + configSet := false + certificateSet := false buffers := flag.Int("buffers", 16, "number of audio buffers to use") profile := flag.Bool("profile", false, "add http server to serve profiles") noiseSuppressionEnabled := flag.Bool("noise-suppression", false, "enable noise suppression for microphone input") @@ -173,19 +175,24 @@ func main() { }() } - userConfig := config.NewConfig(cfgfn) - - certificateSet := false flag.CommandLine.Visit(func(theFlag *flag.Flag) { switch theFlag.Name { case "server": serverSet = true case "username": usernameSet = true + case "config": + configSet = true case "certificate": certificateSet = true } }) + if configSet { + if err := config.RequireConfigFile(*cfgfn); err != nil { + handle_raw_error(err) + } + } + userConfig := config.NewConfig(cfgfn) if !serverSet { server = userConfig.GetDefaultServer()