# Check Any Mac App’s Signature and Notarization From the Terminal

> How to read codesign, spctl and stapler output like a practitioner: Team ID, hardened runtime, entitlements, quarantine flags, and the red flags that separate a normal result from one worth a second look.

HisnLabs Security & Research Team · Published 2026-09-18
Canonical: https://hisnlabs.com/en/blog/macos-code-signature-notarization-terminal

Gatekeeper runs this check automatically the first time you open a downloaded app, and most of the time you never see the details — a dialog appears, you click through it, done. This lab is about seeing what Gatekeeper saw: which developer signed the app, whether that signature is intact, whether Apple’s notary service checked it, and what it is allowed to do once it runs. Every command here ships with Xcode’s command-line tools (`xcode-select --install` if you do not have them yet), and every one of them is read-only — you are inspecting, not changing, the app.

## The signature itself: codesign -dvvv

codesign is Apple’s tool for creating and inspecting code signatures. The `-d` flag displays information about the signed code at a path, and per its man page, "Increasing levels of verbosity produce more output" — so `-dvvv` (display, three levels of verbose) gets you the full picture in one command:

*Terminal — full signature detail*

```console
$ codesign -dvvv /Applications/Example.app
# example output, trimmed to the fields that matter
Executable=/Applications/Example.app/Contents/MacOS/Example
Identifier=com.example.app
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=... flags=0x10000(runtime) hashes=...
Signature size=4741
Authority=Developer ID Application: Example Software LLC (ABCDE12345)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Team Identifier=ABCDE12345
Runtime Version=14.0.0
```

Four fields to read every time. `Authority` is the certificate chain: a normal, external app should end in `Apple Root CA` by way of `Developer ID Certification Authority`, with the top line naming the developer. `Team Identifier` is Apple’s ten-character ID for the developer account — the value to compare across apps you believe come from the same company, since it does not change between their releases. `flags=0x10000(runtime)` means the hardened runtime is enabled, a set of additional restrictions (like resisting code injection into the process) that Apple requires for notarization. And the absence of any `Authority` line at all — just `Signature=adhoc` — means the app is unsigned or self-signed with no chain to Apple whatsoever.

## Does the signature still match the files: --verify --deep --strict

A signature is a promise about a specific set of bytes at signing time. `--verify` checks whether that promise still holds — per the man page, it confirms "that the code at those path(s) is signed, that the signature is valid, and that all sealed components are unaltered." Two extra flags matter for an app bundle, which is a directory full of nested resources, frameworks and helper executables, not a single file:

*Terminal — deep, strict verification*

```console
$ codesign --verify --deep --strict --verbose=2 /Applications/Example.app
/Applications/Example.app: valid on disk
/Applications/Example.app: satisfies its Designated Requirement
```

`--deep` matters because, per the man page, verification of nested content is by default "limited to a shallow investigation that may not detect changes to the nested code" — deep mode recursively verifies every embedded framework and helper tool, not just the outer bundle. `--strict` adds extra checks Apple considers important enough to not be on by default, including that any symlink inside the bundle "point[s] to sealed files inside its bundle," rejecting one that points outside the app or to something unsealed — a known trick for smuggling an unsigned payload into an otherwise legitimately signed bundle. If either check fails, you will see `code failed to satisfy specified code requirement` or a note naming exactly which nested item does not match what was originally sealed — read that line, it names the file.

## Reading the entitlements

Entitlements are the specific permissions an app’s signature grants it — camera access, the ability to reach the network unsandboxed, disabling library validation, and so on. `codesign -d --entitlements -` extracts them; per the man page, "Embedded entitlement data will be extracted likewise and written to" the given path, and `-` means standard output:

*Terminal — an app’s declared entitlements*

```console
$ codesign -d --entitlements - /Applications/Example.app
# example output, trimmed
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
```

Most entitlements are unremarkable and match what the app obviously needs — a video-calling app requesting camera access is not a finding. The one worth pausing on is `disable-library-validation`: it means the app will load code from outside its own signed bundle, which is a normal need for some plugin-based software and a wider door than most apps require. It is a detail to note, not an automatic red flag, and it is exactly the kind of detail you cannot see without asking.

## Did Apple’s notary service actually check it: spctl and stapler

A valid signature only proves an app has not been altered since a developer signed it — it says nothing about whether Apple has looked at it. That is what notarization adds. Apple’s own documentation describes the automated notary service as scanning "your software for malicious components, checks for code-signing issues, and returns the results to you quickly."  When it passes, "the notary service generates a ticket for you to staple to your software; the notary service also publishes that ticket online where Gatekeeper can find it."

`spctl --assess` is the practical way to ask Gatekeeper’s own policy engine for its verdict rather than inferring it yourself. Per its man page, `--assess` "perform[s] an assessment on the files given," and `-v` / `--verbose`, repeated for more detail, is described simply as requesting "more verbose output":

*Terminal — Gatekeeper’s own assessment*

```console
$ spctl --assess -vv /Applications/Example.app
/Applications/Example.app: accepted
source=Notarized Developer ID
```

