Skip to content

Commit 6e6993f

Browse files
committed
Fix sandbox denylist symlink regression
1 parent 7e2d943 commit 6e6993f

File tree

2 files changed

+54
-3
lines changed

2 files changed

+54
-3
lines changed

internal/cli/eval.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2780,14 +2780,19 @@ func resolveSandboxDenylistPaths(configured []string, outputDir string) []string
27802780
if err != nil {
27812781
return nil
27822782
}
2783+
repoRoot = canonicalizeExistingPath(repoRoot)
27832784

27842785
candidates := []string{
27852786
filepath.Join(repoRoot, "tasks"),
27862787
filepath.Join(repoRoot, "eval-results"),
27872788
filepath.Join(repoRoot, "sessions"),
27882789
}
27892790
if outputDir != "" {
2790-
candidates = append(candidates, outputDir)
2791+
if filepath.IsAbs(outputDir) {
2792+
candidates = append(candidates, outputDir)
2793+
} else {
2794+
candidates = append(candidates, filepath.Join(repoRoot, outputDir))
2795+
}
27912796
}
27922797
for _, path := range configured {
27932798
if path == "" {
@@ -2807,6 +2812,7 @@ func resolveSandboxDenylistPaths(configured []string, outputDir string) []string
28072812
if err != nil {
28082813
continue
28092814
}
2815+
absPath = canonicalizeExistingPath(absPath)
28102816
if _, exists := seen[absPath]; exists {
28112817
continue
28122818
}
@@ -2816,6 +2822,17 @@ func resolveSandboxDenylistPaths(configured []string, outputDir string) []string
28162822
return denylist
28172823
}
28182824

2825+
func canonicalizeExistingPath(path string) string {
2826+
if path == "" {
2827+
return path
2828+
}
2829+
resolved, err := filepath.EvalSymlinks(path)
2830+
if err != nil {
2831+
return path
2832+
}
2833+
return resolved
2834+
}
2835+
28192836
// detectQuotaError checks if agent log contains rate limit or quota errors.
28202837
// Returns (hasError, isRecoverable) where hasError indicates if any quota/rate
28212838
// limit pattern was found, and isRecoverable indicates if the error is transient.

internal/cli/eval_prompt_test.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,8 +1115,6 @@ func TestResolveSandboxDenylistPaths(t *testing.T) {
11151115
}
11161116

11171117
func TestResolveSandboxDenylistPathsIncludesOutputDir(t *testing.T) {
1118-
t.Parallel()
1119-
11201118
origDir, _ := os.Getwd()
11211119
repoRoot := t.TempDir()
11221120
if err := os.Chdir(repoRoot); err != nil {
@@ -1139,6 +1137,42 @@ func TestResolveSandboxDenylistPathsIncludesOutputDir(t *testing.T) {
11391137
}
11401138
}
11411139

1140+
func TestResolveSandboxDenylistPathsCanonicalizesSymlinkRepoRoot(t *testing.T) {
1141+
origDir, _ := os.Getwd()
1142+
realRoot := t.TempDir()
1143+
aliasParent := t.TempDir()
1144+
aliasRoot := filepath.Join(aliasParent, "repo-alias")
1145+
if err := os.Symlink(realRoot, aliasRoot); err != nil {
1146+
t.Fatalf("create symlink: %v", err)
1147+
}
1148+
if err := os.Chdir(aliasRoot); err != nil {
1149+
t.Fatalf("chdir: %v", err)
1150+
}
1151+
t.Setenv("PWD", aliasRoot)
1152+
defer func() { _ = os.Chdir(origDir) }()
1153+
1154+
got := resolveSandboxDenylistPaths(nil, "")
1155+
want := filepath.Join(realRoot, "tasks")
1156+
unwanted := filepath.Join(aliasRoot, "tasks")
1157+
1158+
hasWant := false
1159+
hasUnwanted := false
1160+
for _, path := range got {
1161+
if path == want {
1162+
hasWant = true
1163+
}
1164+
if path == unwanted {
1165+
hasUnwanted = true
1166+
}
1167+
}
1168+
if !hasWant {
1169+
t.Fatalf("expected canonical denylist path %s, got %v", want, got)
1170+
}
1171+
if hasUnwanted {
1172+
t.Fatalf("unexpected symlink denylist path %s in %v", unwanted, got)
1173+
}
1174+
}
1175+
11421176
func TestParseAgentBehaviorMetrics(t *testing.T) {
11431177
t.Parallel()
11441178

0 commit comments

Comments
 (0)