Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions assets/go-licenses.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cmd/embedded.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import (
"strings"

"code.gitea.io/gitea/modules/assetfs"
"code.gitea.io/gitea/modules/glob"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util"

"github.com/gobwas/glob"
"github.com/urfave/cli/v3"
)

Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ require (
github.com/go-redsync/redsync/v4 v4.13.0
github.com/go-sql-driver/mysql v1.9.3
github.com/go-webauthn/webauthn v0.13.4
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.3.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,6 @@ github.com/go-webauthn/webauthn v0.13.4 h1:q68qusWPcqHbg9STSxBLBHnsKaLxNO0RnVKaA
github.com/go-webauthn/webauthn v0.13.4/go.mod h1:MglN6OH9ECxvhDqoq1wMoF6P6JRYDiQpC9nc5OomQmI=
github.com/go-webauthn/x v0.1.24 h1:6LaWf2zzWqbyKT8IyQkhje1/1KCGhlEkMz4V1tDnt/A=
github.com/go-webauthn/x v0.1.24/go.mod h1:2o5XKJ+X1AKqYKGgHdKflGnoQFQZ6flJ2IFCBKSbSOw=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
Expand Down
5 changes: 2 additions & 3 deletions models/git/protected_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/glob"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"

"github.com/gobwas/glob"
"github.com/gobwas/glob/syntax"
"xorm.io/builder"
)

Expand Down Expand Up @@ -77,7 +76,7 @@ func init() {
// IsRuleNameSpecial return true if it contains special character
func IsRuleNameSpecial(ruleName string) bool {
for i := 0; i < len(ruleName); i++ {
if syntax.Special(ruleName[i]) {
if glob.IsSpecialByte(ruleName[i]) {
return true
}
}
Expand Down
3 changes: 1 addition & 2 deletions models/git/protected_branch_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import (
"sort"

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/glob"
"code.gitea.io/gitea/modules/optional"

"github.com/gobwas/glob"
)

type ProtectedBranchRules []*ProtectedBranch
Expand Down
3 changes: 1 addition & 2 deletions models/git/protected_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import (

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/modules/glob"
"code.gitea.io/gitea/modules/timeutil"

"github.com/gobwas/glob"
)

// ProtectedTag struct
Expand Down
2 changes: 1 addition & 1 deletion modules/actions/workflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import (
"strings"

"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/glob"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
webhook_module "code.gitea.io/gitea/modules/webhook"

"github.com/gobwas/glob"
"github.com/nektos/act/pkg/jobparser"
"github.com/nektos/act/pkg/model"
"github.com/nektos/act/pkg/workflowpattern"
Expand Down
184 changes: 184 additions & 0 deletions modules/glob/glob.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package glob

import (
"errors"
"fmt"
"regexp"

"code.gitea.io/gitea/modules/util"
)

// Reference: https://github.com/gobwas/glob/blob/master/glob.go

type Glob interface {
Match(string) bool
}

type globCompiler struct {
nonSeparatorChars string
globPattern []rune
regexpPattern string
regexp *regexp.Regexp
pos int
}

// compileChars compiles character class patterns like [abc] or [!abc]
func (g *globCompiler) compileChars() (string, error) {
result := ""
if g.pos < len(g.globPattern) && g.globPattern[g.pos] == '!' {
g.pos++
result += "^"
}

for g.pos < len(g.globPattern) {
c := g.globPattern[g.pos]
g.pos++

if c == ']' {
return "[" + result + "]", nil
}

if c == '\\' {
if g.pos >= len(g.globPattern) {
return "", errors.New("unterminated character class escape")
}
result += "\\" + string(g.globPattern[g.pos])
g.pos++
} else {
result += string(c)
}
}

return "", errors.New("unterminated character class")
}

// compile compiles the glob pattern into a regular expression
func (g *globCompiler) compile(subPattern bool) (string, error) {
result := ""

for g.pos < len(g.globPattern) {
c := g.globPattern[g.pos]
g.pos++

if subPattern && c == '}' {
return "(" + result + ")", nil
}

switch c {
case '*':
if g.pos < len(g.globPattern) && g.globPattern[g.pos] == '*' {
g.pos++
result += ".*" // match any sequence of characters
} else {
result += g.nonSeparatorChars + "*" // match any sequence of non-separator characters
}
case '?':
result += g.nonSeparatorChars // match any single non-separator character
case '[':
chars, err := g.compileChars()
if err != nil {
return "", err
}
result += chars
case '{':
subResult, err := g.compile(true)
if err != nil {
return "", err
}
result += subResult
case ',':
if subPattern {
result += "|"
} else {
result += ","
}
case '\\':
if g.pos >= len(g.globPattern) {
return "", errors.New("no character to escape")
}
result += "\\" + string(g.globPattern[g.pos])
g.pos++
case '.', '+', '^', '$', '(', ')', '|':
result += "\\" + string(c) // escape regexp special characters
default:
result += string(c)
}
}

return result, nil
}

func newGlobCompiler(pattern string, separators ...rune) (Glob, error) {
g := &globCompiler{globPattern: []rune(pattern)}

// Escape separators for use in character class
escapedSeparators := regexp.QuoteMeta(string(separators))
if escapedSeparators != "" {
g.nonSeparatorChars = "[^" + escapedSeparators + "]"
} else {
g.nonSeparatorChars = "."
}

compiled, err := g.compile(false)
if err != nil {
return nil, err
}

g.regexpPattern = "^" + compiled + "$"

regex, err := regexp.Compile(g.regexpPattern)
if err != nil {
return nil, fmt.Errorf("failed to compile regexp: %w", err)
}

g.regexp = regex
return g, nil
}

func (g *globCompiler) Match(s string) bool {
return g.regexp.MatchString(s)
}

func Compile(pattern string, separators ...rune) (Glob, error) {
return newGlobCompiler(pattern, separators...)
}

func MustCompile(pattern string, separators ...rune) Glob {
g, err := Compile(pattern, separators...)
if err != nil {
panic(err)
}
return g
}

func IsSpecialByte(c byte) bool {
return c == '*' || c == '?' || c == '\\' || c == '[' || c == ']' || c == '{' || c == '}'
}

// QuoteMeta returns a string that quotes all glob pattern meta characters
// inside the argument text; For example, QuoteMeta(`{foo*}`) returns `\[foo\*\]`.
// Reference: https://github.com/gobwas/glob/blob/master/glob.go
func QuoteMeta(s string) string {
pos := 0
for pos < len(s) && !IsSpecialByte(s[pos]) {
pos++
}
if pos == len(s) {
return s
}
b := make([]byte, pos+2*(len(s)-pos))
copy(b, s[0:pos])
to := pos
for ; pos < len(s); pos++ {
if IsSpecialByte(s[pos]) {
b[to] = '\\'
to++
}
b[to] = s[pos]
to++
}
return util.UnsafeBytesToString(b[0:to])
}
Loading