Skip to content

Commit fbc5f8a

Browse files
xgopilotpionxe
andcommitted
test: improve updater and workspace coverage
Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: pionxe <148670367+pionxe@users.noreply.github.com>
1 parent 4edc502 commit fbc5f8a

2 files changed

Lines changed: 517 additions & 0 deletions

File tree

internal/security/workspace_test.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,19 @@ func TestWorkspaceSandboxCheckShortCircuits(t *testing.T) {
196196
}
197197
}
198198

199+
func TestWorkspaceSandboxCheckRejectsInvalidCapabilityToken(t *testing.T) {
200+
t.Parallel()
201+
202+
root := t.TempDir()
203+
action := fileAction(ActionTypeRead, "filesystem_read_file", "read_file", root, "notes.txt")
204+
action.Payload.CapabilityToken = &CapabilityToken{}
205+
206+
_, err := NewWorkspaceSandbox().Check(context.Background(), action)
207+
if err == nil || !strings.Contains(err.Error(), "capability token path not allowed") {
208+
t.Fatalf("expected capability token path rejection, got %v", err)
209+
}
210+
}
211+
199212
func TestBuildWorkspacePlan(t *testing.T) {
200213
t.Parallel()
201214

@@ -263,6 +276,21 @@ func TestBuildWorkspacePlan(t *testing.T) {
263276
wantOK: true,
264277
wantTarget: ".",
265278
},
279+
{
280+
name: "sandbox target type falls back to target type",
281+
action: Action{
282+
Type: ActionTypeRead,
283+
Payload: ActionPayload{
284+
ToolName: "filesystem_grep",
285+
Resource: "filesystem_grep",
286+
Workdir: root,
287+
TargetType: TargetTypeDirectory,
288+
Target: "docs",
289+
},
290+
},
291+
wantOK: true,
292+
wantTarget: "docs",
293+
},
266294
}
267295

268296
for _, tt := range tests {
@@ -291,6 +319,21 @@ func TestBuildWorkspacePlan(t *testing.T) {
291319
}
292320
}
293321

322+
func TestWorkspaceSandboxValidateWorkspacePlanErrors(t *testing.T) {
323+
t.Parallel()
324+
325+
sandbox := NewWorkspaceSandbox()
326+
_, err := sandbox.validateWorkspacePlan(workspacePlan{
327+
root: filepath.Join(t.TempDir(), "missing"),
328+
target: "notes.txt",
329+
targetType: TargetTypePath,
330+
actionType: ActionTypeRead,
331+
})
332+
if err == nil || !strings.Contains(err.Error(), "resolve workspace root") {
333+
t.Fatalf("expected resolve workspace root error, got %v", err)
334+
}
335+
}
336+
294337
func TestNeedsWorkspaceSandbox(t *testing.T) {
295338
t.Parallel()
296339

@@ -441,6 +484,13 @@ func TestCanonicalWorkspaceRoot(t *testing.T) {
441484
if _, ok := sandbox.canonicalRoots.Load(cleanedPathKey(existing)); !ok {
442485
t.Fatalf("expected canonical root cache entry for %q", existing)
443486
}
487+
gotCached, err := sandbox.canonicalWorkspaceRoot(existing)
488+
if err != nil {
489+
t.Fatalf("canonicalWorkspaceRoot(cached) error: %v", err)
490+
}
491+
if !samePathKey(gotCached, got) {
492+
t.Fatalf("canonicalWorkspaceRoot(cached) = %q, want %q", gotCached, got)
493+
}
444494

445495
missing := filepath.Join(t.TempDir(), "missing", "dir")
446496
_, err = sandbox.canonicalWorkspaceRoot(missing)
@@ -776,6 +826,54 @@ func TestWorkspaceExecutionPlanValidateForExecution(t *testing.T) {
776826
t.Fatalf("expected valid plan, got %v", err)
777827
}
778828
})
829+
830+
t.Run("nearest existing path failure is returned", func(t *testing.T) {
831+
t.Parallel()
832+
833+
root := t.TempDir()
834+
file := filepath.Join(root, "file.txt")
835+
mustWriteWorkspaceFile(t, file, "x")
836+
plan := &WorkspaceExecutionPlan{
837+
Root: root,
838+
Target: filepath.Join(file, "child.txt"),
839+
RequestedTarget: filepath.Join("file.txt", "child.txt"),
840+
anchorPath: file,
841+
anchorSnapshot: pathSnapshot{},
842+
}
843+
err := plan.ValidateForExecution()
844+
if err == nil || !strings.Contains(err.Error(), "inspect path") {
845+
t.Fatalf("expected inspect path error, got %v", err)
846+
}
847+
})
848+
849+
t.Run("anchor path mismatch is rejected", func(t *testing.T) {
850+
t.Parallel()
851+
852+
root := t.TempDir()
853+
targetA := filepath.Join(root, "a")
854+
targetB := filepath.Join(root, "b")
855+
if err := os.MkdirAll(targetA, 0o755); err != nil {
856+
t.Fatalf("mkdir targetA: %v", err)
857+
}
858+
if err := os.MkdirAll(targetB, 0o755); err != nil {
859+
t.Fatalf("mkdir targetB: %v", err)
860+
}
861+
snapshot, err := capturePathSnapshot(targetB)
862+
if err != nil {
863+
t.Fatalf("capturePathSnapshot(targetB): %v", err)
864+
}
865+
plan := &WorkspaceExecutionPlan{
866+
Root: root,
867+
Target: targetA,
868+
RequestedTarget: "a",
869+
anchorPath: targetB,
870+
anchorSnapshot: snapshot,
871+
}
872+
err = plan.ValidateForExecution()
873+
if err == nil || !strings.Contains(err.Error(), "changed before execution") {
874+
t.Fatalf("expected changed-before-execution error, got %v", err)
875+
}
876+
})
779877
}
780878

