Skip to content

fix(security): write server.env 0600 in a 0700 data dir and repair older installs (GHSA-mh4f-3xj9-4gc4) - #14994

Merged
diegosouzapw merged 1 commit into
release/v3.8.51from
fix/env-writers-private-modes
Sep 28, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.51from
fix/env-writers-private-modes

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

⚠️ base-red inherited: #14963

Fixes GHSA-mh4f-3xj9-4gc4 (reported by @peterbussch). Follow-up to GHSA-2pg2-xm9r-8544 (#14712).

Problem

server.env holds the generated JWT_SECRET, STORAGE_ENCRYPTION_KEY and API_KEY_SECRET. Two writers created the file and the data directory without an explicit mode:

  • scripts/build/bootstrap-env.mjs, used by npm run dev / npm start and the Docker image;
  • electron/main.js, the desktop app.

Under the usual umask 022 that gives 0644 / 0755, readable by other local accounts: the staff group on macOS, Debian's 0755 homes, and the Docker ./data bind mount. bootstrapEnv() only rewrites the file when a secret is missing, so a file an earlier version left 0644 stays that way.

Fix

The package-root .env written by scripts/dev/sync-env.mjs is left as is. Since #11436 it no longer receives generated secrets, and forcing 0600 on a global install owned by root (sudo npm i -g) would stop the user's CLI from reading it.

Validation

  • tests/unit/bootstrap-env-private-modes.test.ts, 3 cases under umask 022:
    • a fresh write gives 0700 / 0600;
    • an existing 0644 file is repaired without being rewritten;
    • a rewrite keeps 0600.
    • All 3 fail on the old code (0755 / 0644).
  • tests/unit/electron-server-env-private-modes.test.ts: a source guard for electron/main.js, which exports nothing. It has positive anchors, and 3 of its 4 cases fail on the old file.
  • Existing suites stay green: bootstrap-env, bootstrap-env-sqlite-classifier, electron-main, cli-electron-to-cli-migration-server-env-7302.

Operators on macOS, Debian or the Docker bind mount should rotate JWT_SECRET, API_KEY_SECRET and the storage key if other local accounts could read the old file. Rotating the storage key needs a migration of the encrypted credentials.

…der installs (GHSA-mh4f-3xj9-4gc4)

server.env holds the generated JWT_SECRET, STORAGE_ENCRYPTION_KEY and
API_KEY_SECRET. scripts/build/bootstrap-env.mjs (npm run dev/start, the Docker
image) and electron/main.js wrote it and created the data dir without an
explicit mode, so under the usual umask 022 they came out 0644 / 0755 —
readable by other local accounts (macOS staff group, Debian 0755 homes, the
Docker ./data bind mount). bootstrapEnv() only rewrites the file when a secret
is missing, so an existing 0644 file stayed 0644.

Both writers now create the dir 0700 and the file 0600 (plus chmod, since
mode only applies on create), and repair an existing server.env / the dir's
"other" bits on every start. Same contract as bin/cli/privateDataDir.mjs from
GHSA-2pg2-xm9r-8544; chmod stays best-effort (Windows, foreign-owned dirs).

The package-root .env written by sync-env.mjs is left as is: since #11436 it no
longer receives generated secrets, and forcing 0600 on a root-owned global
install would stop the user's CLI from reading it.

Tests fail on the old code (bootstrap: 0755/0644; electron source guard).

Reported-by: peterbussch
@diegosouzapw
diegosouzapw merged commit a13e82d into release/v3.8.51 Sep 28, 2026
16 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant