# Static analysis
Source: https://docs.privacycommand.privacykey.org/static-analysis

What privacycommand extracts from a bundle without running it — the bulk of the report.

Static analysis reads the bundle on disk without executing it, so you can run it
against a bundle you don't trust.

It is also where most of the report comes from. Roughly forty detectors run over
a bundle; this page groups them by the question they answer.

## Identity — is this what it says it is?

**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**

This check reports 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**

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*.

| Axis | Where it comes from |
|---|---|
| **Requested** | Info.plist usage keys and the entitlements that declare them — the bullet above |
| **Granted** | What macOS has actually recorded, read from the system's TCC databases |
| **Used** | What a [monitored run](https://docs.privacycommand.privacykey.org/monitored-runs) observed. Camera, microphone and screen recording only; everything else reads `—` |

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.

> **Note**
>
> **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](https://docs.privacycommand.privacykey.org/privileged-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](https://docs.privacycommand.privacykey.org/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](#dylib-hijacking-surface) 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.

<a id="dylib-hijacking-surface" />

**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](https://docs.privacycommand.privacykey.org/batch-scan) triage table.

## Every finding is explained

privacycommand ships a Knowledge Base entry alongside each detector. When a
finding says the app has `com.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](https://docs.privacycommand.privacykey.org/develop/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:

```bash
codesign -dvv /Applications/SomeApp.app
codesign -d --entitlements :- /Applications/SomeApp.app
spctl --assess -vvv /Applications/SomeApp.app
```

For the rest it's a location rather than a command — `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](https://docs.privacycommand.privacykey.org/binary-and-network)**

Network call sites, and decompiling them.

**[Privacy labels](https://docs.privacycommand.privacykey.org/privacy-labels)**

Developer claims against binary contents.
