Related: previous report #29
("Windows restricted_token: Hermes tools fail because MSYS2/git-bash cannot initialize (CreateFileMapping error 5)").
The PR #275 fix noted in the CHANGELOG does not resolve this on 0.9.40; this issue
tracks the post-fix reproduction with new procmon evidence (per-process msys-*-sigwait
named-pipe denials, see Actual behavior).
finsafe version
0.9.40 (output of finsafe version)
Platform
- Windows 11 Pro, build 26200 (NT 10.0.26200.9168)
finsafe probe --json highlights: appcontainer_works=true, helper_installed=false, conpty_present=true, projfs_feature_enabled=false, restart_required=false
Component
Personal CLI (finsafe run, self-confine, probe, doctor)
Steps to reproduce
Environment used for the repro:
- Agent: Hermes Agent
v0.20.6 (2026.8.27, upstream 64b96bb5), installed at C:\Users\xuls2\AppData\Local\hermes\hermes-agent (git-based install). Hermes's terminal/filesystem tools spawn the bundled Git-for-Windows bash: C:\Users\xuls2\AppData\Local\hermes\git\bin\bash.exe (MSYS2/Cygwin runtime msys-2.0.dll).
- Working dir:
D:\Hermes\finsafe (contains ./workspace).
Wrapper policy hermes-windows-interactive.yaml:
schema_version: 1
kind: local-wrapper
program_mode: interactive
degrade:
allow_fallback: false
audit:
require_policy_digest: true
require_resolved_posture: true
network: host
resources:
memory_max: "2G"
pids_max: "256"
cpu_max: "100000 100000"
windows:
backend: restricted_token
filesystem:
skip_default_deny_read: true
read_only_paths: []
read_write_paths:
- "./workspace"
- "${USERPROFILE}/AppData/Local/FinSAFE/hermes"
- "${USERPROFILE}/AppData/Local/FinSAFE/hermes/tmp"
Notes on the environment setup (so you can reproduce without guessing):
- The real Hermes home (
%LOCALAPPDATA%\hermes) is refused by FinSAFE as an inheritable write root ("Electron userData product folder"), so Hermes is pointed at a dedicated sandbox home via HERMES_HOME=C:\Users\xuls2\AppData\Local\FinSAFE\hermes (with config.yaml, .env, auth.json copied in).
- The sandbox has no writable system temp dir, so
TEMP/TMP/TMPDIR are redirected to C:\Users\xuls2\AppData\Local\FinSAFE\hermes\tmp.
Minimal isolated reproduction (no LLM involved, two side-by-side runs):
# [1] CONTROL — bash as the DIRECT broker (works, prints BASH-OK):
finsafe --policy hermes-windows-interactive.yaml self-confine -- "C:\Users\xuls2\AppData\Local\hermes\git\bin\bash.exe" -c "echo BASH-OK"
# [2] BUG — cmd spawns bash as a CHILD process (bash dies in Cygwin init):
finsafe --policy hermes-windows-interactive.yaml self-confine -- cmd /c "C:\Users\xuls2\AppData\Local\hermes\git\bin\bash.exe -c \"echo CHILD-BASH-OK\""
Full reproduction through Hermes tools (Hermes keeps retrying, so expect it to run for a while):
$env:HERMES_HOME='C:\Users\xuls2\AppData\Local\FinSAFE\hermes'
$env:TEMP='C:\Users\xuls2\AppData\Local\FinSAFE\hermes\tmp'
$env:TMP='C:\Users\xuls2\AppData\Local\FinSAFE\hermes\tmp'
$env:TMPDIR='C:\Users\xuls2\AppData\Local\FinSAFE\hermes\tmp'
finsafe --policy hermes-windows-interactive.yaml self-confine -- hermes chat -q "Use write_file to create ./workspace/tool-test.txt containing HELLO, then reply exactly: TOOL-DONE"
Expected behavior
A bash process spawned as a child of the sandboxed broker (Python/cmd/hermes) should initialize the Cygwin runtime and run normally, just like the direct-broker bash in repro [1]. Hermes's terminal, write_file, read_file, and search_files tools (all bash-backed) should complete their work inside the sandbox.
Actual behavior
Any bash spawned by the broker (not exec'd directly) dies during Cygwin runtime initialization with a Win32 error 5 (access denied):
-
Via cmd (repro [2]):
0 [main] bash (PID) C:\Users\xuls2\AppData\Local\hermes\git\bin\..\usr\bin\bash.exe: *** fatal error - CreateFileMapping S-1-5-21-497800499-2125783479-1825478815-1001.1, Win32 error 5. Terminating.
-
Via Hermes tools (same class of failure, different Cygwin object):
0 [main] bash (PID) C:\Users\xuls2\AppData\Local\hermes\git\bin\..\usr\bin\bash.exe: *** fatal error - couldn't create signal pipe, Win32 error 5
with child exit code 3221225794 (0xC0000142, STATUS_DLL_INIT_FAILED).
Consequences inside Hermes (from C:\Users\xuls2\AppData\Local\FinSAFE\hermes\logs\errors.log, session 20260831_213024_a20862):
agent.tool_executor: Tool terminal returned error: {"output": "0 [main] bash (...) ... *** fatal error - couldn't create signal pipe, Win32 error 5", "exit_code": 3221225794, ...}
agent.tool_executor: Tool write_file returned error: {"bytes_written": 0, ..., "error": "Failed to write file: 0 [main] bash (...) ... fatal error - ..."}
agent.tool_executor: Tool read_file returned error: {"content": "", ..., "error": "File not found: D:\\Hermes\\finsafe\\workspace\\tool-test.txt"}
agent.tool_executor: Tool search_files returned error: {"total_count": 0, "error": "File search requires 'rg' (ripgrep) or 'find'. ..."}
tools.environments.base: init_session failed (session=...): snapshot bootstrap failed with exit code 256 - falling back to bash -l per command
execute_code fails independently with the same restricted-token write class:
tools.code_kernel: session kernel failed: PermissionError: [Errno 13] Permission denied:
'C:\Users\xuls2\AppData\Local\FinSAFE\hermes\tmp\hermes_kernel_2oq_ydc0\hermes_tools.py'
Plain LLM queries (no tools) work fine under the sandbox: agent starts, outbound HTTPS to the LLM provider works, session state persists to the dedicated home.
Additional context
Process Monitor capture (attached: 00.CSV excerpt below)
Captured during the repro window (21:30-21:31, 12,806 events, 12,368 from bash.exe). Exactly 19 events with Result = ACCESS DENIED, all from bash.exe, all CreateFile:
Path : \Device\NamedPipe\msys-dc23a859c8284af1-<PID>-sigwait (per-process Cygwin signal pipe)
Result : ACCESS DENIED
Detail : Desired Access: Generic Write, Read Attributes
Disposition: Open Options: Synchronous IO Non-Alert, Non-Directory File
Failing sequence for one bash (PID 14252) — the process creates its own pipe, then its own open-for-write is denied:
CreateFile ...\git\usr\bin\msys-2.0.dll SUCCESS (Cygwin DLL load)
CreatePipe \Device\NamedPipe\msys-dc23a859c8284af1-14252-sigwait SUCCESS
CreateFile \Device\NamedPipe\msys-dc23a859c8284af1-14252-sigwait ACCESS DENIED <- bash dies here
CloseFile (pipe) SUCCESS
Control (direct-broker bash, PID 5688, repro [1]) — same pipe, same access, all SUCCESS:
CreatePipe -> CreateFile -> ReadFile -> CloseFile.
The 286 CreateFileMapping events in this capture are all image/DLL section mappings (e.g. imm32.dll, bash.exe, .sdb) with SUCCESS / FILE LOCKED WITH ONLY READERS — normal. The SID-named shared-memory mapping denial (Win32 error 5) appeared in the cmd -> bash repro instead, i.e. both Cygwin objects fail depending on which one Cygwin init hits first.
The denied bash PIDs in the capture (15012, 10064, 14732, 10916, 12296, ...) exactly match the PIDs in Hermes's errors.log for the same window.
Root-cause analysis (evidence-backed)
Cygwin creates per-process IPC objects (the msys-<hash>-<pid>-sigwait named pipe, and the SID-named shared-memory mapping S-1-5-21-...-1001.1) whose DACLs grant the normal user SID. Under FinSAFE's RestrictedToken backend the user SID is not usable for writes — writes require the FinSAFE WRITE_RESTRICTED capability SID, which is absent from these Cygwin-created DACLs — so the process's own open-for-write is denied with Win32 error 5 and Cygwin aborts.
This is the same class as issue #29. The current CHANGELOG entry ("RestrictedToken + Git for Windows bash", PR #275) grants capability SIDs on existing MSYS2 SID-named file mappings before CreateRestrictedToken, which covers the directly exec'd broker only — it does not cover the per-process msys-*-sigwait named pipes (or the mappings when they must be created by the sandboxed child), which is why bash children spawned later by the broker still crash.
Suggested fix direction: extend the pre-restriction capability-SID grant to the per-process msys-*-sigwait named pipes created by sandboxed Cygwin processes (and re-check the shared-memory mapping case when the child, not the host, is the creator), or otherwise arrange for Cygwin-created IPC object DACLs to include the WRITE_RESTRICTED SID.
Environment detail
-
Hermes: v0.20.6 — Install directory: C:\Users\xuls2\AppData\Local\hermes\hermes-agent, Install method: git, Python: 3.11.15, OpenAI SDK: 2.24.0
-
Bundled git-bash: C:\Users\xuls2\AppData\Local\hermes\git\bin\bash.exe -> ...\usr\bin\bash.exe, msys-2.0.dll
-
finsafe probe --json full output:
{"schema_version":3,"host_os":"windows","host_arch":"x86_64","windows":{"appcontainer_works":true,"helper_installed":false,"conpty_present":true,"projfs_feature_enabled":false,"projection_smoke_works":false,"restart_required":false,"learning_mode_trace_api_available":false,"learning_mode_security_environment_api_available":false}}
Related
finsafe version
0.9.40(output offinsafe version)Platform
finsafe probe --jsonhighlights:appcontainer_works=true,helper_installed=false,conpty_present=true,projfs_feature_enabled=false,restart_required=falseComponent
Personal CLI (
finsafe run,self-confine,probe,doctor)Steps to reproduce
Environment used for the repro:
v0.20.6(2026.8.27, upstream 64b96bb5), installed atC:\Users\xuls2\AppData\Local\hermes\hermes-agent(git-based install). Hermes's terminal/filesystem tools spawn the bundled Git-for-Windows bash:C:\Users\xuls2\AppData\Local\hermes\git\bin\bash.exe(MSYS2/Cygwin runtimemsys-2.0.dll).D:\Hermes\finsafe(contains./workspace).Wrapper policy
hermes-windows-interactive.yaml:Notes on the environment setup (so you can reproduce without guessing):
%LOCALAPPDATA%\hermes) is refused by FinSAFE as an inheritable write root ("Electron userData product folder"), so Hermes is pointed at a dedicated sandbox home viaHERMES_HOME=C:\Users\xuls2\AppData\Local\FinSAFE\hermes(withconfig.yaml,.env,auth.jsoncopied in).TEMP/TMP/TMPDIRare redirected toC:\Users\xuls2\AppData\Local\FinSAFE\hermes\tmp.Minimal isolated reproduction (no LLM involved, two side-by-side runs):
Full reproduction through Hermes tools (Hermes keeps retrying, so expect it to run for a while):
Expected behavior
A bash process spawned as a child of the sandboxed broker (Python/cmd/hermes) should initialize the Cygwin runtime and run normally, just like the direct-broker bash in repro [1]. Hermes's
terminal,write_file,read_file, andsearch_filestools (all bash-backed) should complete their work inside the sandbox.Actual behavior
Any bash spawned by the broker (not exec'd directly) dies during Cygwin runtime initialization with a Win32 error 5 (access denied):
Via
cmd(repro [2]):Via Hermes tools (same class of failure, different Cygwin object):
with child exit code
3221225794(0xC0000142, STATUS_DLL_INIT_FAILED).Consequences inside Hermes (from
C:\Users\xuls2\AppData\Local\FinSAFE\hermes\logs\errors.log, session20260831_213024_a20862):execute_codefails independently with the same restricted-token write class:Plain LLM queries (no tools) work fine under the sandbox: agent starts, outbound HTTPS to the LLM provider works, session state persists to the dedicated home.
Additional context
Process Monitor capture (attached:
00.CSVexcerpt below)Captured during the repro window (21:30-21:31, 12,806 events, 12,368 from bash.exe). Exactly 19 events with Result = ACCESS DENIED, all from
bash.exe, allCreateFile:Failing sequence for one bash (PID 14252) — the process creates its own pipe, then its own open-for-write is denied:
Control (direct-broker bash, PID 5688, repro [1]) — same pipe, same access, all SUCCESS:
CreatePipe->CreateFile->ReadFile->CloseFile.The 286
CreateFileMappingevents in this capture are all image/DLL section mappings (e.g.imm32.dll,bash.exe,.sdb) withSUCCESS/FILE LOCKED WITH ONLY READERS— normal. The SID-named shared-memory mapping denial (Win32 error 5) appeared in thecmd -> bashrepro instead, i.e. both Cygwin objects fail depending on which one Cygwin init hits first.The denied bash PIDs in the capture (15012, 10064, 14732, 10916, 12296, ...) exactly match the PIDs in Hermes's
errors.logfor the same window.Root-cause analysis (evidence-backed)
Cygwin creates per-process IPC objects (the
msys-<hash>-<pid>-sigwaitnamed pipe, and the SID-named shared-memory mappingS-1-5-21-...-1001.1) whose DACLs grant the normal user SID. Under FinSAFE's RestrictedToken backend the user SID is not usable for writes — writes require the FinSAFEWRITE_RESTRICTEDcapability SID, which is absent from these Cygwin-created DACLs — so the process's own open-for-write is denied with Win32 error 5 and Cygwin aborts.This is the same class as issue #29. The current CHANGELOG entry ("RestrictedToken + Git for Windows bash", PR #275) grants capability SIDs on existing MSYS2 SID-named file mappings before
CreateRestrictedToken, which covers the directly exec'd broker only — it does not cover the per-processmsys-*-sigwaitnamed pipes (or the mappings when they must be created by the sandboxed child), which is why bash children spawned later by the broker still crash.Suggested fix direction: extend the pre-restriction capability-SID grant to the per-process
msys-*-sigwaitnamed pipes created by sandboxed Cygwin processes (and re-check the shared-memory mapping case when the child, not the host, is the creator), or otherwise arrange for Cygwin-created IPC object DACLs to include theWRITE_RESTRICTEDSID.Environment detail
Hermes:
v0.20.6—Install directory: C:\Users\xuls2\AppData\Local\hermes\hermes-agent,Install method: git,Python: 3.11.15,OpenAI SDK: 2.24.0Bundled git-bash:
C:\Users\xuls2\AppData\Local\hermes\git\bin\bash.exe->...\usr\bin\bash.exe,msys-2.0.dllfinsafe probe --jsonfull output:{"schema_version":3,"host_os":"windows","host_arch":"x86_64","windows":{"appcontainer_works":true,"helper_installed":false,"conpty_present":true,"projfs_feature_enabled":false,"projection_smoke_works":false,"restart_required":false,"learning_mode_trace_api_available":false,"learning_mode_security_environment_api_available":false}}Related