[GH-ISSUE #4299] [Feature] Decode embedded launch and library constraints in inspect_signature #1704

Open
opened 2026-10-16 21:09:15 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 16, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4299

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Native analysis or decompilation

Current limitation

A code signature can embed launch constraints on the executable itself, its parent process and its responsible process, and a library constraint that limits which libraries the process may load; Apple's WWDC23 session on environment constraints says launch constraints are enforced starting in macOS 13.3 and library load constraints starting in macOS Sonoma. These constraints tell an analyst which processes may start a component and what it may load, which is key context for privileged helpers, XPC services and operating-system tools. inspect_signature does not report them at all. This request asks it to report each embedded constraint by kind, as a structured tree of the facts and operators Apple documents, without evaluating whether a launch would succeed.

An analyst examining a helper tool or a system binary wants to know "who is allowed to launch this?" and "what is this process allowed to load?". REA cannot answer either today.

Observations use REA 6.5.0 (npm rea-agents@6.5.0, tag rea-agents-6.5.0, commit 3b79af28e80785afce9f5998d8f03289402a1847) on macOS 27.0.1 (26A434) arm64 with Xcode 27.0 (27A266a). The cited source files are unchanged on main at d52a488.

Fixture: a clang binary signed with a self constraint (team-identifier plus validation-category $in [3, 6]), a parent constraint (signing-identifier com.example.parent) and a library constraint ($or of team-identifier and validation-category 1):

printf 'int main(void){return 0;}\n' > h.c && clang -o lc h.c
H='<?xml version="1.0" encoding="UTF-8"?><plist version="1.0">'
echo "$H<dict><key>team-identifier</key><string>ABCDE12345</string><key>validation-category</key><dict><key>\$in</key><array><integer>3</integer><integer>6</integer></array></dict></dict></plist>" > self.plist
echo "$H<dict><key>signing-identifier</key><string>com.example.parent</string></dict></plist>" > parent.plist
echo "$H<dict><key>\$or</key><dict><key>team-identifier</key><string>ABCDE12345</string><key>validation-category</key><integer>1</integer></dict></dict></plist>" > lib.plist
codesign -f -s - -i com.example.lc --launch-constraint-self self.plist --launch-constraint-parent parent.plist --library-constraint lib.plist ./lc
codesign -d --verbose=4 ./lc

codesign reports the constraints:

Launch Constraints:
	Has Self Launch Constraints
	Has Parent Launch Constraints
Library Load Constraints:
	Has Library Load Constraints

It then prints three dictionaries, each with the keys ccat, comp, reqs and vers, where reqs holds the supplied constraint. The release build's rea inspect-signature ./lc --json result contains no constraint information: the word "constraint" does not occur anywhere in it.

Stock macOS 27.0.1 binaries carry embedded constraints too:

  • /usr/libexec/transparency-sysdiagnose has only a parent constraint. It nests $or and $and over on-authorized-authapfs-volume, on-system-volume, signing-identifier com.apple.sysdiagnosed, validation-category 1, and an apple-internal fact that Apple's fact list does not include.
  • /usr/libexec/gputoolsserviced has self and parent constraints, with the facts developer-mode (not in Apple's list) and is-init-proc.
  • /usr/libexec/xpcproxy has a self constraint whose dictionary holds ccat 1, comp 1 and vers 1, with no reqs key.

Current behavior, source-cited at the release tag:

codesign's dictionary dump does not label which kind each dictionary belongs to. It prints the dictionaries after the summary in an order that matched self, parent, library on the fixtures, but no documentation states that the order is fixed. Kind attribution therefore needs a source that names the kind. One example: on the fixtures, the special-slot listing at --verbose=5 showed non-zero hashes at slots -8 and -9 (self, parent), -10 (responsible-only fixture) and -11 (library). That slot mapping is an observation from these fixtures, not something Apple documents.

Use cases

No response

Desired outcome and success criteria

inspect_signature adds a constraints section to its result for the same slice its other fields describe.

  • Presence by kind. For each of self, parent, responsible and library: present, absent, or unknown, with the reason when unknown. Absent is reported explicitly, not omitted.
  • Envelope. For each present constraint, the envelope fields codesign shows (ccat, comp, vers) with their raw values, labelled as undocumented. REA does not interpret them.
  • Constraint tree. The requirement dictionary (reqs) as a structured tree that keeps every key, operator and value with its property-list type. Nothing is coerced or dropped.
  • Fact annotations. Each fact key carries Apple's documented meaning when it is one of the documented facts (cdhash, entitlements, is-init-proc, is-sip-protected, launch-type, on-authorized-authapfs-volume, on-system-volume, signing-identifier, team-identifier, validation-category). Any other key (for example developer-mode or apple-internal) is kept and marked unrecognized.
  • Value annotations. validation-category values 1 to 6 and 10 carry Apple's table descriptions. Values 7, 8 and 9 are labelled as system-generated categories, not as invalid. launch-type 0 and 3 carry their documented meaning, and 1 and 2 are labelled reserved for the operating system.
  • Operator annotations. $and, $and-array, $or, $or-array, $in, $lt, $lte, $gt, $gte, $optional and $query are named. $optional records whether it sits inside $or/$or-array (true only if its contents are true) or elsewhere (also true when the value is indeterminate). $query operations are decoded into Apple's operation names (select key, select index, match string, and so on), and unknown operation codes are kept and marked unrecognized.
  • No evaluation. The result does not evaluate whether any process satisfies the constraints. Facts such as launch-type, is-init-proc and the volume facts depend on the launch context, which static inspection cannot observe. A limitation says so.
  • Kind attribution. Attribution comes from a source that identifies the kind (or from a single-kind case that codesign's summary makes unambiguous), never from print order alone. When the kind of a decoded dictionary cannot be established, its kind is unknown and the tree is still reported.

Acceptance criteria

  • For the fixture above (the three plists are Apple's documented examples adapted with $in, signing-identifier and $or), the result reports self, parent and library constraints as present and responsible as absent. Each tree equals its source plist key for key, and validation-category values 3, 6 and 1 carry Apple's descriptions.
  • For a copy signed only with --launch-constraint-responsible and a plist {launch-type: 3}, the result reports only a responsible constraint and annotates launch-type 3 as the Launch Services app-launch type.
  • For a copy signed with a self constraint that uses Apple's camera $query example ([[1, "com.apple.security.device.camera"], [5, true]]), the result names operation 1 as select key and operation 5 as match Boolean.
  • For a copy signed with a library constraint whose $or contains $optional, the result marks that $optional as being inside $or.
  • For stock /usr/libexec/transparency-sysdiagnose on macOS 27.0.1, the result reports exactly one constraint, of kind parent, with the nested $or/$and structure intact and apple-internal marked unrecognized rather than dropped.
  • For stock /usr/libexec/xpcproxy on macOS 27.0.1, the result reports a self constraint with envelope ccat 1 and no reqs tree, with a limitation that the envelope meaning is undocumented.
  • For an unmodified clang output and for stock /bin/ls, all four kinds are reported as absent.
  • No result claims that a launch or library load is allowed or denied, and the commands recorded in provenance only display signatures.

Expected interaction with targets

Read existing files or captured data

Target and scope details (optional)

Affected area

inspect_signature (MCP) and rea inspect-signature (CLI) in the native macOS provider: signature capture and parsing, the result schema, generated tool documentation, and tests with self-built constrained fixtures.

Non-goals

  • Evaluating whether a given parent, responsible process or library satisfies a constraint, or predicting launch outcomes.
  • Spawn constraints stored under the SpawnConstraint key of a launchd property list. These are not part of the code signature, and inspect_plist already returns that dictionary raw.
  • The other CodeDirectory posture facts (requested separately) and per-slice reporting for universal files (open PR #3583).
  • Constraints that the operating system applies through trust caches rather than the code signature.
  • Creating, modifying or validating constraint files.

Verification available so far

I can suggest a sample or manual check

Verification details (optional)

No response

Alternatives considered

No response

Possible direction

No response

References

Apple documentation (quoted from developer.apple.com):

  • Defining launch environment and library constraints: "The constraint dictionaries you create contain facts and operations." "The top level of a constraint dictionary is implicitly an $and operation that includes all of the facts and operations in the dictionary." It lists the ten facts named above, the validation-category table ("7, 8, or 9 These values aren't appropriate for validation-category facts in constraints as they represent categories of binaries that the system generates in certain restricted situations."), the launch-type values ("Launch type 0 is the default ... Starting in macOS 14.0, Launch Services sets launch type 3 when launching an executable to start an app ... Launch types 1 and 2 are reserved for use by the operating system."), the operator table including the context-dependent $optional rule ("Inside $or and $or-array operators, $optional is true only if the fact or operator it contains is true. In all other contexts ... $optional is true if the value of the fact or operator it contains is true, or when the value is indeterminate."), and the eleven entitlements-query operation codes.
  • Applying launch environment and library constraints: defines the self constraint ("The constraint applies to the executable that embeds the constraint in its code signature."), the parent and responsible constraints, and library constraints. It notes: "You don't embed spawn constraints for launchd daemons and agents into their executable file's code signature."
  • WWDC23 session 10266, Protect your Mac app with environment constraints (transcript): "Library load constraints and launchd plist constraints can be included in apps targeting any macOS version. They are enforced starting in macOS Sonoma. Launch constraints can be added to apps targeting macOS 13.3 or later and they are enforced starting in macOS 13.3."
  • man codesign on macOS 27.0.1 documents --launch-constraint-self, --launch-constraint-parent, --launch-constraint-responsible and --library-constraint for signing. It documents no display option for constraints; codesign -d prints the summary at --verbose=3 and the dictionaries at --verbose=4, as shown above. The page also says that, by default, constraints "are checked and errors are reported but their validity is not required. This default behavior allows developers to set contraints for newer OS versions", which is why unknown facts must be kept, not rejected.
  • The Apple pages do not document the signature slot, blob format or the ccat/comp/vers envelope. The slot numbers in Current limitation are fixture observations only.

Related REA history: no open or closed issue or PR mentions launch or library constraints (searched "launch constraint" and "library constraint"). Closed, unmerged PR #934 added other signing-posture facts but not constraints.

Originally created by @coygeek on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4299 ### Before submitting - [X] I searched the open and closed issues and pull requests for a related proposal. - [X] I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here. - [X] This request does not include credentials, private samples, or sensitive target data. ### Area Native analysis or decompilation ### Current limitation A code signature can embed launch constraints on the executable itself, its parent process and its responsible process, and a library constraint that limits which libraries the process may load; Apple's WWDC23 session on environment constraints says launch constraints are enforced starting in macOS 13.3 and library load constraints starting in macOS Sonoma. These constraints tell an analyst which processes may start a component and what it may load, which is key context for privileged helpers, XPC services and operating-system tools. `inspect_signature` does not report them at all. This request asks it to report each embedded constraint by kind, as a structured tree of the facts and operators Apple documents, without evaluating whether a launch would succeed. An analyst examining a helper tool or a system binary wants to know "who is allowed to launch this?" and "what is this process allowed to load?". REA cannot answer either today. Observations use REA 6.5.0 (npm `rea-agents@6.5.0`, tag `rea-agents-6.5.0`, commit `3b79af28e80785afce9f5998d8f03289402a1847`) on macOS 27.0.1 (26A434) arm64 with Xcode 27.0 (27A266a). The cited source files are unchanged on main at `d52a488`. Fixture: a `clang` binary signed with a self constraint (`team-identifier` plus `validation-category` `$in` `[3, 6]`), a parent constraint (`signing-identifier` `com.example.parent`) and a library constraint (`$or` of `team-identifier` and `validation-category` `1`): ```sh printf 'int main(void){return 0;}\n' > h.c && clang -o lc h.c H='<?xml version="1.0" encoding="UTF-8"?><plist version="1.0">' echo "$H<dict><key>team-identifier</key><string>ABCDE12345</string><key>validation-category</key><dict><key>\$in</key><array><integer>3</integer><integer>6</integer></array></dict></dict></plist>" > self.plist echo "$H<dict><key>signing-identifier</key><string>com.example.parent</string></dict></plist>" > parent.plist echo "$H<dict><key>\$or</key><dict><key>team-identifier</key><string>ABCDE12345</string><key>validation-category</key><integer>1</integer></dict></dict></plist>" > lib.plist codesign -f -s - -i com.example.lc --launch-constraint-self self.plist --launch-constraint-parent parent.plist --library-constraint lib.plist ./lc codesign -d --verbose=4 ./lc ``` `codesign` reports the constraints: ```text Launch Constraints: Has Self Launch Constraints Has Parent Launch Constraints Library Load Constraints: Has Library Load Constraints ``` It then prints three dictionaries, each with the keys `ccat`, `comp`, `reqs` and `vers`, where `reqs` holds the supplied constraint. The release build's `rea inspect-signature ./lc --json` result contains no constraint information: the word "constraint" does not occur anywhere in it. Stock macOS 27.0.1 binaries carry embedded constraints too: - `/usr/libexec/transparency-sysdiagnose` has only a parent constraint. It nests `$or` and `$and` over `on-authorized-authapfs-volume`, `on-system-volume`, `signing-identifier` `com.apple.sysdiagnosed`, `validation-category` `1`, and an `apple-internal` fact that Apple's fact list does not include. - `/usr/libexec/gputoolsserviced` has self and parent constraints, with the facts `developer-mode` (not in Apple's list) and `is-init-proc`. - `/usr/libexec/xpcproxy` has a self constraint whose dictionary holds `ccat` `1`, `comp` `1` and `vers` `1`, with no `reqs` key. Current behavior, source-cited at the release tag: - The provider already runs `codesign -d --verbose=4`, whose stderr includes the summary and the dumped dictionaries ([NativeMacOSProvider.ts L594-L598](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/src/native/NativeMacOSProvider.ts#L594-L598)). The parser keeps only `key=value` lines and the CodeDirectory line, so the constraint block is discarded ([codesign.ts L23-L35](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/src/native/parsers/codesign.ts#L23-L35)). - The result schema has no constraint field ([nativeInspection.ts L146-L160](https://github.com/morluto/rea/blob/3b79af28e80785afce9f5998d8f03289402a1847/src/domain/native/nativeInspection.ts#L146-L160)). codesign's dictionary dump does not label which kind each dictionary belongs to. It prints the dictionaries after the summary in an order that matched self, parent, library on the fixtures, but no documentation states that the order is fixed. Kind attribution therefore needs a source that names the kind. One example: on the fixtures, the special-slot listing at `--verbose=5` showed non-zero hashes at slots -8 and -9 (self, parent), -10 (responsible-only fixture) and -11 (library). That slot mapping is an observation from these fixtures, not something Apple documents. ### Use cases _No response_ ### Desired outcome and success criteria `inspect_signature` adds a constraints section to its result for the same slice its other fields describe. - **Presence by kind.** For each of `self`, `parent`, `responsible` and `library`: `present`, `absent`, or `unknown`, with the reason when unknown. Absent is reported explicitly, not omitted. - **Envelope.** For each present constraint, the envelope fields codesign shows (`ccat`, `comp`, `vers`) with their raw values, labelled as undocumented. REA does not interpret them. - **Constraint tree.** The requirement dictionary (`reqs`) as a structured tree that keeps every key, operator and value with its property-list type. Nothing is coerced or dropped. - **Fact annotations.** Each fact key carries Apple's documented meaning when it is one of the documented facts (`cdhash`, `entitlements`, `is-init-proc`, `is-sip-protected`, `launch-type`, `on-authorized-authapfs-volume`, `on-system-volume`, `signing-identifier`, `team-identifier`, `validation-category`). Any other key (for example `developer-mode` or `apple-internal`) is kept and marked `unrecognized`. - **Value annotations.** `validation-category` values 1 to 6 and 10 carry Apple's table descriptions. Values 7, 8 and 9 are labelled as system-generated categories, not as invalid. `launch-type` 0 and 3 carry their documented meaning, and 1 and 2 are labelled reserved for the operating system. - **Operator annotations.** `$and`, `$and-array`, `$or`, `$or-array`, `$in`, `$lt`, `$lte`, `$gt`, `$gte`, `$optional` and `$query` are named. `$optional` records whether it sits inside `$or`/`$or-array` (true only if its contents are true) or elsewhere (also true when the value is indeterminate). `$query` operations are decoded into Apple's operation names (select key, select index, match string, and so on), and unknown operation codes are kept and marked unrecognized. - **No evaluation.** The result does not evaluate whether any process satisfies the constraints. Facts such as `launch-type`, `is-init-proc` and the volume facts depend on the launch context, which static inspection cannot observe. A limitation says so. - **Kind attribution.** Attribution comes from a source that identifies the kind (or from a single-kind case that codesign's summary makes unambiguous), never from print order alone. When the kind of a decoded dictionary cannot be established, its kind is `unknown` and the tree is still reported. **Acceptance criteria** - [ ] For the fixture above (the three plists are Apple's documented examples adapted with `$in`, `signing-identifier` and `$or`), the result reports self, parent and library constraints as present and responsible as absent. Each tree equals its source plist key for key, and `validation-category` values `3`, `6` and `1` carry Apple's descriptions. - [ ] For a copy signed only with `--launch-constraint-responsible` and a plist `{launch-type: 3}`, the result reports only a responsible constraint and annotates `launch-type` `3` as the Launch Services app-launch type. - [ ] For a copy signed with a self constraint that uses Apple's camera `$query` example (`[[1, "com.apple.security.device.camera"], [5, true]]`), the result names operation 1 as select key and operation 5 as match Boolean. - [ ] For a copy signed with a library constraint whose `$or` contains `$optional`, the result marks that `$optional` as being inside `$or`. - [ ] For stock `/usr/libexec/transparency-sysdiagnose` on macOS 27.0.1, the result reports exactly one constraint, of kind parent, with the nested `$or`/`$and` structure intact and `apple-internal` marked unrecognized rather than dropped. - [ ] For stock `/usr/libexec/xpcproxy` on macOS 27.0.1, the result reports a self constraint with envelope `ccat` `1` and no `reqs` tree, with a limitation that the envelope meaning is undocumented. - [ ] For an unmodified `clang` output and for stock `/bin/ls`, all four kinds are reported as absent. - [ ] No result claims that a launch or library load is allowed or denied, and the commands recorded in provenance only display signatures. ### Expected interaction with targets Read existing files or captured data ### Target and scope details (optional) **Affected area** `inspect_signature` (MCP) and `rea inspect-signature` (CLI) in the native macOS provider: signature capture and parsing, the result schema, generated tool documentation, and tests with self-built constrained fixtures. **Non-goals** - Evaluating whether a given parent, responsible process or library satisfies a constraint, or predicting launch outcomes. - Spawn constraints stored under the `SpawnConstraint` key of a launchd property list. These are not part of the code signature, and `inspect_plist` already returns that dictionary raw. - The other CodeDirectory posture facts (requested separately) and per-slice reporting for universal files (open PR #3583). - Constraints that the operating system applies through trust caches rather than the code signature. - Creating, modifying or validating constraint files. ### Verification available so far I can suggest a sample or manual check ### Verification details (optional) _No response_ ### Alternatives considered _No response_ ### Possible direction _No response_ ### References Apple documentation (quoted from developer.apple.com): - [Defining launch environment and library constraints](https://developer.apple.com/documentation/security/defining-launch-environment-and-library-constraints): "The constraint dictionaries you create contain *facts* and *operations*." "The top level of a constraint dictionary is implicitly an `$and` operation that includes all of the facts and operations in the dictionary." It lists the ten facts named above, the `validation-category` table ("`7`, `8`, or `9` These values aren't appropriate for `validation-category` facts in constraints as they represent categories of binaries that the system generates in certain restricted situations."), the `launch-type` values ("Launch type 0 is the default ... Starting in macOS 14.0, Launch Services sets launch type 3 when launching an executable to start an app ... Launch types 1 and 2 are reserved for use by the operating system."), the operator table including the context-dependent `$optional` rule ("Inside `$or` and `$or-array` operators, `$optional` is true only if the fact or operator it contains is true. In all other contexts ... `$optional` is true if the value of the fact or operator it contains is true, or when the value is indeterminate."), and the eleven entitlements-query operation codes. - [Applying launch environment and library constraints](https://developer.apple.com/documentation/security/applying-launch-environment-and-library-constraints): defines the self constraint ("The constraint applies to the executable that embeds the constraint in its code signature."), the parent and responsible constraints, and library constraints. It notes: "You don't embed spawn constraints for `launchd` daemons and agents into their executable file's code signature." - [WWDC23 session 10266, Protect your Mac app with environment constraints](https://developer.apple.com/videos/play/wwdc2023/10266/) (transcript): "Library load constraints and launchd plist constraints can be included in apps targeting any macOS version. They are enforced starting in macOS Sonoma. Launch constraints can be added to apps targeting macOS 13.3 or later and they are enforced starting in macOS 13.3." - `man codesign` on macOS 27.0.1 documents `--launch-constraint-self`, `--launch-constraint-parent`, `--launch-constraint-responsible` and `--library-constraint` for signing. It documents no display option for constraints; `codesign -d` prints the summary at `--verbose=3` and the dictionaries at `--verbose=4`, as shown above. The page also says that, by default, constraints "are checked and errors are reported but their validity is not required. This default behavior allows developers to set contraints for newer OS versions", which is why unknown facts must be kept, not rejected. - The Apple pages do not document the signature slot, blob format or the `ccat`/`comp`/`vers` envelope. The slot numbers in Current limitation are fixture observations only. Related REA history: no open or closed issue or PR mentions launch or library constraints (searched "launch constraint" and "library constraint"). Closed, unmerged PR #934 added other signing-posture facts but not constraints.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1704
No description provided.