HookStack
Back to catalogue
SecurityPreToolUse· Write|Edit

Lock file write protection

Lock files stay intact - package manager only

Prevents the agent from directly modifying lock files (pnpm-lock.yaml, package-lock.json, yarn.lock, Cargo.lock, Gemfile.lock, poetry.lock, etc.). Lock files must only be written by the package manager to stay consistent and reproducible.

What does the Lock file write protection hook do?

Lock file write protection is a Claude Code PreToolUse hook matching Write|Edit. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Lock files stay intact - package manager only.

As a PreToolUse hook it runs before the action completes, so it can block or adjust what Claude is about to do. Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.

Use cases

  • Prevent lock file corruption
  • Ensure reproducible builds
  • Multi-ecosystem projects (npm, pnpm, yarn, cargo, bundler)

Tags

#security#files#lockfile#protection#dependencies#reproducibility

settings.json fragment

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/protect-lockfiles.mjs",
            "type": "command"
          }
        ],
        "matcher": "Write|Edit"
      }
    ]
  }
}

Script · .claude/hooks/protect-lockfiles.mjs

#!/usr/bin/env node
// @hookstack pre-edit-protect-lockfiles
// Bloque les éditions directes de fichiers de lock (PreToolUse Write|Edit)
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";

const LOCKFILES = [
	/pnpm-lock\.yaml$/,
	/package-lock\.json$/,
	/yarn\.lock$/,
	/Gemfile\.lock$/,
	/poetry\.lock$/,
	/Pipfile\.lock$/,
	/composer\.lock$/,
	/Cargo\.lock$/,
];

export function run(input) {
	const filePath = input.tool_input?.file_path ?? "";
	if (!filePath) return null;
	const match = LOCKFILES.find((p) => p.test(filePath));
	return match
		? {
				decision: "block",
				reason: `Lock file ${filePath} must not be edited directly. Run the package manager instead (pnpm install, npm install, cargo build…).`,
			}
		: null;
}

/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
	const input = JSON.parse(readFileSync(0, "utf8"));
	const result = run(input);
	if (result) process.stdout.write(JSON.stringify(result));
}

Learn more

Related hooks