asdf98 commited on
Commit
d44fddd
·
verified ·
1 Parent(s): 5f55e07

fix: harden browser navigation resolution and scheme blocking

Browse files
Files changed (1) hide show
  1. src-tauri/src/browser/navigation.rs +46 -36
src-tauri/src/browser/navigation.rs CHANGED
@@ -1,36 +1,46 @@
1
- use tauri::{AppHandle, Manager};
2
- use crate::adblock::engine::AdBlockState;
3
-
4
- /// Resolve user input to a navigable URL with HTTPS-first
5
- pub fn resolve_url(input: &str) -> String {
6
- let trimmed = input.trim();
7
- if trimmed.is_empty() { return "https://duckduckgo.com".to_string(); }
8
- if trimmed.starts_with("http://") || trimmed.starts_with("https://") {
9
- // HTTPS-first: upgrade http to https unless localhost
10
- if trimmed.starts_with("http://") {
11
- let host = trimmed.split("//").nth(1).and_then(|s| s.split('/').next()).unwrap_or("");
12
- if host != "localhost" && host != "127.0.0.1" && host != "::1" {
13
- return trimmed.replacen("http://", "https://", 1);
14
- }
15
- }
16
- return trimmed.to_string();
17
- }
18
- if trimmed.contains('.') && !trimmed.contains(' ') {
19
- return format!("https://{trimmed}");
20
- }
21
- format!("https://duckduckgo.com/?q={}", urlencoded(trimmed))
22
- }
23
-
24
- /// Check if a URL should be blocked by adblock at navigation level
25
- pub fn navigation_blocked(app: &AppHandle, url: &str) -> bool {
26
- let state = app.state::<AdBlockState>();
27
- state.should_block(url, url, "document")
28
- }
29
-
30
- fn urlencoded(value: &str) -> String {
31
- value.bytes().flat_map(|b| match b {
32
- b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => vec![b as char],
33
- b' ' => vec!['+'],
34
- _ => format!("%{b:02X}").chars().collect(),
35
- }).collect()
36
- }
 
 
 
 
 
 
 
 
 
 
 
1
+ use tauri::{AppHandle, Manager};
2
+ use crate::adblock::engine::AdBlockState;
3
+
4
+ /// Resolve user input to a navigable URL with HTTPS-first.
5
+ pub fn resolve_url(input: &str) -> String {
6
+ let trimmed = input.trim();
7
+ if trimmed.is_empty() { return "https://duckduckgo.com".to_string(); }
8
+ let lower = trimmed.to_ascii_lowercase();
9
+ if lower.starts_with("javascript:") || lower.starts_with("data:") || lower.starts_with("file:") || lower.starts_with("tauri:") || lower.starts_with("asset:") {
10
+ return format!("https://duckduckgo.com/?q={}", urlencoded(trimmed));
11
+ }
12
+ if trimmed.starts_with("http://") || trimmed.starts_with("https://") {
13
+ if trimmed.starts_with("http://") {
14
+ let host = trimmed.split("//").nth(1).and_then(|s| s.split('/').next()).unwrap_or("");
15
+ if host != "localhost" && host != "127.0.0.1" && host != "::1" {
16
+ return trimmed.replacen("http://", "https://", 1);
17
+ }
18
+ }
19
+ return trimmed.to_string();
20
+ }
21
+ if trimmed.contains('.') && !trimmed.contains(' ') {
22
+ return format!("https://{trimmed}");
23
+ }
24
+ format!("https://duckduckgo.com/?q={}", urlencoded(trimmed))
25
+ }
26
+
27
+ /// Check if a URL should be blocked by security policy or adblock at navigation level.
28
+ pub fn navigation_blocked(app: &AppHandle, url: &str) -> bool {
29
+ let lower = url.trim().to_ascii_lowercase();
30
+ if lower.starts_with("javascript:") || lower.starts_with("data:") || lower.starts_with("file:") || lower.starts_with("tauri:") || lower.starts_with("asset:") {
31
+ return true;
32
+ }
33
+ if !(lower.starts_with("http://") || lower.starts_with("https://") || lower.starts_with("lumaref-action://")) {
34
+ return true;
35
+ }
36
+ let state = app.state::<AdBlockState>();
37
+ state.should_block(url, url, "document")
38
+ }
39
+
40
+ fn urlencoded(value: &str) -> String {
41
+ value.bytes().flat_map(|b| match b {
42
+ b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => vec![b as char],
43
+ b' ' => vec!['+'],
44
+ _ => format!("%{b:02X}").chars().collect(),
45
+ }).collect()
46
+ }