`source=Notarized Developer ID` is the result you want to see — it means Gatekeeper found a valid Developer ID signature and a notarization ticket, whether that ticket is stapled to the app or was found online. A result of `source=Unnotarized Developer ID` means the app is signed but Apple has not (or not yet) notarized it, and a flat `rejected` means Gatekeeper would block it from launching in its default configuration.

To check the staple specifically, `xcrun stapler validate` looks for the ticket physically attached to the app rather than asking Gatekeeper to look it up online — useful for confirming an app will still assess correctly with no internet connection at all:

*Terminal — checking the stapled ticket*

```console
$ xcrun stapler validate /Applications/Example.app
Processing: /Applications/Example.app
The validate action worked!
```

## The quarantine flag: where the first-run check comes from

Apple’s Platform Security guide explains that "Gatekeeper also tracks the provenance of files written by downloaded software" and "requests user approval before opening downloaded software for the first time." The mechanism behind that is an extended attribute, `com.apple.quarantine`, that Safari, Mail and other apps attach to anything they save from the network. `xattr -l` lists it, and per the man page, this option "causes both the attribute names and corresponding values to be displayed":

*Terminal — checking whether a file is still marked as downloaded*

```console
$ xattr -l ~/Downloads/Example.dmg
com.apple.quarantine: 0081;65e1a2b3;Safari;
```

No output at all means the file carries no quarantine flag — either it was created locally, arrived by a path that does not set the attribute (some archive tools and package managers skip it), or the flag was manually removed with `xattr -d com.apple.quarantine`. That last case is worth knowing about for a different reason: it is a documented way people bypass Gatekeeper’s first-run check entirely, on files they trust or think they trust, and it is worth being deliberate about rather than pasting from an unfamiliar set of terminal instructions.

## A worked comparison: two apps claiming the same developer

A concrete way to use all of this together: say you have two copies of an app that both claim to be from the same company, one from the developer’s own site and one from a link someone sent you. Run `codesign -dvvv` on both and compare the `Team Identifier` line — it is a ten-character string tied to a specific Apple Developer account, and unlike a display name or a bundle identifier, it is not something a second party can casually reproduce without access to that account’s signing certificate. If the two copies show different Team Identifiers, you are not looking at two builds of the same app; you are looking at two different signers, one of whom is not who the download claimed. Follow that with `spctl --assess -vv` on the suspicious copy — a mismatched or absent notarization source on the copy that claims to be identical to a notarized original is the confirmation, not just a clue.

## Reading the whole picture, and the actual red flags

- No `Authority` chain at all, or a chain that does not end in Apple Root CA — the app has no accountable developer identity behind it.
- `codesign --verify` fails, especially with a message naming a specific nested file — something inside the bundle changed after it was signed.
- `spctl --assess` returns `rejected`, or the Team Identifier in `codesign -dvvv` does not match the developer you expect for that product.
- A quarantine flag that was clearly stripped on a file you did not download yourself, or that arrived through an unusual channel (a script, an email attachment renamed to look like something else).
- Entitlements that are wide open — full disk access, disabled library validation, unrestricted network access — on an app whose stated purpose does not obviously need them.

None of these checks look at what the app actually does once it is running and connected to the network — that is a different kind of question, answered by watching its traffic rather than its signature. A clean signature and a valid notarization ticket are a real, meaningful floor: they say Apple has seen this exact set of bytes and found no code-signing issues at the time of the check. They are the beginning of trusting an app, not the end of it.

## How FireAI and HisnLabs fit in

A clean signature and a stapled ticket say an app hasn’t been tampered with since Apple checked it — they say nothing about what it connects to afterward, which is the question FireAI’s per-app rules and on-device review are built to keep answering, signature by signature, connection by connection.

FireAI is HisnLabs’ own product: an on-device AI firewall for Mac. It shows every connection your apps make, in plain language, and lets you decide what leaves your Mac — its AI runs locally, so your traffic is never sent to us or anyone else. HisnLabs’ security research team is the group that keeps that decision-making accurate: cataloguing which domains are ordinary telemetry versus a real product, tracking the country and network behind a connection, and training the on-device model (its FireAI Pilot feature) on real traffic patterns, all without any of it leaving your Mac.

You can read the technical decisions behind it, or try FireAI for 17 days, at [FireAI, by HisnLabs](https://hisnlabs.com/fireai/en/download).

## Sources

- [codesign(1) man page (Xcode man pages mirror)](https://keith.github.io/xcode-man-pages/codesign.1.html)
- [spctl(8) man page (Xcode man pages mirror)](https://keith.github.io/xcode-man-pages/spctl.8.html)
- [Apple Developer: Notarizing macOS software before distribution](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution)
- [Apple Platform Security: Gatekeeper and runtime protection](https://support.apple.com/guide/security/gatekeeper-and-runtime-protection-sec5599b66df/web)
- [xattr(1) man page (Xcode man pages mirror)](https://keith.github.io/xcode-man-pages/xattr.1.html)
