# 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. Nothing executes, so this is the part
you can safely run against something you don't trust at all.

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

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

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.
