feat(privacy): add watcher-side privacy filter (drop/redact before send) - #135
feat(privacy): add watcher-side privacy filter (drop/redact before send)#135TimeToBuildBob wants to merge 2 commits into
Conversation
Greptile SummaryAdds watcher-side privacy filtering before window events are sent to the server.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains; the current code catches both regex syntax errors and TypeError from non-string pattern values, logs the malformed rule, and continues startup. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Read current window] --> B[Apply privacy-filter rules]
B -->|Drop| C[Skip heartbeat]
B -->|Pass or redact| D[Apply research/title transforms]
D --> E[Create window event]
E --> F[Send heartbeat to aw-server]
Reviews (2): Last reviewed commit: "fix(privacy): skip non-string regex patt..." | Re-trigger Greptile |
| try: | ||
| pattern = re.compile(pattern_str) | ||
| except re.error as exc: |
There was a problem hiding this comment.
Non-string patterns crash startup
When a privacy-filter table contains a non-string pattern such as pattern = 123, re.compile raises TypeError, which is not caught here and terminates the watcher during startup instead of logging and skipping the invalid rule.
| try: | |
| pattern = re.compile(pattern_str) | |
| except re.error as exc: | |
| try: | |
| pattern = re.compile(pattern_str) | |
| except (re.error, TypeError) as exc: |
There was a problem hiding this comment.
Good catch. Fixed in cdf5c3b: compile_privacy_rules now catches TypeError from non-string TOML pattern values and skips the malformed rule. Added a regression test for pattern = 123; all 24 focused privacy-filter tests pass.
|
@greptileai review |
|
@TimeToBuildBob The watcher-side privacy filter should also be configurable via webui (non-technical user-facing central config) similar to the server-side privacy filter (but watcher-only config should also be supported, if e.g. server is untrusted). |
Adds a client-side privacy filter that drops or redacts window events before they are sent to aw-server, so sensitive data never leaves the machine at all. New module `aw_watcher_window/privacy_filter.py`: - `compile_privacy_rules(raw)` — validates and compiles regex patterns; invalid regexes and unknown actions are skipped with an error log - `apply_privacy_filters(window, rules)` — returns None (drop) or a filtered copy (redact); never mutates the input dict Config via `[[aw-watcher-window.privacy_filter]]` TOML tables: pattern = "(?i)private browsing|incognito" action = "drop" # or "redact" field = "title" # optional; defaults to "title" replacement = "excluded" # optional; used for redact action Integration in main.py: - Privacy filter runs before research and exclude_title transforms - A None return from transform_window skips the heartbeat entirely - heartbeat_loop passes privacy_filter_rules through 23 new tests in tests/test_privacy_filter.py; all existing tests pass. Mirrors the server-side privacy_filters engine in aw-server-rust (#600) so users can enforce the same rules at both the watcher and server layers. macOS note: the swift strategy bypasses this Python transform; use --strategy jxa or --strategy applescript to enable it on macOS.
cdf5c3b to
9176770
Compare
|
Good point. The TOML config in this PR covers the watcher-only path you described (useful when the server is untrusted or the user wants filtering before data ever leaves the machine). For webui configurability, the shape I'd expect is:
That touches aw-server and aw-webui (separate repos), so it's naturally a follow-up rather than something that fits cleanly into this PR. Would you prefer I scope it here (which would expand the PR significantly) or open a follow-up issue to track it? Also resolved the rebase conflict with master (#136, #137) — the branch is up to date now. |
Summary
Adds a client-side privacy filter that drops or redacts window events before they are sent to aw-server, so sensitive data never leaves the machine at all.
This mirrors the server-side
privacy_filtersengine that landed in aw-server-rust#600, allowing users to enforce the same rules at both layers. Previously the watcher only supported dropping titles viaexclude_title/exclude_titles(redact to"excluded"). This PR adds explicitdropandredactactions with user-configurable regex patterns and replacement strings.What changed
aw_watcher_window/privacy_filter.py(new) — pure Python, no new deps:compile_privacy_rules(raw)— validates and compiles regex patterns; invalid regexes and unknown actions are skipped with an error log rather than crashingapply_privacy_filters(window, rules)— returnsNone(drop) or a filtered copy (redact); never mutates the input dictaw_watcher_window/config.py— exposesprivacy_filter_rulesfrom[[aw-watcher-window.privacy_filter]]TOML array of tablesaw_watcher_window/main.py— privacy filter runs first intransform_window; aNonereturn skips the heartbeat;heartbeat_loopthreads the rules throughtests/test_privacy_filter.py(new) — 23 tests covering compile, drop, redact, input immutability, edge casesREADME.md— documents the new config section with examplesConfig example
Rule fields:
pattern(Python regex),field(default"title"),action("drop"or"redact"),replacement(for redact; default"excluded").Ordering
Privacy filter runs before Research Edition and
exclude_titletransforms. A"drop"rule exits immediately — subsequent rules are not evaluated for that event.macOS note
The default
swiftstrategy bypasses this Python transform (same as Research Edition). Use--strategy jxaor--strategy applescriptto enable watcher-side privacy filtering on macOS.Test plan
python3 -m pytest tests/test_privacy_filter.py -v— 23 passedpython3 -m pytest tests/test_main.py -v— 12 passed (no regressions)droprule matching title: heartbeat skippedredactrule matching title: heartbeat sent with replacement value