uttrflow / uttrflow/uttrflow-swift

The log privacy gate only matches variable names, so error descriptions are logged public, one of them with the home-folder path

Open
#658 0 comments 0 reactions 0 assignees View on GitHub
area:general bug P0 security
Dominant language
Swift
Stars
4
Forks
17
Avg merge
3h 32m
Merged PRs (30d)
277

Description

## What happens

`Scripts/log_privacy_audit.py` (the gate from #361) decides whether a `privacy: .public` interpolation is user text by splitting the interpolated *identifier* into camelCase parts and matching them against a word list (`USER_TEXT`, `:21-25`; the check at `:125-140`). A value named `error`, `why`, `failure` or `front` never matches, whatever it contains. The gate also only recognises calls on `log` / `logger` / `*Log` (`:13`), not `print`, `NSLog`, `os_log` or `FileHandle.standardError`. None of those exist in app sources today.

Interpolations that pass the gate but can carry more than a fixed sentence:

- `Sources/Uttrflow/AppDelegate.swift:414`: `"the corpus would not open: \(String(describing: error), privacy: .public)"`. The error can be `PredictStoreError.cannotOpen(path)` (`Sources/UttrflowPredictStore/SQLite.swift:8`, thrown at `:34`), whose payload is the full database path under the home folder, which contains the account's user name. It can also be `.query(String)` with a raw SQLite message.
- `AppDelegate.swift:439`: `"the suggestion model did not load: \(String(describing: error), privacy: .public)"`. Model load and download errors from the hub library typically carry file paths and URLs.
- `AppDelegate.swift:1653`: `"store change failed: \(error.localizedDescription, privacy: .public)"`.
- `AppDelegate.swift:1083-1084`: `why` falls back to `String(describing: error)` for any error that is not an `UttrflowFailure`.

`log show` / sysdiagnose output is what people attach to bug reports, and public values are also retained in the unified log across reboots.

## Why it matters

The user name in a path is personal data under the project's own rules. More importantly, "named `error`, so it is safe" is the kind of gap that lets user text through later: an error type that embeds a clip, a field value or a transcript would pass silently.

## Acceptance criteria

- The four lines above log a fixed description (a case name, or a `userMessage`) publicly, and any payload with `privacy: .private`.
- The gate treats `String(describing:)`, `.localizedDescription` and `\(error)` interpolated as `.public` as findings unless allowlisted with a reason, the same way its existing exceptions are recorded.
- Tests for the gate (next to the script, or in the `make verify` step that runs it) cover a `String(describing: error)` public interpolation.

Contributor guide

Open the contributing guide

Research direction

Start with Scripts/log_privacy_audit.py, the four AppDelegate.swift locations, and Sources/UttrflowPredictStore/SQLite.swift lines 8 and 34. Run the gate through the make verify step and inspect its existing exception tests, then add coverage for a public String(describing: error) interpolation. Done means the four reported lines expose only fixed descriptions publicly, keep payloads private, and the gate finds the unsafe interpolation unless it has a reasoned allowlist entry.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, swift
Domain
desktop, security, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.