Files
khadhroony-bot3/ks-wallet/tests/legacy_characterization.rs
2026-08-11 15:15:03 +02:00

210 lines
9.5 KiB
Rust

// file: ks-wallet/tests/legacy_characterization.rs
// version: 2
//! External characterization tests for the legacy Solana JSON wallet format.
use solana_signer::Signer; // rust-rules: trait-import
use zeroize::Zeroize; // rust-rules: trait-import
fn write_legacy_fixture(path: &std::path::Path, keypair: &solana_keypair::Keypair) {
let mut keypair_bytes = keypair.to_bytes();
let encoded = serde_json::to_vec(keypair_bytes.as_slice())
.unwrap_or_else(|error| panic!("legacy fixture must serialize: {error}"));
keypair_bytes.zeroize();
std::fs::write(path, encoded)
.unwrap_or_else(|error| panic!("legacy fixture must be writable: {error}"));
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))
.unwrap_or_else(|error| panic!("legacy fixture permissions must be set: {error}"));
}
}
#[test]
fn legacy_path_is_alias_json() {
let directory = tempfile::tempdir()
.unwrap_or_else(|error| panic!("temporary directory must exist: {error}"));
let store = ks_wallet::TemporaryWalletStore::new(directory.path())
.unwrap_or_else(|error| panic!("unexpected store error: {error}"));
let alias = ks_wallet::WalletAlias::parse("legacy-name")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
assert_eq!(
store.wallet_path(&alias).file_name(),
std::option::Option::Some(std::ffi::OsStr::new("legacy-name.json"))
);
}
#[tokio::test]
async fn legacy_standard_json_fixture_preserves_public_key() {
let directory = tempfile::tempdir()
.unwrap_or_else(|error| panic!("temporary directory must exist: {error}"));
let store = ks_wallet::TemporaryWalletStore::new(directory.path())
.unwrap_or_else(|error| panic!("unexpected store error: {error}"));
let alias = ks_wallet::WalletAlias::parse("legacy-fixture")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
let expected = solana_keypair::Keypair::new();
write_legacy_fixture(&store.wallet_path(&alias), &expected);
let loaded = store
.load(alias)
.await
.unwrap_or_else(|error| panic!("legacy fixture must load: {error}"));
assert_eq!(loaded.public_key(), expected.pubkey().to_string());
assert_eq!(loaded.persistence(), ks_wallet::WalletPersistence::Persistent);
}
#[tokio::test]
async fn legacy_missing_file_preserves_current_error_contract() {
let directory = tempfile::tempdir()
.unwrap_or_else(|error| panic!("temporary directory must exist: {error}"));
let store = ks_wallet::TemporaryWalletStore::new(directory.path())
.unwrap_or_else(|error| panic!("unexpected store error: {error}"));
let alias = ks_wallet::WalletAlias::parse("missing")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
let error = store
.load(alias)
.await
.err()
.unwrap_or_else(|| panic!("missing legacy wallet must fail"));
assert_eq!(error.code(), "wallet_file_metadata_failed");
}
#[tokio::test]
async fn legacy_errors_and_debug_do_not_disclose_local_path() {
let directory = tempfile::tempdir()
.unwrap_or_else(|error| panic!("temporary directory must exist: {error}"));
let canary_directory = directory.path().join("wallet-path-canary");
std::fs::create_dir_all(&canary_directory)
.unwrap_or_else(|error| panic!("canary directory must be created: {error}"));
let store = ks_wallet::TemporaryWalletStore::new(&canary_directory)
.unwrap_or_else(|error| panic!("unexpected store error: {error}"));
let debug = format!("{store:?}");
assert!(!debug.contains("wallet-path-canary"));
assert!(!debug.contains(canary_directory.to_string_lossy().as_ref()));
let alias = ks_wallet::WalletAlias::parse("missing-canary")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
let error = store
.load(alias)
.await
.err()
.unwrap_or_else(|| panic!("missing legacy wallet must fail"));
let message = error.to_string();
assert_eq!(error.code(), "wallet_file_metadata_failed");
assert!(!message.contains("wallet-path-canary"));
assert!(!message.contains(canary_directory.to_string_lossy().as_ref()));
}
#[tokio::test]
async fn legacy_corrupted_json_and_short_keypair_are_distinct() {
let directory = tempfile::tempdir()
.unwrap_or_else(|error| panic!("temporary directory must exist: {error}"));
let store = ks_wallet::TemporaryWalletStore::new(directory.path())
.unwrap_or_else(|error| panic!("unexpected store error: {error}"));
let corrupted_alias = ks_wallet::WalletAlias::parse("corrupted-json")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
let corrupted_path = store.wallet_path(&corrupted_alias);
std::fs::write(&corrupted_path, b"not-json")
.unwrap_or_else(|error| panic!("corrupted fixture must be writable: {error}"));
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import
std::fs::set_permissions(&corrupted_path, std::fs::Permissions::from_mode(0o600))
.unwrap_or_else(|error| panic!("fixture permissions must be set: {error}"));
}
let corrupted_error = store
.load(corrupted_alias)
.await
.err()
.unwrap_or_else(|| panic!("corrupted JSON must fail"));
assert_eq!(corrupted_error.code(), "wallet_keypair_json_invalid");
let short_alias = ks_wallet::WalletAlias::parse("short-keypair")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
let short_path = store.wallet_path(&short_alias);
std::fs::write(&short_path, b"[1,2,3]")
.unwrap_or_else(|error| panic!("short fixture must be writable: {error}"));
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import
std::fs::set_permissions(&short_path, std::fs::Permissions::from_mode(0o600))
.unwrap_or_else(|error| panic!("fixture permissions must be set: {error}"));
}
let short_error = store
.load(short_alias)
.await
.err()
.unwrap_or_else(|| panic!("short keypair must fail"));
assert_eq!(short_error.code(), "wallet_keypair_length_invalid");
}
#[tokio::test]
async fn legacy_create_refuses_existing_wallet_and_preserves_first_pubkey() {
let directory = tempfile::tempdir()
.unwrap_or_else(|error| panic!("temporary directory must exist: {error}"));
let store = ks_wallet::TemporaryWalletStore::new(directory.path())
.unwrap_or_else(|error| panic!("unexpected store error: {error}"));
let alias = ks_wallet::WalletAlias::parse("collision")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
let first = store
.create(alias.clone())
.await
.unwrap_or_else(|error| panic!("first legacy wallet must be created: {error}"));
let second_error = store
.create(alias.clone())
.await
.err()
.unwrap_or_else(|| panic!("second legacy create must fail"));
assert_eq!(second_error.code(), "wallet_file_already_exists");
let loaded = store
.load(alias)
.await
.unwrap_or_else(|error| panic!("first legacy wallet must remain readable: {error}"));
assert_eq!(loaded.public_key(), first.public_key());
}
#[cfg(unix)]
#[tokio::test]
async fn legacy_file_with_open_permissions_is_rejected() {
use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import
let directory = tempfile::tempdir()
.unwrap_or_else(|error| panic!("temporary directory must exist: {error}"));
let store = ks_wallet::TemporaryWalletStore::new(directory.path())
.unwrap_or_else(|error| panic!("unexpected store error: {error}"));
let alias = ks_wallet::WalletAlias::parse("open-permissions")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
let keypair = solana_keypair::Keypair::new();
let path = store.wallet_path(&alias);
write_legacy_fixture(&path, &keypair);
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o644))
.unwrap_or_else(|error| panic!("fixture permissions must be changed: {error}"));
let error = store
.load(alias)
.await
.err()
.unwrap_or_else(|| panic!("open permissions must fail"));
assert_eq!(error.code(), "wallet_file_permissions_too_open");
}
#[cfg(unix)]
#[tokio::test]
async fn legacy_symlink_is_rejected() {
let directory = tempfile::tempdir()
.unwrap_or_else(|error| panic!("temporary directory must exist: {error}"));
let store = ks_wallet::TemporaryWalletStore::new(directory.path())
.unwrap_or_else(|error| panic!("unexpected store error: {error}"));
let target_alias = ks_wallet::WalletAlias::parse("target")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
let link_alias = ks_wallet::WalletAlias::parse("linked")
.unwrap_or_else(|error| panic!("unexpected alias error: {error}"));
let keypair = solana_keypair::Keypair::new();
let target_path = store.wallet_path(&target_alias);
write_legacy_fixture(&target_path, &keypair);
std::os::unix::fs::symlink(&target_path, store.wallet_path(&link_alias))
.unwrap_or_else(|error| panic!("legacy symlink fixture must be created: {error}"));
let error = store
.load(link_alias)
.await
.err()
.unwrap_or_else(|| panic!("legacy symlink must fail"));
assert_eq!(error.code(), "wallet_file_type_invalid");
}