Skip to content

fix(mac): harden CUA helper distribution integrity - #2030

Merged
yyhhyyyyyy merged 11 commits into
devfrom
fix/macos-cua-release-integrity
Jul 26, 2026
Merged

fix(mac): harden CUA helper distribution integrity#2030
yyhhyyyyyy merged 11 commits into
devfrom
fix/macos-cua-release-integrity

Conversation

@yyhhyyyyyy

@yyhhyyyyyy yyhhyyyyyy commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fix the macOS clean-install Gatekeeper failure caused by the bundled CUA helper.

The failure required two conditions:

  1. The upstream universal CUA executable contained absolute Xcode build-machine LC_RPATH entries.
  2. electron-builder re-signed the already customized helper with the main application's inherited entitlements, including disable-library-validation.

Together, these caused syspolicy_check distribution to report a fatal error even though ordinary codesign, stapler, and spctl checks passed.

Changes

  • Remove disallowed build-machine RPATHs before signing.
  • Sanitize universal Mach-O slices independently when their RPATHs differ.
  • Reject unexpected non-system linked libraries and remaining invalid RPATHs.
  • Model CUA signing through the existing package-purpose contract.
  • Preserve both Apple ID and keychain-profile distribution signing modes.
  • Sign the CUA helper once with its dedicated entitlement allowlist.
  • Prevent electron-builder from re-signing the managed helper subtree.
  • Remove codesign --deep from signing while retaining strict deep verification.
  • Verify the final helper's:
    • Developer ID authority
    • Team ID
    • secure timestamp
    • hardened runtime
    • bundle identifier
    • exact entitlements
    • Mach-O load paths
  • Run syspolicy_check distribution against the final application.
  • Extract the staged macOS updater ZIP and apply the same application and CUA checks to its actual DeepChat.app.
  • Record CUA and updater ZIP verification as fail-closed manifest and release-index evidence.
  • Harden temporary signing-keychain cleanup and retain useful, redacted security diagnostics.
  • Reject unsafe ZIP paths, escaping helper symlinks, unsupported file types, and missing entitlements.

Summary by CodeRabbit

  • Bug Fixes

    • Improved macOS distribution integrity checks to prevent Gatekeeper failures on clean installs.
    • Added stronger verification for the bundled Computer Use helper, including signature/Team ID, entitlements, and allowed runtime load paths.
    • Hardened updater ZIP handling by validating safe archive contents and verifying both the updater payload and the final macOS app with distribution checks.
  • Release Process

    • Updated macOS release metadata schema/versioning and centralized macOS verification evidence generation.
    • Improved signing/notarization flow to support purpose-specific signing behavior.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 71228ce3-9ae3-4f6b-b522-f995b8acac35

📥 Commits

Reviewing files that changed from the base of the PR and between 9e2b046 and ef2a6bd.

📒 Files selected for processing (18)
  • docs/issues/cua-macos-distribution-integrity/spec.md
  • scripts/apple-notarization.js
  • scripts/build-cua-plugin-runtime.mjs
  • scripts/ci/assemble-release.mjs
  • scripts/ci/package-contract.mjs
  • scripts/ci/package-manifest.mjs
  • scripts/ci/verify-cua-macos-helper.mjs
  • scripts/ci/verify-release-assets.mjs
  • scripts/cua-macos-contract.mjs
  • scripts/notarize-dmg.js
  • scripts/sign-cua-helper.mjs
  • test/main/scripts/buildCuaPluginRuntime.test.ts
  • test/main/scripts/cuaMacosContract.test.ts
  • test/main/scripts/notarizeDmg.test.ts
  • test/main/scripts/packageContract.test.ts
  • test/main/scripts/releaseAssembly.test.ts
  • test/main/scripts/signCuaHelper.test.ts
  • test/main/scripts/verifyCuaMacosHelper.test.ts
🚧 Files skipped from review as they are similar to previous changes (15)
  • test/main/scripts/verifyCuaMacosHelper.test.ts
  • test/main/scripts/cuaMacosContract.test.ts
  • scripts/ci/package-contract.mjs
  • scripts/ci/verify-release-assets.mjs
  • test/main/scripts/signCuaHelper.test.ts
  • scripts/apple-notarization.js
  • scripts/cua-macos-contract.mjs
  • docs/issues/cua-macos-distribution-integrity/spec.md
  • test/main/scripts/buildCuaPluginRuntime.test.ts
  • test/main/scripts/packageContract.test.ts
  • scripts/ci/verify-cua-macos-helper.mjs
  • scripts/ci/package-manifest.mjs
  • test/main/scripts/releaseAssembly.test.ts
  • scripts/sign-cua-helper.mjs
  • scripts/build-cua-plugin-runtime.mjs

