Harden auth/vault path sanitization and clean WeKnora docs
This commit is contained in:
@@ -23,11 +23,13 @@ pub fn ai_vault_actor_id() -> String {
|
||||
}
|
||||
|
||||
/// Encode actor id for managed path segment (aligned with mnote-web).
|
||||
///
|
||||
/// 不保留 `.` / `/` 等路径元字符,避免 actor=`..` 时穿越 `users/` 目录。
|
||||
pub fn encode_actor_segment(actor_id: &str) -> String {
|
||||
actor_id
|
||||
.chars()
|
||||
.map(|c| match c {
|
||||
'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' => c.to_string(),
|
||||
'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' => c.to_string(),
|
||||
other => format!("~{:02x}", other as u32),
|
||||
})
|
||||
.collect()
|
||||
@@ -786,6 +788,17 @@ mod tests {
|
||||
|
||||
static AI_TEST_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
#[test]
|
||||
fn encode_actor_segment_rejects_path_traversal_dots() {
|
||||
// `..` 不得原样保留,否则 join("users", "..") 可穿越目录。
|
||||
let encoded = encode_actor_segment("..");
|
||||
assert!(!encoded.contains('.'), "encoded={encoded}");
|
||||
assert_eq!(encoded, "~2e~2e");
|
||||
let dotted = encode_actor_segment("a.b");
|
||||
assert!(!dotted.contains('.'), "encoded={dotted}");
|
||||
assert_eq!(dotted, "a~2eb");
|
||||
}
|
||||
|
||||
fn with_temp_workspace<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&Path) -> R,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -108,10 +108,11 @@ pub fn load_or_create_hmac_key(path: &Path) -> Result<Vec<u8>, VaultError> {
|
||||
)
|
||||
})?;
|
||||
let hex = raw.trim();
|
||||
if hex.len() < 32 {
|
||||
// 生成侧写 32 字节 → 64 hex;读取侧要求一致,拒绝弱 key。
|
||||
if hex.len() < 64 {
|
||||
return Err(VaultError::bad_request_code(
|
||||
"vault_hmac_key_invalid",
|
||||
"HMAC key 过短",
|
||||
"HMAC key 过短(需要至少 32 字节 / 64 hex)",
|
||||
));
|
||||
}
|
||||
return hex::decode(hex).map_err(|e| {
|
||||
|
||||
Reference in New Issue
Block a user