From 2f87a133a28b35d237631bef1a2afc44664b565f Mon Sep 17 00:00:00 2001 From: Yurii Polishchuk Date: Mon, 9 Mar 2026 18:20:40 +0100 Subject: [PATCH 1/3] fix: registry commands now respect AWS_CONFIG_FILE environment variable granted registry add/sync/remove/setup ignored the AWS_CONFIG_FILE environment variable and always wrote to ~/.aws/config. The assume command was fixed for this in #229, but registry commands were missed. This change reuses cfaws.GetAWSConfigPath() which already handles AWS_CONFIG_FILE correctly, consistent with AWS CLI behavior: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html --- pkg/granted/registry/ini.go | 14 +++---- pkg/granted/registry/ini_test.go | 70 ++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 pkg/granted/registry/ini_test.go diff --git a/pkg/granted/registry/ini.go b/pkg/granted/registry/ini.go index 916c08c1..ef031bc3 100644 --- a/pkg/granted/registry/ini.go +++ b/pkg/granted/registry/ini.go @@ -4,21 +4,17 @@ import ( "fmt" "os" "path" - "path/filepath" "github.com/common-fate/clio" + "github.com/fwdcloudsec/granted/pkg/cfaws" "gopkg.in/ini.v1" ) -// Find the ~/.aws/config absolute path based on OS. +// getDefaultAWSConfigLocation returns the AWS config file path, +// respecting the AWS_CONFIG_FILE environment variable per +// https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html func getDefaultAWSConfigLocation() (string, error) { - h, err := os.UserHomeDir() - if err != nil { - return "", err - } - - configPath := filepath.Join(h, ".aws", "config") - return configPath, nil + return cfaws.GetAWSConfigPath(), nil } // loadAWSConfigFile loads the `~/.aws/config` file, and creates it if it doesn't exist. diff --git a/pkg/granted/registry/ini_test.go b/pkg/granted/registry/ini_test.go new file mode 100644 index 00000000..956841e9 --- /dev/null +++ b/pkg/granted/registry/ini_test.go @@ -0,0 +1,70 @@ +package registry + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetDefaultAWSConfigLocation(t *testing.T) { + tests := []struct { + name string + envValue string + wantCustom bool + }{ + { + name: "uses AWS_CONFIG_FILE when set", + envValue: "/custom/path/config", + wantCustom: true, + }, + { + name: "falls back to default when not set", + envValue: "", + wantCustom: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Setenv("AWS_CONFIG_FILE", tt.envValue) + + got, err := getDefaultAWSConfigLocation() + assert.NoError(t, err) + if tt.wantCustom { + assert.Equal(t, tt.envValue, got) + } else { + assert.Contains(t, got, ".aws/config") + } + }) + } +} + +func TestLoadAWSConfigFile_RespectsEnvVar(t *testing.T) { + // Create a temp dir with an AWS config file + tmpDir := t.TempDir() + customConfigPath := filepath.Join(tmpDir, "custom-aws-config") + err := os.WriteFile(customConfigPath, []byte("[profile test]\nregion = us-east-1\n"), 0600) + assert.NoError(t, err) + + t.Setenv("AWS_CONFIG_FILE", customConfigPath) + + cfg, path, err := loadAWSConfigFile() + assert.NoError(t, err) + assert.Equal(t, customConfigPath, path) + assert.NotNil(t, cfg) + + // Verify it loaded the correct file + sec, err := cfg.GetSection("profile test") + assert.NoError(t, err) + assert.Equal(t, "us-east-1", sec.Key("region").String()) +} + +func TestLoadAWSConfigFile_DefaultPath(t *testing.T) { + t.Setenv("AWS_CONFIG_FILE", "") + + _, path, err := loadAWSConfigFile() + assert.NoError(t, err) + assert.Contains(t, path, ".aws/config") +} From 6de7be7e0d5df3361954a477aa5829659e402d52 Mon Sep 17 00:00:00 2001 From: Yurii Polishchuk Date: Sun, 3 May 2026 20:39:00 +0200 Subject: [PATCH 2/3] fix(registry): inline cfaws.GetAWSConfigPath and harden default-path test Drop the getDefaultAWSConfigLocation wrapper and call cfaws.GetAWSConfigPath directly to match how assume / sso / credentials already resolve the AWS config path (per review on #927). Also sandbox HOME and tighten the path assertion in TestLoadAWSConfigFile_DefaultPath so it neither touches the user's real ~/.aws/ nor passes for unrelated paths like ".aws/config-backup". --- pkg/granted/registry/ini.go | 15 +++--------- pkg/granted/registry/ini_test.go | 41 ++++++-------------------------- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/pkg/granted/registry/ini.go b/pkg/granted/registry/ini.go index ef031bc3..5d18de49 100644 --- a/pkg/granted/registry/ini.go +++ b/pkg/granted/registry/ini.go @@ -10,19 +10,10 @@ import ( "gopkg.in/ini.v1" ) -// getDefaultAWSConfigLocation returns the AWS config file path, -// respecting the AWS_CONFIG_FILE environment variable per -// https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html -func getDefaultAWSConfigLocation() (string, error) { - return cfaws.GetAWSConfigPath(), nil -} - -// loadAWSConfigFile loads the `~/.aws/config` file, and creates it if it doesn't exist. +// loadAWSConfigFile loads the AWS config file, and creates it if it doesn't exist. +// It respects the AWS_CONFIG_FILE environment variable. func loadAWSConfigFile() (*ini.File, string, error) { - filepath, err := getDefaultAWSConfigLocation() - if err != nil { - return nil, "", err - } + filepath := cfaws.GetAWSConfigPath() if _, err := os.Stat(filepath); os.IsNotExist(err) { clio.Infof("created AWS config file: %s", filepath) diff --git a/pkg/granted/registry/ini_test.go b/pkg/granted/registry/ini_test.go index 956841e9..8d0c6577 100644 --- a/pkg/granted/registry/ini_test.go +++ b/pkg/granted/registry/ini_test.go @@ -8,39 +8,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestGetDefaultAWSConfigLocation(t *testing.T) { - tests := []struct { - name string - envValue string - wantCustom bool - }{ - { - name: "uses AWS_CONFIG_FILE when set", - envValue: "/custom/path/config", - wantCustom: true, - }, - { - name: "falls back to default when not set", - envValue: "", - wantCustom: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Setenv("AWS_CONFIG_FILE", tt.envValue) - - got, err := getDefaultAWSConfigLocation() - assert.NoError(t, err) - if tt.wantCustom { - assert.Equal(t, tt.envValue, got) - } else { - assert.Contains(t, got, ".aws/config") - } - }) - } -} - func TestLoadAWSConfigFile_RespectsEnvVar(t *testing.T) { // Create a temp dir with an AWS config file tmpDir := t.TempDir() @@ -62,9 +29,15 @@ func TestLoadAWSConfigFile_RespectsEnvVar(t *testing.T) { } func TestLoadAWSConfigFile_DefaultPath(t *testing.T) { + // Sandbox HOME: loadAWSConfigFile auto-creates ~/.aws/config when missing, + // so without this it would touch the real user's home dir on a fresh machine. + tmpHome := t.TempDir() + t.Setenv("HOME", tmpHome) t.Setenv("AWS_CONFIG_FILE", "") _, path, err := loadAWSConfigFile() assert.NoError(t, err) - assert.Contains(t, path, ".aws/config") + // Exact match — a substring check would also pass for unrelated paths + // like "/foo/.aws/config-backup". + assert.Equal(t, filepath.Join(tmpHome, ".aws", "config"), path) } From 06ef650a4c470e977910e5c2c53e44eef0dd9a7b Mon Sep 17 00:00:00 2001 From: Yurii Polishchuk Date: Tue, 12 May 2026 13:46:24 +0200 Subject: [PATCH 3/3] chore(lint): replace deprecated reflect.Ptr with reflect.Pointer Newer govet `inline` analyzer flags reflect.Ptr as a deprecated alias for reflect.Pointer and fails CI. --- pkg/granted/settings/set.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/granted/settings/set.go b/pkg/granted/settings/set.go index 1ddeb170..ebe85955 100644 --- a/pkg/granted/settings/set.go +++ b/pkg/granted/settings/set.go @@ -188,7 +188,7 @@ func FieldOptions(cfg any) map[string]Field { configValue := reflect.ValueOf(cfg) // Check if cfg is a pointer to a struct - if configType.Kind() == reflect.Ptr && configType.Elem().Kind() == reflect.Struct { + if configType.Kind() == reflect.Pointer && configType.Elem().Kind() == reflect.Struct { configType = configType.Elem() configValue = configValue.Elem() } else if configType.Kind() != reflect.Struct { @@ -212,7 +212,7 @@ func FieldOptions(cfg any) map[string]Field { } //subfield structs reflect as a pointer - if kind == reflect.Ptr { + if kind == reflect.Pointer { // Dereference the pointer to get the underlying value if !fieldValue.IsNil() { fieldValue = fieldValue.Elem()