📝 Walkthrough

Walkthrough

The PR adds purpose-aware CUA macOS signing, Darwin load-path sanitation, nested helper verification, updater ZIP validation, distribution-policy checks, and schema-versioned release evidence for macOS packaging.

Changes

CUA macOS distribution integrity

Layer / File(s) Summary
Signing contracts and purpose resolution
scripts/cua-macos-contract.mjs, scripts/sign-cua-helper.mjs, scripts/plugin.mjs, scripts/apple-notarization.js, scripts/macos-release-contract.mjs, scripts/notarize-dmg.js
Adds Darwin load-path and entitlement contracts, explicit signing-purpose validation, structured signing results, keychain cleanup handling, notarization environment validation, labeled Team ID validation, and --purpose forwarding.
Runtime sanitation and helper signing
scripts/build-cua-plugin-runtime.mjs, electron-builder.yml, .github/workflows/_package-macos.yml, test/main/scripts/buildCuaPluginRuntime.test.ts, test/main/scripts/cuaMacosContract.test.ts, test/main/plugin/pluginService.test.ts, test/main/scripts/packageWorkflow.test.ts
Sanitizes disallowed RPATHs across architectures, signs helpers with the selected purpose, prevents electron-builder from signing the managed helper subtree, and passes purpose and architecture through the macOS workflow.
Final helper and updater verification
scripts/ci/verify-cua-macos-helper.mjs, scripts/ci/package-manifest.mjs, test/main/scripts/packageContract.test.ts, test/main/scripts/verifyCuaMacosHelper.test.ts, test/main/scripts/notarizeDmg.test.ts
Verifies helper signatures, entitlements, symlink boundaries, Mach-O paths, updater ZIP entries, extracted applications, and syspolicy_check distribution results.
Versioned release evidence and gates
scripts/ci/package-contract.mjs, scripts/ci/assemble-release.mjs, scripts/ci/verify-release-assets.mjs, scripts/ci/classify-package-impact.mjs, test/main/scripts/releaseAssembly.test.ts, docs/architecture/ci-release-packaging/spec.md, docs/issues/cua-macos-distribution-integrity/spec.md
Raises manifest and release-index schemas to version 2, derives macOS distribution checks centrally, requires helper and updater ZIP evidence, expands macOS impact classification, and documents the verification boundary and validation requirements.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MacOSWorkflow
  participant plugin.mjs
  participant build-cua-plugin-runtime.mjs
  participant sign-cua-helper.mjs
  participant package-manifest
  participant release-index
  MacOSWorkflow->>plugin.mjs: bundle CUA with PACKAGE_PURPOSE and TARGET_ARCH
  plugin.mjs->>build-cua-plugin-runtime.mjs: forward --purpose
  build-cua-plugin-runtime.mjs->>sign-cua-helper.mjs: sanitize and sign helper
  package-manifest->>package-manifest: verify helper, app, DMG, and updater ZIP
  package-manifest->>release-index: record macOS distribution checks
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: hardening macOS CUA helper distribution integrity.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/macos-cua-release-integrity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/apple-notarization.js (1)

16-21: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Error message assumes a single caller.

validateAppleTeamId is now also invoked from scripts/ci/verify-cua-macos-helper.mjs and scripts/ci/package-manifest.mjs with a teamId option that may not originate from DEEPCHAT_APPLE_NOTARY_TEAM_ID. Including the received value (or an optional label) makes CI failures diagnosable.

