Harden auth/vault path sanitization and clean WeKnora docs
This commit is contained in:
@@ -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"));
|
||||
|
||||
Reference in New Issue
Block a user