Repository navigation
• Independent security audit of 3.4.3 — source, signed binary, and how to verify your own copy #3761
Ranbo-codez
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Nobody asked for this and I am not affiliated with the project. Vorssaint kept coming up in places I read, always with some version of "it does thirty things, it's free, what's the catch?", so I spent a day checking rather than speculating. Posting it here because this is where the people deciding whether to install it actually are, and because every claim below can be checked against the source in this repo.
Disclosure: this was done with heavy help from an AI model (Claude Opus 5.5 Max Ultracode). I directed the work, it did the heavy lifting. Every finding carries a file reference or a reproducible command precisely so that it does not rest on my word or the model's.
Short version: nothing hidden, and several things are done better than in commercial utilities I have looked at.
Scope
a5da7633e09d0ebbf5b5607c0434ded28a239469(29 Oct 2026) — 706 Swift files, 313,427 lines, plus build scripts, CI workflows and the full 2,059-commit history.Vorssaint-3.4.3.dmg, 27,809,266 bytes, SHA-256d1d59435f949539968adcbc9aa2545a6b90610b753bf843d8113c85615a20396, from the GitHub release.Method, and why it is worth stating
I ran this twice. The first pass reached four wrong conclusions because it leaned on searching the source for strings.
Text search cannot establish that a capability is absent — an empty result means the pattern did not match, not that the behaviour is missing. So the second pass led with evidence that does not depend on spelling an API correctly:
nm -u), 4,376 symbols — what the compiled code can actually call.dlsym/dlopenstring, since dynamic resolution bypasses the import table.The corrections section at the end lists exactly what the first pass got wrong.
Does the shipped binary match this source?
Every independent comparison agreed.
Signature. The DMG satisfies the exact designated requirement the updater itself pins in
Sources/Vorssaint/Services/Update/UpdateInstallerSupport.swift:66, and the staged app satisfies the stricter one at line 123 that also pins the bundle identifier. Team ID in the certificate matches the constant in the source.Notarization.
spctlreturnsaccepted/source=Notarized Developer ID, and the stapled ticket validates offline.Entitlements. The shipped binary declares exactly the four in
Resources/Vorssaint.entitlements— Apple Events, camera, audio input, calendars — and nothing more. Nodisable-library-validation, noallow-unsigned-executable-memory, noget-task-allow. Hardened Runtime on.Network hosts. All 19 hosts in the compiled binary appear in the source. Zero extras.
Bundle contents. Four executables, all accounted for, and
Resources/now-playing.plis byte-identical to the file in this repo.Findings
pull_request_targetcode executionNothing rose to a vulnerability, and nothing warranted a private security report.
Keyboard input
This is the part users most deserve a straight answer about, and the part my first pass got wrong.
The app creates 31
CGEventtaps; 13 observekeyDown. That is inherent to the features — text expansion cannot match a trigger it cannot see, and a switcher cannot react to Command-Tab without watching modifiers — but it is worth stating plainly rather than leaving for someone to discover.What constrains it:
textSnippetsEnabled,superKeyEnabled,keyboardDebounceEnabledand so on are allfalsein the defaults map).FinderCutPaste.swift:391. None constructs a network request.TextSnippetSupport.swift:59), held in memory and cleared on app switch, click, or expansion. Never serialised.Privileged components
The fan-control daemon. Registered via
SMAppService.daemon(), so it must be approved in System Settings; zero entitlements. The XPC channel is mutually authenticated: the daemon sets a code-signing requirement on the listener itself atSources/FanControlHelper/main.swift:602, built fromFanControlXPC.swift:22-31, and the app pins the daemon in return atFanControlService.swift:285and:427. Because the requirement sits on the listener, non-matching clients are rejected before the delegate runs. Interface is five fan methods, levels clamped 0-100, one-second watchdog restoring automatic cooling.The sudoers rule (
SudoersSupport.swift:17-19):Scoped to one uid, root only, absolute path, both argument vectors fully specified so no other
pmsetinvocation matches. Written and validated withvisudo -cinShellSupport.swift:173, rolled back on failure. This is tighter than most NOPASSWD rules I have seen in shipping software.Root shell paths. Six run a command as root. Four are hardcoded constants. The kill path interpolates a value, but it is validated against
^[A-Za-z0-9: ]+$inKillProcessSupport.swift:53-58first, which excludes every shell metacharacter, so the interpolation is safe by construction.Third-party update feeds.
AppUpdateFeedSupport.swift:44-53fetchesSUFeedURLfrom other installed apps, behind a validator that requires HTTPS, rejects embedded credentials, raw IPs,.local/.localhostand non-ASCII labels, and drops feed configs carrying tokens or custom headers. Worth noting in any privacy discussion, since it means outbound traffic is not limited to the hardcoded host list.Screenshot sharing. The endpoint is overridable only for the
.devbundle identifier (ScreenshotSharingSupport.swift:84-95), so release builds cannot be redirected.Supply chain
uses:entries pinned to full commit SHAs.pull_request_targetworkflow performs no checkout, which the file documents deliberately. No PR title or body reaches a shell; the one user-influenced value is env-passed and regex-validated.Verifying your own copy
These need no admin password and change nothing:
The third should produce no output; the fourth should list exactly four entitlements.
Limits
One suggestion
shouldAcceptNewConnectioninSources/FanControlHelper/main.swift:566returnstrueunconditionally. That is correct, because the real gate issetConnectionCodeSigningRequirementon the listener at line 602 — but read in isolation the delegate looks unguarded, and I briefly concluded it was a vulnerability before spotting line 602. A one-line comment at 566 pointing to it would save the next reviewer the same detour.Where I got it wrong
Four wrong conclusions in the first pass, all from the same error — treating an empty search as proof of absence:
grep -Ewith an escaped pipe matches a literal character, not alternationsetCodeSigningRequirement; the API issetConnectionCodeSigningRequirementCGEventTapCreate; Swift spells itCGEvent.tapCreateThe second nearly produced a vulnerability report about something that was never broken. Listing them because a review whose misses are hidden cannot be judged, and because the lesson generalises: a search proves presence, never absence.
Bottom line
Nothing deceptive, and several things here are done properly rather than merely adequately. The sudoers rule is scoped more tightly than most shipping software manages. The updater verifies what it installs before swapping it in, rather than trusting the download. The fan daemon's XPC channel is authenticated in both directions. Those are the three places utilities in this category usually get it wrong, and none of them is wrong here.
What an audit cannot settle is the part that is not a code question. The app holds broad permissions and updates itself, so running it means trusting the maintainer on an ongoing basis rather than trusting one build that someone checked once. That is true of every auto-updating utility with this level of access, and it is a judgement each person makes for themselves. The findings above are meant to inform that judgement, not to replace it.
For what it is worth, the things that would strengthen it further are small: a reproducible build, and a comment at line 566. Neither is a defect.
Happy to expand on any part of this, and glad to be corrected on anything I have read wrong.
All reactions