Join our Newsletter — 33% off our NHI Course
Home› Guides› Git Hooks and Pre-commit Security Guide: Use hooks…
Guide Secrets Management

Git Hooks and Pre-commit Security Guide: Use hooks as guardrails without trusting them

PDF for newsletter subscribers · free to read online

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 28 October 2026 13 min read
On this page

A git hook is a script that Git runs automatically at a point in its workflow, such as before a commit or after a checkout, and that makes hooks both a cheap security control and a way to run code on a developer's machine. Teams use pre-commit hooks to stop secrets, keys and unformatted code reaching a repository; attackers have used hook execution during clone to run code before a developer could inspect anything. AI coding agents add a third layer: Claude Code and Cursor now have their own hook systems that run shell commands around the agent's actions, configured by files that can be committed to the repository. This guide explains what git hooks are and where they run, what they can and cannot enforce, how to deploy pre-commit secret scanning properly, the risks of hooks as an execution path, and how agent hooks fit. It is written for developers, platform and DevSecOps teams. Scanner choice and remediation of leaked secrets are covered in the Git Secret Scanning Guide.

Key takeaways

  • Client-side hooks are advisory. They live on the developer's machine, are not enforced by the remote, and Git documents that pre-commit, commit-msg and pre-merge-commit can be bypassed with --no-verify. Enforce the same checks again server-side or in CI.
  • Hooks are code execution. A hook runs with the user's permissions and environment, including cloud credentials, tokens and SSH agent access. Treat hook sources and hook configuration as untrusted until reviewed.
  • Pin what you run. With the pre-commit framework, each hook comes from a repository at a rev; pin it to a commit hash (pre-commit autoupdate --freeze) rather than a movable tag.
  • Keep Git patched. CVE-2024-32002 let a crafted repository with submodules write a hook that ran during a recursive clone on case-insensitive filesystems that support symlinks, with no chance to inspect it.
  • Agent hooks need the same governance. Claude Code and Cursor hooks run commands around agent actions and can be defined in project files; administrators can restrict them to managed configuration.

What git hooks are and where they run

The Git documentation states that hooks live by default in $GIT_DIR/hooks (usually .git/hooks), that the location can be changed with the core.hooksPath configuration variable, and that hooks without the executable bit are ignored. A fresh .git/hooks directory contains only .sample files, which do nothing until renamed. The .git directory is not part of what you commit, so hooks are not shared through a normal push; teams share them through a tracked directory plus core.hooksPath, or through a framework that installs them.

HookRuns onInvoked bySecurity useBypass
pre-commitDeveloper machinegit commitSecret scanning, linting, blocking large or binary files--no-verify (documented)
commit-msgDeveloper machinegit commit, git mergeEnforce ticket references or sign-off text--no-verify (documented)
pre-merge-commitDeveloper machinegit mergeSame checks as pre-commit for merges--no-verify (documented)
pre-pushDeveloper machinegit pushScan the commits about to be pushed; block pushes to protected branchesLocal control; the developer can remove or skip it
post-checkout, post-mergeDeveloper machinegit checkout or switch, git clone (unless --no-checkout), git mergeEnvironment set-up; also an execution path an attacker wantsNot a control
pre-receive, update, proc-receiveGit servergit-receive-pack when it handles a pushReject pushes that contain secrets or break policyCannot be skipped by the pusher
post-receive, post-updateGit servergit-receive-pack after a pushNotifications, audit, triggering scansNot a gate

On hosted platforms such as GitHub and GitLab you usually cannot install raw server-side hooks; the equivalents are push protection, push rules, protected branches and required status checks. Self-managed Git servers can run pre-receive hooks directly.

What hooks can and cannot enforce

  • Can: give fast feedback before a secret ever enters history, which is far cheaper than rewriting history and rotating a credential after a push. Most leaks are accidents, and a pre-commit hook catches the accident at the keyboard.
  • Cannot: guarantee anything. The developer controls their own machine. Git documents --no-verify for pre-commit, the pre-commit framework documents a SKIP environment variable listing hook IDs to skip, and gitleaks documents SKIP=gitleaks to disable its hook. A hook a developer never installed does nothing.
  • Conclusion: treat client-side hooks as a usability layer and put the enforcing control where the developer cannot turn it off: server-side hooks, platform push protection and CI checks on every pull request.

