Files
Agent-System/internal/agent/remote_result_test.go
T
weichenandCursor 0a61cfbe90 feat(agent): add typed-result-v1 and fail-closed remote parser
Remote /v1/run now emits additive result_v1; Go validates run_id, digest, and paths before materializing, and only accepts missing typed payloads with an explicit legacy handshake.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 15:25:18 +00:00

136 lines
4.4 KiB
Go

package agent
import (
"encoding/json"
"strings"
"testing"
)
func TestDecodeRemoteRun_TypedSuccess(t *testing.T) {
body := []byte("hello")
rv := BuildResultV1("ws1", "success", false, "", []struct {
RelPath string
Content []byte
}{{"output.md", body}})
raw, _ := json.Marshal(map[string]any{
"ok": true, "run_id": "ws1", "runner_protocol": "typed-v1",
"capabilities": map[string]any{"typed_result": true},
"patches": []map[string]string{{"rel_path": "output.md", "content": "hello"}},
"result_v1": rv,
})
got, err := DecodeRemoteRun(raw, "ws1", true)
if err != nil || !got.OK || got.Legacy {
t.Fatalf("typed success: err=%v ok=%v legacy=%v", err, got != nil && got.OK, got != nil && got.Legacy)
}
if string(got.Incoming["output.md"]) != "hello" {
t.Fatal("patch body")
}
}
func TestDecodeRemoteRun_TypedPresentInvalidNoFallback(t *testing.T) {
rv := BuildResultV1("ws1", "success", false, "", []struct {
RelPath string
Content []byte
}{{"output.md", []byte("hello")}})
raw, _ := json.Marshal(map[string]any{
"ok": true, "run_id": "ws1", "runner_protocol": "typed-v1",
"patches": []map[string]string{{"rel_path": "output.md", "content": "TAMPER"}},
"result_v1": rv,
})
if _, err := DecodeRemoteRun(raw, "ws1", true); err == nil {
t.Fatal("tampered typed body must fail closed")
}
}
func TestDecodeRemoteRun_RunMismatch(t *testing.T) {
rv := BuildResultV1("other-run", "success", false, "", []struct {
RelPath string
Content []byte
}{{"output.md", []byte("hello")}})
raw, _ := json.Marshal(map[string]any{
"ok": true, "run_id": "other-run",
"patches": []map[string]string{{"rel_path": "output.md", "content": "hello"}},
"result_v1": rv,
})
if _, err := DecodeRemoteRun(raw, "ws1", true); err == nil {
t.Fatal("foreign run_id must fail closed")
}
}
func TestDecodeRemoteRun_HandshakeDowngrade(t *testing.T) {
raw, _ := json.Marshal(map[string]any{
"ok": true, "run_id": "ws1",
"capabilities": map[string]any{"typed_result": true},
"patches": []map[string]string{{"rel_path": "output.md", "content": "hello"}},
})
if _, err := DecodeRemoteRun(raw, "ws1", true); err == nil {
t.Fatal("typed capability without result_v1 is a downgrade attack")
}
}
func TestDecodeRemoteRun_MissingTypedWithoutLegacyHandshake(t *testing.T) {
raw, _ := json.Marshal(map[string]any{
"ok": true, "patches": []map[string]string{{"rel_path": "output.md", "content": "hello"}},
})
if _, err := DecodeRemoteRun(raw, "ws1", true); err == nil {
t.Fatal("implicit legacy must fail after P4")
}
}
func TestDecodeRemoteRun_LegacyHandshakeStillWorks(t *testing.T) {
raw, _ := json.Marshal(map[string]any{
"ok": true, "runner_protocol": "legacy",
"fallback_reason": "Connection error.",
"retryable": true,
"patches": []map[string]string{{"rel_path": "output.md", "content": "hello"}},
})
got, err := DecodeRemoteRun(raw, "ws1", true)
if err != nil || !got.OK || !got.Legacy {
t.Fatalf("legacy handshake: err=%v got=%+v", err, got)
}
}
func TestDecodeRemoteRun_LegacyDoesNotLeakPaths(t *testing.T) {
raw, _ := json.Marshal(map[string]any{
"ok": false, "runner_protocol": "legacy",
"fallback_reason": "failed /tmp/secret/token=abc",
"patches": []any{},
})
got, err := DecodeRemoteRun(raw, "ws1", true)
if err != nil {
t.Fatal(err)
}
if got.OK {
t.Fatal("want failure")
}
if strings.Contains(got.Reason, "/tmp") || strings.Contains(got.Reason, "token") {
t.Fatalf("leaked reason %q", got.Reason)
}
}
func TestDecodeRemoteRun_PathEscapeMember(t *testing.T) {
rv := ResultV1{
SchemaVersion: 1, RunID: "ws1", Outcome: "success",
Members: []ResultV1Member{{Kind: "file", Path: "../etc/passwd", Digest: DigestBytes([]byte("x")), Size: 1}},
}
rv.ContentDigest = rv.MembersDigest()
raw, _ := json.Marshal(map[string]any{
"ok": true, "result_v1": rv,
"patches": []map[string]string{{"rel_path": "../etc/passwd", "content": "x"}},
})
if _, err := DecodeRemoteRun(raw, "ws1", true); err == nil {
t.Fatal("escaped member path must fail closed")
}
}
func TestDecodeRemoteRun_OversizedMember(t *testing.T) {
big := strings.Repeat("a", MaxResultMemberBytes+1)
raw, _ := json.Marshal(map[string]any{
"ok": true, "runner_protocol": "legacy",
"patches": []map[string]string{{"rel_path": "output.md", "content": big}},
})
if _, err := DecodeRemoteRun(raw, "ws1", true); err == nil {
t.Fatal("oversized member must fail closed")
}
}