Skip to content

fix(desktop): declare macOS usage descriptions for protected folders - #56608

Open
argszero wants to merge 2 commits into
anomalyco:devfrom
argszero:mac-folder-permissions
Open

argszero wants to merge 2 commits into
anomalyco:devfrom
argszero:mac-folder-permissions

Conversation

@argszero

@argszero argszero commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #56479

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do

This is the first half of what the report asks for: the bundle's usage descriptions.

packages/desktop/electron-builder.config.ts declared no folder usage description for macOS — its mac.extendInfo held only the AutoFill key. macOS gates Documents, Desktop and Downloads behind TCC, and the usage description is the only thing that turns an access denial into a permission prompt. Without it the system denies silently: the app never appears under Privacy & Security → Files and Folders, so there is nothing for the user to allow, and every location-scoped API call for a workspace in one of those folders fails with EPERM — which is the empty model selector in the report.

The change adds NSDocumentsFolderUsageDescription, NSDesktopFolderUsageDescription and NSDownloadsFolderUsageDescription, one per protected folder. A workspace can be any folder the user picks, so a workspace in any of the three needs its own string; declaring one does not cover the others.

The second half — explaining an unreadable workspace in the UI instead of showing an empty model list — is left alone, because #50952 is already open on that direction.

How did you verify your code works?

Three assertions added to shared packaging defaults in packages/desktop/electron-builder.config.test.ts, beside the existing mac.extendInfo check on the same config: each of the three keys must be a non-empty string. Empty counts as missing here — macOS shows no prompt for an empty description either — so they use toMatch(/\S/) rather than toBeString().

  • With the config reverted, shared packaging defaults fails (6 pass / 1 fail, Received value must be a string: undefined); with the change it passes (7 pass / 0 fail).
  • Read the resolved config back to confirm what the packager merges into the bundle's Info.plist: all three keys present with the text above.
  • oxlint on both changed files: 0 warnings, 0 errors.
  • bun run typecheck in packages/desktop reports 14 errors, all in packages/app, packages/gui-extensions, packages/session-ui and src/main/storage/drafts.ts; the pristine tree at this base reports the same 14, and none is in a file this PR touches.

What I could not verify: I have no macOS machine here, so nothing here exercised the TCC prompt, a packaged .app, or the EPERM path itself. The claim rests on the missing keys being the documented cause — which the report already measured against the built bundle — and on the config now carrying them. A maintainer with a Mac still needs to confirm the prompt appears. scripts/picker.test.ts also fails in this environment (2 failures, The service was stopped); it fails identically on the pristine tree.

AI-assisted implementation and tests; reviewed before publication.

Screenshots / recordings

Not applicable; packaging configuration only.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #50951

@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Oct 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 72 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent 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.

The fix looks correct, but I couldn't verify it end to end: the folder-access prompt only shows up in a packaged macOS app, and I only had Linux. A maintainer with a Mac should confirm the prompt appears for a ~/Documents workspace after tccutil reset SystemPolicyDocumentsFolder <bundle id>.

What I checked: the keys go into getBase(), which the dev, beta and prod configs all share, and none of them overrides mac. The bundled CLI is launched by the app, so macOS should treat the app as the one asking for access, and the CLI shouldn't need keys of its own. The config test fails on the base (7 pass / 1 fail) and passes with this change (8 pass / 0 fail).

Public API: unchanged; this only adds keys to the macOS app bundle's Info.plist.

One small request: please fold the test into the existing one (see the inline comment).

Related: #50951

])
})

test("declares a macOS usage description for every folder a workspace can live in", () => {

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.

This can be three assertions in the existing shared packaging defaults test, which already checks mac.extendInfo (the AutoFill key) on the same config. A separate test with a multi-line comment is more than this config change needs, so it can be dropped in favour of that.

@argszero

Copy link
Copy Markdown
Contributor Author

Folded — thanks. The three assertions now sit in shared packaging defaults, next to the existing NSAutoFillRequiresTextContentTypeForOneTimeCodeOnMac check on the same config, and the separate test is gone. Commit 5e49aacc49.

The assertions still bite after the move: with the config reverted to dev the same test now fails (Received value must be a string: undefined, 6 pass / 1 fail), and with the change it passes (7 pass / 0 fail, 179 expect calls). They still use toMatch(/\S/) rather than toBeString() so an empty description fails as well, since macOS prompts for neither.

Agreed on the verification gap — I have no Mac either, so a maintainer confirming the prompt after tccutil reset SystemPolicyDocumentsFolder is the remaining step.

This branch has not been deployed

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

Labels

contributor needs:compliance This means the issue will auto-close after 2 hours.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

desktop: macOS app silently denied ~/Documents (missing NSDocumentsFolderUsageDescription) -> empty model selector

1 participant