pub mod audit;
pub mod catalog;
pub mod config;
pub mod env;
pub mod keys;
pub mod logs;
pub mod managed_backends;
pub mod mcp;
pub mod models;
pub mod routes_api;
pub mod status;
pub mod traffic;
pub mod uptime;
use crate::admin::auth::{
extract_csrf_cookie, generate_csrf_token, validate_admin_token, validate_csrf_tokens,
};
use crate::admin::state::SharedState;
use crate::admin::ws::ws_handler;
use axum::{
extract::{ConnectInfo, DefaultBodyLimit, State},
http::StatusCode,
middleware,
response::IntoResponse,
routing::{delete, get, post, put},
Json, Router,
};
use dashmap::DashMap;
use std::net::{IpAddr, SocketAddr};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, LazyLock};
pub(super) fn is_valid_timestamp(s: &str) -> bool {
let b = s.as_bytes();
if b.len() < 10 {
return false;
}
b[0..4].iter().all(|c| c.is_ascii_digit())
&& b[4] == b'-'
&& b[5..7].iter().all(|c| c.is_ascii_digit())
&& b[7] == b'-'
&& b[8..10].iter().all(|c| c.is_ascii_digit())
&& (b.len() == 10
|| (b.len() >= 19
&& (b[10] == b'T' || b[10] == b' ')
&& b[11..13].iter().all(|c| c.is_ascii_digit())
&& b[13] == b':'
&& b[14..16].iter().all(|c| c.is_ascii_digit())
&& b[16] == b':'
&& b[17..19].iter().all(|c| c.is_ascii_digit())))
}
pub(super) fn check_time_range(since: Option<&str>, until: Option<&str>) -> Option<&'static str> {
if since.is_some_and(|s| !is_valid_timestamp(s)) {
return Some("since");
}
if until.is_some_and(|u| !is_valid_timestamp(u)) {
return Some("until");
}
None
}
static READ_RATE_BUCKETS: LazyLock<DashMap<IpAddr, std::collections::VecDeque<u64>>> =
LazyLock::new(DashMap::new);
static WRITE_RATE_BUCKETS: LazyLock<DashMap<IpAddr, std::collections::VecDeque<u64>>> =
LazyLock::new(DashMap::new);
static ADMIN_READ_RPM: AtomicU32 = AtomicU32::new(240);
static ADMIN_WRITE_RPM: AtomicU32 = AtomicU32::new(60);
pub fn set_admin_rpm(rpm: u32) {
ADMIN_READ_RPM.store(rpm, Ordering::Relaxed);
ADMIN_WRITE_RPM.store(rpm, Ordering::Relaxed);
}
pub fn reset_admin_rate_limit() {
READ_RATE_BUCKETS.clear();
WRITE_RATE_BUCKETS.clear();
}
fn prune_stale_rate_limit_entries(
now_ms: u64,
bucket: &DashMap<IpAddr, std::collections::VecDeque<u64>>,
last_prune: &std::sync::atomic::AtomicU64,
) {
let last = last_prune.load(Ordering::Relaxed);
if now_ms.saturating_sub(last) < 60_000 {
return;
}
if last_prune
.compare_exchange(last, now_ms, Ordering::Relaxed, Ordering::Relaxed)
.is_err()
{
return; }
let cutoff = now_ms.saturating_sub(60_000);
bucket.retain(|_, window| window.back().is_some_and(|&ts| ts >= cutoff));
}
fn check_admin_rate_limit_with_rpm(
ip: IpAddr,
rpm: u32,
bucket: &DashMap<IpAddr, std::collections::VecDeque<u64>>,
last_prune: &std::sync::atomic::AtomicU64,
) -> bool {
let now_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
let cutoff = now_ms.saturating_sub(60_000);
prune_stale_rate_limit_entries(now_ms, bucket, last_prune);
let mut window = bucket.entry(ip).or_default();
while window.front().is_some_and(|&ts| ts < cutoff) {
window.pop_front();
}
if window.len() >= rpm as usize {
return false;
}
window.push_back(now_ms);
true
}
fn check_admin_rate_limit(ip: IpAddr, is_read: bool) -> bool {
static LAST_READ_PRUNE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
static LAST_WRITE_PRUNE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
if is_read {
check_admin_rate_limit_with_rpm(
ip,
ADMIN_READ_RPM.load(Ordering::Relaxed),
&READ_RATE_BUCKETS,
&LAST_READ_PRUNE,
)
} else {
check_admin_rate_limit_with_rpm(
ip,
ADMIN_WRITE_RPM.load(Ordering::Relaxed),
&WRITE_RATE_BUCKETS,
&LAST_WRITE_PRUNE,
)
}
}
async fn admin_rate_limit_middleware(
req: axum::extract::Request,
next: middleware::Next,
) -> Result<axum::response::Response, StatusCode> {
let path = req.uri().path();
if matches!(path, "/admin/health" | "/admin/csrf-token") {
return Ok(next.run(req).await);
}
let ip = req
.extensions()
.get::<ConnectInfo<SocketAddr>>()
.map(|ci| ci.0.ip())
.unwrap_or(IpAddr::V4(std::net::Ipv4Addr::LOCALHOST));
let is_read = matches!(
*req.method(),
axum::http::Method::GET | axum::http::Method::HEAD | axum::http::Method::OPTIONS
);
if !check_admin_rate_limit(ip, is_read) {
tracing::warn!(%ip, "admin API rate limit exceeded");
return Err(StatusCode::TOO_MANY_REQUESTS);
}
Ok(next.run(req).await)
}
pub(super) fn is_safe_model_name(name: &str) -> bool {
!name.is_empty()
&& !name.contains("..")
&& !name.contains('?')
&& !name.contains('#')
&& name
.chars()
.all(|c| c.is_alphanumeric() || "-_./:@".contains(c))
}
fn is_localhost_host(host: &str) -> bool {
matches!(host, "127.0.0.1" | "localhost" | "[::1]" | "::1")
}
async fn reject_cross_origin(
req: axum::extract::Request,
next: middleware::Next,
) -> Result<axum::response::Response, StatusCode> {
if let Some(origin) = req.headers().get("origin") {
let origin_str = origin.to_str().map_err(|_| StatusCode::BAD_REQUEST)?;
let is_local = match url::Url::parse(origin_str) {
Ok(url) => url.host_str().is_some_and(is_localhost_host),
Err(_) => false,
};
if !is_local {
return Err(StatusCode::FORBIDDEN);
}
} else {
let host_valid = req
.headers()
.get("host")
.and_then(|h| h.to_str().ok())
.map(|h| {
let host_part = if h.starts_with('[') {
h.split_once(']').map_or(h, |(bracket, _)| {
&h[..bracket.len() + 1]
})
} else {
match h.rsplit_once(':') {
Some((host, port)) if port.bytes().all(|b| b.is_ascii_digit()) => host,
_ => h,
}
};
is_localhost_host(host_part)
})
.unwrap_or(false);
if !host_valid {
return Err(StatusCode::FORBIDDEN);
}
}
Ok(next.run(req).await)
}
pub async fn validate_csrf(
axum::extract::State(shared): axum::extract::State<SharedState>,
req: axum::extract::Request,
next: middleware::Next,
) -> axum::response::Response {
let method = req.method().clone();
if matches!(
method,
axum::http::Method::POST
| axum::http::Method::PUT
| axum::http::Method::DELETE
| axum::http::Method::PATCH
) {
let headers = req.headers();
let header_token = headers
.get("x-csrf-token")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
let cookie_token = headers
.get("cookie")
.and_then(|v| v.to_str().ok())
.and_then(extract_csrf_cookie)
.unwrap_or_default();
if !validate_csrf_tokens(header_token, &cookie_token) {
let body = serde_json::json!({
"type": "error",
"error": {
"type": "permission_error",
"message": "CSRF token missing or invalid. Fetch a token from GET /admin/csrf-token."
}
});
return (StatusCode::FORBIDDEN, axum::Json(body)).into_response();
}
if shared.issued_csrf_tokens.get(header_token).is_none() {
let body = serde_json::json!({
"type": "error",
"error": {
"type": "permission_error",
"message": "CSRF token not recognized or already used. Fetch a new token from GET /admin/csrf-token."
}
});
return (StatusCode::FORBIDDEN, axum::Json(body)).into_response();
}
shared.issued_csrf_tokens.invalidate(header_token);
}
next.run(req).await
}
async fn get_csrf_token(State(shared): State<SharedState>) -> axum::response::Response {
let token = generate_csrf_token();
shared.issued_csrf_tokens.insert(token.clone(), ());
let body = serde_json::json!({"csrf_token": token});
axum::http::Response::builder()
.status(StatusCode::OK)
.header("content-type", "application/json")
.header(
"set-cookie",
format!("csrf_token={token}; Path=/admin; SameSite=Strict; Max-Age=86400"),
)
.body(axum::body::Body::from(
serde_json::to_string(&body).unwrap(),
))
.unwrap()
.into_response()
}
pub fn admin_router(shared: SharedState, token: Arc<zeroize::Zeroizing<String>>) -> Router {
let public = Router::new()
.route("/admin/health", get(health))
.route("/admin/csrf-token", get(get_csrf_token))
.with_state(shared.clone())
.layer(middleware::from_fn(admin_rate_limit_middleware));
let protected = Router::new()
.route(
"/admin/api/config",
get(config::get_config).put(config::put_config),
)
.route(
"/admin/api/config/overrides",
get(config::get_config_overrides),
)
.route(
"/admin/api/config/overrides/{key}",
delete(config::delete_config_override),
)
.route("/admin/api/env", get(config::get_env))
.route("/admin/api/env/import", post(env::import_env))
.route("/admin/api/env/export", get(env::export_env))
.route("/admin/api/metrics", get(logs::get_metrics))
.route(
"/admin/api/observability/overview",
get(logs::get_observability_overview),
)
.route("/admin/api/requests", get(logs::get_requests))
.route("/admin/api/requests/{id}", get(logs::get_request_by_id))
.route("/admin/api/backends", get(get_backends))
.route(
"/admin/api/backends/managed",
get(managed_backends::list).post(managed_backends::create),
)
.route(
"/admin/api/backends/managed/{name}",
put(managed_backends::update).delete(managed_backends::delete),
)
.route(
"/admin/api/keys",
post(keys::create_key).get(keys::list_keys),
)
.route(
"/admin/api/keys/{id}",
put(keys::update_key).delete(keys::revoke_key),
)
.route(
"/admin/api/keys/{id}/spend",
get(super::spend::get_key_spend),
)
.route(
"/admin/api/models",
get(models::list_models).post(models::add_model),
)
.route("/admin/api/models/discover", post(models::discover_models))
.route("/admin/api/models/{name}", delete(models::remove_model))
.route("/admin/api/audit", get(audit::get_audit_log))
.route(
"/admin/api/mcp-servers",
get(mcp::list_mcp_servers).post(mcp::add_mcp_server),
)
.route(
"/admin/api/mcp-servers/{name}",
delete(mcp::remove_mcp_server),
)
.route("/admin/api/catalog/providers", get(catalog::list_providers))
.route(
"/admin/api/catalog/providers/{id}/models",
get(catalog::list_provider_models),
)
.route(
"/admin/api/catalog/providers/{id}/refresh",
post(catalog::refresh_provider_models),
)
.route("/admin/api/status", get(status::get_status))
.route("/admin/api/traffic", get(traffic::get_traffic))
.route("/admin/api/uptime", get(uptime::get_uptime))
.route(
"/admin/api/routes",
get(routes_api::list_routes).post(routes_api::create_route),
)
.route(
"/admin/api/routes/{id}",
put(routes_api::update_route).delete(routes_api::delete_route),
)
.route(
"/admin/api/routes/{id}/providers",
get(routes_api::list_route_providers_handler)
.post(routes_api::add_route_provider_handler),
)
.route(
"/admin/api/routes/{id}/providers/reorder",
put(routes_api::reorder_route_providers_handler),
)
.route(
"/admin/api/routes/{id}/providers/{provider_id}",
put(routes_api::update_route_provider_handler)
.delete(routes_api::remove_route_provider_handler),
)
.with_state(shared.clone())
.layer(middleware::from_fn_with_state(
shared.clone(),
validate_csrf,
))
.layer(middleware::from_fn_with_state(
token.clone(),
validate_admin_token,
))
.layer(middleware::from_fn(reject_cross_origin))
.layer(middleware::from_fn(admin_rate_limit_middleware));
let ws_state = (shared.clone(), token.clone());
let ws_route = Router::new()
.route("/admin/ws", get(ws_handler))
.with_state(ws_state)
.layer(middleware::from_fn(reject_cross_origin));
let spa_route = Router::new()
.route("/admin/", get(serve_spa))
.route("/admin", get(serve_spa))
.route(
"/",
get(|| async { axum::response::Redirect::permanent("/admin/") }),
);
public
.merge(protected)
.merge(ws_route)
.merge(spa_route)
.layer(DefaultBodyLimit::max(1_048_576))
}
async fn health() -> Json<serde_json::Value> {
Json(serde_json::json!({"status": "ok"}))
}
static SPA_HTML: &str = include_str!("../../../admin-ui/dist/index.html");
async fn serve_spa() -> axum::response::Response {
let mut nonce_bytes = [0u8; 16];
getrandom::fill(&mut nonce_bytes).expect("getrandom");
let nonce = base64_url_encode(&nonce_bytes);
let html = SPA_HTML.replace("__CSP_NONCE__", &nonce);
let csp = format!(
"default-src 'self'; script-src 'self' 'nonce-{nonce}'; \
style-src 'self' 'nonce-{nonce}' https://fonts.bunny.net; \
font-src https://fonts.bunny.net; \
connect-src 'self' ws: wss:; img-src 'self' data:; \
frame-ancestors 'none'"
);
axum::http::Response::builder()
.status(StatusCode::OK)
.header("content-type", "text/html; charset=utf-8")
.header("content-security-policy", csp)
.header("x-frame-options", "DENY")
.header("referrer-policy", "no-referrer")
.body(axum::body::Body::from(html))
.unwrap()
.into_response()
}
fn base64_url_encode(input: &[u8]) -> String {
use base64::Engine;
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(input)
}
pub(super) async fn get_backends(State(shared): State<SharedState>) -> Json<serde_json::Value> {
let config = shared
.runtime_config
.read()
.unwrap_or_else(|e| e.into_inner());
let mut backends = Vec::new();
for (name, mapping) in &config.model_mappings {
let metrics = shared
.backend_metrics
.get(name)
.map(|m| m.snapshot())
.unwrap_or_default();
backends.push(serde_json::json!({
"name": name,
"big_model": mapping.big_model,
"small_model": mapping.small_model,
"metrics": {
"requests_total": metrics.requests_total,
"requests_success": metrics.requests_success,
"requests_error": metrics.requests_error,
}
}));
}
Json(serde_json::json!({ "backends": backends }))
}
pub(crate) fn emit_audit(shared: &SharedState, entry: crate::admin::db::AuditEntry) {
let db = shared.db.clone();
tokio::task::spawn_blocking(move || {
let conn = db.lock().unwrap_or_else(|e| e.into_inner());
if let Err(e) = crate::admin::db::insert_audit_entry(&conn, &entry) {
tracing::warn!(error = %e, action = %entry.action, "failed to write audit log");
}
});
}
#[cfg(test)]
mod tests {
use super::*;
use axum::body::Body;
use axum::http::Request;
use tower::ServiceExt;
fn test_router() -> Router {
set_admin_rpm(10_000);
let shared = crate::admin::state::SharedState::new_for_test();
let token = Arc::new(zeroize::Zeroizing::new("test-token".to_string()));
admin_router(shared, token)
}
#[tokio::test]
async fn origin_localhost_allowed() {
let app = test_router();
let req = Request::get("/admin/api/config")
.header("origin", "http://localhost:9090")
.header("authorization", "Bearer test-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_ne!(resp.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn origin_evil_rejected() {
let app = test_router();
let req = Request::get("/admin/api/config")
.header("origin", "http://evil.com")
.header("authorization", "Bearer test-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn no_origin_localhost_host_allowed() {
let app = test_router();
let req = Request::get("/admin/api/config")
.header("host", "localhost:9090")
.header("authorization", "Bearer test-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_ne!(resp.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn no_origin_127_host_allowed() {
let app = test_router();
let req = Request::get("/admin/api/config")
.header("host", "127.0.0.1:9090")
.header("authorization", "Bearer test-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_ne!(resp.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn no_origin_evil_host_rejected() {
let app = test_router();
let req = Request::get("/admin/api/config")
.header("host", "evil.com")
.header("authorization", "Bearer test-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn no_origin_no_host_rejected() {
let app = test_router();
let req = Request::get("/admin/api/config")
.header("authorization", "Bearer test-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[test]
fn admin_rate_limit_enforced() {
let ip: IpAddr = "198.51.100.1".parse().unwrap();
static TEST_PRUNE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let bucket = DashMap::<IpAddr, std::collections::VecDeque<u64>>::new();
bucket.remove(&ip);
assert!(check_admin_rate_limit_with_rpm(ip, 3, &bucket, &TEST_PRUNE));
assert!(check_admin_rate_limit_with_rpm(ip, 3, &bucket, &TEST_PRUNE));
assert!(check_admin_rate_limit_with_rpm(ip, 3, &bucket, &TEST_PRUNE));
assert!(!check_admin_rate_limit_with_rpm(
ip,
3,
&bucket,
&TEST_PRUNE
));
bucket.remove(&ip);
}
#[test]
fn sliding_window_blocks_on_rpm_exceeded() {
let ip: IpAddr = "10.88.77.66".parse().unwrap();
static TEST_PRUNE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let bucket = DashMap::<IpAddr, std::collections::VecDeque<u64>>::new();
assert!(check_admin_rate_limit_with_rpm(ip, 2, &bucket, &TEST_PRUNE));
assert!(check_admin_rate_limit_with_rpm(ip, 2, &bucket, &TEST_PRUNE));
assert!(
!check_admin_rate_limit_with_rpm(ip, 2, &bucket, &TEST_PRUNE),
"3rd request must be blocked when rpm=2"
);
}
#[tokio::test]
async fn post_without_csrf_returns_403() {
let app = test_router();
let req = Request::post("/admin/api/keys")
.header("host", "localhost:9090")
.header("authorization", "Bearer test-token")
.header("content-type", "application/json")
.body(Body::from(r#"{"description":"test"}"#))
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn post_with_valid_csrf_passes_middleware() {
set_admin_rpm(10_000);
let shared = crate::admin::state::SharedState::new_for_test();
let token_str = "a".repeat(64);
shared.issued_csrf_tokens.insert(token_str.clone(), ());
let app = admin_router(
shared,
Arc::new(zeroize::Zeroizing::new("test-token".to_string())),
);
let req = Request::post("/admin/api/keys")
.header("host", "localhost:9090")
.header("authorization", "Bearer test-token")
.header("content-type", "application/json")
.header("x-csrf-token", &token_str)
.header("cookie", format!("csrf_token={token_str}"))
.body(Body::from(r#"{"description":"test"}"#))
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_ne!(resp.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn post_with_unissued_csrf_returns_403() {
let app = test_router();
let token = "b".repeat(64); let req = Request::post("/admin/api/keys")
.header("host", "localhost:9090")
.header("authorization", "Bearer test-token")
.header("content-type", "application/json")
.header("x-csrf-token", &token)
.header("cookie", format!("csrf_token={token}"))
.body(Body::from(r#"{"description":"test"}"#))
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn delete_without_csrf_returns_403() {
let app = test_router();
let req = Request::delete("/admin/api/keys/1")
.header("host", "localhost:9090")
.header("authorization", "Bearer test-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn get_csrf_token_sets_cookie() {
let app = test_router();
let req = Request::get("/admin/csrf-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let set_cookie = resp
.headers()
.get("set-cookie")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
assert!(
set_cookie.contains("csrf_token="),
"Set-Cookie must include csrf_token"
);
assert!(
set_cookie.contains("SameSite=Strict"),
"cookie must be SameSite=Strict"
);
assert!(
!set_cookie.to_lowercase().contains("httponly"),
"csrf_token cookie must not be httpOnly"
);
}
#[tokio::test]
async fn get_csrf_token_returns_json() {
let app = test_router();
let req = Request::get("/admin/csrf-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body_bytes = axum::body::to_bytes(resp.into_body(), 1 << 16)
.await
.unwrap();
let body: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap();
let token = body["csrf_token"].as_str().unwrap();
assert_eq!(token.len(), 64);
}
#[tokio::test]
async fn get_request_does_not_require_csrf() {
let app = test_router();
let req = Request::get("/admin/api/config")
.header("host", "localhost:9090")
.header("authorization", "Bearer test-token")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert_ne!(resp.status(), StatusCode::FORBIDDEN);
}
#[test]
fn aws_access_key_id_uses_secret_pattern() {
let mask = |v: &str| {
if !v.is_empty() {
"***REDACTED***".to_string()
} else {
"<unset>".to_string()
}
};
assert_eq!(mask("AKIAIOSFODNN7EXAMPLE"), "***REDACTED***");
assert_eq!(mask(""), "<unset>");
}
#[test]
fn google_access_token_uses_secret_pattern() {
let mask = |v: &str| {
if !v.is_empty() {
"***REDACTED***".to_string()
} else {
"<unset>".to_string()
}
};
assert_eq!(mask("ya29.someoauthtoken"), "***REDACTED***");
}
}
#[cfg(test)]
mod timestamp_tests {
use super::is_valid_timestamp;
#[test]
fn accepts_date_only() {
assert!(is_valid_timestamp("2026-03-31"));
}
#[test]
fn accepts_datetime_utc() {
assert!(is_valid_timestamp("2026-03-31T12:00:00Z"));
}
#[test]
fn accepts_datetime_no_tz() {
assert!(is_valid_timestamp("2026-03-31T12:00:00"));
}
#[test]
fn rejects_empty() {
assert!(!is_valid_timestamp(""));
}
#[test]
fn rejects_arbitrary_string() {
assert!(!is_valid_timestamp("not-a-date"));
}
#[test]
fn rejects_sql_injection_attempt() {
assert!(!is_valid_timestamp("'; DROP TABLE request_log; --"));
}
#[test]
fn rejects_too_short() {
assert!(!is_valid_timestamp("2026-03"));
}
}