Skip to main content

for_html_unquoted_attribute

Function for_html_unquoted_attribute 

Source
pub fn for_html_unquoted_attribute(input: &str) -> String
Expand description

encodes input for safe embedding in an unquoted HTML attribute value.

this is the most aggressive HTML encoder, encoding whitespace, quote characters, grave accents, and many punctuation characters that could terminate an unquoted attribute value.

prefer quoted attributes whenever possible. unquoted attributes are fragile and this encoder exists only for cases where quoting is not an option.

§caveat: grave accent

the grave accent (`, U+0060) is encoded as ` because unpatched internet explorer treats it as an attribute delimiter. however, numeric character references decode back to the original character, so this encoding cannot fully protect against the IE bug in all injection scenarios. the safest mitigation is to avoid unquoted attributes entirely.

§encoded characters (partial list)

inputoutput
tab	
LF

FF
CR
space 
&&
<&lt;
>&gt;
"&#34;
'&#39;
/&#47;
=&#61;
`&#96;

C0/C1 control characters, DEL, and unicode non-characters are replaced with -. NEL (U+0085) is encoded as &#133;. line separator (U+2028) and paragraph separator (U+2029) are encoded as &#8232; and &#8233;.

§examples

use contextual_encoder::for_html_unquoted_attribute;

assert_eq!(for_html_unquoted_attribute("hello world"), "hello&#32;world");
assert_eq!(for_html_unquoted_attribute("a=b"), "a&#61;b");
Examples found in repository?
examples/contexts.rs (line 47)
18fn main() {
19    let input = r#"<script>alert("xss")</script>"#;
20
21    println!("input: {input}");
22    println!();
23
24    // -----------------------------------------------------------------------
25    // comparison: same input across all encoders
26    // -----------------------------------------------------------------------
27
28    // html text content AND quoted attributes (safe default when unsure)
29    println!("--- html ---");
30    println!("  for_html:                     {}", for_html(input));
31
32    // html text nodes only — does NOT encode quotes, so never use in attributes
33    println!(
34        "  for_html_content:             {}",
35        for_html_content(input)
36    );
37
38    // quoted attribute values only — does NOT encode >, slightly more minimal
39    println!(
40        "  for_html_attribute:           {}",
41        for_html_attribute(input)
42    );
43
44    // unquoted attribute values — most aggressive, encodes whitespace/grave/etc.
45    println!(
46        "  for_html_unquoted_attribute:  {}",
47        for_html_unquoted_attribute(input)
48    );
49    println!();
50
51    // universal js encoder — safe in event attrs, <script> blocks, and .js files
52    println!("--- javascript ---");
53    println!("  for_javascript:               {}", for_javascript(input));
54
55    // html event attributes (onclick="...") — does not escape /
56    println!(
57        "  for_javascript_attribute:     {}",
58        for_javascript_attribute(input)
59    );
60
61    // <script> blocks — uses \" and \' (not safe in html attributes)
62    println!(
63        "  for_javascript_block:         {}",
64        for_javascript_block(input)
65    );
66
67    // standalone .js / json files — minimal, NOT safe in any html context
68    println!(
69        "  for_javascript_source:        {}",
70        for_javascript_source(input)
71    );
72
73    // ES6 template literals (`...`) — escapes backtick and ${} interpolation
74    let template_input = r#"`Hello ${name}`, welcome</script>"#;
75    println!(
76        "  for_js_template:              {}",
77        for_js_template(template_input)
78    );
79    println!();
80
81    // quoted css string values, e.g., content: "..." or font-family: "..."
82    println!("--- css ---");
83    println!("  for_css_string:               {}", for_css_string(input));
84
85    // css url() values — like for_css_string but parens pass through
86    println!("  for_css_url:                  {}", for_css_url(input));
87    println!();
88
89    // uri component (query params, path segments) — NOT for full urls
90    println!("--- uri ---");
91    println!(
92        "  for_uri_component:            {}",
93        for_uri_component(input)
94    );
95
96    // form-urlencoded value (WHATWG) — space→+, different safe set from RFC 3986
97    println!(
98        "  for_form_urlencoded:          {}",
99        for_form_urlencoded(input)
100    );
101    println!();
102
103    // xml 1.0 aliases — identical to the html encoders
104    println!("--- xml 1.0 ---");
105    println!("  for_xml:                      {}", for_xml(input));
106    println!("  for_xml_content:              {}", for_xml_content(input));
107    println!(
108        "  for_xml_attribute:            {}",
109        for_xml_attribute(input)
110    );
111
112    // xml-only contexts
113    println!("  for_xml_comment:              {}", for_xml_comment(input));
114    println!("  for_cdata:                    {}", for_cdata(input));
115    println!();
116
117    // xml 1.1 — restricted chars get &#xHH; instead of space
118    println!("--- xml 1.1 ---");
119    let xml11_input = "a\x01b<c>";
120    println!("  for_xml11:                    {}", for_xml11(xml11_input));
121    println!(
122        "  for_xml11_content:            {}",
123        for_xml11_content(xml11_input)
124    );
125    println!(
126        "  for_xml11_attribute:          {}",
127        for_xml11_attribute(xml11_input)
128    );
129    println!();
130
131    // rust literals — \xHH escapes, UTF-8 byte encoding for byte strings
132    println!("--- rust ---");
133    println!("  for_rust_string:              {}", for_rust_string(input));
134    println!("  for_rust_char:                {}", for_rust_char(input));
135    println!(
136        "  for_rust_byte_string:         {}",
137        for_rust_byte_string(input)
138    );
139    println!();
140
141    // json string value — \\uXXXX escapes for control chars and specials
142    println!("--- json ---");
143    println!("  for_json:                     {}", for_json(input));
144    println!();
145
146    // sql string literals — single-quote doubling (standard) or backslash escaping (mysql)
147    println!("--- sql ---");
148    println!("  for_sql:                      {}", for_sql(input));
149    println!(
150        "  for_sql_backslash:            {}",
151        for_sql_backslash(input)
152    );
153
154    // -----------------------------------------------------------------------
155    // practical: one realistic input per sink, correct encoder for each
156    // -----------------------------------------------------------------------
157
158    let user_name = r#"Bob <img src=x onerror="alert(1)">"#;
159    let user_query = "hello world & goodbye";
160    let user_text = r#"hi from </script><script>alert(1)</script>"#;
161    let user_css_text = r#"hello "css" \ test"#;
162
163    println!("--- practical usage ---");
164
165    // html text node — for_html_content is the right encoder
166    println!(r#"  <p>{}</p>"#, for_html_content(user_name));
167
168    // nested context: uri component inside an html attribute.
169    // encode from inside out: first percent-encode the query value,
170    // then html-attribute-encode the entire href.
171    let href = format!("/search?q={}", for_uri_component(user_query));
172    println!(r#"  <a href="{}">search</a>"#, for_html_attribute(&href),);
173
174    // actual css string context: a quoted content value in a stylesheet
175    println!(
176        r#"  <style>.msg::after {{ content: "{}"; }}</style>"#,
177        for_css_string(user_css_text),
178    );
179
180    // javascript string inside an event-handler attribute
181    println!(
182        r#"  <button onclick="greet('{}');">hi</button>"#,
183        for_javascript_attribute(user_text),
184    );
185}