You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
cso: Docker socket admission fails on macOS 27 (Bun realpathSync EOPNOTSUPP on sockets) #3248
On macOS 27, gstack-cso doctor / provision-images report ISOLATION_FAILED: Pinned local Docker socket is unavailable even when docker info works (seen with Colima; Docker Desktop is affected the same way). All scanners then show unavailable.
Root cause
lib/cso/docker.ts canonicalizes the socket with fs.realpathSync(requestedSocket) (and the registry socket at the registrySocket mount). Under Bun 1.4.2/1.4.3 on macOS 27, realpathSync throws EOPNOTSUPP for any Unix socket, while lstat/stat succeed. Node is not affected. Upstream Bun report: oven-sh/bun#45752
Repro
import{dockerEndpoint}from'./lib/cso/docker';awaitdockerEndpoint(tmpHome,{HOME: tmpHome,DOCKER_HOST: 'unix:///path/to/colima/docker.sock'});// -> CsoError ISOLATION_FAILED 'Pinned local Docker socket is unavailable'
Local fix that works
Resolve the leaf symlink chain by hand and canonicalize only the parent directory, keeping every existing check (isSocket, dev/inode pinning, symlink refusal for the registry socket):
functionrealSocketPath(path: string): string{letcurrent=path;for(lethops=0;hops<40;hops++){constparent=fs.realpathSync(dirname(current));if(!fs.lstatSync(current).isSymbolicLink())returnjoin(parent,basename(current));current=resolve(parent,fs.readlinkSync(current));}thrownewCsoError('ISOLATION_FAILED','Docker socket symlink chain is too deep');}
Used at both realpathSync call sites on sockets. With it, doctor reports local-docker-isolation: ready, and all 6 qualified scanner images provision. A regression test (socket reached through a symlinked dir + symlinked leaf, and a dangling symlink still refused) fails without the change and passes with it. The rest of the non-integration cso-* suite has the same failure set with and without the change.
Related, found while getting the full audit to run once the socket fix was in: Semgrep never completes on a large repo, and the real cause is hidden behind a cleanup error.
Symptom:scanner:semgrep ends not_assessed with ISOLATION_FAILED: Exact reproduction cleanup failed; verification evidence was withheld and the watchdog remains responsible. No containers are left behind.
What actually happens: I added a temporary log of the error swallowed in DockerGroup.cleanup(). It is DEADLINE: Docker operation exceeded the reproduction deadline, thrown from cleanup()'s own docker ps/rm calls.
scanners.ts runs Semgrep with --jobs=1, and scanner-executor.ts caps each scanner at min(300, remaining) seconds.
On a repo of about 6,000 files (TS/TSX, 55 MiB), single-job Semgrep needs more than 300 s, so the scan hits the deadline. Each attempt failed at about 5 min 02 s, with both 4 and 8 VM CPUs.
DockerGroup.docker() computes its timeout from the same this.deadline, so every cleanup command gets about 1 ms and times out.
cleanup() catches that and rethrows the generic cleanup message, so the scan timeout is never reported.
Reference point: the same pinned image and ruleset (/policy/catalog/semgrep/rules.yml), with the same flags except --jobs=8, run offline (--network none --read-only --cap-drop ALL), finishes in 3 min 25 s over 6,046 files.
Suggestions:
Give cleanup its own short budget, separate from the scan deadline, so teardown can still run after a scan timeout.
Report the scan DEADLINE as the gap, not the derived cleanup failure.
Consider --jobs derived from the admitted CPU limit, or a larger per-scanner cap for Semgrep.
Same environment as above: gstack 1.91.67.0, macOS 27.0.1 arm64, Colima 0.10.3, Docker 29.
The socket fix itself (realpathSocket() in lib/cso/docker.ts) shipped in 1.91.96.0 (#3164), after the 1.91.67.0 you were running. It resolves the leaf by hand on EOPNOTSUPP and keeps every socket check. Running gstack-upgrade should clear the ISOLATION_FAILED: Pinned local Docker socket is unavailable from doctor.
#3303 adds an endpoint-level regression test: dockerEndpoint() is called with realpathSync refusing every socket, through a symlinked directory and a symlinked leaf like Colima's, and the endpoint is pinned to the socket's identity. Negative controls show that non-sockets, dangling or looping links, .. paths and TCP are still refused.
Your Semgrep finding is a separate bug: on a large repo the 300 s per-scanner cap is reported as a derived cleanup failure, and cleanup shares the scan's deadline. It isn't in #3303. Thanks for tracing it to DockerGroup.cleanup(). I've passed it on to be tracked separately.
Fixed in #3303, released as v1.91.152.0 (404ddf0). Thanks for the detailed report and root-cause analysis. If anything still fails after you upgrade, reopen this with the gstack-cso output. — GStack Minder
Activity
ivanlion commentedon Oct 17, 2026
Related, found while getting the full audit to run once the socket fix was in: Semgrep never completes on a large repo, and the real cause is hidden behind a cleanup error.
Symptom:
scanner:semgrependsnot_assessedwithISOLATION_FAILED: Exact reproduction cleanup failed; verification evidence was withheld and the watchdog remains responsible. No containers are left behind.What actually happens: I added a temporary log of the error swallowed in
DockerGroup.cleanup(). It isDEADLINE: Docker operation exceeded the reproduction deadline, thrown fromcleanup()'s owndocker ps/rmcalls.scanners.tsruns Semgrep with--jobs=1, andscanner-executor.tscaps each scanner atmin(300, remaining)seconds.DockerGroup.docker()computes its timeout from the samethis.deadline, so every cleanup command gets about 1 ms and times out.cleanup()catches that and rethrows the generic cleanup message, so the scan timeout is never reported.Reference point: the same pinned image and ruleset (
/policy/catalog/semgrep/rules.yml), with the same flags except--jobs=8, run offline (--network none --read-only --cap-drop ALL), finishes in 3 min 25 s over 6,046 files.Suggestions:
DEADLINEas the gap, not the derived cleanup failure.--jobsderived from the admitted CPU limit, or a larger per-scanner cap for Semgrep.Same environment as above: gstack 1.91.67.0, macOS 27.0.1 arm64, Colima 0.10.3, Docker 29.
capy-ai commentedon Oct 19, 2026
The socket fix itself (
realpathSocket()inlib/cso/docker.ts) shipped in 1.91.96.0 (#3164), after the 1.91.67.0 you were running. It resolves the leaf by hand onEOPNOTSUPPand keeps every socket check. Runninggstack-upgradeshould clear theISOLATION_FAILED: Pinned local Docker socket is unavailablefromdoctor.#3303 adds an endpoint-level regression test:
dockerEndpoint()is called withrealpathSyncrefusing every socket, through a symlinked directory and a symlinked leaf like Colima's, and the endpoint is pinned to the socket's identity. Negative controls show that non-sockets, dangling or looping links,..paths and TCP are still refused.Your Semgrep finding is a separate bug: on a large repo the 300 s per-scanner cap is reported as a derived cleanup failure, and cleanup shares the scan's deadline. It isn't in #3303. Thanks for tracing it to
DockerGroup.cleanup(). I've passed it on to be tracked separately.v1.91.152.0 fix(cso): start and run on repos with nested Git repos, l…
capy-ai commentedon Oct 19, 2026
Fixed in #3303, released as v1.91.152.0 (404ddf0). Thanks for the detailed report and root-cause analysis. If anything still fails after you upgrade, reopen this with the gstack-cso output. — GStack Minder