| 174 | } |
| 175 | |
| 176 | func TestReadReference(t *testing.T) { |
| 177 | r := New(testFS()) |
| 178 | |
| 179 | data, cleaned, err := r.ReadReference("lark-calendar", "references/agenda.md") |
| 180 | if err != nil { |
| 181 | t.Fatalf("ReadReference error: %v", err) |
| 182 | } |
| 183 | if string(data) != "# Agenda" { |
| 184 | t.Errorf("content: got %q", string(data)) |
| 185 | } |
| 186 | if cleaned != "references/agenda.md" { |
| 187 | t.Errorf("cleaned path: got %q", cleaned) |
| 188 | } |
| 189 | |
| 190 | if _, _, err := r.ReadReference("lark-calendar", "references/nope.md"); err == nil { |
| 191 | t.Error("expected not-found error") |
| 192 | } else if !strings.Contains(err.Error(), "not found") { |
| 193 | t.Errorf("message: got %q", err.Error()) |
| 194 | } |
| 195 | |
| 196 | if _, _, err := r.ReadReference("lark-calendar", "references"); err == nil { |
| 197 | t.Error("expected directory error") |
| 198 | } else if !strings.Contains(err.Error(), "is a directory") { |
| 199 | t.Errorf("message: got %q", err.Error()) |
| 200 | } |
| 201 | |
| 202 | for _, bad := range []string{"../../etc/passwd", "/etc/passwd", "..", "", "references/../../im/SKILL.md", `..\..\x`} { |
| 203 | data, _, err := r.ReadReference("lark-calendar", bad) |
| 204 | if err == nil { |
| 205 | t.Errorf("expected rejection for %q", bad) |
| 206 | } |
| 207 | if data != nil { |
| 208 | t.Errorf("content leaked for %q: %q", bad, string(data)) |
| 209 | } |
| 210 | var verr *errs.ValidationError |
| 211 | if !errors.As(err, &verr) { |
| 212 | t.Errorf("expected validation error for %q, got %T", bad, err) |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | func TestParseFrontmatter(t *testing.T) { |
| 218 | cases := []struct { |