92 lines
3.3 KiB
Rust
92 lines
3.3 KiB
Rust
// API/security contract for unauthenticated relay RPC rejection.
|
|
//
|
|
// Scope: preserve the security boundary between transport authentication and
|
|
// handler execution for media, HID, and paste RPCs.
|
|
// Targets: server entrypoint and relay helper modules.
|
|
// Why: without TLS/mTLS on the server builder, any client could stream media or
|
|
// HID reports into the remote controlled target.
|
|
|
|
const ENTRYPOINT: &str = include_str!(concat!(
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
"/server/src/main/entrypoint.rs"
|
|
));
|
|
const SERVER_SECURITY: &str = include_str!(concat!(
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
"/server/src/security.rs"
|
|
));
|
|
const INPUT_RPC: &str = include_str!(concat!(
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
"/server/src/main/relay_service/input_stream_rpc.rs"
|
|
));
|
|
const CAMERA_RPC: &str = include_str!(concat!(
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
"/server/src/main/relay_service/camera_stream_rpc.rs"
|
|
));
|
|
const MICROPHONE_RPC: &str = include_str!(concat!(
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
"/server/src/main/relay_service/microphone_stream_rpc.rs"
|
|
));
|
|
const RPC_HELPERS: &str = include_str!(concat!(
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
"/server/src/main/rpc_helpers.rs"
|
|
));
|
|
|
|
#[test]
|
|
fn server_installs_tls_before_exposing_relay_services() {
|
|
let tls_idx = ENTRYPOINT
|
|
.find("security::server_tls_config()?")
|
|
.expect("entrypoint should request TLS config");
|
|
let relay_idx = ENTRYPOINT
|
|
.find(".add_service(RelayServer::new(handler))")
|
|
.expect("entrypoint should expose relay service");
|
|
assert!(
|
|
tls_idx < relay_idx,
|
|
"TLS must be configured before media/HID RPC services are exposed"
|
|
);
|
|
assert!(ENTRYPOINT.contains("server = server.tls_config(tls)?"));
|
|
}
|
|
|
|
#[test]
|
|
fn production_mtls_requires_a_client_ca_unless_operator_explicitly_opts_out() {
|
|
assert!(SERVER_SECURITY.contains("client_ca_root"));
|
|
assert!(SERVER_SECURITY.contains("LESAVKA_TLS_CLIENT_AUTH_OPTIONAL"));
|
|
assert!(
|
|
SERVER_SECURITY.contains("TLS enabled with required client certificate authentication"),
|
|
"required client certificate auth should be the normal CA-backed path"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn powerful_rpc_handlers_depend_on_transport_auth_boundary() {
|
|
for (name, source, lease_marker) in [
|
|
("keyboard", INPUT_RPC, "capture_power.acquire_session()"),
|
|
("mouse", INPUT_RPC, "capture_power.acquire_session()"),
|
|
("camera", CAMERA_RPC, "activate_camera()"),
|
|
("microphone", MICROPHONE_RPC, "reserve_microphone_sink"),
|
|
] {
|
|
assert!(
|
|
source.contains(lease_marker),
|
|
"{name} RPC should continue using runtime leases after transport auth"
|
|
);
|
|
assert!(
|
|
!source.contains("LESAVKA_ALLOW_INSECURE"),
|
|
"{name} RPC should not override transport security inside handler code"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn unauthenticated_paste_payloads_map_to_unauthenticated_status() {
|
|
assert!(RPC_HELPERS.contains("paste::decrypt(&req)"));
|
|
assert!(RPC_HELPERS.contains("Status::unauthenticated"));
|
|
assert!(
|
|
RPC_HELPERS
|
|
.find("paste::decrypt(&req)")
|
|
.expect("decrypt marker")
|
|
< RPC_HELPERS
|
|
.find("paste::type_text")
|
|
.expect("type text marker"),
|
|
"paste payloads must authenticate/decrypt before HID typing begins"
|
|
);
|
|
}
|