Skip to content
subcloakdocs

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.

The I think I've been hit sheet: warnings to do Revoke and Rotate from another device and not to run the package again, then 1 Contain with Stop .helper (process 4821) and Fix 6 problems Watch found, and 2 Revoke with Revoke the 2 GitHub tokens found on this Mac and Revoke GitHub CLI's access

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.

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. 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, node or 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.

  2. 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.

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.

One row per credential Watch knows about, Rotate …, grouped from most to least dangerous:

  1. Code and package publishing: GitHub and registry tokens that can publish code.
  2. Cloud, servers and SSH: AWS, Google Cloud, Kubernetes and SSH keys.
  3. AI providers: Anthropic, OpenAI and other AI keys.
  4. Other services.
  5. 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.

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.

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.

  1. Click Revoke… on the Revoke row. The sheet lists each token, masked, with where it is.

  2. 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.

  3. 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.

  4. Tick I understand: the tokens I tick stop working everywhere, and this can’t be undone, then click Revoke Token (or Revoke N Tokens).

The Revoke these GitHub tokens? sheet: two tokens ticked, one unticked because it fails GitHub's checksum and one because it's only in a project folder, the three notes, the I understand checkbox, 58 of GitHub's 60 revoke requests an hour left, and Cancel and Revoke 2 Tokens buttons
  • 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’s hosts.yml or ~/.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.”

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.com or gitlab.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.
  • 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.

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.