Skip to content

fix: match globMatch rules with Go Casbin (doublestar) - #585

Closed
bardrop wants to merge 2 commits into
apache:masterfrom
bardrop-oss:fix-globmatch-doublestar
Closed

bardrop wants to merge 2 commits into
apache:masterfrom
bardrop-oss:fix-globmatch-doublestar

Conversation

@bardrop

@bardrop bardrop commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #582

globMatch used minimatch with its default options, while Go Casbin's util.GlobMatch uses doublestar. They differ a lot on paths:

  • * / ** skip dot-files: /admin/.env did not match /admin/*, /files/.git/config did not match /files/**
  • * never matched an empty segment: /admin/ did not match /admin/*
  • a leading ! negated the whole pattern: /a/b matched !/c
  • a trailing slash was ignored: /a/b/ matched /a/b
  • extglobs like +(b) worked

So a globMatch deny rule (p, guest, /admin/*, read, deny) let /admin/.env and /admin/ through in Node, while Go denies them.

minimatch has no option for the empty-segment and trailing-slash cases, so this PR adds a small matcher with doublestar's rules instead: * (any non-/ run, also empty and dot-files), ?, ** as a whole segment (zero or more directories; /a/** also matches /a), [...] / [!...] / [^...], {a,b} and \ escapes. Braces are expanded first (max 1024 alternatives), then the tokens are matched with a small dynamic program over (token, position), so it runs in O(tokens × length) and never backtracks: /**/a/**/a/**/a/**/y against a 6000-character path takes ~1 ms (minimatch needs minutes there). A bad pattern (/[a, /{a, trailing \) matches nothing; Go returns an error there. The old */ workaround for minimatch is not needed anymore. minimatch is no longer used, so I removed it from dependencies (and its 3 entries from yarn.lock; yarn install --frozen-lockfile passes).

Testing

  • New globMatch cases in test/util.test.ts fail before (/admin/.env vs /admin/*) and pass after. All 71 literal globMatch expectations in the file (old + new, incl. keys with \n) give the same result in Go Casbin v2.135.0 util.GlobMatch. A long-path test guards against backtracking.
  • The model/policy from the issue now prints false for /admin/users, /admin/.env and /admin/, same as Go.
  • Random diff against Go util.GlobMatch on 120,000 key/pattern pairs (path-like and random characters): old code differs on 844, new code on 94. The 94 left are odd patterns where doublestar has its own quirks: 3 or more * in a row, empty {} alternatives ({a,}), ** inside braces (/a vs /a/{**,x} is false in Go, but /a vs /a/** is true), and some empty matches before /** (/ vs /*/** is false in Go). Happy to handle any of those if you want exact parity there.
  • jest (17 suites, 239 passed), eslint . --ext .js,.ts, prettier, npm run build clean.

globMatch used minimatch with its defaults: `*` and `**` skip
dot-files, `*` never matches an empty segment, a leading `!` negates
the pattern, extglobs work and a trailing slash is ignored. Go Casbin
uses doublestar, which does none of that, so a globMatch deny rule like
`/admin/*` let `/admin/.env` and `/admin/` through in Node only.

Now the pattern is turned into a RegExp with doublestar rules (`*`,
`?`, `**` segments, classes, braces, `\\` escapes); a bad pattern
matches nothing. minimatch is no longer used, so drop the dependency.

Fixes #582
The RegExp built for `**` backtracked: a pattern like
/**/a/**/a/**/y took seconds on a 6000-character path. Expand braces
first, then match tokens with a dynamic program over (token, position),
which takes O(tokens * length) time. `**` now also matches line breaks,
like in Go.
@hsluoyz

hsluoyz commented Nov 15, 2026

Copy link
Copy Markdown
Member

see: #582 (comment)

@hsluoyz hsluoyz closed this Nov 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

globMatch does not match dot-prefixed segments or an empty * segment, so deny rules are skipped (differs from Go Casbin)

2 participants