fix(rss): fix the rss endpoint having the wrong content type (#6570)
This commit is contained in:
parent
71a17c9329
commit
a36b365f4d
@ -30,6 +30,7 @@ class StatusPage extends BeanModel {
|
||||
]);
|
||||
|
||||
if (statusPage) {
|
||||
response.type("application/rss+xml");
|
||||
response.send(await StatusPage.renderRSS(statusPage, slug));
|
||||
} else {
|
||||
response.status(404).send(UptimeKumaServer.getInstance().indexHTML);
|
||||
|
||||
38
test/backend-test/test-status-page.js
Normal file
38
test/backend-test/test-status-page.js
Normal file
@ -0,0 +1,38 @@
|
||||
const { describe, test } = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const StatusPage = require("../../server/model/status_page");
|
||||
const { STATUS_PAGE_ALL_UP, STATUS_PAGE_ALL_DOWN, STATUS_PAGE_PARTIAL_DOWN, STATUS_PAGE_MAINTENANCE } = require("../../src/util");
|
||||
|
||||
describe("StatusPage", () => {
|
||||
describe("getStatusDescription()", () => {
|
||||
test("returns 'No Services' when status is -1", () => {
|
||||
const description = StatusPage.getStatusDescription(-1);
|
||||
assert.strictEqual(description, "No Services");
|
||||
});
|
||||
|
||||
test("returns 'All Systems Operational' when all services are up", () => {
|
||||
const description = StatusPage.getStatusDescription(STATUS_PAGE_ALL_UP);
|
||||
assert.strictEqual(description, "All Systems Operational");
|
||||
});
|
||||
|
||||
test("returns 'Partially Degraded Service' when some services are down", () => {
|
||||
const description = StatusPage.getStatusDescription(STATUS_PAGE_PARTIAL_DOWN);
|
||||
assert.strictEqual(description, "Partially Degraded Service");
|
||||
});
|
||||
|
||||
test("returns 'Degraded Service' when all services are down", () => {
|
||||
const description = StatusPage.getStatusDescription(STATUS_PAGE_ALL_DOWN);
|
||||
assert.strictEqual(description, "Degraded Service");
|
||||
});
|
||||
|
||||
test("returns 'Under maintenance' when status page is in maintenance", () => {
|
||||
const description = StatusPage.getStatusDescription(STATUS_PAGE_MAINTENANCE);
|
||||
assert.strictEqual(description, "Under maintenance");
|
||||
});
|
||||
|
||||
test("returns '?' for unknown status values", () => {
|
||||
const description = StatusPage.getStatusDescription(999);
|
||||
assert.strictEqual(description, "?");
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -159,4 +159,86 @@ test.describe("Status Page", () => {
|
||||
// @todo Test certificate expiry
|
||||
// @todo Test domain names
|
||||
|
||||
test("RSS feed escapes malicious monitor names", async ({ page }, testInfo) => {
|
||||
test.setTimeout(60000);
|
||||
|
||||
// Test various XSS payloads in monitor names
|
||||
const maliciousMonitorName1 = "<script>alert(1)</script>";
|
||||
const maliciousMonitorName2 = "x</title><script>alert(document.domain)</script><title>";
|
||||
const normalMonitorName = "Production API Server";
|
||||
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
// Create first monitor with script tag payload
|
||||
await expect(page.getByTestId("monitor-type-select")).toBeVisible();
|
||||
await page.getByTestId("monitor-type-select").selectOption("http");
|
||||
await page.getByTestId("friendly-name-input").fill(maliciousMonitorName1);
|
||||
await page.getByTestId("url-input").fill("https://malicious1.example.com");
|
||||
await page.getByTestId("save-button").click();
|
||||
await page.waitForURL("/dashboard/*");
|
||||
|
||||
// Create second monitor with title breakout payload
|
||||
await page.goto("./add");
|
||||
await page.getByTestId("monitor-type-select").selectOption("http");
|
||||
await page.getByTestId("friendly-name-input").fill(maliciousMonitorName2);
|
||||
await page.getByTestId("url-input").fill("https://malicious2.example.com");
|
||||
await page.getByTestId("save-button").click();
|
||||
await page.waitForURL("/dashboard/*");
|
||||
|
||||
// Create third monitor with normal name
|
||||
await page.goto("./add");
|
||||
await page.getByTestId("monitor-type-select").selectOption("http");
|
||||
await page.getByTestId("friendly-name-input").fill(normalMonitorName);
|
||||
await page.getByTestId("url-input").fill("https://normal.example.com");
|
||||
await page.getByTestId("save-button").click();
|
||||
await page.waitForURL("/dashboard/*");
|
||||
|
||||
// Create a status page
|
||||
await page.goto("./add-status-page");
|
||||
await page.getByTestId("name-input").fill("Security Test");
|
||||
await page.getByTestId("slug-input").fill("security-test");
|
||||
await page.getByTestId("submit-button").click();
|
||||
await page.waitForURL("/status/security-test?edit");
|
||||
|
||||
// Add a group and all monitors
|
||||
await page.getByTestId("add-group-button").click();
|
||||
await page.getByTestId("group-name").fill("Test Group");
|
||||
|
||||
// Add all three monitors
|
||||
await page.getByTestId("monitor-select").click();
|
||||
await page.getByTestId("monitor-select").getByRole("option", { name: maliciousMonitorName1 }).click();
|
||||
await page.getByTestId("monitor-select").click();
|
||||
await page.getByTestId("monitor-select").getByRole("option", { name: maliciousMonitorName2 }).click();
|
||||
await page.getByTestId("monitor-select").click();
|
||||
await page.getByTestId("monitor-select").getByRole("option", { name: normalMonitorName }).click();
|
||||
|
||||
await page.getByTestId("save-button").click();
|
||||
await expect(page.getByTestId("edit-sidebar")).toHaveCount(0);
|
||||
|
||||
// Fetch the RSS feed
|
||||
const rssResponse = await page.request.get("/status/security-test/rss");
|
||||
expect(rssResponse.status()).toBe(200);
|
||||
expect(rssResponse.headers()["content-type"]).toBe("application/rss+xml; charset=utf-8");
|
||||
expect(rssResponse.ok()).toBeTruthy();
|
||||
|
||||
const rssContent = await rssResponse.text();
|
||||
|
||||
// Attach RSS content for inspection
|
||||
await testInfo.attach("rss-feed.xml", {
|
||||
body: rssContent,
|
||||
contentType: "application/xml"
|
||||
});
|
||||
|
||||
// Verify all payloads are escaped using CDATA
|
||||
expect(rssContent).toContain(`<title><![CDATA[${maliciousMonitorName1} is down]]></title>`);
|
||||
expect(rssContent).toContain(`<title><![CDATA[${maliciousMonitorName2} is down]]></title>`);
|
||||
expect(rssContent).toContain(`<title><![CDATA[${normalMonitorName} is down]]></title>`);
|
||||
|
||||
// Verify RSS feed structure is valid
|
||||
expect(rssContent).toContain("<?xml version=\"1.0\"");
|
||||
expect(rssContent).toContain("<rss");
|
||||
expect(rssContent).toContain("</rss>");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user