fix: restore link capture on Windows and macOS - #3
Merged
Conversation
…urce-app rules Link capture was broken on both platforms, for unrelated reasons. Windows: the shell registration was written once on first boot and never reconciled again, so a moved, updated or reinstalled executable left HKCU pointing at a path that no longer existed. Since HKCU\Software\Classes shadows HKLM, a leftover entry from a local build also hijacked link handling from the real installation — Store (MSIX) or standalone alike. macOS: AppDelegate called super.applicationDidFinishLaunching, a selector FlutterAppDelegate does not implement. It compiles because the superclass adopts NSApplicationDelegate, but the objc_msgSendSuper hits an unimplemented selector and aborts the process during launch — precisely when Launch Services hands it a URL to open. Registration - Reconcile the handler on every launch, before delegating to the resident instance, so a courier process repairs it too. - Never register from a build tree, and remove entries a build tree owns. - Under MSIX, drop per-user entries shadowing the package manifest. - Make "set as default" actually re-register instead of only opening system settings; point macOS at Desktop & Dock rather than the pre-Ventura pane. - Detect MSIX via GetCurrentPackageFullName: the previous environment-variable probe was inheritable, and a false positive disabled registration entirely. macOS - Drop the unimplemented super call in applicationDidFinishLaunching. - Register the running bundle, not whatever Launch Services resolves, so a debug build cannot claim the association and die with `flutter clean`. - Report setDefaultApplication failures instead of discarding them. - Resolve the Flutter window through the delegate outlet; NSApp.windows.first could return the tray window. - LSHandlerRank Owner: as Alternate the app ruled itself out as a browser. - Include Safari's cryptex path in browser detection. - Widen the login-item catch: MissingPluginException killed the launch. Windows IPC - Give the pipe an explicit descriptor: DACL limited to the current user and SYSTEM, low-integrity label so medium-integrity callers can still deliver. - Reject remote clients and require first-instance ownership, and use SECURITY_IDENTIFICATION so a squatter cannot impersonate the client. - Launch the app unelevated after install (runasoriginaluser): inheriting the installer's token put the mutex and pipe out of reach of Slack and Teams. - Buffer the argv URL through the pipe server instead of an async* wrapper that dropped events flushed before subscription. - Log unrecognised launch arguments; dropped links left no trace at all. Window and rendering - Sequence window setup instead of relying on waitUntilReadyToShow's callback, which is not awaited. - Serialise mode transitions and drive the window from one place only; two concurrent paths could leave it visible with no content. - Arm the picker blur guard by timer only. Arming it on first focus meant the focus event from showing the window closed the picker immediately. - Reconcile window visibility from the hidden state as a safety net. - Re-scan browsers after resetting a corrupt config, and show an explanation in the picker instead of a blank window. - Clamp picker position safely; on small or scaled displays it threw and the app went deaf to every later link. Security - Reject non-launchable URLs before any Process.start: a crafted `--gpu-launcher=` reached the browser as argv and ran arbitrary binaries. - Match the file scheme case-insensitively; `FILE://` bypassed the extension allowlist. Reject UNC paths, which leaked a NetNTLMv2 hash on probe. - Redact errors and stack traces, and cap startup_crash.log: a failed launch wrote the full URL to disk, contradicting PRIVACY.md. - Validate the release URL as github.com before handing it to the shell. - Surface malformed IPC payloads as FormatException instead of TypeError. Features - Capture the microsoft-edge: protocol (opt-in). Teams, Outlook, Widgets and Start search wrap links in it, bypassing the default browser entirely. - Private windows from the picker with Shift, using the switch each browser family expects. On macOS this needs `open -n`, or --args is discarded. - Rules scoped to the originating app, resolved from the parent process on Windows and approximated by the frontmost app on macOS. App-scoped rules take precedence over domain rules. - Self-diagnostics in Settings that explain a broken registration and offer a one-click repair. Build reproducibility - Track pubspec.lock and pin the Flutter version in CI and release workflows; every release was previously built against whatever stable resolved to. Docs: add docs/LINK_CAPTURE.md covering the capture path, registry precedence, the log messages that identify each failure, and the new features.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
harden IPC and add source-app rules
Link capture was broken on both platforms, for unrelated reasons.
Windows: the shell registration was written once on first boot and never reconciled again, so a moved, updated or reinstalled executable left HKCU pointing at a path that no longer existed. Since HKCU\Software\Classes shadows HKLM, a leftover entry from a local build also hijacked link handling from the real installation — Store (MSIX) or standalone alike.
macOS: AppDelegate called super.applicationDidFinishLaunching, a selector FlutterAppDelegate does not implement. It compiles because the superclass adopts NSApplicationDelegate, but the objc_msgSendSuper hits an unimplemented selector and aborts the process during launch — precisely when Launch Services hands it a URL to open.
Registration
macOS
flutter clean.Windows IPC
Window and rendering
Security
--gpu-launcher=reached the browser as argv and ran arbitrary binaries.FILE://bypassed the extension allowlist. Reject UNC paths, which leaked a NetNTLMv2 hash on probe.Features
open -n, or --args is discarded.Build reproducibility
Docs: add docs/LINK_CAPTURE.md covering the capture path, registry precedence, the log messages that identify each failure, and the new features.