Deploying pre-commit secret scanning

  1. Choose a framework. The pre-commit framework reads a .pre-commit-config.yaml with a list of repos, each with a repo URL, a rev (the revision or tag to clone at) and a list of hooks by id. Repository-local hooks use the sentinel repo: local.
  2. Add a secret scanner. gitleaks documents this configuration:
    repos:
      - repo: https://github.com/gitleaks/gitleaks
        rev: v8.24.2
        hooks:
          - id: gitleaks
    Check the project for the current release before you pin it.
  3. Pin to a hash. Run pre-commit autoupdate --freeze, which stores frozen hashes in rev instead of tag names. A tag can be moved to malicious code; a commit hash cannot.
  4. Install for the right hook types. pre-commit install installs the pre-commit hook; add pre-push with pre-commit install --hook-type pre-commit --hook-type pre-push, so a second scan runs on everything about to leave the machine.
  5. Baseline the repository. Run pre-commit run --all-files once to find existing findings, triage them, and fix or document them before enforcing.
  6. Run the same config in CI. The pre-commit documentation describes run --all-files as useful for CI. Make it a required check on pull requests, so a skipped local hook is caught before merge.
  7. Turn on platform enforcement. Enable push protection or a pre-receive secret scan so a push containing a known secret pattern is rejected outright.
  8. Log bypasses. Where your platform supports push protection bypass reasons, require one and review them. See the Git Secret Scanning Guide.

Rollback: if a new hook blocks legitimate work, tune the rule or allow-list the false positive in the scanner's configuration and ship the change through review, rather than telling developers to use --no-verify, which teaches the habit you are trying to remove.

Choosing how to manage hooks

ApproachHow it worksChoose whenSecurity trade-off
Native hooks in .git/hooksScripts placed by hand or by a set-up scriptOne-off personal checksNot shared or versioned; easy to forget and hard to audit
Tracked directory plus core.hooksPathHooks committed to the repository; each clone points core.hooksPath at themSmall teams wanting plain shell scripts with no extra toolingHook changes arrive through normal code review, but every developer must opt in
pre-commit frameworkA YAML config names hook repositories, revs and hook IDs; the tool installs and caches themPolyglot repositories and organisations standardising checksPulls third-party code, so pin to hashes and mirror hook repositories
Language-specific managers (for example Husky in npm projects)Installed through the project's package managerSingle-ecosystem projects already using that toolingInherits that ecosystem's dependency risk; review install scripts
Server-side and platform controlsPre-receive hooks, push protection, required CI checksAlways, as the enforcing layerCannot be skipped by the developer; slower feedback

Small teams: a tracked hooks directory or a short pre-commit config with a secret scanner, plus the same scan in CI, covers most of the value. Large organisations: publish an approved, hash-pinned pre-commit baseline, mirror the hook repositories internally, distribute agent guard hooks through managed settings and measure adoption per repository.

Hooks as an attack path

RiskHow it happensControl
Hook written during cloneCVE-2024-32002: a crafted repository with submodules could make Git write into .git instead of the submodule worktree and plant a hook that ran during clone, on case-insensitive filesystems with symlink supportUpgrade Git (fixed in 2.45.1, 2.44.1, 2.43.4, 2.42.2, 2.41.1, 2.40.2 and 2.39.4); disabling symlink support blocks the attack; avoid recursive clones of untrusted repositories
Poisoned hook repositoryA pre-commit config pins a hook repo by tag, and the tag is moved or the repo is compromisedPin rev to a commit hash; review hook repos like dependencies; mirror them internally
Malicious tracked hooks directoryA repository ships a hooks folder and a set-up script that sets core.hooksPath to itReview set-up scripts; do not run project bootstrap commands from untrusted repos
Global hooks firing everywhereA global core.hooksPath or global pre-commit set-up runs hooks in every repository, including untrusted onesThe pre-commit documentation says a global set-up is not recommended because it can run hooks when interacting with an untrusted repository
Credential exposureHooks inherit the environment, so they can read cloud credentials, tokens and SSH agent sockets (ATT&CK T1552.001)Short-lived credentials, no long-lived tokens in environment variables or dotfiles
PersistenceAn attacker with access to a workstation adds a hook that runs on every commit (event-triggered execution, T1546)EDR monitoring of changes to .git/hooks and core.hooksPath; file integrity checks on shared hook directories

Git's own design limits some of this. The git-config documentation describes protected configuration (system, global and command scopes) and states that certain options are only respected there, because a repository's own config cannot be trusted in the same way. Hooks themselves are never transferred by clone or fetch, which is why the CVE-2024-32002 class of bug, writing into .git, mattered so much.

Hooks in AI coding agents

AI coding agents have adopted the hook idea for their own loops, and the configuration often lives in the repository, next to the code an agent or developer is opening.

