Browse Source

feat(rust-volume-server): validate malformed fid routes in native http path

codex-rust-volume-server-bootstrap
Chris Lu 4 weeks ago
parent
commit
1ce0174b23
  1. 84
      rust/volume_server/src/main.rs

84
rust/volume_server/src/main.rs

@ -470,6 +470,24 @@ fn try_handle_native_http(
return Ok(true);
}
if matches!(parsed.method.as_str(), "GET" | "HEAD" | "POST" | "PUT") {
if let Some((vid, fid)) = extract_fid_route_parts(&parsed.path) {
if !is_valid_volume_id_token(&vid) || !is_valid_fid_token(&fid) {
consume_bytes(stream, parsed.header_len + parsed.content_length)?;
write_native_http_response(
stream,
"400 Bad Request",
"text/plain; charset=utf-8",
b"",
parsed.method == "HEAD",
parsed.request_id.as_deref(),
)?;
let _ = stream.shutdown(Shutdown::Both);
return Ok(true);
}
}
}
let is_admin_control = role == ListenerRole::HttpAdmin
&& (parsed.path == "/status" || parsed.path == "/healthz")
&& (parsed.method == "GET" || parsed.method == "HEAD");
@ -602,6 +620,72 @@ fn normalize_request_path(target: &str) -> String {
.unwrap_or(raw)
}
fn extract_fid_route_parts(path: &str) -> Option<(String, String)> {
let trimmed = path.trim_start_matches('/');
if trimmed.is_empty() {
return None;
}
let segments: Vec<&str> = trimmed.split('/').collect();
if segments.len() == 1 {
if let Some((vid, fid_token)) = segments[0].rsplit_once(',') {
return Some((vid.to_string(), strip_optional_extension(fid_token)));
}
return None;
}
if (segments.len() == 2 || segments.len() == 3)
&& !segments[0].is_empty()
&& segments[0].bytes().all(|b| b.is_ascii_digit())
{
let fid = if segments.len() == 2 {
strip_optional_extension(segments[1])
} else {
segments[1].to_string()
};
return Some((segments[0].to_string(), fid));
}
None
}
fn strip_optional_extension(token: &str) -> String {
if let Some((base, _)) = token.rsplit_once('.') {
if !base.is_empty() {
return base.to_string();
}
}
token.to_string()
}
fn is_valid_volume_id_token(vid: &str) -> bool {
!vid.is_empty() && vid.parse::<u32>().is_ok()
}
fn is_valid_fid_token(fid: &str) -> bool {
if fid.is_empty() {
return false;
}
let (base, delta) = match fid.rsplit_once('_') {
Some((base, delta)) => (base, Some(delta)),
None => (fid, None),
};
if base.len() <= 8 || base.len() > 24 {
return false;
}
if !base.bytes().all(|b| b.is_ascii_hexdigit()) {
return false;
}
if let Some(delta_value) = delta {
if delta_value.is_empty() || !delta_value.bytes().all(|b| b.is_ascii_digit()) {
return false;
}
}
true
}
fn is_public_read_method(method: &str) -> bool {
matches!(method, "GET" | "HEAD" | "OPTIONS")
}

Loading…
Cancel
Save