If you wired up gitleaks or Husky to stop yourself committing a .env file, and you commit using the button in Cursor's Source Control panel, that hook has not been running since early August.
No error. No warning. No --no-verify anywhere in sight. The commit just succeeds, exactly as it would if the hook had run and passed.
TL;DR
Since Cursor 3.15.6 (2026-08-06), reporters found the bundled git extension sets core.hooksPath to the platform null device on git invocations from the Source Control panel, so commit-time hooks are skipped. Committing the same changes from the integrated terminal runs them normally. Cursor acknowledged it on 2026-08-12 and it was still reproducing on 3.17.21, the current stable release, as of 2026-08-28. Checking git config core.hooksPath in your own terminal will not reveal it.
What was reported, and by whom
The first report landed on the Cursor forum on 2026-08-07, against build 3.15.6 from the day before. The reporter decompiled the bundled git extension and found it setting core.hooksPath to the platform null device (/dev/null on macOS and Linux, \\.\nul on Windows) on the git subprocess.
More than a dozen people have since reported or reproduced it on that thread, across macOS, Windows and WSL2. A second thread was opened independently. The strongest corroboration isn't the headcount, though, since most people posted after reading the root-cause analysis. It's that one reporter confirmed it on Windows using git's own trace2 instrumentation, and another measured commit time dropping from 37 seconds to 32 milliseconds, which is what a skipped hook looks like on the clock.
Two Cursor staff replied on 2026-08-10 and 2026-08-12, saying "what you're seeing isn't intended" and that the team was tracking it. Worth stating plainly: this reads as a regression, not a decision, and the vendor said so quickly.
As of 2026-08-28 it is still open. The thread carries confirmations against 3.16.29, 3.17.8 and 3.17.21, no changelog entry mentions hooks or the git extension, and 3.17.21 is what Cursor's download endpoint serves as current stable today.
Scope, stated honestly. What people have demonstrated is the commit path, so pre-commit, commit-msg and prepare-commit-msg. The mechanism would plausibly reach pre-push and other hooks too, but nobody has published a test of that, so treat wider impact as likely rather than established.
Why git config will tell you nothing
Here's the part that turns an annoying bug into a genuinely dangerous one, and it's why the obvious check is worse than useless.
The setting doesn't arrive through a config file. Git reads config from environment variables when GIT_CONFIG_COUNT is set, and that is how the value reaches the subprocess. It exists only inside the process the editor spawned. Your terminal is a different process, so it never sees it.
We reproduced that on a scratch repo with plain git 2.43.0, no Cursor involved:
$ git commit -q -m one
PRE-COMMIT HOOK RAN
$ env GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0=core.hooksPath \
GIT_CONFIG_VALUE_0=/dev/null \
git commit -q -m two
# no output. the hook did not run.
# inside that environment, git reports the value:
$ env GIT_CONFIG_COUNT=1 ... git config core.hooksPath
/dev/null
# in your own terminal, which is what you would actually type:
$ git config core.hooksPath
# empty
$ git config --show-origin --get core.hooksPath
# nothing, in any config file
So a developer who suspects something is wrong, runs git config core.hooksPath, and sees an empty result will reasonably conclude their hooks are fine. They aren't. The check and the bug never intersect, because the check runs in the one process where the value was never set.
Treat "my git config looks clean" as no evidence at all here. The only thing that proves a hook ran is the hook leaving something behind.
The check that actually works
Make the hook prove itself. Add one line to it, commit both ways, and compare.
Add an evidence line to your existing .git/hooks/pre-commit (or your Husky pre-commit):
date >> /tmp/hook-ran.log
Stage a trivial change and commit it using the Source Control panel button.
Stage another trivial change and commit it from the integrated terminal with git commit -m test.
Run cat /tmp/hook-ran.log. Two entries means your hooks are running everywhere. One entry means the panel skipped it, and the terminal is the only path currently enforcing your checks.
Remove the evidence line afterwards.
If you want the mechanism rather than the symptom, git will show you its own config resolution:
GIT_TRACE2_CONFIG_PARAMS="core.*" GIT_TRACE2_EVENT="$PWD/t2.log" git commit -m test
Plain GIT_TRACE2_EVENT on its own logs nothing useful here. The CONFIG_PARAMS filter is what makes git emit a def_param line naming the key, the value, and a scope of command.
The hooks are not the only thing switched off
Buried in the same reports is a second key that has drawn almost no attention: core.attributesFile is pointed at the null device too.
That disables your .gitattributes. Line-ending normalization, diff drivers, merge drivers, and any filter you configured all stop applying to commits made through the panel. For a team that uses .gitattributes to keep CRLF out of the repository, or to run a cleaning filter over files before they're stored, this is a correctness problem that will show up later as a confusing diff rather than as an error now.
Nobody has written this one up. If your repository leans on .gitattributes, it's worth the same marker-file treatment.
What to do about it
Commit from the terminal for now. It's the workaround the thread converged on and it needs no configuration change.
Then check what already got through. If this has been live on your machine since early August, the useful question isn't whether the hook works today. It's whether anything slipped past it in the last three weeks. Scan your history rather than your working tree, because a secret that was committed and then deleted is still in the history:
gitleaks detect --source . --log-opts="--all"
If that turns something up, rotate the credential first and clean the history second. Rotating second leaves the key live while you rewrite commits.
The wider point, which outlasts this bug
Cursor will fix this. When they do, the specific advice above stops mattering and the lesson underneath it does not.
A pre-commit hook is not a security control. It's a convenience that runs only if whatever invoked git decided to run it, and you generally don't control that decision. A GUI button, a CI runner, a cloud agent, a teammate typing --no-verify because the hook was slow: every one of them is a path around it, and none of them announces itself.
We say this on our own pages that recommend setting up secret scanning hooks, and it's worth repeating with a live example attached. Hooks belong in the stack as the fast first layer that catches your own mistakes early. The layer you actually rely on is the one that runs somewhere you don't control: scanning the repository, and scanning the deployed app.
This bug is a good argument for that structure, because it removed the first layer for three weeks without telling anybody.
Why are my pre-commit hooks not running in Cursor?
If you committed from the Source Control panel rather than the terminal, reporters found the bundled git extension points core.hooksPath at the null device for that invocation, so git finds no commit-time hooks. Reported 2026-08-07 against 3.15.6, acknowledged by Cursor on 2026-08-12, still reproducing on 3.17.21 as of 2026-08-28.
Does git config core.hooksPath tell me if I am affected?
No, and that's the trap. The value arrives as environment variables on the process the editor spawned, not in any config file, so in your terminal git config core.hooksPath comes back empty and --show-origin finds nothing. A clean result there is not evidence your hooks ran.
How do I actually check whether my hooks ran?
Make the hook leave evidence. Append a line to a file from inside the hook, commit once from the panel and once from the terminal, then compare. The missing entry tells you which path skipped it.
What is the workaround?
Commit from the integrated terminal. The same staged changes run hooks normally there. Beyond that, stop treating a local hook as the thing standing between you and a leaked key.
Does this mean secrets I committed are exposed?
It means a commit your hook would have blocked may have gone through. Whether anything sensitive is genuinely in your history is a separate question, and you answer it by scanning the history directly rather than trusting the hook.
Don't Rely on a Hook You Can't See Run
CheckYourVibe scans what your deployed app actually ships, which is the layer no editor setting can silently switch off.