Enforce fs source containment
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
@@ -276,6 +277,61 @@ func TestFSValidatorJSONSchemaSuccess(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSValidatorJSONSchemaPathContainment(t *testing.T) {
|
||||
t.Run("nested schema inside root succeeds", func(t *testing.T) {
|
||||
v := NewFSValidator(fstest.MapFS{
|
||||
"schemas/nested/events.schema.json": &fstest.MapFile{Data: []byte(`{
|
||||
"type": "object",
|
||||
"required": ["events"],
|
||||
"properties": {
|
||||
"events": {"type": "array"}
|
||||
}
|
||||
}`)},
|
||||
}, "schemas")
|
||||
|
||||
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)}, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: "nested/events.schema.json",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if res.Status != domain.ValidationPassed || !res.IsValid {
|
||||
t.Fatalf("expected passed/valid, got status=%q valid=%v", res.Status, res.IsValid)
|
||||
}
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
schemaPath string
|
||||
wantErr string
|
||||
}{
|
||||
{name: "parent escape rejected", schemaPath: "../outside.schema.json", wantErr: "escapes source root"},
|
||||
{name: "absolute path rejected", schemaPath: "/outside.schema.json", wantErr: "must be relative"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v := NewFSValidator(fstest.MapFS{
|
||||
"schemas/events.schema.json": &fstest.MapFile{Data: []byte(`{"type":"object"}`)},
|
||||
"outside.schema.json": &fstest.MapFile{Data: []byte(`{"type":"object"}`)},
|
||||
"schemas/outside.schema.json": &fstest.MapFile{Data: []byte(`{"type":"object"}`)},
|
||||
}, "schemas")
|
||||
|
||||
_, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(`{"events":[]}`)}, domain.OutputContract{
|
||||
ValidationMode: domain.ValidationJSONSchema,
|
||||
SchemaPath: tc.schemaPath,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected schema path error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.wantErr) {
|
||||
t.Fatalf("expected error to contain %q, got %v", tc.wantErr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSValidatorSingleSchemaFileUsesBaseName(t *testing.T) {
|
||||
v := NewFSValidator(fstest.MapFS{
|
||||
"events.schema.json": &fstest.MapFile{Data: []byte(`{
|
||||
|
||||
Reference in New Issue
Block a user