Skip to content

Commit ec4cd0b

Browse files
authored
fix(dell): escape XML PCDATA in UEFI password import buffer (#60)
When building the Dell SystemConfiguration import buffer, the current UEFI password was inserted directly into XML element text. Passwords containing XML markup characters like `&`, `<`, or `>` could produce invalid XML or incorrect payloads. Add a small `XmlPcdata` formatter to escape element text content before inserting the password into `<Attribute Name="OldSetupPassword">...</Attribute>`. Also add unit tests covering escaping behavior and the import-buffer usage. Signed-off-by: Dmitry Porokh <dporokh@nvidia.com>
1 parent 4a5f5fd commit ec4cd0b

1 file changed

Lines changed: 58 additions & 1 deletion

File tree

src/dell.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1939,7 +1939,8 @@ impl Bmc {
19391939
target: "BIOS".to_string(),
19401940
},
19411941
import_buffer: format!(
1942-
r##"<SystemConfiguration><Component FQDD="BIOS.Setup.1-1"><!-- <Attribute Name="OldSysPassword"></Attribute>--><!-- <Attribute Name="NewSysPassword"></Attribute>--><Attribute Name="OldSetupPassword">{current_uefi_password}</Attribute><Attribute Name="NewSetupPassword"></Attribute></Component></SystemConfiguration>"##
1942+
r##"<SystemConfiguration><Component FQDD="BIOS.Setup.1-1"><!-- <Attribute Name="OldSysPassword"></Attribute>--><!-- <Attribute Name="NewSysPassword"></Attribute>--><Attribute Name="OldSetupPassword">{}</Attribute><Attribute Name="NewSetupPassword"></Attribute></Component></SystemConfiguration>"##,
1943+
XmlPcdata(current_uefi_password)
19431944
),
19441945
};
19451946

@@ -2298,8 +2299,37 @@ impl UpdateParameters {
22982299
}
22992300
}
23002301

2302+
// Escapes XML character data for element text content (PCDATA).
2303+
// Use this for values inserted between tags, e.g. <x>value</x>.
2304+
//
2305+
// Do not use for XML attributes; attribute values require different escaping.
2306+
struct XmlPcdata<'a>(&'a str);
2307+
2308+
impl std::fmt::Display for XmlPcdata<'_> {
2309+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2310+
// XML: 2.4 Character Data and Markup
2311+
for c in self.0.chars() {
2312+
match c {
2313+
// The ampersand character (&) and the left angle
2314+
// bracket (<) MUST NOT appear in their literal form,
2315+
// except ... . If they are needed elsewhere, they
2316+
// MUST be escaped.
2317+
'&' => "&amp;".fmt(f)?,
2318+
'<' => "&lt;".fmt(f)?,
2319+
// The right angle bracket (>) may be represented
2320+
// using the string " &gt; ", and MUST, for
2321+
// compatibility, be escaped using ... "&gt;"
2322+
'>' => "&gt;".fmt(f)?,
2323+
_ => c.fmt(f)?,
2324+
}
2325+
}
2326+
Ok(())
2327+
}
2328+
}
2329+
23012330
#[cfg(test)]
23022331
mod tests {
2332+
use super::XmlPcdata;
23032333
use std::collections::HashMap;
23042334

23052335
// Mirrors the attribute-merge logic in machine_setup_oem so we can test it
@@ -2367,4 +2397,31 @@ mod tests {
23672397
let attrs = build_oem_attributes(&extra, false);
23682398
assert_eq!(attrs["IPMILan.1.Enable"], serde_json::json!("Disabled"));
23692399
}
2400+
2401+
#[test]
2402+
fn test_xml_pcdata_escapes_markup_chars() {
2403+
let input = r#"before & <tag> > after"#;
2404+
let escaped = XmlPcdata(input).to_string();
2405+
assert_eq!(escaped, "before &amp; &lt;tag&gt; &gt; after");
2406+
}
2407+
2408+
#[test]
2409+
fn test_xml_pcdata_leaves_plain_text_unchanged() {
2410+
let input = "abcXYZ123_- ";
2411+
let escaped = XmlPcdata(input).to_string();
2412+
assert_eq!(escaped, input);
2413+
}
2414+
2415+
#[test]
2416+
fn test_xml_pcdata_in_import_buffer_context() {
2417+
let password = r#"a&<b>c"#;
2418+
let xml = format!(
2419+
r##"<Attribute Name="OldSetupPassword">{}</Attribute>"##,
2420+
XmlPcdata(password)
2421+
);
2422+
assert_eq!(
2423+
xml,
2424+
r#"<Attribute Name="OldSetupPassword">a&amp;&lt;b&gt;c</Attribute>"#
2425+
);
2426+
}
23702427
}

0 commit comments

Comments
 (0)