huskies: merge 1026
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
//! HTML sanitisation for user-supplied text fields.
|
||||
//!
|
||||
//! Uses ammonia to strip dangerous HTML tags and attributes while preserving
|
||||
//! the visible text content. Sanitisation that actually fires is logged at
|
||||
//! WARN so operators can spot abuse patterns.
|
||||
|
||||
use sha2::Digest;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// Sanitise `value` for the named `field`.
|
||||
///
|
||||
/// Strips all HTML tags (keeping their text content) and removes dangerous
|
||||
/// attributes. Returns `(sanitised_value, was_modified)`. When `was_modified`
|
||||
/// is `true` the caller should log at WARN.
|
||||
pub(super) fn sanitize_html(field: &str, value: &str) -> (String, bool) {
|
||||
// Build an ammonia cleaner that allows NO tags but keeps text content.
|
||||
// clear_content_tags is also set to empty so that <script>...</script>
|
||||
// content is preserved as literal text rather than silently discarded.
|
||||
let clean = ammonia::Builder::new()
|
||||
.tags(HashSet::new())
|
||||
.clean_content_tags(HashSet::new())
|
||||
.clean(value)
|
||||
.to_string();
|
||||
|
||||
let modified = clean != value;
|
||||
if modified {
|
||||
crate::slog_warn!(
|
||||
"[validation] HTML sanitised in field '{}': fingerprint={}",
|
||||
field,
|
||||
fingerprint(value)
|
||||
);
|
||||
}
|
||||
(clean, modified)
|
||||
}
|
||||
|
||||
/// Return an 8-hex-char SHA-256 fingerprint of the input string.
|
||||
fn fingerprint(input: &str) -> String {
|
||||
let hash = sha2::Sha256::digest(input.as_bytes());
|
||||
hash[..4].iter().map(|b| format!("{b:02x}")).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn script_tags_stripped_content_preserved() {
|
||||
let (out, modified) = sanitize_html("name", "<script>alert('xss')</script>");
|
||||
assert!(modified);
|
||||
assert!(!out.contains("<script>"));
|
||||
assert!(out.contains("alert("));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_text_unchanged() {
|
||||
let (out, modified) = sanitize_html("name", "Hello World");
|
||||
assert!(!modified);
|
||||
assert_eq!(out, "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_event_stripped() {
|
||||
let (out, modified) = sanitize_html("name", r#"<img onload="evil()">"#);
|
||||
assert!(modified);
|
||||
assert!(!out.contains("onload"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user