From a414f376b9f16ba9f02595c56c33b21cc196167e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2026 07:18:27 -0700 Subject: [PATCH] Chore(deps): Bump js-yaml from 4.3.1 to 5.3.0 (#7032) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 4 +-- src/utils/config/api-response.js | 5 ++-- src/utils/config/api-response.test.js | 5 +--- src/utils/config/config.check-copy.test.js | 2 +- src/utils/config/config.js | 7 ++--- src/utils/config/docker.js | 5 ++-- src/utils/config/docker.test.js | 5 +--- src/utils/config/kubernetes.js | 4 +-- src/utils/config/kubernetes.test.js | 5 +--- src/utils/config/proxmox.js | 5 ++-- src/utils/config/proxmox.test.js | 5 +--- src/utils/config/service-helpers.js | 6 ++--- src/utils/config/service-helpers.test.js | 5 +--- src/utils/config/widget-helpers.js | 5 ++-- src/utils/config/widget-helpers.test.js | 5 +--- src/utils/config/yaml.js | 14 ++++++++++ src/utils/config/yaml.test.js | 31 ++++++++++++++++++++++ src/utils/mcp/homepage-mcp.js | 7 ++--- 19 files changed, 76 insertions(+), 51 deletions(-) create mode 100644 src/utils/config/yaml.js create mode 100644 src/utils/config/yaml.test.js diff --git a/package.json b/package.json index 1b638c5bd..2c861ed56 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "gamedig": "^5.3.3", "i18next": "^26.3.6", "ical.js": "^2.2.1", - "js-yaml": "^4.3.1", + "js-yaml": "^5.3.0", "json-rpc-2.0": "^1.7.1", "luxon": "^3.7.2", "memory-cache": "^0.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22077d43d..be7c971fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,8 +36,8 @@ importers: specifier: ^2.2.1 version: 2.2.1 js-yaml: - specifier: ^4.3.1 - version: 4.3.1 + specifier: ^5.3.0 + version: 5.3.0 json-rpc-2.0: specifier: ^1.7.1 version: 1.7.1 diff --git a/src/utils/config/api-response.js b/src/utils/config/api-response.js index fa87a80fc..6642e2067 100644 --- a/src/utils/config/api-response.js +++ b/src/utils/config/api-response.js @@ -1,8 +1,6 @@ import { promises as fs } from "fs"; import path from "path"; -import yaml from "js-yaml"; - import checkAndCopyConfig, { CONF_DIR, getSettings, substituteEnvironmentVars } from "utils/config/config"; import { cleanServiceGroups, @@ -12,6 +10,7 @@ import { servicesFromKubernetes, } from "utils/config/service-helpers"; import { cleanWidgetGroups, widgetsFromConfig } from "utils/config/widget-helpers"; +import { loadYaml } from "utils/config/yaml"; /** * Compares services by weight then by name. @@ -30,7 +29,7 @@ export async function bookmarksResponse() { const bookmarksYaml = path.join(CONF_DIR, "bookmarks.yaml"); const rawFileContents = await fs.readFile(bookmarksYaml, "utf8"); const fileContents = substituteEnvironmentVars(rawFileContents); - const bookmarks = yaml.load(fileContents); + const bookmarks = loadYaml(fileContents); if (!bookmarks) return []; diff --git a/src/utils/config/api-response.test.js b/src/utils/config/api-response.test.js index c1f8b0d83..f61b7588f 100644 --- a/src/utils/config/api-response.test.js +++ b/src/utils/config/api-response.test.js @@ -30,10 +30,7 @@ vi.mock("fs", () => ({ promises: fs, })); -vi.mock("js-yaml", () => ({ - default: yaml, - ...yaml, -})); +vi.mock("utils/config/yaml", () => ({ loadYaml: yaml.load })); vi.mock("utils/config/config", () => config); vi.mock("utils/config/widget-helpers", () => widgetHelpers); diff --git a/src/utils/config/config.check-copy.test.js b/src/utils/config/config.check-copy.test.js index 21b961356..b6b34cd96 100644 --- a/src/utils/config/config.check-copy.test.js +++ b/src/utils/config/config.check-copy.test.js @@ -13,7 +13,7 @@ const { fs, yaml } = vi.hoisted(() => ({ })); vi.mock("fs", () => fs); -vi.mock("js-yaml", () => ({ default: yaml, ...yaml })); +vi.mock("utils/config/yaml", () => ({ loadYaml: yaml.load })); describe("utils/config/config checkAndCopyConfig", () => { const originalEnv = process.env; diff --git a/src/utils/config/config.js b/src/utils/config/config.js index 22f0e2c73..65b8866f1 100644 --- a/src/utils/config/config.js +++ b/src/utils/config/config.js @@ -1,9 +1,10 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync } from "fs"; import { join } from "path"; -import yaml from "js-yaml"; import cache from "memory-cache"; +import { loadYaml } from "utils/config/yaml"; + const cacheKey = "homepageEnvironmentVariables"; const homepageVarPrefix = "HOMEPAGE_VAR_"; const homepageFilePrefix = "HOMEPAGE_FILE_"; @@ -42,7 +43,7 @@ export default function checkAndCopyConfig(config) { } try { - yaml.load(readFileSync(configYaml, "utf8")); + loadYaml(readFileSync(configYaml, "utf8")); return true; } catch (e) { return { ...e, config }; @@ -85,7 +86,7 @@ export function getSettings() { const settingsYaml = join(CONF_DIR, "settings.yaml"); const rawFileContents = readFileSync(settingsYaml, "utf8"); const fileContents = substituteEnvironmentVars(rawFileContents); - const initialSettings = yaml.load(fileContents) ?? {}; + const initialSettings = loadYaml(fileContents) ?? {}; if (initialSettings.layout) { // support yaml list but old spec was object so convert to that diff --git a/src/utils/config/docker.js b/src/utils/config/docker.js index fb60aa406..a48449eb3 100644 --- a/src/utils/config/docker.js +++ b/src/utils/config/docker.js @@ -1,9 +1,8 @@ import { readFileSync } from "fs"; import path from "path"; -import yaml from "js-yaml"; - import checkAndCopyConfig, { CONF_DIR, substituteEnvironmentVars } from "utils/config/config"; +import { loadYaml } from "utils/config/yaml"; export function getDefaultDockerArgs(platform = process.platform) { if (platform !== "win32" && platform !== "darwin") { @@ -19,7 +18,7 @@ export default function getDockerArguments(server) { const configFile = path.join(CONF_DIR, "docker.yaml"); const rawConfigData = readFileSync(configFile, "utf8"); const configData = substituteEnvironmentVars(rawConfigData); - const servers = yaml.load(configData); + const servers = loadYaml(configData); if (!server) { return getDefaultDockerArgs(); diff --git a/src/utils/config/docker.test.js b/src/utils/config/docker.test.js index 236d3c5b9..4012f6fb3 100644 --- a/src/utils/config/docker.test.js +++ b/src/utils/config/docker.test.js @@ -21,10 +21,7 @@ vi.mock("fs", () => ({ readFileSync: fs.readFileSync, })); -vi.mock("js-yaml", () => ({ - default: yaml, - ...yaml, -})); +vi.mock("utils/config/yaml", () => ({ loadYaml: yaml.load })); vi.mock("utils/config/config", () => ({ default: checkAndCopyConfig, diff --git a/src/utils/config/kubernetes.js b/src/utils/config/kubernetes.js index 680c408e7..178994a58 100644 --- a/src/utils/config/kubernetes.js +++ b/src/utils/config/kubernetes.js @@ -2,16 +2,16 @@ import { readFileSync } from "fs"; import path from "path"; import { ApiextensionsV1Api, KubeConfig } from "@kubernetes/client-node"; -import yaml from "js-yaml"; import checkAndCopyConfig, { CONF_DIR, substituteEnvironmentVars } from "utils/config/config"; +import { loadYaml } from "utils/config/yaml"; export function getKubernetes() { checkAndCopyConfig("kubernetes.yaml"); const configFile = path.join(CONF_DIR, "kubernetes.yaml"); const rawConfigData = readFileSync(configFile, "utf8"); const configData = substituteEnvironmentVars(rawConfigData); - return yaml.load(configData); + return loadYaml(configData); } export const getKubeConfig = () => { diff --git a/src/utils/config/kubernetes.test.js b/src/utils/config/kubernetes.test.js index b86b3f1b2..dbe3a870b 100644 --- a/src/utils/config/kubernetes.test.js +++ b/src/utils/config/kubernetes.test.js @@ -32,10 +32,7 @@ vi.mock("fs", () => ({ readFileSync: fs.readFileSync, })); -vi.mock("js-yaml", () => ({ - default: yaml, - ...yaml, -})); +vi.mock("utils/config/yaml", () => ({ loadYaml: yaml.load })); vi.mock("utils/config/config", () => ({ default: checkAndCopyConfig, diff --git a/src/utils/config/proxmox.js b/src/utils/config/proxmox.js index c8b809a69..aa9d124b7 100644 --- a/src/utils/config/proxmox.js +++ b/src/utils/config/proxmox.js @@ -1,14 +1,13 @@ import { readFileSync } from "fs"; import path from "path"; -import yaml from "js-yaml"; - import checkAndCopyConfig, { CONF_DIR, substituteEnvironmentVars } from "utils/config/config"; +import { loadYaml } from "utils/config/yaml"; export function getProxmoxConfig() { checkAndCopyConfig("proxmox.yaml"); const configFile = path.join(CONF_DIR, "proxmox.yaml"); const rawConfigData = readFileSync(configFile, "utf8"); const configData = substituteEnvironmentVars(rawConfigData); - return yaml.load(configData); + return loadYaml(configData); } diff --git a/src/utils/config/proxmox.test.js b/src/utils/config/proxmox.test.js index a2c49800b..30da199af 100644 --- a/src/utils/config/proxmox.test.js +++ b/src/utils/config/proxmox.test.js @@ -18,10 +18,7 @@ vi.mock("fs", () => ({ readFileSync: fs.readFileSync, })); -vi.mock("js-yaml", () => ({ - default: yaml, - ...yaml, -})); +vi.mock("utils/config/yaml", () => ({ loadYaml: yaml.load })); vi.mock("utils/config/config", () => ({ default: checkAndCopyConfig, diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index a22cfc6ac..d028d3db3 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -2,12 +2,12 @@ import { promises as fs } from "fs"; import path from "path"; import Docker from "dockerode"; -import yaml from "js-yaml"; import checkAndCopyConfig, { CONF_DIR, getSettings, substituteEnvironmentVars } from "utils/config/config"; import getDockerArguments from "utils/config/docker"; import { getKubeConfig } from "utils/config/kubernetes"; import * as shvl from "utils/config/shvl"; +import { loadYaml } from "utils/config/yaml"; import kubernetes from "utils/kubernetes/export"; import createLogger from "utils/logger"; import { parseVersionForUrl } from "utils/proxy/api-helpers"; @@ -56,7 +56,7 @@ export async function servicesFromConfig() { const servicesYaml = path.join(CONF_DIR, "services.yaml"); const rawFileContents = await fs.readFile(servicesYaml, "utf8"); const fileContents = substituteEnvironmentVars(rawFileContents); - const services = yaml.load(fileContents); + const services = loadYaml(fileContents); return parseServicesToGroups(services); } @@ -66,7 +66,7 @@ export async function servicesFromDocker() { const dockerYaml = path.join(CONF_DIR, "docker.yaml"); const rawDockerFileContents = await fs.readFile(dockerYaml, "utf8"); const dockerFileContents = substituteEnvironmentVars(rawDockerFileContents); - const servers = yaml.load(dockerFileContents); + const servers = loadYaml(dockerFileContents); if (!servers) { return []; diff --git a/src/utils/config/service-helpers.test.js b/src/utils/config/service-helpers.test.js index 7555ad038..1032edc77 100644 --- a/src/utils/config/service-helpers.test.js +++ b/src/utils/config/service-helpers.test.js @@ -70,10 +70,7 @@ vi.mock("fs", () => ({ promises: fs, })); -vi.mock("js-yaml", () => ({ - default: yaml, - ...yaml, -})); +vi.mock("utils/config/yaml", () => ({ loadYaml: yaml.load })); vi.mock("utils/config/config", () => config); vi.mock("dockerode", () => ({ default: Docker })); diff --git a/src/utils/config/widget-helpers.js b/src/utils/config/widget-helpers.js index 93f71194c..561424c96 100644 --- a/src/utils/config/widget-helpers.js +++ b/src/utils/config/widget-helpers.js @@ -1,9 +1,8 @@ import { promises as fs } from "fs"; import path from "path"; -import yaml from "js-yaml"; - import checkAndCopyConfig, { CONF_DIR, substituteEnvironmentVars } from "utils/config/config"; +import { loadYaml } from "utils/config/yaml"; export async function widgetsFromConfig() { checkAndCopyConfig("widgets.yaml"); @@ -11,7 +10,7 @@ export async function widgetsFromConfig() { const widgetsYaml = path.join(CONF_DIR, "widgets.yaml"); const rawFileContents = await fs.readFile(widgetsYaml, "utf8"); const fileContents = substituteEnvironmentVars(rawFileContents); - const widgets = yaml.load(fileContents); + const widgets = loadYaml(fileContents); if (!widgets) return []; diff --git a/src/utils/config/widget-helpers.test.js b/src/utils/config/widget-helpers.test.js index 4d7bbbdbc..2bec9e517 100644 --- a/src/utils/config/widget-helpers.test.js +++ b/src/utils/config/widget-helpers.test.js @@ -18,10 +18,7 @@ vi.mock("fs", () => ({ promises: fs, })); -vi.mock("js-yaml", () => ({ - default: yaml, - ...yaml, -})); +vi.mock("utils/config/yaml", () => ({ loadYaml: yaml.load })); vi.mock("utils/config/config", () => config); diff --git a/src/utils/config/yaml.js b/src/utils/config/yaml.js new file mode 100644 index 000000000..f8125ab11 --- /dev/null +++ b/src/utils/config/yaml.js @@ -0,0 +1,14 @@ +import * as yaml from "js-yaml"; + +const EMPTY_DOCUMENT_ERROR = "expected a document, but the input is empty"; +const DEFAULT_SCHEMA = yaml.CORE_SCHEMA.withTags(yaml.timestampTag, yaml.mergeTag); + +export function loadYaml(input, options) { + try { + return yaml.load(input, { schema: DEFAULT_SCHEMA, ...options }); + } catch (error) { + // js-yaml v4 returned undefined for empty and comment-only documents. + if (error?.reason === EMPTY_DOCUMENT_ERROR) return undefined; + throw error; + } +} diff --git a/src/utils/config/yaml.test.js b/src/utils/config/yaml.test.js new file mode 100644 index 000000000..4af6b7b3a --- /dev/null +++ b/src/utils/config/yaml.test.js @@ -0,0 +1,31 @@ +import { describe, expect, it } from "vitest"; + +import { loadYaml } from "./yaml"; + +describe("utils/config/yaml", () => { + it.each(["", " \n", "# comment only\n"])("loads an empty document from %j as undefined", (input) => { + expect(loadYaml(input)).toBeUndefined(); + }); + + it("loads a populated document", () => { + expect(loadYaml("title: Homepage\n")).toEqual({ title: "Homepage" }); + }); + + it("preserves v4 merge key behavior", () => { + expect(loadYaml("defaults: &defaults\n href: https://example.com\nservice:\n <<: *defaults\n")).toEqual({ + defaults: { href: "https://example.com" }, + service: { href: "https://example.com" }, + }); + }); + + it("preserves v4 timestamp behavior without enabling YAML 1.1 booleans", () => { + expect(loadYaml("date: 2026-08-21\nenabled: yes\n")).toEqual({ + date: new Date("2026-08-21T00:00:00.000Z"), + enabled: "yes", + }); + }); + + it("still rejects invalid YAML", () => { + expect(() => loadYaml("value: [\n")).toThrow(); + }); +}); diff --git a/src/utils/mcp/homepage-mcp.js b/src/utils/mcp/homepage-mcp.js index ee83cc214..65c28e494 100644 --- a/src/utils/mcp/homepage-mcp.js +++ b/src/utils/mcp/homepage-mcp.js @@ -2,9 +2,10 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { createHash, timingSafeEqual } from "node:crypto"; import { join } from "path"; -import yaml from "js-yaml"; +import * as yaml from "js-yaml"; import { CONF_DIR } from "utils/config/config"; +import { loadYaml } from "utils/config/yaml"; const PROTOCOL_VERSION = "2025-11-25"; const SERVER_INFO = { @@ -118,7 +119,7 @@ function readConfig(file) { } function parseYamlConfig(file) { - const parsed = yaml.load(readConfig(file) || ""); + const parsed = loadYaml(readConfig(file) || ""); return parsed ?? []; } @@ -128,7 +129,7 @@ function validateYaml(file, content) { } try { - yaml.load(content || ""); + loadYaml(content || ""); return { valid: true }; } catch (error) { return {