Skip to content

Security audit: pin MASShortcut, confirm URL tasks, harden config auto-load#1765

Merged
rxhanson merged 1 commit into
rxhanson:mainfrom
JakubAnderwald:security/audit-2026-05
May 26, 2026
Merged

Security audit: pin MASShortcut, confirm URL tasks, harden config auto-load#1765
rxhanson merged 1 commit into
rxhanson:mainfrom
JakubAnderwald:security/audit-2026-05

Conversation

@JakubAnderwald

Copy link
Copy Markdown
Contributor

This PR is the result of a source-level security audit of Rectangle. The full report is included as SECURITY_AUDIT.md at the repo root and explains the methodology, every finding (with severity), what was changed, and what was deliberately left for follow-up.

TL;DR — outbound data

The audit confirms what the README implies: Rectangle contains no telemetry, no analytics, and no crash reporting. The only outbound network channel is the EdDSA-signed Sparkle update feed at https://rectangleapp.com/downloads/updates.xml (system profiling is off by default — SUSendProfileInfo/SUEnableSystemProfiling are not set).

Fixes in this PR

  1. [High] Supply chain — pin MASShortcut. Rectangle.xcodeproj/project.pbxproj had MASShortcut pinned to branch = master, so every build silently pulled the latest tip with no review gate. Pinned to the current HEAD commit (2f9fbb3f959b7a683c6faaf9638d22afad37a235) via kind = revision. Recommend follow-up: tag MASShortcut releases upstream and switch to upToNextMajorVersion, matching how Sparkle is configured.

  2. [Medium] URL handler — confirm execute-task actions. rectangle://execute-task?name=ignore-app&app-bundle-id=... (and unignore-app) previously mutated the disabled-apps defaults silently from any URL source, including a web page. Added an NSAlert confirmation showing the requested action and bundle-id. The confirmation is skipped only when Rectangle itself is frontmost, so the in-app/Stream-Deck/Shortcuts.app workflows are unchanged. execute-action is intentionally left alone — those actions are reversible and silent execution is the documented contract.

  3. [Medium] Config.loadFromSupportDir() — confirm + harden. On every launch Rectangle auto-loaded any ~/Library/Application Support/Rectangle/RectangleConfig.json and applied it (overwriting shortcuts and the entire defaults set), so any process running as the user could rewrite Rectangle config silently. Now:

    • Asks the user to Apply / Discard before importing.
    • Refuses symlinks and world-writable files (and warns the user).
    • Adds a 1 MiB size cap inside load(fileUrl:) so both auto-load and the explicit Import button are protected from OOM via a giant config file.

Not changed in this PR (low severity, documented in SECURITY_AUDIT.md)

  • Optional scheme/bundle-id shape validation in the URL handler.
  • codesign --force --deep in .github/workflows/build.yml (Apple-deprecated; tangentially runs-on: macos-26 is also not a valid runner label).
  • Adding a SECURITY.md with a disclosure address.

Verification

  • plutil -lint reports the modified project.pbxproj is valid; brace count is balanced.
  • No xcodebuild archive build was attempted from the audit machine (no Xcode + no signing identity). Maintainer CI will exercise the full build on this PR.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Addresses three issues identified in a source-level security audit
(see SECURITY_AUDIT.md for the full report):

1. [High] MASShortcut SPM dependency was pinned to `branch = master`,
   meaning every build pulled the latest tip with no review gate. Pin
   to the current HEAD revision SHA so future upstream changes require
   an explicit bump.

2. [Medium] `rectangle://execute-task=ignore-app/unignore-app` URL
   actions silently mutated the disabled-apps defaults when triggered
   from outside Rectangle (e.g. a web page). Require an NSAlert
   confirmation, surfacing the bundle-id, unless Rectangle itself is
   frontmost.

3. [Medium] `Config.loadFromSupportDir()` silently auto-loaded any
   RectangleConfig.json dropped in Application Support on launch. Add
   a confirmation prompt, reject symlinks and world-writable files,
   and apply a 1 MiB size cap to the loader (defense against OOM via
   giant config).

Also includes SECURITY_AUDIT.md documenting the full review, including
confirmation that the app contains no telemetry, analytics, or crash
reporting and that the only outbound network channel is the
EdDSA-signed Sparkle update feed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rxhanson rxhanson merged commit 85f92c0 into rxhanson:main May 26, 2026
1 check passed
@rxhanson

Copy link
Copy Markdown
Owner

Thanks! I appreciate the audit. I tested out the additional checks and they looked good.

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.

2 participants