diff --git a/pkg/config/config.go b/pkg/config/config.go index 55a1fac13e..0b8dddd9e7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -110,8 +110,8 @@ func Load(path string) (c Global, err error) { // c.Verbose = true // c.Write(config.File()) func (c Global) Write(path string) (err error) { - bb, _ := yaml.Marshal(&c) // Marshaling no longer errors; this is back compat - return os.WriteFile(path, bb, os.ModePerm) + bb, _ := yaml.Marshal(&c) // Marshaling no longer errors; this is back compat + return os.WriteFile(path, bb, 0600) // Owner read/write only for security } // Apply populated values from a function to the config. @@ -218,10 +218,10 @@ func RepositoriesPath() string { // ~/.config/func // ~/.config/func/repositories func CreatePaths() (err error) { - if err = os.MkdirAll(Dir(), os.ModePerm); err != nil { + if err = os.MkdirAll(Dir(), 0755); err != nil { // rwxr-xr-x for directories return fmt.Errorf("error creating global config path: %v", err) } - if err = os.MkdirAll(RepositoriesPath(), os.ModePerm); err != nil { + if err = os.MkdirAll(RepositoriesPath(), 0755); err != nil { // rwxr-xr-x for directories return fmt.Errorf("error creating global config repositories path: %v", err) } return diff --git a/pkg/config/config_permissions_test.go b/pkg/config/config_permissions_test.go new file mode 100644 index 0000000000..2c29f438da --- /dev/null +++ b/pkg/config/config_permissions_test.go @@ -0,0 +1,109 @@ +package config + +import ( + "os" + "path/filepath" + "runtime" + "testing" +) + +// TestWrite_FilePermissions verifies that config files are written with +// secure permissions (0600) instead of overly permissive 0777. +func TestWrite_FilePermissions(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("File permission tests not applicable on Windows") + } + + // Create a temporary directory for testing + tmpDir := t.TempDir() + configPath := filepath.Join(tmpDir, "config.yaml") + + // Create a config and write it + cfg := New() + cfg.Builder = "test-builder" + cfg.Registry = "test-registry" + + err := cfg.Write(configPath) + if err != nil { + t.Fatalf("Failed to write config: %v", err) + } + + // Check file permissions + info, err := os.Stat(configPath) + if err != nil { + t.Fatalf("Failed to stat config file: %v", err) + } + + mode := info.Mode().Perm() + expectedMode := os.FileMode(0600) + + if mode != expectedMode { + t.Errorf("Config file has incorrect permissions: got %o, want %o", mode, expectedMode) + } + + // Verify the file is readable and writable by owner + if mode&0400 == 0 { + t.Error("Config file is not readable by owner") + } + if mode&0200 == 0 { + t.Error("Config file is not writable by owner") + } + + // Verify the file is NOT accessible by group or others + if mode&0040 != 0 { + t.Error("Config file is readable by group (should not be)") + } + if mode&0020 != 0 { + t.Error("Config file is writable by group (should not be)") + } + if mode&0004 != 0 { + t.Error("Config file is readable by others (should not be)") + } + if mode&0002 != 0 { + t.Error("Config file is writable by others (should not be)") + } +} + +// TestCreatePaths_DirectoryPermissions verifies that config directories +// are created with appropriate permissions (0755). +func TestCreatePaths_DirectoryPermissions(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("File permission tests not applicable on Windows") + } + + // Set up a temporary XDG_CONFIG_HOME + tmpDir := t.TempDir() + t.Setenv("XDG_CONFIG_HOME", tmpDir) + + // Create the paths + err := CreatePaths() + if err != nil { + t.Fatalf("Failed to create paths: %v", err) + } + + // Check directory permissions + configDir := Dir() + info, err := os.Stat(configDir) + if err != nil { + t.Fatalf("Failed to stat config directory: %v", err) + } + + mode := info.Mode().Perm() + expectedMode := os.FileMode(0755) + + if mode != expectedMode { + t.Errorf("Config directory has incorrect permissions: got %o, want %o", mode, expectedMode) + } + + // Check repositories directory permissions + repoDir := RepositoriesPath() + info, err = os.Stat(repoDir) + if err != nil { + t.Fatalf("Failed to stat repositories directory: %v", err) + } + + mode = info.Mode().Perm() + if mode != expectedMode { + t.Errorf("Repositories directory has incorrect permissions: got %o, want %o", mode, expectedMode) + } +}