[PR #934] [CLOSED] feat(native): report code-signing posture in inspect_signature #2511

Closed
opened 2026-10-17 21:07:00 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/934
Author: @kaoru0822-kitauji
Created: 10/7/2026
Status: ❌ Closed

Base: main ← Head: feat/signature-posture


📝 Commits (10+)

  • 25ca63f feat(native): report code-signing posture in inspect_signature
  • 37824e8 fix(native): verify nested code and keep ticket read failures local
  • 94243fa refactor(native): extract signature verification from inspect_signature
  • 73eb20a Merge remote-tracking branch 'origin/main' into feat/signature-posture
  • ce8e969 fix(native): require Apple origin for platform facets and keep cancellation
  • 4f46836 Merge remote-tracking branch 'origin/main' into feat/signature-posture
  • 949ab42 fix(native): flag nested-code paths that codesign split on newlines
  • 57e9118 Merge origin/main into feat/signature-posture
  • 423a89e Merge remote-tracking branch 'origin/main' into feat/signature-posture
  • a427ddf Merge origin/main into feat/signature-posture

📊 Changes

28 files changed (+3572 additions, -213 deletions)

View changed files

📝 .github/workflows/ci.yml (+3 -2)
📝 docs/testing.md (+14 -0)
📝 package.json (+3 -1)
➕ scripts/verify/apple/code-signature.mjs (+247 -0)
📝 skill-src/reverse-engineer-anything/references/native-and-artifacts.md (+6 -0)
➕ src/domain/native/codeSigningPosture.ts (+101 -0)
📝 src/domain/native/nativeInspection.ts (+12 -0)
➕ src/native/CodeSigningPolicy.test.ts (+245 -0)
➕ src/native/CodeSigningPolicy.ts (+395 -0)
➕ src/native/CodesignVerification.ts (+140 -0)
📝 src/native/NativeMacOSProvider.ts (+16 -208)
➕ src/native/NativeNestedCode.test.ts (+78 -0)
➕ src/native/NativeNestedCode.ts (+101 -0)
➕ src/native/NativeObservation.ts (+11 -0)
➕ src/native/NativeSignatureEntitlements.ts (+15 -0)
➕ src/native/NativeSignatureInspection.ts (+245 -0)
➕ src/native/NativeSignaturePosture.ts (+255 -0)
➕ src/native/NativeSignatureProbes.ts (+148 -0)
➕ src/native/NativeSignatureSlices.ts (+51 -0)
➕ src/native/NotarizationTicket.test.ts (+39 -0)

...and 8 more files

📄 Description

Summary

inspect_signature reduced the CodeDirectory to a single hardened_runtime boolean and never verified the signature. An analyst asking "can a library be planted in this process?", "will dyld honor DYLD_INSERT_LIBRARIES?", "can I attach a debugger?", or "is this bundle still validly sealed, and is it stapled?" had to run codesign by hand.

This adds the observed signing facts and derived security facets, each tied to the evidence behind it. The schema change is additive.

Change and scope

Observed

  • code_directory: the version, and flags decoded from their numeric value rather than codesign's display text (runtime, library-validation, restrict, kill, adhoc, linker-signed, …; unknown bits are kept in hex). Also code and special slot counts, location, hash type, platform identifier, runtime version and executable-segment flags.
  • sealed_resources: version, rules and files, or none.
  • verification: local codesign --verify --strict on the opened .app or Mach-O, with its status, exit code and diagnostics. codesign validates nested code concurrently, so its progress lines arrive in no fixed order. They are reported as a sorted validated_nested_code list, which keeps results deterministic.
  • stapled_ticket: whether a notarization ticket is stapled at Contents/CodeResources, with its sha256 and size. It is not-applicable for a bare Mach-O.

Derived (security_facets, basis: "derived", each with evidence naming the flags or entitlements used):

Facet States
library-validation enforced, disabled, not-enforced, unknown
dyld-environment-variables ignored, honored, unknown
debugger-attach allowed, blocked, unknown
executable-memory restricted, jit-allowed, unsigned-allowed, protection-disabled, unrestricted, unknown
app-sandbox sandboxed, not-sandboxed, unknown

Platform binaries and the explicit library-validation/restrict flags take precedence over entitlements. Facets are unknown when flags or the aggregate entitlements are unavailable, for example with mixed per-slice signing.

Not included

  • The stapled ticket's validity and Apple's notarization record are not checked, because that needs the network. Gatekeeper and spctl policy, revocation, SIP, AMFI and setuid policy are not checked either. Limitations say so.

Contract and boundary impact

  • CLI and MCP: the output schema gains code_directory, sealed_resources, verification, stapled_ticket and security_facets. Input and existing fields are unchanged.
  • Effects: one more local codesign run, and a read of Contents/CodeResources. There is no network access.
  • Generated metadata: catalog, skill (version 28) and conformance digests are regenerated.

Real outputs, trimmed:

Target Flags Verify Ticket Library validation DYLD_* Debugger
Fixture (ad hoc) adhoc valid absent not-enforced honored allowed
Raycast runtime valid present enforced ignored blocked
Brave kill,restrict,library-validation,runtime invalid: disallowed com.apple.FinderInfo xattr present enforced (flag) ignored (restrict) blocked
/usr/bin/ssh runtime, platform identifier 26 valid not-applicable enforced (platform) ignored blocked

Evidence and regression coverage

  • src/domain/codeSigningPosture.test.ts:
    • flag decoding, including unknown bits;
    • every facet branch: unsigned or ad hoc, hardened runtime, relaxing entitlements, explicit flags, platform binary, and missing flags or entitlements.
  • src/native/parsers/codesign.test.ts: CodeDirectory and sealed-resource parsing, plus the unsealed, unsigned and incomplete cases.
  • tests/boundary/providers/native/nativeSignaturePosture.test.ts:
    • a stapled ticket digest;
    • bundle-level verification;
    • failed strict verification kept as an observation;
    • a bare Mach-O, where the ticket is not applicable.
  • Real lane npm run verify:code-signature, added to the macOS CI job. It uses Command Line Tools only.
    • It re-signs copies of the clang-built fixture with real codesign: ad hoc, runtime, runtime with entitlements, and library,restrict. It asserts the decoded flags and facets.
    • /usr/bin/true must report a platform identifier.
    • A bundle with an added unsealed file must fail strict verification.
    • CLI and stdio MCP results must match.

Remaining proof gaps

  • No CI fixture has a genuinely stapled ticket, because stapling needs Apple notarization. Ticket presence is covered by unit tests and checked manually on Raycast and Discord.

Validation performed

  • npm run check:changed: passed.
  • npm run test:focused -- tests/boundary/providers/native/nativeProvider.test.ts tests/boundary/providers/native/nativeInventory.golden.test.ts tests/boundary/providers/native/nativeSignaturePosture.test.ts tests/boundary/cli/nativeSignatureCli.test.ts tests/boundary/mcp/toolSchemaValidity.test.ts tests/boundary/cli/productCatalog.test.ts tests/acceptance/contractPresentation.test.ts: 58 passed.
  • npm run docs:generate && npm run docs:check, npx oxfmt --check ., npm run lint: no new errors.
  • npm run verify:code-signature: {"ok":true,"mocked":false,"cli":true,"stdio_mcp":true,"variants":["adhoc","runtime","runtime+entitlements","library,restrict"],"platform_identifier":26,"tampered_verification":"invalid"}.

Compatibility, safety, and release

  • Breaking changes: none; the schema change is additive.
  • Real Hopper/Ghidra coverage: not applicable.
  • Real OS coverage: macOS 26.6.2 arm64 locally, and the macOS 14 CI job.
  • Security: read-only, with no network access and no process launch beyond codesign.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/934 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/7/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `feat/signature-posture` --- ### 📝 Commits (10+) - [`25ca63f`](https://github.com/morluto/rea/commit/25ca63f231c770893d9a8c16af510abb3230fe66) feat(native): report code-signing posture in inspect_signature - [`37824e8`](https://github.com/morluto/rea/commit/37824e8c46ee5b5eabaaef1b0ab71d0f458b011b) fix(native): verify nested code and keep ticket read failures local - [`94243fa`](https://github.com/morluto/rea/commit/94243fa5a1c84166304128c33ba8621c1c6785cc) refactor(native): extract signature verification from inspect_signature - [`73eb20a`](https://github.com/morluto/rea/commit/73eb20a4b974f47051bbc5ea0f3d158e85f78ff4) Merge remote-tracking branch 'origin/main' into feat/signature-posture - [`ce8e969`](https://github.com/morluto/rea/commit/ce8e969112e452b754808d450c9ed7a596aee526) fix(native): require Apple origin for platform facets and keep cancellation - [`4f46836`](https://github.com/morluto/rea/commit/4f4683617876f8512ac21132d8d2290562d0776b) Merge remote-tracking branch 'origin/main' into feat/signature-posture - [`949ab42`](https://github.com/morluto/rea/commit/949ab427fb670a3e3cf57e0aeb389729ad142cea) fix(native): flag nested-code paths that codesign split on newlines - [`57e9118`](https://github.com/morluto/rea/commit/57e91187cfcdddbcd9cf2d4e04e5b834a29b00d4) Merge origin/main into feat/signature-posture - [`423a89e`](https://github.com/morluto/rea/commit/423a89e7e4f7a6fa4d80bfeb1366eb75a8cc129b) Merge remote-tracking branch 'origin/main' into feat/signature-posture - [`a427ddf`](https://github.com/morluto/rea/commit/a427ddf797cde6ed7f2ce2ff58afa83d596ae7c3) Merge origin/main into feat/signature-posture ### 📊 Changes **28 files changed** (+3572 additions, -213 deletions) <details> <summary>View changed files</summary> 📝 `.github/workflows/ci.yml` (+3 -2) 📝 `docs/testing.md` (+14 -0) 📝 `package.json` (+3 -1) ➕ `scripts/verify/apple/code-signature.mjs` (+247 -0) 📝 `skill-src/reverse-engineer-anything/references/native-and-artifacts.md` (+6 -0) ➕ `src/domain/native/codeSigningPosture.ts` (+101 -0) 📝 `src/domain/native/nativeInspection.ts` (+12 -0) ➕ `src/native/CodeSigningPolicy.test.ts` (+245 -0) ➕ `src/native/CodeSigningPolicy.ts` (+395 -0) ➕ `src/native/CodesignVerification.ts` (+140 -0) 📝 `src/native/NativeMacOSProvider.ts` (+16 -208) ➕ `src/native/NativeNestedCode.test.ts` (+78 -0) ➕ `src/native/NativeNestedCode.ts` (+101 -0) ➕ `src/native/NativeObservation.ts` (+11 -0) ➕ `src/native/NativeSignatureEntitlements.ts` (+15 -0) ➕ `src/native/NativeSignatureInspection.ts` (+245 -0) ➕ `src/native/NativeSignaturePosture.ts` (+255 -0) ➕ `src/native/NativeSignatureProbes.ts` (+148 -0) ➕ `src/native/NativeSignatureSlices.ts` (+51 -0) ➕ `src/native/NotarizationTicket.test.ts` (+39 -0) _...and 8 more files_ </details> ### 📄 Description ## Summary `inspect_signature` reduced the CodeDirectory to a single `hardened_runtime` boolean and never verified the signature. An analyst asking "can a library be planted in this process?", "will dyld honor `DYLD_INSERT_LIBRARIES`?", "can I attach a debugger?", or "is this bundle still validly sealed, and is it stapled?" had to run `codesign` by hand. This adds the observed signing facts and derived security facets, each tied to the evidence behind it. The schema change is additive. ## Change and scope **Observed** - `code_directory`: the version, and flags decoded from their numeric value rather than codesign's display text (`runtime`, `library-validation`, `restrict`, `kill`, `adhoc`, `linker-signed`, …; unknown bits are kept in hex). Also code and special slot counts, location, hash type, platform identifier, runtime version and executable-segment flags. - `sealed_resources`: version, rules and files, or `none`. - `verification`: local `codesign --verify --strict` on the opened `.app` or Mach-O, with its status, exit code and diagnostics. codesign validates nested code concurrently, so its progress lines arrive in no fixed order. They are reported as a sorted `validated_nested_code` list, which keeps results deterministic. - `stapled_ticket`: whether a notarization ticket is stapled at `Contents/CodeResources`, with its sha256 and size. It is `not-applicable` for a bare Mach-O. **Derived** (`security_facets`, `basis: "derived"`, each with `evidence` naming the flags or entitlements used): | Facet | States | | --- | --- | | `library-validation` | `enforced`, `disabled`, `not-enforced`, `unknown` | | `dyld-environment-variables` | `ignored`, `honored`, `unknown` | | `debugger-attach` | `allowed`, `blocked`, `unknown` | | `executable-memory` | `restricted`, `jit-allowed`, `unsigned-allowed`, `protection-disabled`, `unrestricted`, `unknown` | | `app-sandbox` | `sandboxed`, `not-sandboxed`, `unknown` | Platform binaries and the explicit `library-validation`/`restrict` flags take precedence over entitlements. Facets are `unknown` when flags or the aggregate entitlements are unavailable, for example with mixed per-slice signing. **Not included** - The stapled ticket's validity and Apple's notarization record are not checked, because that needs the network. Gatekeeper and `spctl` policy, revocation, SIP, AMFI and setuid policy are not checked either. Limitations say so. ## Contract and boundary impact - **CLI and MCP:** the output schema gains `code_directory`, `sealed_resources`, `verification`, `stapled_ticket` and `security_facets`. Input and existing fields are unchanged. - **Effects:** one more local `codesign` run, and a read of `Contents/CodeResources`. There is no network access. - **Generated metadata:** catalog, skill (version 28) and conformance digests are regenerated. Real outputs, trimmed: | Target | Flags | Verify | Ticket | Library validation | DYLD_* | Debugger | | --- | --- | --- | --- | --- | --- | --- | | Fixture (ad hoc) | `adhoc` | valid | absent | not-enforced | honored | allowed | | Raycast | `runtime` | valid | present | enforced | ignored | blocked | | Brave | `kill,restrict,library-validation,runtime` | **invalid**: disallowed `com.apple.FinderInfo` xattr | present | enforced (flag) | ignored (`restrict`) | blocked | | `/usr/bin/ssh` | `runtime`, platform identifier 26 | valid | not-applicable | enforced (platform) | ignored | blocked | ## Evidence and regression coverage - `src/domain/codeSigningPosture.test.ts`: - flag decoding, including unknown bits; - every facet branch: unsigned or ad hoc, hardened runtime, relaxing entitlements, explicit flags, platform binary, and missing flags or entitlements. - `src/native/parsers/codesign.test.ts`: CodeDirectory and sealed-resource parsing, plus the unsealed, unsigned and incomplete cases. - `tests/boundary/providers/native/nativeSignaturePosture.test.ts`: - a stapled ticket digest; - bundle-level verification; - failed strict verification kept as an observation; - a bare Mach-O, where the ticket is not applicable. - **Real lane `npm run verify:code-signature`**, added to the macOS CI job. It uses Command Line Tools only. - It re-signs copies of the clang-built fixture with real `codesign`: ad hoc, `runtime`, `runtime` with entitlements, and `library,restrict`. It asserts the decoded flags and facets. - `/usr/bin/true` must report a platform identifier. - A bundle with an added unsealed file must fail strict verification. - CLI and stdio MCP results must match. **Remaining proof gaps** - No CI fixture has a genuinely stapled ticket, because stapling needs Apple notarization. Ticket presence is covered by unit tests and checked manually on Raycast and Discord. ## Validation performed - `npm run check:changed`: passed. - `npm run test:focused -- tests/boundary/providers/native/nativeProvider.test.ts tests/boundary/providers/native/nativeInventory.golden.test.ts tests/boundary/providers/native/nativeSignaturePosture.test.ts tests/boundary/cli/nativeSignatureCli.test.ts tests/boundary/mcp/toolSchemaValidity.test.ts tests/boundary/cli/productCatalog.test.ts tests/acceptance/contractPresentation.test.ts`: 58 passed. - `npm run docs:generate && npm run docs:check`, `npx oxfmt --check .`, `npm run lint`: no new errors. - `npm run verify:code-signature`: `{"ok":true,"mocked":false,"cli":true,"stdio_mcp":true,"variants":["adhoc","runtime","runtime+entitlements","library,restrict"],"platform_identifier":26,"tampered_verification":"invalid"}`. ## Compatibility, safety, and release - Breaking changes: none; the schema change is additive. - Real Hopper/Ghidra coverage: not applicable. - Real OS coverage: macOS 26.6.2 arm64 locally, and the macOS 14 CI job. - Security: read-only, with no network access and no process launch beyond `codesign`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:07:00 +00:00
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#2511
No description provided.