Commit 47d6da0884d for nodejs

commit 47d6da0884dee87675e5f6e8bdf4a33b729cb744
Author: Gürgün Dayıoğlu <hey@gurgun.day>
Date:   Sun Nov 15 22:15:27 2026 +0300

    fs,path: fix glob globstar section bounds

    The last start index of each globstar-delimited body section was
    computed from the non-globstar prefix counts in reverse order. When
    sections have different lengths, the bound is too tight, so paths
    such as `a/b/c/d/e` failed to match `**/a/b/c/**/d/**`. This affected
    `path.matchesGlob()` and `exclude` patterns in `fs.glob()`.

    Compute the bound from the number of non-globstar parts in the
    section and every section after it.

    Signed-off-by: Gürgün Dayıoğlu <hey@gurgun.day>
    PR-URL: https://github.com/nodejs/node/pull/66738
    Reviewed-By: Aviv Keller <me@aviv.sh>
    Reviewed-By: Efe Karasakal <hi@efe.dev>

diff --git a/src/glob/glob_matcher.cc b/src/glob/glob_matcher.cc
index e0cca5cf43e..53d59fb8a35 100644
--- a/src/glob/glob_matcher.cc
+++ b/src/glob/glob_matcher.cc
@@ -673,12 +673,9 @@ class PathMatcher {
     }
     const ptrdiff_t file_length = static_cast<ptrdiff_t>(file.size()) -
                                   static_cast<ptrdiff_t>(file_tail_match);
-    {
-      size_t i = sections.size() - 1;
-      for (Section& s : sections) {
-        s.after = file_length - (static_cast<ptrdiff_t>(sections[i--].non_gs) +
-                                 static_cast<ptrdiff_t>(s.end - s.begin));
-      }
+    // leave room for this section and every section after it
+    for (Section& s : sections) {
+      s.after = file_length - static_cast<ptrdiff_t>(non_gs - s.non_gs);
     }
     return MatchBodySections(
         file, pattern, sections, fi, 0, 0, file_tail_match != 0);
diff --git a/test/parallel/test-path-glob.js b/test/parallel/test-path-glob.js
index 11633f52245..6b53e26bdd6 100644
--- a/test/parallel/test-path-glob.js
+++ b/test/parallel/test-path-glob.js
@@ -16,6 +16,9 @@ const globs = {
     ['foo\\bar\\baz\\boo', 'foo\\[bc-r]ar\\baz\\*', true], // Matches 'bar' or 'car' in 'foo\\bar'
     ['foo\\bar\\baz', 'foo/**', true], // Matches anything in 'foo'
     ['foo\\bar\\baz', '*', false], // No match
+    ['a\\b\\c\\d\\e', '**/a/b/c/**/d/**', true], // Globstar sections of different lengths
+    ['x\\a\\b\\c\\d\\e', '**/a/b/c/**/d/**/e', true], // Globstar sections of different lengths
+    ['a\\b\\c\\d', '**/a/b/c/**/d/**', false], // Trailing globstar needs a segment
   ],
   posix: [
     ['foo/bar/baz', 'foo/[bcr]ar/baz', true], // Matches 'bar' or 'car' in 'foo/bar'
@@ -28,6 +31,9 @@ const globs = {
     ['foo/bar/baz/boo', 'foo/[bc-r]ar/baz/*', true], // Matches 'bar' or 'car' in 'foo/bar'
     ['foo/bar/baz', 'foo/**', true], // Matches anything in 'foo'
     ['foo/bar/baz', '*', false], // No match
+    ['a/b/c/d/e', '**/a/b/c/**/d/**', true], // Globstar sections of different lengths
+    ['x/a/b/c/d/e', '**/a/b/c/**/d/**/e', true], // Globstar sections of different lengths
+    ['a/b/c/d', '**/a/b/c/**/d/**', false], // Trailing globstar needs a segment
   ],
 };