Stop being pedantic by default

Up to now, the xmpp-rs projects have been very strict about incoming
data. This has served us, as developers of the libraries, well,
uncovering bugs in our and remote implementations which we could then
get fixed.

However, this behaviour is unexpected to users of the library. In the
XMPP world, unexpected child elements and attributes are generally
expected to be ignored. While this could be opted-into previously, the
feature flag for that sounded more dangerous than it was
("disable-validation"). In addition, the tribal knowledge needed to know
about that feature flag may not have reached some people who tried the
library and gave up because of that.

With this change, we make the non-pedantic behaviour the default. For
development and debugging purposes, users can always opt into the
pedantic behaviour as needed, using the newly-introduced `pedantic`
feature flags on all affected crates.
This commit is contained in:
Jonas Schäfer 2026-03-27 12:07:53 +01:00
commit e2dcd8ac66
41 changed files with 99 additions and 107 deletions

View file

@ -32,8 +32,8 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
[features] [features]
# Build xmpp-parsers to make components instead of clients. # Build xmpp-parsers to make components instead of clients.
component = [] component = []
# Disable validation of unknown attributes. # Raise errors on unexpected children and attributes.
disable-validation = [ "xso/non-pedantic" ] pedantic = [ "xso/pedantic" ]
# Enable serde support in jid crate # Enable serde support in jid crate
serde = [ "jid/serde" ] serde = [ "jid/serde" ]
# Enable some additional logging in helpers # Enable some additional logging in helpers

View file

@ -18,6 +18,10 @@ XXXX-YY-ZZ RELEASER <admin@example.com>
- bind::BindFeature::required is now a bool, thanks to xsos flag. - bind::BindFeature::required is now a bool, thanks to xsos flag.
- jingle_rtp::Description::rtcp_mux is now a bool flag too. - jingle_rtp::Description::rtcp_mux is now a bool flag too.
- stream_limits::Limits now extract directly to NonZeroU32. - stream_limits::Limits now extract directly to NonZeroU32.
- Removed the `disable-validation` feature and made the behaviour the
new default. That way, unknown child elements and attributes will
not be rejected by xmpp-parsers by default anymore. The `pedantic`
feature can be used to opt into the previous default behaviour.
* Improvements: * Improvements:
- Make Prioritys inner i8 pub, which had been broken since the - Make Prioritys inner i8 pub, which had been broken since the
conversion to xso. (!632) conversion to xso. (!632)

View file

