diff --git a/Cargo.lock b/Cargo.lock index 4b1f7f1..b3b2667 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,13 +152,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.91" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.2", + "syn 3.0.4", ] [[package]] @@ -243,9 +243,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" dependencies = [ "async-channel", "async-task", @@ -274,9 +274,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chacha20" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", "cpufeatures 0.3.0", @@ -425,9 +425,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "der" @@ -581,13 +581,13 @@ dependencies = [ [[package]] name = "foreign-types-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.4", ] [[package]] @@ -608,15 +608,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-lite" @@ -633,15 +633,15 @@ dependencies = [ [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-task", @@ -712,9 +712,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -728,9 +728,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hybrid-array" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" dependencies = [ "typenum", ] @@ -753,9 +753,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -764,9 +764,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.188" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22053b6a34f84abc97f9129e61334f40174659a1b9bd18c970b83db6a9a6348b" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "linux-raw-sys" @@ -776,9 +776,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "memchr" @@ -1014,7 +1014,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.2", + "syn 3.0.4", ] [[package]] @@ -1038,7 +1038,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 3.0.2", + "syn 3.0.4", ] [[package]] @@ -1123,9 +1123,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a207d6d6a2b7fc470b80443726053f18a2481b7e1eee970597051596567987a3" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -1147,22 +1147,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.2", + "syn 3.0.4", ] [[package]] @@ -1188,9 +1188,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow", ] @@ -1267,9 +1267,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "uuid" -version = "1.24.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "js-sys", "serde_core", @@ -1290,9 +1290,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -1303,9 +1303,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1313,9 +1313,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", @@ -1326,9 +1326,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] @@ -1561,14 +1561,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.18.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe96480bed92df2b442a1a30df364e12d08eed03aeb061f2b8dc6afb2be91119" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.4", "zbus_names", "zvariant", "zvariant_utils", @@ -1587,9 +1587,9 @@ dependencies = [ [[package]] name = "zbus_xml" -version = "5.2.0" +version = "5.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ab0513c0a66a60a8718d5ad712a5094845a20d95b5c1c81e2bd8e904a52b4f" +checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" dependencies = [ "serde", "winnow", @@ -1597,6 +1597,15 @@ dependencies = [ "zvariant", ] +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + [[package]] name = "zeroize" version = "1.9.0" @@ -1611,40 +1620,41 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zvariant" -version = "5.13.1" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911" +checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147" dependencies = [ "endi", "enumflags2", "serde", "winnow", + "zcheapstr", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.13.1" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12" +checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.4", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.5.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.119", + "syn 3.0.4", "winnow", ] diff --git a/src/cdp.rs b/src/cdp.rs index a71f386..5acf0c8 100644 --- a/src/cdp.rs +++ b/src/cdp.rs @@ -1422,7 +1422,7 @@ impl Executor for CdpExecutor { } } -fn fetch_json_targets(config: &CdpConfig) -> Result { +fn discover_websocket_url(config: &CdpConfig) -> Result { let mut stream = TcpStream::connect_timeout(&config.endpoint(), MAX_IO_TIMEOUT) .map_err(|_| CdpError::Protocol)?; stream @@ -1486,11 +1486,7 @@ fn fetch_json_targets(config: &CdpConfig) -> Result { if body.len() != content_length { return Err(CdpError::Protocol); } - serde_json::from_slice(&body).map_err(|_| CdpError::Protocol) -} - -fn discover_websocket_url(config: &CdpConfig) -> Result { - let targets = fetch_json_targets(config)?; + let targets: Value = serde_json::from_slice(&body).map_err(|_| CdpError::Protocol)?; let mut matches = targets .as_array() .ok_or(CdpError::Protocol)? @@ -1745,7 +1741,7 @@ fn endpoint_owner_process_ids(port: u16, _process_id: u32) -> Result= descriptors.len() || written % 8 != 0 { return Err(CdpError::StaleTarget); } - for descriptor in descriptors[..written].as_chunks::<8>().0 { + for descriptor in descriptors[..written].as_chunks::<8>().0.iter() { let file_descriptor = i32::from_ne_bytes(descriptor[..4].try_into().map_err(|_| CdpError::Protocol)?); let descriptor_type = u32::from_ne_bytes( @@ -1784,12 +1780,14 @@ fn endpoint_owner_process_ids(port: u16, _process_id: u32) -> Result Result, CdpError> { +fn endpoint_owner_process_ids(port: u16, _process_id: u32) -> Result, CdpError> { const AF_INET: u32 = 2; const ERROR_INSUFFICIENT_BUFFER: u32 = 122; const MAX_TABLE_BYTES: usize = 16 * 1024 * 1024; const NO_ERROR: u32 = 0; + const TCP_LISTEN: u32 = 2; const TCP_TABLE_OWNER_PID_LISTENER: i32 = 3; + const TCP_ROW_BYTES: usize = 24; #[link(name = "iphlpapi")] unsafe extern "system" { @@ -1840,16 +1838,6 @@ fn get_extended_tcp_table() -> Result, CdpError> { if written > table.len() || written < 4 { return Err(CdpError::Protocol); } - table.truncate(written); - Ok(table) -} - -#[cfg(windows)] -fn endpoint_owner_process_ids(port: u16, _process_id: u32) -> Result, CdpError> { - const TCP_LISTEN: u32 = 2; - const TCP_ROW_BYTES: usize = 24; - - let table = get_extended_tcp_table()?; let row_count = u32::from_ne_bytes(table[..4].try_into().map_err(|_| CdpError::Protocol)?) as usize; if 4usize @@ -1858,7 +1846,7 @@ fn endpoint_owner_process_ids(port: u16, _process_id: u32) -> Result table.len()) + .is_none_or(|required| required > written) { return Err(CdpError::Protocol); } @@ -2762,44 +2750,6 @@ mod tests { } } - fn interactive_request( - operation_id: &str, - action: Action, - target: TargetRef, - verification: VerificationPolicy, - ) -> ActionRequest { - ActionRequest { - protocol_version: PROTOCOL_VERSION, - action_version: PROTOCOL_VERSION, - target_version: PROTOCOL_VERSION, - verification_version: PROTOCOL_VERSION, - operation_id: operation_id.to_string(), - subject: "subject".to_string(), - session_id: "session".to_string(), - authority: SignedAuthority { - grant: AuthorityGrant { - protocol_version: PROTOCOL_VERSION, - issuer: "host".to_string(), - key_id: "key".to_string(), - operation_id: operation_id.to_string(), - subject: "subject".to_string(), - session_id: "session".to_string(), - risk: SafetyClass::Reversible, - expires_at_ms: i64::MAX, - policy_generation: "generation".to_string(), - action_hash: "0".repeat(64), - }, - signature: "0".repeat(128), - }, - action, - target, - interaction_mode: crate::InteractionMode::Interactive, - deadline_at_ms: i64::MAX, - verification, - safety: SafetyClass::Reversible, - } - } - #[test] fn only_exact_local_channel_is_accepted() { let process_id = std::process::id(); @@ -2978,7 +2928,7 @@ mod tests { #[cfg(any(target_os = "linux", target_os = "macos", windows))] #[test] - fn capabilities_never_advertise_click_and_follow_live_ownership() { + fn capabilities_are_empty_before_observation() { let listener = std::net::TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).expect("listener"); let port = listener.local_addr().expect("local address").port(); let process_id = std::process::id(); @@ -2988,13 +2938,28 @@ mod tests { CdpConfig::localhost(port, "page-1", process_id, process_generation).expect("config"); let mut channel = button_channel(); channel.endpoint = config.endpoint(); - let mut executor = CdpExecutor::new(config, channel).expect("executor"); + let executor = CdpExecutor::new(config, channel).expect("executor"); let capabilities = executor.capabilities().expect("capabilities"); assert_eq!(capabilities.permissions.get("cdp"), Some(&false)); assert_eq!(capabilities.permissions.get("root_frame_only"), Some(&true)); assert!(capabilities.supported_actions.is_empty()); assert!(capabilities.action_capabilities.is_empty()); assert_eq!(capabilities.display_geometry_hash, "0".repeat(64)); + } + + #[cfg(any(target_os = "linux", target_os = "macos", windows))] + #[test] + fn capabilities_never_advertise_click_after_observation() { + let listener = std::net::TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).expect("listener"); + let port = listener.local_addr().expect("local address").port(); + let process_id = std::process::id(); + let process_generation = + CdpConfig::process_generation(process_id).expect("process generation"); + let config = + CdpConfig::localhost(port, "page-1", process_id, process_generation).expect("config"); + let mut channel = button_channel(); + channel.endpoint = config.endpoint(); + let executor = CdpExecutor::new(config, channel).expect("executor"); let observation = executor .semantic_observation(&CancellationToken::default(), i64::MAX) @@ -3035,6 +3000,25 @@ mod tests { }, ] ); + } + + #[cfg(any(target_os = "linux", target_os = "macos", windows))] + #[test] + fn capabilities_are_empty_when_observation_expires() { + let listener = std::net::TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).expect("listener"); + let port = listener.local_addr().expect("local address").port(); + let process_id = std::process::id(); + let process_generation = + CdpConfig::process_generation(process_id).expect("process generation"); + let config = + CdpConfig::localhost(port, "page-1", process_id, process_generation).expect("config"); + let mut channel = button_channel(); + channel.endpoint = config.endpoint(); + let executor = CdpExecutor::new(config, channel).expect("executor"); + + executor + .semantic_observation(&CancellationToken::default(), i64::MAX) + .expect("observation"); let expires_at_ms = { let mut latest = executor.latest.write().expect("observation"); @@ -3054,12 +3038,50 @@ mod tests { .expect("observation") .observation .expires_at_ms = expires_at_ms; + } + + #[cfg(any(target_os = "linux", target_os = "macos", windows))] + #[test] + fn capabilities_are_empty_without_live_ownership() { + let listener = std::net::TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).expect("listener"); + let port = listener.local_addr().expect("local address").port(); + let process_id = std::process::id(); + let process_generation = + CdpConfig::process_generation(process_id).expect("process generation"); + let config = + CdpConfig::localhost(port, "page-1", process_id, process_generation).expect("config"); + let mut channel = button_channel(); + channel.endpoint = config.endpoint(); + let executor = CdpExecutor::new(config, channel).expect("executor"); + + executor + .semantic_observation(&CancellationToken::default(), i64::MAX) + .expect("observation"); drop(listener); let capabilities = executor.capabilities().expect("capabilities"); assert_eq!(capabilities.permissions.get("cdp"), Some(&false)); assert!(capabilities.supported_actions.is_empty()); assert_eq!(capabilities.display_geometry_hash, "0".repeat(64)); + } + + #[cfg(any(target_os = "linux", target_os = "macos", windows))] + #[test] + fn capabilities_are_empty_with_stale_process_generation() { + let listener = std::net::TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).expect("listener"); + let port = listener.local_addr().expect("local address").port(); + let process_id = std::process::id(); + let process_generation = + CdpConfig::process_generation(process_id).expect("process generation"); + let config = + CdpConfig::localhost(port, "page-1", process_id, process_generation).expect("config"); + let mut channel = button_channel(); + channel.endpoint = config.endpoint(); + let mut executor = CdpExecutor::new(config, channel).expect("executor"); + + executor + .semantic_observation(&CancellationToken::default(), i64::MAX) + .expect("observation"); executor.config.process_generation.push_str("-stale"); let capabilities = executor.capabilities().expect("capabilities"); @@ -3837,14 +3859,38 @@ mod tests { )), ]); drop(channel); - let request = interactive_request( - "cdp-set-value", - Action::SetValue { + let request = ActionRequest { + protocol_version: PROTOCOL_VERSION, + action_version: PROTOCOL_VERSION, + target_version: PROTOCOL_VERSION, + verification_version: PROTOCOL_VERSION, + operation_id: "cdp-set-value".to_string(), + subject: "subject".to_string(), + session_id: "session".to_string(), + authority: SignedAuthority { + grant: AuthorityGrant { + protocol_version: PROTOCOL_VERSION, + issuer: "host".to_string(), + key_id: "key".to_string(), + operation_id: "cdp-set-value".to_string(), + subject: "subject".to_string(), + session_id: "session".to_string(), + risk: SafetyClass::Reversible, + expires_at_ms: i64::MAX, + policy_generation: "generation".to_string(), + action_hash: "0".repeat(64), + }, + signature: "0".repeat(128), + }, + action: Action::SetValue { value: value.to_string(), }, - TargetRef::Element { target }, - VerificationPolicy::TargetValueHash { sha256: value_hash }, - ); + target: TargetRef::Element { target }, + interaction_mode: crate::InteractionMode::Interactive, + deadline_at_ms: i64::MAX, + verification: VerificationPolicy::TargetValueHash { sha256: value_hash }, + safety: SafetyClass::Reversible, + }; let directory = tempfile::tempdir().expect("temporary directory"); crate::restrict_directory(directory.path()).expect("restrict temporary directory"); let report = Engine::new( diff --git a/src/lib.rs b/src/lib.rs index ee9a91b..eaee624 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -#![allow(unknown_lints)] -#![allow(clippy::chunks_exact_to_as_chunks)] #![allow(clippy::collapsible_if, clippy::needless_return)] use std::collections::BTreeMap; use std::fs::{File, OpenOptions}; @@ -130,17 +128,6 @@ pub enum DeliveryRoute { Unknown, } -impl DeliveryRoute { - pub fn as_str(&self) -> &'static str { - match self { - DeliveryRoute::TargetAddressed => "targetAddressed", - DeliveryRoute::Pointer => "pointer", - DeliveryRoute::PerProcessEvent => "perProcessEvent", - DeliveryRoute::Unknown => "unknown", - } - } -} - #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "snake_case")] pub enum BackgroundSupport { @@ -1267,11 +1254,69 @@ impl NativeRuntime { } #[cfg(windows)] { - return windows_native_press(_key, _count, _delay_ms); + use windows::Win32::UI::Input::KeyboardAndMouse::*; + + let vk = win_key_code(_key).ok_or(NativeError)?; + + for i in 0.._count { + if i > 0 { + if let Some(delay) = _delay_ms { + std::thread::sleep(std::time::Duration::from_millis(delay)); + } + } + let down = INPUT { + r#type: INPUT_KEYBOARD, + Anonymous: INPUT_0 { + ki: KEYBDINPUT { + wVk: vk, + wScan: 0, + dwFlags: KEYBD_EVENT_FLAGS::default(), + time: 0, + dwExtraInfo: 0, + }, + }, + }; + let up = INPUT { + r#type: INPUT_KEYBOARD, + Anonymous: INPUT_0 { + ki: KEYBDINPUT { + wVk: vk, + wScan: 0, + dwFlags: KEYEVENTF_KEYUP, + time: 0, + dwExtraInfo: 0, + }, + }, + }; + let _ = unsafe { SendInput(&[down, up], std::mem::size_of::() as i32) }; + } + return Ok(()); } #[cfg(target_os = "macos")] { - return macos_native_press(_key, _count, _delay_ms); + if !native_permissions() + .get("accessibility") + .and_then(serde_json::Value::as_bool) + .unwrap_or(false) + { + return Err(NativeError); + } + let code = mac_key_name_code(_key) + .or_else(|| { + _key.chars() + .next() + .and_then(|ch| mac_key_code(ch.to_ascii_lowercase())) + }) + .ok_or(NativeError)?; + for i in 0.._count { + if i > 0 + && let Some(delay) = _delay_ms + { + std::thread::sleep(std::time::Duration::from_millis(delay)); + } + let _ = mac_post_key(code, 0); + } + Ok(()) } #[cfg(not(any(target_os = "macos", target_os = "linux", windows)))] Err(NativeError) @@ -6759,7 +6804,7 @@ impl Drop for LedgerLock { } } -fn validate_request_versions(request: &ActionRequest) -> Result<(), ProtocolError> { +fn validate_request(request: &ActionRequest) -> Result<(), ProtocolError> { if request.protocol_version != PROTOCOL_VERSION || request.action_version != PROTOCOL_VERSION || request.target_version != PROTOCOL_VERSION @@ -6770,10 +6815,6 @@ fn validate_request_versions(request: &ActionRequest) -> Result<(), ProtocolErro "unsupported protocol version".to_string(), )); } - Ok(()) -} - -fn validate_request_identifiers(request: &ActionRequest) -> Result<(), ProtocolError> { for (name, value) in [ ("operation_id", request.operation_id.as_str()), ("subject", request.subject.as_str()), @@ -6788,10 +6829,6 @@ fn validate_request_identifiers(request: &ActionRequest) -> Result<(), ProtocolE return Err(ProtocolError::InvalidRequest(format!("invalid {name}"))); } } - Ok(()) -} - -fn validate_request_action_and_target(request: &ActionRequest) -> Result<(), ProtocolError> { let auxiliary = matches!( request.action, Action::Screenshot { .. } @@ -6840,10 +6877,6 @@ fn validate_request_action_and_target(request: &ActionRequest) -> Result<(), Pro "action requires a fenced semantic element target".to_string(), )); } - Ok(()) -} - -fn validate_request_verification(request: &ActionRequest) -> Result<(), ProtocolError> { validate_verification(&request.verification)?; match (&request.action, &request.verification) { (Action::SetValue { value }, VerificationPolicy::TargetValueHash { sha256 }) @@ -6903,14 +6936,6 @@ fn validate_request_verification(request: &ActionRequest) -> Result<(), Protocol Ok(()) } -fn validate_request(request: &ActionRequest) -> Result<(), ProtocolError> { - validate_request_versions(request)?; - validate_request_identifiers(request)?; - validate_request_action_and_target(request)?; - validate_request_verification(request)?; - Ok(()) -} - fn target_provenance_is_valid(target: &TargetRef) -> bool { match target { TargetRef::Coordinates { @@ -7874,78 +7899,6 @@ fn default_ledger_path_with_env(get_env: impl Fn(&str) -> Option, -) -> Result<(), NativeError> { - use windows::Win32::UI::Input::KeyboardAndMouse::*; - - let vk = win_key_code(_key).ok_or(NativeError)?; - - for i in 0.._count { - if i > 0 { - if let Some(delay) = _delay_ms { - std::thread::sleep(std::time::Duration::from_millis(delay)); - } - } - let down = INPUT { - r#type: INPUT_KEYBOARD, - Anonymous: INPUT_0 { - ki: KEYBDINPUT { - wVk: vk, - wScan: 0, - dwFlags: KEYBD_EVENT_FLAGS::default(), - time: 0, - dwExtraInfo: 0, - }, - }, - }; - let up = INPUT { - r#type: INPUT_KEYBOARD, - Anonymous: INPUT_0 { - ki: KEYBDINPUT { - wVk: vk, - wScan: 0, - dwFlags: KEYEVENTF_KEYUP, - time: 0, - dwExtraInfo: 0, - }, - }, - }; - let _ = unsafe { SendInput(&[down, up], std::mem::size_of::() as i32) }; - } - Ok(()) -} - -#[cfg(target_os = "macos")] -fn macos_native_press(_key: &str, _count: u32, _delay_ms: Option) -> Result<(), NativeError> { - if !native_permissions() - .get("accessibility") - .and_then(serde_json::Value::as_bool) - .unwrap_or(false) - { - return Err(NativeError); - } - let code = mac_key_name_code(_key) - .or_else(|| { - _key.chars() - .next() - .and_then(|ch| mac_key_code(ch.to_ascii_lowercase())) - }) - .ok_or(NativeError)?; - for i in 0.._count { - if i > 0 - && let Some(delay) = _delay_ms - { - std::thread::sleep(std::time::Duration::from_millis(delay)); - } - let _ = mac_post_key(code, 0); - } - Ok(()) -} - #[cfg(test)] mod tests { use super::{ @@ -8109,15 +8062,6 @@ mod tests { ); } - #[test] - fn test_delivery_route_as_str() { - use super::DeliveryRoute; - assert_eq!(DeliveryRoute::TargetAddressed.as_str(), "targetAddressed"); - assert_eq!(DeliveryRoute::Pointer.as_str(), "pointer"); - assert_eq!(DeliveryRoute::PerProcessEvent.as_str(), "perProcessEvent"); - assert_eq!(DeliveryRoute::Unknown.as_str(), "unknown"); - } - #[cfg(target_os = "macos")] #[test] fn macos_semantic_scroll_names_one_page_action_per_direction() { diff --git a/src/main.rs b/src/main.rs index 133a787..2280417 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -#![allow(unknown_lints)] -#![allow(clippy::chunks_exact_to_as_chunks)] use std::io; use std::path::PathBuf; use std::process::ExitCode;