781879
func TestCapturePathSnapshotAndEqual(t *testing.T) {
@@ -893,6 +991,28 @@ func TestNearestExistingPath(t *testing.T) {
893991
return cleanedPathKey(filepath.Join(root, "broken"))
894992
},
895993
},
994+
{
995+
name: "returns inspect error for non-not-exist lstat",
996+
setup: func(t *testing.T) (string, string) {
997+
t.Helper()
998+
root := t.TempDir()
999+
file := filepath.Join(root, "file.txt")
1000+
mustWriteWorkspaceFile(t, file, "x")
1001+
return root, filepath.Join(file, "child.txt")
1002+
},
1003+
expectErr: "inspect path",
1004+
},
1005+
{
1006+
name: "missing root path returns root",
1007+
setup: func(t *testing.T) (string, string) {
1008+
t.Helper()
1009+
root := filepath.Join(t.TempDir(), "missing-root")
1010+
return root, root
1011+
},
1012+
expect: func(root string, target string) string {
1013+
return cleanedPathKey(root)
1014+
},
1015+
},
8961016
}
8971017

8981018
for _, tt := range tests {
@@ -918,6 +1038,27 @@ func TestNearestExistingPath(t *testing.T) {
9181038
}
9191039
}
9201040

1041+
func TestEnsureNoSymlinkEscapeReturnsNearestPathError(t *testing.T) {
1042+
t.Parallel()
1043+
1044+
root := t.TempDir()
1045+
file := filepath.Join(root, "file.txt")
1046+
mustWriteWorkspaceFile(t, file, "x")
1047+
1048+
_, err := ensureNoSymlinkEscape(root, filepath.Join(file, "child.txt"), filepath.Join("file.txt", "child.txt"))
1049+
if err == nil || !strings.Contains(err.Error(), "inspect path") {
1050+
t.Fatalf("expected inspect path error, got %v", err)
1051+
}
1052+
}
1053+
1054+
func TestValidateTargetVolumeNoVolumeShortCircuit(t *testing.T) {
1055+
t.Parallel()
1056+
1057+
if err := validateTargetVolume(filepath.Join(t.TempDir(), "workspace"), filepath.Join(t.TempDir(), "target")); err != nil {
1058+
t.Fatalf("validateTargetVolume() error = %v, want nil on non-volume paths", err)
1059+
}
1060+
}
1061+
9211062
func TestSplitRelativePath(t *testing.T) {
9221063
t.Parallel()
9231064

0 commit comments

Comments
 (0)