♻️ Proposed change
-export function validateAppleTeamId(teamId) {
-  if (!APPLE_TEAM_ID_PATTERN.test(teamId)) {
-    throw new Error('DEEPCHAT_APPLE_NOTARY_TEAM_ID must be a 10-character Apple team ID')
-  }
+export function validateAppleTeamId(teamId, label = 'DEEPCHAT_APPLE_NOTARY_TEAM_ID') {
+  if (typeof teamId !== 'string' || !APPLE_TEAM_ID_PATTERN.test(teamId)) {
+    throw new Error(`${label} must be a 10-character Apple team ID`)
+  }
   return teamId
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/apple-notarization.js` around lines 16 - 21, Update
validateAppleTeamId to accept an optional label or otherwise include the
received teamId in its validation error, rather than always referencing
DEEPCHAT_APPLE_NOTARY_TEAM_ID. Preserve the existing validation behavior and
return value while making failures from verify-cua-macos-helper.mjs and
package-manifest.mjs identify the relevant input.
🧹 Nitpick comments (9)
scripts/ci/package-manifest.mjs (1)

195-196: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Injected runCommand is not propagated to the nested verifiers.

verifyMacZipDistribution accepts a runCommand override and uses it for unzip/ditto, but calls verifyCuaMacHelper/verifyMacApp with only { teamId }, so those fall back to the real execFileAsync. Tests hide this by injecting the verifiers themselves; propagating keeps the seam consistent.

♻️ Proposed change
-    await verifyCuaMacHelper(extractedAppPath, { teamId })
-    await verifyMacApp(extractedAppPath, { teamId })
+    await verifyCuaMacHelper(extractedAppPath, { teamId, runCommand })
+    await verifyMacApp(extractedAppPath, { teamId, runCommand })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/package-manifest.mjs` around lines 195 - 196, Update
verifyMacZipDistribution so its calls to verifyCuaMacHelper and verifyMacApp
pass through the injected runCommand alongside teamId, ensuring nested
verification uses the same command runner override.
test/main/scripts/buildCuaPluginRuntime.test.ts (2)

26-30: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Type the injected callbacks with their real arguments.

The implementation always calls inspectExecutable(executable) and inspectArchitectures(executable), but the declared type is zero-arg — which is why the test at Line 178 has to widen it to (targetPath?: string). Declaring the parameter keeps the test double honest about the contract.

♻️ Proposed change
-        inspectExecutable: () => {
+        inspectExecutable: (targetPath: string) => {
           rpaths: string[]
           linkedLibraries: string[]
         }
-        inspectArchitectures: () => string[]
+        inspectArchitectures: (targetPath: string) => string[]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/main/scripts/buildCuaPluginRuntime.test.ts` around lines 26 - 30, Update
the injected callback types in the test setup so inspectExecutable and
inspectArchitectures each accept the executable path argument used by the
implementation, matching the production contract and eliminating the need for
the later type widening near the affected test.

157-225: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No coverage for the architecture-parity guard.

The universal path throws CUA helper architecture set changed during sanitation when lipo -create loses a slice, and the spec lists per-slice rebuild integrity as a fail-closed boundary. A case where inspectArchitectures returns ['x86_64','arm64'] for the original and ['arm64'] for the rebuilt binary would lock that in.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/main/scripts/buildCuaPluginRuntime.test.ts` around lines 157 - 225, Add
a test alongside the asymmetric RPATH case for the universal rebuild path where
inspectArchitectures returns both x86_64 and arm64 for the original executable
but only arm64 for rebuiltExecutable. Assert enforceDarwinLoadPathContract
throws “CUA helper architecture set changed during sanitation” and preserves the
fail-closed architecture-parity guard.
scripts/build-cua-plugin-runtime.mjs (1)

486-506: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

No ensureTool guard for install_name_tool / lipo.

Other Darwin steps (validateDarwinArchitecture, signDarwinHelper) call ensureTool before use, so a missing/unusable command tool yields a clear error. The sanitation path invokes /usr/bin/install_name_tool and /usr/bin/lipo directly; on a runner without Command Line Tools this fails with a raw spawn error mid-mutation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/build-cua-plugin-runtime.mjs` around lines 486 - 506, Update the
Darwin sanitation flow around enforceThinDarwinLoadPathContract and the
corresponding lipo usage to call ensureTool for install_name_tool and lipo
before any mutation or invocation. Reuse the existing tool-validation pattern
from validateDarwinArchitecture and signDarwinHelper, ensuring missing or
unusable tools fail clearly before processing begins.
scripts/ci/verify-release-assets.mjs (1)

115-123: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the darwin required-check list into the shared contract.

This list is now duplicated verbatim in scripts/ci/assemble-release.mjs (lines 58-66) and in scripts/ci/package-manifest.mjs's emitted checks. A single exported constant in scripts/ci/package-contract.mjs would keep producer and verifier from drifting the next time a macOS check is added.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/verify-release-assets.mjs` around lines 115 - 123, Move the shared
macOS required-check names from the local logic around requiredChecks into an
exported constant in package-contract.mjs. Update verify-release-assets.mjs,
assemble-release.mjs, and package-manifest.mjs to import and reuse that
constant, preserving the existing check order and platform-specific behavior.
test/main/scripts/releaseAssembly.test.ts (1)

207-225: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Restore release-index.json in a finally.

If the rejects.toThrow assertion fails, line 224 never runs and the tampered index leaks into any later assertion in this test body.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/main/scripts/releaseAssembly.test.ts` around lines 207 - 225, Wrap the
tampered release-index verification in a try/finally block, keeping the
rejects.toThrow assertion in the try and moving the writeFile call that restores
originalReleaseIndex into finally. Ensure release-index.json is restored even
when verifyReleaseAssets or the assertion fails.
test/main/scripts/packageContract.test.ts (1)

396-466: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Guard extractionRoot before asserting cleanup.

await expect(lstat(extractionRoot)).rejects.toThrow() passes trivially if extractionRoot is still '' (i.e. ditto was never invoked), so the cleanup assertion can silently become a no-op. Add expect(extractionRoot).not.toBe('') before each cleanup check.

♻️ Proposed tweak
     const extractedAppPath = path.join(extractionRoot, 'DeepChat.app')
+    expect(extractionRoot).not.toBe('')
     expect(verifyCuaMacHelper).toHaveBeenCalledWith(extractedAppPath, {
     expect(verifyMacApp).not.toHaveBeenCalled()
+    expect(extractionRoot).not.toBe('')
     await expect(lstat(extractionRoot)).rejects.toThrow()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/main/scripts/packageContract.test.ts` around lines 396 - 466, Guard both
cleanup assertions in the tests around verifyMacZipDistribution by asserting
extractionRoot is non-empty before calling lstat. Add
expect(extractionRoot).not.toBe('') in both test cases after the distribution
verification and before the rejects-toThrow cleanup check, ensuring ditto
actually ran before cleanup is validated.
scripts/sign-cua-helper.mjs (2)

31-79: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Consider redacting error.message too, not just error.stderr.

Node's execFile rejection message embeds the full command line (Command failed: /usr/bin/security create-keychain -p <password> …). You correctly drop it by constructing a fresh Error, but nothing prevents a future change (or { cause: error }) from reattaching it. Running the raw message through redactSensitiveSecurityDiagnostic before discarding/including it would make the redaction robust by construction.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/sign-cua-helper.mjs` around lines 31 - 79, The error sanitization in
formatSensitiveSecurityError only processes error.stderr; update it to also pass
error.message through redactSensitiveSecurityDiagnostic before discarding or
including it. Ensure any message-derived details cannot expose sensitive command
arguments, while preserving the existing formatted error output behavior.

291-305: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Clarify that development is only valid as the implicit default

resolveCuaSigningPurpose returns DEVELOPMENT_SIGNING_PURPOSE for undefined/empty values, but validateArtifactPurpose still rejects explicit --purpose development. Add a short comment or test callout explaining this shortcut if it is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/sign-cua-helper.mjs` around lines 291 - 305, Clarify in
resolveCuaSigningPurpose that DEVELOPMENT_SIGNING_PURPOSE is permitted only
through the implicit undefined/empty-purpose default path, while explicit
non-empty purposes continue through validateArtifactPurpose and may reject
development. Add a brief comment or focused test callout at this boundary
without changing validation behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/ci/verify-cua-macos-helper.mjs`:
- Around line 291-297: Update the codesign verification invocation in the helper
verification flow to include the --deep flag alongside --strict when applying
the Team-ID test requirement. Keep the existing requirement expression and
helperAppPath unchanged, matching the behavior used by verifyMacAppDistribution.

In `@scripts/cua-macos-contract.mjs`:
- Around line 62-78: Update isAllowedDarwinLoadPath so any path containing "."
or ".." traversal segments is rejected regardless of whether it is absolute,
relative, or prefixed with `@rpath`. Remove the value.startsWith('/') restriction
from the segment validation while preserving the existing null, empty, and
allowed-prefix checks.

In `@test/main/scripts/cuaMacosContract.test.ts`:
- Around line 57-80: Update isAllowedDarwinLoadPath so matched `@executable_path`,
`@loader_path`, and `@rpath` prefixes validate their remaining path components with
the same separator-level ./.. traversal check used for absolute paths. Preserve
valid `@-prefixed` relative paths, and extend the allows-only test with a negative
assertion for a traversal path such as `@loader_path/`../../../../etc/evil.dylib.

---

Outside diff comments:
In `@scripts/apple-notarization.js`:
- Around line 16-21: Update validateAppleTeamId to accept an optional label or
otherwise include the received teamId in its validation error, rather than
always referencing DEEPCHAT_APPLE_NOTARY_TEAM_ID. Preserve the existing
validation behavior and return value while making failures from
verify-cua-macos-helper.mjs and package-manifest.mjs identify the relevant
input.

---

Nitpick comments:
In `@scripts/build-cua-plugin-runtime.mjs`:
- Around line 486-506: Update the Darwin sanitation flow around
enforceThinDarwinLoadPathContract and the corresponding lipo usage to call
ensureTool for install_name_tool and lipo before any mutation or invocation.
Reuse the existing tool-validation pattern from validateDarwinArchitecture and
signDarwinHelper, ensuring missing or unusable tools fail clearly before
processing begins.

In `@scripts/ci/package-manifest.mjs`:
- Around line 195-196: Update verifyMacZipDistribution so its calls to
verifyCuaMacHelper and verifyMacApp pass through the injected runCommand
alongside teamId, ensuring nested verification uses the same command runner
override.

In `@scripts/ci/verify-release-assets.mjs`:
- Around line 115-123: Move the shared macOS required-check names from the local
logic around requiredChecks into an exported constant in package-contract.mjs.
Update verify-release-assets.mjs, assemble-release.mjs, and package-manifest.mjs
to import and reuse that constant, preserving the existing check order and
platform-specific behavior.

In `@scripts/sign-cua-helper.mjs`:
- Around line 31-79: The error sanitization in formatSensitiveSecurityError only
processes error.stderr; update it to also pass error.message through
redactSensitiveSecurityDiagnostic before discarding or including it. Ensure any
message-derived details cannot expose sensitive command arguments, while
preserving the existing formatted error output behavior.
- Around line 291-305: Clarify in resolveCuaSigningPurpose that
DEVELOPMENT_SIGNING_PURPOSE is permitted only through the implicit
undefined/empty-purpose default path, while explicit non-empty purposes continue
through validateArtifactPurpose and may reject development. Add a brief comment
or focused test callout at this boundary without changing validation behavior.

In `@test/main/scripts/buildCuaPluginRuntime.test.ts`:
- Around line 26-30: Update the injected callback types in the test setup so
inspectExecutable and inspectArchitectures each accept the executable path
argument used by the implementation, matching the production contract and
eliminating the need for the later type widening near the affected test.
- Around line 157-225: Add a test alongside the asymmetric RPATH case for the
universal rebuild path where inspectArchitectures returns both x86_64 and arm64
for the original executable but only arm64 for rebuiltExecutable. Assert
enforceDarwinLoadPathContract throws “CUA helper architecture set changed during
sanitation” and preserves the fail-closed architecture-parity guard.

In `@test/main/scripts/packageContract.test.ts`:
- Around line 396-466: Guard both cleanup assertions in the tests around
verifyMacZipDistribution by asserting extractionRoot is non-empty before calling
lstat. Add expect(extractionRoot).not.toBe('') in both test cases after the
distribution verification and before the rejects-toThrow cleanup check, ensuring
ditto actually ran before cleanup is validated.

In `@test/main/scripts/releaseAssembly.test.ts`:
- Around line 207-225: Wrap the tampered release-index verification in a
try/finally block, keeping the rejects.toThrow assertion in the try and moving
the writeFile call that restores originalReleaseIndex into finally. Ensure
release-index.json is restored even when verifyReleaseAssets or the assertion
fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b080721d-f802-4009-96f2-729f8a500b79

📥 Commits

Reviewing files that changed from the base of the PR and between c44c1fc and 9e2b046.

📒 Files selected for processing (24)
  • .github/workflows/_package-macos.yml
  • docs/architecture/ci-release-packaging/spec.md
  • docs/issues/cua-macos-distribution-integrity/spec.md
  • electron-builder.yml
  • scripts/apple-notarization.js
  • scripts/build-cua-plugin-runtime.mjs
  • scripts/ci/assemble-release.mjs
  • scripts/ci/classify-package-impact.mjs
  • scripts/ci/package-contract.mjs
  • scripts/ci/package-manifest.mjs
  • scripts/ci/verify-cua-macos-helper.mjs
  • scripts/ci/verify-release-assets.mjs
  • scripts/cua-macos-contract.mjs
  • scripts/macos-release-contract.mjs
  • scripts/plugin.mjs
  • scripts/sign-cua-helper.mjs
  • test/main/plugin/pluginService.test.ts
  • test/main/scripts/buildCuaPluginRuntime.test.ts
  • test/main/scripts/cuaMacosContract.test.ts
  • test/main/scripts/packageContract.test.ts
  • test/main/scripts/packageWorkflow.test.ts
  • test/main/scripts/releaseAssembly.test.ts
  • test/main/scripts/signCuaHelper.test.ts
  • test/main/scripts/verifyCuaMacosHelper.test.ts

Comment thread scripts/ci/verify-cua-macos-helper.mjs
Comment thread scripts/cua-macos-contract.mjs
Comment thread test/main/scripts/cuaMacosContract.test.ts
@yyhhyyyyyy
yyhhyyyyyy merged commit ee5d423 into dev Jul 26, 2026
15 checks passed
@zhangmo8
zhangmo8 deleted the fix/macos-cua-release-integrity branch July 27, 2026 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant