test(sandbox): close Windows ACL coverage gaps
This commit is contained in:
@@ -239,6 +239,8 @@ export class AclSandbox {
|
||||
this.tempWriteSidPtr = this.tempWriteSid === undefined ? undefined : parseSid(this.tempWriteSid)
|
||||
|
||||
const tempDir = this.mode === 'read-only' || this.tempDirOption === null ? null : this.tempDirOption
|
||||
/* v8 ignore next -- constructor validation requires workspace-write to supply
|
||||
an explicit temp directory or null; the other branches normalize to null. */
|
||||
if (tempDir === undefined) throw new Error('AclSandbox workspace-write temp directory was not resolved')
|
||||
if (tempDir !== null) {
|
||||
if (!existsSync(tempDir) || !statSync(tempDir).isDirectory()) {
|
||||
|
||||
@@ -186,6 +186,28 @@ describe('AclSandbox constructor validation', () => {
|
||||
expect(sandbox.mode).toBe('read-only')
|
||||
expect(sandbox.tempDir).toBeUndefined()
|
||||
})
|
||||
|
||||
it('rejects temp authority under read-only', () => {
|
||||
const workspace = scratch()
|
||||
const temp = scratch()
|
||||
expect(() => new AclSandbox({ writableDirs: [workspace], tempDir: temp, mode: 'read-only' }))
|
||||
.toThrow(/read-only does not accept a temp directory/u)
|
||||
expect(() => new AclSandbox({ writableDirs: [workspace], tempDir: null, writeSid: 'S-1-4-9000-1', mode: 'read-only' }))
|
||||
.toThrow(/read-only does not accept write SIDs/u)
|
||||
expect(() => new AclSandbox({ writableDirs: [workspace], tempDir: null, tempWriteSid: 'S-1-4-9000-1-1', mode: 'read-only' }))
|
||||
.toThrow(/read-only does not accept write SIDs/u)
|
||||
})
|
||||
|
||||
it('rejects a temp SID when temp writes are disabled', () => {
|
||||
const workspace = scratch()
|
||||
expect(() => new AclSandbox({
|
||||
writableDirs: [workspace],
|
||||
tempDir: null,
|
||||
writeSid: 'S-1-4-9000-2',
|
||||
tempWriteSid: 'S-1-4-9000-2-1',
|
||||
mode: 'workspace-write',
|
||||
})).toThrow(/temp write SID requires a temp directory/u)
|
||||
})
|
||||
})
|
||||
|
||||
describe('AclSandbox init', () => {
|
||||
@@ -274,20 +296,29 @@ describe('AclSandbox init', () => {
|
||||
await expect(sandbox.init()).rejects.toBeInstanceOf(Win32Error)
|
||||
})
|
||||
|
||||
it('reports a failed close of the current process token', async () => {
|
||||
const { closeHandle } = state.stubs as HappyStubs
|
||||
it('aggregates failed current and restricted token closes after init', async () => {
|
||||
const { closeHandle, createRestrictedToken } = state.stubs as HappyStubs
|
||||
const workspace = scratch()
|
||||
const sandbox = new AclSandbox({ writableDirs: [workspace], tempDir: null, writeSid: 'S-1-4-9000-9', mode: 'workspace-write' })
|
||||
const restrictedToken = 99n
|
||||
createRestrictedToken.mockImplementation((
|
||||
_existing: unknown, _flags: unknown, _dc: unknown, _ds: unknown, _pc: unknown, _pd: unknown,
|
||||
_rc: unknown, _rs: unknown, slot: NativePtr,
|
||||
) => {
|
||||
koffi.encode(slot, PVOID, restrictedToken)
|
||||
return 1
|
||||
})
|
||||
// fresh() hands out 1n to OpenProcess and 2n to OpenProcessToken; the
|
||||
// token-layer close of 1n succeeds and init's close of 2n fails.
|
||||
closeHandle.mockImplementation((handle: NativePtr) => (handle === 2n ? 0 : 1))
|
||||
closeHandle.mockImplementation((handle: NativePtr) => (handle === 2n || handle === restrictedToken ? 0 : 1))
|
||||
// The failure lands after this.token is stored but before this.api is
|
||||
// assigned. Cleanup retries the still-open handle and reports both close
|
||||
// failures after releasing the restricted token and parsed SIDs.
|
||||
// failures plus the restricted-token close after releasing parsed SIDs.
|
||||
await expect(sandbox.init()).rejects.toMatchObject({
|
||||
errors: [
|
||||
{ api: 'CloseHandle' },
|
||||
{ api: 'CloseHandle' },
|
||||
{ api: 'CloseHandle' },
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user