Handle bad feed path encoding (#9)

This commit is contained in:
phucnguyen1707 2026-06-12 11:07:24 +07:00 committed by GitHub
parent 3d4345b665
commit 8f4691584c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 4 deletions

View file

@ -118,6 +118,14 @@ describe("CommandBoard API contracts", () => {
expect(opmlBody).toContain("<opml");
});
it("rejects malformed encoded feed discovery paths as client errors", async () => {
const response = await fetch(`${baseUrl}/rss/discover/%E0%A4%A.xml`);
const body = await response.json() as { error: string };
expect(response.status).toBe(400);
expect(body).toEqual({ error: "Invalid path encoding" });
});
it("exposes sh1pt project and action contracts", async () => {
const projectsResponse = await fetch(`${baseUrl}/api/plugins/sh1pt/projects`);
const projectsBody = await projectsResponse.json() as { projects: Array<{ id: string; board: string; status: string; actions: number }> };

View file

@ -190,6 +190,11 @@ async function route(request: IncomingMessage, response: ServerResponse) {
const formattedDiscover = matchFormattedDiscoverPath(url.pathname);
if (request.method === "GET" && formattedDiscover) {
if (formattedDiscover.format === "invalid-encoding") {
json(response, 400, { error: "Invalid path encoding" });
return;
}
const result = await discoverFeeds({
q: formattedDiscover.keyword,
type: "all",
@ -344,23 +349,34 @@ function listParam(value: string | null) {
function matchFormattedDiscoverPath(pathname: string) {
const rss = /^\/rss\/discover\/(.+)\.xml$/.exec(pathname);
if (rss) {
return { format: "rss" as const, keyword: decodeURIComponent(rss[1]) };
return formattedDiscoverMatch("rss", rss[1]);
}
const opml = /^\/opml\/discover\/(.+)\.xml$/.exec(pathname) ?? /^\/rss\/discover\/(.+)\.opml$/.exec(pathname);
if (opml) {
return { format: "opml" as const, keyword: decodeURIComponent(opml[1]) };
return formattedDiscoverMatch("opml", opml[1]);
}
const atom = /^\/atom\/discover\/(.+)\.xml$/.exec(pathname);
if (atom) {
return { format: "atom" as const, keyword: decodeURIComponent(atom[1]) };
return formattedDiscoverMatch("atom", atom[1]);
}
const jsonFeed = /^\/json-feed\/discover\/(.+)\.json$/.exec(pathname);
if (jsonFeed) {
return { format: "json-feed" as const, keyword: decodeURIComponent(jsonFeed[1]) };
return formattedDiscoverMatch("json-feed", jsonFeed[1]);
}
return undefined;
}
function formattedDiscoverMatch(format: "rss" | "opml" | "atom" | "json-feed", encodedKeyword: string) {
try {
return { format, keyword: decodeURIComponent(encodedKeyword) };
} catch (error) {
if (error instanceof URIError) {
return { format: "invalid-encoding" as const };
}
throw error;
}
}
export function startCommandBoardServer(port = Number(process.env.PORT ?? 4010)) {
const server = createCommandBoardServer();
server.listen(port, () => {