use axum::extract::State;
use axum::http::{HeaderValue, StatusCode, header};
use axum::response::IntoResponse;
use crate::server::AppState;
pub async fn did_log(State(state): State<AppState>) -> impl IntoResponse {
let config = state.config.read().await;
let label = match config.vtc_did.as_deref().and_then(did_log_label) {
Some(label) => label,
None => return (StatusCode::NOT_FOUND, "did log not found").into_response(),
};
let path = config
.store
.data_dir
.join("did")
.join(format!("{label}.jsonl"));
drop(config);
let body = match tokio::fs::read(&path).await {
Ok(bytes) => bytes,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return (StatusCode::NOT_FOUND, "did log not found").into_response();
}
Err(e) => {
tracing::error!(error = %e, path = %path.display(), "failed to read did log");
return (StatusCode::INTERNAL_SERVER_ERROR, "did log read failed").into_response();
}
};
(
StatusCode::OK,
[(
header::CONTENT_TYPE,
HeaderValue::from_static("application/jsonl"),
)],
body,
)
.into_response()
}
fn is_safe_label(s: &str) -> bool {
!s.is_empty()
&& s.len() <= 253
&& s != "."
&& s != ".."
&& !s.contains("..")
&& !s.contains('/')
&& !s.contains('\\')
&& !s.chars().any(char::is_control)
}
fn did_log_label(did: &str) -> Option<String> {
let suffix = did.strip_prefix("did:webvh:")?;
let label = suffix.split(':').next_back()?;
if is_safe_label(label) {
Some(label.to_string())
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn safe_label_accepts_hostnames_and_scids() {
assert!(is_safe_label("vtc.example.com"));
assert!(is_safe_label("abc123"));
assert!(is_safe_label("zQmPLwUBtaqz3a"));
assert!(is_safe_label("foo-bar_baz"));
}
#[test]
fn safe_label_rejects_traversal_and_empty() {
assert!(!is_safe_label(""));
assert!(!is_safe_label("."));
assert!(!is_safe_label(".."));
assert!(!is_safe_label("foo/bar"));
assert!(!is_safe_label("foo\\bar"));
assert!(!is_safe_label("../etc/passwd"));
assert!(!is_safe_label("a..b"));
}
#[test]
fn did_log_label_takes_the_last_component() {
assert_eq!(
did_log_label("did:webvh:abc123:vtc.example.com").as_deref(),
Some("vtc.example.com")
);
assert_eq!(
did_log_label("did:webvh:abc123:vtc.example.com:v1").as_deref(),
Some("v1")
);
}
#[test]
fn did_log_label_returns_none_for_non_webvh() {
assert!(did_log_label("did:key:z6Mk…").is_none());
assert!(did_log_label("not a did").is_none());
}
#[test]
fn did_log_label_returns_none_when_last_component_unsafe() {
assert!(did_log_label("did:webvh:abc123:../../etc/passwd").is_none());
}
}