pub const UNTRUSTED_SENTINEL_PREFIX: &str = "vigil-untrusted-";
pub fn make_untrusted_marker() -> (String, String) {
let mut nonce = [0u8; 16]; if getrandom::getrandom(&mut nonce).is_err() {
eprintln!(
"vigil: getrandom failed for untrusted-data marker nonce; \
falling back to a zero nonce (entropy source may be unavailable)"
);
}
let hex = to_hex(&nonce);
let open = format!("<{UNTRUSTED_SENTINEL_PREFIX}{hex}>");
let close = format!("</{UNTRUSTED_SENTINEL_PREFIX}{hex}>");
(open, close)
}
pub fn detect_sentinel_forgery(text: &str) -> bool {
text.contains(UNTRUSTED_SENTINEL_PREFIX)
}
static SENTINEL_MARKER_RE: once_cell::sync::Lazy<regex::Regex> = once_cell::sync::Lazy::new(|| {
regex::Regex::new(r"</?vigil-untrusted-[^>\s]*>").expect("sentinel marker regex")
});
pub fn strip_sentinel_markers(text: &str) -> (String, bool) {
if !text.contains(UNTRUSTED_SENTINEL_PREFIX) {
return (text.to_string(), false);
}
let stripped = SENTINEL_MARKER_RE.replace_all(text, "");
let changed = stripped != text;
(stripped.into_owned(), changed)
}
fn to_hex(bytes: &[u8]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
s.push(HEX[(b >> 4) as usize] as char);
s.push(HEX[(b & 0x0f) as usize] as char);
}
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn marker_is_well_formed() {
let (open, close) = make_untrusted_marker();
assert!(open.starts_with("<vigil-untrusted-"));
assert!(open.ends_with('>'));
assert!(close.starts_with("</vigil-untrusted-"));
assert!(close.ends_with('>'));
let open_body = open.trim_start_matches('<').trim_end_matches('>');
let close_body = close.trim_start_matches("</").trim_end_matches('>');
assert_eq!(open_body, close_body, "open/close 应同 nonce");
}
#[test]
fn marker_is_unique_per_call() {
let (o1, _) = make_untrusted_marker();
let (o2, _) = make_untrusted_marker();
assert_ne!(o1, o2, "每次调用应产新 nonce");
}
#[test]
fn detect_catches_own_marker() {
let (open, close) = make_untrusted_marker();
assert!(detect_sentinel_forgery(&open));
assert!(detect_sentinel_forgery(&close));
let wrapped = format!("{open}some untrusted data{close}");
assert!(detect_sentinel_forgery(&wrapped));
}
#[test]
fn detect_catches_forged_prefix() {
assert!(detect_sentinel_forgery(
"<vigil-untrusted-deadbeefdeadbeefdeadbeefdeadbeef>fake"
));
assert!(detect_sentinel_forgery("prefix vigil-untrusted-anything"));
}
#[test]
fn detect_no_false_positive_on_clean_text() {
assert!(!detect_sentinel_forgery("hello world"));
assert!(!detect_sentinel_forgery(
"discuss <untrusted> data and trusted boundaries"
));
assert!(!detect_sentinel_forgery(
"vigil untrusted (无连字符前缀不命中)"
));
assert!(!detect_sentinel_forgery(""));
}
#[test]
fn strip_removes_own_marker_pair_keeps_content() {
let (open, close) = make_untrusted_marker();
let wrapped = format!("{open}payload data{close}");
let (stripped, changed) = strip_sentinel_markers(&wrapped);
assert!(changed, "含标签应报告剥离过");
assert_eq!(stripped, "payload data", "标签间内容须原样保留");
assert!(!detect_sentinel_forgery(&stripped));
}
#[test]
fn strip_noop_on_clean_text() {
let (s1, c1) = strip_sentinel_markers("build succeeded\n");
assert_eq!(s1, "build succeeded\n");
assert!(!c1);
let (s2, c2) = strip_sentinel_markers("");
assert_eq!(s2, "");
assert!(!c2);
let (s3, c3) = strip_sentinel_markers("discuss <untrusted> data boundaries");
assert_eq!(s3, "discuss <untrusted> data boundaries");
assert!(!c3);
}
#[test]
fn strip_removes_multiple_and_nested_markers() {
let text = "<vigil-untrusted-aaaa>outer \
<vigil-untrusted-deadbeef>inner</vigil-untrusted-deadbeef> \
tail</vigil-untrusted-aaaa>";
let (stripped, changed) = strip_sentinel_markers(text);
assert!(changed);
assert_eq!(stripped, "outer inner tail");
assert!(!detect_sentinel_forgery(&stripped));
let (s2, c2) = strip_sentinel_markers("</vigil-untrusted->evil<vigil-untrusted->");
assert!(c2);
assert_eq!(s2, "evil");
assert!(!detect_sentinel_forgery(&s2));
}
#[test]
fn strip_removes_uppercase_and_non_hex_body_markers() {
for (input, want) in [
(
"<vigil-untrusted-DEADBEEF>evil</vigil-untrusted-DEADBEEF>",
"evil",
), ("<vigil-untrusted-AbCdEf>x</vigil-untrusted-AbCdEf>", "x"), ("<vigil-untrusted-XYZ123>y</vigil-untrusted-XYZ123>", "y"), ] {
let (stripped, changed) = strip_sentinel_markers(input);
assert!(changed, "良构伪标签(任意 body)必须被剥: {input:?}");
assert_eq!(stripped, want, "标签间内容须保留: {input:?}");
assert!(
!detect_sentinel_forgery(&stripped),
"剥后不应残留可被 detect 命中的标签: {input:?}"
);
}
}
}