/** * Windows ABI constants for the ACL-sandbox backend. * * Every value was verified against the actual MinGW Windows headers on this * machine (C:\Strawberry\c\x86_64-w64-mingw32\include\) and cross-checked at * runtime by verify/abi-probe.cpp (same numbers; static_asserts passed). * Regenerate the probe with: * g++ -std=c++20 -municode -O2 -o abi-probe.exe abi-probe.cpp -ladvapi32 && .\abi-probe.exe * * The port intentionally excludes two pieces of the original POC * (github.com/huoyaoyuan/windows-acl-restrict-poc @ 10e4dfb), both verified * empirically on Windows 11 build 26200: * - S-1-2-1 (console logon SID) in the restricting list: the POC created it * via CreateWellKnownSid(WinLocalLogonSid) which fails here with * ERROR_INVALID_PARAMETER (87), leaving a garbage SID that makes * CreateRestrictedToken fail with ERROR_INVALID_SID (1337); using the * correct WinConsoleLogonSid does produce a valid S-1-2-1, but the child * then still dies with STATUS_DLL_INIT_FAILED (0xC0000142) whenever * CREATE_NO_WINDOW / CREATE_NEW_CONSOLE is used. * - Console isolation: under this restriction scheme a hidden console is not * attainable, so children share the host console (stdio redirection is * pipe-based and unaffected). * @module @deepseek-ai/dsh-sandbox-windows-acl/win32-abi */ // ---- winnt.h --------------------------------------------------------------- // TOKEN_* access rights (winnt.h lines ~3928) /** TOKEN_ASSIGN_PRIMARY: required to create a process with the token (CreateProcessAsUser). */ export const TOKEN_ASSIGN_PRIMARY = 0x0001 /** TOKEN_DUPLICATE: required to duplicate a token (DuplicateTokenEx). */ export const TOKEN_DUPLICATE = 0x0002 /** TOKEN_QUERY: required to read token information (GetTokenInformation). */ export const TOKEN_QUERY = 0x0008 /** TOKEN_ADJUST_DEFAULT: required to change a token's default DACL. */ export const TOKEN_ADJUST_DEFAULT = 0x0080 // SID_AND_ATTRIBUTES.Attributes flags (winnt.h lines ~3446) /** * SE_GROUP_LOGON_ID: marks a token group SID as the logon SID (compared with * `>>> 0` — the flag's high bit makes it negative as a signed 32-bit number). */ export const SE_GROUP_LOGON_ID = 0xC0000000 // Generic file access (winnt.h lines ~5893-5913): // FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES // | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE /** STANDARD_RIGHTS_WRITE (== READ_CONTROL): the standard-rights component of generic write access. */ export const STANDARD_RIGHTS_WRITE = 0x00020000 // == READ_CONTROL /** FILE_GENERIC_WRITE: every file-write permission bit plus SYNCHRONIZE. */ export const FILE_GENERIC_WRITE = 0x00120116 /** DELETE: remove or rename the object (winnt.h line ~3009). */ export const DELETE = 0x00010000 /** FILE_DELETE_CHILD: remove or rename a directory's children (winnt.h line ~5907). */ export const FILE_DELETE_CHILD = 0x0040 // The POC granted FILE_GENERIC_WRITE minus READ_CONTROL, which displays as // "Write" in Explorer/icacls (windows-acl-restrict-poc.cpp line 16). The // sandbox grant adds DELETE and FILE_DELETE_CHILD so confined // delete/rename/git operations inside the granted trees pass the token's // access check too; Write+DELETE displays as "Modify" in icacls. // WRITE_DAC/WRITE_OWNER stay OUT deliberately — granting them would let the // child take ownership or rewrite DACLs and escape the allowlist (the // security boundary). /** * GRANT_MASK: FILE_GENERIC_WRITE minus READ_CONTROL plus DELETE and * FILE_DELETE_CHILD — the write+delete access mask the capability-SID ACEs grant * (displays as "Modify" in Explorer/icacls). WRITE_DAC/WRITE_OWNER are * deliberately excluded: they would let the confined child take ownership or * rewrite DACLs. */ export const GRANT_MASK = (FILE_GENERIC_WRITE | DELETE | FILE_DELETE_CHILD) & ~STANDARD_RIGHTS_WRITE // 0x00110156 /** * FILE_ALL_ACCESS (winnt.h line ~2789: STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE * | 0x1FF): full file-object access. The mask of the ACE merged into the * restricted token's DEFAULT DACL — the token holder must keep full access to * every NEW object it creates (pipes included), and the ACE must name a * restricting SID so the write pass-2 check passes at creation. */ export const FILE_ALL_ACCESS = 0x1F01FF // CreateRestrictedToken flags (winnt.h lines ~4284) /** DISABLE_MAX_PRIVILEGE: strip the token's maximum-privilege elevation so the confined child cannot escalate. */ export const DISABLE_MAX_PRIVILEGE = 0x1 /** LUA_TOKEN: produce a limited-user (filtered admin) token. */ export const LUA_TOKEN = 0x4 /** WRITE_RESTRICTED: intersect write access with the restricting SIDs' ACL grants — the sandbox's core mechanism. */ export const WRITE_RESTRICTED = 0x8 // WELL_KNOWN_SID_TYPE (winnt.h lines ~3369-3407) /** WinWorldSid: S-1-1-0 (Everyone) — the only well-known SID the restricted tokens use (keep-alive group; see token.ts). */ export const WinWorldSid = 1 // TOKEN_INFORMATION_CLASS (winnt.h line ~3963: TokenUser=1, TokenGroups=2) /** TokenGroups: GetTokenInformation class returning the token's group SIDs. */ export const TokenGroups = 2 /** TokenDefaultDacl: the token's default DACL — the DACL every NEW object created without an explicit SD takes. */ export const TokenDefaultDacl = 6 // SECURITY_INFORMATION (winnt.h line ~4293) /** DACL_SECURITY_INFORMATION: read/write only the DACL of a security descriptor. */ export const DACL_SECURITY_INFORMATION = 0x00000004 // PROCESS access rights (winnt.h lines ~4364) /** PROCESS_QUERY_INFORMATION: read exit status and times of a process handle. */ export const PROCESS_QUERY_INFORMATION = 0x0400 // ---- accctrl.h ------------------------------------------------------------- // SE_OBJECT_TYPE (accctrl.h line ~22: SE_UNKNOWN_OBJECT_TYPE=0, SE_FILE_OBJECT=1) /** SE_FILE_OBJECT: the trustee path names a filesystem object. */ export const SE_FILE_OBJECT = 1 // TRUSTEE_FORM / TRUSTEE_TYPE (accctrl.h lines ~38-55): both enums start at 0 /** TRUSTEE_IS_UNKNOWN: TRUSTEE_TYPE unknown (TrusteeForm carries the shape). */ export const TRUSTEE_IS_UNKNOWN = 0 /** TRUSTEE_IS_SID: TRUSTEE_FORM — Trustee.ptstrName is a SID pointer. */ export const TRUSTEE_IS_SID = 0 /** NO_MULTIPLE_TRUSTEE: Trustee.pMultipleTrustee is null. */ export const NO_MULTIPLE_TRUSTEE = 0 // ACCESS_MODE (accctrl.h line ~127: NOT_USED_ACCESS=0, GRANT_ACCESS=1, REVOKE_ACCESS=4) /** GRANT_ACCESS: SetEntriesInAclW adds the entry as an allow ACE. */ export const GRANT_ACCESS = 1 /** REVOKE_ACCESS: SetEntriesInAclW removes the matching allow ACE. */ export const REVOKE_ACCESS = 4 // grfInheritance (accctrl.h lines ~137-142) /** * SUB_CONTAINERS_AND_OBJECTS_INHERIT: the ACE applies to the directory, its * subdirectories, and files (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE). */ export const SUB_CONTAINERS_AND_OBJECTS_INHERIT = 0x3 // == OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE // ---- winbase.h ------------------------------------------------------------- /** * STARTF_USESTDHANDLES: STARTUPINFOW dwFlags — the child uses the hStd* * handles, required because Node clears stdio inheritability at startup. */ export const STARTF_USESTDHANDLES = 0x00000100 /** HANDLE_FLAG_INHERIT: SetHandleInformation flag re-enabling handle inheritance for the spawned child's stdio handles. */ export const HANDLE_FLAG_INHERIT = 0x1 /** INFINITE: never-timeout wait value. */ export const INFINITE = 0xFFFFFFFF /** MAX_PATH: legacy path length bound. */ export const MAX_PATH = 260 // winbase.h line ~410: the confined child starts suspended so the runner can // assign it to the kill-on-close job before any of its code runs. /** CREATE_SUSPENDED: create the child with its primary thread suspended until ResumeThread. */ export const CREATE_SUSPENDED = 0x4 // winbase.h lines ~497-499: GetStdHandle selectors. /** STD_INPUT_HANDLE: GetStdHandle selector for the standard input. */ export const STD_INPUT_HANDLE = -10 /** STD_OUTPUT_HANDLE: GetStdHandle selector for the standard output. */ export const STD_OUTPUT_HANDLE = -11 /** STD_ERROR_HANDLE: GetStdHandle selector for the standard error. */ export const STD_ERROR_HANDLE = -12 // FormatMessageW flags (winbase.h lines ~1446-1469) /** FORMAT_MESSAGE_FROM_SYSTEM: format the message from the system message table. */ export const FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 /** FORMAT_MESSAGE_IGNORE_INSERTS: skip insert-sequence substitution. */ export const FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200 // ---- error codes ----------------------------------------------------------- /** ERROR_SUCCESS: the operation succeeded. */ export const ERROR_SUCCESS = 0 /** ERROR_INSUFFICIENT_BUFFER: a size-probe call succeeded but needs a larger buffer. */ export const ERROR_INSUFFICIENT_BUFFER = 122 /** ERROR_BROKEN_PIPE: the pipe's other end has closed. */ export const ERROR_BROKEN_PIPE = 109 /** ERROR_NO_DATA: the pipe is being closed. */ export const ERROR_NO_DATA = 232 /** ERROR_LOCK_VIOLATION: a byte-range lock conflicts with an existing lock (winerror.h line ~78). */ export const ERROR_LOCK_VIOLATION = 33 // ---- lock files (fileapi.h / minwinbase.h / winnt.h) ----------------------- // CreateFileW dwDesiredAccess for the ACL lock files: plain read+write is // enough to take byte-range locks. /** GENERIC_READ: generic read access (winnt.h line ~3028). */ export const GENERIC_READ = 0x80000000 /** GENERIC_WRITE: generic write access (winnt.h line ~3029). */ export const GENERIC_WRITE = 0x40000000 // CreateFileW dwShareMode: the lock file is shared for read/write but NOT // for delete — if a locked file could be deleted and recreated underneath the // lock holder, two processes could hold "the same" lock on different files. /** FILE_SHARE_READ: other opens may read (winnt.h line ~5949). */ export const FILE_SHARE_READ = 0x00000001 /** FILE_SHARE_WRITE: other opens may write (winnt.h line ~5950). */ export const FILE_SHARE_WRITE = 0x00000002 /** FILE_SHARE_DELETE: other opens may delete (winnt.h line ~5951) — deliberately NOT used for lock files. */ export const FILE_SHARE_DELETE = 0x00000004 /** OPEN_ALWAYS: create the lock file if absent, open it otherwise (fileapi.h line ~21). */ export const OPEN_ALWAYS = 4 // LockFileEx dwFlags (minwinbase.h lines ~180-181, included by winbase.h). /** LOCKFILE_EXCLUSIVE_LOCK: request an exclusive byte-range lock. */ export const LOCKFILE_EXCLUSIVE_LOCK = 0x2 /** LOCKFILE_FAIL_IMMEDIATELY: fail with ERROR_LOCK_VIOLATION instead of waiting. */ export const LOCKFILE_FAIL_IMMEDIATELY = 0x1 // ACE_HEADER.AceType (winnt.h lines ~3449-3463) /** ACCESS_ALLOWED_ACE_TYPE: an access-allowed ACE granting the mask to the trustee. */ export const ACCESS_ALLOWED_ACE_TYPE = 0 // SID structure (winnt.h line ~280 SID_IDENTIFIER_AUTHORITY; line ~286 // #define SID_MAX_SUB_AUTHORITIES 15). /** SID_MAX_SUB_AUTHORITIES: the most subauthorities a SID may carry. */ export const SID_MAX_SUB_AUTHORITIES = 15 // ACE_HEADER.AceFlags (winnt.h lines ~3477-3524): inherited ACEs shown when // reading a DACL are marked with this bit and are not part of the explicit // DACL edits this module makes. /** INHERITED_ACE: the ACE was inherited from the parent object, not stored explicitly. */ export const INHERITED_ACE = 0x10 // ---- job object (winnt.h lines ~4859-4866, ~5138, ~5190-5199) -------------- // JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE: the child dies when the runner's last // job handle closes — the orphan-child backstop for the runner design. /** JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE: the child dies when the runner's last job handle closes — the orphan-child backstop. */ export const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000 // JOBOBJECTINFOCLASS: JobObjectBasicAccountingInformation=1, ..., ExtendedLimit=9. /** JobObjectExtendedLimitInformation: JOBOBJECTINFOCLASS for the extended limit structure. */ export const JobObjectExtendedLimitInformation = 9 // sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), verified by abi-probe. /** sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), verified by abi-probe. */ export const JOBOBJECT_EXTENDED_LIMIT_SIZE = 144 // LimitFlags offset inside JOBOBJECT_EXTENDED_LIMIT_INFORMATION // (BasicLimitInformation@0 + PerProcessUserTimeLimit@0 + PerJobUserTimeLimit@8), // verified by abi-probe. /** * LimitFlags offset inside JOBOBJECT_EXTENDED_LIMIT_INFORMATION * (BasicLimitInformation@0 + PerProcessUserTimeLimit@0 + * PerJobUserTimeLimit@8), verified by abi-probe. */ export const JOBOBJECT_EXTENDED_LIMIT_FLAGS_OFFSET = 16 // ---- ABI layout, verified by verify/abi-probe.cpp (x64) -------------------- /** SECURITY_MAX_SID_SIZE: maximum SID byte size. */ export const SECURITY_MAX_SID_SIZE = 68 /** SID_AND_ATTRIBUTES stride: { PSID Sid @0 (8); DWORD Attributes @8 (4) } + pad. */ export const SID_AND_ATTRIBUTES_SIZE = 16 /** TOKEN_GROUPS.Groups[] starts at offset 8 (GroupCount @0 + alignment). */ export const TOKEN_GROUPS_OFFSET = 8 /** sizeof(EXPLICIT_ACCESS_W): perms@0 mode@4 inheritance@8 Trustee@16. */ export const EXPLICIT_ACCESS_W_SIZE = 48 /** Trustee offset inside EXPLICIT_ACCESS_W. */ export const TRUSTEE_W_OFFSET = 16 /** ptstrName offset inside TRUSTEE_W (=> 40 inside EXPLICIT_ACCESS_W). */ export const TRUSTEE_W_PTSTRNAME_OFFSET = 24 /** sizeof(STARTUPINFOW), verified by abi-probe. */ export const STARTUPINFOW_SIZE = 104 /** sizeof(PROCESS_INFORMATION), verified by abi-probe. */ export const PROCESS_INFORMATION_SIZE = 24