Protect your Mac
Respond to an incident
New in 1.2.0 Requires Cloak Watch 1.2.0 or later.
Use Cloak Watch's I think I've been hit plan to contain an attack, revoke and rotate keys in the right order, and check the old keys and the Mac are clean.
I think I’ve been hit is Cloak Watch’s plan for the hours after a bad install: what to stop, which keys to revoke and rotate and in what order, and how to check the Mac and the old keys afterwards. Watch builds it from what it found on this Mac, in the order the write-ups for the 2025 and 2026 incidents called for. Nothing changes until you press a button on a row.
The plan on a test Mac with a running stealer, a malicious package and two GitHub tokens.
Open the plan
Section titled “Open the plan”Open it from either place:
- Inbox: click I Think I’ve Been Hit… at the top.
- The Watch menu: choose I Think I’ve Been Hit….
The sheet lists four steps, each with rows for this Mac. The footer counts what’s done, for example 3 of 22 done. Click Done to close it. Each time you open it, Watch builds it again from what it has found.
The four steps
Section titled “The four steps”| Step | What it’s for |
|---|---|
| 1 Contain | Stop what’s running and remove what persists |
| 2 Revoke | Kill the tokens a worm uses to spread |
| 3 Rotate | Replace every key this Mac holds, most dangerous first |
| 4 Verify | Check the old keys are dead and the Mac is clean |
Warnings at the top of the sheet say when to change your plan:
| Warning | When it shows |
|---|---|
| “Something on this Mac may be watching what you type. Do the Revoke and Rotate steps from another device you trust, and come back here for Verify.” | A program is running from a stealer’s path, a known attack launch item or file was found, or another Act now card matched known malware |
| “Don’t run the package or script again to see what it does. Some worms wipe the home folder when they can’t send what they stole.” | A known malicious package or file was found |
| “Something ran as administrator. Watch can’t prove that’s gone: after rotating, back up your files and reinstall macOS.” | A malicious launch daemon was found in /Library/LaunchDaemons, or a file stealers use to keep your password |
1 Contain
Section titled “1 Contain”-
Stop programs. A row such as Stop .helper (process 4821) appears for each program running from a place stealers use (see Watch mode). Click Stop…, then Stop. Watch sends the program the normal quit signal; its file stays where it is until you fix the problem.
Just before it signals, Watch checks the process is still the one it found: the same program, started at the same time, and for a script run by
python,nodeor another interpreter, the same script. If anything changed, it says “Nothing was stopped.” It won’t stop a program that runs as another user; stop that one from Activity Monitor with an administrator’s password. -
Fix what persists. Fix N problems Watch found runs Fix all for every problem on the Mac. Watch unloads launch items and moves files to Quarantine first, in a safe order. Nothing is deleted, and everything can be put back from Settings › Quarantine.
When nothing is running from those places, the step says Nothing to stop on this Mac right now.
2 Revoke
Section titled “2 Revoke”Worms use stolen GitHub and package-registry tokens to spread, so these come before anything else.
| Row | What it does |
|---|---|
| Revoke the N GitHub tokens found on this Mac | Revoke… sends the tokens you tick to GitHub’s revoke service, which switches them off at once, even ones you can’t sign in to revoke. See Revoke GitHub tokens. |
| Revoke the GitHub token issued by … | A token a credential store files under another host, such as GitHub Enterprise Server. Watch never sends it anywhere: revoke it on that host. |
| Revoke GitHub CLI’s access | The GitHub CLI keeps its token in the Keychain, which Watch doesn’t read. Open goes to GitHub, where you revoke the GitHub CLI app, then sign in again with gh auth login. |
| Revoke your npm tokens, Revoke your PyPI API tokens, Revoke your crates.io tokens, Revoke your RubyGems API keys | Open goes to each registry’s token page. |
| Check GitHub for things you didn’t make | Repositories, runners and workflows a worm may have created with your account. |
3 Rotate
Section titled “3 Rotate”One row per credential Watch knows about, Rotate …, grouped from most to least dangerous:
- Code and package publishing: GitHub and registry tokens that can publish code.
- Cloud, servers and SSH: AWS, Google Cloud, Kubernetes and SSH keys.
- AI providers: Anthropic, OpenAI and other AI keys.
- Other services.
- Sessions and the Mac: Sign out of every other session; Move your crypto to a new wallet, when a wallet app or the MetaMask extension is installed; Change your Mac’s login password, when something may be watching what you type.
Where Watch knows the page for making a new key, the row has Open. Rotating means making a new key, putting it where you use it (including your CI’s secrets), then deleting the old one.
4 Verify
Section titled “4 Verify”| Row | What it does |
|---|---|
| Scan the whole Mac again | Scan Now runs a full scan, so anything still there shows up again. |
| Check the old … is dead | Check asks the company that issued the key whether it still works. See Check an old key is dead. |
| Check Apple’s XProtect is current | Check shows XProtect’s rules version and when it was installed, XProtect Remediator’s version and when it last ran (from the system log of the last 3 days, read only when you press Check), and whether automatic security updates are on. |
Rows Watch can’t do for you have an I’ve done this checkbox. The ticks are your own notes: Watch doesn’t check them.
Revoke GitHub tokens
Section titled “Revoke GitHub tokens”Watch finds GitHub tokens in files on this Mac (ghp_, github_pat_, gho_, ghu_ and ghr_ tokens) and can ask GitHub’s revoke service to switch them off. GitHub’s service works for any token, including ones you can’t sign in to revoke. Nothing is sent until you confirm.
-
Click Revoke… on the Revoke row. The sheet lists each token, masked, with where it is.
-
Tick the tokens to revoke. A token starts unticked, with the reason, when it fails GitHub’s checksum (“Fails GitHub’s checksum: probably a test value”) or is only in a project folder (“Only in a project folder: may be a sample”): fixtures and cloned repositories often hold sample tokens. You can tick it yourself.
-
Read the three notes: “This can’t be undone. GitHub can’t switch a revoked token back on; you’ll make new ones where you need them.” “GitHub emails each token’s owner to say it was revoked.” And: Watch sends each token you tick to GitHub (
api.github.com) over HTTPS, directly and never through a proxy, and nowhere else. Nothing goes to Subcloak. -
Tick I understand: the tokens I tick stop working everywhere, and this can’t be undone, then click Revoke Token (or Revoke N Tokens).
- GitHub allows 60 revoke requests an hour, and each request can carry up to 1,000 tokens. The sheet says how many are left this hour. Watch keeps only the times of its requests, never the tokens, to count the hour.
- Only github.com tokens are sent. A token a credential store files under another host (in
~/.git-credentials, the GitHub CLI’shosts.ymlor~/.netrc) is listed as “issued by” that host, and never sent. - When GitHub accepts, Watch says “GitHub accepted N tokens. It emails each owner. Check below that they’re dead.”
Check an old key is dead
Section titled “Check an old key is dead”After you rotate a key, Check on its Verify row sends that one key to the official service of the company that issued it, to ask whether it still works. It goes directly, never through a proxy and without following redirects, and nowhere else. Nothing goes to Subcloak.
| Issuer | Where Watch asks |
|---|---|
| GitHub | api.github.com (/user) |
| npm | registry.npmjs.org (/-/whoami) |
| Anthropic | api.anthropic.com (/v1/models) |
| OpenAI | api.openai.com (/v1/models) |
| Hugging Face | huggingface.co |
| GitLab | gitlab.com |
| Slack | slack.com (auth.test) |
| AWS | sts.amazonaws.com (GetCallerIdentity), signed with the key’s secret: the access key ID and the signature are sent, the secret itself isn’t |
| Answer | Meaning |
|---|---|
| “… says this key is no longer valid.” (green tick) | The issuer refused the key. It’s dead. |
| “… still accepts this key. Rotate it, then check again.” | The old key still works: delete it at the issuer. |
| Couldn’t tell | The issuer asked Watch to slow down, answered something that doesn’t say either way, or couldn’t be reached. Try again later. |
Watch reads the key when you press Check and keeps it in memory until you close the sheet, so checking again after you’ve replaced it in its file still tests the old key. It’s never written anywhere.
Some keys are never checked, and their rows say why:
- Keys another host issued. GitHub Enterprise Server and self-managed GitLab tokens are labelled “Issued by” that host and never sent to
github.comorgitlab.com. - Keys that look like OpenAI’s but aren’t. Only
sk-proj-,sk-svcacct-,sk-admin-and older keys with OpenAI’s marker go to OpenAI. DeepSeek, Moonshot, DashScope and LiteLLM keys look the same; check those with the service you made them for.
After the plan
Section titled “After the plan”- Watch keeps watching. Anything that comes back, such as a launch item recreated by a program you didn’t stop, raises a new card.
- Look at the Feed headline for the incident, if there is one: it lists what the attack took and anything else to check.
- If something ran as administrator, rotating isn’t enough: back up your files and reinstall macOS.
From Terminal
Section titled “From Terminal”watchctl playbook prints the same four steps for a fresh scan. It only reads: it never stops, revokes or sends anything, and without the app’s feed it lists only what the built-in checks found. See watchctl playbook.