flawopen.com/Cross-Site Scripting/Rust
Imagine a guestbook where visitors write a public note. Stored XSS is like someone writing a note that isn't just text — it's a hidden trick that makes the guestbook page itself start doing things, like stealing the next visitor's login session, the moment anyone opens the page to read it.
Cross-site scripting happens when untrusted input reaches the rendered page without proper output encoding. Rust's standard library has no HTML awareness at all, so this almost always means format! was used to build an HTML response directly instead of going through an auto-escaping template crate.
In 2005, an 19-year-old user exploited a stored XSS flaw on MySpace to create the "Samy" worm — a script that added itself to every profile that viewed it, infecting over a million profiles within about 20 hours and forcing MySpace offline to contain it. It remains one of the most-cited demonstrations of how quickly stored XSS can self-propagate.
Source: widely documented in security industry retrospectives — see References below.// format! has no HTML awareness
let body = format!(
"<div>Welcome, {}</div>", name
);
// Askama auto-escapes .html templates #[derive(Template)] #[template(path = "welcome.html")] struct Welcome<'a> { name: &'a str } // welcome.html: <div>Welcome, {{ name }}</div>
Askama (and similar crates like Tera) auto-escape template expressions based on the template file's extension — a .html template escapes HTML-special characters in {{ name }} automatically. format! has no concept of output context; it substitutes the value as literal text into the format string, whatever that text contains.
Unlike Go's html/template, Rust's std has no HTML-aware output mechanism at all — the safety has to come entirely from an external crate (Askama, Tera, Maud) chosen and used correctly.
A template file saved with a non-.html extension, or explicitly marked with a different escape mode, won't get HTML escaping even inside an Askama project — worth confirming the escape mode explicitly rather than assuming it from context.
serde_json::to_string() produces valid JSON, but valid JSON isn't automatically safe to embed directly inside an HTML <script> block — certain characters still need HTML-context escaping on top of JSON serialization.
Memory safety and output-encoding safety are unrelated properties — a perfectly memory-safe format! call can still produce exploitable HTML output.
True only if the specific template/expression is using the crate's auto-escaping path — most template crates also offer an explicit unescaped-output mode for legitimate raw-HTML cases, which carries the same risk as any other language's raw-output escape hatch.
grep -rn "format!(" --include="*.rs" . | grep -i "<div\|<p\|<span\|<html"
Same underlying shape — untrusted data mixed into a command's structure without encoding — but the target and damage differ: SQL injection targets the database, XSS targets other users' browsers.
Maud (a compile-time HTML macro crate) also escapes by default for text content — the underlying principle is the same across these crates: check what each one's default behavior actually does for the values you interpolate, and where its explicit unescaped-output option is.