fix(macos): read AI tool versions from package.json to avoid Gatekeeper malware prompt#167
Open
swarit-stepsecurity wants to merge 1 commit into
Conversation
…er prompt The AI CLI/agent/framework detectors read each tool's version by executing it with --version. On macOS, launching a Node-based CLI boots Node, which dlopen's the tool's native .node addons during module init (before argv is even parsed); a quarantined addon such as merkle-tree-napi.darwin-arm64.node makes Gatekeeper pop an "Apple could not verify ... is free of malware" prompt at the end user — a security tool triggering a malware warning. Resolve the version statically from the npm package.json (npm's canonical, required version field) when the binary resolves into a node_modules tree, falling back to --version only for standalone (non-npm) binaries, which are typically notarized. Mirrors the static-first version resolution already used for IDEs in resolveDarwinVersion. Factors the node_modules root resolution out of resolveInstallPath into shared helpers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Users on macOS see a Gatekeeper dialog during a scan:
merkle-tree-napi.darwin-arm64.nodeis a native Node addon (a Mach-O dylib) bundled as a transitive dependency of one of the AI CLIs on the machine. A security tool triggering a malware warning is exactly the wrong impression.Root cause
The AI CLI / agent / framework detectors read each tool's version by executing it with
--version(aicli.gogetVersion,agent.gogetVersion,framework.gogetVersion). These AI CLIs are npm-installed Node programs. Launching one boots Node, whichdlopens the tool's native.nodeaddons during module init — before argv is even parsed, so the flag is irrelevant. When a quarantined addon (com.apple.quarantine, never cleared after npm download) is loaded, Gatekeeper on recent macOS assesses it and blocks with the popup.Merely reading a file's bytes never triggers this; only loading/executing does.
Fix
Move version detection to file-based (static-first): read the version from the npm
package.jsonwhen the binary resolves into anode_modulestree, and only fall back to executing--versionfor standalone (non-npm) binaries (which are typically notarized, so no prompt).package.json'sversionis npm's required, canonical field (semver) — authoritative, not a heuristic. Verified it matches--versionoutput for the affected tools (e.g.claude:package.json2.1.209==claude --version2.1.209 (Claude Code)).resolveDarwinVersion(which was added to dodge an analogous exec hazard — the "Coveo incident").node_modulesroot resolution already inresolveInstallPath(factored into sharednodePackageRoot/resolveRealPathhelpers) and the existingreadJSONVersionhelper. Handles both the Unix symlink layout and the Windows.cmd/.ps1shim layout.detectClaudeCoworkanddetectLMStudioAppalready read versions fromInfo.plist/registry, so they were already safe and are unchanged.Tests
TestVersionFromPackageJSON— unit coverage of the shared helper (npm package w/ and w/opackage.json, standalone binary, empty path).TestAICLIDetector_VersionFromPackageJSON/TestAgentDetector_VersionFromPackageJSON— end-to-end: the detected version comes frompackage.jsonand the exec fallback (stubbed with a deliberately different value) does not run.resolveInstallPathbehavior preserved).go build ./...,go vet ./internal/detector/, andgo test ./...all green.