{"record":{"id":"f697446642356759","repo":"zeroclaw-labs/zeroclaw","slug":"local-ipc-endpoint-lock-directory-is-writable-b","errorCode":null,"errorMessage":"local IPC endpoint lock directory {} is writable by other users without the sticky bit; its entries could be replaced. Restrict it (chmod go-w or +t) or point ZEROCLAW_SOCKET at a private directory","messagePattern":"local IPC endpoint lock directory (.+?) is writable by other users without the sticky bit; its entries could be replaced\\. Restrict it \\(chmod go-w or \\+t\\) or point ZEROCLAW_SOCKET at a private directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/rpc/local.rs","lineNumber":305,"sourceCode":"        };\n        let metadata = std::fs::metadata(parent).with_context(|| {\n            format!(\n                \"inspecting local IPC endpoint lock directory {}\",\n                parent.display()\n            )\n        })?;\n        let euid = unsafe { libc::geteuid() };\n        let mode = metadata.mode();\n        if metadata.uid() != euid && metadata.uid() != 0 {\n            anyhow::bail!(\n                \"local IPC endpoint lock directory {} is owned by uid {}; \\\n                 it must belong to the daemon user or root\",\n                parent.display(),\n                metadata.uid()\n            );\n        }\n        if mode & 0o022 != 0 && mode & 0o1000 == 0 {\n            anyhow::bail!(\n                \"local IPC endpoint lock directory {} is writable by other \\\n                 users without the sticky bit; its entries could be replaced. \\\n                 Restrict it (chmod go-w or +t) or point ZEROCLAW_SOCKET at a \\\n                 private directory\",\n                parent.display()\n            );\n        }\n        Ok(())\n    }\n\n    /// Rejects pre-existing lock entries that do not provide the guarantees a\n    /// freshly created lock would have.\n    ///\n    /// The inode must be a regular file owned by the current user with no\n    /// group/other access, and still linked at the time of inspection. A\n    /// foreign-owned or permissive entry could be locked by another local\n    /// user to block startup, or unlinked and recreated by its owner to hand\n    /// two daemons different lock inodes.","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/rpc/local.rs#L287-L323","documentation":"The lock directory is group- or other-writable (mode bits 0o022 set) and lacks the sticky bit (0o1000). Any local user could then replace the lock entry and sabotage or spoof endpoint lifecycle ownership, so ZeroClaw refuses to acquire the lock. The sticky bit keeps /tmp-style shared directories usable because it restricts unlink/rename to the entry owner.","triggerScenarios":"ZEROCLAW_SOCKET placed directly in /tmp or another world-writable directory without +t; a group-writable directory (mode 0775/0777) shared by several service accounts; a container image that creates the socket dir with permissive defaults.","commonSituations":"Quick local setups pointing the socket at /tmp; directories created by Dockerfiles with chmod 777; legacy shared /var/run dirs with group write for multi-user tooling.","solutions":["Restrict the directory: chmod go-w /path/to/dir (removes group/other write).","Or set the sticky bit: chmod +t /path/to/dir.","Or move the socket to a private directory owned by the daemon user and update ZEROCLAW_SOCKET.","Audit whoever created the directory with permissive modes so restarts don't regress."],"exampleFix":"# before\nexport ZEROCLAW_SOCKET=/tmp/zeroclaw.sock   # /tmp mode 1777 is fine, but a custom /tmp/zeroclaw dir with 0777 is not\nchmod 0777 /tmp/zeroclaw\n\n# after\nchmod go-w /tmp/zeroclaw        # or: chmod +t /tmp/zeroclaw\n# better: private dir\nmkdir -p \"$XDG_RUNTIME_DIR/zeroclaw\"\nexport ZEROCLAW_SOCKET=\"$XDG_RUNTIME_DIR/zeroclaw/zeroclaw.sock\"","handlingStrategy":"validation","validationCode":"fn dir_not_foreign_writable(sock: &std::path::Path) -> bool {\n    let dir = sock.parent().unwrap_or(std::path::Path::new(\".\"));\n    std::fs::metadata(dir)\n        .map(|m| m.mode() & 0o022 == 0 || m.mode() & 0o1000 != 0)\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"if !dir_not_foreign_writable(&sock_path) {\n    anyhow::bail!(\"socket dir is foreign-writable; chmod go-w or +t first\");\n}","preventionTips":["Prefer sticky-bit shared dirs (like /tmp itself, mode 1777) or private dirs; avoid 0777 custom dirs.","Audit Dockerfiles and provisioning scripts for chmod 777 on runtime dirs.","Check permissions in a pre-start hook so the failure is actionable before the daemon launches."],"tags":["unix","permissions","sticky-bit","ipc","security","filesystem"],"backgroundTag":"insecure-file-permissions","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}