From e4f92df78c6fcd2ca0810f0c60bc02eefbce9888 Mon Sep 17 00:00:00 2001 From: Nigel Ihza Firdausy <106657905+Ni-zav@users.noreply.github.com> Date: Sun, 11 Oct 2026 21:17:17 +0700 Subject: [PATCH] fix(meta): reject incomplete and mismatched GPX documents --- crates/meta/src/gpx.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/meta/src/gpx.rs b/crates/meta/src/gpx.rs index a8e8e9071..e63499033 100644 --- a/crates/meta/src/gpx.rs +++ b/crates/meta/src/gpx.rs @@ -96,7 +96,9 @@ struct Pending { /// Parse a GPX document. pub fn parse_gpx(s: &str) -> Result { let mut reader = quick_xml::Reader::from_str(s); - reader.config_mut().check_end_names = false; + // Mismatched end tags must be rejected; otherwise the path stack can + // attribute a point's time or segment to the wrong enclosing element. + reader.config_mut().check_end_names = true; let mut path: Vec> = Vec::new(); let mut seen_gpx = false; let mut log = Tracklog::default(); @@ -169,6 +171,9 @@ pub fn parse_gpx(s: &str) -> Result { if !seen_gpx { return Err(GpxError::NotGpx); } + if !path.is_empty() { + return Err(GpxError::Xml("unclosed GPX elements".into())); + } // stable: points logged at the same second keep their file order log.points.sort_by(|a, b| a.time.total_cmp(&b.time)); Ok(log) @@ -362,6 +367,12 @@ mod tests { fn rejects_non_gpx_and_bad_input() { assert_eq!(parse_gpx(""), Err(GpxError::NotGpx)); assert!(parse_gpx("").is_err()); + // A partial logger write must not be mistaken for a complete track, + // even if it contains a valid timed point. + let point = r#""#; + assert!(matches!(parse_gpx(point), Err(GpxError::Xml(_)))); + let wrong_close = r#""#; + assert!(matches!(parse_gpx(wrong_close), Err(GpxError::Xml(_)))); assert!( parse_gpx("") .unwrap()