Protect your Mac
Detection catalogue
Every check Cloak Watch runs, by ID, with what it looks for, the tier it raises, how it avoids false alarms, and what to do when one fires.
Cloak Watch runs four kinds of checks. Feed matches compare your inventory with the signed feed’s list of known malicious versions, Subcloak’s incident packs and the feed’s list of exploited flaws. Built-in checks look for the shapes attacks leave behind, such as a hook that runs a hidden script or a workflow that leaks secrets, so they can catch the next attack before any feed names it. Headline checks answer Check my Mac for each headline and raise a card when this Mac is affected. Checks beyond this Mac look at your GitHub account and the domains you add, only when you turn them on. Every check on the Mac reads files and metadata only; none runs package code, scripts, MCP servers or git. The few exceptions that ask a program something, such as Safari’s extension list or a container engine’s image list, are noted where they apply.
Each check has an ID. You see it under Details › Checks that found it on an incident page. Every check and every fix is free on every plan. Learn more on an incident opens its write-up on headlines.subcloak.com when a headline covers it, and otherwise this page at the check’s ID.
Tiers and the starting point
Section titled “Tiers and the starting point”Each check raises an incident in one of three tiers: Act now (shown as Fix now), Review (Worth a look) or FYI (Good to know).
The first scan records a starting point. Review and FYI findings from built-in checks about what was already on your Mac then join a quiet baseline: they don’t show in the Inbox or notify you, and Watch tells you about changes since. Anything at Act now always alerts, on day one too, whether a built-in check or a feed match found it. Feed matches at any tier always alert. Reset the starting point in Settings › Advanced.
Packages (PKG)
Section titled “Packages (PKG)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| PKG-1 | A package version on the feed: in a lockfile, installed in a project, installed globally, or in a download cache. Matches an incident pack’s affected versions, or the feed’s index of malicious versions. | Act now when it’s installed or cached. Review when only a lockfile pins it (“Only a lockfile pins it: nothing is installed or cached yet.”). Suspected packs are at most Review. | Exact name and version only. The incident says whether the version arrived inside the window when the bad version was available; that doesn’t change the tier. |
| PKG-2 | A file whose SHA-256 matches a known malware file from an incident pack or Watch’s built-in list. | Act now | Exact hash only. |
| PKG-3 | Known payload file names inside a package folder: setup.mjs, Math_Symbol.js, setup_bun.js, bun_environment.js. |
Act now when the file’s content matches a known payload. Review when only the name matches (except setup.mjs, which needs the content). |
Content check, not the name alone. |
| PKG-4 | A Python startup file (a .pth file, sitecustomize.py, usercustomize.py, or a .start file and the code it calls) with a known attack file name, or code that downloads, decodes or starts programs, such as base64, urlopen, os.system, Popen( or curl. Python runs these every time it starts. Fix: Watch moves the file to Quarantine, and Python stops running it at once; then recreate the affected Python environments. |
Act now | Common harmless .pth files are allowed (editable installs, setuptools, virtualenv, coverage and similar). Other startup code is PER-7 at Review. |
| PKG-5 | An installed package whose preinstall, install or postinstall script runs a file over 100 KB or downloads a runtime such as Bun. |
Review | About 70 packages that legitimately build or download at install (esbuild, sharp, Prisma, SWC and similar) are allowed. |
| PKG-6 | A bun binary or bun-v*.zip in your temporary folder or Downloads, or a tmp.dpkg_*.lock file in the temporary folder. |
Review; Act now when a PKG-3 payload is also present | Bun installed with Homebrew or in ~/.bun isn’t flagged. |
| PKG-7 | A package registry or index in your settings (.npmrc, pip.conf, Cargo config, .gemrc and others) that isn’t a known public registry. |
Review | Company mirrors show here too: mark the incident not a problem once. An index added with extra-index-url raises REPO-10 instead. |
| PKG-8 | Install cooldowns, per tool: npm, pnpm, Yarn, Bun, Deno, pip, uv, Poetry and Bundler, each read from the version Watch finds and the file that version reads (per tool from 1.2.0). One card for tools with no cooldown, or whose version has none (“npm installs brand-new versions”), and one card for a cooldown you set that your version ignores (“A cooldown you set isn’t being applied”), such as npm older than 11.10, pip older than 26.0, uv older than 0.9.17, Yarn 1, or a Bun value under an hour. | FYI for no cooldown; Review for a cooldown that’s ignored | Only tools on the Mac or named by a lockfile. pnpm 11 and Deno 2.9 wait a day by default and count as on. Fix it in Install Guard. |
| PKG-9 | GitHub Actions workflows in your projects that contain known attack strings (for example a step named “Run Copilot” or toJSON(secrets)). |
Review | Only repositories in your code folders. Actions pinned by tag are REPO-1 from 1.1. |
| PKG-10 New in 1.2.0 | A container image an incident pack names, by tag, digest or image ID, in a running container engine (Docker Desktop, OrbStack, Colima, Rancher Desktop or Podman). See Container images. | From the pack | Images are read only while their engine runs. Watch asks the engine for its image list and each image’s settings, nothing else. |
| PKG-11 New in 1.2.0 | A package manager setting that weakens a safe default: certificate checks off (strict-ssl=false, NODE_TLS_REJECT_UNAUTHORIZED=0, pip trusted-host, uv allow-insecure-host, GOINSECURE), Go’s checksum database off (GOSUMDB=off, or GONOSUMDB, GONOSUMCHECK or GOPRIVATE set to *), every install script allowed (pnpm dangerouslyAllowAllBuilds, npm allow-scripts=*), or a cooldown set to 0. Title: “… weakens a package manager’s safety setting”. Also: “An install cooldown was removed” when a cooldown disappears between scans. |
Review; FYI for GOPROXY=direct, Yarn unsafeHttpWhitelist, Cargo replace-with and npm ignore-scripts=false |
Read from user and project config files and export lines in shell profiles. Turning a cooldown off in Install Guard doesn’t raise the removed-cooldown card. Fix: remove the setting unless you need it. |
Persistence (PER)
Section titled “Persistence (PER)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| PER-1 | A launch agent or daemon with a known attack label, such as com.user.gh-token-monitor, or one named in an incident pack. |
Act now | Exact label. |
| PER-2 | A launch agent or daemon whose program is in /tmp, /Library/Caches, /Users/Shared or an unusual hidden folder in your home, or a com.apple.* label running from outside the system folders. |
Review | Programs signed by Apple or with a Developer ID are skipped. Items already there at the starting point join the quiet baseline. |
| PER-3 | A known attack file at an exact path, such as stealer files in your home folder or /Library/Caches, or a file indicator from an incident pack. |
Act now | Exact path; a pack’s hash or content check, when it has one, must match too. |
| PER-4 | Shell profile lines that shut the Mac down, download and run a script, run osascript, decode base64, hold a long base64 blob, or add a hidden folder to PATH. |
Act now for shutdown; Review for the rest | One incident per line. Review lines already there at the starting point join the quiet baseline; a shutdown line always alerts. |
| PER-5 | A global Git core.hooksPath pointing into a hidden folder, or an executable Git hook that makes network calls. |
Review | Husky and lefthook layouts are allowed. |
| PER-6 | Commits authored as claude@users.noreply.github.com in a repository’s history. |
Review; Act now when the same repository also has an AGT-1 hook | Only the combination is urgent. |
| PER-7 New in 1.2.0 | Python startup code that isn’t clearly harmful: an import line in a .pth file, a sitecustomize.py or usercustomize.py in site-packages, or a Python 3.15 .start file, in every Python Watch finds (Homebrew, python.org, the Command Line Tools and Xcode, pyenv, uv, conda, asdf, mise, pipx, Poetry, ~/Library/Python, ~/.local and project venv, .venv, env and .env folders). The card names the package that installed the file when its install record lists it. |
Review | setuptools, virtualenv, editable installs, namespace packages, coverage and similar are allowed. Startup code that downloads, decodes or starts programs is PKG-4 at Act now. Fix: Quarantine the file, then reinstall the package from a version you trust if you need it. |
AI agents (AGT)
Section titled “AI agents (AGT)”“A repository you don’t own” means a clone whose remote isn’t you: not your GitHub login, not an owner of a repository you’ve committed to or pushed from on this Mac, and with no commits or pushes from this Mac. Folders with no Git remote are never treated as someone else’s. See AI agents and MCP.
| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| AGT-1 | Hooks in Claude Code settings (.claude/settings*.json, plugins) that run node, bash -c, curl, wget, python, osascript, base64, eval or a hidden path. |
Act now for known attack hooks; Review for the rest | Watch’s own hook and Anthropic’s official plugin marketplace are excluded. |
| AGT-2 | A .vscode/tasks.json task that runs when the folder opens (runOn: folderOpen). |
Act now for a known attack task such as one labelled “Environment Setup”; Review otherwise | Review tasks already there at the starting point join the quiet baseline; a known attack task always alerts. |
| AGT-3 | Settings that send an AI tool’s traffic somewhere else: a base-URL, proxy or certificate override that doesn’t point at the provider, or an apiKeyHelper script. |
Review; FYI for an apiKeyHelper in your user settings |
Company gateways show here too: mark the incident not a problem. |
| AGT-4 | A project’s Claude Code settings that approve its MCP servers in advance (enableAllProjectMcpServers, enabledMcpjsonServers). |
Review | |
| AGT-5 | Broad allow rules such as Bash(*) or *, which let an agent run any command without asking. |
FYI | |
| AGT-6 | Hidden text in rules files and skills (CLAUDE.md, AGENTS.md, .cursorrules, SKILL.md and others): Unicode tag characters, three or more zero-width characters, or text-direction overrides. |
Act now for tag characters; Review for zero-width characters and overrides | Flag emoji, which use tag characters, are allowed. |
| AGT-8 | AI tool login files present (Claude, Codex, Cursor, Gemini). Attacks increasingly collect them. | FYI | Presence only; Watch never reads them. |
| AGT-9 New in 1.2.0 | Tool approval turned off: Claude Code permissions.defaultMode: "bypassPermissions", Codex approval_policy = "never" with sandbox_mode = "danger-full-access", VS Code-family chat.tools.global.autoApprove, or a catch-all terminal auto-approve rule. “A cloned repo turns off …’s approval prompts” or “… runs commands without asking”. |
Review when a repository you don’t own sets it in its shared project settings; FYI in your own settings | Specific allow rules such as "npm test": true aren’t flagged. Watch never edits agent settings; the card names the key to remove. |
| AGT-10 New in 1.2.0 | A project’s Claude Code or Codex settings that set the environment of the agent’s commands: PATH, BASH_ENV, NODE_OPTIONS, PYTHONPATH, GIT_SSH_COMMAND, DYLD_* and similar. |
Review; Act now when PATH reaches a folder inside the repository that holds its own git, node, python, npm, sh or similar (“A repo replaces the git or node that … runs”) |
|
| AGT-11 New in 1.2.0 | An AI tool or extension at a release an incident says was tampered with. | Review | Exact release. Fix: update. |
| DEV-1 New in 1.2.0 | A Dev Container initializeCommand in a repository you don’t own (“A cloned repo’s dev container runs a command on your Mac”). It runs on the Mac, not in the container, when an editor opens the folder. |
Review; Act now when it downloads, decodes or pipes into a shell | Only .devcontainer.json and .devcontainer/ files in repositories you don’t own. |
MCP servers (MCP)
Section titled “MCP servers (MCP)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| MCP-1 | An enabled MCP server whose command runs from a hidden folder, /tmp, a temporary folder, a file inside node_modules, or sh -c / bash -c; or a server an incident pack names. |
Act now | Known tools’ own folders are allowed. Act now, so it alerts even for a server that was already there at the starting point. |
| MCP-2 | An MCP server that launches a package version on the feed (through npx, bunx, pnpm dlx, uvx, pipx, uv or docker). |
Act now; Review when the command doesn’t pin a version and not every version is bad | Exact version. |
| MCP-3 | A new tool appearing on an MCP server that an agent already uses. Shown in AI Activity, from Claude Code’s hooks. | Review | |
| MCP-5 | A tool result that reads like instructions to the model (prompt injection). Shown in AI Activity, from agent hooks; Claude Code and Codex are also told to treat the text as data. | Review | |
| MCP-6 New in 1.2.0 | A project MCP server (.mcp.json, .cursor/mcp.json, .vscode/mcp.json, .codex/config.toml, .gemini/settings.json and others) in a repository you don’t own (“A cloned repo brings its own MCP server”). |
Review; Act now when the server runs from a hidden or temporary folder, a file inside node_modules, or an inline sh -c script |
Turned-off servers are skipped. |
| MCP-7 New in 1.2.0 | A remote MCP server on a tunnel or throwaway host (ngrok, trycloudflare, webhook.site and similar) or a bare IP address, or one whose host changed since Watch first saw it. | Review | Local addresses are skipped. |
| MCP-8 New in 1.2.0 | An AI tool released before fixes for project config running before you trusted the folder: Cursor older than 1.3, Claude Code older than 1.0.111 or 2.1.196, Codex CLI older than 0.131.0 (“… can run a project’s code before you trust it”). | Review | Fix: update, and until then don’t open repositories you didn’t write in it. |
Extensions (EXT)
Section titled “Extensions (EXT)”Watch reads extensions in VS Code, VS Code Insiders, Cursor, Windsurf, VSCodium, Kiro, Trae, Positron and Antigravity, JetBrains IDEs, Chromium browsers, Firefox and Safari. Each fix uses the editor’s or browser’s own uninstall. See Extensions.
| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| EXT-1 | An editor extension version on the feed, including Datadog’s list of malicious editor extensions. Watch checks both the VS Code Marketplace and Open VSX IDs. | Act now | Exact ID and version, or the versions Datadog’s samples name. |
| EXT-2 | An Open VSX extension (in Cursor, Windsurf and other non-VS Code editors) with the name of a popular extension but a different publisher, as copycat extensions do. | Review | Only a list of 32 popular names; trusted publishers are excluded. |
| EXT-3 | A browser extension on the feed, or on a community list of malicious extensions, matched by its ID. | Act now; Review when it’s on a list but turned off in the browser | Exact ID and version. A list match’s card names the list and its license. |
| EXT-4 New in 1.2.0 | An editor extension installed from a .vsix file whose publisher nothing else on the Mac vouches for (“Extension installed from a file, not a marketplace”). |
Review | Platform vendors, publishers of popular extensions, and publishers with another extension from a marketplace are trusted. |
| EXT-5 New in 1.2.0 | An editor extension that pulls in another publisher’s extension as a dependency or pack member, when that extension is installed in the same editor and isn’t from a known vendor. | Review | |
| EXT-6 New in 1.2.0 | Hidden code: 32 or more invisible characters in a row (variation selectors, Unicode tag characters or zero-width characters) in an extension’s shipped JavaScript (“Hidden code in the … extension”). GlassWorm hid its code this way. | Act now; Review for runs of 8 to 31 tag characters or variation selectors | An emoji’s single selector never counts. Watch reads the entry files and the .js files at the top and in out, dist, lib, src, client and build, at most 12 files and 32 MB per extension. |
| EXT-7 New in 1.2.0 | The same extension ID installed in two editors from two different publishers, when the copies don’t name the same source repository. | Review | |
| EXT-8 New in 1.2.0 | A JetBrains plugin (IntelliJ IDEA, PyCharm, WebStorm, GoLand, Rider, CLion, RustRover, Android Studio and others) at a version an incident pack names. | From the pack | Read from META-INF/plugin.xml inside each plugin. |
| EXT-9 New in 1.2.0 | A Chromium browser running an unpacked extension, or one added with --load-extension on the command line. |
Act now from a hidden, temporary, cache or shared folder; Review otherwise; FYI for an unpacked extension inside one of your code folders (usually one you’re building) | |
| EXT-10 New in 1.2.0 | A store-installed browser extension that updates from a server other than the store (“A browser extension updates from its own server”). | Review | Extensions installed by policy are skipped. |
| EXT-11 New in 1.2.0 | An extension Chrome flagged (its malware or policy verdict, Safe Browsing’s blocklist, or the greylist) that is still turned on (“An extension Chrome flagged is still turned on”). | Act now when it was flagged as malware; Review otherwise | |
| EXT-12 New in 1.2.0 | A browser extension at a version an incident pack names, such as a hijacked release. | From the pack | Exact ID and version. |
Skills (SKL)
Section titled “Skills (SKL)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| SKL-1 | An agent skill on the feed, by file hash, content or name, or on Datadog’s malicious agent-skill list, matched by where the skill came from: its GitHub owner, repository and folder (from 1.2.0). | Act now | Never matched by the repository alone. Fix: Watch moves the skill’s folder to Quarantine, or only its SKILL.md when the folder holds other things, and asks you to check the rest. |
| SKL-2 | A SKILL.md whose instructions download and run something, such as curl … | bash “prerequisites”. |
Review |
Secrets (SEC)
Section titled “Secrets (SEC)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| SEC-1 | Keys and tokens saved in plain text: in shell profiles, ~/.env and .envrc, ~/.gitconfig, ~/.aws/config, AI agent and MCP configs (including MCP env blocks and headers), rules files, and your projects’ .env files. Types: AWS access keys, GitHub, GitLab, npm, PyPI, Hugging Face, Slack, Stripe, Anthropic, OpenAI and Google keys and tokens, private keys and JSON Web Tokens. |
Review | One incident per file. Example and template files and obvious placeholders are skipped. Values are never stored: Watch keeps a masked form (the first four and last two characters and the length, or only the length for values under 24 characters and passwords). |
| SEC-2 | Shell history lines that download and run scripts, or reach known lure domains, as fake installer pages ask you to paste. | Review | Off in the app. Run it with watchctl scan --history. Known installers (Homebrew, rustup, Bun, uv, Deno, Claude) are skipped. |
| SEC-3 New in 1.2.0 | What kind of credential each file holds. “Publish tokens are stored in plain text”: npm, PyPI, crates.io and RubyGems tokens, the GitHub CLI’s token outside the Keychain, and ~/.git-credentials. “Long-lived keys a stealer would take”: AKIA AWS access keys, SSH keys without a passphrase, static Kubernetes tokens and client keys, Google Cloud service-account keys and ~/.netrc passwords. |
Review for publish tokens, which worms use to spread; FYI for long-lived keys | Watch never shows or keeps the value. For SSH keys it reads only the key’s header. The card lists what to rotate, GitHub first. |
| SEC-4 New in 1.2.0 | Keys in a project’s git history, on every branch: about 220 patterns derived from Gitleaks. One card per repository (“2 keys in the git history of shopapp”), with each key masked, its file, line and commit, and whether it’s still in the current files or only in history. | Review | Lockfiles, vendored code, node_modules, images and binaries are skipped. Turn it off in Settings › Privacy. See Keys in git history. |
Repositories and CI (REPO)
Section titled “Repositories and CI (REPO)”These checks read the GitHub Actions workflows, Git settings and package-manager settings in the local clones under your code folders, as files. Watch never runs git or a workflow. REPO-1 to REPO-7 are derived from the audits of zizmor, a GitHub Actions security linter by William Woodruff, under the MIT License; the notice is in Settings › Advanced, under Acknowledgements: click Show next to Third-party notices.
REPO-1, REPO-4 and REPO-5 are hygiene findings: each is one FYI card for the whole Mac, with a row per workflow. The others are one Review card per repository.
| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| REPO-1 | Workflows that use third-party actions by tag or branch instead of a full 40-character commit SHA (“Workflows use actions by tag, not by commit”). Based on zizmor’s unpinned-uses. | FYI | GitHub’s own actions/* and github/* actions may use tags. Local (./) and docker:// steps are skipped. |
| REPO-2 | A pull_request_target or workflow_run workflow that checks out the pull request’s own code (a ref: to the pull request’s head, refs/pull/…, or gh pr checkout), so untrusted code runs with the repository’s secrets. Based on zizmor’s dangerous-triggers. |
Review | Only those two triggers, and only with a checkout of the pull request’s code. |
| REPO-3 | Text an attacker controls, such as an issue or pull request title or body, a comment or review body, a commit message or github.head_ref, put into a run: step or a github-script script: with ${{ }} (“A workflow puts untrusted text into a shell command”). Based on zizmor’s template-injection. |
Review | Only ${{ }} inside a script. Passing the value through env: and using "$VAR" is safe and isn’t flagged. |
| REPO-4 | permissions: write-all, or no permissions at all in a workflow that forks can trigger (pull_request_target, workflow_run, issue_comment). Based on zizmor’s excessive-permissions. |
FYI | A missing permissions block is flagged only for those triggers. |
| REPO-5 | A release workflow (on release, or on push with tags:) that restores a cache: actions/cache, Swatinem/rust-cache, or a setup action’s cache:. A cache another workflow wrote could reach what you publish. Based on zizmor’s cache-poisoning. |
FYI | Only workflows that publish. |
| REPO-6 | A workflow that uses an action that was compromised: tj-actions/changed-files at 45.0.7 or older (GHSA-mrrh-fwg8-r2c3), or reviewdog/action-setup@v1 and the five reviewdog actions that use it (GHSA-qmg3-hpqr-gqvc). Based on zizmor’s known-vulnerable-actions, as a built-in list. |
Review | tj-actions/changed-files pinned by commit SHA isn’t judged by version. The five reviewdog actions are flagged however they’re pinned, as their advisory says. |
| REPO-7 | ACTIONS_ALLOW_UNSECURE_COMMANDS set to true, which turns back on workflow commands that let a step’s output set variables and paths. Based on zizmor’s insecure-commands. |
Review | |
| REPO-8 | A token or password inside a remote URL, or a url.<base>.insteadOf rule, in a repository’s .git/config (“A token is saved in a Git remote URL”). |
Review | A user name alone (git@, ada@) and a variable such as $TOKEN aren’t flagged. Watch keeps the remote’s name, the host and the kind of credential, never the value. |
| REPO-9 | A Terraform state file (*.tfstate or *.tfstate.backup) in a project, at the top level or up to two folders down (“Terraform state is stored in a repository”). State holds the secrets Terraform manages in plain text. |
Review | Watch notes the file’s size and never opens it. .terraform/terraform.tfstate, the backend’s own pointer file, isn’t counted. |
| REPO-10 | pip told to search a second index with extra-index-url (in pip.conf in your home folder, /etc or a project, or --extra-index-url in a project’s requirements*.txt), or uv told to merge indexes with index-strategy = "unsafe-…" in uv.toml. A public package with the same name as a private one can then win. |
Review; FYI for well-known public indexes such as download.pytorch.org |
|
| REPO-11 | An npm scope sent to a private registry in an .npmrc (for example @shopapp:registry=…), with a package of that scope in your projects that was resolved from the public registry, or that’s at version 99 or higher (“A private package may have come from the public registry”). |
Review | Vendors’ own registries (npm.fontawesome.com, GitHub Packages) are skipped. |
| REPO-12 New in 1.2.0 | A workflow shaped like a documented attack (“A known malicious workflow is in …”): Shai-Hulud’s shai-hulud-workflow.yml, a discussion.yaml backdoor where a self-hosted runner runs a Discussion’s text, a workflow that sends toJSON(secrets) to an outside address, or GhostAction’s “security audit” workflows. |
Act now, even without a headline | Ordinary workflows that mention secrets don’t match. Fix: delete the workflow, check the repository’s Actions run history, and rotate the GitHub token and every workflow secret. |
To learn whether a headline’s planted workflows are in your clones, see repositories and workflows in Check my Mac.
Git settings (GIT)
Section titled “Git settings (GIT)”Git runs some programs its settings name, and AI agents and editors run git status in the background as soon as you open a project. A normal clone doesn’t copy a repository’s settings: they arrive with a .git folder from a zip, a synced folder or a shared drive. Watch reads every repository’s .git/config in your code folders, its submodules’ config, files that config includes, and bare repositories nested inside the project. It never runs Git and never changes its config: each card gives the git config command to run.
| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| GIT-1 | A repository’s own Git settings that name a program Git runs: core.fsmonitor, core.pager, core.sshCommand, credential.helper, diff and merge tools, filters and core.hooksPath (“…’s Git settings run a program when Git starts”). This was disclosed as GitSpawn in September 2026. If the repository has no record of being cloned, the card says so. |
Act now when the program is inside the repository or in a temporary, shared or hidden folder, or the setting downloads or decodes something; Review for other programs Watch can’t place; FYI when the config only includes a file from the repository | Editors, pagers, Git LFS, gh, credential managers and similar tools don’t raise a card. For core.fsmonitor the card also gives git config --global core.fsmonitor false. |
| GIT-2 | Your global Git settings: trusting every repository (safe.directory = *), protocol.file.allow = always, core.protectHFS or core.protectNTFS turned off, or an include from a temporary or unusual folder (“Your global Git settings weaken a safety check”). |
Review; Act now when a global or included file runs a suspicious program (“Your global Git settings run a suspicious program”) | Reads ~/.gitconfig, ~/.config/git/config, /etc/gitconfig, Homebrew’s gitconfig and their includes. |
| GIT-3 | A .gitmodules shaped like a known attack on git clone (“…’s submodules are shaped like a Git clone attack”): a submodule path ending in a hidden carriage return (CVE-2025-48384), a path through a symbolic link, including one that differs only in case (CVE-2024-32002), an update = !command line, or a URL starting with -. |
Review; Act now when the link points into .git |
The card says which Git versions fix the bugs (2.45.1 and 2.50.1, and matching point releases) and not to clone it with --recursive. |
Runners (RUN)
Section titled “Runners (RUN)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| RUN-1 | A self-hosted GitHub Actions runner on this Mac, in your home folder or /Users/Shared, or an actions.runner.* launch item. The Shai-Hulud 2.0 worm registered victims’ Macs as runners in a hidden ~/.dev-env folder. Watch reads the runner’s name and the repository it serves, never its credentials. |
Act now for a runner in a hidden folder, named SHA1HULUD, owned by root or started as a daemon (“A GitHub Actions runner on this Mac looks like a worm’s backdoor”); Review for a runner whose owner none of your clones come from; FYI for the rest |
Fix for Act now: stop it, quarantine its folder and rotate GitHub tokens. Otherwise the card links to the repository’s runner settings: if you set it up, nothing to do. |
Trust stores (TRUST)
Section titled “Trust stores (TRUST)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| TRUST-1 | A root certificate that you, an app or an administrator added to the keychains’ trust settings, so the Mac trusts it for every secure connection. Debugging proxies do this on purpose; adware and malware do it too. | Review; FYI when its name is a known debugging or local-development tool’s own certificate authority (Cloak Proxy, mitmproxy, Charles, Proxyman, HTTP Toolkit, Fiddler, mkcert, Burp Suite, Caddy, Docker Desktop, Laravel Valet) | Watch never removes it. The fix: if you don’t use it any more, delete it in Keychain Access. |
Distrusted certificate authorities in the other trust stores on the Mac (Homebrew’s, each Python certifi, Node and Electron, JDKs) are found by a headline’s trust-store check.
Versions (VER)
Section titled “Versions (VER)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| VER-1 | An installed copy of macOS, Safari, a browser, an editor, Docker Desktop, a runtime or a developer tool at a version with a flaw known to be exploited, from the feed’s list built from Apple’s actively exploited macOS and Safari flaws and CISA’s Known Exploited Vulnerabilities catalog. One card per copy, such as “macOS 26.6.2 has an exploited flaw”, naming the flaw and the release that fixes it. | Review | Only exploited flaws, only in installed apps and tools and the OS, never a library in a lockfile: Watch isn’t a CVE scanner. See versions for where each version is read. |
Headline checks (CHK)
Section titled “Headline checks (CHK)”When a headline’s own checks answer Check my Mac with This Mac is affected or Relevant to you, Watch opens one card for that headline. Its ID names the kind of check that found it. Package answers don’t open CHK cards: the headline’s incident packs raise PKG-1 and the others.
| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| CHK-version | An app, tool or macOS version in the range a headline names. | Review | Each copy is judged on its own. |
| CHK-trustStore | A trust store on the Mac that still trusts a certificate authority a headline names, by its public key. | Review | Apple’s roots count as fixed once Apple’s revocation list on this Mac is newer than Apple’s block. |
| CHK-serviceFootprint | A login, token or config file for a service a headline names. The card is titled Relevant to you: and lists ordered rotation steps. | FYI | Presence only: Watch can’t see your account. |
| CHK-repoConfig | A workflow in a local clone with a headline’s workflow file name, address, string or action. | Review | |
| CHK-hostFile | A file or folder at a path a headline names. | Review | Exact path only. |
Affected cards are titled Affected: and the headline’s title, and list each place with its fix. When the headline’s incident pack has already opened a card for the same files, Watch keeps only the pack’s card.
Homebrew (BREW)
Section titled “Homebrew (BREW)”Watch never runs brew: each fix is a command for you to run. See Homebrew and Docker.
| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| BREW-1 | Homebrew settings in a brew.env file or a shell profile that redirect downloads or turn off its checks (“Homebrew settings redirect downloads or turn off its checks”): HOMEBREW_*_DOMAIN or *_GIT_REMOTE overrides, HOMEBREW_NO_VERIFY_ATTESTATIONS, HOMEBREW_NO_REQUIRE_TAP_TRUST, HOMEBREW_CURLRC and similar. |
Review | A company mirror shows here too: mark it as not a problem. |
| BREW-2 | A third-party tap whose Git remote isn’t the repository its name says, or whose remote changed since Watch first saw it. | Review | |
| BREW-3 | A tap or formula an incident pack names. | From the pack | |
| BREW-4 | A cask app with no quarantine flag, so Gatekeeper never checked it, that is unsigned or ad-hoc signed (“… skipped Gatekeeper and isn’t signed”). | Review | Fix: brew reinstall --cask, without --no-quarantine. |
Docker (DOCK)
Section titled “Docker (DOCK)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| DOCK-1 | A file in ~/.docker/cli-plugins (or another Docker plugin folder) that isn’t a link into Docker Desktop, OrbStack, Rancher Desktop, Podman or Homebrew and isn’t signed by an identified developer (“Docker CLI plugin from an unknown source”). |
Review | Docker’s own plugin names, which Docker’s docs have you install by hand, are never quarantined: the card asks you to compare the file’s SHA-256 with the plugin’s release page, or to reinstall Docker Desktop. Unknown plugins can be moved to Quarantine. |
| DOCK-2 | A registry password or token saved in ~/.docker/config.json (“Docker registry passwords are saved in plain text”). |
Review | Watch records which registries, never the password. |
| DOCK-3 | A remote registry in insecure-registries, or a Docker context, daemon host or DOCKER_HOST that uses plain tcp:// to another machine (“Docker pulls from … without TLS”, “Docker talks to … without TLS”). |
Review | Local addresses are skipped. |
Container images an incident names are PKG-10.
Running programs (PROC)
Section titled “Running programs (PROC)”From Watch mode, once a minute while Check running programs is on.
| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| PROC-1 | A program running from a path a published incident names, such as ~/.helper or ~/.config/sysmon, or a script at one of those paths run by python, node, sh or another interpreter (“Running now: a program … uses”). |
Act now | Only your own processes. When the same file also has a card, that card says “It’s running now”. |
| PROC-2 | An unsigned or ad-hoc-signed program running straight from a temporary folder or as a hidden file in your home folder (“Unsigned program running from …”). | Review |
Malware (MAL)
Section titled “Malware (MAL)”| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| MAL-1 | A crypto-wallet app in /Applications or ~/Applications (Ledger Live, Trezor Suite, Exodus, Electrum, Atomic Wallet, Coinomi, Guarda, Wasabi Wallet, Sparrow, Bitcoin Core, Keplr, Monero GUI) that isn’t signed by its developer, was changed after it was signed, or whose developer changed since Watch first saw it. |
Act now; Review for an app signed by a developer other than the one its maker publishes | Watch reads the app’s Info.plist and signature, never the wallet’s data. Fix: Watch moves the app to Quarantine; reinstall it from the vendor and move your funds from another device. |
| MAL-2 | Apple’s XProtect: “macOS won’t install XProtect updates by itself” when Install Security Responses and system files is off, or “XProtect’s rules are N days old” when they’re more than 60 days old. | Review when updates are off; FYI when the rules are old | Watch shows Apple’s rules; it doesn’t copy or use them. |
Network (NET)
Section titled “Network (NET)”These need the optional network extension. Watch looks at which program opens each outgoing connection and where it goes, never what’s sent. See Network alerts.
| ID | What it looks for | Tier | False alarms |
|---|---|---|---|
| NET-1 | Any program connecting to a host or IP address an incident pack names (“… connected to …, named in an incident”). | Act now | Common hosts such as github.com, registry.npmjs.org and pypi.org are never treated as incident addresses. |
| NET-5 | Shapes 2025 and 2026 attacks on developers used: a program asking for cloud credentials at a cloud metadata address; an install script or editor sending data to a request catcher such as webhook.site, or talking to Telegram’s bot API; an editor querying a Solana node; an unsigned program looking up the Mac’s public IP address; an install script making 8 or more GitHub API calls in 15 seconds. | Act now for cloud metadata when an install script started it; Review for the rest, including an editor reaching cloud metadata (Azure and AWS editor extensions check it) | Solana checks skip editors with a web3 extension installed. Mark a card as not a problem to stop it coming back. |
GitHub (GH)
Section titled “GitHub (GH)”Only when you connect GitHub. Watch reads your repositories and account read-only and matches everything on this Mac.
| ID | What it looks for | Tier |
|---|---|---|
| GH-1 | A known-malicious package version pinned in a repository’s lockfile on GitHub. | Act now when the repository has workflows that could have installed it; Review otherwise |
| GH-2 | Worm activity on your account: repositories named like s1ngularity’s or Shai-Hulud’s, a self-hosted runner named SHA1HULUD, or the worms’ workflows (“… used your GitHub account”). |
Act now |
| GH-3 | A private repository made public. | Review; Act now for three in a day, a name ending in -migration, or with other worm signs |
| GH-4 | A run of a workflow an incident names since the incident began (“A workflow from … ran in …”): treat it as a leak. | Act now |
| GH-5 | A workflow on GitHub that matches a headline’s workflow check (“On GitHub: …”). | Review |
| GH-6 | A public repository that runs workflows on a self-hosted runner. | Review |
| GH-7 | Webhooks over http, without certificate checks or to a request-capture service; webhooks without a secret. |
Review; FYI without a secret |
| GH-8 | Deploy keys that can push: added in the last 14 days, or older. | Review for recent ones; FYI otherwise |
| GH-9 | Outside collaborators who can push. | FYI |
| GH-10 | Unprotected default branches on public or shared repositories. | FYI |
| GH-11 | Actions settings that give workflows more than they need: a write token by default, workflows that approve pull requests, fork workflows without approval. | FYI |
| GH-12 | An SSH, signing or GPG key added to your account in the last 14 days that isn’t on this Mac. | Review |
The same workflow rules as your local clones (REPO-1 to REPO-7 and PKG-9) also run on GitHub’s copies, titled “On GitHub: …”.
Domains (DOM)
Section titled “Domains (DOM)”Only for the domains you add.
| ID | What it looks for | Tier |
|---|---|---|
| DOM-1 | The certificate has expired or expires within 14 days; or it expires within 30 days. | Review; FYI within 30 days |
| DOM-2 | The certificate chain uses a certificate authority a headline distrusts (“Replace the certificate for …”). | Review |
| DOM-3 | macOS doesn’t trust the certificate for the domain. | Review |
| DOM-4 | A CNAME points at a name that no longer exists, so someone could claim it (subdomain takeover). | Review |
| DOM-5 | No CAA record, so any certificate authority may issue for the domain. | FYI |
| DOM-6 | CAA records don’t allow the certificate authority that issued the current certificate. | FYI |
When a local TLS-inspecting proxy, such as Cloak Proxy or a company proxy, answers for a domain, Watch says so and doesn’t judge the certificate it saw.
Under review
Section titled “Under review”Under review cards come from Subcloak’s release scanner, not from a check on your Mac: “Under review: unusual change in …”, then “Reported to …” and, once a registry or public database confirms it, “Removed by …” at Act now. They have no check ID. The scanner is in a test period and doesn’t send verdicts to Macs yet.
Planned checks
Section titled “Planned checks”These checks are planned and aren’t in Cloak Watch 1.2.
| ID | Planned check |
|---|---|
| AGT-7 | An AI CLI started with --dangerously-skip-permissions, --yolo or --trust-all-tools by a package manager |
| MCP-4 | An MCP server the registry has taken down or flagged |
| NET-2 to NET-4 | Unsigned programs going online in general; AI traffic to unknown providers; secrets in prompts (Guard covers secrets in prompts today: see Guard rules) |