Files
deepseek-harness/packages/sandbox/sandbox-windows-acl/tests/token-failure-paths.spec.ts
Huanqi Cao 59a2e4d825 fix(ci): restore the native Windows coverage denominator to green
The windows-native job has been red since #1990 put the sandbox-windows-acl sources into the Windows 100%-per-file denominator without tests carrying them, and #1543 dropped the authoring.ts V8 ignore for the POSIX-only owner-execute branch. Non-blocking at merge time, the red state has propagated to every later pull request.

Cover every in-process ACL-sandbox failure branch with stub-based failure-path suites (ffi/acl/token/spawn/index), following the package's existing failure-paths pattern; the package now measures 100% per file under the Windows denominator. Exclude only the runner entry from the win32 denominator: it executes exclusively as a spawned child outside the instrumented run, and its behavior is pinned end-to-end by the runner suite. Restore the authoring.ts narrow V8 ignore and add one for the dispose token guard whose absent-token arm is lifecycle-unreachable. Update the dual-lane Agent Note with the denominator composition.
2026-08-10 19:07:35 +08:00

437 lines
15 KiB
TypeScript

/**
* Restricted-token failure-path tests with stub binding tables (the
* failure-paths.spec.ts pattern): every checked Win32 call in the token
* pipeline — open, logon-SID scan, well-known SID creation, default-DACL
* merge, restricted-token creation — has a failing counterpart, and each
* failure closes or frees what it created before throwing. Pure stubs — no
* real Win32 calls, so these run on every platform; the real-FFI round-trip
* lives in acl.spec.ts (win32 only).
*/
import { describe, expect, it, vi } from 'vitest'
import koffi from 'koffi'
import { allocBytes, isNullPtr } from '../src/ffi.ts'
import type { NativePtr, Win32Bindings } from '../src/ffi.ts'
import { Win32Error } from '../src/errors.ts'
import {
createRestrictedToken, findLogonSid, makeWellKnownSid, openCurrentProcessToken, setTokenDefaultDaclGrant,
} from '../src/token.ts'
import * as abi from '../src/win32-abi.ts'
const PVOID = koffi.pointer('void')
describe('openCurrentProcessToken failure paths', () => {
it('reports when OpenProcess yields no handle', () => {
const api = {
openProcess: vi.fn(() => 0n),
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
let caught: unknown
try {
openCurrentProcessToken(api)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('OpenProcess')
})
it('closes the process handle and reports when OpenProcessToken fails', () => {
const closeHandle = vi.fn(() => 1)
const api = {
openProcess: vi.fn(() => 7n),
openProcessToken: vi.fn(() => 0),
closeHandle,
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
let caught: unknown
try {
openCurrentProcessToken(api)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('OpenProcessToken')
expect(closeHandle).toHaveBeenCalledWith(7n)
})
it('reports a failed CloseHandle of the process handle', () => {
const api = {
openProcess: vi.fn(() => 7n),
openProcessToken: vi.fn((_process: unknown, _access: unknown, slot: NativePtr) => {
koffi.encode(slot, PVOID, 9n)
return 1
}),
closeHandle: vi.fn(() => 0),
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
let caught: unknown
try {
openCurrentProcessToken(api)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('CloseHandle')
})
it('rejects a NULL token handle after a successful OpenProcessToken', () => {
const api = {
openProcess: vi.fn(() => 7n),
openProcessToken: vi.fn(() => 1), // succeeds without writing the out slot
closeHandle: vi.fn(() => 1),
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
let caught: unknown
try {
openCurrentProcessToken(api)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('OpenProcessToken')
})
})
/**
* The stub the logon-SID scan needs: the size probe writes `needed`, the
* second call fills a TOKEN_GROUPS buffer (GroupCount@0, SID pointer@8,
* attributes@16) with the state's one group. The CopySid mock comes back
* beside the table for the one test that asserts on its arguments.
*/
function logonApi(state: {
needed: number
groupCount: number
sidPtr: bigint
logon: boolean
secondOk?: boolean
sidLength?: number
copyOk?: boolean
}): { api: Win32Bindings; copySid: ReturnType<typeof vi.fn> } {
const copySid = vi.fn(() => (state.copyOk === false ? 0 : 1))
const api = {
getTokenInformation: vi.fn((_token: unknown, cls: number, info: Buffer | null, _length: number, needed: NativePtr) => {
if (cls !== abi.TokenGroups) throw new Error(`unexpected token information class ${cls}`)
if (info === null) {
koffi.encode(needed, 'uint32', state.needed)
return 0 // the size probe is expected to "fail"
}
if (state.secondOk === false) return 0
info.writeUInt32LE(state.groupCount, 0)
if (state.groupCount > 0) {
info.writeBigUInt64LE(state.sidPtr, abi.TOKEN_GROUPS_OFFSET)
info.writeUInt32LE(state.logon ? abi.SE_GROUP_LOGON_ID : 0, abi.TOKEN_GROUPS_OFFSET + 8)
}
return 1
}),
getLengthSid: vi.fn(() => state.sidLength ?? 12),
copySid,
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
return { api, copySid }
}
describe('findLogonSid failure paths', () => {
const token = 9n as NativePtr
it('reports a size probe that wrote nothing', () => {
const { api } = logonApi({ needed: 0, groupCount: 0, sidPtr: 0n, logon: false })
let caught: unknown
try {
findLogonSid(api, token)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('GetTokenInformation')
})
it('rejects an implausibly small TokenGroups size', () => {
const { api } = logonApi({ needed: 4, groupCount: 0, sidPtr: 0n, logon: false })
let caught: unknown
try {
findLogonSid(api, token)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('GetTokenInformation')
})
it('reports a failed TokenGroups read', () => {
const { api } = logonApi({ needed: 24, groupCount: 1, sidPtr: 77n, logon: true, secondOk: false })
let caught: unknown
try {
findLogonSid(api, token)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('GetTokenInformation')
})
it('skips a NULL group SID pointer and throws when no logon SID remains', () => {
const { api } = logonApi({ needed: 24, groupCount: 1, sidPtr: 0n, logon: true })
expect(() => findLogonSid(api, token)).toThrow(/no logon SID found/u)
})
it('skips a non-logon group and throws when no logon SID remains', () => {
const { api } = logonApi({ needed: 24, groupCount: 1, sidPtr: 77n, logon: false })
expect(() => findLogonSid(api, token)).toThrow(/no logon SID found/u)
})
it('reports a zero logon-SID length', () => {
const { api } = logonApi({ needed: 24, groupCount: 1, sidPtr: 77n, logon: true, sidLength: 0 })
let caught: unknown
try {
findLogonSid(api, token)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('GetLengthSid')
})
it('reports a failed CopySid of the logon SID', () => {
const { api } = logonApi({ needed: 24, groupCount: 1, sidPtr: 77n, logon: true, copyOk: false })
let caught: unknown
try {
findLogonSid(api, token)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('CopySid')
})
it('copies the logon SID and returns the new allocation', () => {
const { api, copySid } = logonApi({ needed: 24, groupCount: 1, sidPtr: 77n, logon: true })
const copy = findLogonSid(api, token)
expect(isNullPtr(copy)).toBe(false)
expect(copySid).toHaveBeenCalledWith(12, copy, 77n)
})
})
describe('makeWellKnownSid failure paths', () => {
it('reports when CreateWellKnownSid fails', () => {
const api = {
createWellKnownSid: vi.fn(() => 0),
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
let caught: unknown
try {
makeWellKnownSid(api, abi.WinWorldSid)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('CreateWellKnownSid')
})
it('reports when the created well-known SID is invalid', () => {
const api = {
createWellKnownSid: vi.fn(() => 1),
isValidSid: vi.fn(() => 0),
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
let caught: unknown
try {
makeWellKnownSid(api, abi.WinWorldSid)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('IsValidSid')
})
})
/**
* The stub the default-DACL merge needs: the size probe writes `needed`, the
* second call fills the DACL pointer slot, and the merge/apply calls follow
* the state's results.
*/
function daclApi(state: {
needed: number
currentDacl: bigint
secondOk?: boolean
mergeResult?: number
newDacl: bigint
setTokenInfo?: number
}): Win32Bindings {
const api = {
getTokenInformation: vi.fn((_token: unknown, cls: number, info: Buffer | null, _length: number, needed: NativePtr) => {
if (cls !== abi.TokenDefaultDacl) throw new Error(`unexpected token information class ${cls}`)
if (info === null) {
koffi.encode(needed, 'uint32', state.needed)
return 0 // the size probe is expected to "fail"
}
if (state.secondOk === false) return 0
info.writeBigUInt64LE(state.currentDacl, 0)
return 1
}),
setEntriesInAclW: vi.fn((_count: unknown, _entries: unknown, _old: unknown, newAcl: NativePtr) => {
if (state.mergeResult !== undefined && state.mergeResult !== 0) return state.mergeResult
koffi.encode(newAcl, PVOID, state.newDacl)
return 0
}),
setTokenInformation: vi.fn(() => state.setTokenInfo ?? 1),
localFree: vi.fn(() => 0n),
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
return api
}
describe('setTokenDefaultDaclGrant failure paths', () => {
const token = 9n as NativePtr
const sid = 77n as NativePtr
it('reports a size probe that wrote nothing', () => {
const api = daclApi({ needed: 0, currentDacl: 0n, newDacl: 0n })
let caught: unknown
try {
setTokenDefaultDaclGrant(api, token, sid)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('GetTokenInformation')
})
it('reports a failed default-DACL read', () => {
const api = daclApi({ needed: 8, currentDacl: 88n, secondOk: false, newDacl: 0n })
let caught: unknown
try {
setTokenDefaultDaclGrant(api, token, sid)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('GetTokenInformation')
})
it('rejects a token that carries no default DACL', () => {
const api = daclApi({ needed: 8, currentDacl: 0n, newDacl: 0n })
expect(() => { setTokenDefaultDaclGrant(api, token, sid) }).toThrow(/no default DACL/u)
})
it('reports a failed SetEntriesInAclW merge', () => {
const api = daclApi({ needed: 8, currentDacl: 88n, mergeResult: 5, newDacl: 0n })
let caught: unknown
try {
setTokenDefaultDaclGrant(api, token, sid)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('SetEntriesInAclW')
})
it('rejects a NULL merged default DACL', () => {
const api = daclApi({ needed: 8, currentDacl: 88n, newDacl: 0n })
let caught: unknown
try {
setTokenDefaultDaclGrant(api, token, sid)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('SetEntriesInAclW')
})
it('frees the merged DACL and reports when SetTokenInformation fails', () => {
const localFree = vi.fn(() => 0n)
const api = daclApi({ needed: 8, currentDacl: 88n, newDacl: 99n, setTokenInfo: 0 })
;(api.localFree as unknown as ReturnType<typeof vi.fn>).mockImplementation(localFree)
let caught: unknown
try {
setTokenDefaultDaclGrant(api, token, sid)
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('SetTokenInformation')
expect(localFree).toHaveBeenCalledWith(99n)
})
it('frees the merged DACL after a successful apply', () => {
const localFree = vi.fn(() => 0n)
const api = daclApi({ needed: 8, currentDacl: 88n, newDacl: 99n })
;(api.localFree as unknown as ReturnType<typeof vi.fn>).mockImplementation(localFree)
setTokenDefaultDaclGrant(api, token, sid)
expect(localFree).toHaveBeenCalledWith(99n)
})
})
describe('createRestrictedToken failure paths', () => {
it('builds the read-only restricting list without a write SID', () => {
const create = vi.fn((
_existing: unknown, _flags: unknown, _dc: unknown, _ds: unknown, _pc: unknown, _pd: unknown,
count: number, _sids: unknown, slot: NativePtr,
) => {
koffi.encode(slot, PVOID, 9n)
expect(count).toBe(2)
return 1
})
const api = { createRestrictedToken: create } as unknown as Win32Bindings
const logon = allocBytes(12)
expect(createRestrictedToken(api, 1n as NativePtr, logon, undefined, { world: 2n as NativePtr }, 'read-only')).toBe(9n)
})
it('builds the workspace-write restricting list with the write SID', () => {
const create = vi.fn((
_existing: unknown, _flags: unknown, _dc: unknown, _ds: unknown, _pc: unknown, _pd: unknown,
count: number, _sids: unknown, slot: NativePtr,
) => {
koffi.encode(slot, PVOID, 9n)
expect(count).toBe(3)
return 1
})
const api = { createRestrictedToken: create } as unknown as Win32Bindings
const logon = allocBytes(12)
expect(createRestrictedToken(api, 1n as NativePtr, logon, 3n as NativePtr, { world: 2n as NativePtr }, 'workspace-write')).toBe(9n)
})
it('reports when CreateRestrictedToken fails', () => {
const api = {
createRestrictedToken: vi.fn(() => 0),
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
const logon = allocBytes(12)
let caught: unknown
try {
createRestrictedToken(api, 1n as NativePtr, logon, undefined, { world: 2n as NativePtr }, 'read-only')
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('CreateRestrictedToken')
})
it('rejects a NULL token handle after a successful CreateRestrictedToken', () => {
const api = {
createRestrictedToken: vi.fn(() => 1), // succeeds without writing the out slot
getLastError: vi.fn(() => 5),
formatMessageW: vi.fn(() => 0),
} as unknown as Win32Bindings
const logon = allocBytes(12)
let caught: unknown
try {
createRestrictedToken(api, 1n as NativePtr, logon, undefined, { world: 2n as NativePtr }, 'read-only')
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(Win32Error)
expect((caught as Win32Error).api).toBe('CreateRestrictedToken')
})
})