feat: add interactive network approval and domain rejection#698
Conversation
db2ec7c to
29e89e2
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces a network approval feature, allowing users to interactively approve or deny network requests to unknown hosts via OS-level notifications. The implementation includes cross-platform support (Linux, macOS, Windows), a runtime-mutable host filter, and persistence logic for user decisions. I have identified several issues: the approval backend creates a new Tokio runtime on every call, which is inefficient; persistence errors are handled inconsistently; JSON manipulation in the config writer is prone to panics; and the timeout logic is missing or ignored in the notification backends.
29e89e2 to
2282f15
Compare
|
All review comments done. Would be cool to have this - this makes maintaining the allow list a lot easier. |
|
This is a really good idea @klassm - I am away this weekend, but will take a good look first thing Monday - out of interest , it might be good to rig this up to the Approval backend on linux (unless you already have), but that could be a follow up patch. |
|
Great, thanks @lukehinds :-). Approval backend on linux also sounds nice - I would not do it directly, though. I don't have a Linux system, so that's hard to test :-) |
|
Interesting feature! One small UX thought: "allow once" might be ambiguous since it actually allows for the whole session, not just a single request. Maybe something like:
Also, what do you think about a dropdown with those three options plus Allow/Deny buttons instead of four (or more) separate buttons? |
f2586e1 to
10c4c23
Compare
|
@tho I changed the buttons to distinguish between once, session and always. |
1bb15a3 to
273f6a7
Compare
|
@lukehinds Did you have a chance to have a look again? 😇 |
3ec989e to
8912421
Compare
|
hi @klassm , I have and I promise I am keeping an eye on this, its just a big change which commits a lot to the supervisor backend arch, so i need to make sure its malable to other planned changes. |
8912421 to
170d100
Compare
82926e1 to
6b1d919
Compare
6b1d919 to
fc7b7e9
Compare
PR Review SummarySize
Affected crates
Blast radius — ModerateThis PR touches: source code,documentation Updated automatically on each push to this PR. |
cbef37a to
82233c1
Compare
- Deny/Approve buttons with Once/Session/Always duration dropdown - Blacklist (reject_domain) hosts auto-denied without prompting - Once approval bypasses runtime filter re-check - Deny(Session/Always) adds to runtime deny filter and persists - Tests for filter blacklist, deny scope, approval scope
When multiple concurrent requests arrive for the same host, the dedup mechanism in NetworkApprovalBackend used a Notify-only pending map. After the first request's dialog was resolved, waiting requests would always return Granted(Session) regardless of the actual decision, because they only checked whether the pending entry was removed. Replace the pending map value from Arc<Notify> to Arc<PendingEntry> which stores both the Notify and the actual decision. The first request writes its decision into the slot before notifying waiters, and waiters read the actual decision instead of assuming Granted. This fixes both: - Repeated approval popups after denying a host (denied waiters still got Granted, causing the next request to trigger a new dialog) - A security bug where denied hosts were approved for concurrent waiting requests Signed-off-by: klassm <klassm@users.noreply.github.com>
When network_profile was None (the default case), config_writer was None, causing 'Approve Always' to silently degrade to session-only. The host was only added to the in-memory runtime filter and never written to the profile JSON, so the approval popup reappeared on every session. Now always create a ConfigWriter, falling back to the 'default' profile name when no network_profile is explicitly configured. Signed-off-by: klassm <klassm@users.noreply.github.com>
2cbbea4 to
fe656d9
Compare
The approval channel receiver loop processes requests sequentially. When multiple CONNECT requests for the same host queued in the channel, each one would show a separate approval dialog because the approval backend never re-checked the runtime filter before entering the dialog path. After the first request was approved (adding the host to the runtime filter), subsequent queued requests should have been granted immediately without a dialog. Now request_network_approval_async checks the runtime filter first: - If the host is already allowed, returns Granted(Session) immediately - If the host is explicitly denied (DenyHost/DenyLinkLocal), returns Denied immediately - Otherwise, proceeds to the dialog/dedup path as before Signed-off-by: klassm <klassm@users.noreply.github.com>
… proxy, CRLF Bug 4 (HIGH): External proxy path bypassed network approval entirely. When an external proxy was configured and the host was not bypassed, handle_external_proxy was called without checking the runtime filter or sending approval requests. Now routes through handle_connect_with_approval when approval context is available. Bug 9 (HIGH): DNS rebinding vulnerability on Once-approved connections. resolve_host did DNS resolution without checking link-local IPs. A malicious domain could resolve to 169.254.169.254 on the second lookup, accessing cloud metadata. Now uses check_host which validates link-local IPs, and denies the connection if the check fails. Bug 17 (HIGH): DenyLinkLocal not blocked before approval channel. handle_connect_with_approval only short-circuited on DenyHost, not DenyLinkLocal. A link-local IP could reach the approval dialog. Now both DenyHost and DenyLinkLocal are blocked before the approval channel in both primary and runtime filter checks. Bug 2 (MEDIUM): Double check_host call in request_network_approval_async. The runtime filter was checked twice consecutively (once for is_allowed, once for DenyHost/DenyLinkLocal match). Now checks once and branches on the stored result. Bug 1 (LOW): is_denied() on NetworkApprovalDecision did not consider Timeout variant. A timeout is semantically a denial, so Timeout is now included in is_denied(). Bug 12 (LOW): external.rs send_response did not sanitize CRLF in reason strings, inconsistent with connect.rs. Now sanitizes like connect.rs does. Signed-off-by: klassm <klassm@users.noreply.github.com>
When a sandboxed process requests access to a blocked host, the user is now prompted via an OS dialog to approve or deny the request. Approved hosts are allowed for the session; optionally persisted to the active profile for permanent access.