Identity — is this what it says it is?
Code signing
Code signing
Full chain validation: Developer ID, the signing Team ID, hardened-runtime
flags, and the
spctl assessment. The 10-character Team ID is expanded to
the developer’s registered name, so A1B2C3D4E5 becomes a company you can
recognise.Notarization
Notarization
A deep dive rather than a yes/no: whether the ticket is stapled, what
spctl says about it, and the SHA-256 of the bundle. Every relaxation is
reported with the entitlement or flag responsible.Provenance
Provenance
Where the bundle came from — quarantine attributes, Mac App Store receipt
if present, and the update mechanism the app carries (Sparkle, its own
updater, or none).
Capability — what is it allowed to do?
- Entitlements — the full list, with the sandbox and hardened-runtime exceptions called out rather than buried.
- TCC — which privacy-protected resources the app declares an interest in, and the usage-description strings it will show you when it asks.
- Privacy Manifest — Apple’s declared manifest, cross-checked against what the binary actually links. A manifest that omits an API the binary calls is more interesting than either fact alone.
- App extensions and helpers — every embedded extension, XPC service, and helper binary, each analysed rather than counted.
Requested, granted, used
Declaring an interest is only the first of three questions, and the Static tab crosses all three in one table — Permissions: requested vs granted vs used.
The gaps are the point. A capability requested and granted but never touched
across a run you drove properly is worth a question; one macOS has granted that
the app never declared a reason for is worth a larger one.
The granted column needs Full Disk Access — privacycommand’s own, not the
inspected app’s. macOS protects the TCC databases, so a copy of
privacycommand without FDA can’t read them. It doesn’t guess: the column
reads
unknown and the table shows a card with a button into the right
System Settings pane. Grant it and reopen the app.This is a TCC grant, not root — the helper is a separate
thing and won’t help here. Four things go missing without FDA, all of them in
this table: the granted column; any row that exists only because macOS
recorded a grant — including the granted-but-never-declared case above, which
has no row at all when the grant can’t be read; the System access granted
list below the table, which is where the inspected app’s own Full Disk Access,
Accessibility, Screen Recording or Input Monitoring grants would appear; and
the Verdict column, which collapses to Grant unknown on every row.That last one costs more than it looks. Used in binary, not declared — a
warning that needs no TCC data at all, since it comes from the binary — is
computed after the unknown-grant check and never reached, so the warning and
the row ordering that surfaces it both disappear. The rest of the static
report is unaffected.Content — what does it ship?
- SDK fingerprints — analytics, advertising, and attribution SDKs matched against a fingerprint database. See Telemetry.
- Hard-coded domains and URLs — hosts sitting in the binary and in bundled resources.
- URL schemes and document types — what the app registers to handle, which is also what other apps can use to poke it.
- Frameworks and rpaths — what it links, where it looks, and whether the search paths widen the dylib hijacking surface.
- Embedded assets and resources — including scripts and databases shipped inside the bundle.
- Secrets and licence keys — names of credential-shaped strings. It reports that a key-shaped thing exists and what it appears to be for; it does not print the value.
- Feature flags and trial state —
isPro,isTrial,subscription_status,experiment_id, and the platform-specific switches behind them (LaunchDarkly, Optimizely, Firebase Remote Config, PostHog, Statsig, Unleash).
Risk — what should worry you?
Anti-analysis signals. Checks a binary can make to notice it’s being inspected — debugger detection, VM detection, integrity checks. Present in plenty of legitimate DRM, so this is a signal rather than a verdict. Dylib hijacking surface. Where a writable or under-specified library search path would let an attacker land code inside the app’s process. Behaviour analysis. The detectors above produce signals; a behaviour pass turns combinations of them into findings with a risk tier, which is what drives the summary and the batch scan triage table.Every finding is explained
privacycommand ships a Knowledge Base entry alongside each detector. When a finding says the app hascom.apple.security.cs.disable-library-validation,
you get a plain-English explanation of what that entitlement relaxes and why an
app might legitimately want it — not just the raw key.
This is a deliberate contract: a new detector is not complete without its
Knowledge Base entry. If you’re adding one, see
Contributing.
Showing your work
Fourteen of the risk contributors carry a show source button — the magnifying glass next to the row on the risk-score breakdown. It opens a popover naming where that evidence actually came from. For the signing-related ones that’s a command you can re-run yourself:Contents/Info.plist,
Contents/MacOS/<executable>, or the Files or Network tab for anything a
monitored run produced. The popover text is selectable, and where there’s a
file behind it, Reveal in Finder takes you there. Contributors outside those
fourteen have no button — for those, the Knowledge Base entry above is the
explanation.
The Executive Summary rows have a one-click copy, but what it copies is a
Markdown summary of the finding — severity, message, evidence, Knowledge Base
id — not a command.
Next
The binary summary
Network call sites, and decompiling them.
Privacy labels
Developer claims against binary contents.