AspectClaude CodeCursor
What a hook isA user-defined shell command, HTTP endpoint, MCP tool call, LLM prompt or subagent run at lifecycle pointsA custom script run at points in the agent loop, exchanging JSON over stdio
Example eventsSessionStart, UserPromptSubmit, PreToolUse (can block), PostToolUse, Stop, SessionEndbeforeShellExecution, beforeMCPExecution, beforeReadFile, afterFileEdit, stop
Where configured~/.claude/settings.json; .claude/settings.json (can be committed); .claude/settings.local.json; managed policy settings; pluginsEnterprise MDM file; team dashboard; project .cursor/hooks.json (runs in any trusted workspace); ~/.cursor/hooks.json
BlockingExit code 2 blocks on events that can block; other non-zero codes are non-blockingExit code 2 or a deny decision blocks; deny wins over ask and allow
Failure behaviourA timed-out PreToolUse hook does not block the tool callCrashes, timeouts and other non-zero exits fail open unless failClosed: true is set
Admin controlallowManagedHooksOnly blocks user, project, local and plugin hooks; strictPluginOnlyCustomization locks hooks to plugins and managed settingsEnterprise and team hook files take priority over project and user files
  • As guardrails: agent hooks are useful security controls: a PreToolUse or beforeShellExecution hook can block destructive commands, and beforeReadFile can stop an agent reading .env files or key material. Claude Code's documentation advises using its permission system rather than a hook for a hard allow or deny, and both products fail open in some failure modes, so design guard hooks to fail closed where the product allows it.
  • As an execution path: a project-level hook file is code that runs when you work in that repository. The Amazon Q MCP configuration vulnerability showed the pattern: a repository's configuration file started processes as soon as the folder was opened, and those processes inherited the developer's AWS credentials and tokens. Review agent configuration in pull requests like code, and use workspace trust. See the Coding Agent Configuration File Attacks guide.
  • Centralise in organisations: deploy required guard hooks through managed settings, restrict hooks to managed sources where you can, and inventory which repositories carry agent hook files.

Where identity fits

Hooks matter for non-human identity because they run where developer and machine credentials live. A pre-commit secret scanner is the first line of defence against API keys, cloud keys and tokens entering source control; a malicious hook is a credential stealer with the developer's full environment. Prefer short-lived, federated credentials on developer machines, keep secrets out of shell profiles and environment variables where possible, and give AI agents their own scoped credentials rather than the developer's. See the Developer Workstation and IDE Security Guide.

Detection and measurement

  • Endpoint: alert on new or modified files under .git/hooks in repositories, changes to core.hooksPath in global config, and hook processes spawning network tools or reading credential files.
  • Repository: flag pull requests that add or change .pre-commit-config.yaml, tracked hooks directories, .claude/settings.json or .cursor/hooks.json, and require code owner review for them.
  • Platform: track push protection blocks and bypasses, and CI secret scan failures that local hooks should have caught.

Metrics: share of active repositories with a pre-commit config that includes a secret scanner; share of those pinned to commit hashes; secrets caught in CI that a local hook should have caught (a proxy for skipped or uninstalled hooks); push protection bypasses per month; developer machines on a Git version below the patched releases; repositories carrying agent hook files without code owner review.

Common failure patterns

  • Hooks as the only control: relying on pre-commit for secret prevention with no server-side or CI check, so --no-verify or a missing install defeats it.
  • Tags instead of hashes: pinning hook repositories to tags, so a moved tag silently changes the code that runs on every developer machine.
  • Normalising bypass: documentation that tells developers to use --no-verify or SKIP when a hook is noisy.
  • Unpatched Git: developer machines and build agents on old Git versions while the team clones external repositories with submodules.
  • Global hooks on everything: running hooks in every repository, including untrusted clones, against the pre-commit project's own advice.
  • Agent hooks committed without review: project-level agent configuration merged without anyone noticing that it runs commands, the same trust gap the Amazon Q case exposed.

Lessons from incidents

The Amazon Q case involved an MCP configuration file rather than a git hook, but the lesson is the same: files in a repository that a tool executes automatically are code, and they run with everything the developer can reach. CVE-2024-32002 showed the git-native version, where a crafted repository planted a hook during clone. Treat hook files and agent configuration as part of the attack surface you review, patch Git promptly, and make sure a malicious hook would find only short-lived, narrowly scoped credentials.

Framework mapping

FrameworkReferenceRelevance
NIST SP 800-53 Rev. 5SA-11, SA-15, CM-3, IA-5(7)Developer testing, development tools, change control, no embedded static authenticators
NIST CSF 2.0PR.PS-06Secure software development practices integrated and monitored
MITRE ATT&CKT1195.001, T1546, T1552.001, T1204.002Compromised development tools, event-triggered execution, credentials in files, malicious files run by users

Practitioner checklist

  • Add a pre-commit config with a secret scanner to every active repository.
  • Pin hook repositories to commit hashes with autoupdate --freeze.
  • Install pre-commit and pre-push hook types.
  • Run the same checks in CI as a required status check.
  • Enable push protection or a pre-receive secret scan on the server.
  • Patch Git on developer machines and build agents.
  • Avoid global hooks and recursive clones of untrusted repositories.
  • Require code owner review for hook and agent configuration files.
  • Deploy agent guard hooks through managed settings and restrict unmanaged hooks.
  • Track bypasses, CI catches and hash-pinning coverage.

Standards and references

Related NHI Mgmt Group resources: Git Secret Scanning Guide · Git Repository Security Guide · Coding Agent Configuration File Attacks · AI Coding Agents Security Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 28 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.