Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cli/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,11 @@ pub fn parse_flags(args: &[String]) -> Flags {
.and_then(|s| s.parse().ok())
.or(config.max_output),
allowed_domains: config.allowed_domains,
navigation_domains: config.navigation_domains,
navigation_domains: config.navigation_domains.map(|domains| {
crate::native::network::filter_by_ceiling(
domains.into_iter().map(|d| d.to_lowercase()).collect(),
)
}),
resource_domains: config.resource_domains,
action_policy: config.action_policy,
confirm_actions: env::var("AGENT_BROWSER_CONFIRM_ACTIONS")
Expand Down
8 changes: 7 additions & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use connection::{ensure_daemon, get_socket_dir, send_command, DaemonOptions};
use flags::{clean_args, parse_flags, Flags};
use install::run_install;
use output::{
print_command_help, print_help, print_response_with_opts, print_version, OutputOptions,
print_ceiling, print_command_help, print_help, print_response_with_opts, print_version,
OutputOptions,
};
use upgrade::run_upgrade;

Expand Down Expand Up @@ -341,6 +342,11 @@ fn main() {
return;
}

if args.iter().any(|a| a == "--show-ceiling") {
print_ceiling();
return;
}

if clean.is_empty() {
print_help();
return;
Expand Down
63 changes: 62 additions & 1 deletion cli/src/native/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ pub struct DaemonState {
pub backend_type: BackendType,
pub ref_map: RefMap,
pub domain_filter: Arc<RwLock<Option<DomainFilter>>>,
/// Per-session cache of localhost handshake results (host:port → passed).
pub handshake_cache: Arc<RwLock<HashMap<String, bool>>>,
pub event_tracker: EventTracker,
pub session_name: Option<String>,
pub session_id: String,
Expand Down Expand Up @@ -222,6 +224,7 @@ impl DaemonState {
backend_type: BackendType::Cdp,
ref_map: RefMap::new(),
domain_filter: Arc::new(RwLock::new(None)),
handshake_cache: Arc::new(RwLock::new(HashMap::new())),
event_tracker: EventTracker::new(),
session_name: env::var("AGENT_BROWSER_SESSION_NAME").ok(),
session_id: env::var("AGENT_BROWSER_SESSION").unwrap_or_else(|_| "default".to_string()),
Expand Down Expand Up @@ -297,6 +300,7 @@ impl DaemonState {
let client = browser.client.clone();
let mut rx = browser.client.subscribe();
let domain_filter = self.domain_filter.clone();
let handshake_cache = self.handshake_cache.clone();
let routes = self.routes.clone();
let origin_headers = self.origin_headers.clone();
let proxy_credentials = self.proxy_credentials.clone();
Expand Down Expand Up @@ -383,7 +387,7 @@ impl DaemonState {
let rt = routes.read().await;
let oh = origin_headers.read().await;

resolve_fetch_paused(&client, df.as_ref(), &rt, &oh, &paused).await;
resolve_fetch_paused(&client, df.as_ref(), &handshake_cache, &rt, &oh, &paused).await;
}
Ok(_) => continue,
Err(broadcast::error::RecvError::Lagged(_)) => continue,
Expand Down Expand Up @@ -1709,6 +1713,12 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
}
}

// Clear handshake cache for the new browser session
{
let mut cache = state.handshake_cache.write().await;
cache.clear();
}

state.engine = engine.as_deref().unwrap_or("chrome").to_string();
write_engine_file(&state.session_id, &state.engine);
write_extensions_file(&state.session_id);
Expand Down Expand Up @@ -1859,6 +1869,23 @@ async fn handle_navigate(cmd: &Value, state: &mut DaemonState) -> Result<Value,
}
}

// Localhost handshake check
{
if let Ok(parsed) = url::Url::parse(url) {
if let Some(hostname) = parsed.host_str() {
if network::is_localhost(hostname) {
let port = parsed.port().unwrap_or(80);
network::check_cached_handshake(
&state.handshake_cache,
hostname,
port,
)
.await?;
}
}
}
}

// WebDriver backend path
if let Some(ref wb) = state.webdriver_backend {
if state.browser.is_none() {
Expand Down Expand Up @@ -5973,6 +6000,7 @@ fn browser_metadata_from_version(version: &Value) -> Option<Value> {
async fn resolve_fetch_paused(
client: &CdpClient,
domain_filter: Option<&DomainFilter>,
handshake_cache: &RwLock<HashMap<String, bool>>,
routes: &[RouteEntry],
origin_headers: &HashMap<String, HashMap<String, String>>,
paused: &FetchPausedRequest,
Expand Down Expand Up @@ -6052,6 +6080,39 @@ async fn resolve_fetch_paused(
}
return;
}

// Localhost handshake check for document navigations
if is_document && network::is_localhost(hostname) {
let port = parsed.port().unwrap_or(80);
if network::check_cached_handshake(handshake_cache, hostname, port)
.await
.is_err()
{
let error_body = format!(
"<html><body><h1>Blocked</h1><p>localhost:{} did not pass application handshake.</p></body></html>",
port
);
let encoded = base64::Engine::encode(
&base64::engine::general_purpose::STANDARD,
error_body.as_bytes(),
);
let _ = client
.send_command(
"Fetch.fulfillRequest",
Some(json!({
"requestId": paused.request_id,
"responseCode": 403,
"responseHeaders": [
{ "name": "Content-Type", "value": "text/html" },
],
"body": encoded,
})),
Some(session_id),
)
.await;
return;
}
}
}
}
}
Expand Down
Loading