Skip to main content

micromegas_object_cache/
validation.rs

1use anyhow::{Result, bail};
2
3/// Validate a request key: reject structurally unsafe keys (empty, absolute, or
4/// containing `..` traversal segments) and enforce that it falls under one of
5/// the `allowed_prefixes`.
6///
7/// An empty `allowed_prefixes` list means allow-all (still enforcing structure).
8/// Callers that must fail closed are responsible for refusing to configure an
9/// empty list.
10pub fn validate_key(key: &str, allowed_prefixes: &[String]) -> Result<()> {
11    if key.is_empty() {
12        bail!("empty key");
13    }
14    if key.starts_with('/') {
15        bail!("key must not start with /");
16    }
17    if key.split('/').any(|seg| seg == "..") {
18        bail!("key must not contain ..");
19    }
20    if allowed_prefixes.is_empty() {
21        return Ok(());
22    }
23    // Admit the key if it matches any prefix on the equal-or-`{p}/`-boundary
24    // rule, so `blobs` admits `blobs` and `blobs/x` but not `blobs-secret/y`.
25    let allowed = allowed_prefixes
26        .iter()
27        .any(|p| key == p || key.starts_with(&format!("{p}/")));
28    if !allowed {
29        bail!("key {key} is outside allowed prefixes {allowed_prefixes:?}");
30    }
31    Ok(())
32}