mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-22 21:00:51 +02:00
fix(browse): self-repair broken Windows DACLs on state dirs (#1605)
icacls '/inheritance:r /grant:r' can partially fail on localized or domain accounts: inheritance strips but the user grant doesn't resolve, leaving a machine-SID-only DACL the owner can't even list — the sidebar/PTY failure chain in #1605, caused by the very hardening call meant to protect the dir. mkdirSecure now verifies listability after hardening (a real readdir — fs.accessSync doesn't consult NTFS ACLs) and repairs via icacls /reset, re-hardens, and if hardening breaks access again leaves inherited ACLs: functional-but-unhardened beats hardened-but-unusable. The icacls calls carry windowsHide (#1835's last two sites) and the fork's static spawn-hide tripwire lands here, pinning every covered site. file-permissions.test.ts is already in the windows-free-tests curated shard, so the DACL contract executes on windows-latest. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Sina Matian
Claude Fable 5
parent
270f1a038a
commit
fa4e4c3f2c
@@ -22,6 +22,7 @@ import {
|
||||
writeSecureFile,
|
||||
appendSecureFile,
|
||||
mkdirSecure,
|
||||
repairBrokenDacl,
|
||||
__resetWarnedForTests,
|
||||
} from '../src/file-permissions';
|
||||
|
||||
@@ -145,4 +146,35 @@ describe('mkdirSecure', () => {
|
||||
expect(fs.existsSync(path.join(tmpDir, 'a', 'b'))).toBe(true);
|
||||
expect(fs.existsSync(d)).toBe(true);
|
||||
});
|
||||
|
||||
test('created directory is listable by the creating process', () => {
|
||||
// #1605 contract: whatever ACL hardening happens, the client must be
|
||||
// able to read its own state dir immediately after creation.
|
||||
const d = path.join(tmpDir, 'state');
|
||||
mkdirSecure(d);
|
||||
fs.writeFileSync(path.join(d, 'browse.json'), '{}');
|
||||
expect(fs.readdirSync(d)).toContain('browse.json');
|
||||
});
|
||||
});
|
||||
|
||||
describe('repairBrokenDacl', () => {
|
||||
test('is a no-op on non-Windows platforms', () => {
|
||||
if (process.platform === 'win32') return;
|
||||
const d = path.join(tmpDir, 'dir');
|
||||
fs.mkdirSync(d);
|
||||
expect(() => repairBrokenDacl(d)).not.toThrow();
|
||||
});
|
||||
|
||||
test('on Windows, does not throw and directory stays listable', () => {
|
||||
if (process.platform !== 'win32') return;
|
||||
const d = path.join(tmpDir, 'dir');
|
||||
fs.mkdirSync(d);
|
||||
expect(() => repairBrokenDacl(d)).not.toThrow();
|
||||
expect(() => fs.readdirSync(d)).not.toThrow();
|
||||
});
|
||||
|
||||
test('on Windows, swallows icacls failure on a nonexistent path', () => {
|
||||
if (process.platform !== 'win32') return;
|
||||
expect(() => repairBrokenDacl(path.join(tmpDir, 'nonexistent'))).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user