diff --git a/review/docmap_test.go b/review/docmap_test.go index 99cb2c0..7ad51c0 100644 --- a/review/docmap_test.go +++ b/review/docmap_test.go @@ -376,6 +376,50 @@ func TestLoadMatchingDocs_Deduplication(t *testing.T) { } } +func TestValidateDocPath(t *testing.T) { + valid := []string{ + "docs/design.md", + "docs/domain/contexts/risk/risk-controls.md", + "README.md", + "a/b/c", + } + for _, p := range valid { + if err := validateDocPath(p); err != nil { + t.Errorf("expected valid path %q to pass, got error: %v", p, err) + } + } + + invalid := []string{ + "/etc/passwd", + "/docs/design.md", + "docs/../../../etc/passwd", + "../sibling-repo/file.md", + "a/b/../c", + } + for _, p := range invalid { + if err := validateDocPath(p); err == nil { + t.Errorf("expected path %q to be rejected, but it was accepted", p) + } + } +} + +func TestLoadMatchingDocs_PathTraversalRejected(t *testing.T) { + fetcher := &fakeDocFetcher{ + files: map[string]string{ + "../secret.md": "should not be fetched", + }, + } + content, err := LoadMatchingDocs(context.Background(), fetcher, "owner", "repo", + []string{"../secret.md"}, DocMapOptions{MaxBytes: DefaultDocMapMaxBytes}) + if err != nil { + t.Fatalf("unexpected hard error: %v", err) + } + // Bad path should be skipped (warned), not injected. + if strings.Contains(content, "should not be fetched") { + t.Errorf("path traversal doc was injected, expected it to be skipped") + } +} + // ============================================================ // Helpers // ============================================================