AN
Warsaw, PL

WritingExperiments

The NDA was in a git branch

A friendly recruiter, a real-looking healthcare project and an NDA that could only be read with git checkout. Inside the tarball: a post-checkout hook that would have run a stranger's script on my laptop. What the hook does, which git commands trigger it, and how I open such archives now.

Security · Git · AI agents · Hiring scams

A recruiter asked me to sign an NDA that lived in a git branch. Checking out that branch would have run a stranger’s script on my laptop.

A perfectly normal conversation

It started at the end of September with a “Senior Technical Recruiter” on LinkedIn: a healthcare MVP, 16 weeks, B2B, $11–12K a month. Then a Dropbox link to a .tar.gz with the project docs.

I didn’t run anything. ChatGPT unpacked the archive in its own sandbox, I read the documents and sent real feedback: Go instead of Node.js, an append-only ledger for ownership transfers, a modular monolith instead of a zoo of services. I even caught the documents disagreeing about the timeline. “Earlier planning versions”, I was told. Plausible.

Then I proposed a team with me as tech lead. Twenty-two minutes later: “looks reasonable”. The fastest sign-off of my career. Next step: pricing, and the signed NDA, which is “under the dedicated NDA branch”.

A branch. In a tarball.

The tell

A repository shipped as an archive had bothered me from the start, and I hadn’t run git on it. Getting that NDA meant a git checkout. So the next day I gave Claude Code the link first and asked for a short analysis, without saying why.

A few minutes later: this is not a project, it’s bait with malware.

What was in .git/hooks

Fourteen inert *.sample files, and one without the suffix: an executable post-checkout, dressed up as a “Chrome update helper”. The agent read it as text, looked at the branches only through read-only git with hooks switched off, and then checked my Mac anyway. Nothing.

What the hook does:

  • sets TRIGGER=0, then “checks” file sizes and never sets TRIGGER=1;
  • runs the payload if TRIGGER is still 0 — so always;
  • picks a script by operating system from brightlaunch-ext75642[.]vercel[.]app/api/{m,l,w}, saves it to $TMPDIR, runs it in the background and leaves a marker file;
  • prints nothing. The checkout looks completely normal.

Nobody downloaded the payload, so I can’t tell you what it does. Remote code execution as you is enough: your SSH keys, cloud tokens and browser sessions are in reach.

The README helpfully says: git checkout NDA. An NDA in a branch is an instruction to run git checkout, dressed up as paperwork.

Probably because git clone never copies the sender’s hooks. A tarball of a working copy does, executable bit included.

I checked which commands fire post-checkout in a throwaway repository with a harmless hook (git 2.49):

Runs the hook Doesn’t run it
git checkout <branch>, even the current one git status, git log, git diff, git show
git switch git stash
git checkout -- <file>, git restore git reset --hard
git worktree add git clone (hooks aren’t copied)

The right column holds only while .git/config is clean: config has its own ways to run commands, and disabling hooks doesn’t stop them.

This isn’t an “AI saved me” story

The hook doesn’t care who types git checkout: me, my IDE or my agent. “Check the NDA in the NDA branch” is a perfectly normal task for a coding agent, and checkout is the obvious first step. Git runs the hook as you.

No agent ran this hook. Mine was careful this time. I don’t want my security to depend on “this time”.

How I open such archives now

  1. An archive with a .git folder is an executable. Treat it like one.
  2. Read .git/hooks and .git/config as text before any git command.
  3. Read branches without checking them out: git show NDA:path/to/file, with hooks off: git -c core.hooksPath=/dev/null …. Better still, copy only .git into a clean bare repository, rename the hooks, drop the executable bit and replace the config.
  4. Anything more goes into a throwaway VM, agents included.
  5. Already ran a checkout in one? Assume compromise and rotate keys and tokens from a clean machine.

I reported the domain to Vercel and the file to Dropbox, and reported and blocked the “recruiter” on LinkedIn.

Do you sandbox your coding agents — a container, a VM, a separate user? Do you let them run git in repositories you didn’t create?

Screenshot of a LinkedIn chat with the recruiter name and avatars hidden; highlighted: please send the signed official NDA, it is included in the shared project materials under the dedicated NDA branch.