Files
khadhroony-solana-project/crates/ksp-app-wallet-desk/tests/desktop_security.rs
2026-08-22 08:18:38 +02:00

325 lines
16 KiB
Rust

// file: crates/ksp-app-wallet-desk/tests/desktop_security.rs
// version: 14
//! Static desktop security contracts for the Wallet Desk pre.002 shell.
fn app_root() -> std::path::PathBuf {
return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
}
fn read_text(path: &std::path::Path) -> String {
let source = std::fs::read_to_string(path);
assert!(source.is_ok(), "unable to read {}", path.display());
return match source {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => String::new(),
};
}
#[test]
fn initial_capability_surface_is_core_plus_tracing_only() {
let root = app_root();
let capability = read_text(root.join("capabilities/default.json").as_path());
assert!(capability.contains("\"core:default\""));
assert!(capability.contains("\"tracing:default\""));
assert!(!capability.contains("dialog:"));
assert!(!capability.contains("fs:"));
}
#[test]
fn frontend_shell_uses_no_browser_native_dialog_or_secret_storage() {
let root = app_root();
let main = read_text(root.join("frontend/ts/main.ts").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
for forbidden in ["window.alert", "window.confirm", "window.prompt", "localStorage", "sessionStorage"] {
assert!(!main.contains(forbidden), "forbidden frontend primitive {forbidden}");
assert!(!html.contains(forbidden), "forbidden frontend primitive {forbidden}");
}
}
#[test]
fn locked_inventory_frontend_never_receives_protected_wallet_identity_fields() {
let root = app_root();
let inventory = read_text(root.join("src/wallet_inventory.rs").as_path());
assert!(!inventory.contains("pubkey:"));
assert!(!inventory.contains("alias:"));
assert!(!inventory.contains("notes:"));
assert!(!inventory.contains("password:"));
}
#[test]
fn create_passwords_are_request_only_and_protected_projection_is_purged() {
let root = app_root();
let session = read_text(root.join("src/wallet_session.rs").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
let authorized = session.split("pub(crate) struct WalletAuthorizedDto").nth(1);
assert!(authorized.is_some());
let authorized = authorized.unwrap_or_default().split("}\n").next().unwrap_or_default();
let authorized_fields = authorized.lines().filter(|line| return line.trim_start().starts_with("pub(crate) ")).collect::<std::vec::Vec<_>>().join("\n");
assert!(!authorized_fields.contains("password"));
let request_start = session.find("pub(crate) struct WalletCreateRequestDto");
assert!(request_start.is_some());
let request_start = request_start.unwrap_or_default();
assert!(session[request_start..].contains("owner_password"));
assert!(session[request_start..].contains("view_password"));
assert!(main.contains("clearAuthorizedProjection"));
assert!(main.contains("clearCreateFormSensitiveInputs"));
assert!(!main.contains("frontendDebug(\"main\", request.ownerPassword"));
assert!(!main.contains("frontendTrace(\"main\", request.viewPassword"));
}
#[test]
fn unlock_passwords_and_config_secret_names_never_cross_rust_to_frontend() {
let root = app_root();
let session = read_text(root.join("src/wallet_session.rs").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
let authorized = session.split("pub(crate) struct WalletAuthorizedDto").nth(1);
assert!(authorized.is_some());
let authorized = authorized.unwrap_or_default().split("}\n").next().unwrap_or_default();
let authorized_fields = authorized.lines().filter(|line| return line.trim_start().starts_with("pub(crate) ")).collect::<std::vec::Vec<_>>().join("\n");
assert!(!authorized_fields.contains("password"));
let unlock_request = session.find("pub(crate) struct WalletUnlockRequestDto");
assert!(unlock_request.is_some());
assert!(session[unlock_request.unwrap_or_default()..].contains("password: String"));
assert!(main.contains("clearUnlockSensitiveInputs"));
assert!(!main.contains("KSP_SECRET_WALLET_PASS_"));
assert!(!html.contains("KSP_SECRET_WALLET_PASS_"));
assert!(!main.contains("variableName"));
assert!(!main.contains("effectiveSource"));
}
#[test]
fn wallet_selection_does_not_auto_unlock_configured_secrets() {
let root = app_root();
let main = read_text(root.join("frontend/ts/main.ts").as_path());
let selection_start = main.find("async function selectWallet");
let selection_end = main.find("function bindWalletTableSelection");
assert!(selection_start.is_some());
assert!(selection_end.is_some());
let selection = &main[selection_start.unwrap_or_default()..selection_end.unwrap_or(main.len())];
assert!(selection.contains("select_wallet"));
assert!(!selection.contains("unlock_wallet_view_configured"));
assert!(!selection.contains("unlock_wallet_owner_configured"));
}
#[test]
fn balance_refresh_never_accepts_pubkey_or_endpoint_url_from_frontend() {
let root = app_root();
let state = read_text(root.join("src/app_state.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
let command_start = tauri.find("async fn refresh_wallet_balance");
assert!(command_start.is_some());
let command = &tauri[command_start.unwrap_or_default()..];
let command = command.split("#[tauri::command]").next().unwrap_or_default();
assert!(!command.contains("request:"));
assert!(!command.contains("pubkey"));
let refresh_start = main.find("async function refreshWalletBalance");
let refresh_end = main.find("function bindBalanceActions");
assert!(refresh_start.is_some());
assert!(refresh_end.is_some());
let refresh = &main[refresh_start.unwrap_or_default()..refresh_end.unwrap_or(main.len())];
assert!(refresh.contains("refresh_wallet_balance"));
assert!(!refresh.contains("pubkey"));
assert!(!main.contains("transportUrl"));
assert!(!main.contains("endpointUrl"));
assert!(state.contains("wallet.pubkey()"));
}
#[test]
fn import_picker_path_and_transfer_secret_never_cross_into_frontend() {
let root = app_root();
let package = read_text(root.join("package.json").as_path());
let capability = read_text(root.join("capabilities/default.json").as_path());
let import = read_text(root.join("src/wallet_import.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(!package.contains("@tauri-apps/plugin-dialog"));
assert!(!capability.contains("dialog:"));
assert!(!capability.contains("fs:"));
assert!(!main.contains("@tauri-apps/plugin-dialog"));
assert!(!main.contains("sourcePath"));
assert!(!main.contains("source_path"));
assert!(import.contains("zeroize::Zeroizing"));
let inspection = import.split("pub(crate) struct WalletTransferInspectionDto").nth(1);
assert!(inspection.is_some());
let inspection = inspection.unwrap_or_default().split("}\n").next().unwrap_or_default();
let inspection_fields = inspection.lines().filter(|line| return line.trim_start().starts_with("pub(crate) ")).collect::<std::vec::Vec<_>>().join("\n");
assert!(!inspection_fields.contains("path"));
assert!(!inspection_fields.contains("password"));
let request = import.split("pub(crate) struct WalletImportRequestDto").nth(1);
assert!(request.is_some());
let request = request.unwrap_or_default().split("}\n").next().unwrap_or_default();
let request_fields = request.lines().filter(|line| return line.trim_start().starts_with("pub(crate) ")).collect::<std::vec::Vec<_>>().join("\n");
assert!(request_fields.contains("owner_password"));
assert!(request_fields.contains("view_password"));
assert!(!request_fields.contains("path"));
let command_start = tauri.find("async fn inspect_import_source");
assert!(command_start.is_some());
let command = &tauri[command_start.unwrap_or_default()..];
let command = command.split("#[tauri::command]").next().unwrap_or_default();
assert!(command.contains("WalletImportSourceRequestDto"));
assert!(!command.contains("source_path"));
}
#[test]
fn owner_metadata_values_are_request_only_and_state_conflict_forces_reauthorization() {
let root = app_root();
let metadata = read_text(root.join("src/wallet_metadata.rs").as_path());
let state = read_text(root.join("src/app_state.rs").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(!metadata.contains("derive(Clone"));
assert!(!metadata.contains("derive(Debug"));
assert!(state.contains("recover_owner_state_conflict"));
assert!(state.contains("inspect_locked_wallet_file"));
assert!(state.contains("ERROR_CODE_STATE_CONFLICT"));
assert!(main.contains("isWalletStateConflict"));
assert!(main.contains("select_wallet"));
assert!(main.contains("clearAuthorizedProjection"));
assert!(!main.contains("frontendDebug(\"main\", request.text"));
assert!(!main.contains("frontendTrace(\"main\", request.text"));
assert!(!main.contains("frontendDebug(\"main\", request.alias"));
assert!(!main.contains("window.confirm"));
assert!(main.contains("deleteNoteModal = new Modal(modalElement)"));
}
#[test]
fn rotation_passwords_are_request_only_confirmed_frontend_side_and_never_logged() {
let root = app_root();
let security = read_text(root.join("src/wallet_security.rs").as_path());
let state = read_text(root.join("src/app_state.rs").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(!security.contains("derive(Clone"));
assert!(!security.contains("derive(Debug"));
let request = security.split("pub(crate) struct WalletPasswordRotationRequestDto").nth(1);
assert!(request.is_some());
let fields = request
.unwrap_or_default()
.split("}\n")
.next()
.unwrap_or_default()
.lines()
.filter(|line| return line.trim_start().starts_with("pub(crate) "))
.collect::<std::vec::Vec<_>>()
.join("\n");
assert_eq!(fields.matches("password").count(), 1);
assert!(!fields.contains("confirm"));
assert!(state.contains("OwnerPassword::new(request.password)"));
assert!(state.contains("ViewPassword::new(request.password)"));
assert!(state.contains("view.rotate_view_password"));
assert!(state.contains("owner.rotate_view_password"));
assert!(main.contains("password.value !== confirmation.value"));
assert!(main.contains("clearRotationSensitiveInputs"));
assert!(main.contains("viewAuthorized"));
assert!(main.contains("recoverRotationStateConflict"));
assert!(!main.contains("frontendDebug(\"main\", request.password"));
assert!(!main.contains("frontendTrace(\"main\", request.password"));
assert!(!main.contains("KSP_SECRET_WALLET_PASS_"));
}
#[test]
fn strong_view_administration_is_owner_only_uses_bootstrap_modals_and_returns_no_secret() {
let root = app_root();
let security = read_text(root.join("src/wallet_security.rs").as_path());
let state = read_text(root.join("src/app_state.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
let status = security.split("pub(crate) struct WalletViewSecurityStatusDto").nth(1);
assert!(status.is_some());
let fields = status
.unwrap_or_default()
.split("}\n")
.next()
.unwrap_or_default()
.lines()
.filter(|line| return line.trim_start().starts_with("pub(crate) "))
.collect::<std::vec::Vec<_>>()
.join("\n");
assert!(!fields.contains("password"));
assert!(!fields.contains("pubkey"));
assert!(!fields.contains("alias"));
assert!(!fields.contains("note"));
assert!(state.contains("begin_owner_operation(\"view_disable\")"));
assert!(state.contains("begin_owner_operation(\"view_recreate\")"));
assert!(!state.contains("begin_view_operation(\"view_disable\")"));
assert!(!state.contains("begin_view_operation(\"view_recreate\")"));
let disable = tauri.find("async fn disable_wallet_view");
assert!(disable.is_some());
let disable = &tauri[disable.unwrap_or_default()..];
let disable = disable.split("#[tauri::command]").next().unwrap_or_default();
assert!(!disable.contains("request:"));
let recreate = tauri.find("async fn recreate_wallet_view");
assert!(recreate.is_some());
let recreate = &tauri[recreate.unwrap_or_default()..];
let recreate = recreate.split("#[tauri::command]").next().unwrap_or_default();
assert!(recreate.contains("WalletPasswordRotationRequestDto"));
assert!(main.contains("new Modal(disableModalElement)"));
assert!(main.contains("new Modal(recreateModalElement)"));
assert!(main.contains("clearStrongViewSensitiveInputs"));
assert!(main.contains("hidden.bs.modal"));
assert!(!main.contains("window.confirm"));
assert!(!main.contains("frontendDebug(\"main\", request.password"));
assert!(!main.contains("frontendTrace(\"main\", request.password"));
}
#[test]
fn owner_export_never_projects_key_material_or_full_destination_path_to_frontend() {
let root = app_root();
let export = read_text(root.join("src/wallet_export.rs").as_path());
let state = read_text(root.join("src/app_state.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
let request = export.split("pub(crate) struct WalletExportRequestDto").nth(1);
assert!(request.is_some());
let request_fields = request
.unwrap_or_default()
.split("}\n")
.next()
.unwrap_or_default()
.lines()
.filter(|line| return line.trim_start().starts_with("pub(crate) "))
.collect::<std::vec::Vec<_>>()
.join("\n");
assert!(request_fields.contains("format"));
assert!(!export.contains("#[derive(Clone, serde::Deserialize"));
assert!(!export.contains("#[derive(Debug, serde::Deserialize"));
assert!(!request_fields.contains("path"));
assert!(!request_fields.contains("password"));
assert!(!request_fields.contains("secret"));
let result = export.split("pub(crate) struct WalletExportResultDto").nth(1);
assert!(result.is_some());
let result_fields = result
.unwrap_or_default()
.split("}\n")
.next()
.unwrap_or_default()
.lines()
.filter(|line| return line.trim_start().starts_with("pub(crate) "))
.collect::<std::vec::Vec<_>>()
.join("\n");
assert!(result_fields.contains("destination_name"));
assert!(result_fields.contains("format"));
assert!(result_fields.contains("wallet_id"));
assert!(!result_fields.contains("path"));
assert!(!result_fields.contains("pubkey"));
assert!(!result_fields.contains("password"));
assert!(!result_fields.contains("secret"));
assert!(!result_fields.contains("content"));
assert!(!result_fields.contains("bytes"));
assert!(state.contains("owner.export_transfer_file"));
assert!(!state.contains("owner.export_transfer(format"));
let command_start = tauri.find("async fn export_wallet_transfer");
assert!(command_start.is_some());
let command = &tauri[command_start.unwrap_or_default()..];
let command = command.split("#[tauri::command]").next().unwrap_or_default();
assert!(command.contains("WalletExportRequestDto"));
assert!(command.contains("WalletExportResultDto"));
assert!(!main.contains("navigator.clipboard"));
assert!(!main.contains("destinationPath"));
assert!(!main.contains("destination_path"));
assert!(!main.contains("keypairBytes"));
assert!(!main.contains("secretKey"));
assert!(main.contains("exportWalletModal = new Modal(modalElement)"));
}