From 20776beefa2a91d4438afce711fe43109fdf2999 Mon Sep 17 00:00:00 2001 From: rowkav09 Date: Fri, 2 Oct 2026 17:15:49 +0100 Subject: [PATCH] Read status provider labels from own keys only Closes #1101 --- src/app-status.js | 6 ++++-- test/app-status.test.js | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/app-status.js b/src/app-status.js index 0d1210a2..b6c54251 100644 --- a/src/app-status.js +++ b/src/app-status.js @@ -4,6 +4,8 @@ import { createBuildProvenance } from "./build-provenance.js"; import { createTrayHealth } from "./tray-health.js"; const PROVIDER_LABELS = Object.freeze({ jellyfin: "Jellyfin", emby: "Emby", plex: "Plex", navidrome: "Navidrome" }); +// Own keys only, so an id such as "constructor" is not read off Object.prototype. +function providerLabel(id) { return typeof id === "string" && Object.hasOwn(PROVIDER_LABELS, id) ? PROVIDER_LABELS[id] : undefined; } const STATE_BY_FAILURE = Object.freeze({ unauthorized: "authentication_failed", unreachable: "unreachable", timeout: "unreachable", error: "error" }); // Tracks what the local status page shows. It only keeps the latest poll @@ -69,7 +71,7 @@ export function createAppStatus({ config, version = null, now = () => Date.now() try { rows = servers(); } catch { rows = []; } if (!Array.isArray(rows)) return Object.freeze([]); return Object.freeze(rows.map((row, index) => Object.freeze({ - type: PROVIDER_LABELS[row?.provider] ?? word(row?.provider), + type: providerLabel(row?.provider) ?? word(row?.provider), address: serverOrigin(config.servers?.[index]?.serverUrl), user: text(row?.displayName), state: word(row?.state), @@ -108,7 +110,7 @@ export function createAppStatus({ config, version = null, now = () => Date.now() build: buildLabel(build), uptimeMs: Math.max(0, elapsedNow() - startedElapsed), server: Object.freeze({ - type: PROVIDER_LABELS[config.provider] ?? config.provider, + type: providerLabel(config.provider) ?? config.provider, address: serverOrigin(config.serverUrl), user: text(config.identity?.displayName), state: safeMode ? "safe_mode" : failure ? STATE_BY_FAILURE[failure] : lastOkAt === null ? "starting" : "connected", diff --git a/test/app-status.test.js b/test/app-status.test.js index 95913df9..588719e9 100644 --- a/test/app-status.test.js +++ b/test/app-status.test.js @@ -371,3 +371,8 @@ test("the card cover status reports a short allow-listed reason and nothing else status.setCardArtwork(() => ({ state: "failed", reason: "http://secret.example/?token=abc" })); assert.equal(status.snapshot().cardArtwork.reason, "unknown"); }); + +test("a provider id that names an Object.prototype property is shown as its own text, not a function", () => { + const status = createAppStatus({ config: { ...config, provider: "constructor" }, version: "0.1.1-dev" }); + assert.equal(status.snapshot().server.type, "constructor"); +});