This document argues that Thaw’s security requirements are met for its intended environment: a local macOS menu bar utility used by a single interactive user on their own Mac.
Update it when trust boundaries or major features change. It supports OpenSSF
Best Practices documentation criteria (assurance_case, security requirements,
architecture). Thaw holds the OpenSSF Best Practices Gold badge, with the
passing, silver, and gold tiers each at 100%. See §6.4 for the coverage
measurement and the exclusion set it rests on.
Users can expect:
- Thaw runs locally; no Thaw account and no first-party tracking backend.
- Privileged OS permissions (Accessibility, Screen Recording) are requested explicitly and used for menu-bar management features.
- Automation that mutates settings via
thaw://requires an explicit user-approved allowlist and matching code signature / Team ID. - Software updates are delivered over HTTPS and authenticated with Sparkle EdDSA; release builds are Developer ID–signed and notarized.
- Security issues can be reported privately; maintainers aim to acknowledge within 48 hours.
Users cannot expect:
- Protection against an attacker who already controls their unlocked Mac session or who has been granted the same TCC permissions.
- A hardened multi-tenant server security model (Thaw is not a network service).
- Security fixes for abandoned old releases (only the latest stable is supported).
- That third-party menu bar apps themselves are trustworthy.
| Asset | Why it matters |
|---|---|
| Integrity of menu bar layout / visibility | Unexpected hide/show can disrupt status indicators and user trust |
| User preferences and profiles | Local configuration; automation abuse could change behavior |
| Diagnostic logs | May contain environment details if logging is enabled |
| Update channel | Compromised updates → arbitrary code as the user |
| TCC permissions held by Thaw | AX / screen capture are powerful on macOS |
| ID | Scenario | Likelihood | Impact |
|---|---|---|---|
| T1 | Malicious local app sends thaw:// settings URLs |
Medium | Config abuse, UX disruption |
| T2 | Supply-chain compromise of a SwiftPM dependency or CI | Low–Med | Code execution in Thaw |
| T3 | Tampered download / MITM on update feed | Low (HTTPS + signatures) | Code execution |
| T4 | Malicious crafted profile / defaults data | Low–Med | Crash or unexpected layout |
| T5 | Confused-deputy via Accessibility APIs | Inherent | Thaw can affect other apps’ status items by design |
| T6 | Untrusted network attacker remote to the Mac | Low | Limited: no open server socket by design |
Out of scope: physical access to unlocked Mac; bugs in macOS itself; compromise of Apple’s notarization infrastructure.
Untrusted local apps / scripts User (interactive)
│ │
│ thaw:// │ UI / TCC prompts
▼ ▼
┌────────────────── Settings URI handler ──────────────────┐
│ allowlist + Team ID check │ boolean/enum/range checks │
└──────────────────────────────┬───────────────────────────┘
│
▼
Thaw.app (user privileges)
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
XPC helpers macOS TCC / AX Sparkle (HTTPS)
(item PIDs, capture) WindowServer EdDSA verify
| Boundary | What crosses | Controls |
|---|---|---|
| External URL → settings | thaw://set etc. |
Allowlisted keys; range checks; sender whitelist + SecCode Team ID binding |
| App ↔ XPC helpers | Codable requests | Fixed service names; limited request vocabulary; capture requests accept only menu-bar window IDs, validated bounds, and BGRA size limits; listeners accept only same-team peers (XPCListener(requirement: .isFromSameTeam())), and a teamless ad-hoc build logs a warning that it is running without peer validation |
| App ↔ network | Appcast / downloads | HTTPS; Sparkle EdDSA (SUPublicEDKey); Apple notarization on shipped builds |
| App ↔ OS | AX / capture | Explicit TCC; features degrade without grants |
| Principle | How Thaw applies it |
|---|---|
| Economy of mechanism | Small XPC protocols (four request cases each); URI settings limited to known keys |
| Fail-safe defaults | Settings URI mutation denied unless allowlisted; permissions off until user grants |
| Complete mediation | Each settings URI request re-checks whitelist + signature; numeric keys clamped to ranges |
| Open design | GPL-3.0; public repo; security policy and this assurance case |
| Separation of privilege | XPC helpers separated from UI (PID lookup vs recyclable SkyLight capture); release signing keys not on the public download host as long-lived plaintext |
| Least privilege | Requests only needed TCC rights; no root requirement for normal use |
| Least common mechanism | Per-user install; no shared multi-user daemon for core features |
| Psychological acceptability | Clear permission prompts; authorize dialog lists what automation can do |
Mapped loosely to CWE / OWASP-style classes relevant to a desktop app:
| Weakness | Countermeasure |
|---|---|
| Memory corruption (CWE-119 etc.) | Primary implementation in Swift (memory-safe); limited bridging |
| Injection via URLs / settings | Allowlist of keys; typed parsers; range validation (SettingsURIHandler) |
| Broken access control for automation | User confirm + persistent whitelist + Team ID re-verify |
| Insecure update / delivery | HTTPS + Sparkle EdDSA + Developer ID + notarization |
| Hard-coded secrets in repo | Signing material in GitHub Actions secrets, not source |
| Dependency vulnerabilities | SPM lockfile; CI; Dependabot (.github/dependabot.yml) |
| Overly verbose public logging of secrets | Diagnostic logging is optional and local; avoid logging secrets |
Static analysis (SonarCloud) and SwiftLint run in CI to catch a subset of defect classes before merge.
-
Accessibility by design can manipulate other apps’ menu bar items, so users must trust Thaw similarly to other AX utilities.
-
Private/undocumented WindowServer interactions increase compatibility and maintenance risk across macOS versions. Offscreen icon capture uses SkyLight in
MenuBarCaptureServicerather than the UI process; the helper is recycled after a capture budget because eachSLWindowListCreateImageFromArraycall leaks a small dictionary in the caller. That contains growth in Thaw but does not remove the leak from the platform API. -
Update delivery depends on a GitHub Pages path. The Sparkle appcast is served from a
github.ioorigin, which does not redirect across a repository owner transfer, so builds released before the move tothaw-app(three owners:stonerl,nightah,diazdesandi) rely on the previous path continuing to serve the feed. Moving the appcast to a project-controlled domain is planned. -
Statement coverage (
test_statement_coverage80) is Met against a documented exclusion set. Statement coverage forthaw-app_Thawis 84%, as recorded in the criterion's justification on bestpractices.dev. The suite is entirely Swift Testing.Branch coverage (
test_branch_coverage80, a Gold criterion) is N/A. No FLOSS toolchain produces reliable condition or branch coverage for Swift: coverage is collected withxcodebuild -enableCodeCoverageand imported into SonarCloud as line coverage throughxcresultparser, and SonarCloud leavesbranch_coverageandconditions_to_coverempty. Apple'sxccovis line-oriented. The criterion allows N/A when no FLOSS tool can measure branch coverage in the language, and the full rationale is recorded against the criterion on bestpractices.dev.The measurement excludes code whose substance cannot execute in a headless CI run: WindowServer/CGS private APIs, Accessibility permissions, Carbon hotkey registration, live display and running-app state, NSWindow/NSPanel subclasses, SwiftUI view bodies, XPC wiring, and
@mainentry points. The set and the rule governing it are insonar-project.properties, which requires that pure value types, algorithms, Codable models and parsing stay measured even under a UI-sounding path. Two files that buried both kinds of code (MenuBarSection,MenuBarItem) were split along that seam rather than excluded whole, so their logic still counts.There is still no enforced 80% gate in CI; the figure is measured per analysis and this claim should be re-checked when it moves. The residual gap is concentrated in
ProfileManager(about 700 lines reachable only through a liveAppState, which no test can currently construct). That is a testability limit, not an untested-logic claim, and narrowing those entry points is the route to raising it further.
| Claim | Evidence |
|---|---|
| Private vuln reporting + response SLA | .github/SECURITY.md |
| URI allowlist + signature binding | Thaw/Utilities/SettingsURIHandler.swift, docs/URI_SCHEMES.md |
| Update authenticity | SUFeedURL / SUPublicEDKey in Thaw/Resources/Info.plist; Sparkle release actions |
| Architecture | docs/ARCHITECTURE.md |
| Automated analysis | .github/workflows/ci.yml, SonarCloud project thaw-app_Thaw |
Test coverage (test_statement_coverage80 Met) |
SonarCloud coverage measure; CI coverage.xml; 84% per the criterion justification on bestpractices.dev; exclusion set and its governing rule in sonar-project.properties |
| Badge level | bestpractices.dev/projects/13303: Gold, with passing, silver, and gold each at 100% |
| Dependency monitoring | .github/dependabot.yml |
| Date | Note |
|---|---|
| 2026-08-15 | Documented MenuBarCaptureService: same-team XPC, menu-bar-only window IDs, BGRA size limits, and helper recycle to contain the SkyLight dictionary leak |
| 2026-07-27 | Initial version from development for OpenSSF docs |
| 2026-07-27 | Deferred Silver coverage claim; measured ~44% |
| 2026-07-31 | Claimed Silver test_statement_coverage80; measured 80.8% after migrating the suite to Swift Testing and documenting the exclusion set. Corrected the SonarCloud project key to thaw-app_Thaw |
| 2026-08-31 | Added MenuBarCaptureService to the trust boundaries and recorded the same-team XPC peer requirement |
| 2026-09-01 | Restated the badge as Gold rather than a Silver coverage claim, refreshed statement coverage to the 84% recorded on bestpractices.dev, and documented the N/A rationale for branch coverage |