| id | 84 |
|---|---|
| title | Symlink default-deny for file discovery |
| status | 🔲 |
| summary | Skip symlinks by default during directory walks; add --follow-symlinks opt-in flag. |
Prevent symlink-based attacks. A malicious symlink in
a repo can cause mdsmith check to read or
mdsmith fix to overwrite files outside the project.
filepath.Walk in walkDir follows file symlinks by
default. The existing --no-follow-symlinks CLI flag
is opt-in and skips all symlinks by setting the
pattern-based no-follow-symlinks config key to **.
A symlink like
ln -s /etc/cron.d/jobs evil.md in a repo causes
mdsmith fix . to overwrite the target.
Among compared linters, only Prettier v3 rejects symlinks (PR #14627). textlint is incidentally safe (glob default). markdownlint, Vale, and remark-lint all follow symlinks.
Replace NoFollowSymlinks []string (pattern-based
opt-out) with FollowSymlinks bool (global opt-in).
In walkDir, skip all entries where
info.Mode()&os.ModeSymlink != 0 unless
FollowSymlinks is true.
Replace --no-follow-symlinks with
--follow-symlinks:
mdsmith check --follow-symlinks .
mdsmith fix --follow-symlinks .Replace no-follow-symlinks with follow-symlinks:
follow-symlinks: true # opt-in, default false- Deprecate
no-follow-symlinksconfig key: if present, emit a warning suggesting migration to the newfollow-symlinks: falsedefault. - Deprecate
--no-follow-symlinksCLI flag: accept silently (it's now the default).
The write-side TOCTOU is handled by plan 83 section C
(atomic writes). os.Rename(tmp, path) replaces the
symlink
itself, not the target — no separate Lstat check
needed.
Consider migrating from filepath.Walk to
filepath.WalkDir (Go 1.16+). WalkDir provides
d.Type() with fs.ModeSymlink without extra
os.Lstat calls. This makes symlink detection cheaper.
- Replace
NoFollowSymlinks []stringwithFollowSymlinks boolinconfig.Config - Replace
--no-follow-symlinkswith--follow-symlinksin CLI flag sets - Update
ResolveOptsto useFollowSymlinks bool - Update
walkDirto skip symlinks by default - Update
resolveGlobto skip symlinks by default - Add deprecation warning for old config key
- Update tests in
files_test.go - Add integration test: symlink to file outside
project is skipped by default, followed with
--follow-symlinks
- Symlinks are skipped by default in directory walks
-
--follow-symlinksflag enables symlink following -
follow-symlinks: truein config enables symlink following - Old
no-follow-symlinksconfig emits deprecation warning - Both
checkandfixrespect the setting - All tests pass:
go test ./... -
go tool golangci-lint runreports no issues