Skip to content

Harden the Electron renderer and packaged app capabilities #6

Closed

Description

@lastobelus

Outcome

The packaged macOS application exposes only the capabilities Markover uses and applies a documented Electron security baseline.

Acceptance criteria

  • Unused camera, microphone, and Bluetooth usage descriptions are absent from the packaged app.
  • NSAllowsArbitraryLoads is absent unless a specific documented feature requires it.
  • Renderer sandboxing and a tight Content Security Policy are enabled.
  • Unexpected navigation, new windows, and permission requests are denied.
  • Privileged IPC validates sender identity and payload shape.
  • Electron fuse choices are documented and verified against the packaged bundle.
  • Automated tests cover the enforceable boundaries, and packaged-app verification records the rest.

Intended PR stack

Bottom to top:

  1. Remove excess packaged capabilities and introduce shared hardening primitives.
  2. Enforce renderer, navigation, window, permission, and CSP boundaries.
  3. Validate privileged IPC, apply the fuse decision, and verify the packaged result.

Activity

  1. added this to the Focused preview milestone on Aug 3, 2026
  2. self-assigned this
    on Aug 3, 2026
  3. added
    launch:blockerBlocks the next Markover announcement gate
    securitySecurity and trust hardening
    launchWork required for an announcement or release gate
    on Aug 3, 2026
  4. lastobelus commented on Aug 3, 2026

    @lastobelus
    OwnerAuthor

    Stack scaffold initialized locally from origin/main. No empty placeholder PRs were submitted.

    Bottom → top:

    • agent/launch-app-capabilities
    • agent/launch-app-renderer-boundaries
    • agent/launch-app-ipc-hardening

    Begin on the bottom branch, keep each layer independently reviewable, and run gh stack submit once the layers contain their real implementation commits.

  5. lastobelus commented on Aug 7, 2026

    @lastobelus
    OwnerAuthor

    Work intent

    phase: complete
    summary: "PR #103 merged the third and final issue #6 slice after green CI and a terminal-clean Codex review"
    touch-points:
      - "PR 1: scripts/package-macos.ts, scripts/macos-artifact-preflight.ts, scripts/macos-release-contract.ts, config/macos/info.plist, macOS entitlements, package dependencies, fuse policy, and signed-bundle verification"
      - "PR 2: src/main.ts BrowserWindow and session security hooks, src/index.html CSP, renderer sandboxing, and navigation, window, and permission denial"
      - "PR 3: privileged IPC sender, main-frame, and payload validation across src/main.ts, src/preload.ts, shared validators, #39 shutdown messages, #52 activation messages, and any final #15 deletion/cleanup channels"
      - "automated boundary tests, renderer smoke, signed packaged smoke, plist and fuse inspection, and adversarial packaged verification"
    blocked-by: []
    may-block:
      - "#10"
      - "#11"
      - "#63"
      - "#95"
    branch: "t3code/issue-six-ipc-hardening"
    thread: "ece7b845-bc50-498c-8919-5f8185912432"

    Confirmed decomposition: the old launch-app branches are zero-implementation scaffolds and are not bases. PR 1 owns package capabilities and fuses, PR 2 owns renderer/session boundaries, and PR 3 owns privileged IPC plus final integrated verification.

    Ownership: merged PR #75 / issue #39 owns durability and shutdown ordering. Merged PR #76 / issue #52 owns authenticated activation, deep-link dispatch, renderer acknowledgement, protocol registration, and development-handler lifecycle. Issue #15 owns review deletion and attachment cleanup behavior plus its IPC semantics. Issue #6 hardens the Electron and IPC boundaries around those operations without absorbing their product behavior.

    Approved fuse contract: disable RunAsNode, Node-options environment handling, and Node CLI inspection; enable cookie encryption, embedded-ASAR integrity validation, and ASAR-only loading; leave browser-specific V8 snapshots disabled; retain the file-protocol privilege fuse while Markover loads its renderer and local preview images through file URLs. Verify the profile and adversarial cases in signed packages.

    Approved renderer/session contract: explicitly enable sandboxing, context isolation, and web security while disabling Node integration and webviews. Deny renderer navigation, redirects, new windows, webview attachment, and all permission checks and requests. Keep #52 deep links on the authenticated activation-message path. Tighten CSP to bundled scripts and styles, required dynamic style attributes, and the required self/data/blob/file image sources while explicitly denying forms, workers, media, objects, frames, and connections. Regression-test sandboxed preload behavior and local attachment/image rendering.

    Approved IPC contract: route every renderer-to-main invoke and one-way message through centralized guards for the active main window webContents, its main frame, and the canonical index entry URL with only expected startup query data. Validate exact arity and runtime payload shape before side effects; reject invalid invokes and drop plus diagnose invalid one-way messages. Share validators across review trees and IDs, attachments, settings, startup and smoke messages, clipboard operations, #39 snapshot/status responses, #52 activation responses, and any final #15 deletion/cleanup messages. Validate main-to-renderer payloads in the preload before exposing them through the narrow context bridge, and never expose raw Electron objects.

    Approved sequencing: complete the claim and implementation interview without waiting for #52, but cut implementation only after PR #76 merges. Rebase t3code/issue-six onto merged main, use it for PR 1, stack PR 2 on PR 1 and PR 3 on PR 2, and never base work on the old scaffold branches or volatile PR #76. Commit, test, and push each slice before opening the next; rebase upper layers as lower PRs land and run final integrated packaged validation at the top. PRs 1 and 2 need not wait for #15, but PR 3 must consume #15 PR 1's final IPC surface or an explicit schema handoff.

    Approved merge gate: every PR passes focused tests and ci:local. PR 1 verifies both macOS architectures, signatures, entitlements, stripped capability metadata, preserved markover URL registration, exact fuse values, and negative RunAsNode, Node-options, CLI-inspection, loose-code, and ASAR-tamper probes. PR 2 runs sandboxed renderer smoke for preload startup, CSP, denial hooks, and required local/data/blob image rendering. PR 3 covers forged senders, subframes, wrong URLs, malformed payloads, extra arguments, and integrated #39 shutdown plus #52 activation behavior. The top stack passes both packaged CI lanes and an isolated local signed-package smoke. Clean-profile release evidence stays with #90 and #11.

    Tracked follow-up: #95 moves the renderer and attachments from file URLs to narrowly scoped internal protocols, disables GrantFileProtocolExtraPrivileges, removes file: from CSP, and tightens URL validation after #6. It targets Broad announcement and does not block Focused preview.

    Implementation authorization: approved by the maintainer on 2026-08-07. Begin PR 1 immediately after PR #76 merges; hold PR 3 for #15 PR 1 or its explicit IPC schema handoff.

    Execution checkpoint: PR 1 merged through PR #96 as squash commit 65a8621. PR 2 merged through PR #98 as squash commit 005d83c after all four CI lanes and Codex review passed. The third PR now begins from that merged main commit on t3code/issue-six-ipc-hardening. It owns centralized privileged IPC sender/frame/URL and payload validation plus final integrated verification; #15 supplied its accepted future-channel schemas in issue comment #5224158411. Issue #15 waits for this guard before implementing deletion behavior, and issue #63 remains blocked until this complete #6 stack lands.

    Review checkpoint: draft PR #103 is open at commit ddebb1d. Local ci:local passed 414 tests plus integrated Electron smoke; the hardened ad-hoc-signed arm64 package passed exact fuse inspection, adversarial hardening probes, packaged renderer smoke, and strict codesign verification. Issue #15 remains behavior owner for deletion and attachment cleanup and may consume these schemas after merge; #63 remains blocked until #103 lands.

    Landing checkpoint: PR #103 merged reviewed head ddebb1d as squash commit 46e17d9 on 2026-08-09. All four CI lanes passed, including arm64 and Intel packaged smoke; Codex returned a current-head thumbs-up and no review threads remain. Issue #6 is closed and its project item is Done. Issue #15 PR 1 and issue #63 are now unblocked from issue #6.

  6. moved this from Todo to In Progress in Markover Announcement Readinesson Aug 7, 2026
  7. lastobelus commented on Aug 8, 2026

    @lastobelus
    OwnerAuthor

    Issue #15 IPC schema handoff

    Final renderer-facing channels for #15 PR 1:

    • review:context-menu:open — invoke with exactly one { reviewId: string }; resolves void. It opens the shared native tab/document-list menu. The main process owns menu actions and confirmation.
    • attachment:remove — invoke with exactly one { reviewId: string, attachmentId: string, tree: ReviewTree }; resolves { reviewId: string, attachmentId: string, outcome: "cancelled" | "trashed" }. Main validates the existing stored reference, confirms according to settings, durably saves the attachment-free tree, and only then moves the resolved file to Trash.
    • review:trashed — main-to-renderer event with exactly { reviewId: string }, emitted only after the complete review directory reaches Trash. The renderer then removes that session and selects the next available review.

    There is intentionally no renderer handler for the native Review menu’s Move Review to Trash or Clean Up Unused Attachments commands. Both run through one main-process controller; cleanup scans valid reviews, skips invalid/unreadable reviews, shows count and byte size, and trashes only after confirmation.

    For #6 PR 3: enforce exact arity and keys, the managed-review ID grammar, the allocated attachment-ID grammar, and the shared ReviewTree validator. Reject malformed invokes before side effects. No request or response contains a filesystem path. Validate both invoke results and the outbound event in preload. This is the schema-handoff path agreed in #15; #15 PR 1 will implement product semantics atop the merged guard.

  8. lastobelus commented on Aug 8, 2026

    @lastobelus
    OwnerAuthor

    Deep-link handoff from #52 / PR #76

    PR #76 landed on main as 16c3e31. Rebase this thread onto current main before implementation.

    The contract now available for #6 to harden without absorbing is:

    • markover open returns {reviewId,status,reviewUrl}.
    • src/review-url.ts owns the strict canonical / PR-scoped URL grammar.
    • The authenticated local activation route uses bounded renderer acknowledgement and now participates in the existing shutdown mutation barrier.
    • Renderer readiness is tracked per window so a link waits for a recreated renderer before dispatch.
    • Packaged macOS owns early open-url, exact CFBundleURLTypes, first-launch registration, and QA registration suppression.
    • Generated native development handlers own explicit install/status/repair/replace/remove lifecycle, exact identity proof, endpoint credentials, bounded diagnostics, transactional handler replacement, and LaunchServices ownership restoration.
    • Custom links are verified through Safari/macOS/Terminal open. T3 Code and Codex host clicks are unsupported at present; merged PR Handle direct PR follow-up work #74 owns the standalone open '<reviewUrl>' Terminal fallback.
    • Add HTTPS universal review links after Apple signing is available #87 owns any future HTTPS universal-link/AASA work and is deferred pending Sign, notarize, and add release provenance and rollback guidance #13 plus an Apple Developer Program decision; do not implement that in Harden the Electron renderer and packaged app capabilities #6.

    For #6:

    • preserve the settled activation semantics and handler-lifecycle ownership;
    • validate sender, active main frame, and payload for the new IPC messages;
    • apply navigation/window/permission/CSP/fuse hardening around these paths;
    • include URL/protocol/handler paths in packaged and adversarial verification;
    • do not change the custom URL grammar or absorb HTTPS/AASA.

    Validation at merge: 401 local tests plus renderer smoke; Node 22/24 and arm64/Intel packaged CI all green. Eleven actionable automated-review findings were fixed and resolved before merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

launchWork required for an announcement or release gatelaunch:blockerBlocks the next Markover announcement gatesecuritySecurity and trust hardening

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions