Commit Graph
4 Commits
Author SHA1 Message Date
Emad DoughanandGitHub e7b3ba07bb fix(config-protection): match protected filenames case-insensitively (#2543)
On a case-insensitive filesystem (macOS APFS/HFS+, Windows NTFS) a write to
`.ESLINTRC.JS` lands on the exact same inode as `.eslintrc.js`, but the guard
looked the basename up in PROTECTED_FILES with a case-sensitive `Set.has`.
Every entry in that Set is lowercase, so any case-variant path missed the
branch entirely and returned exit 0 — a single Write silently overwrote a
live config while the hook reported success.

Reproduced on macOS APFS: `.eslintrc.js` and `.ESLINTRC.JS` share one inode,
yet the hook returned exit 2 for the former and exit 0 for the latter, and the
uppercase write replaced the real config's contents.

This is a one-step bypass of the whole guard and needs no shell access, unlike
the known delete-then-recreate route.

Fix: also test `basename.toLowerCase()`. All 32 PROTECTED_FILES entries are
already lowercase, so the fallback is exact. On a genuinely case-sensitive
filesystem this costs at most a false positive on a distinct file whose name
differs from a protected one by case alone.

Behaviour deliberately unchanged: first-time creation is still allowed (the
bootstrap affordance), non-config paths still pass through, and the existing
lstat/ENOENT fail-closed semantics are untouched.

Test: adds a case-variant case that asserts exit 2. It guards itself with an
inode comparison and skips on case-sensitive filesystems rather than asserting
something untrue there. Verified in both directions — it FAILS against the
unpatched hook (`Got 0; 0 !== 2`) and passes with the fix. Suite: 9/9.
2026-07-22 12:17:04 -04:00
375d750b4c fix: integrate recent hook and docs PRs (#1905)
Integrates useful changes from #1882, #1884, #1889, #1893, #1898, #1899, and #1903:
- fix rule install docs to preserve language directories
- correct Ruby security command examples
- harden dev-server hook command-substitution parsing
- add Prisma patterns skill and catalog/package surfaces
- allow first-time protected config creation while blocking existing configs
- read cost metrics from Stop hook transcripts
- emit suggest-compact additionalContext on stdout

Co-authored-by: Jamkris <dltmdgus1412@gmail.com>
Co-authored-by: Levi-Evan <levishantz@gmail.com>
Co-authored-by: gaurav0107 <gauravdubey0107@gmail.com>
Co-authored-by: richm-spp <richard.millar@salarypackagingplus.com.au>
Co-authored-by: zomia <zomians@outlook.jp>
Co-authored-by: donghyeun02 <donghyeun02@gmail.com>
2026-05-14 21:37:28 -04:00
Affaan Mustafa 1d0aa5ac2a fix: fold session manager blockers into one candidate 2026-03-24 23:08:27 -04:00
Charlie TonneslanandGitHub fdb10ba116 feat(hooks): add config protection hook to block linter config manipulation (#758)
* feat(hooks): add config protection hook to block linter config manipulation

Agents frequently modify linter/formatter configs (.eslintrc, biome.json,
.prettierrc, .ruff.toml, etc.) to make checks pass instead of fixing
the actual code.

This PreToolUse hook intercepts Write/Edit/MultiEdit calls targeting
known config files and blocks them with a steering message that directs
the agent to fix the source code instead.

Covers: ESLint, Prettier, Biome, Ruff, ShellCheck, Stylelint, and
Markdownlint configs.

Fixes #733

* Address review: fix dead code, add missing configs, export run()

- Removed pyproject.toml from PROTECTED_FILES (was dead code since
  it was also in PARTIAL_CONFIG_FILES). Added comment explaining why
  it's intentionally excluded.
- Removed PARTIAL_CONFIG_FILES entirely (no longer needed).
- Added missing ESLint v9 TypeScript flat configs: eslint.config.ts,
  eslint.config.mts, eslint.config.cts
- Added missing Prettier ESM config: prettier.config.mjs
- Exported run() function for in-process execution via run-with-flags,
  avoiding the spawnSync overhead (~50-100ms per call).

* Handle stdin truncation gracefully, log warning instead of fail-open

If stdin exceeds 1MB, the JSON would be malformed and the catch
block would silently pass through. Now we detect truncation and
log a warning. The in-process run() path is not affected.
2026-03-22 15:39:54 -07:00