diff --git a/src/lib/credentials.test.ts b/src/lib/credentials.test.ts index 447d9d3..485132f 100644 --- a/src/lib/credentials.test.ts +++ b/src/lib/credentials.test.ts @@ -216,15 +216,31 @@ describe('ensureRestrictiveMode', () => { spawnSync: spawn, }); - expect(spawn).toHaveBeenCalledWith( + // First call: /reset re-enables inheritance from the parent directory so the + // owner can always access the file (fixes EPERM on Microsoft Account / domain + // account machines where USERNAME does not resolve to the file-owner SID). + expect(spawn).toHaveBeenNthCalledWith( + 1, 'icacls', - [credentialsPath, '/inheritance:r', '/grant:r', 'alice:F'], + [credentialsPath, '/reset'], { shell: false, stdio: 'ignore', windowsHide: true, }, ); + // Second call: /grant:r adds an explicit Full Control entry as belt-and-suspenders. + expect(spawn).toHaveBeenNthCalledWith( + 2, + 'icacls', + [credentialsPath, '/grant:r', 'alice:F'], + { + shell: false, + stdio: 'ignore', + windowsHide: true, + }, + ); + expect(spawn).toHaveBeenCalledTimes(2); }); it('warns on Windows when credentials ACL tightening cannot run', () => { diff --git a/src/lib/credentials.ts b/src/lib/credentials.ts index c67944b..efe8525 100644 --- a/src/lib/credentials.ts +++ b/src/lib/credentials.ts @@ -231,7 +231,21 @@ function ensureWindowsRestrictiveAcl(path: string, options: RestrictiveModeOptio } const run = options.spawnSync ?? spawnSync; - const result = run('icacls', [path, '/inheritance:r', '/grant:r', `${username}:F`], { + + // Reset to re-enable inheritance from the parent directory first. + // Using /inheritance:r (the previous approach) strips all inherited ACEs and + // relies solely on the USERNAME-based grant — on Windows the env USERNAME may + // not resolve to the same SID that owns the file (e.g. Microsoft Account / + // domain account mismatches), which leaves the file unreadable by anyone. + // /reset restores inherited ACEs so the owner can always access the file, then + // the explicit /grant:r adds a belt-and-suspenders Full-Control entry. + run('icacls', [path, '/reset'], { + shell: false, + stdio: 'ignore', + windowsHide: true, + }); + + const result = run('icacls', [path, '/grant:r', `${username}:F`], { shell: false, stdio: 'ignore', windowsHide: true,