Strip CR/LF/NUL from every outbound s2s line
Free-form trailing text (message bodies, kick reasons, topics, metadata) now passes through a single sanitizing choke-point in the serializer, so no such text can carry an embedded line break that would smuggle a second command onto the link. Line-based input closes the obvious path today, but sanitizing at the point of serialization removes the whole class regardless of where the text originated (federation, config, future non-line sources).
This commit is contained in:
parent
2fd5676ff2
commit
5d67133295
1 changed files with 20 additions and 2 deletions
|
|
@ -227,7 +227,7 @@ impl Protocol for InspIrcd {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize(&mut self, action: &NetAction) -> Vec<String> {
|
fn serialize(&mut self, action: &NetAction) -> Vec<String> {
|
||||||
match action {
|
let lines = match action {
|
||||||
NetAction::Burst => vec![self.sourced(format!("BURST {}", self.ts))],
|
NetAction::Burst => vec![self.sourced(format!("BURST {}", self.ts))],
|
||||||
NetAction::EndBurst => vec![self.sourced("ENDBURST".to_string())],
|
NetAction::EndBurst => vec![self.sourced("ENDBURST".to_string())],
|
||||||
NetAction::Pong { token, from } => {
|
NetAction::Pong { token, from } => {
|
||||||
|
|
@ -321,7 +321,12 @@ impl Protocol for InspIrcd {
|
||||||
NetAction::Raw(s) => vec![s.clone()],
|
NetAction::Raw(s) => vec![s.clone()],
|
||||||
// Internal: the link layer handles these before serialization.
|
// Internal: the link layer handles these before serialization.
|
||||||
NetAction::DeferRegister { .. } | NetAction::DeferPassword { .. } | NetAction::SendEmail { .. } => vec![],
|
NetAction::DeferRegister { .. } | NetAction::DeferPassword { .. } | NetAction::SendEmail { .. } => vec![],
|
||||||
}
|
};
|
||||||
|
// A trailing parameter can carry free-form text (message bodies, kick
|
||||||
|
// reasons, topics, metadata). Strip CR/LF/NUL at this single choke-point
|
||||||
|
// so no such text — whatever its origin — can smuggle a second command
|
||||||
|
// onto the s2s link.
|
||||||
|
lines.into_iter().map(|l| l.replace(['\r', '\n', '\0'], "")).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sid(&self) -> &str {
|
fn sid(&self) -> &str {
|
||||||
|
|
@ -467,6 +472,19 @@ mod tests {
|
||||||
assert_eq!(lines, vec![":42S ENCAP 0IR CHGIDENT 0IRAAAAAB cool".to_string()]);
|
assert_eq!(lines, vec![":42S ENCAP 0IR CHGIDENT 0IRAAAAAB cool".to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Free-form text with embedded line breaks must not smuggle a second line
|
||||||
|
// onto the link: the serializer strips CR/LF/NUL from every outbound line.
|
||||||
|
#[test]
|
||||||
|
fn strips_crlf_from_trailing_text() {
|
||||||
|
let lines = proto().serialize(&NetAction::Notice {
|
||||||
|
from: "42SAAAAAA".into(),
|
||||||
|
to: "0IRAAAAAB".into(),
|
||||||
|
text: "hi\r\nKILL 0IRAAAAAB :pwned".into(),
|
||||||
|
});
|
||||||
|
assert_eq!(lines, vec![":42SAAAAAA NOTICE 0IRAAAAAB :hiKILL 0IRAAAAAB :pwned".to_string()]);
|
||||||
|
assert!(!lines[0].contains('\n') && !lines[0].contains('\r'));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_ftopic_and_filters_own() {
|
fn parses_ftopic_and_filters_own() {
|
||||||
let ev = proto().parse(":0IRAAAAAB FTOPIC #chan 1783845132 1783845140 :Welcome all");
|
let ev = proto().parse(":0IRAAAAAB FTOPIC #chan 1783845132 1783845140 :Welcome all");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue