fix: close Windows workspace output sharing gap
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"context"
|
||||
"github.com/aritmolab/thothii/tools/thothctl/internal/compose"
|
||||
"github.com/aritmolab/thothii/tools/thothctl/internal/config"
|
||||
"github.com/aritmolab/thothii/tools/thothctl/internal/safeio"
|
||||
)
|
||||
|
||||
func TestParseWorkspaceCommands(t *testing.T) {
|
||||
@@ -349,3 +350,201 @@ func TestMakeInputAcceptsMaximumAnnotationWithReviewDigestPair(t *testing.T) {
|
||||
t.Fatalf("validateIngress() = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkspaceIngressAcceptsExactCountsAndByteBounds(t *testing.T) {
|
||||
root, err := filepath.EvalSymlinks(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
paths := make([]string, 0, 32)
|
||||
for i := 0; i < 32; i++ {
|
||||
path := filepath.Join(root, fmt.Sprintf("schema-%02d.sql", i))
|
||||
if err := os.WriteFile(path, []byte("x"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
paths = append(paths, path)
|
||||
}
|
||||
assumptions := make([]string, 0, maxAssumptions)
|
||||
for i := 0; i < maxAssumptions; i++ {
|
||||
assumptions = append(assumptions, fmt.Sprintf("column_%d=table_%d", i, i))
|
||||
}
|
||||
command := SuggestFksRequest{WorkspaceID: "psd", FromSQL: paths, Assume: assumptions}
|
||||
for _, path := range paths {
|
||||
if b, e := safeio.ReadCanonicalUTF8(path, maxSQLFile); e != nil {
|
||||
t.Fatalf("direct read %s: %v", path, e)
|
||||
} else if len(b) != 1 {
|
||||
t.Fatalf("direct read length=%d", len(b))
|
||||
}
|
||||
}
|
||||
env, payload, err := makeInput(command)
|
||||
if err != nil {
|
||||
t.Fatalf("exact file/assumption counts rejected: %v", err)
|
||||
}
|
||||
if len(env.SQL) != 32 || len(env.Assume) != maxAssumptions {
|
||||
t.Fatalf("derived envelope counts = sql %d assumptions %d", len(env.SQL), len(env.Assume))
|
||||
}
|
||||
if err := validateIngress([]byte(payload), env); err != nil {
|
||||
t.Fatalf("exact-bound envelope rejected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkspaceIngressRejectsSQLAggregateOverLimit(t *testing.T) {
|
||||
root, err := filepath.EvalSymlinks(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
paths := make([]string, 0, 17)
|
||||
for i := 0; i < 17; i++ {
|
||||
path := filepath.Join(root, fmt.Sprintf("schema-%02d.sql", i))
|
||||
if err := os.WriteFile(path, bytes.Repeat([]byte("x"), maxSQLFile), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
paths = append(paths, path)
|
||||
}
|
||||
command := SuggestFksRequest{WorkspaceID: "psd", FromSQL: paths[:16]}
|
||||
if _, _, err := makeInput(command); err != nil {
|
||||
t.Fatalf("exact SQL aggregate rejected: %v", err)
|
||||
}
|
||||
command.FromSQL = paths
|
||||
if _, _, err := makeInput(command); err == nil {
|
||||
t.Fatal("accepted SQL aggregate over 16 MiB")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateIngressRejectsTrailingAndUnknownFields(t *testing.T) {
|
||||
command := EvidenceRequest{WorkspaceID: "psd", DryRun: true}
|
||||
expected, payload, err := makeInput(command)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := validateIngress([]byte(payload), expected); err != nil {
|
||||
t.Fatalf("generated envelope rejected: %v", err)
|
||||
}
|
||||
for _, invalid := range []string{
|
||||
payload + " {}",
|
||||
strings.TrimSuffix(payload, "}") + `,"unknown":1}`,
|
||||
} {
|
||||
if err := validateIngress([]byte(invalid), expected); err == nil {
|
||||
t.Errorf("accepted invalid ingress envelope %q", invalid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateResultRejectsIdentitySchemaAndUnknownValues(t *testing.T) {
|
||||
base := validWorkspaceResult("succeeded", "ok")
|
||||
cases := []Result{
|
||||
base,
|
||||
func() Result { r := base; r.SchemaVersion = 2; return r }(),
|
||||
func() Result { r := base; r.WorkspaceID = "other"; return r }(),
|
||||
func() Result { r := base; r.Operation = "run"; return r }(),
|
||||
func() Result { r := base; r.WorkspaceRevision = "short"; return r }(),
|
||||
func() Result { r := base; r.DescriptorBlob = "short"; return r }(),
|
||||
func() Result { r := base; r.RunID = "not-a-run"; return r }(),
|
||||
func() Result { r := base; r.ChildRuns = map[string]string{"child": "not-a-run"}; return r }(),
|
||||
func() Result {
|
||||
r := base
|
||||
r.ArtifactIdentities = []ArtifactIdentity{{Kind: "fk", Digest: "not-a-digest"}}
|
||||
return r
|
||||
}(),
|
||||
func() Result { r := base; r.CompletedStages = nil; return r }(),
|
||||
}
|
||||
for i, result := range cases {
|
||||
err := validateResult(result, "psd", "evidence")
|
||||
if (i == 0 && err != nil) || (i > 0 && err == nil) {
|
||||
t.Errorf("case %d validateResult() = %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublishCandidateRejectsInvalidEncodingDigestUTF8AndYAML(t *testing.T) {
|
||||
result := validWorkspaceResult("succeeded", "ok")
|
||||
result.Operation = "suggest-fks"
|
||||
result.RunID = strings.Repeat("b", 32)
|
||||
valid := []byte("candidates: []\n")
|
||||
validDigest := DigestBytes(valid)
|
||||
result.ArtifactIdentities = []ArtifactIdentity{{Kind: "fk-candidates", Digest: validDigest}}
|
||||
cases := []hostExport{
|
||||
{MediaType: "application/yaml", SHA256: validDigest, ContentBase64: "%%%"},
|
||||
{MediaType: "application/yaml", SHA256: DigestBytes([]byte("other")), ContentBase64: base64.StdEncoding.EncodeToString(valid)},
|
||||
{MediaType: "application/yaml", SHA256: validDigest, ContentBase64: base64.StdEncoding.EncodeToString([]byte{0xff})},
|
||||
{MediaType: "application/yaml", SHA256: DigestBytes([]byte("not yaml")), ContentBase64: base64.StdEncoding.EncodeToString([]byte("["))},
|
||||
}
|
||||
for i, candidate := range cases {
|
||||
if err := publishCandidate(&candidate, result, ""); err == nil {
|
||||
t.Errorf("case %d accepted invalid candidate export", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRejectsUnsafeInputsBeforeComposeInvocation(t *testing.T) {
|
||||
root, err := filepath.EvalSymlinks(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
realSQL := filepath.Join(root, "real.sql")
|
||||
if err := os.WriteFile(realSQL, []byte("select 1"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
unsafeSQL := filepath.Join(root, "linked.sql")
|
||||
if err := os.Symlink(realSQL, unsafeSQL); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
marker := filepath.Join(root, "invoked")
|
||||
fake := filepath.Join(root, "docker")
|
||||
if err := os.WriteFile(fake, []byte("#!/bin/sh\ntouch '"+marker+"'\n"), 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
command := SuggestFksRequest{WorkspaceID: "psd", FromSQL: []string{unsafeSQL}}
|
||||
if _, err := Run(context.Background(), config.Installation{ProjectDirectory: root}, compose.NewRunner(fake), command, nil); err == nil {
|
||||
t.Fatal("accepted unsafe SQL input")
|
||||
}
|
||||
if _, err := os.Stat(marker); !os.IsNotExist(err) {
|
||||
t.Fatalf("Compose was invoked for unsafe input: stat err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeInputRejectsOversizedOrInvalidAnnotation(t *testing.T) {
|
||||
root, err := filepath.EvalSymlinks(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
path := filepath.Join(root, "annotations.md")
|
||||
if err := os.WriteFile(path, bytes.Repeat([]byte("a"), maxAnnotations+1), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
command := CheckSchemaRequest{WorkspaceID: "psd", Resume: strings.Repeat("0", 32), Annotations: path, ReviewedCandidates: DigestBytes([]byte("reviewed"))}
|
||||
if _, _, err := makeInput(command); err == nil {
|
||||
t.Fatal("accepted annotation over 16 MiB")
|
||||
}
|
||||
if err := os.WriteFile(path, []byte{0xff}, 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := makeInput(command); err == nil {
|
||||
t.Fatal("accepted non-UTF-8 annotation")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRejectsTrailingOrUnknownResultEnvelope(t *testing.T) {
|
||||
for name, suffix := range map[string]string{
|
||||
"trailing": "} {}",
|
||||
"unknown": `,"unknown":1`,
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
root, err := filepath.EvalSymlinks(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
response := `{"schemaVersion":1,"status":"succeeded","code":"ok","workspaceId":"psd","workspaceRevision":"` + strings.Repeat("0", 40) + `","descriptorBlob":"` + strings.Repeat("a", 40) + `","operation":"evidence","completedStages":[]` + suffix
|
||||
if name == "unknown" {
|
||||
response += "}"
|
||||
}
|
||||
fake := filepath.Join(root, "docker")
|
||||
if err := os.WriteFile(fake, []byte("#!/bin/sh\ncat >/dev/null\nprintf '%s' '"+response+"'\n"), 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := Run(context.Background(), config.Installation{ProjectDirectory: root}, compose.NewRunner(fake), EvidenceRequest{WorkspaceID: "psd"}, nil); err == nil {
|
||||
t.Fatal("accepted malformed result envelope")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user