Harden auth/vault path sanitization and clean WeKnora docs

This commit is contained in:
Agent Board
2026-07-28 17:04:27 +08:00
parent 2deaf59f7b
commit 26ff1a9c9a
190 changed files with 13454 additions and 4987 deletions
+63 -42
View File
@@ -428,6 +428,36 @@ fn sock_reachable(path: &Path) -> bool {
}
}
/// Path-segment safe id for UDS HTTP paths (reject CRLF / query / slash inject).
fn sanitize_item_id_for_path(id: &str) -> Result<String, VaultError> {
let id = id.trim();
if id.is_empty() || id.len() > 128 {
return Err(VaultError::new(
VaultStatus::BadRequest,
"vault_item_id_invalid",
"vault item id 无效",
));
}
if !id
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.')
{
return Err(VaultError::new(
VaultStatus::BadRequest,
"vault_item_id_invalid",
"vault item id 含非法字符",
));
}
if id.contains("..") {
return Err(VaultError::new(
VaultStatus::BadRequest,
"vault_item_id_invalid",
"vault item id 不得含 ..",
));
}
Ok(id.to_string())
}
/// HTTP/1.1 request over UDS; returns status + body.
#[cfg(unix)]
fn uds_http(
@@ -650,7 +680,7 @@ fn cmd_list(args: ListArgs) -> Result<Value, VaultError> {
}
fn cmd_get(args: GetArgs) -> Result<Value, VaultError> {
let id = args.id.clone();
let id = sanitize_item_id_for_path(&args.id)?;
with_transport(
args.local,
args.remote,
@@ -671,7 +701,7 @@ fn cmd_get(args: GetArgs) -> Result<Value, VaultError> {
}
fn cmd_resolve(args: ResolveArgs) -> Result<Value, VaultError> {
let id = args.id.clone();
let id = sanitize_item_id_for_path(&args.id)?;
let field = args.field.clone();
let account_id = args.account_id.clone();
let secret_id = args.secret_id.clone();
@@ -712,7 +742,7 @@ fn cmd_resolve(args: ResolveArgs) -> Result<Value, VaultError> {
}
fn cmd_login(args: LoginArgs) -> Result<Value, VaultError> {
let id = args.id.clone();
let id = sanitize_item_id_for_path(&args.id)?;
let force = args.force_refresh;
with_transport(
args.local,
@@ -740,7 +770,7 @@ fn cmd_login(args: LoginArgs) -> Result<Value, VaultError> {
}
fn cmd_session(args: SessionArgs) -> Result<Value, VaultError> {
let id = args.id.clone();
let id = sanitize_item_id_for_path(&args.id)?;
let cookie = args.cookie_header.clone();
let expires = args.expires_at.clone();
let source = args.source.clone();
@@ -962,14 +992,11 @@ fn dispatch_request(raw: &str) -> (u16, String) {
if let Err(e) = authorize_bearer(auth.as_deref(), "get") {
return err_response(e);
}
let id = p.trim_start_matches("/v1/items/");
if id.is_empty() || id.contains('/') {
return err_response(VaultError::bad_request_code(
"vault_item_id_invalid",
"条目 id 无效",
));
}
match get_ai_vault_item(id) {
let id = match sanitize_item_id_for_path(p.trim_start_matches("/v1/items/")) {
Ok(id) => id,
Err(e) => return err_response(e),
};
match get_ai_vault_item(&id) {
Ok(mut v) => {
if let Some(obj) = v.as_object_mut() {
obj.insert("transport".into(), json!("uds"));
@@ -983,15 +1010,13 @@ fn dispatch_request(raw: &str) -> (u16, String) {
if let Err(e) = authorize_bearer(auth.as_deref(), "resolve") {
return err_response(e);
}
let mid = p
.trim_start_matches("/v1/items/")
.trim_end_matches("/resolve");
if mid.is_empty() {
return err_response(VaultError::bad_request_code(
"vault_item_id_invalid",
"条目 id 无效",
));
}
let mid = match sanitize_item_id_for_path(
p.trim_start_matches("/v1/items/")
.trim_end_matches("/resolve"),
) {
Ok(id) => id,
Err(e) => return err_response(e),
};
let parsed: Value = serde_json::from_str(body).unwrap_or(json!({}));
let field = parsed
.get("field")
@@ -1006,7 +1031,7 @@ fn dispatch_request(raw: &str) -> (u16, String) {
.or_else(|| parsed.get("secret_id"))
.and_then(Value::as_str);
let actor = format!("vaultd:{}", ai_vault_actor_id());
match resolve_ai_vault_secret(mid, field, &actor, None, account_id, secret_id) {
match resolve_ai_vault_secret(&mid, field, &actor, None, account_id, secret_id) {
Ok(mut v) => {
if let Some(obj) = v.as_object_mut() {
obj.insert("transport".into(), json!("uds"));
@@ -1020,15 +1045,13 @@ fn dispatch_request(raw: &str) -> (u16, String) {
if let Err(e) = authorize_bearer(auth.as_deref(), "login") {
return err_response(e);
}
let mid = p
.trim_start_matches("/v1/items/")
.trim_end_matches("/login");
if mid.is_empty() {
return err_response(VaultError::bad_request_code(
"vault_item_id_invalid",
"条目 id 无效",
));
}
let mid = match sanitize_item_id_for_path(
p.trim_start_matches("/v1/items/")
.trim_end_matches("/login"),
) {
Ok(id) => id,
Err(e) => return err_response(e),
};
let parsed: Value = serde_json::from_str(body).unwrap_or(json!({}));
let force = parsed
.get("forceRefresh")
@@ -1036,7 +1059,7 @@ fn dispatch_request(raw: &str) -> (u16, String) {
.and_then(Value::as_bool)
.unwrap_or(false);
let actor = format!("vaultd:{}", ai_vault_actor_id());
match login_ai_vault_credential(mid, force, &actor, None) {
match login_ai_vault_credential(&mid, force, &actor, None) {
Ok(mut v) => {
if let Some(obj) = v.as_object_mut() {
obj.insert("transport".into(), json!("uds"));
@@ -1050,15 +1073,13 @@ fn dispatch_request(raw: &str) -> (u16, String) {
if let Err(e) = authorize_bearer(auth.as_deref(), "session") {
return err_response(e);
}
let mid = p
.trim_start_matches("/v1/items/")
.trim_end_matches("/session");
if mid.is_empty() {
return err_response(VaultError::bad_request_code(
"vault_item_id_invalid",
"条目 id 无效",
));
}
let mid = match sanitize_item_id_for_path(
p.trim_start_matches("/v1/items/")
.trim_end_matches("/session"),
) {
Ok(id) => id,
Err(e) => return err_response(e),
};
let parsed: Value = serde_json::from_str(body).unwrap_or(json!({}));
let cookie = parsed
.get("cookieHeader")
@@ -1074,7 +1095,7 @@ fn dispatch_request(raw: &str) -> (u16, String) {
.and_then(Value::as_str)
.unwrap_or("human_bridge");
let actor = format!("vaultd:{}", ai_vault_actor_id());
match put_ai_vault_session(mid, cookie, expires, source, &actor, None) {
match put_ai_vault_session(&mid, cookie, expires, source, &actor, None) {
Ok(mut v) => {
if let Some(obj) = v.as_object_mut() {
obj.insert("transport".into(), json!("uds"));