← Back to PRs

#11408: Security: resolve symlink target permissions in safeStat; skip doctor config warning for symlinks (#11307)

by lailoo open 2026-02-07 19:43 View on GitHub →
commands stale
## Summary Fixes #11307 Also addresses the `openclaw status` false positive reported in the [issue comment](https://github.com/openclaw/openclaw/issues/11307#issuecomment-2841078291) by @fredcy. ## Problem `openclaw doctor` and `openclaw status` warn that config file is group/world readable when using nix-openclaw (home-manager). This is a false positive because: 1. The config is a nix-managed **symlink**, and symlinks always show permissions as `0o777` regardless of target permissions 2. The target file lives in `/nix/store` which is **read-only** — `chmod` would fail 3. The containing `~/.openclaw` directory is mode `700`, protecting all files within ## Root Cause - `safeStat()` in `audit-fs.ts` uses `lstat()` which returns the symlink's own mode (`0o777`), not the target's real permissions\n- `doctor-state-integrity.ts` checks `stat.mode & 0o077` against the symlink mode, triggering a false warning\n\n## Fix (two layers)\n\n### 1. `safeStat()` — resolve symlink target permissions\n\nWhen the path is a symlink, follow it with `fs.stat()` to get the **target's real permissions**. This fixes all callers (doctor, status, security audit) at once. Broken symlinks are handled gracefully with `isSymlink: true` preserved in the error path.\n\n### 2. `doctor-state-integrity.ts` — skip warning for read-only symlink targets\n\nFor symlinks whose target is **not writable** (e.g. `/nix/store`), skip the permission warning entirely — `chmod` would fail and the directory-level protection is sufficient.\n\nFor symlinks to **writable** world-readable targets, the warning is **still shown** — this is a real security concern.\n\n## Model resolution priority\n\n| Scenario | Warning? | Action |\n|---|---|---|\n| Regular file, mode 644 | ✅ Yes | Offer chmod 600 |\n| Symlink → read-only target (nix store) | ❌ No | Skip (chmod would fail, dir protects) |\n| Symlink → writable 644 target | ✅ Yes | Offer chmod 600 |\n| Broken symlink | ❌ No | Error logged |\n\n## Changes\n- `src/security/audit-fs.ts`: `safeStat()` follows symlinks to get target permissions (+28 lines)\n- `src/commands/doctor-state-integrity.ts`: Skip warning for read-only symlink targets (+14 lines)\n- `src/security/audit-fs.symlink.test.ts`: 5 tests covering symlink resolution, broken symlinks, and `inspectPathPermissions`\n- `CHANGELOG.md`: Add entry\n\n## Testing\n\n- 5 symlink-specific tests pass (`audit-fs.symlink.test.ts`)\n- 43 doctor tests pass (`pnpm vitest run src/commands/doctor`)\n- `pnpm lint` passes with 0 errors\n\n## vs PR #11458\n\nBoth PRs fix the same issue. Key differences:\n- This PR fixes **both** `safeStat` (affects all callers including `openclaw status`) **and** `doctor`\n- This PR distinguishes read-only vs writable symlink targets (same approach as #11458)\n- #11458 only fixes `doctor`, not `safeStat`/`status`

Most Similar PRs