Skip to content

Commit 4fe1066

Browse files
authored
Replace gobwas/glob package (go-gitea#35478)
https://github.com/gobwas/glob is unmaintained and has bugs.
1 parent 688abac commit 4fe1066

File tree

29 files changed

+418
-45
lines changed

29 files changed

+418
-45
lines changed

assets/go-licenses.json

Lines changed: 0 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/embedded.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import (
1212
"strings"
1313

1414
"code.gitea.io/gitea/modules/assetfs"
15+
"code.gitea.io/gitea/modules/glob"
1516
"code.gitea.io/gitea/modules/log"
1617
"code.gitea.io/gitea/modules/options"
1718
"code.gitea.io/gitea/modules/public"
1819
"code.gitea.io/gitea/modules/setting"
1920
"code.gitea.io/gitea/modules/templates"
2021
"code.gitea.io/gitea/modules/util"
2122

22-
"github.com/gobwas/glob"
2323
"github.com/urfave/cli/v3"
2424
)
2525

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ require (
6161
github.com/go-redsync/redsync/v4 v4.13.0
6262
github.com/go-sql-driver/mysql v1.9.3
6363
github.com/go-webauthn/webauthn v0.13.4
64-
github.com/gobwas/glob v0.2.3
6564
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
6665
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
6766
github.com/golang-jwt/jwt/v5 v5.3.0

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,6 @@ github.com/go-webauthn/webauthn v0.13.4 h1:q68qusWPcqHbg9STSxBLBHnsKaLxNO0RnVKaA
364364
github.com/go-webauthn/webauthn v0.13.4/go.mod h1:MglN6OH9ECxvhDqoq1wMoF6P6JRYDiQpC9nc5OomQmI=
365365
github.com/go-webauthn/x v0.1.24 h1:6LaWf2zzWqbyKT8IyQkhje1/1KCGhlEkMz4V1tDnt/A=
366366
github.com/go-webauthn/x v0.1.24/go.mod h1:2o5XKJ+X1AKqYKGgHdKflGnoQFQZ6flJ2IFCBKSbSOw=
367-
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
368-
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
369367
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
370368
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
371369
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=

models/git/protected_branch.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ import (
1717
repo_model "code.gitea.io/gitea/models/repo"
1818
"code.gitea.io/gitea/models/unit"
1919
user_model "code.gitea.io/gitea/models/user"
20+
"code.gitea.io/gitea/modules/glob"
2021
"code.gitea.io/gitea/modules/log"
2122
"code.gitea.io/gitea/modules/timeutil"
2223
"code.gitea.io/gitea/modules/util"
2324

24-
"github.com/gobwas/glob"
25-
"github.com/gobwas/glob/syntax"
2625
"xorm.io/builder"
2726
)
2827

@@ -77,7 +76,7 @@ func init() {
7776
// IsRuleNameSpecial return true if it contains special character
7877
func IsRuleNameSpecial(ruleName string) bool {
7978
for i := 0; i < len(ruleName); i++ {
80-
if syntax.Special(ruleName[i]) {
79+
if glob.IsSpecialByte(ruleName[i]) {
8180
return true
8281
}
8382
}

models/git/protected_branch_list.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ import (
88
"sort"
99

1010
"code.gitea.io/gitea/models/db"
11+
"code.gitea.io/gitea/modules/glob"
1112
"code.gitea.io/gitea/modules/optional"
12-
13-
"github.com/gobwas/glob"
1413
)
1514

1615
type ProtectedBranchRules []*ProtectedBranch

models/git/protected_tag.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ import (
1111

1212
"code.gitea.io/gitea/models/db"
1313
"code.gitea.io/gitea/models/organization"
14+
"code.gitea.io/gitea/modules/glob"
1415
"code.gitea.io/gitea/modules/timeutil"
15-
16-
"github.com/gobwas/glob"
1716
)
1817

1918
// ProtectedTag struct

modules/actions/workflows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import (
1010
"strings"
1111

1212
"code.gitea.io/gitea/modules/git"
13+
"code.gitea.io/gitea/modules/glob"
1314
"code.gitea.io/gitea/modules/log"
1415
api "code.gitea.io/gitea/modules/structs"
1516
webhook_module "code.gitea.io/gitea/modules/webhook"
1617

17-
"github.com/gobwas/glob"
1818
"github.com/nektos/act/pkg/jobparser"
1919
"github.com/nektos/act/pkg/model"
2020
"github.com/nektos/act/pkg/workflowpattern"

modules/glob/glob.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package glob
5+
6+
import (
7+
"errors"
8+
"fmt"
9+
"regexp"
10+
11+
"code.gitea.io/gitea/modules/util"
12+
)
13+
14+
// Reference: https://github.com/gobwas/glob/blob/master/glob.go
15+
16+
type Glob interface {
17+
Match(string) bool
18+
}
19+
20+
type globCompiler struct {
21+
nonSeparatorChars string
22+
globPattern []rune
23+
regexpPattern string
24+
regexp *regexp.Regexp
25+
pos int
26+
}
27+
28+
// compileChars compiles character class patterns like [abc] or [!abc]
29+
func (g *globCompiler) compileChars() (string, error) {
30+
result := ""
31+
if g.pos < len(g.globPattern) && g.globPattern[g.pos] == '!' {
32+
g.pos++
33+
result += "^"
34+
}
35+
36+
for g.pos < len(g.globPattern) {
37+
c := g.globPattern[g.pos]
38+
g.pos++
39+
40+
if c == ']' {
41+
return "[" + result + "]", nil
42+
}
43+
44+
if c == '\\' {
45+
if g.pos >= len(g.globPattern) {
46+
return "", errors.New("unterminated character class escape")
47+
}
48+
result += "\\" + string(g.globPattern[g.pos])
49+
g.pos++
50+
} else {
51+
result += string(c)
52+
}
53+
}
54+
55+
return "", errors.New("unterminated character class")
56+
}
57+
58+
// compile compiles the glob pattern into a regular expression
59+
func (g *globCompiler) compile(subPattern bool) (string, error) {
60+
result := ""
61+
62+
for g.pos < len(g.globPattern) {
63+
c := g.globPattern[g.pos]
64+
g.pos++
65+
66+
if subPattern && c == '}' {
67+
return "(" + result + ")", nil
68+
}
69+
70+
switch c {
71+
case '*':
72+
if g.pos < len(g.globPattern) && g.globPattern[g.pos] == '*' {
73+
g.pos++
74+
result += ".*" // match any sequence of characters
75+
} else {
76+
result += g.nonSeparatorChars + "*" // match any sequence of non-separator characters
77+
}
78+
case '?':
79+
result += g.nonSeparatorChars // match any single non-separator character
80+
case '[':
81+
chars, err := g.compileChars()
82+
if err != nil {
83+
return "", err
84+
}
85+
result += chars
86+
case '{':
87+
subResult, err := g.compile(true)
88+
if err != nil {
89+
return "", err
90+
}
91+
result += subResult
92+
case ',':
93+
if subPattern {
94+
result += "|"
95+
} else {
96+
result += ","
97+
}
98+
case '\\':
99+
if g.pos >= len(g.globPattern) {
100+
return "", errors.New("no character to escape")
101+
}
102+
result += "\\" + string(g.globPattern[g.pos])
103+
g.pos++
104+
case '.', '+', '^', '$', '(', ')', '|':
105+
result += "\\" + string(c) // escape regexp special characters
106+
default:
107+
result += string(c)
108+
}
109+
}
110+
111+
return result, nil
112+
}
113+
114+
func newGlobCompiler(pattern string, separators ...rune) (Glob, error) {
115+
g := &globCompiler{globPattern: []rune(pattern)}
116+
117+
// Escape separators for use in character class
118+
escapedSeparators := regexp.QuoteMeta(string(separators))
119+
if escapedSeparators != "" {
120+
g.nonSeparatorChars = "[^" + escapedSeparators + "]"
121+
} else {
122+
g.nonSeparatorChars = "."
123+
}
124+
125+
compiled, err := g.compile(false)
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
g.regexpPattern = "^" + compiled + "$"
131+
132+
regex, err := regexp.Compile(g.regexpPattern)
133+
if err != nil {
134+
return nil, fmt.Errorf("failed to compile regexp: %w", err)
135+
}
136+
137+
g.regexp = regex
138+
return g, nil
139+
}
140+
141+
func (g *globCompiler) Match(s string) bool {
142+
return g.regexp.MatchString(s)
143+
}
144+
145+
func Compile(pattern string, separators ...rune) (Glob, error) {
146+
return newGlobCompiler(pattern, separators...)
147+
}
148+
149+
func MustCompile(pattern string, separators ...rune) Glob {
150+
g, err := Compile(pattern, separators...)
151+
if err != nil {
152+
panic(err)
153+
}
154+
return g
155+
}
156+
157+
func IsSpecialByte(c byte) bool {
158+
return c == '*' || c == '?' || c == '\\' || c == '[' || c == ']' || c == '{' || c == '}'
159+
}
160+
161+
// QuoteMeta returns a string that quotes all glob pattern meta characters
162+
// inside the argument text; For example, QuoteMeta(`{foo*}`) returns `\[foo\*\]`.
163+
// Reference: https://github.com/gobwas/glob/blob/master/glob.go
164+
func QuoteMeta(s string) string {
165+
pos := 0
166+
for pos < len(s) && !IsSpecialByte(s[pos]) {
167+
pos++
168+
}
169+
if pos == len(s) {
170+
return s
171+
}
172+
b := make([]byte, pos+2*(len(s)-pos))
173+
copy(b, s[0:pos])
174+
to := pos
175+
for ; pos < len(s); pos++ {
176+
if IsSpecialByte(s[pos]) {
177+
b[to] = '\\'
178+
to++
179+
}
180+
b[to] = s[pos]
181+
to++
182+
}
183+
return util.UnsafeBytesToString(b[0:to])
184+
}

0 commit comments

Comments
 (0)