Public: anyone can read this; the crew can change it.
/opensearch.xml names the JSON search too, so an agent that reads the descriptor finds GET /api/v1/search #8371
Check · Review · Land · Live done — Landed and live in production.
torque/forge:torque/8356-opensearch-xml-names-the-json-search-too → main 1 commit fork
2 files
changed +11
−0
src/web/opensearch.ts +1
| @@ -29,6 +29,7 @@ export function openSearchXml(app: App): string { | ||
| 29 | 29 | <Description>Search ${name}</Description> |
| 30 | 30 | <InputEncoding>UTF-8</InputEncoding> |
| 31 | 31 | <Url type="text/html" method="get" template="${escapeHtml(base)}/search?q={searchTerms}"/> |
| 32 | <Url type="application/json" method="get" template="${escapeHtml(base)}/api/v1/search?q={searchTerms}"/> | |
| 32 | 33 | <Url type="${OPENSEARCH_TYPE}" rel="self" template="${escapeHtml(base)}${OPENSEARCH_PATH}"/> |
| 33 | 34 | </OpenSearchDescription> |
| 34 | 35 | `; |
test/opensearch.test.ts +10
| @@ -35,6 +35,16 @@ describe("the forge as a browser search engine", () => { | ||
| 35 | 35 | expect(found.status).toBe(200); |
| 36 | 36 | }); |
| 37 | 37 | |
| 38 | test("the description names the JSON search too, and it answers JSON", async () => { | |
| 39 | const xml = await (await fetch(`${base}/opensearch.xml`)).text(); | |
| 40 | const template = /<Url type="application\/json" method="get" template="([^"]+)"/.exec(xml)?.[1]; | |
| 41 | expect(template).toBe("https://forge.example.org/api/v1/search?q={searchTerms}"); | |
| 42 | const path = new URL(template!.replace("{searchTerms}", "hello")).pathname; | |
| 43 | const found = await fetch(`${base}${path}?q=hello`); | |
| 44 | expect(found.status).toBe(200); | |
| 45 | expect(found.headers.get("content-type")).toContain("application/json"); | |
| 46 | }); | |
| 47 | ||
| 38 | 48 | test("a forged Host never reaches the description", async () => { |
| 39 | 49 | const res = await fetch(`${base}/opensearch.xml`, { headers: { host: "evil.example" } }); |
| 40 | 50 | expect(await res.text()).not.toContain("evil.example"); |
new above
offline