Cross-module callers get an opaque ref plus rotating lease tokens so stale writers fail closed, and file I/O is refused inside a unit of work. Co-authored-by: Cursor <cursoragent@cursor.com>
120 lines
3.2 KiB
Go
120 lines
3.2 KiB
Go
package workspace
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"chat-agent-system/internal/app/uow"
|
|
)
|
|
|
|
func issued(t *testing.T, cap *Capability, id string, project int64) WorkspaceRef {
|
|
t.Helper()
|
|
ref, err := cap.Issue(WorkspaceRef{ID: id, ProjectScope: project, SchemaVersion: SchemaVersion})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return ref
|
|
}
|
|
|
|
func TestWorkspaceRefHasNoPhysicalPathFields(t *testing.T) {
|
|
var ref WorkspaceRef
|
|
_ = ref.ID
|
|
_ = ref.SchemaVersion
|
|
_ = ref.FencingToken
|
|
_ = ref.ProjectScope
|
|
_ = ref.RunID
|
|
_ = ref.Environment
|
|
}
|
|
|
|
func TestCapabilityRejectsBadKeys(t *testing.T) {
|
|
cap := NewCapability(t.TempDir(), nil)
|
|
ref := issued(t, cap, "ws1", 7)
|
|
ctx := context.Background()
|
|
for _, key := range []string{"", "../etc/passwd", "/etc/passwd", "..", "a/../b", "a//b", "%2e%2e/secret", "ok/%2fetc"} {
|
|
if _, err := cap.ReadArtifact(ctx, ref, key); err == nil {
|
|
t.Fatalf("key %q should reject", key)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCapabilityRejectsSymlinkEscape(t *testing.T) {
|
|
root := t.TempDir()
|
|
cap := NewCapability(root, nil)
|
|
ref := issued(t, cap, "ws1", 7)
|
|
outside := filepath.Join(t.TempDir(), "secret")
|
|
if err := os.WriteFile(outside, []byte("nope"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wsDir := filepath.Join(root, "workspaces", "ws1")
|
|
if err := os.MkdirAll(wsDir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
link := filepath.Join(wsDir, "leak")
|
|
if err := os.Symlink(outside, link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := cap.ReadArtifact(context.Background(), ref, "leak"); err != ErrPathEscape {
|
|
t.Fatalf("symlink: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStaleFencingRejected(t *testing.T) {
|
|
cap := NewCapability(t.TempDir(), nil)
|
|
old := issued(t, cap, "ws1", 3)
|
|
_ = issued(t, cap, "ws1", 3)
|
|
ctx := context.Background()
|
|
if err := cap.WriteArtifact(ctx, old, "output.md", []byte("stale")); err != ErrStaleFencing {
|
|
t.Fatalf("old writer: %v", err)
|
|
}
|
|
fresh := issued(t, cap, "ws1", 3)
|
|
if err := cap.WriteArtifact(ctx, fresh, "output.md", []byte("ok")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestUnknownSchemaRejected(t *testing.T) {
|
|
cap := NewCapability(t.TempDir(), nil)
|
|
_, err := cap.Issue(WorkspaceRef{ID: "ws1", SchemaVersion: 99})
|
|
if err != ErrUnknownSchema {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestNoFileIOInsideUnitOfWork(t *testing.T) {
|
|
cap := NewCapability(t.TempDir(), nil)
|
|
ref := issued(t, cap, "ws1", 1)
|
|
ctx := uow.WithScope(context.Background(), stubScope{})
|
|
if err := cap.WriteArtifact(ctx, ref, "output.md", []byte("x")); err != ErrIOInTransaction {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCommitProjectionCompleteMarker(t *testing.T) {
|
|
cap := NewCapability(t.TempDir(), nil)
|
|
ref := issued(t, cap, "ws1", 1)
|
|
ctx := context.Background()
|
|
if err := cap.WriteArtifact(ctx, ref, "output.md", []byte("hello")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := cap.CommitProjection(ctx, ref, "output.md", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := cap.ReadArtifact(ctx, ref, "output.md.complete")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !containsComplete(got) {
|
|
t.Fatalf("marker %q", got)
|
|
}
|
|
}
|
|
|
|
func containsComplete(b []byte) bool {
|
|
return string(b) != "" && (string(b)[len(b)-1] == '\n')
|
|
}
|
|
|
|
type stubScope struct{}
|
|
|
|
func (stubScope) Context() context.Context { return context.Background() }
|