Skip to content

Commit 0e007ef

Browse files
authored
Merge pull request #99 from shubham-stepsecurity/sm/fix
fix(paths): add support for expanding $HOME
2 parents 567a4a3 + 11e2f91 commit 0e007ef

2 files changed

Lines changed: 108 additions & 4 deletions

File tree

internal/paths/paths.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ package paths
1616

1717
import (
1818
"os"
19+
"path/filepath"
20+
"strings"
1921

2022
"github.com/step-security/dev-machine-guard/internal/config"
2123
)
@@ -38,20 +40,62 @@ func SetOverride(s string) {
3840

3941
// Home returns the resolved install dir. Falls back to LegacyHome when
4042
// nothing else is set. Empty string is possible only when the home
41-
// directory itself cannot be resolved.
43+
// directory itself cannot be resolved. A leading $HOME or ~ token in
44+
// any source is expanded via expandHome so the returned path is
45+
// canonical for the current OS, keeping the migration warning in main
46+
// from misfiring on hand-edited values like "$HOME/.stepsecurity" that
47+
// resolve to the legacy default.
48+
//
49+
// Note: this is a superset of resolveSearchDirs in internal/scan/scanner.go,
50+
// which only expands the exact literal "$HOME" — the install dir comes
51+
// from operator-edited config so it has to tolerate the "$HOME/foo" /
52+
// "~/foo" forms our docs use; search_dirs come from --search-dirs flag
53+
// values that operators don't combine with subpaths.
4254
func Home() string {
4355
if cliOverride != "" {
44-
return cliOverride
56+
return expandHome(cliOverride)
4557
}
4658
if v := os.Getenv(HomeEnvVar); v != "" {
47-
return v
59+
return expandHome(v)
4860
}
4961
if config.InstallDir != "" {
50-
return config.InstallDir
62+
return expandHome(config.InstallDir)
5163
}
5264
return LegacyHome()
5365
}
5466

67+
// expandHome replaces a leading $HOME or ~ token with the resolved
68+
// user home directory. Returns the input unchanged when the home
69+
// directory cannot be resolved or no token is present. Callers that
70+
// hand-edit config.json with "$HOME/.stepsecurity" — the literal value
71+
// our docs use — get the same canonical form as LegacyHome(), which
72+
// keeps the migration warning in main from misfiring on identical
73+
// paths.
74+
func expandHome(s string) string {
75+
if s == "" {
76+
return s
77+
}
78+
if !strings.HasPrefix(s, "$HOME") && !strings.HasPrefix(s, "~") {
79+
return s
80+
}
81+
home, err := os.UserHomeDir()
82+
if err != nil || home == "" {
83+
return s
84+
}
85+
switch {
86+
case s == "$HOME" || s == "~":
87+
return home
88+
case strings.HasPrefix(s, "$HOME/") || strings.HasPrefix(s, `$HOME\`):
89+
// filepath.Join + Clean canonicalises separators so Windows
90+
// gets C:\Users\me\.stepsecurity (not C:\Users\me/.stepsecurity)
91+
// and the equality check against LegacyHome() can succeed.
92+
return filepath.Join(home, s[len("$HOME"):])
93+
case strings.HasPrefix(s, "~/") || strings.HasPrefix(s, `~\`):
94+
return filepath.Join(home, s[len("~"):])
95+
}
96+
return s
97+
}
98+
5599
// LegacyHome returns ~/.stepsecurity. Exposed for the migration check
56100
// in main and for ShowConfigure displays. Mirrors config.LegacyDir but
57101
// kept here so callers can grab the legacy path without taking a

internal/paths/paths_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package paths
22

33
import (
44
"os"
5+
"path/filepath"
56
"strings"
67
"testing"
78

@@ -86,6 +87,65 @@ func TestHome_CLIOverridesEnv(t *testing.T) {
8687
}
8788
}
8889

90+
func TestHome_ExpandsHomeTokenFromConfig(t *testing.T) {
91+
withOverride(t, "")
92+
withEnv(t, HomeEnvVar, "")
93+
withConfigInstallDir(t, "$HOME/.stepsecurity")
94+
95+
home, err := os.UserHomeDir()
96+
if err != nil || home == "" {
97+
t.Skip("home dir unresolved in this environment")
98+
}
99+
want := filepath.Join(home, ".stepsecurity")
100+
if got := Home(); got != want {
101+
t.Errorf("Home() = %q, want %q (config $HOME should expand)", got, want)
102+
}
103+
// And the migration warning's equality check must now succeed.
104+
if Home() != LegacyHome() {
105+
t.Errorf("Home()=%q vs LegacyHome()=%q — expected equal after $HOME expansion", Home(), LegacyHome())
106+
}
107+
}
108+
109+
func TestHome_ExpandsTildeFromEnvVar(t *testing.T) {
110+
withOverride(t, "")
111+
withConfigInstallDir(t, "")
112+
withEnv(t, HomeEnvVar, "~/agent")
113+
114+
home, err := os.UserHomeDir()
115+
if err != nil || home == "" {
116+
t.Skip("home dir unresolved in this environment")
117+
}
118+
want := filepath.Join(home, "agent")
119+
if got := Home(); got != want {
120+
t.Errorf("Home() = %q, want %q (env ~ should expand)", got, want)
121+
}
122+
}
123+
124+
func TestHome_ExpandsHomeFromCLIFlag(t *testing.T) {
125+
withConfigInstallDir(t, "")
126+
withEnv(t, HomeEnvVar, "")
127+
withOverride(t, "$HOME/custom")
128+
129+
home, err := os.UserHomeDir()
130+
if err != nil || home == "" {
131+
t.Skip("home dir unresolved in this environment")
132+
}
133+
want := filepath.Join(home, "custom")
134+
if got := Home(); got != want {
135+
t.Errorf("Home() = %q, want %q (CLI $HOME should expand)", got, want)
136+
}
137+
}
138+
139+
func TestHome_AbsolutePathUnchanged(t *testing.T) {
140+
withOverride(t, "")
141+
withEnv(t, HomeEnvVar, "")
142+
withConfigInstallDir(t, "/opt/stepsecurity")
143+
144+
if got := Home(); got != "/opt/stepsecurity" {
145+
t.Errorf("Home() = %q, want /opt/stepsecurity (absolute path must not be modified)", got)
146+
}
147+
}
148+
89149
func TestSetOverride_Sticks(t *testing.T) {
90150
withOverride(t, "")
91151
SetOverride("/sticky")

0 commit comments

Comments
 (0)