fix: prevent learning mode from collapsing read paths to $HOME

Files directly under ~ (e.g., ~/.gitignore, ~/.npmrc) were collapsed
to the home directory, defeating sandboxing. Now keeps exact file paths
when the parent directory would be $HOME.
This commit is contained in:
2026-02-13 11:38:51 -06:00
parent c95fca830b
commit a04f5feee2
2 changed files with 46 additions and 5 deletions

View File

@@ -220,9 +220,15 @@ func CollapsePaths(paths []string) []string {
} }
} }
// For standalone paths, use their parent directory // For standalone paths, use their parent directory — but never collapse to $HOME
for _, p := range standalone { for _, p := range standalone {
result = append(result, filepath.Dir(p)) parent := filepath.Dir(p)
if parent == home {
// Keep exact file path to avoid opening entire home directory
result = append(result, p)
} else {
result = append(result, parent)
}
} }
// Sort and deduplicate (remove sub-paths of other paths) // Sort and deduplicate (remove sub-paths of other paths)

View File

@@ -78,6 +78,7 @@ func TestCollapsePaths(t *testing.T) {
name string name string
paths []string paths []string
contains []string // paths that should be in the result contains []string // paths that should be in the result
notContains []string // paths that must NOT be in the result
}{ }{
{ {
name: "multiple paths under same app dir", name: "multiple paths under same app dir",
@@ -111,6 +112,33 @@ func TestCollapsePaths(t *testing.T) {
"/home/testuser/.config/opencode", "/home/testuser/.config/opencode",
}, },
}, },
{
name: "files directly under home stay as exact paths",
paths: []string{
"/home/testuser/.gitignore",
"/home/testuser/.npmrc",
},
contains: []string{
"/home/testuser/.gitignore",
"/home/testuser/.npmrc",
},
notContains: []string{"/home/testuser"},
},
{
name: "mix of home files and app dir paths",
paths: []string{
"/home/testuser/.gitignore",
"/home/testuser/.cache/opencode/db/main.sqlite",
"/home/testuser/.cache/opencode/version",
"/home/testuser/.npmrc",
},
contains: []string{
"/home/testuser/.gitignore",
"/home/testuser/.npmrc",
"/home/testuser/.cache/opencode",
},
notContains: []string{"/home/testuser"},
},
} }
for _, tt := range tests { for _, tt := range tests {
@@ -134,6 +162,13 @@ func TestCollapsePaths(t *testing.T) {
t.Errorf("CollapsePaths() = %v, missing expected path %q", got, want) t.Errorf("CollapsePaths() = %v, missing expected path %q", got, want)
} }
} }
for _, bad := range tt.notContains {
for _, g := range got {
if g == bad {
t.Errorf("CollapsePaths() = %v, should NOT contain %q", got, bad)
}
}
}
}) })
} }
} }