changeset 52719:77b95a4abbb2 stable

narrow: stricter validation of narrowspec patterns in rhg Do the same whitespace-at-the-edge validation in rhg that we do in Python.
author Arseniy Alekseyev <aalekseyev@janestreet.com>
date Tue, 28 Jan 2025 17:29:59 +0000
parents 1c83ebf78f74
children b019b5798e8f
files rust/hg-core/src/narrow.rs rust/hg-core/src/sparse.rs rust/rhg/src/error.rs
diffstat 3 files changed, 42 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/rust/hg-core/src/narrow.rs	Fri Jan 10 16:14:40 2025 +0000
+++ b/rust/hg-core/src/narrow.rs	Tue Jan 28 17:29:59 2025 +0000
@@ -97,11 +97,26 @@
     Ok((m, warnings))
 }
 
+fn is_whitespace(b: &u8) -> bool {
+    // should match what .strip() in Python does
+    b.is_ascii_whitespace() || *b == 0x0b
+}
+
+fn starts_or_ends_with_whitespace(s: &[u8]) -> bool {
+    let w = |b: Option<&u8>| b.map(is_whitespace).unwrap_or(false);
+    w(s.first()) || w(s.last())
+}
+
 fn validate_patterns(patterns: &[u8]) -> Result<(), SparseConfigError> {
     for pattern in patterns.split(|c| *c == b'\n') {
         if pattern.is_empty() {
             continue;
         }
+        if starts_or_ends_with_whitespace(pattern) {
+            return Err(SparseConfigError::WhitespaceAtEdgeOfPattern(
+                pattern.to_owned(),
+            ));
+        }
         for prefix in VALID_PREFIXES.iter() {
             if pattern.starts_with(prefix.as_bytes()) {
                 return Ok(());
--- a/rust/hg-core/src/sparse.rs	Fri Jan 10 16:14:40 2025 +0000
+++ b/rust/hg-core/src/sparse.rs	Tue Jan 28 17:29:59 2025 +0000
@@ -87,6 +87,10 @@
     /// An invalid pattern prefix was given to the narrow spec. Includes the
     /// entire pattern for context.
     InvalidNarrowPrefix(Vec<u8>),
+    /// Narrow/sparse patterns can not begin or end in whitespace
+    /// because the Python parser strips the whitespace when parsing
+    /// the config file.
+    WhitespaceAtEdgeOfPattern(Vec<u8>),
     #[from]
     HgError(HgError),
     #[from]
@@ -136,6 +140,20 @@
                     VALID_PREFIXES.join(", ")
                 )),
             },
+            SparseConfigError::WhitespaceAtEdgeOfPattern(vec) => {
+                HgError::Abort {
+                    message: String::from_utf8_lossy(&format_bytes!(
+                        b"narrow pattern with whitespace at the edge: {}",
+                        vec
+                    ))
+                    .to_string(),
+                    detailed_exit_code: STATE_ERROR,
+                    hint: Some(
+                        "narrow patterns can't begin or end in whitespace"
+                            .to_string(),
+                    ),
+                }
+            }
             SparseConfigError::HgError(hg_error) => hg_error,
             SparseConfigError::PatternError(pattern_error) => HgError::Abort {
                 message: pattern_error.to_string(),
--- a/rust/rhg/src/error.rs	Fri Jan 10 16:14:40 2025 +0000
+++ b/rust/rhg/src/error.rs	Tue Jan 28 17:29:59 2025 +0000
@@ -295,6 +295,15 @@
                     exit_codes::ABORT,
                 )
             }
+            SparseConfigError::WhitespaceAtEdgeOfPattern(prefix) => {
+                Self::abort_with_exit_code_bytes(
+                    format_bytes!(
+                        b"narrow pattern with whitespace at the edge: {}",
+                        &prefix
+                    ),
+                    exit_codes::ABORT,
+                )
+            }
             SparseConfigError::IncludesInNarrow => Self::abort(
                 "including other spec files using '%include' \
                     is not supported in narrowspec",