Enforce fs source containment

This commit is contained in:
2026-07-05 00:10:47 +00:00
parent 39485d87f6
commit a16f66cbc7
9 changed files with 327 additions and 24 deletions

View File

@@ -881,6 +881,38 @@ output:
}
}
func TestPrepareWithPromptFSRejectsEscapedContentFile(t *testing.T) {
promptFS := fstest.MapFS{
"assets/prompts/fs-escape.yaml": &fstest.MapFile{Data: []byte(`
id: fs.escape
version: "1.0.0"
default_profile: local-fast
messages:
- role: user
content_file: ../outside.tmpl
output:
format: text
validation_mode: none
repair_attempts: 0
`)},
"assets/outside.tmpl": &fstest.MapFile{Data: []byte(`Outside root.`)},
}
engine, err := scriptorium.NewEngine(scriptorium.Config{
PromptDir: t.TempDir(),
ProfileDir: "./examples/profiles",
SchemaDir: "./examples/schemas",
}, scriptorium.WithPromptFS(promptFS, "assets/prompts"))
if err != nil {
t.Fatalf("expected engine construction to succeed, got %v", err)
}
_, err = engine.Prepare(context.Background(), scriptorium.RunRequest{PromptID: "fs.escape"})
if !errors.Is(err, scriptorium.ErrPromptLoad) {
t.Fatalf("expected ErrPromptLoad, got %v", err)
}
}
func TestPrepareWorksWithPromptFile(t *testing.T) {
promptDir := t.TempDir()
promptPath := filepath.Join(promptDir, "single.yaml")
@@ -893,7 +925,7 @@ inputs:
required: true
messages:
- role: user
content: "Summarize {{input \"transcript\"}} from file."
content_file: ./single.tmpl
output:
format: text
validation_mode: none
@@ -901,6 +933,9 @@ output:
`), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(promptDir, "single.tmpl"), []byte(`Summarize {{input "transcript"}} from file.`), 0o644); err != nil {
t.Fatal(err)
}
engine, err := scriptorium.NewEngine(scriptorium.Config{
ProfileDir: "./examples/profiles",
@@ -922,6 +957,9 @@ output:
if prepared.PromptID != "single.file.prompt" {
t.Fatalf("unexpected prompt id: %q", prepared.PromptID)
}
if len(prepared.Messages) != 1 || !strings.Contains(prepared.Messages[0].Content, "from file") {
t.Fatalf("expected content_file body from prompt file, got %+v", prepared.Messages)
}
}
func TestPrepareWorksWithProfileFSOverBuiltIns(t *testing.T) {