use serde_json::Value;
use super::super::super::FindingSink;
use crate::context::TraceContext;
#[cfg(test)]
mod tests;
pub(in crate::checks) fn trace_context_format(context: &TraceContext<'_>, sink: &mut FindingSink) {
for (event, _, _) in context.messages() {
let Some(payload) = event.message_payload() else {
continue;
};
for envelope in ["params", "result"] {
let meta = payload
.get(envelope)
.and_then(|member| member.get("_meta"))
.and_then(Value::as_object);
let Some(meta) = meta else { continue };
if let Some(value) = meta.get("traceparent") {
sink.examined();
if let Err(reason) = validate_traceparent(value) {
sink.push(
Some(event.seq),
format!("{envelope}._meta.traceparent {reason}"),
);
}
}
for key in ["tracestate", "baggage"] {
if let Some(value) = meta.get(key) {
sink.examined();
if !value.is_string() {
sink.push(
Some(event.seq),
format!("{envelope}._meta.{key} is not a string"),
);
}
}
}
}
}
}
fn validate_traceparent(value: &Value) -> Result<(), String> {
let Some(text) = value.as_str() else {
return Err("is not a string".to_owned());
};
let parts: Vec<&str> = text.split('-').collect();
let [version, trace_id, parent_id, flags] = parts.as_slice() else {
return Err(format!(
"is {text:?}; W3C Trace Context requires four `-`-separated fields"
));
};
let hex = |s: &str| {
s.chars()
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
};
if version.len() != 2 || !hex(version) {
return Err(format!(
"has version {version:?}; expected two lowercase hex digits"
));
}
if trace_id.len() != 32 || !hex(trace_id) {
return Err(format!(
"has trace-id {trace_id:?}; expected 32 lowercase hex digits"
));
}
if trace_id.bytes().all(|b| b == b'0') {
return Err("has an all-zero trace-id, which W3C Trace Context forbids".to_owned());
}
if parent_id.len() != 16 || !hex(parent_id) {
return Err(format!(
"has parent-id {parent_id:?}; expected 16 lowercase hex digits"
));
}
if parent_id.bytes().all(|b| b == b'0') {
return Err("has an all-zero parent-id, which W3C Trace Context forbids".to_owned());
}
if flags.len() != 2 || !hex(flags) {
return Err(format!(
"has flags {flags:?}; expected two lowercase hex digits"
));
}
Ok(())
}