Abort startup on a config file pixa cannot read (closes #176)
check / check (push) Failing after 4s
check / check (push) Failing after 4s
Of the places pixa looks for its config file on its own, it passed over any place where os.Stat failed, so a file in a directory pixa may not enter was skipped without a word and pixa started on a later file or on the environment and defaults. Now only a path that does not exist, or that runs through a file (such as under a HOME of /dev/null), is passed over; any other error aborts startup naming the file, as a file that does not parse already did. README.md says so where it gives the search order. A config.yml that links to itself tests this as root too. Model: opus-5-5
This commit was merged in pull request #181.
This commit is contained in:
@@ -564,6 +564,116 @@ func TestMalformedConfigFileAbortsStartup(t *testing.T) {
|
||||
t.Logf("got expected error: %v", err)
|
||||
}
|
||||
|
||||
// TestConfigFileInDirectoryPixaMayNotEnterAbortsStartup checks that a
|
||||
// config file pixa cannot read because it may not enter its directory
|
||||
// aborts startup instead of being passed over.
|
||||
func TestConfigFileInDirectoryPixaMayNotEnterAbortsStartup(t *testing.T) {
|
||||
if os.Geteuid() == 0 {
|
||||
t.Skip("root may enter any directory")
|
||||
}
|
||||
|
||||
home := t.TempDir()
|
||||
configDir := filepath.Join(home, ".config", "pixa-test-nonexistent-app")
|
||||
configPath := filepath.Join(configDir, "config.yml")
|
||||
|
||||
err := os.MkdirAll(configDir, 0o700)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create config directory: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(configPath, []byte(signingKeyLine), 0o600)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write config: %v", err)
|
||||
}
|
||||
|
||||
err = os.Chmod(configDir, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove the config directory's permissions: %v", err)
|
||||
}
|
||||
|
||||
// Give the directory back its permissions so t.TempDir can remove it.
|
||||
t.Cleanup(func() {
|
||||
//nolint:gosec // G302: a directory needs its execute bit to be removed
|
||||
_ = os.Chmod(configDir, 0o700)
|
||||
})
|
||||
|
||||
// The ~/.config candidate is the only one that exists: the appname
|
||||
// rules out /etc, and the working directory is empty.
|
||||
t.Setenv("PIXA_CONFIG_PATH", "")
|
||||
t.Setenv("HOME", home)
|
||||
t.Chdir(t.TempDir())
|
||||
|
||||
log := slog.New(slog.DiscardHandler)
|
||||
|
||||
sc, err := loadConfigFile(log, "pixa-test-nonexistent-app")
|
||||
if err == nil {
|
||||
t.Fatalf("config file pixa cannot read must abort startup, got config: %v",
|
||||
sc)
|
||||
}
|
||||
|
||||
t.Logf("got expected error: %v", err)
|
||||
|
||||
if !strings.Contains(err.Error(), configPath) {
|
||||
t.Errorf("error %q does not name the config file %s", err.Error(), configPath)
|
||||
}
|
||||
}
|
||||
|
||||
// TestConfigFileLinkingToItselfAbortsStartup checks that a config file
|
||||
// pixa cannot read for a reason other than not existing aborts startup,
|
||||
// as root too: a symbolic link to itself fails with "too many levels of
|
||||
// symbolic links".
|
||||
func TestConfigFileLinkingToItselfAbortsStartup(t *testing.T) {
|
||||
workDir := t.TempDir()
|
||||
|
||||
err := os.Symlink("config.yml", filepath.Join(workDir, "config.yml"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create symbolic link: %v", err)
|
||||
}
|
||||
|
||||
// Only the working directory's config.yml is there: the appname rules
|
||||
// out /etc, and HOME is empty.
|
||||
t.Setenv("PIXA_CONFIG_PATH", "")
|
||||
t.Setenv("HOME", t.TempDir())
|
||||
t.Chdir(workDir)
|
||||
|
||||
log := slog.New(slog.DiscardHandler)
|
||||
|
||||
sc, err := loadConfigFile(log, "pixa-test-nonexistent-app")
|
||||
if err == nil {
|
||||
t.Fatalf("config file pixa cannot read must abort startup, got config: %v",
|
||||
sc)
|
||||
}
|
||||
|
||||
t.Logf("got expected error: %v", err)
|
||||
|
||||
if !strings.Contains(err.Error(), "config.yml") {
|
||||
t.Errorf("error %q does not name the config file config.yml", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// TestConfigPathThroughFileIsPassedOver checks that a config file path
|
||||
// that runs through a file, such as one under a HOME of /dev/null, is
|
||||
// passed over like one that does not exist, since no file can be there.
|
||||
func TestConfigPathThroughFileIsPassedOver(t *testing.T) {
|
||||
// No config file is there: the appname rules out /etc, HOME is
|
||||
// /dev/null, and the working directory is empty.
|
||||
t.Setenv("PIXA_CONFIG_PATH", "")
|
||||
t.Setenv("HOME", os.DevNull)
|
||||
t.Chdir(t.TempDir())
|
||||
|
||||
log := slog.New(slog.DiscardHandler)
|
||||
|
||||
sc, err := loadConfigFile(log, "pixa-test-nonexistent-app")
|
||||
if err != nil {
|
||||
t.Fatalf("a config path through a file must be passed over, got error: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
if sc != nil {
|
||||
t.Errorf("expected no config file, got config: %v", sc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureStateDirCreatesDirectory(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user