@ -19,7 +19,7 @@ impl MessagePayload for Attention {}
mod tests { mod tests {
use super::*; use super::*;
use minidom::Element; use minidom::Element;
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
use xso::error::{Error, FromElementError}; use xso::error::{Error, FromElementError};
#[test] #[test]
@ -33,7 +33,7 @@ mod tests {
Attention::try_from(elem).unwrap(); Attention::try_from(elem).unwrap();
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<attention xmlns='urn:xmpp:attention:0'><coucou/></attention>" let elem: Element = "<attention xmlns='urn:xmpp:attention:0'><coucou/></attention>"
@ -47,7 +47,7 @@ mod tests {
assert_eq!(message, "Unknown child in Attention element."); assert_eq!(message, "Unknown child in Attention element.");
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_attribute() { fn test_invalid_attribute() {
let elem: Element = "<attention xmlns='urn:xmpp:attention:0' coucou=''/>" let elem: Element = "<attention xmlns='urn:xmpp:attention:0' coucou=''/>"

View file

@ -69,7 +69,7 @@ mod tests {
use super::*; use super::*;
use crate::hashes::Algo; use crate::hashes::Algo;
use minidom::Element; use minidom::Element;
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
use xso::error::{Error, FromElementError}; use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
@ -121,7 +121,7 @@ mod tests {
assert_eq!(data.data, b"\0\0\0"); assert_eq!(data.data, b"\0\0\0");
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid() { fn test_invalid() {
let elem: Element = "<data xmlns='urn:xmpp:avatar:data' id='coucou'/>" let elem: Element = "<data xmlns='urn:xmpp:avatar:data' id='coucou'/>"

View file

@ -72,7 +72,7 @@ impl From<BindResponse> for Jid {
mod tests { mod tests {
use super::*; use super::*;
use minidom::Element; use minidom::Element;
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
use xso::error::{Error, FromElementError}; use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
@ -118,7 +118,7 @@ mod tests {
); );
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_resource() { fn test_invalid_resource() {
let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource attr='coucou'>resource</resource></bind>" let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource attr='coucou'>resource</resource></bind>"

View file

@ -62,7 +62,7 @@ pub struct Blocked;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
use xso::error::{Error, FromElementError}; use xso::error::{Error, FromElementError};
use super::*; use super::*;
@ -126,7 +126,7 @@ mod tests {
assert_eq!(unblock.items, two_items); assert_eq!(unblock.items, two_items);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid() { fn test_invalid() {
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking' coucou=''/>" let elem: Element = "<blocklist xmlns='urn:xmpp:blocking' coucou=''/>"
@ -169,7 +169,7 @@ mod tests {
assert_eq!(message, "Unknown attribute in Unblock element."); assert_eq!(message, "Unknown attribute in Unblock element.");
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_non_empty_blocklist_request() { fn test_non_empty_blocklist_request() {
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></blocklist>".parse().unwrap(); let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></blocklist>".parse().unwrap();

View file

@ -187,7 +187,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn unknown_child() { fn unknown_child() {
let elem: Element = "<data xmlns='urn:xmpp:bob' cid='sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org'><coucou/></data>" let elem: Element = "<data xmlns='urn:xmpp:bob' cid='sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org'><coucou/></data>"
.parse() .parse()

View file

@ -197,7 +197,7 @@ mod tests {
use super::*; use super::*;
use crate::caps; use crate::caps;
use minidom::Element; use minidom::Element;
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
use xso::error::{Error, FromElementError}; use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
@ -226,7 +226,7 @@ mod tests {
); );
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<c xmlns='http://jabber.org/protocol/caps' node='coucou' hash='sha-256' ver='K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4='><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash></c>".parse().unwrap(); let elem: Element = "<c xmlns='http://jabber.org/protocol/caps' node='coucou' hash='sha-256' ver='K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4='><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash></c>".parse().unwrap();

View file

@ -70,7 +70,7 @@ mod tests {
assert_eq!(message, "This is not a ChatState element."); assert_eq!(message, "This is not a ChatState element.");
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<gone xmlns='http://jabber.org/protocol/chatstates'><coucou/></gone>" let elem: Element = "<gone xmlns='http://jabber.org/protocol/chatstates'><coucou/></gone>"
@ -84,7 +84,7 @@ mod tests {
assert_eq!(message, "Unknown child in ChatState::Gone element."); assert_eq!(message, "Unknown child in ChatState::Gone element.");
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_attribute() { fn test_invalid_attribute() {
let elem: Element = "<inactive xmlns='http://jabber.org/protocol/chatstates' coucou=''/>" let elem: Element = "<inactive xmlns='http://jabber.org/protocol/chatstates' coucou=''/>"

View file

@ -485,7 +485,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
fn test_wrong_child() { fn test_wrong_child() {
let elem: Element = "<x xmlns='jabber:x:data' type='cancel'><coucou/></x>" let elem: Element = "<x xmlns='jabber:x:data' type='cancel'><coucou/></x>"
.parse() .parse()

View file

@ -449,7 +449,7 @@ mod tests {
#[test] #[test]
#[cfg_attr( #[cfg_attr(
feature = "disable-validation", not(feature = "pedantic"),
should_panic = "Validate::try_from(element).is_err()" should_panic = "Validate::try_from(element).is_err()"
)] )]
fn test_fails_with_invalid_children() { fn test_fails_with_invalid_children() {

View file

@ -81,7 +81,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = let elem: Element =
"<delay xmlns='urn:xmpp:delay' stamp='2002-09-10T23:08:25+00:00'><coucou/></delay>" "<delay xmlns='urn:xmpp:delay' stamp='2002-09-10T23:08:25+00:00'><coucou/></delay>"

View file

@ -241,7 +241,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid() { fn test_invalid() {
let elem: Element = let elem: Element =
"<query xmlns='http://jabber.org/protocol/disco#info'><coucou/></query>" "<query xmlns='http://jabber.org/protocol/disco#info'><coucou/></query>"

View file

@ -233,7 +233,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<c xmlns='urn:xmpp:caps'><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash><hash xmlns='urn:xmpp:hashes:1' algo='sha3-256'>+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=</hash></c>".parse().unwrap(); let elem: Element = "<c xmlns='urn:xmpp:caps'><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash><hash xmlns='urn:xmpp:hashes:1' algo='sha3-256'>+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=</hash></c>".parse().unwrap();
let error = ECaps2::try_from(elem).unwrap_err(); let error = ECaps2::try_from(elem).unwrap_err();

View file

@ -72,7 +72,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = let elem: Element =
"<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:otr:0'><coucou/></encryption>" "<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:otr:0'><coucou/></encryption>"

View file

@ -58,7 +58,7 @@ mod tests {
#[test] #[test]
#[cfg(not(feature = "component"))] // feature = "component" changes <message/> namespace #[cfg(not(feature = "component"))] // feature = "component" changes <message/> namespace
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<forwarded xmlns='urn:xmpp:forward:0'><message xmlns='jabber:client'/><coucou/></forwarded>" let elem: Element = "<forwarded xmlns='urn:xmpp:forward:0'><message xmlns='jabber:client'/><coucou/></forwarded>"
.parse() .parse()

View file

@ -305,7 +305,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<hash xmlns='urn:xmpp:hashes:2' algo='sha-1'><coucou/></hash>" let elem: Element = "<hash xmlns='urn:xmpp:hashes:2' algo='sha-1'><coucou/></hash>"
.parse() .parse()

View file

@ -42,7 +42,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = let elem: Element =
"<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'><coucou/></idle>" "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'><coucou/></idle>"

View file

@ -770,7 +770,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child_in_reason() { fn test_invalid_child_in_reason() {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><decline/><a/></reason></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><decline/><a/></reason></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();

View file

@ -316,7 +316,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_received_unknown_child() { fn test_received_unknown_child() {
let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><coucou/></received>".parse().unwrap(); let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><coucou/></received>".parse().unwrap();
let error = Received::try_from(elem).unwrap_err(); let error = Received::try_from(elem).unwrap_err();
@ -358,7 +358,7 @@ mod tests {
); );
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_received() { fn test_invalid_received() {
let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator' coucou=''/>".parse().unwrap(); let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator' coucou=''/>".parse().unwrap();
@ -401,7 +401,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_checksum_unknown_child() { fn test_checksum_unknown_child() {
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file><coucou/></checksum>".parse().unwrap(); let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file><coucou/></checksum>".parse().unwrap();
let error = Checksum::try_from(elem).unwrap_err(); let error = Checksum::try_from(elem).unwrap_err();
@ -440,7 +440,7 @@ mod tests {
); );
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_checksum() { fn test_invalid_checksum() {
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator' coucou=''><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap(); let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator' coucou=''><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap();
@ -481,7 +481,7 @@ mod tests {
assert_eq!(range2.hashes, hashes); assert_eq!(range2.hashes, hashes);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_range() { fn test_invalid_range() {
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' coucou=''/>" let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' coucou=''/>"

View file

@ -300,7 +300,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<query xmlns='urn:xmpp:mam:2'><coucou/></query>" let elem: Element = "<query xmlns='urn:xmpp:mam:2'><coucou/></query>"
.parse() .parse()

View file

@ -154,7 +154,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_unknown_child() { fn test_unknown_child() {
let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>" let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>"
.parse() .parse()

View file

@ -47,7 +47,7 @@ mod tests {
Replace::try_from(elem).unwrap(); Replace::try_from(elem).unwrap();
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_attribute() { fn test_invalid_attribute() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou' coucou=''/>" let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou' coucou=''/>"
@ -62,7 +62,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = let elem: Element =
"<replace xmlns='urn:xmpp:message-correct:0' id='coucou'><coucou/></replace>" "<replace xmlns='urn:xmpp:message-correct:0' id='coucou'><coucou/></replace>"

View file

@ -127,7 +127,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_muc_invalid_child() { fn test_muc_invalid_child() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>" let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>"
.parse() .parse()
@ -153,7 +153,7 @@ mod tests {
assert_eq!(elem, elem2); assert_eq!(elem, elem2);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_muc_invalid_attribute() { fn test_muc_invalid_attribute() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>" let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>"

View file

@ -403,7 +403,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'> let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'>
<coucou/> <coucou/>
@ -433,7 +433,7 @@ mod tests {
assert_eq!(elem, elem2); assert_eq!(elem, elem2);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_attribute() { fn test_invalid_attribute() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>" let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>"
@ -468,7 +468,7 @@ mod tests {
assert_eq!(message, "Required attribute 'code' missing."); assert_eq!(message, "Required attribute 'code' missing.");
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_status_invalid_child() { fn test_status_invalid_child() {
let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'> let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'>
@ -579,7 +579,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_continue_invalid() { fn test_continue_invalid() {
let elem: Element = let elem: Element =
"<continue xmlns='http://jabber.org/protocol/muc#user'><foobar/></continue>" "<continue xmlns='http://jabber.org/protocol/muc#user'><foobar/></continue>"
@ -606,7 +606,7 @@ mod tests {
assert_eq!(elem2, elem3); assert_eq!(elem2, elem3);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_reason_invalid_attribute() { fn test_reason_invalid_attribute() {
let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>" let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>"
@ -620,7 +620,7 @@ mod tests {
assert_eq!(message, "Unknown attribute in Reason element.".to_owned()); assert_eq!(message, "Unknown attribute in Reason element.".to_owned());
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_reason_invalid() { fn test_reason_invalid() {
let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user'> let elem: Element = "<reason xmlns='http://jabber.org/protocol/muc#user'>
@ -636,7 +636,7 @@ mod tests {
assert_eq!(message, "Unknown child in Reason element.".to_owned()); assert_eq!(message, "Unknown child in Reason element.".to_owned());
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_item_invalid_attr() { fn test_item_invalid_attr() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user' let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'

View file

@ -15,7 +15,7 @@ generate_elem_id!(
mod tests { mod tests {
use super::*; use super::*;
use minidom::Element; use minidom::Element;
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
use xso::error::{Error, FromElementError}; use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
@ -48,7 +48,7 @@ mod tests {
assert_eq!(elem1, elem2); assert_eq!(elem1, elem2);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid() { fn test_invalid() {
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'><coucou/></nick>" let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'><coucou/></nick>"
@ -62,7 +62,7 @@ mod tests {
assert_eq!(message, "Unknown child in Nick element."); assert_eq!(message, "Unknown child in Nick element.");
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_attribute() { fn test_invalid_attribute() {
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick' coucou=''/>" let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick' coucou=''/>"

View file

@ -53,7 +53,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = let elem: Element =
"<occupant-id xmlns='urn:xmpp:occupant-id:0' id='foo'><coucou/></occupant-id>" "<occupant-id xmlns='urn:xmpp:occupant-id:0' id='foo'><coucou/></occupant-id>"

View file

@ -22,7 +22,7 @@ impl IqGetPayload for Ping {}
mod tests { mod tests {
use super::*; use super::*;
use minidom::Element; use minidom::Element;
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
use xso::error::{Error, FromElementError}; use xso::error::{Error, FromElementError};
#[test] #[test]
@ -43,7 +43,7 @@ mod tests {
assert_eq!(elem1, elem2); assert_eq!(elem1, elem2);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid() { fn test_invalid() {
let elem: Element = "<ping xmlns='urn:xmpp:ping'><coucou/></ping>" let elem: Element = "<ping xmlns='urn:xmpp:ping'><coucou/></ping>"
@ -57,7 +57,7 @@ mod tests {
assert_eq!(message, "Unknown child in Ping element."); assert_eq!(message, "Unknown child in Ping element.");
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_attribute() { fn test_invalid_attribute() {
let elem: Element = "<ping xmlns='urn:xmpp:ping' coucou=''/>".parse().unwrap(); let elem: Element = "<ping xmlns='urn:xmpp:ping' coucou=''/>".parse().unwrap();

View file

@ -533,7 +533,7 @@ mod tests {
assert!(payload.is("test", "invalid")); assert!(payload.is("test", "invalid"));
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_status_child() { fn test_invalid_status_child() {
#[cfg(not(feature = "component"))] #[cfg(not(feature = "component"))]
@ -556,7 +556,7 @@ mod tests {
); );
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_attribute() { fn test_invalid_attribute() {
#[cfg(not(feature = "component"))] #[cfg(not(feature = "component"))]

View file

@ -278,7 +278,7 @@ mod tests {
assert_eq!(message, "This is not a Payload element."); assert_eq!(message, "This is not a Payload element.");
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid_attribute() { fn test_invalid_attribute() {
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event' coucou=''/>" let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event' coucou=''/>"

View file

@ -264,7 +264,7 @@ mod tests {
assert_eq!(roster.items[0].subscription, Subscription::Remove); assert_eq!(roster.items[0].subscription, Subscription::Remove);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
#[test] #[test]
fn test_invalid() { fn test_invalid() {
let elem: Element = "<query xmlns='jabber:iq:roster'><coucou/></query>" let elem: Element = "<query xmlns='jabber:iq:roster'><coucou/></query>"
@ -317,7 +317,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_item_unknown_child() { fn test_item_unknown_child() {
let elem: Element = let elem: Element =
"<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>" "<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>"

View file

@ -130,7 +130,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>" let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>"
.parse() .parse()

View file

@ -263,7 +263,7 @@ mod tests {
/// Some servers apparently use a non-namespaced 'lang' attribute, which is invalid as not part /// Some servers apparently use a non-namespaced 'lang' attribute, which is invalid as not part
/// of the schema. This tests whether we can parse it when disabling validation. /// of the schema. This tests whether we can parse it when disabling validation.
#[cfg(feature = "disable-validation")] #[cfg(not(feature = "pedantic"))]
#[test] #[test]
fn invalid_failure_with_non_prefixed_text_lang() { fn invalid_failure_with_non_prefixed_text_lang() {
let elem: Element = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> let elem: Element = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>

View file

@ -76,7 +76,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")] #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = let elem: Element =
"<stanza-id xmlns='urn:xmpp:sid:0' by='a@b' id='x'><coucou/></stanza-id>" "<stanza-id xmlns='urn:xmpp:sid:0' by='a@b' id='x'><coucou/></stanza-id>"

View file

@ -121,10 +121,7 @@ fn empty_namespace_mismatch() {
} }
#[test] #[test]
#[cfg_attr( #[cfg_attr(not(feature = "pedantic"), should_panic = "unexpected result: Ok(")]
feature = "disable-validation",
should_panic = "unexpected result: Ok("
)]
fn empty_unexpected_attribute() { fn empty_unexpected_attribute() {
#[allow(unused_imports)] #[allow(unused_imports)]
use core::{ use core::{
@ -153,10 +150,7 @@ fn empty_ignores_xml_lang() {
} }
#[test] #[test]
#[cfg_attr( #[cfg_attr(not(feature = "pedantic"), should_panic = "unexpected result: Ok(")]
feature = "disable-validation",
should_panic = "unexpected result: Ok("
)]
fn empty_unexpected_child() { fn empty_unexpected_child() {
#[allow(unused_imports)] #[allow(unused_imports)]
use core::{ use core::{
@ -944,10 +938,7 @@ fn text_extract_negative_absent_child() {
} }
#[test] #[test]
#[cfg_attr( #[cfg_attr(not(feature = "pedantic"), should_panic = "unexpected result: Ok(")]
feature = "disable-validation",
should_panic = "unexpected result: Ok("
)]
fn text_extract_negative_unexpected_attribute_in_child() { fn text_extract_negative_unexpected_attribute_in_child() {
#[allow(unused_imports)] #[allow(unused_imports)]
use core::{ use core::{
@ -961,10 +952,7 @@ fn text_extract_negative_unexpected_attribute_in_child() {
} }
#[test] #[test]
#[cfg_attr( #[cfg_attr(not(feature = "pedantic"), should_panic = "unexpected result: Ok(")]
feature = "disable-validation",
should_panic = "unexpected result: Ok("
)]
fn text_extract_negative_unexpected_child_in_child() { fn text_extract_negative_unexpected_child_in_child() {
#[allow(unused_imports)] #[allow(unused_imports)]
use core::{ use core::{
@ -1647,7 +1635,7 @@ fn element_catch_one_negative_none() {
} }
#[test] #[test]
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
fn element_catch_one_negative_more_than_one_child() { fn element_catch_one_negative_more_than_one_child() {
#[allow(unused_imports)] #[allow(unused_imports)]
use core::{ use core::{
@ -1690,7 +1678,7 @@ fn element_catch_maybe_one_roundtrip_some() {
} }
#[test] #[test]
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
fn element_catch_maybe_one_negative_more_than_one_child() { fn element_catch_maybe_one_negative_more_than_one_child() {
#[allow(unused_imports)] #[allow(unused_imports)]
use core::{ use core::{
@ -2073,10 +2061,7 @@ fn ignore_unknown_attributes_positive() {
} }
#[test] #[test]
#[cfg_attr( #[cfg_attr(not(feature = "pedantic"), should_panic = "unexpected result: Ok(")]
feature = "disable-validation",
should_panic = "unexpected result: Ok("
)]
fn ignore_unknown_attributes_negative_unexpected_child() { fn ignore_unknown_attributes_negative_unexpected_child() {
#[allow(unused_imports)] #[allow(unused_imports)]
use core::{ use core::{
@ -2119,10 +2104,7 @@ fn ignore_unknown_children_positive() {
} }
#[test] #[test]
#[cfg_attr( #[cfg_attr(not(feature = "pedantic"), should_panic = "unexpected result: Ok(")]
feature = "disable-validation",
should_panic = "unexpected result: Ok("
)]
fn ignore_unknown_children_negative_unexpected_attribute() { fn ignore_unknown_children_negative_unexpected_attribute() {
#[allow(unused_imports)] #[allow(unused_imports)]
use core::{ use core::{
@ -2265,10 +2247,7 @@ fn discard_attribute_absent_roundtrip() {
} }
#[test] #[test]
#[cfg_attr( #[cfg_attr(not(feature = "pedantic"), should_panic = "unexpected result: Ok(")]
feature = "disable-validation",
should_panic = "unexpected result: Ok("
)]
fn discard_attribute_fails_on_other_unexpected_attributes() { fn discard_attribute_fails_on_other_unexpected_attributes() {
#[allow(unused_imports)] #[allow(unused_imports)]
use core::{ use core::{

View file

@ -318,7 +318,7 @@ macro_rules! check_ns_only {
macro_rules! check_no_children { macro_rules! check_no_children {
($elem:ident, $name:tt) => { ($elem:ident, $name:tt) => {
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
for _ in $elem.children() { for _ in $elem.children() {
return Err(xso::error::Error::Other( return Err(xso::error::Error::Other(
concat!("Unknown child in ", $name, " element.").into(), concat!("Unknown child in ", $name, " element.").into(),
@ -330,7 +330,7 @@ macro_rules! check_no_children {
macro_rules! check_no_attributes { macro_rules! check_no_attributes {
($elem:ident, $name:tt) => { ($elem:ident, $name:tt) => {
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
for _ in $elem.attrs() { for _ in $elem.attrs() {
return Err(xso::error::Error::Other( return Err(xso::error::Error::Other(
concat!("Unknown attribute in ", $name, " element.").into(), concat!("Unknown attribute in ", $name, " element.").into(),
@ -342,7 +342,7 @@ macro_rules! check_no_attributes {
macro_rules! check_no_unknown_attributes { macro_rules! check_no_unknown_attributes {
($elem:ident, $name:tt, [$($attr:tt),*]) => ( ($elem:ident, $name:tt, [$($attr:tt),*]) => (
#[cfg(not(feature = "disable-validation"))] #[cfg(feature = "pedantic")]
for ((ns, attr), _) in $elem.attrs() { for ((ns, attr), _) in $elem.attrs() {
$( $(
if *ns == ::xso::exports::rxml::Namespace::NONE && attr == $attr { if *ns == ::xso::exports::rxml::Namespace::NONE && attr == $attr {

View file

@ -90,7 +90,7 @@ serde = [ "xmpp-parsers/serde" ]
dns = [ "hickory-resolver", "idna" ] dns = [ "hickory-resolver", "idna" ]
component = ["xmpp-parsers/component", "insecure-tcp"] component = ["xmpp-parsers/component", "insecure-tcp"]
disable-validation = ["xmpp-parsers/disable-validation"] pedantic = ["xmpp-parsers/pedantic"]
[lints.rust] [lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(xmpprs_doc_build)'] } unexpected_cfgs = { level = "warn", check-cfg = ['cfg(xmpprs_doc_build)'] }

View file

@ -1,5 +1,10 @@
Version NEXT: Version NEXT:
0000-00-00 RELEASER <releaser@domain> 0000-00-00 RELEASER <releaser@domain>
* Breaking:
- Removed the `disable-validation` feature and made the behaviour the
new default. The newly-introduced `pedantic` feature can be used to
opt into the previous default behaviour of rejecting all unknown
child elements and attributes.
* Added: * Added:
- Expose `client_auth` method to allow manual stream setups for advanced - Expose `client_auth` method to allow manual stream setups for advanced
use cases. use cases.

View file

@ -31,7 +31,7 @@ default = [ "std" ]
macros = [ "dep:xso_proc", "rxml/macros" ] macros = [ "dep:xso_proc", "rxml/macros" ]
minidom = [ "xso_proc/minidom"] minidom = [ "xso_proc/minidom"]
panicking-into-impl = ["xso_proc/panicking-into-impl"] panicking-into-impl = ["xso_proc/panicking-into-impl"]
non-pedantic = [] pedantic = []
std = [] std = []
[package.metadata.docs.rs] [package.metadata.docs.rs]

View file

@ -1,4 +1,8 @@
Version NEXT: Version NEXT:
* Breaking
- Removed the `non-pedantic` feature and made the behaviour the default.
- Added the `pedantic` feature, opting into the previous default
behaviour of rejecting unknown attributes and children.
* Changes * Changes
- Fix some Clippy warnings - Fix some Clippy warnings
- Fix build with minidom and without std (!661) - Fix build with minidom and without std (!661)

View file

@ -349,17 +349,17 @@ pub trait AsOptionalXmlText {
pub enum UnknownAttributePolicy { pub enum UnknownAttributePolicy {
/// All unknown attributes are discarded. /// All unknown attributes are discarded.
/// ///
/// This is the default policy if the crate is built with the /// This is the default policy if the crate is built without the
/// `non-pedantic` feature. /// `pedantic` feature.
#[cfg_attr(feature = "non-pedantic", default)] #[cfg_attr(not(feature = "pedantic"), default)]
Discard, Discard,
/// The first unknown attribute which is encountered generates a fatal /// The first unknown attribute which is encountered generates a fatal
/// parsing error. /// parsing error.
/// ///
/// This is the default policy if the crate is built **without** the /// This is the default policy if the crate is built with the
/// `non-pedantic` feature. /// `pedantic` feature.
#[cfg_attr(not(feature = "non-pedantic"), default)] #[cfg_attr(feature = "pedantic", default)]
Fail, Fail,
} }
@ -386,17 +386,17 @@ impl UnknownAttributePolicy {
pub enum UnknownChildPolicy { pub enum UnknownChildPolicy {
/// All unknown children are discarded. /// All unknown children are discarded.
/// ///
/// This is the default policy if the crate is built with the /// This is the default policy if the crate is built without the
/// `non-pedantic` feature. /// `pedantic` feature.
#[cfg_attr(feature = "non-pedantic", default)] #[cfg_attr(not(feature = "pedantic"), default)]
Discard, Discard,
/// The first unknown child which is encountered generates a fatal /// The first unknown child which is encountered generates a fatal
/// parsing error. /// parsing error.
/// ///
/// This is the default policy if the crate is built **without** the /// This is the default policy if the crate is built with the
/// `non-pedantic` feature. /// `pedantic` feature.
#[cfg_attr(not(feature = "non-pedantic"), default)] #[cfg_attr(feature = "pedantic", default)]
Fail, Fail,
} }