Repository navigation
cask/audit: gktool scan rejects resource-only .bundle directories inside suites, causing false fails_gatekeeper_check failures #24246
Description
[This issue was prompted by Joe, drafted by Claude Code, and reviewed by Joe.]
brew doctor output
Warning: Some installed casks are deprecated or disabled.
You should find replacements for the following casks:
chromedriver
exist-db
oxygen-xml-editor
Warning: Some installed formulae are deprecated or disabled.
You should find replacements for the following formulae:
gemini-cli
icu4c@76
openjdk@11
postgresql@14
Warning: Broken symlinks were found:
/opt/homebrew/bin/gittower
Remove them with `brew cleanup`Verification
- I ran
brew updatetwice and am still able to reproduce my issue. - I have supplied my
brew doctoroutput above and it seems unrelated to my issue. (Theoxygen-xml-editorwarning is a consequence of the problem described here.) - This issue's title and/or description do not reference a single formula e.g.
brew install wget. (The bug is in the audit logic inHomebrew/brew;oxygen-xml-editoris used only as a concrete example.) - I did not use AI/LLM to create this issue, or I disclosed the tool and model used; I will answer maintainer questions myself without AI/LLM. (Disclosure: Claude Code with Claude Sonnet 5.5 was used to investigate and draft this issue. Every command and result below was run and verified locally.)
brew config output
HOMEBREW_VERSION: 7.0.9-86-geb56e2b
ORIGIN: https://github.com/Homebrew/brew
HEAD: eb56e2b1f748e63d02be18a7660eec4e4cf32af1
Last commit: 2 hours ago
Branch: main
Core tap HEAD: 3e60bfb7e813eaedb3ffc19532998cd9bd2fd750
Core tap last commit: 50 minutes ago
Core cask tap HEAD: ebc35ca0d4a978d28e3aefe03e3f5c137d8b0096
Core cask tap last commit: 19 minutes ago
HOMEBREW_PREFIX: /opt/homebrew
HOMEBREW_NO_ENV_HINTS: set
HOMEBREW_NO_UPGRADE_AUTO_UPDATES_CASKS: set
Homebrew Ruby: 4.0.7 => /opt/homebrew/Library/Homebrew/vendor/portable-ruby/4.0.7_1/bin/ruby
CPU: 11-core 64-bit arm_lobos
Clang: 21.0.0 build 2100
Git: 2.56.0 => /opt/homebrew/bin/git
Curl: 8.7.1 => /usr/bin/curl
macOS: 27.0.1-arm64
CLT: 27.0.0.0.1788430756
Xcode: 27.0
Metal Toolchain: N/A
Rosetta 2: falseWhat were you trying to do (and why)?
Understand why the oxygen-xml-editor cask was disabled in Homebrew/homebrew-cask#293579 with the reason "fails the macOS Gatekeeper check", because Oxygen XML Editor installs and launches normally on macOS with no Gatekeeper prompts.
#24227 (merged 2026-10-11) extended audit_signing to walk suite artifacts and run gktool scan on every bundle inside them. The day after, a batch of casks was disabled with fails_gatekeeper_check (for example Homebrew/homebrew-cask#293579, Homebrew/homebrew-cask#293595, Homebrew/homebrew-cask#293580 and Homebrew/homebrew-cask#293575). I wanted to find out whether the Oxygen failure reflects a real signing problem.
What happened (include all command output)?
With the disable! line removed from Casks/o/oxygen-xml-editor.rb in the local homebrew/cask tap, brew audit --cask --online oxygen-xml-editor fails:
==> Auditing signing
==> Verifying Gatekeeper status of /private/tmp/cask-audit20261013-6441-vkskoz/Oxygen XML Editor
Error: 1 problem in 1 cask detected.
audit for oxygen-xml-editor: failed
- Signature verification failed for Oxygen XML Editor/.install4j/Messages.bundle:
Scan completed, but failed because the software has been altered.
The homebrew/cask tap requires all casks to be signed and notarized by Apple.
Please contact the upstream developer and ask them to sign and notarize their software.
That is the only failure. Everything else the audit examines in this suite passes: all 7 .app bundles, .install4j/jre.bundle, and the downloaded DMG, which is signed with a Developer ID, notarized, and stapled (spctl --assess --type open --context context:primary-signature reports accepted, source=Notarized Developer ID).
.install4j/Messages.bundle is a resource-only bundle created by the install4j installer framework. It contains only localization files and has no Info.plist, no executable code and no signature:
$ find Messages.bundle
Messages.bundle/Contents
Messages.bundle/Contents/Resources
Messages.bundle/Contents/Resources/{de,en,fr,ja,nl,zh_CN}.lproj
Messages.bundle/Contents/Resources/{de,en,fr,ja,nl,zh_CN}.lproj/Localizable.strings
$ codesign -dvv Messages.bundle
Messages.bundle: bundle format unrecognized, invalid, or unsuitable
The result depends on the quarantine attribute that Homebrew sets on downloads. The same bundle scans cleanly without it:
$ gktool scan Messages.bundle # copied out of the DMG, quarantine removed
Scan completed and software is allowed by system policy. # exit 0
$ gktool scan Messages.bundle # same copy, com.apple.quarantine set
Scan completed, but failed because the software has been altered. # exit 70
So the "altered" message is not a sign of tampering or missing notarization: gktool cannot assess a bundle with no Info.plist or code once it is quarantined.
What did you expect to happen?
A suite whose apps, installer disk image and executables are all signed and notarized should not fail the signing audit because it also ships a data-only directory that happens to be named *.bundle. The audit should skip, or tolerate, bundles that contain nothing Gatekeeper could assess (no Info.plist and no Mach-O executables), rather than reporting them as "altered" and recommending that the cask be disabled.
Step-by-step reproduction instructions (running brew commands)
# 1. In the local homebrew/cask checkout, delete the `disable!` line from Casks/o/oxygen-xml-editor.rb
# (a cask disabled with :fails_gatekeeper_check has signing failures treated as expected, so nothing is reported).
cd "$(brew --repo homebrew/cask)"
sed -i '' '/disable!/d' Casks/o/oxygen-xml-editor.rb
# 2. Run the audit. It downloads the DMG (which Homebrew quarantines) and runs the signing audit.
brew audit --cask --online oxygen-xml-editor
# => Signature verification failed for Oxygen XML Editor/.install4j/Messages.bundle:
# Scan completed, but failed because the software has been altered.Or, without Homebrew's cask machinery, using the same DMG (https://www.oxygenxml.com/InstData/Editor/MacOSX/VM/oxygen-openjdk.dmg, build 2026091405, sha256 c444633b0ceb3d2d4d2cffcde68b33c7f7ead66eaa2594f29aacd827df23ea6c):
curl -LO https://www.oxygenxml.com/InstData/Editor/MacOSX/VM/oxygen-openjdk.dmg
hdiutil attach -nobrowse -readonly -mountpoint /tmp/oxy oxygen-openjdk.dmg
ditto "/tmp/oxy/Oxygen XML Editor/.install4j/Messages.bundle" /tmp/Messages.bundle
hdiutil detach /tmp/oxy
gktool scan /tmp/Messages.bundle # exit 0: allowed by system policy
find /tmp/Messages.bundle -exec xattr -w com.apple.quarantine "0081;6aa9a017;Homebrew Cask;" {} \;
gktool scan /tmp/Messages.bundle # exit 70: "failed because the software has been altered"Suggested direction
The audit already skips the folder itself because gktool rejects plain folders. The same reasoning could apply to bundle directories that have no Contents/Info.plist and no Mach-O executables: treat them as plain resources and do not scan them. If maintainers prefer a narrower change, a signing_audit_skiplist entry for this cask would also unblock it. In either case, the casks disabled solely because of this false positive could then be reinstated. I'm happy to open a PR for whichever approach you prefer.
Activity
cask/audit: skip resource-only bundles