mirror of
https://github.com/gethomepage/homepage.git
synced 2026-09-12 07:05:56 -07:00
Chore: simplify sanitizeErrorURL
This commit is contained in:
@@ -69,11 +69,5 @@ export function jsonArrayFilter(data, filter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeErrorURL(errorURL) {
|
export function sanitizeErrorURL(errorURL) {
|
||||||
// Dont display sensitive params on frontend
|
return `${new URL(errorURL).hostname} (see logs for details)`;
|
||||||
const url = new URL(errorURL);
|
|
||||||
["apikey", "api_key", "token", "t", "access_token", "auth"].forEach((key) => {
|
|
||||||
if (url.searchParams.has(key)) url.searchParams.set(key, "***");
|
|
||||||
if (url.hash.includes(key)) url.hash = url.hash.replace(new RegExp(`${key}=[^&]+`), `${key}=***`);
|
|
||||||
});
|
|
||||||
return url.toString();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,25 +83,14 @@ describe("utils/proxy/api-helpers", () => {
|
|||||||
expect(jsonArrayFilter(data, (item) => item.a > 1)).toEqual([{ a: 2 }]);
|
expect(jsonArrayFilter(data, (item) => item.a > 1)).toEqual([{ a: 2 }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sanitizeErrorURL redacts sensitive query params and hash fragments", () => {
|
it("sanitizeErrorURL returns only the hostname regardless of where credentials appear", () => {
|
||||||
const input = "https://example.com/path?apikey=123&token=abc#access_token=xyz&other=1";
|
const input = "https://user:pass@example.com/secret-path-key/status?custom_secret=abc#token=xyz";
|
||||||
const output = sanitizeErrorURL(input);
|
expect(sanitizeErrorURL(input)).toBe("example.com (see logs for details)");
|
||||||
|
|
||||||
const url = new URL(output);
|
|
||||||
expect(url.searchParams.get("apikey")).toBe("***");
|
|
||||||
expect(url.searchParams.get("token")).toBe("***");
|
|
||||||
expect(url.hash).toContain("access_token=***");
|
|
||||||
expect(url.hash).toContain("other=1");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sanitizeErrorURL only redacts known keys", () => {
|
it("sanitizeErrorURL accepts URL objects and omits the port", () => {
|
||||||
const input = "https://example.com/path?api_key=123&safe=ok#auth=abc&safe_hash=1";
|
expect(sanitizeErrorURL(new URL("http://192.168.1.10:8080/api?apikey=123"))).toBe(
|
||||||
const output = sanitizeErrorURL(input);
|
"192.168.1.10 (see logs for details)",
|
||||||
|
);
|
||||||
const url = new URL(output);
|
|
||||||
expect(url.searchParams.get("api_key")).toBe("***");
|
|
||||||
expect(url.searchParams.get("safe")).toBe("ok");
|
|
||||||
expect(url.hash).toContain("auth=***");
|
|
||||||
expect(url.hash).toContain("safe_hash=1");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ describe("utils/proxy/handlers/credentialed", () => {
|
|||||||
expect(res.body).toEqual({
|
expect(res.body).toEqual({
|
||||||
error: {
|
error: {
|
||||||
message: "HTTP Error",
|
message: "HTTP Error",
|
||||||
url: "http://x/api/statistics",
|
url: "x (see logs for details)",
|
||||||
data: { detail: "Invalid token." },
|
data: { detail: "Invalid token." },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -414,7 +414,7 @@ describe("utils/proxy/handlers/credentialed", () => {
|
|||||||
expect(params.headers["X-Finnhub-Token"]).toBe("finnhub-token");
|
expect(params.headers["X-Finnhub-Token"]).toBe("finnhub-token");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sanitizes embedded query params when a downstream error contains a url", async () => {
|
it("replaces embedded error urls with the hostname when a downstream error contains a url", async () => {
|
||||||
getServiceWidget.mockResolvedValue({ type: "linkwarden", url: "http://example", key: "token" });
|
getServiceWidget.mockResolvedValue({ type: "linkwarden", url: "http://example", key: "token" });
|
||||||
httpProxy.mockResolvedValue([500, "application/json", { error: { message: "oops", url: "http://bad" } }]);
|
httpProxy.mockResolvedValue([500, "application/json", { error: { message: "oops", url: "http://bad" } }]);
|
||||||
|
|
||||||
@@ -424,7 +424,7 @@ describe("utils/proxy/handlers/credentialed", () => {
|
|||||||
await credentialedProxyHandler(req, res);
|
await credentialedProxyHandler(req, res);
|
||||||
|
|
||||||
expect(res.statusCode).toBe(500);
|
expect(res.statusCode).toBe(500);
|
||||||
expect(res.body.error.url).toContain("apikey=***");
|
expect(res.body.error.url).toBe("example (see logs for details)");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("ends the response for 204/304 statuses", async () => {
|
it("ends the response for 204/304 statuses", async () => {
|
||||||
@@ -451,7 +451,7 @@ describe("utils/proxy/handlers/credentialed", () => {
|
|||||||
|
|
||||||
expect(res.statusCode).toBe(500);
|
expect(res.statusCode).toBe(500);
|
||||||
expect(res.body.error.message).toBe("Invalid data");
|
expect(res.body.error.message).toBe("Invalid data");
|
||||||
expect(res.body.error.url).toContain("http://example/api/v1/collections");
|
expect(res.body.error.url).toBe("example (see logs for details)");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies the response mapping function when provided", async () => {
|
it("applies the response mapping function when provided", async () => {
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ describe("utils/proxy/handlers/generic", () => {
|
|||||||
await genericProxyHandler(req, res);
|
await genericProxyHandler(req, res);
|
||||||
|
|
||||||
expect(res.statusCode).toBe(200);
|
expect(res.statusCode).toBe(200);
|
||||||
expect(res.body.error.url).toContain("apikey=***");
|
expect(res.body.error.url).toBe("example (see logs for details)");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns an Invalid data error when validation fails", async () => {
|
it("returns an Invalid data error when validation fails", async () => {
|
||||||
@@ -223,7 +223,7 @@ describe("utils/proxy/handlers/generic", () => {
|
|||||||
|
|
||||||
expect(res.statusCode).toBe(500);
|
expect(res.statusCode).toBe(500);
|
||||||
expect(res.body.error.message).toBe("HTTP Error");
|
expect(res.body.error.message).toBe("HTTP Error");
|
||||||
expect(res.body.error.url).toContain("apikey=***");
|
expect(res.body.error.url).toBe("example (see logs for details)");
|
||||||
expect(res.body.error.data).toBe("fail");
|
expect(res.body.error.data).toBe("fail");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -436,6 +436,6 @@ describe("utils/proxy/http httpProxy", () => {
|
|||||||
expect(status).toBe(500);
|
expect(status).toBe(500);
|
||||||
expect(contentType).toBe("application/json");
|
expect(contentType).toBe("application/json");
|
||||||
expect(data.error.message).toBe("boom");
|
expect(data.error.message).toBe("boom");
|
||||||
expect(data.error.url).toContain("apikey=***");
|
expect(data.error.url).toBe("example.com (see logs for details)");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ describe("widgets/dockhand/proxy", () => {
|
|||||||
|
|
||||||
expect(res.statusCode).toBe(500);
|
expect(res.statusCode).toBe(500);
|
||||||
expect(res.body.error.message).toBe("HTTP Error");
|
expect(res.body.error.message).toBe("HTTP Error");
|
||||||
expect(res.body.error.url).toContain("token=***");
|
expect(res.body.error.url).toBe("dockhand (see logs for details)");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("supports token auth", async () => {
|
it("supports token auth", async () => {
|
||||||
|
|||||||
@@ -47,7 +47,14 @@ export default async function frigateProxyHandler(req, res, map) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (loginStatus !== 200) {
|
if (loginStatus !== 200) {
|
||||||
logger.error("HTTP Error %d calling %s", loginStatus, sanitizeErrorURL(loginUrl));
|
const errorURL = new URL(loginUrl);
|
||||||
|
logger.error(
|
||||||
|
"HTTP Error %d calling %s//%s%s...",
|
||||||
|
loginStatus,
|
||||||
|
errorURL.protocol,
|
||||||
|
errorURL.host,
|
||||||
|
errorURL.pathname,
|
||||||
|
);
|
||||||
return res.status(status).json({
|
return res.status(status).json({
|
||||||
error: {
|
error: {
|
||||||
message: `HTTP Error ${status} while trying to login to Frigate`,
|
message: `HTTP Error ${status} while trying to login to Frigate`,
|
||||||
@@ -63,7 +70,8 @@ export default async function frigateProxyHandler(req, res, map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status >= 400) {
|
if (status >= 400) {
|
||||||
logger.error("HTTP Error %d calling %s", status, sanitizeErrorURL(url));
|
const errorURL = new URL(url);
|
||||||
|
logger.error("HTTP Error %d calling %s//%s%s...", status, errorURL.protocol, errorURL.host, errorURL.pathname);
|
||||||
return res.status(status).json({
|
return res.status(status).json({
|
||||||
error: {
|
error: {
|
||||||
message: `HTTP Error ${status} from Frigate`,
|
message: `HTTP Error ${status} from Frigate`,
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ describe("widgets/frigate/proxy", () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
error: expect.objectContaining({
|
error: expect.objectContaining({
|
||||||
message: "HTTP Error 401 from Frigate",
|
message: "HTTP Error 401 from Frigate",
|
||||||
url: "http://frigate/api/stats",
|
url: "frigate (see logs for details)",
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -162,7 +162,7 @@ describe("widgets/frigate/proxy", () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
error: expect.objectContaining({
|
error: expect.objectContaining({
|
||||||
message: "HTTP Error 401 while trying to login to Frigate",
|
message: "HTTP Error 401 while trying to login to Frigate",
|
||||||
url: "http://frigate/api/stats",
|
url: "frigate (see logs for details)",
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ export default async function komodoProxyHandler(req, res) {
|
|||||||
let resultData = data;
|
let resultData = data;
|
||||||
|
|
||||||
if (status >= 400) {
|
if (status >= 400) {
|
||||||
logger.error("HTTP Error %d calling %s", status, sanitizeErrorURL(url));
|
const errorURL = new URL(url);
|
||||||
|
logger.error("HTTP Error %d calling %s//%s%s...", status, errorURL.protocol, errorURL.host, errorURL.pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
|
|||||||
Reference in New Issue
Block a user