Skip to content

fix(core): protect every file Memnox installs a hook into - #132

Open
Cedric921 wants to merge 1 commit into
Memnox:mainfrom
Cedric921:fix/protect-every-hook-file
Open

Cedric921 wants to merge 1 commit into
Memnox:mainfrom
Cedric921:fix/protect-every-hook-file

Conversation

@Cedric921

Copy link
Copy Markdown
Contributor

Closes #27.

What this changes

Memnox installs its policy hook into five files. It protected one. Measured on main:

.codex/hooks.json                protected=no   named=no
.cursor/hooks.json               protected=no   named=no
.gemini/settings.json            protected=no   named=no
.codeium/windsurf/hooks.json     protected=no   named=no
.claude/settings.json            protected=yes  named=yes

So Cursor could edit ~/.cursor/hooks.json, drop the policy hook, and self-protection.ts would not refuse it — the agent removing its own guardrail, which is the one thing the module exists to stop. Same for Codex, Gemini CLI and Windsurf. With the change all five read yes.

How it stays fixed

AGENT_HOOK_FILES is one list in gate/protected-paths.ts, exported from @memnox/core. The installer in cli/src/protect/agent-hooks.ts builds its four paths from it, isProtectedPath matches against it, and NAMES_PROTECTED is now built from it rather than spelled out again. That is the shape the issue asks for: a file gaining a hook cannot be left unguarded, because the installer and the guard read the same line.

It is segments rather than a joined string so the installer keeps using join() and stays right on Windows, while the guard compares the posix spelling it already normalises to.

How it was verified

Four shell lines, one per agent, each refused — sed -i, rm, echo >, cp — and a table test asserting both isProtectedPath and namesProtected for every entry in the shared list, so a future entry is covered the moment it is added.

Two negatives pin that this did not widen into the directories: ~/.cursor/rules.json and ~/.codex/notes.json are still a person's ordinary files.

Test Files  286 passed (286)
     Tests  8461 passed (8461)

prettier, tsc, vitest and knip all clean, on Node 24.

What I did not do

The issue also mentions intercept/os-guard.ts. The kernel profile is a separate list with its own shape, and the acceptance criteria stop at the gate, so I left it alone rather than guess at the seatbelt and Landlock wording. Worth its own issue if the four should be walled there too — happy to take it.

Checklist

  • pnpm format && pnpm typecheck && pnpm test && pnpm deadcode all pass
  • Behaviour change ships with a test
  • No any, no magic values, no console.* outside cli-output.ts
  • If this touches the decision path: still deterministic — one more path list, no model, network or randomness
  • If this changes a verb table: n/a
  • If this changes a command, flag or file it writes: it writes nothing new; four more paths are refused to an agent, which the changeset names

@Cedric921
Cedric921 requested a review from moise10r as a code owner October 6, 2026 14:59
'\\.claude\\/settings(\\.local)?\\.json',
'\\.claude\\.json',
// Built from the one list, so a hook file cannot be installed into and left unnamed here.
...HOOK_FILE_PATHS.map((file) => file.replace(/\./g, '\\.')),

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

self-protection: agents can edit the hook files Memnox installs for Codex, Cursor, Gemini CLI and Windsurf

2 participants