Skip to content

Risk: PROTECTED_KEYS in write-routes.ts is hand-maintained and can silently drift from actual env vars #457

@lonix

Description

@lonix

Problem

src/web/write-routes.ts exports a PROTECTED_KEYS set that gates which keys may appear in YAML import/export payloads:

// write-routes.ts ~line 141
/**
 * Hand-maintained: keep in lock-step with the `WEBUI_*` and bootstrap env
 * vars consumed in src/index.ts and src/web/index.ts. Adding a new env var
 * without updating this list will silently let admins overwrite it via YAML.
 */
export const PROTECTED_KEYS: ReadonlySet<string> = new Set([
  "DISCORD_TOKEN",
  "CLIENT_ID",
  "GUILD_ID",
  "MONGODB_URI",
  "NODE_ENV",
  "DEBUG",
  "WEBUI_ENABLED",
  "WEBUI_BASE_URL",
  "WEBUI_SESSION_SECRET",
  "WEBUI_SESSION_TTL_MINUTES",
  "WEBUI_INACTIVITY_TIMEOUT_MINUTES",
]);

The comment explicitly warns this is hand-maintained. There is no compile-time check, no test, and no lint rule that enforces it stays in sync with:

  • The bootstrap vars listed in src/web/read-only-routes.ts (BOOTSTRAP_VARS)
  • The critical settings in src/services/config-service.ts (criticalSettings)
  • Any new WEBUI_* env var added in the future

A developer who adds a new sensitive env var (e.g. WEBUI_SMTP_PASSWORD, WEBUI_OAUTH_SECRET) and forgets to update PROTECTED_KEYS will expose it to YAML import — a potential privilege-escalation path.

Suggested fix

Derive PROTECTED_KEYS from the same BOOTSTRAP_VARS array that read-only-routes.ts already maintains, or extract the bootstrap vars into a shared constant module and import it in both places. A simple approach:

// src/web/protected-keys.ts  (new shared module)
import { BOOTSTRAP_VARS } from "./read-only-routes.js";

export const PROTECTED_KEYS: ReadonlySet<string> = new Set(
  BOOTSTRAP_VARS.map((v) => v.key),
);

Then import PROTECTED_KEYS from this module in write-routes.ts and remove the hand-maintained copy.

Add a unit test that asserts PROTECTED_KEYS contains at minimum all keys in BOOTSTRAP_VARS.

Affected files

  • src/web/write-routes.ts lines 141–153
  • src/web/read-only-routes.ts lines 66–90 (BOOTSTRAP_VARS)
  • src/services/config-service.ts lines 452–458 (criticalSettings)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions