fix(secrets-mcp 0.5.20): code review plan — export secret types, env map, rollback, API key, MCP tools, web session & validation

- Export/import: optional secret_types map; AddResult includes entry_id
- env_map: dot→__ segment encoding; collision errors
- rollback: FOR UPDATE + txn-consistent snapshot; restore name from history
- regenerate_api_key: rows_affected guard
- MCP: find count propagates errors; add uses entry_id for relations; rollback no encryption key
- Web: load_session_user_strict + JSON handlers key_version; PATCH length limits
- Tests: ExportEntry serde, env segment
This commit is contained in:
voson
2026-04-11 17:10:16 +08:00
parent 2c7dbf890b
commit d772066210
13 changed files with 266 additions and 141 deletions

View File

@@ -44,21 +44,23 @@ pub async fn export(
let mut export_entries: Vec<ExportEntry> = Vec::with_capacity(entries.len());
for entry in &entries {
let secrets = if params.no_secrets {
None
let (secrets, secret_types) = if params.no_secrets {
(None, None)
} else {
let fields = secrets_map.get(&entry.id).map(Vec::as_slice).unwrap_or(&[]);
if fields.is_empty() {
Some(BTreeMap::new())
(Some(BTreeMap::new()), Some(BTreeMap::new()))
} else {
let mk = master_key
.ok_or_else(|| anyhow::anyhow!("master key required to decrypt secrets"))?;
let mut map = BTreeMap::new();
let mut type_map = BTreeMap::new();
for f in fields {
let decrypted = crypto::decrypt_json(mk, &f.encrypted)?;
map.insert(f.name.clone(), decrypted);
type_map.insert(f.name.clone(), f.secret_type.clone());
}
Some(map)
(Some(map), Some(type_map))
}
};
@@ -70,6 +72,7 @@ pub async fn export(
tags: entry.tags.clone(),
metadata: entry.metadata.clone(),
secrets,
secret_types,
});
}