singularity-forge/.plans/directory-safeguards.md
Jeremy McSpadden 45bff3456c feat(gsd): add directory safeguards for system/home paths (#1053)
* feat(gsd): add directory safeguards to prevent running in system/home paths

GSD previously had no protection against being launched from dangerous
directories like $HOME, /, /usr, or /etc. This adds layered validation:

- Blocked system paths (hard stop): /, /usr, /etc, /var, $HOME, tmpdir, etc.
- High entry count heuristic (>200 entries triggers confirmation dialog)
- Symlink resolution via realpathSync to prevent bypass
- Integrated at three chokepoints: projectRoot(), showSmartEntry(), bootstrapGsdDirectory()

Includes 19 tests covering all blocked categories, boundary conditions, and
the assertSafeDirectory throw/return behavior.

* fix: make directory safeguard tests cross-platform (Windows CI)

- Skip Unix-specific blocked path tests on Windows (/, /usr, /etc, etc.)
- Add Windows-specific blocked path tests (C:\, C:\Windows)
- Use platform-appropriate path separator in trailing slash test
- Fix root path normalization for Windows drive letters (C:\ not C:)
2026-03-17 21:57:53 -06:00

1.7 KiB

Directory Safeguards Plan

Problem

GSD had zero protection against being launched from dangerous directories like $HOME, /, /usr, /etc, etc. Running gsd init from these locations would create .gsd/ and write planning files into system directories.

Solution

Added a validate-directory.ts module with layered safeguards:

Layer 1: Blocked system paths (hard stop)

  • Filesystem roots: /, /usr, /bin, /sbin, /etc, /var, /dev, /proc, /sys, /boot, /lib, /lib64
  • macOS: /System, /Library, /Applications, /Volumes, /private
  • Windows: C:\, C:\Windows, C:\Program Files
  • User's $HOME directory itself (subdirs are fine)
  • System temp directory root (os.tmpdir())

Layer 2: High entry count heuristic (warning)

  • Directories with >200 top-level entries trigger a confirmation dialog
  • User can override if they really want to proceed
  • All paths are resolved through realpathSync() before checking
  • Prevents bypassing via symlinks (e.g., ln -s / ~/myproject)

Integration Points

  1. projectRoot() in commands.ts — gateway for all /gsd subcommands (throws on blocked)
  2. showSmartEntry() in guided-flow.ts — smart entry wizard (shows error/confirmation UI)
  3. bootstrapGsdDirectory() in init-wizard.ts — final safety check before writing files (throws on blocked)

Test Coverage

19 tests covering:

  • All blocked path categories (/, /usr, /etc, /var, /usr/local/bin)
  • Home directory (with and without trailing slash)
  • Temp directory root
  • Normal project directories (pass)
  • Empty directories (pass)
  • 200-entry boundary (pass) vs 210-entry (warning)
  • assertSafeDirectory throw behavior
  • Trailing slash normalization