/** * ACL editing helpers: grant/revoke the orphan write SID on a directory via * SetEntriesInAclW + SetNamedSecurityInfoW (the same calls the POC uses, with * the failure handling the POC lacks). Every API call is checked and every * failure is reported with the API name, the exact Win32 code, the formatted * system text, and the affected path. * * Concurrency: grants are read-merge-write against the directory's CURRENT * DACL, and the whole get-merge-set sequence runs under a per-path exclusive * LockFileEx lock (see {@link withPathLock}) so concurrent sandbox instances * cannot clobber each other's ACEs. * @module @deepseek-ai/dsh-sandbox-windows-acl/acl */ import type { NativePtr, Win32Bindings } from './ffi.ts'; /** * Pack one EXPLICIT_ACCESS_W (48 bytes, layout verified by abi-probe.cpp): * perms@0, mode@4, inheritance@8, Trustee@16 { pMultipleTrustee@16, * MultipleTrusteeOperation@24, TrusteeForm@28, TrusteeType@32, ptstrName@40 }. * `permissions` is the access mask; the POC passes 0 for REVOKE_ACCESS, which * removes every ACE for the trustee. * @param sidPtr - the trustee SID the entry names. * @param mode - the access mode (GRANT_ACCESS or REVOKE_ACCESS). * @param permissions - the access mask to grant (0 for REVOKE_ACCESS). * @returns the packed entry buffer. */ export declare function buildExplicitAccess(sidPtr: NativePtr, mode: number, permissions: number): Buffer; /** * One lock file per protected path: `\dsh-acl-locks\.lock`. The lock root derives from * GetTempPathW (never from runner argv or DSH_HOME), and the lowercasing * maps Windows's case-insensitive path spellings onto one lock. * @param api - the binding table. * @param path - the protected directory (absolute). * @returns the lock file path for that directory. */ export declare function lockFilePath(api: Win32Bindings, path: string): string; /** * Run `action` holding the per-path exclusive lock: CreateFileW * (OPEN_ALWAYS, shared read/write but NOT delete — a deletable lock file * could be removed and recreated under the holder, letting two processes * hold "the same" lock), then a one-byte LockFileEx * (LOCKFILE_EXCLUSIVE_LOCK, zeroed OVERLAPPED = lock from offset 0 on the * synchronous handle — see allocOverlapped for why not NULL), then * UnlockFileEx + CloseHandle. Fail-closed: open/lock/unlock/close failures * throw like every other Win32 call in this package; an `action` failure * still unlocks (best-effort) and rethrows the original error. * @param api - the binding table. * @param path - the protected directory (absolute). * @param action - the get-merge-set sequence to serialize. * @returns the action's result. */ export declare function withPathLock(api: Win32Bindings, path: string, action: () => T): T; /** * Grant `GRANT_MASK` (Write+Delete, displays as "Modify") to the orphan SID * on `path`, inheriting to subcontainers and objects. Idempotent: when the * directory's current explicit DACL already carries the exact ACE (the * per-session grant surviving from a previous server lifetime), the * SetNamedSecurityInfoW apply is SKIPPED — it would otherwise re-propagate * the identical ACE across the whole tree (eager inheritance; minutes on * large workspaces). Otherwise read-merge-write: the new ACE merges into the * directory's CURRENT explicit DACL (same shape as {@link revokeWrite}), so * pre-existing explicit ACEs survive. Runs under the per-path lock. The * directory must be owned by the caller (owner implicit WRITE_DAC) — same * precondition as the POC. * @param api - the binding table. * @param path - the directory whose DACL gains the grant (the workspace or temp root). * @param sidPtr - the orphan write SID the ACE names. */ export declare function grantWrite(api: Win32Bindings, path: string, sidPtr: NativePtr): void; /** * Remove every ACE for the orphan SID from the directory DACL (REVOKE_ACCESS * merge — other entries are preserved). Returns whether an ACE removal was * attempted (false when the directory carries no DACL at all). * * Runs under the per-path lock (the whole get-merge-set sequence); the * descriptor/ACL allocation contract lives on {@link readCurrentDacl}. * @param api - the binding table. * @param path - the directory whose DACL loses the orphan-SID ACEs. * @param sidPtr - the orphan write SID whose ACEs are removed. * @returns whether an ACE removal was attempted (false when the directory carries no DACL at all). */ export declare function revokeWrite(api: Win32Bindings, path: string, sidPtr: NativePtr): boolean; //# sourceMappingURL=acl.d.ts.map