mirror of
https://github.com/go-gitea/gitea.git
synced 2026-11-09 17:37:20 -06:00
refactor: lazy init global regexp variables (#40269)
Gitea binary is an all-in-one program. It doesn't make sense to compile dozens or even hundreds of regexps for git hook sub commands.
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/json"
|
||||
)
|
||||
@@ -31,7 +32,9 @@ type emoji struct {
|
||||
description string
|
||||
}
|
||||
|
||||
var slugRe = regexp.MustCompile(`[^a-z0-9]+`)
|
||||
var slugRe = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`[^a-z0-9]+`)
|
||||
})
|
||||
|
||||
func main() {
|
||||
if err := generate(); err != nil {
|
||||
@@ -108,7 +111,7 @@ func fetchEmojis(existingAliases map[string][]string) ([]*emoji, error) {
|
||||
for _, e := range emojis {
|
||||
e.Aliases = existingAliases[e.Emoji]
|
||||
if e.Aliases == nil {
|
||||
e.Aliases = []string{strings.Trim(slugRe.ReplaceAllString(strings.ReplaceAll(strings.ToLower(e.description), "’", ""), "_"), "_")}
|
||||
e.Aliases = []string{strings.Trim(slugRe().ReplaceAllString(strings.ReplaceAll(strings.ToLower(e.description), "’", ""), "_"), "_")}
|
||||
}
|
||||
delete(existingAliases, e.Emoji)
|
||||
for _, alias := range e.Aliases {
|
||||
|
||||
@@ -15,17 +15,24 @@ import (
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// regexp is based on go-license, excluding README and NOTICE
|
||||
// https://github.com/google/go-licenses/blob/master/licenses/find.go
|
||||
// also defined in vite.config.ts
|
||||
var licenseRe = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING).*$`)
|
||||
|
||||
// primaryLicenseRe matches exact primary license filenames without suffixes.
|
||||
// When a directory has both primary and variant files (e.g. LICENSE and
|
||||
// LICENSE.docs), only the primary files are kept.
|
||||
var primaryLicenseRe = regexp.MustCompile(`^(?i)(LICEN[SC]E|COPYING)$`)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
licenseRe *regexp.Regexp
|
||||
primaryLicenseRe *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// regexp is based on go-license, excluding README and NOTICE
|
||||
// https://github.com/google/go-licenses/blob/master/licenses/find.go
|
||||
// also defined in vite.config.ts
|
||||
ret.licenseRe = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING).*$`)
|
||||
// primaryLicenseRe matches exact primary license filenames without suffixes.
|
||||
// When a directory has both primary and variant files (e.g. LICENSE and
|
||||
// LICENSE.docs), only the primary files are kept.
|
||||
ret.primaryLicenseRe = regexp.MustCompile(`^(?i)(LICEN[SC]E|COPYING)$`)
|
||||
return ret
|
||||
})
|
||||
|
||||
// ignoredNames are LicenseEntry.Name values to exclude from the output.
|
||||
var ignoredNames = map[string]bool{
|
||||
@@ -144,7 +151,7 @@ func scanDirForLicenses(dir, modulePath, moduleRoot string) []LicenseEntry {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
if !licenseRe.MatchString(name) {
|
||||
if !globalVars().licenseRe.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
if excludedExt[strings.ToLower(filepath.Ext(name))] {
|
||||
@@ -181,7 +188,7 @@ func scanDirForLicenses(dir, modulePath, moduleRoot string) []LicenseEntry {
|
||||
var primary []LicenseEntry
|
||||
for _, e := range entries {
|
||||
fileName := e.Path[strings.LastIndex(e.Path, "/")+1:]
|
||||
if primaryLicenseRe.MatchString(fileName) {
|
||||
if globalVars().primaryLicenseRe.MatchString(fileName) {
|
||||
primary = append(primary, e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/json"
|
||||
|
||||
@@ -16,11 +17,19 @@ import (
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
)
|
||||
|
||||
// rxDeprecated matches "deprecated" as a word at the start of a description
|
||||
// or preceded by whitespace/punctuation that indicates a leading marker (e.g.
|
||||
// "Deprecated: true", "deprecated (use X instead)"). Rejects negated phrases
|
||||
// like "not deprecated" or "previously deprecated, now supported".
|
||||
var rxDeprecated = regexp.MustCompile(`(?i)(?:^|[\n.;])\s*deprecated\b`)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
rxDeprecated *regexp.Regexp
|
||||
rxSwaggerEnum *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// rxDeprecated matches "deprecated" as a word at the start of a description
|
||||
// or preceded by whitespace/punctuation that indicates a leading marker (e.g.
|
||||
// "Deprecated: true", "deprecated (use X instead)"). Rejects negated phrases
|
||||
// like "not deprecated" or "previously deprecated, now supported".
|
||||
ret.rxDeprecated = regexp.MustCompile(`(?i)(?:^|[\n.;])\s*deprecated\b`)
|
||||
ret.rxSwaggerEnum = regexp.MustCompile(`swagger:enum\s+(\w+)`)
|
||||
return ret
|
||||
})
|
||||
|
||||
// Convert parses a Swagger 2.0 spec and returns an OAS3 spec, applying
|
||||
// Gitea-specific post-processing: file-schema fixups, URI formats,
|
||||
@@ -158,7 +167,7 @@ func addDeprecatedFlags(doc *openapi3.T) {
|
||||
if propRef == nil || propRef.Value == nil || propRef.Ref != "" {
|
||||
continue
|
||||
}
|
||||
if rxDeprecated.MatchString(propRef.Value.Description) {
|
||||
if globalVars().rxDeprecated.MatchString(propRef.Value.Description) {
|
||||
propRef.Value.Deprecated = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -31,8 +30,6 @@ func EnumKey(values []any) string {
|
||||
return strings.Join(strs, "|")
|
||||
}
|
||||
|
||||
var rxSwaggerEnum = regexp.MustCompile(`swagger:enum\s+(\w+)`)
|
||||
|
||||
// ScanSwaggerEnumTypes walks .go files under each dir and returns a map from
|
||||
// a canonical value-set key (see EnumKey) to the Go type names declared with
|
||||
// // swagger:enum TypeName. Multiple type names per key are allowed (e.g.
|
||||
@@ -146,7 +143,7 @@ func registerEnumAnnotation(doc *ast.CommentGroup, specs []ast.Spec, enumTypes m
|
||||
if doc == nil {
|
||||
return nil
|
||||
}
|
||||
matches := rxSwaggerEnum.FindStringSubmatch(doc.Text())
|
||||
matches := globalVars().rxSwaggerEnum.FindStringSubmatch(doc.Text())
|
||||
if len(matches) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
project_model "gitea.dev/models/project"
|
||||
@@ -112,10 +113,15 @@ type Issue struct {
|
||||
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
var (
|
||||
issueTasksPat = regexp.MustCompile(`(^\s*[-*]\s\[[\sxX]\]\s.)|(\n\s*[-*]\s\[[\sxX]\]\s.)`)
|
||||
issueTasksDonePat = regexp.MustCompile(`(^\s*[-*]\s\[[xX]\]\s.)|(\n\s*[-*]\s\[[xX]\]\s.)`)
|
||||
)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
issueTasksPat *regexp.Regexp
|
||||
issueTasksDonePat *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.issueTasksPat = regexp.MustCompile(`(^\s*[-*]\s\[[\sxX]\]\s.)|(\n\s*[-*]\s\[[\sxX]\]\s.)`)
|
||||
ret.issueTasksDonePat = regexp.MustCompile(`(^\s*[-*]\s\[[xX]\]\s.)|(\n\s*[-*]\s\[[xX]\]\s.)`)
|
||||
return ret
|
||||
})
|
||||
|
||||
// IssueIndex represents the issue index table
|
||||
type IssueIndex db.ResourceIndex
|
||||
@@ -463,12 +469,12 @@ func (issue *Issue) IsPoster(uid int64) bool {
|
||||
|
||||
// GetTasks returns the amount of tasks in the issues content
|
||||
func (issue *Issue) GetTasks() int {
|
||||
return len(issueTasksPat.FindAllStringIndex(issue.Content, -1))
|
||||
return len(globalVars().issueTasksPat.FindAllStringIndex(issue.Content, -1))
|
||||
}
|
||||
|
||||
// GetTasksDone returns the amount of completed tasks in the issues content
|
||||
func (issue *Issue) GetTasksDone() int {
|
||||
return len(issueTasksDonePat.FindAllStringIndex(issue.Content, -1))
|
||||
return len(globalVars().issueTasksDonePat.FindAllStringIndex(issue.Content, -1))
|
||||
}
|
||||
|
||||
// GetLastEventTimestamp returns the last user visible event timestamp, either the creation of this issue or the close.
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/setting"
|
||||
@@ -36,10 +37,12 @@ const (
|
||||
)
|
||||
|
||||
// ColumnColorPattern is a regexp witch can validate ColumnColor
|
||||
var ColumnColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")
|
||||
var ColumnColorPattern = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile("^#[0-9a-fA-F]{6}$")
|
||||
})
|
||||
|
||||
func validateColumnColor(color string) error {
|
||||
if len(color) != 0 && !ColumnColorPattern.MatchString(color) {
|
||||
if len(color) != 0 && !ColumnColorPattern().MatchString(color) {
|
||||
return util.ErrorWrap(util.ErrUnprocessableContent, "invalid column color %q, expected a 6-digit hex string like #FF0000", color)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/container"
|
||||
@@ -22,7 +23,9 @@ func init() {
|
||||
db.RegisterModel(new(RepoTopic))
|
||||
}
|
||||
|
||||
var topicPattern = regexp.MustCompile(`^[a-z0-9][-.a-z0-9]*$`)
|
||||
var topicPattern = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`^[a-z0-9][-.a-z0-9]*$`)
|
||||
})
|
||||
|
||||
// Topic represents a topic of repositories
|
||||
type Topic struct {
|
||||
@@ -55,7 +58,7 @@ func (err ErrTopicNotExist) Unwrap() error {
|
||||
|
||||
// ValidateTopic checks a topic by length and match pattern rules
|
||||
func ValidateTopic(topic string) bool {
|
||||
return len(topic) <= 35 && topicPattern.MatchString(topic)
|
||||
return len(topic) <= 35 && topicPattern().MatchString(topic)
|
||||
}
|
||||
|
||||
// SanitizeAndValidateTopics sanitizes and checks an array or topics
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/actionslib/pkg/model"
|
||||
|
||||
@@ -75,7 +76,9 @@ func ParseWorkflowCallConfig(content []byte) (*model.WorkflowCall, error) {
|
||||
const SecretsInherit = "inherit"
|
||||
|
||||
// callerSecretValueRegexp matches the `${{ secrets.NAME }}` form expected for each value in a caller's `secrets:` mapping.
|
||||
var callerSecretValueRegexp = regexp.MustCompile(`^\s*\$\{\{\s*secrets\.([A-Za-z_][A-Za-z0-9_]*)\s*\}\}\s*$`)
|
||||
var callerSecretValueRegexp = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`^\s*\$\{\{\s*secrets\.([A-Za-z_][A-Za-z0-9_]*)\s*\}\}\s*$`)
|
||||
})
|
||||
|
||||
// ParseCallerSecrets decodes a caller's "secrets:" YAML node into one of two forms:
|
||||
// - inherit == true: the caller wrote `secrets: inherit`; mapping is nil
|
||||
@@ -101,7 +104,7 @@ func ParseCallerSecrets(node yaml.Node) (inherit bool, mapping map[string]string
|
||||
if err := v.Decode(&sv); err != nil {
|
||||
return false, nil, fmt.Errorf("decode secret %q: %w", k.Value, err)
|
||||
}
|
||||
matches := callerSecretValueRegexp.FindStringSubmatch(sv)
|
||||
matches := callerSecretValueRegexp().FindStringSubmatch(sv)
|
||||
if len(matches) != 2 {
|
||||
return false, nil, fmt.Errorf("caller secret %q value must be of the form ${{ secrets.NAME }}", k.Value)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/markup"
|
||||
"gitea.dev/modules/translation"
|
||||
@@ -71,15 +72,24 @@ func determineDelimiter(ctx *markup.RenderContext, data []byte) rune {
|
||||
return delimiter
|
||||
}
|
||||
|
||||
// quoteRegexp follows the RFC-4180 CSV standard for when double-quotes are used to enclose fields, then a double-quote appearing inside a
|
||||
// field must be escaped by preceding it with another double quote. https://www.ietf.org/rfc/rfc4180.txt
|
||||
// This finds all quoted strings that have escaped quotes.
|
||||
var quoteRegexp = regexp.MustCompile(`"[^"]*"`)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
quoteRegexp *regexp.Regexp
|
||||
beforeAfterQuotes *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// quoteRegexp follows the RFC-4180 CSV standard for when double-quotes are used to enclose fields, then a double-quote appearing inside a
|
||||
// field must be escaped by preceding it with another double quote. https://www.ietf.org/rfc/rfc4180.txt
|
||||
// This finds all quoted strings that have escaped quotes.
|
||||
ret.quoteRegexp = regexp.MustCompile(`"[^"]*"`)
|
||||
// Looks for possible delimiters right before or after (with spaces after the former) double quotes with closing quotes
|
||||
ret.beforeAfterQuotes = regexp.MustCompile(`([,@\t;|]{0,1}) *(?:"[^"]*")+([,@\t;|]{0,1})`)
|
||||
return ret
|
||||
})
|
||||
|
||||
// removeQuotedStrings uses the quoteRegexp to remove all quoted strings so that we can reliably have each row on one line
|
||||
// (quoted strings often have new lines within the string)
|
||||
func removeQuotedString(text string) string {
|
||||
return quoteRegexp.ReplaceAllLiteralString(text, "")
|
||||
return globalVars().quoteRegexp.ReplaceAllLiteralString(text, "")
|
||||
}
|
||||
|
||||
// guessDelimiter takes up to maxLines of the CSV text, iterates through the possible delimiters, and sees if the CSV Reader reads it without throwing any errors.
|
||||
@@ -133,13 +143,10 @@ func FormatError(err error, locale translation.Locale) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Looks for possible delimiters right before or after (with spaces after the former) double quotes with closing quotes
|
||||
var beforeAfterQuotes = regexp.MustCompile(`([,@\t;|]{0,1}) *(?:"[^"]*")+([,@\t;|]{0,1})`)
|
||||
|
||||
// guessFromBeforeAfterQuotes guesses the limiter by finding a double quote that has a valid delimiter before it and a closing quote,
|
||||
// or a double quote with a closing quote and a valid delimiter after it
|
||||
func guessFromBeforeAfterQuotes(data []byte) rune {
|
||||
rs := beforeAfterQuotes.FindStringSubmatch(string(data)) // returns first match, or nil if none
|
||||
rs := globalVars().beforeAfterQuotes.FindStringSubmatch(string(data)) // returns first match, or nil if none
|
||||
if rs != nil {
|
||||
if rs[1] != "" {
|
||||
return rune(rs[1][0]) // delimiter found left of quoted string
|
||||
|
||||
@@ -74,20 +74,43 @@ func (c *CommitMessage) MessageTrailer() CommitMessageTrailerValues {
|
||||
return c.trailerValues
|
||||
}
|
||||
|
||||
var commitMessageTrailerSplit = sync.OnceValue(func() *regexp.Regexp {
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
commitMessageTrailerSplit *regexp.Regexp
|
||||
hunkRegex *regexp.Regexp
|
||||
refNamePatternInvalid *regexp.Regexp
|
||||
patchCommits *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// ref: https://git-scm.com/docs/git-interpret-trailers
|
||||
// TODO: the regexp is not able to perfectly parse the all kinds of trailers
|
||||
// It was just copied from legacy code, it is not exactly the same as how Git parses the trailer and not quite right in some cases.
|
||||
// For the key characters: it follows RFC 822 field name syntax (or RFC 2822/RFC 5322): printable ASCII characters between 33 and 126 except the colon (:),
|
||||
// but maybe we don't want to make it that complicated, so here we only support some common "symbol-like" characters.
|
||||
return regexp.MustCompile(`(?s)^(?P<content>.*?)(?P<sep>^|^\n|^-{3,}\n+|\n+-{3,}\n+|\n{2,})(?P<trailer>(?:[A-Za-z0-9][-\w]*:[^\n]*(\n\s+[^\n]*)*\n?)*\n*)$`)
|
||||
ret.commitMessageTrailerSplit = regexp.MustCompile(`(?s)^(?P<content>.*?)(?P<sep>^|^\n|^-{3,}\n+|\n+-{3,}\n+|\n{2,})(?P<trailer>(?:[A-Za-z0-9][-\w]*:[^\n]*(\n\s+[^\n]*)*\n?)*\n*)$`)
|
||||
// Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9]
|
||||
ret.hunkRegex = regexp.MustCompile(`^@@ -(?P<beginOld>[0-9]+)(,(?P<endOld>[0-9]+))? \+(?P<beginNew>[0-9]+)(,(?P<endNew>[0-9]+))? @@`)
|
||||
// refNamePatternInvalid is regular expression with unallowed characters in git reference name
|
||||
// They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere.
|
||||
// They cannot have question-mark ?, asterisk *, or open bracket [ anywhere
|
||||
ret.refNamePatternInvalid = regexp.MustCompile(
|
||||
`[\000-\037\177 \\~^:?*[]|` + // No absolutely invalid characters
|
||||
`(?:^[/.])|` + // Not HasPrefix("/") or "."
|
||||
`(?:/\.)|` + // no "/."
|
||||
`(?:\.lock$)|(?:\.lock/)|` + // No ".lock/"" or ".lock" at the end
|
||||
`(?:\.\.)|` + // no ".." anywhere
|
||||
`(?://)|` + // no "//" anywhere
|
||||
`(?:@{)|` + // no "@{"
|
||||
`(?:[/.]$)|` + // no terminal '/' or '.'
|
||||
`(?:^@$)`) // Not "@"
|
||||
ret.patchCommits = regexp.MustCompile(`^From\s(\w+)\s`)
|
||||
return ret
|
||||
})
|
||||
|
||||
// CommitMessageSplitTrailer tries to split the message by the trailer separator
|
||||
// content + sep + trailer will reconstruct the original message
|
||||
func CommitMessageSplitTrailer(s string) (content, sep, trailer string) {
|
||||
s = util.NormalizeStringEOL(s)
|
||||
re := commitMessageTrailerSplit()
|
||||
re := globalVars().commitMessageTrailerSplit
|
||||
v := re.FindStringSubmatch(s)
|
||||
if v == nil {
|
||||
return s, "", ""
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -132,9 +131,6 @@ func ParseDiffHunkString(diffHunk string) (leftLine, leftHunk, rightLine, rightH
|
||||
return leftLine, leftHunk, rightLine, rightHunk
|
||||
}
|
||||
|
||||
// Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9]
|
||||
var hunkRegex = regexp.MustCompile(`^@@ -(?P<beginOld>[0-9]+)(,(?P<endOld>[0-9]+))? \+(?P<beginNew>[0-9]+)(,(?P<endNew>[0-9]+))? @@`)
|
||||
|
||||
const cmdDiffHead = "diff --git "
|
||||
|
||||
func isHeader(lof string, inHunk bool) bool {
|
||||
@@ -192,9 +188,9 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
|
||||
break
|
||||
}
|
||||
// A map with named groups of our regex to recognize them later more easily
|
||||
submatches := hunkRegex.FindStringSubmatch(lof)
|
||||
submatches := globalVars().hunkRegex.FindStringSubmatch(lof)
|
||||
groups := make(map[string]string)
|
||||
for i, name := range hunkRegex.SubexpNames() {
|
||||
for i, name := range globalVars().hunkRegex.SubexpNames() {
|
||||
if i != 0 && name != "" {
|
||||
groups[name] = submatches[i]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -22,27 +21,13 @@ const (
|
||||
pullSuffix = "/head"
|
||||
)
|
||||
|
||||
// refNamePatternInvalid is regular expression with unallowed characters in git reference name
|
||||
// They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere.
|
||||
// They cannot have question-mark ?, asterisk *, or open bracket [ anywhere
|
||||
var refNamePatternInvalid = regexp.MustCompile(
|
||||
`[\000-\037\177 \\~^:?*[]|` + // No absolutely invalid characters
|
||||
`(?:^[/.])|` + // Not HasPrefix("/") or "."
|
||||
`(?:/\.)|` + // no "/."
|
||||
`(?:\.lock$)|(?:\.lock/)|` + // No ".lock/"" or ".lock" at the end
|
||||
`(?:\.\.)|` + // no ".." anywhere
|
||||
`(?://)|` + // no "//" anywhere
|
||||
`(?:@{)|` + // no "@{"
|
||||
`(?:[/.]$)|` + // no terminal '/' or '.'
|
||||
`(?:^@$)`) // Not "@"
|
||||
|
||||
// IsValidRefPattern ensures that the provided string could be a valid reference
|
||||
func IsValidRefPattern(name string) bool {
|
||||
return !refNamePatternInvalid.MatchString(name)
|
||||
return !globalVars().refNamePatternInvalid.MatchString(name)
|
||||
}
|
||||
|
||||
func SanitizeRefPattern(name string) string {
|
||||
return refNamePatternInvalid.ReplaceAllString(name, "_")
|
||||
return globalVars().refNamePatternInvalid.ReplaceAllString(name, "_")
|
||||
}
|
||||
|
||||
// Reference represents a Git ref.
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modules/git/gitcmd"
|
||||
@@ -51,8 +50,6 @@ func (repo *Repository) GetDiffNumChangedFiles(ctx context.Context, base, head s
|
||||
return w.numLines, nil
|
||||
}
|
||||
|
||||
var patchCommits = regexp.MustCompile(`^From\s(\w+)\s`)
|
||||
|
||||
// GetDiff generates and returns patch data between given revisions, optimized for human readability
|
||||
func (repo *Repository) GetDiff(ctx context.Context, compareArg string, w io.Writer) error {
|
||||
return gitcmd.NewCommand("diff", "-p").AddDynamicArguments(compareArg).
|
||||
@@ -119,7 +116,7 @@ func (repo *Repository) ReadPatchCommit(prID int64) (commitSHA string, err error
|
||||
scanner := bufio.NewScanner(loadPatch)
|
||||
scanner.Scan()
|
||||
// Parse the Patch stats, sometimes Migration returns a 404 for the patch file
|
||||
commitSHAGroups := patchCommits.FindStringSubmatch(scanner.Text())
|
||||
commitSHAGroups := globalVars().patchCommits.FindStringSubmatch(scanner.Text())
|
||||
if len(commitSHAGroups) != 0 {
|
||||
commitSHA = commitSHAGroups[1]
|
||||
} else {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/packages"
|
||||
"gitea.dev/modules/util"
|
||||
@@ -38,6 +39,18 @@ const (
|
||||
maxPKGINFOSize = 1024 * 1024
|
||||
)
|
||||
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
namePattern *regexp.Regexp
|
||||
versionPattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// https://man.archlinux.org/man/PKGBUILD.5
|
||||
ret.namePattern = regexp.MustCompile(`\A[a-zA-Z0-9@._+-]+\z`)
|
||||
// (epoch:pkgver-pkgrel)
|
||||
ret.versionPattern = regexp.MustCompile(`\A(?:\d:)?[\w.+~]+(?:-[-\w.+~]+)?\z`)
|
||||
return ret
|
||||
})
|
||||
|
||||
var (
|
||||
ErrMissingPKGINFOFile = util.NewInvalidArgumentErrorf(".PKGINFO file is missing")
|
||||
ErrUnsupportedFormat = util.NewInvalidArgumentErrorf("unsupported package container format")
|
||||
@@ -45,11 +58,6 @@ var (
|
||||
ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
|
||||
ErrInvalidArchitecture = util.NewInvalidArgumentErrorf("package architecture is invalid")
|
||||
|
||||
// https://man.archlinux.org/man/PKGBUILD.5
|
||||
namePattern = regexp.MustCompile(`\A[a-zA-Z0-9@._+-]+\z`)
|
||||
// (epoch:pkgver-pkgrel)
|
||||
versionPattern = regexp.MustCompile(`\A(?:\d:)?[\w.+~]+(?:-[-\w.+~]+)?\z`)
|
||||
|
||||
// caps on the accumulated package file list (vars so tests can lower them); far above
|
||||
// any legitimate package, but low enough to stop metadata amplification
|
||||
maxFileEntries = 100000
|
||||
@@ -251,10 +259,10 @@ func ParsePackageInfo(r io.Reader) (*Package, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !namePattern.MatchString(p.Name) {
|
||||
if !globalVars().namePattern.MatchString(p.Name) {
|
||||
return nil, ErrInvalidName
|
||||
}
|
||||
if !versionPattern.MatchString(p.Version) {
|
||||
if !globalVars().versionPattern.MatchString(p.Version) {
|
||||
return nil, ErrInvalidVersion
|
||||
}
|
||||
if p.FileMetadata.Architecture == "" {
|
||||
|
||||
@@ -127,11 +127,11 @@ func TestParsePackageInfo(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Regexp", func(t *testing.T) {
|
||||
assert.Regexp(t, versionPattern, "1.2_3~4+5")
|
||||
assert.Regexp(t, versionPattern, "1:2_3~4+5")
|
||||
assert.NotRegexp(t, versionPattern, "a:1.0.0-1")
|
||||
assert.NotRegexp(t, versionPattern, "0.0.1/1-1")
|
||||
assert.NotRegexp(t, versionPattern, "1.0.0 -1")
|
||||
assert.Regexp(t, globalVars().versionPattern, "1.2_3~4+5")
|
||||
assert.Regexp(t, globalVars().versionPattern, "1:2_3~4+5")
|
||||
assert.NotRegexp(t, globalVars().versionPattern, "a:1.0.0-1")
|
||||
assert.NotRegexp(t, globalVars().versionPattern, "0.0.1/1-1")
|
||||
assert.NotRegexp(t, globalVars().versionPattern, "1.0.0 -1")
|
||||
})
|
||||
|
||||
t.Run("InvalidVersion", func(t *testing.T) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"regexp"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/packages"
|
||||
@@ -60,7 +61,9 @@ type Dependency struct {
|
||||
Package *string `json:"package"`
|
||||
}
|
||||
|
||||
var nameMatch = regexp.MustCompile(`\A[a-zA-Z][a-zA-Z0-9-_]{0,63}\z`)
|
||||
var nameMatch = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`\A[a-zA-Z][a-zA-Z0-9-_]{0,63}\z`)
|
||||
})
|
||||
|
||||
// ParsePackage reads the metadata and content of a package
|
||||
func ParsePackage(r io.Reader) (*Package, error) {
|
||||
@@ -120,7 +123,7 @@ func parsePackage(r io.Reader) (*Package, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !nameMatch.MatchString(meta.Name) {
|
||||
if !nameMatch().MatchString(meta.Name) {
|
||||
return nil, ErrInvalidName
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/packages"
|
||||
@@ -21,13 +22,20 @@ const (
|
||||
SettingPublicPem = "chef.public_pem"
|
||||
)
|
||||
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
namePattern *regexp.Regexp
|
||||
versionPattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.namePattern = regexp.MustCompile(`\A\S+\z`)
|
||||
ret.versionPattern = regexp.MustCompile(`\A\d+\.\d+(?:\.\d+)?\z`)
|
||||
return ret
|
||||
})
|
||||
|
||||
var (
|
||||
ErrMissingMetadataFile = util.NewInvalidArgumentErrorf("metadata.json file is missing")
|
||||
ErrInvalidName = util.NewInvalidArgumentErrorf("package name is invalid")
|
||||
ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
|
||||
|
||||
namePattern = regexp.MustCompile(`\A\S+\z`)
|
||||
versionPattern = regexp.MustCompile(`\A\d+\.\d+(?:\.\d+)?\z`)
|
||||
)
|
||||
|
||||
// Package represents a Chef package
|
||||
@@ -108,11 +116,11 @@ func ParseChefMetadata(r io.Reader) (*Package, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !namePattern.MatchString(cm.Name) {
|
||||
if !globalVars().namePattern.MatchString(cm.Name) {
|
||||
return nil, ErrInvalidName
|
||||
}
|
||||
|
||||
if !versionPattern.MatchString(cm.Version) {
|
||||
if !globalVars().versionPattern.MatchString(cm.Version) {
|
||||
return nil, ErrInvalidVersion
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/charset"
|
||||
"gitea.dev/modules/json"
|
||||
@@ -117,7 +118,9 @@ type Author struct {
|
||||
Homepage string `json:"homepage,omitempty"`
|
||||
}
|
||||
|
||||
var nameMatch = regexp.MustCompile(`\A[a-z0-9]([_\.-]?[a-z0-9]+)*/[a-z0-9](([_\.]?|-{0,2})[a-z0-9]+)*\z`)
|
||||
var nameMatch = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`\A[a-z0-9]([_\.-]?[a-z0-9]+)*/[a-z0-9](([_\.]?|-{0,2})[a-z0-9]+)*\z`)
|
||||
})
|
||||
|
||||
type ReadSeekAt interface {
|
||||
io.Reader
|
||||
@@ -237,7 +240,7 @@ func ParsePackage(r ReadSeekAt, optVersion ...string) (*PackageInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !nameMatch.MatchString(cj.Name) {
|
||||
if !nameMatch().MatchString(cj.Name) {
|
||||
return nil, ErrInvalidName
|
||||
}
|
||||
|
||||
|
||||
@@ -4,20 +4,36 @@
|
||||
package conan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
patternAuthor = compilePattern("author")
|
||||
patternHomepage = compilePattern("homepage")
|
||||
patternURL = compilePattern("url")
|
||||
patternLicense = compilePattern("license")
|
||||
patternDescription = compilePattern("description")
|
||||
patternTopics = regexp.MustCompile(`(?im)^\s*topics\s*=\s*\((.+)\)`)
|
||||
patternTopicList = regexp.MustCompile(`\s*['"](.+?)['"]\s*,?`)
|
||||
)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
patternAuthor *regexp.Regexp
|
||||
patternHomepage *regexp.Regexp
|
||||
patternURL *regexp.Regexp
|
||||
patternLicense *regexp.Regexp
|
||||
patternDescription *regexp.Regexp
|
||||
patternTopics *regexp.Regexp
|
||||
patternTopicList *regexp.Regexp
|
||||
namePattern *regexp.Regexp
|
||||
revisionPattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.patternAuthor = compilePattern("author")
|
||||
ret.patternHomepage = compilePattern("homepage")
|
||||
ret.patternURL = compilePattern("url")
|
||||
ret.patternLicense = compilePattern("license")
|
||||
ret.patternDescription = compilePattern("description")
|
||||
ret.patternTopics = regexp.MustCompile(`(?im)^\s*topics\s*=\s*\((.+)\)`)
|
||||
ret.patternTopicList = regexp.MustCompile(`\s*['"](.+?)['"]\s*,?`)
|
||||
ret.namePattern = regexp.MustCompile(fmt.Sprintf(`^[a-zA-Z0-9_][a-zA-Z0-9_\+\.-]{%d,%d}$`, minChars-1, maxChars-1))
|
||||
ret.revisionPattern = regexp.MustCompile(fmt.Sprintf(`^[a-zA-Z0-9]{1,%d}$`, maxChars))
|
||||
return ret
|
||||
})
|
||||
|
||||
func compilePattern(name string) *regexp.Regexp {
|
||||
return regexp.MustCompile(`(?im)^\s*` + name + `\s*=\s*['"\(](.+)['"\)]`)
|
||||
@@ -31,29 +47,29 @@ func ParseConanfile(r io.Reader) (*Metadata, error) {
|
||||
|
||||
metadata := &Metadata{}
|
||||
|
||||
m := patternAuthor.FindSubmatch(buf)
|
||||
m := globalVars().patternAuthor.FindSubmatch(buf)
|
||||
if len(m) > 1 && len(m[1]) > 0 {
|
||||
metadata.Author = string(m[1])
|
||||
}
|
||||
m = patternHomepage.FindSubmatch(buf)
|
||||
m = globalVars().patternHomepage.FindSubmatch(buf)
|
||||
if len(m) > 1 && len(m[1]) > 0 {
|
||||
metadata.ProjectURL = string(m[1])
|
||||
}
|
||||
m = patternURL.FindSubmatch(buf)
|
||||
m = globalVars().patternURL.FindSubmatch(buf)
|
||||
if len(m) > 1 && len(m[1]) > 0 {
|
||||
metadata.RepositoryURL = string(m[1])
|
||||
}
|
||||
m = patternLicense.FindSubmatch(buf)
|
||||
m = globalVars().patternLicense.FindSubmatch(buf)
|
||||
if len(m) > 1 && len(m[1]) > 0 {
|
||||
metadata.License = strings.ReplaceAll(strings.ReplaceAll(string(m[1]), "'", ""), "\"", "")
|
||||
}
|
||||
m = patternDescription.FindSubmatch(buf)
|
||||
m = globalVars().patternDescription.FindSubmatch(buf)
|
||||
if len(m) > 1 && len(m[1]) > 0 {
|
||||
metadata.Description = string(m[1])
|
||||
}
|
||||
m = patternTopics.FindSubmatch(buf)
|
||||
m = globalVars().patternTopics.FindSubmatch(buf)
|
||||
if len(m) > 1 && len(m[1]) > 0 {
|
||||
m2 := patternTopicList.FindAllSubmatch(m[1], -1)
|
||||
m2 := globalVars().patternTopicList.FindAllSubmatch(m[1], -1)
|
||||
if len(m2) > 0 {
|
||||
metadata.Keywords = make([]string, 0, len(m2))
|
||||
for _, g := range m2 {
|
||||
|
||||
@@ -5,7 +5,6 @@ package conan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modules/log"
|
||||
@@ -21,12 +20,7 @@ const (
|
||||
DefaultRevision = "0"
|
||||
)
|
||||
|
||||
var (
|
||||
namePattern = regexp.MustCompile(fmt.Sprintf(`^[a-zA-Z0-9_][a-zA-Z0-9_\+\.-]{%d,%d}$`, minChars-1, maxChars-1))
|
||||
revisionPattern = regexp.MustCompile(fmt.Sprintf(`^[a-zA-Z0-9]{1,%d}$`, maxChars))
|
||||
|
||||
ErrValidation = util.NewInvalidArgumentErrorf("could not validate one or more reference fields")
|
||||
)
|
||||
var ErrValidation = util.NewInvalidArgumentErrorf("could not validate one or more reference fields")
|
||||
|
||||
// RecipeReference represents a recipe <Name>/<Version>@<User>/<Channel>#<Revision>
|
||||
type RecipeReference struct {
|
||||
@@ -51,7 +45,7 @@ func NewRecipeReference(name, version, user, channel, revision string) (*RecipeR
|
||||
return nil, ErrValidation
|
||||
}
|
||||
|
||||
if !namePattern.MatchString(name) {
|
||||
if !globalVars().namePattern.MatchString(name) {
|
||||
return nil, ErrValidation
|
||||
}
|
||||
|
||||
@@ -59,13 +53,13 @@ func NewRecipeReference(name, version, user, channel, revision string) (*RecipeR
|
||||
if v == "" {
|
||||
return nil, ErrValidation
|
||||
}
|
||||
if user != "" && !namePattern.MatchString(user) {
|
||||
if user != "" && !globalVars().namePattern.MatchString(user) {
|
||||
return nil, ErrValidation
|
||||
}
|
||||
if channel != "" && !namePattern.MatchString(channel) {
|
||||
if channel != "" && !globalVars().namePattern.MatchString(channel) {
|
||||
return nil, ErrValidation
|
||||
}
|
||||
if revision != "" && !revisionPattern.MatchString(revision) {
|
||||
if revision != "" && !globalVars().revisionPattern.MatchString(revision) {
|
||||
return nil, ErrValidation
|
||||
}
|
||||
|
||||
@@ -124,10 +118,10 @@ func NewPackageReference(recipe *RecipeReference, reference, revision string) (*
|
||||
if recipe == nil {
|
||||
return nil, ErrValidation
|
||||
}
|
||||
if reference == "" || !revisionPattern.MatchString(reference) {
|
||||
if reference == "" || !globalVars().revisionPattern.MatchString(reference) {
|
||||
return nil, ErrValidation
|
||||
}
|
||||
if revision != "" && !revisionPattern.MatchString(revision) {
|
||||
if revision != "" && !globalVars().revisionPattern.MatchString(revision) {
|
||||
return nil, ErrValidation
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/base"
|
||||
"gitea.dev/modules/packages"
|
||||
@@ -34,12 +35,19 @@ var (
|
||||
ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
|
||||
)
|
||||
|
||||
var (
|
||||
fieldPattern = regexp.MustCompile(`\A\S+:`)
|
||||
namePattern = regexp.MustCompile(`\A[a-zA-Z][a-zA-Z0-9\.]*[a-zA-Z0-9]\z`)
|
||||
versionPattern = regexp.MustCompile(`\A[0-9]+(?:[.\-][0-9]+)+\z`)
|
||||
authorReplacePattern = regexp.MustCompile(`[\[\(].+?[\]\)]`)
|
||||
)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
fieldPattern *regexp.Regexp
|
||||
namePattern *regexp.Regexp
|
||||
versionPattern *regexp.Regexp
|
||||
authorReplacePattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.fieldPattern = regexp.MustCompile(`\A\S+:`)
|
||||
ret.namePattern = regexp.MustCompile(`\A[a-zA-Z][a-zA-Z0-9\.]*[a-zA-Z0-9]\z`)
|
||||
ret.versionPattern = regexp.MustCompile(`\A[0-9]+(?:[.\-][0-9]+)+\z`)
|
||||
ret.authorReplacePattern = regexp.MustCompile(`[\[\(].+?[\]\)]`)
|
||||
return ret
|
||||
})
|
||||
|
||||
// Package represents a CRAN package
|
||||
type Package struct {
|
||||
@@ -162,7 +170,7 @@ func ParseDescription(r io.Reader) (*Package, error) {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if !fieldPattern.MatchString(line) {
|
||||
if !globalVars().fieldPattern.MatchString(line) {
|
||||
b.WriteRune(' ')
|
||||
b.WriteString(line)
|
||||
continue
|
||||
@@ -202,12 +210,12 @@ func setField(p *Package, data string) error {
|
||||
|
||||
switch name {
|
||||
case "Package":
|
||||
if !namePattern.MatchString(value) {
|
||||
if !globalVars().namePattern.MatchString(value) {
|
||||
return ErrInvalidName
|
||||
}
|
||||
p.Name = value
|
||||
case "Version":
|
||||
if !versionPattern.MatchString(value) {
|
||||
if !globalVars().versionPattern.MatchString(value) {
|
||||
return ErrInvalidVersion
|
||||
}
|
||||
p.Version = value
|
||||
@@ -220,7 +228,7 @@ func setField(p *Package, data string) error {
|
||||
case "License":
|
||||
p.Metadata.License = value
|
||||
case "Author":
|
||||
p.Metadata.Authors = splitAndTrim(authorReplacePattern.ReplaceAllString(value, ""))
|
||||
p.Metadata.Authors = splitAndTrim(globalVars().authorReplacePattern.ReplaceAllString(value, ""))
|
||||
case "Depends":
|
||||
p.Metadata.Depends = splitAndTrim(value)
|
||||
case "Imports":
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/modules/validation"
|
||||
@@ -42,7 +43,9 @@ const (
|
||||
PropertySymbolID = "nuget.symbol.id"
|
||||
)
|
||||
|
||||
var idmatch = regexp.MustCompile(`\A\w+(?:[.-]\w+)*\z`)
|
||||
var idmatch = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`\A\w+(?:[.-]\w+)*\z`)
|
||||
})
|
||||
|
||||
const maxNuspecFileSize = 3 * 1024 * 1024
|
||||
|
||||
@@ -171,7 +174,7 @@ func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !idmatch.MatchString(p.Metadata.ID) {
|
||||
if !idmatch().MatchString(p.Metadata.ID) {
|
||||
return nil, ErrNuspecInvalidID
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/modules/validation"
|
||||
@@ -24,7 +25,9 @@ var (
|
||||
ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
|
||||
)
|
||||
|
||||
var namePattern = regexp.MustCompile(`\A[a-zA-Z_][a-zA-Z0-9_]*\z`)
|
||||
var namePattern = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`\A[a-zA-Z_][a-zA-Z0-9_]*\z`)
|
||||
})
|
||||
|
||||
// https://github.com/dart-lang/pub-dev/blob/4d582302a8d10152a5cd6129f65bf4f4dbca239d/pkg/pub_package_reader/lib/pub_package_reader.dart#L143
|
||||
const maxPubspecFileSize = 128 * 1024
|
||||
@@ -118,7 +121,7 @@ func ParsePubspecMetadata(r io.Reader) (*Package, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !namePattern.MatchString(p.Name) {
|
||||
if !namePattern().MatchString(p.Name) {
|
||||
return nil, ErrInvalidName
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/packages"
|
||||
@@ -19,14 +20,21 @@ import (
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
manifestPattern *regexp.Regexp
|
||||
toolsVersionPattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.manifestPattern = regexp.MustCompile(`\APackage(?:@swift-(\d+(?:\.\d+)?(?:\.\d+)?))?\.swift\z`)
|
||||
ret.toolsVersionPattern = regexp.MustCompile(`\A// swift-tools-version:(\d+(?:\.\d+)?(?:\.\d+)?)`)
|
||||
return ret
|
||||
})
|
||||
|
||||
var (
|
||||
ErrMissingManifestFile = util.NewInvalidArgumentErrorf("Package.swift file is missing")
|
||||
ErrManifestFileTooLarge = util.NewInvalidArgumentErrorf("Package.swift file is too large")
|
||||
ErrManifestFilesTooLarge = util.NewInvalidArgumentErrorf("Package.swift files are too large")
|
||||
ErrInvalidManifestVersion = util.NewInvalidArgumentErrorf("manifest version is invalid")
|
||||
|
||||
manifestPattern = regexp.MustCompile(`\APackage(?:@swift-(\d+(?:\.\d+)?(?:\.\d+)?))?\.swift\z`)
|
||||
toolsVersionPattern = regexp.MustCompile(`\A// swift-tools-version:(\d+(?:\.\d+)?(?:\.\d+)?)`)
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -133,7 +141,7 @@ func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) {
|
||||
var manifestFiles []*zip.File
|
||||
manifestDir, manifestDepth := "", 0
|
||||
for _, file := range zr.File {
|
||||
if strings.HasSuffix(file.Name, "/") || !manifestPattern.MatchString(path.Base(file.Name)) {
|
||||
if strings.HasSuffix(file.Name, "/") || !globalVars().manifestPattern.MatchString(path.Base(file.Name)) {
|
||||
continue
|
||||
}
|
||||
dir, depth := path.Dir(file.Name), strings.Count(file.Name, "/")
|
||||
@@ -156,7 +164,7 @@ func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) {
|
||||
}
|
||||
|
||||
for _, file := range manifestFiles {
|
||||
manifestMatch := manifestPattern.FindStringSubmatch(path.Base(file.Name))
|
||||
manifestMatch := globalVars().manifestPattern.FindStringSubmatch(path.Base(file.Name))
|
||||
|
||||
if file.UncompressedSize64 > maxManifestFileSize {
|
||||
return nil, ErrManifestFileTooLarge
|
||||
@@ -190,7 +198,7 @@ func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) {
|
||||
Content: string(content),
|
||||
}
|
||||
|
||||
toolsMatch := toolsVersionPattern.FindStringSubmatch(manifest.Content)
|
||||
toolsMatch := globalVars().toolsVersionPattern.FindStringSubmatch(manifest.Content)
|
||||
if len(toolsMatch) == 2 {
|
||||
v, err := version.NewSemver(toolsMatch[1])
|
||||
if err != nil {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
packages_module "gitea.dev/modules/packages"
|
||||
|
||||
@@ -31,20 +32,25 @@ var (
|
||||
|
||||
const maxReadmeSize = 1024 * 1024 // larger READMEs are not stored, they would be rendered on every page view
|
||||
|
||||
// same rules as Terraform's module source address parser (github.com/hashicorp/terraform-registry-address)
|
||||
var (
|
||||
nameRe = regexp.MustCompile(`\A[0-9A-Za-z](?:[0-9A-Za-z_-]{0,62}[0-9A-Za-z])?\z`)
|
||||
systemRe = regexp.MustCompile(`\A[0-9a-z]{1,64}\z`)
|
||||
)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
nameRe *regexp.Regexp
|
||||
systemRe *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// same rules as Terraform's module source address parser (github.com/hashicorp/terraform-registry-address)
|
||||
ret.nameRe = regexp.MustCompile(`\A[0-9A-Za-z](?:[0-9A-Za-z_-]{0,62}[0-9A-Za-z])?\z`)
|
||||
ret.systemRe = regexp.MustCompile(`\A[0-9a-z]{1,64}\z`)
|
||||
return ret
|
||||
})
|
||||
|
||||
func ValidateAddress(namespace, name, provider string) error {
|
||||
if !nameRe.MatchString(namespace) {
|
||||
if !globalVars().nameRe.MatchString(namespace) {
|
||||
return ErrInvalidNamespace
|
||||
}
|
||||
if !nameRe.MatchString(name) {
|
||||
if !globalVars().nameRe.MatchString(name) {
|
||||
return ErrInvalidName
|
||||
}
|
||||
if !systemRe.MatchString(provider) {
|
||||
if !globalVars().systemRe.MatchString(provider) {
|
||||
return ErrInvalidProvider
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -20,10 +20,19 @@ import (
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
var (
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
validNamePattern *regexp.Regexp
|
||||
mentionPattern *regexp.Regexp
|
||||
issueNumericPattern *regexp.Regexp
|
||||
issueAlphanumericPattern *regexp.Regexp
|
||||
crossReferenceIssueNumericPattern *regexp.Regexp
|
||||
crossReferenceCommitPattern *regexp.Regexp
|
||||
timeLogPattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// validNamePattern performs only the most basic validation for user or repository names
|
||||
// Repository name should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.
|
||||
validNamePattern = regexp.MustCompile(`^[a-z0-9_.-]+$`)
|
||||
ret.validNamePattern = regexp.MustCompile(`^[a-z0-9_.-]+$`)
|
||||
|
||||
// NOTE: All below regex matching do not perform any extra validation.
|
||||
// Thus a link is produced even if the linked entity does not exist.
|
||||
@@ -31,22 +40,25 @@ var (
|
||||
// TODO: fix invalid linking issue
|
||||
|
||||
// mentionPattern matches all mentions in the form of "@user" or "@org/team"
|
||||
mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[-\w][-.\w]*?|@[-\w][-.\w]*?/[-\w][-.\w]*?)(?:\s|$|[:,;.?!](\s|$)|'|\)|\])`)
|
||||
ret.mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[-\w][-.\w]*?|@[-\w][-.\w]*?/[-\w][-.\w]*?)(?:\s|$|[:,;.?!](\s|$)|'|\)|\])`)
|
||||
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
|
||||
// TODO: REFERENCE-REGEXP-PATTERN: findValidReferenceIndex checks a non-ASCII trailing character
|
||||
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\'|\"|[^\x00-\x7f])([#!][0-9]+)(?:\s|$|\)|\]|\'|\"|[:;,.?!]\s|[:;,.?!]$|[^\x00-\x7f])`)
|
||||
ret.issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\'|\"|[^\x00-\x7f])([#!][0-9]+)(?:\s|$|\)|\]|\'|\"|[:;,.?!]\s|[:;,.?!]$|[^\x00-\x7f])`)
|
||||
// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
|
||||
issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\"|\')([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$)|\"|\'|,)`)
|
||||
ret.issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\"|\')([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$)|\"|\'|,)`)
|
||||
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
|
||||
// e.g. org/repo#12345
|
||||
// TODO: REFERENCE-REGEXP-PATTERN: findValidReferenceIndex checks a non-ASCII trailing character
|
||||
crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|[^\x00-\x7f])([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$|[^\x00-\x7f])`)
|
||||
ret.crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|[^\x00-\x7f])([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$|[^\x00-\x7f])`)
|
||||
// crossReferenceCommitPattern matches a string that references a commit in a different repository
|
||||
// e.g. go-gitea/gitea@d8a994ef, go-gitea/gitea@d8a994ef243349f321568f9e36d5c3f444b99cae (7-40 characters)
|
||||
crossReferenceCommitPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+)@([0-9a-f]{7,64})(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
||||
ret.crossReferenceCommitPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+)@([0-9a-f]{7,64})(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
||||
// timeLogPattern matches string for time tracking
|
||||
timeLogPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@([0-9]+([\.,][0-9]+)?(w|d|m|h))+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
||||
ret.timeLogPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@([0-9]+([\.,][0-9]+)?(w|d|m|h))+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
||||
return ret
|
||||
})
|
||||
|
||||
var (
|
||||
issueCloseKeywordsPat, issueReopenKeywordsPat *regexp.Regexp
|
||||
issueKeywordWindow int
|
||||
issueKeywordsOnce sync.Once
|
||||
@@ -225,7 +237,7 @@ func FindAllMentionsBytes(content []byte) []RefSpan {
|
||||
ret := make([]RefSpan, 0, 5)
|
||||
pos := 0
|
||||
for {
|
||||
match := mentionPattern.FindSubmatchIndex(content[pos:])
|
||||
match := globalVars().mentionPattern.FindSubmatchIndex(content[pos:])
|
||||
if match == nil {
|
||||
break
|
||||
}
|
||||
@@ -238,7 +250,7 @@ func FindAllMentionsBytes(content []byte) []RefSpan {
|
||||
// FindFirstMentionBytes matches the first mention in then given content
|
||||
// and returns the location of the unvalidated username, including the @ prefix.
|
||||
func FindFirstMentionBytes(content []byte) (bool, RefSpan) {
|
||||
mention := mentionPattern.FindSubmatchIndex(content)
|
||||
mention := globalVars().mentionPattern.FindSubmatchIndex(content)
|
||||
if mention == nil {
|
||||
return false, RefSpan{}
|
||||
}
|
||||
@@ -365,10 +377,10 @@ func FindRenderizableReferenceNumeric(content string, prOnly, crossLinkOnly bool
|
||||
contentBytes := util.UnsafeStringToBytes(content)
|
||||
var match []int
|
||||
if !crossLinkOnly {
|
||||
match = findValidReferenceIndex(issueNumericPattern, contentBytes, 0)
|
||||
match = findValidReferenceIndex(globalVars().issueNumericPattern, contentBytes, 0)
|
||||
}
|
||||
if match == nil {
|
||||
if match = findValidReferenceIndex(crossReferenceIssueNumericPattern, contentBytes, 0); match == nil {
|
||||
if match = findValidReferenceIndex(globalVars().crossReferenceIssueNumericPattern, contentBytes, 0); match == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -390,7 +402,7 @@ func FindRenderizableReferenceNumeric(content string, prOnly, crossLinkOnly bool
|
||||
|
||||
// FindRenderizableCommitCrossReference returns the first unvalidated commit cross reference found in a string.
|
||||
func FindRenderizableCommitCrossReference(content string) (bool, *RenderizableReference) {
|
||||
m := crossReferenceCommitPattern.FindStringSubmatchIndex(content)
|
||||
m := globalVars().crossReferenceCommitPattern.FindStringSubmatchIndex(content)
|
||||
if len(m) < 8 {
|
||||
return false, nil
|
||||
}
|
||||
@@ -436,7 +448,7 @@ func FindRenderizableReferenceRegexp(content string, pattern *regexp.Regexp) *Re
|
||||
|
||||
// FindRenderizableReferenceAlphanumeric returns the first alphanumeric unvalidated references found in a string.
|
||||
func FindRenderizableReferenceAlphanumeric(content string) *RenderizableReference {
|
||||
match := issueAlphanumericPattern.FindStringSubmatchIndex(content)
|
||||
match := globalVars().issueAlphanumericPattern.FindStringSubmatchIndex(content)
|
||||
if match == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -460,7 +472,7 @@ func findAllIssueReferencesBytes(content []byte, links []string, originalContent
|
||||
// trailing spaces (\s#ref,\s), so if we get two consecutive references, the space
|
||||
// from the second reference will be "eaten" by the first one:
|
||||
// ...\s#ref1\s#ref2\s... --> ...`\s#ref1\s`, (not) `#ref2,\s...`
|
||||
for _, pattern := range []*regexp.Regexp{issueNumericPattern, crossReferenceIssueNumericPattern} {
|
||||
for _, pattern := range []*regexp.Regexp{globalVars().issueNumericPattern, globalVars().crossReferenceIssueNumericPattern} {
|
||||
for match := findValidReferenceIndex(pattern, content, 0); match != nil; match = findValidReferenceIndex(pattern, content, match[3]) {
|
||||
if ref := getCrossReference(content, match[2], match[3], false, false); ref != nil {
|
||||
ret = append(ret, ref)
|
||||
@@ -523,7 +535,7 @@ func findAllIssueReferencesBytes(content []byte, links []string, originalContent
|
||||
pos := 0
|
||||
|
||||
for {
|
||||
match := timeLogPattern.FindSubmatchIndex(content[pos:])
|
||||
match := globalVars().timeLogPattern.FindSubmatchIndex(content[pos:])
|
||||
if match == nil {
|
||||
break
|
||||
}
|
||||
@@ -586,7 +598,7 @@ func getCrossReference(content []byte, start, end int, fromLink, prOnly bool) *r
|
||||
return nil
|
||||
}
|
||||
owner, name := parts[0], parts[1]
|
||||
if !validNamePattern.MatchString(owner) || !validNamePattern.MatchString(name) {
|
||||
if !globalVars().validNamePattern.MatchString(owner) || !globalVars().validNamePattern.MatchString(name) {
|
||||
return nil
|
||||
}
|
||||
action, location := findActionKeywords(content, start)
|
||||
|
||||
@@ -483,11 +483,11 @@ func TestRegExp_mentionPattern(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range trueTestCases {
|
||||
found := mentionPattern.FindStringSubmatch(testCase.pat)
|
||||
found := globalVars().mentionPattern.FindStringSubmatch(testCase.pat)
|
||||
assert.Equal(t, testCase.exp, found[1])
|
||||
}
|
||||
for _, testCase := range falseTestCases {
|
||||
res := mentionPattern.MatchString(testCase)
|
||||
res := globalVars().mentionPattern.MatchString(testCase)
|
||||
assert.False(t, res, "[%s] should be false", testCase)
|
||||
}
|
||||
}
|
||||
@@ -515,10 +515,10 @@ func TestRegExp_issueNumericPattern(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range trueTestCases {
|
||||
assert.True(t, issueNumericPattern.MatchString(testCase))
|
||||
assert.True(t, globalVars().issueNumericPattern.MatchString(testCase))
|
||||
}
|
||||
for _, testCase := range falseTestCases {
|
||||
assert.False(t, issueNumericPattern.MatchString(testCase))
|
||||
assert.False(t, globalVars().issueNumericPattern.MatchString(testCase))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,10 +552,10 @@ func TestRegExp_issueAlphanumericPattern(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range trueTestCases {
|
||||
assert.True(t, issueAlphanumericPattern.MatchString(testCase))
|
||||
assert.True(t, globalVars().issueAlphanumericPattern.MatchString(testCase))
|
||||
}
|
||||
for _, testCase := range falseTestCases {
|
||||
assert.False(t, issueAlphanumericPattern.MatchString(testCase))
|
||||
assert.False(t, globalVars().issueAlphanumericPattern.MatchString(testCase))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/log"
|
||||
)
|
||||
@@ -20,7 +21,15 @@ const (
|
||||
|
||||
const escapeRegexpString = "_0[xX](([0-9a-fA-F][0-9a-fA-F])+)_"
|
||||
|
||||
var escapeRegex = regexp.MustCompile(escapeRegexpString)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
escapeRegex *regexp.Regexp
|
||||
extensionReg *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.escapeRegex = regexp.MustCompile(escapeRegexpString)
|
||||
ret.extensionReg = regexp.MustCompile(`^(\.[-\w]+)+$`)
|
||||
return ret
|
||||
})
|
||||
|
||||
func CollectEnvConfigKeys() (keys []string) {
|
||||
for _, env := range os.Environ() {
|
||||
@@ -47,7 +56,7 @@ func ClearEnvConfigKeys() {
|
||||
func decodeEnvSectionKey(encoded string) (ok bool, section, key string) {
|
||||
inKey := false
|
||||
last := 0
|
||||
escapeStringIndices := escapeRegex.FindAllStringIndex(encoded, -1)
|
||||
escapeStringIndices := globalVars().escapeRegex.FindAllStringIndex(encoded, -1)
|
||||
for _, unescapeIdx := range escapeStringIndices {
|
||||
preceding := encoded[last:unescapeIdx[0]]
|
||||
if !inKey {
|
||||
|
||||
@@ -6,7 +6,6 @@ package setting
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/util"
|
||||
@@ -222,14 +221,10 @@ func createMarkupSanitizerRule(name string, sec ConfigSection) (MarkupSanitizerR
|
||||
return rule, true
|
||||
}
|
||||
|
||||
var extensionReg = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`^(\.[-\w]+)+$`)
|
||||
})
|
||||
|
||||
func fileExtensionsToPatterns(sectionName string, extensions []string) []string {
|
||||
patterns := make([]string, 0, len(extensions))
|
||||
for _, extension := range extensions {
|
||||
if !extensionReg().MatchString(extension) {
|
||||
if !globalVars().extensionReg.MatchString(extension) {
|
||||
log.Warn("Config section %s file extension %s is invalid. Extension ignored", sectionName, extension)
|
||||
} else {
|
||||
patterns = append(patterns, "*"+extension)
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestSubjectBodySeparator(t *testing.T) {
|
||||
{"Multiple\n---\n-------\n---\nSeparators", "Multiple\n", "\n-------\n---\nSeparators"},
|
||||
{"Insufficient\n--\nSeparators", "", "Insufficient\n--\nSeparators"},
|
||||
} {
|
||||
loc := mailSubjectSplit.FindStringIndex(testCase.input)
|
||||
loc := globalVars().mailSubjectSplit.FindStringIndex(testCase.input)
|
||||
if loc == nil {
|
||||
assert.Empty(t, testCase.subject, "no subject found, but one expected")
|
||||
assert.Equal(t, testCase.body, testCase.input)
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
texttemplate "text/template"
|
||||
|
||||
@@ -81,10 +82,31 @@ type templateErrorPrettier struct {
|
||||
assets *assetfs.LayeredFS
|
||||
}
|
||||
|
||||
var reGenericTemplateError = regexp.MustCompile(`^template: (.*):([0-9]+): (.*)`)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
reGenericTemplateError *regexp.Regexp
|
||||
reFuncNotDefinedError *regexp.Regexp
|
||||
reUnexpectedOperandError *regexp.Regexp
|
||||
reExpectedEndError *regexp.Regexp
|
||||
reTemplateExecutingError *regexp.Regexp
|
||||
reTemplateExecutingErrorMsg *regexp.Regexp
|
||||
mailSubjectSplit *regexp.Regexp
|
||||
codeMatcher *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.reGenericTemplateError = regexp.MustCompile(`^template: (.*):([0-9]+): (.*)`)
|
||||
ret.reFuncNotDefinedError = regexp.MustCompile(`^template: (.*):([0-9]+): (function "(.*)" not defined)`)
|
||||
ret.reUnexpectedOperandError = regexp.MustCompile(`^template: (.*):([0-9]+): (unexpected "(.*)" in operand)`)
|
||||
ret.reExpectedEndError = regexp.MustCompile(`^template: (.*):([0-9]+): (expected end; found (.*))`)
|
||||
ret.reTemplateExecutingError = regexp.MustCompile(`^template: (.*):([1-9][0-9]*):([1-9][0-9]*): (executing .*)`)
|
||||
ret.reTemplateExecutingErrorMsg = regexp.MustCompile(`^executing "(.*)" at <(.*)>: `)
|
||||
ret.mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}\s*$`)
|
||||
// Match text that is between back ticks.
|
||||
ret.codeMatcher = regexp.MustCompile("`([^`]+)`")
|
||||
return ret
|
||||
})
|
||||
|
||||
func (p *templateErrorPrettier) handleGenericTemplateError(err error) string {
|
||||
groups := reGenericTemplateError.FindStringSubmatch(err.Error())
|
||||
groups := globalVars().reGenericTemplateError.FindStringSubmatch(err.Error())
|
||||
if len(groups) != 4 {
|
||||
return ""
|
||||
}
|
||||
@@ -92,10 +114,8 @@ func (p *templateErrorPrettier) handleGenericTemplateError(err error) string {
|
||||
return p.makeDetailedError(message, tmplName, lineStr, "", "")
|
||||
}
|
||||
|
||||
var reFuncNotDefinedError = regexp.MustCompile(`^template: (.*):([0-9]+): (function "(.*)" not defined)`)
|
||||
|
||||
func (p *templateErrorPrettier) handleFuncNotDefinedError(err error) string {
|
||||
groups := reFuncNotDefinedError.FindStringSubmatch(err.Error())
|
||||
groups := globalVars().reFuncNotDefinedError.FindStringSubmatch(err.Error())
|
||||
if len(groups) != 5 {
|
||||
return ""
|
||||
}
|
||||
@@ -104,10 +124,8 @@ func (p *templateErrorPrettier) handleFuncNotDefinedError(err error) string {
|
||||
return p.makeDetailedError(message, tmplName, lineStr, "", funcName)
|
||||
}
|
||||
|
||||
var reUnexpectedOperandError = regexp.MustCompile(`^template: (.*):([0-9]+): (unexpected "(.*)" in operand)`)
|
||||
|
||||
func (p *templateErrorPrettier) handleUnexpectedOperandError(err error) string {
|
||||
groups := reUnexpectedOperandError.FindStringSubmatch(err.Error())
|
||||
groups := globalVars().reUnexpectedOperandError.FindStringSubmatch(err.Error())
|
||||
if len(groups) != 5 {
|
||||
return ""
|
||||
}
|
||||
@@ -116,10 +134,8 @@ func (p *templateErrorPrettier) handleUnexpectedOperandError(err error) string {
|
||||
return p.makeDetailedError(message, tmplName, lineStr, "", unexpected)
|
||||
}
|
||||
|
||||
var reExpectedEndError = regexp.MustCompile(`^template: (.*):([0-9]+): (expected end; found (.*))`)
|
||||
|
||||
func (p *templateErrorPrettier) handleExpectedEndError(err error) string {
|
||||
groups := reExpectedEndError.FindStringSubmatch(err.Error())
|
||||
groups := globalVars().reExpectedEndError.FindStringSubmatch(err.Error())
|
||||
if len(groups) != 5 {
|
||||
return ""
|
||||
}
|
||||
@@ -127,16 +143,11 @@ func (p *templateErrorPrettier) handleExpectedEndError(err error) string {
|
||||
return p.makeDetailedError(message, tmplName, lineStr, "", unexpected)
|
||||
}
|
||||
|
||||
var (
|
||||
reTemplateExecutingError = regexp.MustCompile(`^template: (.*):([1-9][0-9]*):([1-9][0-9]*): (executing .*)`)
|
||||
reTemplateExecutingErrorMsg = regexp.MustCompile(`^executing "(.*)" at <(.*)>: `)
|
||||
)
|
||||
|
||||
func (p *templateErrorPrettier) handleTemplateRenderingError(err error) string {
|
||||
if groups := reTemplateExecutingError.FindStringSubmatch(err.Error()); len(groups) > 0 {
|
||||
if groups := globalVars().reTemplateExecutingError.FindStringSubmatch(err.Error()); len(groups) > 0 {
|
||||
tmplName, lineStr, posStr, msgPart := groups[1], groups[2], groups[3], groups[4]
|
||||
target := ""
|
||||
if groups = reTemplateExecutingErrorMsg.FindStringSubmatch(msgPart); len(groups) > 0 {
|
||||
if groups = globalVars().reTemplateExecutingErrorMsg.FindStringSubmatch(msgPart); len(groups) > 0 {
|
||||
target = groups[2]
|
||||
}
|
||||
return p.makeDetailedError(msgPart, tmplName, lineStr, posStr, target)
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"html/template"
|
||||
"io"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -104,8 +103,6 @@ func mailBodyFuncMap() template.FuncMap {
|
||||
}
|
||||
}
|
||||
|
||||
var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}\s*$`)
|
||||
|
||||
func newMailRenderer() (*MailRender, error) {
|
||||
subjectTemplates := texttmpl.New("")
|
||||
subjectTemplates.Funcs(mailSubjectTextFuncMap())
|
||||
@@ -155,7 +152,7 @@ func newMailRenderer() (*MailRender, error) {
|
||||
}
|
||||
var subjectContent []byte
|
||||
bodyContent := content
|
||||
loc := mailSubjectSplit.FindIndex(content)
|
||||
loc := globalVars().mailSubjectSplit.FindIndex(content)
|
||||
if loc != nil {
|
||||
subjectContent, bodyContent = content[0:loc[0]], content[loc[1]:]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"html/template"
|
||||
"math"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -78,12 +77,9 @@ func (ut *RenderUtils) RenderCommitBody(msg string, repo *repo.Repository) templ
|
||||
return renderedMessage
|
||||
}
|
||||
|
||||
// Match text that is between back ticks.
|
||||
var codeMatcher = regexp.MustCompile("`([^`]+)`")
|
||||
|
||||
// renderCodeBlock renders "`…`" as highlighted "<code>" block, intended for issue and PR titles
|
||||
func renderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML {
|
||||
htmlWithCodeTags := codeMatcher.ReplaceAllString(string(htmlEscapedTextToRender), `<code class="inline-code-block">$1</code>`) // replace with HTML <code> tags
|
||||
htmlWithCodeTags := globalVars().codeMatcher.ReplaceAllString(string(htmlEscapedTextToRender), `<code class="inline-code-block">$1</code>`) // replace with HTML <code> tags
|
||||
return template.HTML(htmlWithCodeTags)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
user_model "gitea.dev/models/user"
|
||||
@@ -34,13 +35,19 @@ const (
|
||||
maxTimeDifference = 10 * time.Minute
|
||||
)
|
||||
|
||||
var (
|
||||
algorithmPattern = regexp.MustCompile(`algorithm=(\w+)`)
|
||||
versionPattern = regexp.MustCompile(`version=(\d+\.\d+)`)
|
||||
authorizationPattern = regexp.MustCompile(`\AX-Ops-Authorization-(\d+)`)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
algorithmPattern *regexp.Regexp
|
||||
versionPattern *regexp.Regexp
|
||||
authorizationPattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.algorithmPattern = regexp.MustCompile(`algorithm=(\w+)`)
|
||||
ret.versionPattern = regexp.MustCompile(`version=(\d+\.\d+)`)
|
||||
ret.authorizationPattern = regexp.MustCompile(`\AX-Ops-Authorization-(\d+)`)
|
||||
return ret
|
||||
})
|
||||
|
||||
_ auth.Method = &Auth{}
|
||||
)
|
||||
var _ auth.Method = &Auth{}
|
||||
|
||||
// Documentation:
|
||||
// https://docs.chef.io/server/api_chef_server/#required-headers
|
||||
@@ -139,7 +146,7 @@ func getSignVersion(req *http.Request) (string, error) {
|
||||
return "", util.NewInvalidArgumentErrorf("X-Ops-Sign header missing")
|
||||
}
|
||||
|
||||
m := versionPattern.FindStringSubmatch(hdr)
|
||||
m := globalVars().versionPattern.FindStringSubmatch(hdr)
|
||||
if len(m) != 2 {
|
||||
return "", util.NewInvalidArgumentErrorf("invalid X-Ops-Sign header")
|
||||
}
|
||||
@@ -152,7 +159,7 @@ func getSignVersion(req *http.Request) (string, error) {
|
||||
|
||||
version := m[1]
|
||||
|
||||
m = algorithmPattern.FindStringSubmatch(hdr)
|
||||
m = globalVars().algorithmPattern.FindStringSubmatch(hdr)
|
||||
if len(m) == 2 && m[1] != "sha1" && !(m[1] == "sha256" && version == "1.3") {
|
||||
return "", util.NewInvalidArgumentErrorf("unsupported algorithm")
|
||||
}
|
||||
@@ -181,7 +188,7 @@ func verifySignedHeaders(req *http.Request, version string, pub *rsa.PublicKey)
|
||||
func getAuthorizationData(req *http.Request) ([]byte, error) {
|
||||
valueList := make(map[int]string)
|
||||
for k, vs := range req.Header {
|
||||
if m := authorizationPattern.FindStringSubmatch(k); m != nil {
|
||||
if m := globalVars().authorizationPattern.FindStringSubmatch(k); m != nil {
|
||||
index, _ := strconv.Atoi(m[1])
|
||||
var v string
|
||||
if len(vs) == 0 {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
packages_model "gitea.dev/models/packages"
|
||||
@@ -17,10 +18,15 @@ import (
|
||||
packages_service "gitea.dev/services/packages"
|
||||
)
|
||||
|
||||
var (
|
||||
packageNameRegex = regexp.MustCompile(`\A[-_+.\w]+\z`)
|
||||
filenameRegex = regexp.MustCompile(`\A[-_+=:;.()\[\]{}~!@#$%^& \w]+\z`)
|
||||
)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
packageNameRegex *regexp.Regexp
|
||||
filenameRegex *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.packageNameRegex = regexp.MustCompile(`\A[-_+.\w]+\z`)
|
||||
ret.filenameRegex = regexp.MustCompile(`\A[-_+=:;.()\[\]{}~!@#$%^& \w]+\z`)
|
||||
return ret
|
||||
})
|
||||
|
||||
func apiError(ctx *context.Context, status int, obj any) {
|
||||
message := helper.ProcessErrorForUser(ctx, status, obj)
|
||||
@@ -54,11 +60,11 @@ func isValidPackageName(packageName string) bool {
|
||||
if len(packageName) == 1 && !unicode.IsLetter(rune(packageName[0])) && !unicode.IsNumber(rune(packageName[0])) {
|
||||
return false
|
||||
}
|
||||
return packageNameRegex.MatchString(packageName) && packageName != ".."
|
||||
return globalVars().packageNameRegex.MatchString(packageName) && packageName != ".."
|
||||
}
|
||||
|
||||
func isValidFileName(filename string) bool {
|
||||
return filenameRegex.MatchString(filename) &&
|
||||
return globalVars().filenameRegex.MatchString(filename) &&
|
||||
strings.TrimSpace(filename) == filename &&
|
||||
filename != "." && filename != ".."
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
packages_model "gitea.dev/models/packages"
|
||||
"gitea.dev/modules/globallock"
|
||||
@@ -46,7 +47,9 @@ const (
|
||||
|
||||
var (
|
||||
errInvalidParameters = errors.New("request parameters are invalid")
|
||||
illegalCharacters = regexp.MustCompile(`[\\/:"<>|?*]`)
|
||||
illegalCharacters = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`[\\/:"<>|?*]`)
|
||||
})
|
||||
)
|
||||
|
||||
func apiError(ctx *context.Context, status int, obj any) {
|
||||
@@ -471,7 +474,7 @@ func extractPathParameters(ctx *context.Context) (parameters, error) {
|
||||
parts = parts[:len(parts)-1]
|
||||
}
|
||||
|
||||
if illegalCharacters.MatchString(p.Version) {
|
||||
if illegalCharacters().MatchString(p.Version) {
|
||||
return p, errInvalidParameters
|
||||
}
|
||||
|
||||
@@ -482,7 +485,7 @@ func extractPathParameters(ctx *context.Context) (parameters, error) {
|
||||
p.ArtifactID = parts[len(parts)-1]
|
||||
p.GroupID = strings.Join(parts[:len(parts)-1], ".")
|
||||
|
||||
if illegalCharacters.MatchString(p.GroupID) || illegalCharacters.MatchString(p.ArtifactID) {
|
||||
if illegalCharacters().MatchString(p.GroupID) || illegalCharacters().MatchString(p.ArtifactID) {
|
||||
return p, errInvalidParameters
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
packages_model "gitea.dev/models/packages"
|
||||
@@ -90,10 +91,15 @@ func FeedCapabilityResource(ctx *context.Context) {
|
||||
xmlResponse(ctx, http.StatusOK, Metadata)
|
||||
}
|
||||
|
||||
var (
|
||||
searchTermExtract = regexp.MustCompile(`'([^']+)'`)
|
||||
searchTermExact = regexp.MustCompile(`\s+eq\s+'`)
|
||||
)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
searchTermExtract *regexp.Regexp
|
||||
searchTermExact *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.searchTermExtract = regexp.MustCompile(`'([^']+)'`)
|
||||
ret.searchTermExact = regexp.MustCompile(`\s+eq\s+'`)
|
||||
return ret
|
||||
})
|
||||
|
||||
func getSearchTerm(ctx *context.Context) packages_model.SearchValue {
|
||||
searchTerm := strings.Trim(ctx.FormTrim("searchTerm"), "'")
|
||||
@@ -109,11 +115,11 @@ func getSearchTerm(ctx *context.Context) packages_model.SearchValue {
|
||||
// https://www.odata.org/documentation/odata-version-2-0/uri-conventions/ section 4.5
|
||||
// We don't support these queries, just extract the search term.
|
||||
filter := ctx.FormTrim("$filter")
|
||||
match := searchTermExtract.FindStringSubmatch(filter)
|
||||
match := globalVars().searchTermExtract.FindStringSubmatch(filter)
|
||||
if len(match) == 2 {
|
||||
return packages_model.SearchValue{
|
||||
Value: strings.TrimSpace(match[1]),
|
||||
ExactMatch: searchTermExact.MatchString(filter),
|
||||
ExactMatch: globalVars().searchTermExact.MatchString(filter),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
packages_model "gitea.dev/models/packages"
|
||||
@@ -23,20 +24,25 @@ import (
|
||||
)
|
||||
|
||||
// https://peps.python.org/pep-0426/#name
|
||||
var (
|
||||
normalizer = strings.NewReplacer(".", "-", "_", "-")
|
||||
nameMatcher = regexp.MustCompile(`\A(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\.\-_]*[a-zA-Z0-9])\z`)
|
||||
)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
nameMatcher *regexp.Regexp
|
||||
versionMatcher *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.nameMatcher = regexp.MustCompile(`\A(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\.\-_]*[a-zA-Z0-9])\z`)
|
||||
// https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
|
||||
ret.versionMatcher = regexp.MustCompile(`\Av?` +
|
||||
`(?:[0-9]+!)?` + // epoch
|
||||
`[0-9]+(?:\.[0-9]+)*` + // release segment
|
||||
`(?:[-_\.]?(?:a|b|c|rc|alpha|beta|pre|preview)[-_\.]?[0-9]*)?` + // pre-release
|
||||
`(?:-[0-9]+|[-_\.]?(?:post|rev|r)[-_\.]?[0-9]*)?` + // post release
|
||||
`(?:[-_\.]?dev[-_\.]?[0-9]*)?` + // dev release
|
||||
`(?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)?` + // local version
|
||||
`\z`)
|
||||
return ret
|
||||
})
|
||||
|
||||
// https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
|
||||
var versionMatcher = regexp.MustCompile(`\Av?` +
|
||||
`(?:[0-9]+!)?` + // epoch
|
||||
`[0-9]+(?:\.[0-9]+)*` + // release segment
|
||||
`(?:[-_\.]?(?:a|b|c|rc|alpha|beta|pre|preview)[-_\.]?[0-9]*)?` + // pre-release
|
||||
`(?:-[0-9]+|[-_\.]?(?:post|rev|r)[-_\.]?[0-9]*)?` + // post release
|
||||
`(?:[-_\.]?dev[-_\.]?[0-9]*)?` + // dev release
|
||||
`(?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)?` + // local version
|
||||
`\z`)
|
||||
var normalizer = strings.NewReplacer(".", "-", "_", "-")
|
||||
|
||||
func apiError(ctx *context.Context, status int, obj any) {
|
||||
message := helper.ProcessErrorForUser(ctx, status, obj)
|
||||
@@ -225,5 +231,5 @@ func normalizeLabel(label string) string {
|
||||
}
|
||||
|
||||
func isValidNameAndVersion(packageName, packageVersion string) bool {
|
||||
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
|
||||
return globalVars().nameMatcher.MatchString(packageName) && globalVars().versionMatcher.MatchString(packageVersion)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
packages_model "gitea.dev/models/packages"
|
||||
"gitea.dev/modules/json"
|
||||
@@ -35,12 +36,17 @@ const (
|
||||
AcceptZip = "application/vnd.swift.registry.v1+zip"
|
||||
)
|
||||
|
||||
var (
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
scopePattern *regexp.Regexp
|
||||
namePattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#361-package-scope
|
||||
scopePattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9-]{0,38}\z`)
|
||||
ret.scopePattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9-]{0,38}\z`)
|
||||
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#362-package-name
|
||||
namePattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9-_]{0,99}\z`)
|
||||
)
|
||||
ret.namePattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9-_]{0,99}\z`)
|
||||
return ret
|
||||
})
|
||||
|
||||
type headers struct {
|
||||
Status int
|
||||
@@ -327,7 +333,7 @@ func UploadPackageFile(ctx *context.Context) {
|
||||
|
||||
_, err := version.NewSemver(packageVersion)
|
||||
|
||||
if err != nil || !scopePattern.MatchString(packageScope) || !namePattern.MatchString(packageName) {
|
||||
if err != nil || !globalVars().scopePattern.MatchString(packageScope) || !globalVars().namePattern.MatchString(packageName) {
|
||||
apiError(ctx, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
packages_model "gitea.dev/models/packages"
|
||||
@@ -24,7 +25,9 @@ import (
|
||||
packages_service "gitea.dev/services/packages"
|
||||
)
|
||||
|
||||
var packageNameRegex = regexp.MustCompile(`\A[-_+.\w]+\z`)
|
||||
var packageNameRegex = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`\A[-_+.\w]+\z`)
|
||||
})
|
||||
|
||||
const (
|
||||
stateFilename = "tfstate"
|
||||
@@ -81,7 +84,7 @@ func isValidPackageName(packageName string) bool {
|
||||
if len(packageName) == 1 && !unicode.IsLetter(rune(packageName[0])) && !unicode.IsNumber(rune(packageName[0])) {
|
||||
return false
|
||||
}
|
||||
return packageNameRegex.MatchString(packageName) && packageName != ".."
|
||||
return packageNameRegex().MatchString(packageName) && packageName != ".."
|
||||
}
|
||||
|
||||
// UploadState uploads the specific terraform package.
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
@@ -35,10 +36,15 @@ const (
|
||||
tplAuthEdit templates.TplName = "admin/auth/edit"
|
||||
)
|
||||
|
||||
var (
|
||||
separatorAntiPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
langCodePattern = regexp.MustCompile(`^[a-z]{2}-[A-Z]{2}$`)
|
||||
)
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
separatorAntiPattern *regexp.Regexp
|
||||
langCodePattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.separatorAntiPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
ret.langCodePattern = regexp.MustCompile(`^[a-z]{2}-[A-Z]{2}$`)
|
||||
return ret
|
||||
})
|
||||
|
||||
// Authentications show authentication config page
|
||||
func Authentications(ctx *context.Context) {
|
||||
@@ -204,12 +210,12 @@ func parseSSPIConfig(ctx *context.Context, form forms.AuthenticationForm) (*sspi
|
||||
ctx.Data["Err_SSPISeparatorReplacement"] = true
|
||||
return nil, errors.New(ctx.Locale.TrString("form.require_error", ctx.Locale.TrString("form.SSPISeparatorReplacement")))
|
||||
}
|
||||
if separatorAntiPattern.MatchString(form.SSPISeparatorReplacement) {
|
||||
if globalVars().separatorAntiPattern.MatchString(form.SSPISeparatorReplacement) {
|
||||
ctx.Data["Err_SSPISeparatorReplacement"] = true
|
||||
return nil, errors.New(ctx.Locale.TrString("form.alpha_dash_dot_error", ctx.Locale.TrString("form.SSPISeparatorReplacement")))
|
||||
}
|
||||
|
||||
if form.SSPIDefaultLanguage != "" && !langCodePattern.MatchString(form.SSPIDefaultLanguage) {
|
||||
if form.SSPIDefaultLanguage != "" && !globalVars().langCodePattern.MatchString(form.SSPIDefaultLanguage) {
|
||||
ctx.Data["Err_SSPIDefaultLanguage"] = true
|
||||
return nil, errors.New(ctx.Locale.TrString("form.lang_select_error"))
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/templates"
|
||||
@@ -19,7 +20,9 @@ import (
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
var mailDarkSchemeQuery = regexp.MustCompile(`@media\s*\(\s*prefers-color-scheme\s*:\s*dark\s*\)`)
|
||||
var mailDarkSchemeQuery = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`@media\s*\(\s*prefers-color-scheme\s*:\s*dark\s*\)`)
|
||||
})
|
||||
|
||||
func mailPreviewMockData(tmplName string) (map[string]any, error) {
|
||||
mockData := map[string]any{}
|
||||
@@ -51,7 +54,7 @@ func MailPreviewRender(ctx *context.Context) {
|
||||
body = strings.ReplaceAll(body, `src="cid:`, `src="`+setting.AppSubURL+`/devtest/mail-preview-embed/`)
|
||||
// a page can force "color-scheme" on an embedded document but never "prefers-color-scheme"
|
||||
if scheme := ctx.FormString("scheme"); scheme == "light" || scheme == "dark" {
|
||||
body = mailDarkSchemeQuery.ReplaceAllString(body, util.Iif(scheme == "dark", "@media all", "@media not all"))
|
||||
body = mailDarkSchemeQuery().ReplaceAllString(body, util.Iif(scheme == "dark", "@media all", "@media not all"))
|
||||
body = strings.Replace(body, "</head>", "<style>:root {color-scheme: "+scheme+"}</style></head>", 1)
|
||||
}
|
||||
// fragment templates like "mail/base/head" would be sniffed as text/plain otherwise
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
activities_model "gitea.dev/models/activities"
|
||||
asymkey_model "gitea.dev/models/asymkey"
|
||||
@@ -183,7 +184,7 @@ func Milestones(ctx *context.Context) {
|
||||
}
|
||||
|
||||
if len(reposQuery) != 0 {
|
||||
if issueReposQueryPattern.MatchString(reposQuery) {
|
||||
if issueReposQueryPattern().MatchString(reposQuery) {
|
||||
// remove "[" and "]" from string
|
||||
reposQuery = reposQuery[1 : len(reposQuery)-1]
|
||||
// for each ID (delimiter ",") add to int to repoIDs
|
||||
@@ -353,7 +354,9 @@ func Issues(ctx *context.Context) {
|
||||
}
|
||||
|
||||
// Regexp for repos query
|
||||
var issueReposQueryPattern = regexp.MustCompile(`^\[\d+(,\d+)*,?\]$`)
|
||||
var issueReposQueryPattern = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`^\[\d+(,\d+)*,?\]$`)
|
||||
})
|
||||
|
||||
func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
// ----------------------------------------------------
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/modules/log"
|
||||
@@ -38,7 +39,9 @@ func (err ErrFileTypeForbidden) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
var wildcardTypeRe = regexp.MustCompile(`^[a-z]+/\*$`)
|
||||
var wildcardTypeRe = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`^[a-z]+/\*$`)
|
||||
})
|
||||
|
||||
// Verify validates whether a file is allowed to be uploaded. If buf is empty, it will just check if the file
|
||||
// has an allowed file extension.
|
||||
@@ -80,7 +83,7 @@ func Verify(buf []byte, fileName, allowedTypesStr string) error {
|
||||
if mimeType == allowEntry {
|
||||
return nil // mime type is allowed
|
||||
}
|
||||
if wildcardTypeRe.MatchString(allowEntry) && strings.HasPrefix(mimeType, allowEntry[:len(allowEntry)-1]) {
|
||||
if wildcardTypeRe().MatchString(allowEntry) && strings.HasPrefix(mimeType, allowEntry[:len(allowEntry)-1]) {
|
||||
return nil // wildcard match, e.g. image/*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
issues_model "gitea.dev/models/issues"
|
||||
@@ -35,11 +36,13 @@ const (
|
||||
secondsByMonth = 4 * secondsByWeek // seconds in a month
|
||||
)
|
||||
|
||||
var reDuration = regexp.MustCompile(`(?i)^(?:(\d+([\.,]\d+)?)(?:mo))?(?:(\d+([\.,]\d+)?)(?:w))?(?:(\d+([\.,]\d+)?)(?:d))?(?:(\d+([\.,]\d+)?)(?:h))?(?:(\d+([\.,]\d+)?)(?:m))?$`)
|
||||
var reDuration = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`(?i)^(?:(\d+([\.,]\d+)?)(?:mo))?(?:(\d+([\.,]\d+)?)(?:w))?(?:(\d+([\.,]\d+)?)(?:d))?(?:(\d+([\.,]\d+)?)(?:h))?(?:(\d+([\.,]\d+)?)(?:m))?$`)
|
||||
})
|
||||
|
||||
// timeLogToAmount parses time log string and returns amount in seconds
|
||||
func timeLogToAmount(str string) int64 {
|
||||
matches := reDuration.FindAllStringSubmatch(str, -1)
|
||||
matches := reDuration().FindAllStringSubmatch(str, -1)
|
||||
if len(matches) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
auth_model "gitea.dev/models/auth"
|
||||
@@ -111,7 +112,9 @@ func CheckAcceptMediaType(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
var rangeHeaderRegexp = regexp.MustCompile(`bytes=(\d+)-(\d*).*`)
|
||||
var rangeHeaderRegexp = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`bytes=(\d+)-(\d*).*`)
|
||||
})
|
||||
|
||||
// DownloadHandler gets the content from the content store
|
||||
func DownloadHandler(ctx *context.Context) {
|
||||
@@ -128,7 +131,7 @@ func DownloadHandler(ctx *context.Context) {
|
||||
toByte = meta.Size - 1
|
||||
statusCode := http.StatusOK
|
||||
if rangeHdr := ctx.Req.Header.Get("Range"); rangeHdr != "" {
|
||||
match := rangeHeaderRegexp.FindStringSubmatch(rangeHdr)
|
||||
match := rangeHeaderRegexp().FindStringSubmatch(rangeHdr)
|
||||
if len(match) > 1 {
|
||||
statusCode = http.StatusPartialContent
|
||||
fromByte, _ = strconv.ParseInt(match[1], 10, 32)
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"mime"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
repo_model "gitea.dev/models/repo"
|
||||
user_model "gitea.dev/models/user"
|
||||
@@ -31,7 +32,9 @@ import (
|
||||
|
||||
const mailMaxSubjectRunes = 256 // There's no actual limit for subject in RFC 5322
|
||||
|
||||
var subjectRemoveSpaces = regexp.MustCompile(`[\s]+`)
|
||||
var subjectRemoveSpaces = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`[\s]+`)
|
||||
})
|
||||
|
||||
func LoadedTemplates() *templates.MailRender {
|
||||
return templates.MailRenderer()
|
||||
@@ -47,7 +50,7 @@ func SendTestMail(email string) error {
|
||||
}
|
||||
|
||||
func sanitizeSubject(subject string) string {
|
||||
runes := []rune(strings.TrimSpace(subjectRemoveSpaces.ReplaceAllLiteralString(subject, " ")))
|
||||
runes := []rune(strings.TrimSpace(subjectRemoveSpaces().ReplaceAllLiteralString(subject, " ")))
|
||||
if len(runes) > mailMaxSubjectRunes {
|
||||
runes = runes[:mailMaxSubjectRunes]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
issues_model "gitea.dev/models/issues"
|
||||
@@ -564,7 +565,9 @@ func (g *GitlabDownloader) GetComments(ctx context.Context, commentable base.Com
|
||||
return allComments, true, nil
|
||||
}
|
||||
|
||||
var targetBranchChangeRegexp = regexp.MustCompile("^changed target branch from `(.*?)` to `(.*?)`$")
|
||||
var targetBranchChangeRegexp = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile("^changed target branch from `(.*?)` to `(.*?)`$")
|
||||
})
|
||||
|
||||
func (g *GitlabDownloader) convertNoteToComment(localIndex int64, note *gitlab.Note) *base.Comment {
|
||||
comment := &base.Comment{
|
||||
@@ -580,7 +583,7 @@ func (g *GitlabDownloader) convertNoteToComment(localIndex int64, note *gitlab.N
|
||||
|
||||
// Try to find the underlying event of system notes.
|
||||
if note.System {
|
||||
if match := targetBranchChangeRegexp.FindStringSubmatch(note.Body); match != nil {
|
||||
if match := targetBranchChangeRegexp().FindStringSubmatch(note.Body); match != nil {
|
||||
comment.CommentType = issues_model.CommentTypeChangeTargetBranch.String()
|
||||
comment.Meta["OldRef"] = match[1]
|
||||
comment.Meta["NewRef"] = match[2]
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
audit_model "gitea.dev/models/audit"
|
||||
@@ -28,7 +29,9 @@ import (
|
||||
repo_service "gitea.dev/services/repository"
|
||||
)
|
||||
|
||||
var stripExitStatus = regexp.MustCompile(`exit status \d+ - `)
|
||||
var stripExitStatus = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`exit status \d+ - `)
|
||||
})
|
||||
|
||||
// AddPushMirrorRemote registers the push mirror remote.
|
||||
func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) (retErr error) {
|
||||
@@ -132,7 +135,7 @@ func SyncPushMirror(ctx context.Context, mirrorID int64) bool {
|
||||
if err != nil {
|
||||
err = util.SanitizeErrorCredentialURLs(err)
|
||||
log.Error("SyncPushMirror [mirror: %d][repo: %-v]: %v", m.ID, m.Repo, err)
|
||||
m.LastError = stripExitStatus.ReplaceAllLiteralString(err.Error(), "")
|
||||
m.LastError = stripExitStatus().ReplaceAllLiteralString(err.Error(), "")
|
||||
}
|
||||
|
||||
m.LastUpdateUnix = timeutil.TimeStampNow()
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
"uuid"
|
||||
|
||||
@@ -666,7 +667,9 @@ func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *g
|
||||
return nil
|
||||
}
|
||||
|
||||
var escapedSymbols = regexp.MustCompile(`([*[?! \\])`)
|
||||
var escapedSymbols = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`([*[?! \\])`)
|
||||
})
|
||||
|
||||
// IsUserAllowedToMerge check if user is allowed to merge PR with given permissions and branch protections
|
||||
func IsUserAllowedToMerge(ctx context.Context, pr *issues_model.PullRequest, p access_model.Permission, user *user_model.User) (bool, error) {
|
||||
|
||||
@@ -220,7 +220,7 @@ func getDiffTree(ctx context.Context, repo git.RepositoryFacade, baseBranch, hea
|
||||
for scanner.Scan() {
|
||||
treePath := scanner.Text()
|
||||
// escape '*', '?', '[', spaces and '!' prefix
|
||||
treePath = escapedSymbols.ReplaceAllString(treePath, `\$1`)
|
||||
treePath = escapedSymbols().ReplaceAllString(treePath, `\$1`)
|
||||
// no necessary to escape the first '#' symbol because the first symbol is '/'
|
||||
if _, err := fmt.Fprintf(out, "/%s\n", treePath); err != nil {
|
||||
return err
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
webhook_model "gitea.dev/models/webhook"
|
||||
"gitea.dev/modules/base"
|
||||
@@ -267,10 +268,12 @@ func (m matrixConvertor) WorkflowJob(p *api.WorkflowJobPayload) (MatrixPayload,
|
||||
return m.newPayload(text)
|
||||
}
|
||||
|
||||
var urlRegex = regexp.MustCompile(`<a [^>]*?href="([^">]*?)">(.*?)</a>`)
|
||||
var urlRegex = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`<a [^>]*?href="([^">]*?)">(.*?)</a>`)
|
||||
})
|
||||
|
||||
func getMessageBody(htmlText string) string {
|
||||
htmlText = urlRegex.ReplaceAllString(htmlText, "[$2]($1)")
|
||||
htmlText = urlRegex().ReplaceAllString(htmlText, "[$2]($1)")
|
||||
htmlText = strings.ReplaceAll(htmlText, "<br>", "\n")
|
||||
return htmlText
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user