1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// -*- coding: utf-8 -*-
// _ __
// | |/ /___ ___ _ __ ___ _ _ (R)
// | ' </ -_) -_) '_ \/ -_) '_|
// |_|\_\___\___| .__/\___|_|
// |_|
//
// Keeper Secrets Manager
// Copyright 2024-2026 Keeper Security Inc.
// Contact: sm@keepersecurity.com
//
#[cfg(test)]
mod empty_config_tests {
use keeper_secrets_manager_core::cache::KSMCache;
use keeper_secrets_manager_core::core::{ClientOptions, SecretsManager};
use keeper_secrets_manager_core::enums::KvStoreType;
use keeper_secrets_manager_core::storage::InMemoryKeyValueStorage;
use log::Level;
/// Test: Empty JSON config ({}) without token returns Err, not panic (KSM-783)
///
/// This is the primary regression test. Before the fix, initializing with
/// an empty JSON config would panic at load_secret_key() due to
/// None.unwrap() when no KeyClientKey exists in config.
#[test]
fn test_empty_json_config_returns_error_not_panic() {
let storage = InMemoryKeyValueStorage::new(Some("{}".to_string()))
.expect("InMemoryKeyValueStorage should accept empty JSON");
let config = KvStoreType::InMemory(storage);
let options = ClientOptions::new(
String::new(),
config,
Level::Error,
None,
None,
None,
KSMCache::None,
);
let result = SecretsManager::new(options);
assert!(
result.is_err(),
"Empty config without token should return Err, not Ok"
);
if let Err(err) = &result {
let err_msg = format!("{}", err);
assert!(
err_msg.contains("secret key") || err_msg.contains("One time password"),
"Error should mention missing secret key or token, got: {}",
err_msg
);
}
}
/// Test: Empty string config returns Err, not panic (KSM-783)
#[test]
fn test_empty_string_config_returns_error_not_panic() {
let storage_result = InMemoryKeyValueStorage::new(Some(String::new()));
// Empty string may fail at storage creation or at SecretsManager::new
match storage_result {
Ok(storage) => {
let config = KvStoreType::InMemory(storage);
let options = ClientOptions::new(
String::new(),
config,
Level::Error,
None,
None,
None,
KSMCache::None,
);
let result = SecretsManager::new(options);
assert!(
result.is_err(),
"Empty string config without token should return Err, not Ok"
);
}
Err(_) => {
// Also acceptable: storage creation itself returns Err for empty string
}
}
}
/// Test: None config (InMemoryKeyValueStorage::new(None)) returns Err, not panic (KSM-783)
#[test]
fn test_none_config_returns_error_not_panic() {
let storage =
InMemoryKeyValueStorage::new(None).expect("InMemoryKeyValueStorage should accept None");
let config = KvStoreType::InMemory(storage);
let options = ClientOptions::new(
String::new(),
config,
Level::Error,
None,
None,
None,
KSMCache::None,
);
let result = SecretsManager::new(options);
assert!(
result.is_err(),
"None config without token should return Err, not Ok"
);
}
}