From 2a47bdb8e74e34ae633e5502a88461063f069851 Mon Sep 17 00:00:00 2001 From: Arthur Bogaart <526197+abogaart@users.noreply.github.com> Date: Mon, 21 Sep 2026 17:28:23 +0200 Subject: [PATCH 1/2] fix: spawn Apple /bin/chmod by absolute path when hardening credentials The macOS credential hardener strips ACLs with `chmod -N`, spawning `chmod` by bare name. On machines where Nix home-manager or Homebrew coreutils sit ahead of /bin on PATH, that resolves to GNU chmod, which has no `-N` flag and exits non-zero. The hardener treated that as a failure and refused every save from the Provider Settings panel with "could not restrict the credential file to your account; nothing was saved". Spawn /bin/chmod by absolute path instead, matching how the Windows branch already resolves its native tools without consulting PATH, and add a regression test that pins the absolute path. --- server/standalone/key-setup-hardening.mjs | 6 +++++- src/keySetupHardening.test.mjs | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/server/standalone/key-setup-hardening.mjs b/server/standalone/key-setup-hardening.mjs index 002ccabdc..e2d11182c 100644 --- a/server/standalone/key-setup-hardening.mjs +++ b/server/standalone/key-setup-hardening.mjs @@ -123,7 +123,11 @@ export function hardenCredentialFile( if (platform !== 'win32') { try { if (platform === 'darwin') { - const aclRemoval = spawn('chmod', ['-N', filepath], { + // `-N` (strip the ACL) is Apple-only. Spawn /bin/chmod by absolute + // path, never by name: a Nix/Homebrew coreutils profile puts GNU chmod + // first on PATH, and GNU chmod rejects `-N` — which read as a + // hardening failure and refused every save on such machines. + const aclRemoval = spawn('/bin/chmod', ['-N', filepath], { stdio: 'ignore', }); if (!commandCompletedSuccessfully(aclRemoval)) return false; diff --git a/src/keySetupHardening.test.mjs b/src/keySetupHardening.test.mjs index 2578be37d..c519576e3 100644 --- a/src/keySetupHardening.test.mjs +++ b/src/keySetupHardening.test.mjs @@ -55,6 +55,21 @@ test('macOS ACL removal failure stops before chmod and fails closed', () => { assert.deepEqual(fileSystem.calls, [], 'mode bits must not disguise an ACL-removal failure'); }); +test('macOS ACL removal spawns Apple /bin/chmod by absolute path, not via PATH', () => { + const spawned = []; + hardenCredentialFile(FILE, { + platform: 'darwin', + fileSystem: fileSystemWithMode(0o100600), + spawn(command, args) { + spawned.push([command, args]); + return { status: 0, signal: null }; + }, + }); + // A coreutils-first PATH (Nix home-manager, Homebrew) resolves bare `chmod` + // to GNU chmod, which has no `-N` and would fail every save. + assert.deepEqual(spawned, [['/bin/chmod', ['-N', FILE]]]); +}); + test('POSIX hardening verifies the resulting 0600 mode', () => { const goodFs = fileSystemWithMode(0o100600); assert.equal(hardenCredentialFile(FILE, { From f631d5428baffd475696fed0364bbf99c0ba6e22 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Thu, 24 Sep 2026 22:01:56 -0700 Subject: [PATCH 2/2] docs: changelog entry for the macOS chmod fix (#694) Credit the fix in CHANGELOG and tidy the wording of its inline comment. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 5 +++++ server/standalone/key-setup-hardening.mjs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dceec6a9c..419c03e45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +- Saving a key from Provider Settings works again on Macs where Nix or + Homebrew coreutils sit ahead of `/bin` on `PATH`. The credential hardener + now spawns Apple's `/bin/chmod -N` by absolute path; GNU `chmod` has no `-N`, + so the ACL strip failed closed and every save was refused (Arthur Bogaart, #694). + - Render on iPad and iPhone instead of stopping with "An error occurred while rendering." Cesium's per-vertex model atmosphere binds shader `out` parameters directly to varyings, which Apple's Metal/ANGLE backend cannot diff --git a/server/standalone/key-setup-hardening.mjs b/server/standalone/key-setup-hardening.mjs index e2d11182c..78440948c 100644 --- a/server/standalone/key-setup-hardening.mjs +++ b/server/standalone/key-setup-hardening.mjs @@ -125,7 +125,7 @@ export function hardenCredentialFile( if (platform === 'darwin') { // `-N` (strip the ACL) is Apple-only. Spawn /bin/chmod by absolute // path, never by name: a Nix/Homebrew coreutils profile puts GNU chmod - // first on PATH, and GNU chmod rejects `-N` — which read as a + // first on PATH, and GNU chmod rejects `-N` — which was treated as a // hardening failure and refused every save on such machines. const aclRemoval = spawn('/bin/chmod', ['-N', filepath], { stdio: 'ignore',