From 01d0a21e2a88bfce98fb5c23ec9163de34ef8814 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:17:56 +0800 Subject: [PATCH 1/6] feat: refactor regex usage across multiple files and centralize patterns in a new utility --- agent/app/service/app_utils.go | 5 +- agent/app/service/container.go | 4 +- agent/app/service/database_mysql.go | 4 +- agent/app/service/disk_utils.go | 15 +++-- agent/app/service/recycle_bin.go | 5 +- agent/app/service/runtime.go | 9 ++- agent/app/service/runtime_utils.go | 8 ++- agent/app/service/tensorrt_llm.go | 20 +++--- agent/app/service/website.go | 20 +++--- agent/app/service/website_utils.go | 5 +- agent/init/validator/validator.go | 14 +---- agent/server/server.go | 2 + agent/utils/alert/alert.go | 20 +++--- agent/utils/cloud_storage/client/cos.go | 6 +- agent/utils/common/common.go | 12 +--- agent/utils/docker/compose.go | 15 +++-- agent/utils/firewall/client/firewalld.go | 8 +-- agent/utils/firewall/client/iptables.go | 10 +-- agent/utils/nginx/components/location.go | 9 ++- agent/utils/re/re.go | 78 ++++++++++++++++++++++++ 20 files changed, 165 insertions(+), 104 deletions(-) create mode 100644 agent/utils/re/re.go diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 09c1e5ca7b47..0846981a4a30 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -15,7 +15,6 @@ import ( "path" "path/filepath" "reflect" - "regexp" "strconv" "strings" "time" @@ -40,6 +39,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/files" "github.com/1Panel-dev/1Panel/agent/utils/nginx" "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/1Panel-dev/1Panel/agent/utils/req_helper" "github.com/1Panel-dev/1Panel/agent/utils/xpack" "github.com/compose-spec/compose-go/v2/types" @@ -1827,8 +1827,7 @@ func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig config.CpuQuota = 0 } if memLimit, ok := envs[constant.MemoryLimit]; ok { - re := regexp.MustCompile(`(\d+)([A-Za-z]+)`) - matches := re.FindStringSubmatch(memLimit.(string)) + matches := re.GetRegex(re.NumberAlphaPattern).FindStringSubmatch(memLimit.(string)) if len(matches) == 3 { num, err := strconv.ParseFloat(matches[1], 64) if err == nil { diff --git a/agent/app/service/container.go b/agent/app/service/container.go index 8c1e2897f831..7f41c07ace3a 100644 --- a/agent/app/service/container.go +++ b/agent/app/service/container.go @@ -12,7 +12,6 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "sort" "strconv" "strings" @@ -34,6 +33,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/docker" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/build" "github.com/docker/docker/api/types/container" @@ -1166,7 +1166,7 @@ func (u *ContainerService) DownloadContainerLogs(containerType, container, since errCh := make(chan error) go func() { scanner := bufio.NewScanner(stdout) - var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>`) + var ansiRegex = re.GetRegex(re.AnsiEscapePattern) for scanner.Scan() { line := scanner.Text() cleanLine := ansiRegex.ReplaceAllString(line, "") diff --git a/agent/app/service/database_mysql.go b/agent/app/service/database_mysql.go index b8b4ec10eece..2bfe5cc793f2 100644 --- a/agent/app/service/database_mysql.go +++ b/agent/app/service/database_mysql.go @@ -7,7 +7,6 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "strconv" "strings" "time" @@ -24,6 +23,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/compose" "github.com/1Panel-dev/1Panel/agent/utils/encrypt" "github.com/1Panel-dev/1Panel/agent/utils/mysql" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/1Panel-dev/1Panel/agent/utils/mysql/client" _ "github.com/go-sql-driver/mysql" "github.com/jinzhu/copier" @@ -623,7 +623,7 @@ func updateMyCnf(oldFiles []string, group string, param string, value interface{ isOn := false hasGroup := false hasKey := false - regItem, _ := regexp.Compile(`\[*\]`) + regItem := re.GetRegex(re.MysqlGroupPattern) var newFiles []string i := 0 for _, line := range oldFiles { diff --git a/agent/app/service/disk_utils.go b/agent/app/service/disk_utils.go index c43b95c12d4b..dbc9f1598942 100644 --- a/agent/app/service/disk_utils.go +++ b/agent/app/service/disk_utils.go @@ -3,15 +3,16 @@ package service import ( "bufio" "fmt" - "github.com/1Panel-dev/1Panel/agent/app/dto" - "github.com/1Panel-dev/1Panel/agent/app/dto/response" - "github.com/1Panel-dev/1Panel/agent/buserr" - "github.com/1Panel-dev/1Panel/agent/utils/cmd" "os" "os/exec" - "regexp" "strconv" "strings" + + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/app/dto/response" + "github.com/1Panel-dev/1Panel/agent/buserr" + "github.com/1Panel-dev/1Panel/agent/utils/cmd" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) func organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskInfo { @@ -205,12 +206,10 @@ func getDiskType(rota string) string { return "Unknown" } -var kvRe = regexp.MustCompile(`([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)`) - func parseKeyValuePairs(line string) map[string]string { fields := make(map[string]string) - matches := kvRe.FindAllStringSubmatch(line, -1) + matches := re.GetRegex(re.DiskKeyValuePattern).FindAllStringSubmatch(line, -1) for _, m := range matches { key := m[1] raw := m[2] diff --git a/agent/app/service/recycle_bin.go b/agent/app/service/recycle_bin.go index e1c8882619ff..efcbb2e23d76 100644 --- a/agent/app/service/recycle_bin.go +++ b/agent/app/service/recycle_bin.go @@ -5,7 +5,6 @@ import ( "math" "os" "path" - "regexp" "strconv" "strings" "time" @@ -17,6 +16,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/shirou/gopsutil/v4/disk" ) @@ -184,8 +184,7 @@ func createClashDir(clashDir string) error { } func getRecycleBinDTOFromName(filename string) (*response.RecycleBinDTO, error) { - r := regexp.MustCompile(`_1p_file_1p_(.+)_p_(\d+)_(\d+)`) - matches := r.FindStringSubmatch(filename) + matches := re.GetRegex(re.RecycleBinFilePattern).FindStringSubmatch(filename) if len(matches) != 4 { return nil, fmt.Errorf("invalid filename format") } diff --git a/agent/app/service/runtime.go b/agent/app/service/runtime.go index 1b9a36e942ab..9f07e0d07c5a 100644 --- a/agent/app/service/runtime.go +++ b/agent/app/service/runtime.go @@ -36,6 +36,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/docker" "github.com/1Panel-dev/1Panel/agent/utils/env" "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/pkg/errors" "github.com/subosito/gotenv" ) @@ -245,8 +246,10 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt runtimeDTO.Params[k] = v if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") { if strings.Contains(k, "CONTAINER_PORT") { - r := regexp.MustCompile(`_(\d+)$`) - matches := r.FindStringSubmatch(k) + matches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k) + if len(matches) < 2 { + continue + } containerPort, err := strconv.Atoi(v) if err != nil { continue @@ -828,7 +831,7 @@ func (r *RuntimeService) GetPHPConfig(id uint) (*response.PHPConfig, error) { if strings.HasPrefix(line, ";") { continue } - matches := regexp.MustCompile(`^\s*([a-z_]+)\s*=\s*(.*)$`).FindStringSubmatch(line) + matches := re.GetRegex(re.PhpAssignmentPattern).FindStringSubmatch(line) if len(matches) == 3 { params[matches[1]] = matches[2] } diff --git a/agent/app/service/runtime_utils.go b/agent/app/service/runtime_utils.go index e1d19a39cc7c..50502b6ab4ca 100644 --- a/agent/app/service/runtime_utils.go +++ b/agent/app/service/runtime_utils.go @@ -12,7 +12,6 @@ import ( "os/exec" "path" "path/filepath" - "regexp" "strconv" "strings" "time" @@ -34,6 +33,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/compose" "github.com/1Panel-dev/1Panel/agent/utils/docker" "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/pkg/errors" "github.com/subosito/gotenv" "gopkg.in/yaml.v3" @@ -979,8 +979,10 @@ func handleRuntimeDTO(res *response.RuntimeDTO, runtime model.Runtime) error { for k, v := range envs { if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") { if strings.Contains(k, "CONTAINER_PORT") { - r := regexp.MustCompile(`_(\d+)$`) - matches := r.FindStringSubmatch(k) + matches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k) + if len(matches) < 2 { + return fmt.Errorf("invalid container port key: %s", k) + } containerPort, err := strconv.Atoi(v) if err != nil { return err diff --git a/agent/app/service/tensorrt_llm.go b/agent/app/service/tensorrt_llm.go index ac22d15f1aa8..a759b6baa187 100644 --- a/agent/app/service/tensorrt_llm.go +++ b/agent/app/service/tensorrt_llm.go @@ -2,6 +2,13 @@ package service import ( "fmt" + "path" + "strconv" + "strings" + + "github.com/subosito/gotenv" + "gopkg.in/yaml.v3" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" "github.com/1Panel-dev/1Panel/agent/app/dto/response" "github.com/1Panel-dev/1Panel/agent/app/model" @@ -13,12 +20,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/compose" "github.com/1Panel-dev/1Panel/agent/utils/docker" "github.com/1Panel-dev/1Panel/agent/utils/files" - "github.com/subosito/gotenv" - "gopkg.in/yaml.v3" - "path" - "regexp" - "strconv" - "strings" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) type TensorRTLLMService struct{} @@ -57,8 +59,10 @@ func (t TensorRTLLMService) Page(req request.TensorRTLLMSearch) response.TensorR for k, v := range envs { if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") { if strings.Contains(k, "CONTAINER_PORT") { - r := regexp.MustCompile(`_(\d+)$`) - matches := r.FindStringSubmatch(k) + matches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k) + if len(matches) < 2 { + continue + } containerPort, err := strconv.Atoi(v) if err != nil { continue diff --git a/agent/app/service/website.go b/agent/app/service/website.go index 29ae7c7e8ddf..f7d1a6b2b56c 100644 --- a/agent/app/service/website.go +++ b/agent/app/service/website.go @@ -14,7 +14,6 @@ import ( "os" "path" "reflect" - "regexp" "sort" "strconv" "strings" @@ -46,6 +45,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/nginx" "github.com/1Panel-dev/1Panel/agent/utils/nginx/components" "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" + "github.com/1Panel-dev/1Panel/agent/utils/re" "golang.org/x/crypto/bcrypt" ) @@ -1859,27 +1859,24 @@ func (w WebsiteService) GetProxyCache(id uint) (res response.NginxProxyCache, er if len(params) == 0 { return } - zoneRegexp := regexp.MustCompile(`keys_zone=proxy_cache_zone_of_[\w.]+:(\d+)([kmgt]?)`) - sizeRegexp := regexp.MustCompile(`max_size=([0-9.]+)([kmgt]?)`) - inactiveRegexp := regexp.MustCompile(`inactive=(\d+)([smhd])`) for _, param := range params { - if match, _ := regexp.MatchString(`keys_zone=proxy_cache_zone_of_[\w.]+:\d+[kmgt]?`, param); match { - matches := zoneRegexp.FindStringSubmatch(param) + if re.GetRegex(re.ProxyCacheZonePattern).MatchString(param) { + matches := re.GetRegex(re.ProxyCacheZonePattern).FindStringSubmatch(param) if len(matches) > 0 { res.ShareCache, _ = strconv.Atoi(matches[1]) res.ShareCacheUnit = matches[2] } } - if match, _ := regexp.MatchString(`max_size=\d+(\.\d+)?[kmgt]?`, param); match { - matches := sizeRegexp.FindStringSubmatch(param) + if re.GetRegex(re.ProxyCacheMaxSizeValidationPattern).MatchString(param) { + matches := re.GetRegex(re.ProxyCacheMaxSizePattern).FindStringSubmatch(param) if len(matches) > 0 { res.CacheLimit, _ = strconv.ParseFloat(matches[1], 64) res.CacheLimitUnit = matches[2] } } - if match, _ := regexp.MatchString(`inactive=\d+[smhd]`, param); match { - matches := inactiveRegexp.FindStringSubmatch(param) + if re.GetRegex(re.ProxyCacheInactivePattern).MatchString(param) { + matches := re.GetRegex(re.ProxyCacheInactivePattern).FindStringSubmatch(param) if len(matches) > 0 { res.CacheExpire, _ = strconv.Atoi(matches[1]) res.CacheExpireUnit = matches[2] @@ -2598,8 +2595,7 @@ func (w WebsiteService) GetAntiLeech(id uint) (*response.NginxAntiLeechRes, erro } if lDir.GetName() == "expires" { res.Cache = true - re := regexp.MustCompile(`^(\d+)(\w+)$`) - matches := re.FindStringSubmatch(lDir.GetParameters()[0]) + matches := re.GetRegex(re.NumberWordPattern).FindStringSubmatch(lDir.GetParameters()[0]) if matches == nil { continue } diff --git a/agent/app/service/website_utils.go b/agent/app/service/website_utils.go index e7ab54059b2a..b92c18e353e5 100644 --- a/agent/app/service/website_utils.go +++ b/agent/app/service/website_utils.go @@ -9,7 +9,6 @@ import ( "os" "path" "path/filepath" - "regexp" "strconv" "strings" "syscall" @@ -34,6 +33,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/files" "github.com/1Panel-dev/1Panel/agent/utils/nginx" "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/pkg/errors" "gorm.io/gorm" ) @@ -1538,8 +1538,7 @@ func getServer(website model.Website) (*components.Server, error) { func parseTimeString(input string) (int, string, error) { input = strings.TrimSpace(input) - re := regexp.MustCompile(`^(\d+)([smhdw]?)$`) - matches := re.FindStringSubmatch(input) + matches := re.GetRegex(re.DurationWithOptionalUnitPattern).FindStringSubmatch(input) if len(matches) < 2 { return 0, "", fmt.Errorf("invalid time format: %s", input) diff --git a/agent/init/validator/validator.go b/agent/init/validator/validator.go index bf75f9eff1c6..6a58af5824cb 100644 --- a/agent/init/validator/validator.go +++ b/agent/init/validator/validator.go @@ -1,10 +1,10 @@ package validator import ( - "regexp" "unicode" "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/go-playground/validator/v10" ) @@ -25,20 +25,12 @@ func Init() { func checkNamePattern(fl validator.FieldLevel) bool { value := fl.Field().String() - result, err := regexp.MatchString("^[a-zA-Z\u4e00-\u9fa5]{1}[a-zA-Z0-9_\u4e00-\u9fa5]{0,30}$", value) - if err != nil { - global.LOG.Errorf("regexp matchString failed, %v", err) - } - return result + return re.GetRegex(re.ValidatorNamePattern).MatchString(value) } func checkIpPattern(fl validator.FieldLevel) bool { value := fl.Field().String() - result, err := regexp.MatchString(`^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$`, value) - if err != nil { - global.LOG.Errorf("regexp check ip matchString failed, %v", err) - } - return result + return re.GetRegex(re.ValidatorIPPattern).MatchString(value) } func checkPasswordPattern(fl validator.FieldLevel) bool { diff --git a/agent/server/server.go b/agent/server/server.go index 5487a659487a..caac3ec08ad4 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -26,10 +26,12 @@ import ( "github.com/1Panel-dev/1Panel/agent/init/validator" "github.com/1Panel-dev/1Panel/agent/init/viper" "github.com/1Panel-dev/1Panel/agent/utils/encrypt" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/gin-gonic/gin" ) func Start() { + re.InitRegex() viper.Init() dir.Init() log.Init() diff --git a/agent/utils/alert/alert.go b/agent/utils/alert/alert.go index 43850204b1e1..74b229c22c7f 100644 --- a/agent/utils/alert/alert.go +++ b/agent/utils/alert/alert.go @@ -4,6 +4,15 @@ import ( "encoding/json" "errors" "fmt" + "net/http" + "os" + "os/exec" + "strings" + "sync" + "time" + + "github.com/jinzhu/copier" + "github.com/1Panel-dev/1Panel/agent/app/dto" "github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/app/repo" @@ -12,14 +21,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/i18n" "github.com/1Panel-dev/1Panel/agent/utils/email" - "github.com/jinzhu/copier" - "net/http" - "os" - "os/exec" - "regexp" - "strings" - "sync" - "time" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) var cronJobAlertTypes = []string{"shell", "app", "website", "database", "directory", "log", "snapshot", "curl", "cutWebsiteLog", "clean", "ntp"} @@ -424,7 +426,7 @@ func FindRecentSuccessLoginNotInWhitelist(minutes int, whitelist []string) ([]st whitelistMap[ip] = struct{}{} } - ipRegex := regexp.MustCompile(`from\s+([0-9.]+)\s+port\s+(\d+)`) + ipRegex := re.GetRegex(re.AlertIPPattern) for _, line := range lines { line = strings.TrimSpace(line) diff --git a/agent/utils/cloud_storage/client/cos.go b/agent/utils/cloud_storage/client/cos.go index 7a4f996e74b4..9ee46f82082a 100644 --- a/agent/utils/cloud_storage/client/cos.go +++ b/agent/utils/cloud_storage/client/cos.go @@ -6,9 +6,10 @@ import ( "net/http" "net/url" "os" - "regexp" cosSDK "github.com/tencentyun/cos-go-sdk-v5" + + "github.com/1Panel-dev/1Panel/agent/utils/re" ) type cosClient struct { @@ -30,8 +31,7 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) { endpointType := "cos" if len(endpoint) != 0 { - re := regexp.MustCompile(`.*cos-dualstack\..*`) - if re.MatchString(endpoint) { + if re.GetRegex(re.CosDualStackPattern).MatchString(endpoint) { endpointType = "cos-dualstack" } } diff --git a/agent/utils/common/common.go b/agent/utils/common/common.go index 23063185dba6..0fab2a42f906 100644 --- a/agent/utils/common/common.go +++ b/agent/utils/common/common.go @@ -8,7 +8,6 @@ import ( "net" "os/exec" "reflect" - "regexp" "sort" "strconv" "strings" @@ -19,6 +18,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/utils/cmd" + "github.com/1Panel-dev/1Panel/agent/utils/re" "golang.org/x/net/idna" ) @@ -303,12 +303,7 @@ func LoadTimeZoneByCmd() string { } func IsValidDomain(domain string) bool { - pattern := `^([\w\p{Han}\-\*]{1,100}\.){1,10}([\w\p{Han}\-]{1,24}|[\w\p{Han}\-]{1,24}\.[\w\p{Han}\-]{1,24})(:\d{1,5})?$` - match, err := regexp.MatchString(pattern, domain) - if err != nil { - return false - } - return match + return re.GetRegex(re.DomainPattern).MatchString(domain) } func ContainsChinese(text string) bool { @@ -398,8 +393,7 @@ func HandleIPList(content string) ([]string, error) { } func GetSystemVersion(versionString string) string { - re := regexp.MustCompile(`v(\d+\.\d+\.\d+)`) - match := re.FindStringSubmatch(versionString) + match := re.GetRegex(re.VersionPattern).FindStringSubmatch(versionString) if len(match) > 1 { return match[1] } diff --git a/agent/utils/docker/compose.go b/agent/utils/docker/compose.go index 2f12d36eb13e..6cab5e5d04bb 100644 --- a/agent/utils/docker/compose.go +++ b/agent/utils/docker/compose.go @@ -5,14 +5,16 @@ import ( "bytes" "context" "fmt" + "path" + "strings" + "github.com/compose-spec/compose-go/v2/loader" "github.com/compose-spec/compose-go/v2/types" "github.com/docker/compose/v2/pkg/api" "github.com/joho/godotenv" "gopkg.in/yaml.v3" - "path" - "regexp" - "strings" + + "github.com/1Panel-dev/1Panel/agent/utils/re" ) type ComposeService struct { @@ -35,8 +37,7 @@ func GetComposeProject(projectName, workDir string, yml []byte, env []byte, skip Environment: envMap, } projectName = strings.ToLower(projectName) - reg, _ := regexp.Compile(`[^a-z0-9_-]+`) - projectName = reg.ReplaceAllString(projectName, "") + projectName = re.GetRegex(re.ComposeDisallowedCharsPattern).ReplaceAllString(projectName, "") project, err := loader.LoadWithContext(context.Background(), details, func(options *loader.Options) { options.SetProjectName(projectName, true) options.ResolvePaths = true @@ -140,9 +141,7 @@ func loadEnvFile(env []byte) (map[string]string, error) { } func replaceEnvVars(input string, envVars map[string]string) string { - re := regexp.MustCompile(`\$\{([^}]+)\}`) - - return re.ReplaceAllStringFunc(input, func(match string) string { + return re.GetRegex(re.ComposeEnvVarPattern).ReplaceAllStringFunc(input, func(match string) string { varName := match[2 : len(match)-1] if value, exists := envVars[varName]; exists { return value diff --git a/agent/utils/firewall/client/firewalld.go b/agent/utils/firewall/client/firewalld.go index 8d510ea40f47..22725b8a90c8 100644 --- a/agent/utils/firewall/client/firewalld.go +++ b/agent/utils/firewall/client/firewalld.go @@ -2,7 +2,6 @@ package client import ( "fmt" - "regexp" "strings" "sync" @@ -10,10 +9,9 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/controller" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) -var ForwardListRegex = regexp.MustCompile(`^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$`) - type Firewall struct{} func NewFirewalld() (*Firewall, error) { @@ -124,8 +122,8 @@ func (f *Firewall) ListForward() ([]FireInfo, error) { line = strings.TrimFunc(line, func(r rune) bool { return r <= 32 }) - if ForwardListRegex.MatchString(line) { - match := ForwardListRegex.FindStringSubmatch(line) + if re.GetRegex(re.FirewalldForwardPattern).MatchString(line) { + match := re.GetRegex(re.FirewalldForwardPattern).FindStringSubmatch(line) if len(match) < 4 { continue } diff --git a/agent/utils/firewall/client/iptables.go b/agent/utils/firewall/client/iptables.go index dc3610a0b301..8638b4052fd3 100644 --- a/agent/utils/firewall/client/iptables.go +++ b/agent/utils/firewall/client/iptables.go @@ -2,13 +2,13 @@ package client import ( "fmt" - "regexp" "strings" "time" "github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/cmd" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) const ( @@ -24,10 +24,6 @@ const ( const NatChain = "1PANEL" -var ( - natListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`) -) - type Iptables struct { CmdStr string } @@ -97,8 +93,8 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) { line = strings.TrimFunc(line, func(r rune) bool { return r <= 32 }) - if natListRegex.MatchString(line) { - match := natListRegex.FindStringSubmatch(line) + if re.GetRegex(re.IptablesNatListPattern).MatchString(line) { + match := re.GetRegex(re.IptablesNatListPattern).FindStringSubmatch(line) if !strings.Contains(match[13], ":") { match[13] = fmt.Sprintf(":%s", match[13]) } diff --git a/agent/utils/nginx/components/location.go b/agent/utils/nginx/components/location.go index ab81a543a703..a915f8c94765 100644 --- a/agent/utils/nginx/components/location.go +++ b/agent/utils/nginx/components/location.go @@ -2,9 +2,10 @@ package components import ( "fmt" - "regexp" "strconv" "strings" + + "github.com/1Panel-dev/1Panel/agent/utils/re" ) type Location struct { @@ -60,8 +61,7 @@ func NewLocation(directive IDirective) *Location { dirs := dir.GetBlock().GetDirectives() for _, di := range dirs { if di.GetName() == "expires" { - re := regexp.MustCompile(`^(\d+)(\w+)$`) - matches := re.FindStringSubmatch(di.GetParameters()[0]) + matches := re.GetRegex(re.NumberWordPattern).FindStringSubmatch(di.GetParameters()[0]) if matches == nil { continue } @@ -80,8 +80,7 @@ func NewLocation(directive IDirective) *Location { } case "proxy_cache_valid": timeParam := params[len(params)-1] - re := regexp.MustCompile(`^(\d+)(\w+)$`) - matches := re.FindStringSubmatch(timeParam) + matches := re.GetRegex(re.NumberWordPattern).FindStringSubmatch(timeParam) if matches == nil { continue } diff --git a/agent/utils/re/re.go b/agent/utils/re/re.go new file mode 100644 index 000000000000..24fb043d6f09 --- /dev/null +++ b/agent/utils/re/re.go @@ -0,0 +1,78 @@ +package re + +import ( + "fmt" + "regexp" +) + +const ( + NumberAlphaPattern = `(\d+)([A-Za-z]+)` + ComposeDisallowedCharsPattern = `[^a-z0-9_-]+` + ComposeEnvVarPattern = `\$\{([^}]+)\}` + DiskKeyValuePattern = `([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)` + FirewalldForwardPattern = `^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$` + IptablesNatListPattern = `^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$` + ValidatorNamePattern = `^[a-zA-Z\u4e00-\u9fa5]{1}[a-zA-Z0-9_\u4e00-\u9fa5]{0,30}$` + ValidatorIPPattern = `^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$` + DomainPattern = `^([\w\p{Han}\-\*]{1,100}\.){1,10}([\w\p{Han}\-]{1,24}|[\w\p{Han}\-]{1,24}\.[\w\p{Han}\-]{1,24})(:\d{1,5})?$` + ProxyCacheZonePattern = `keys_zone=proxy_cache_zone_of_[\w.]+:(\d+)([kmgt]?)` + ProxyCacheMaxSizePattern = `max_size=([0-9.]+)([kmgt]?)` + ProxyCacheMaxSizeValidationPattern = `max_size=\d+(\.\d+)?[kmgt]?` + ProxyCacheInactivePattern = `inactive=(\d+)([smhd])` + NumberWordPattern = `^(\d+)(\w+)$` + TrailingDigitsPattern = `_(\d+)$` + AlertIPPattern = `from\s+([0-9.]+)\s+port\s+(\d+)` + CosDualStackPattern = `.*cos-dualstack\..*` + VersionPattern = `v(\d+\.\d+\.\d+)` + PhpAssignmentPattern = `^\s*([a-z_]+)\s*=\s*(.*)$` + DurationWithOptionalUnitPattern = `^(\d+)([smhdw]?)$` + MysqlGroupPattern = `\[*\]` + AnsiEscapePattern = `\x1b\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>` + RecycleBinFilePattern = `_1p_file_1p_(.+)_p_(\d+)_(\d+)` +) + +var regexMap = make(map[string]*regexp.Regexp) + +// InitRegex compiles all regex patterns and stores them in the map. +// This function should be called once at program startup. +func InitRegex() { + patterns := []string{ + NumberAlphaPattern, + ComposeDisallowedCharsPattern, + ComposeEnvVarPattern, + DiskKeyValuePattern, + FirewalldForwardPattern, + IptablesNatListPattern, + ValidatorNamePattern, + ValidatorIPPattern, + DomainPattern, + ProxyCacheZonePattern, + ProxyCacheMaxSizePattern, + ProxyCacheMaxSizeValidationPattern, + ProxyCacheInactivePattern, + NumberWordPattern, + TrailingDigitsPattern, + AlertIPPattern, + CosDualStackPattern, + VersionPattern, + PhpAssignmentPattern, + DurationWithOptionalUnitPattern, + MysqlGroupPattern, + AnsiEscapePattern, + RecycleBinFilePattern, + } + + for _, pattern := range patterns { + regexMap[pattern] = regexp.MustCompile(pattern) + } +} + +// GetRegex retrieves a compiled regex by its pattern string. +// Panics if the pattern is not found in the map. +func GetRegex(pattern string) *regexp.Regexp { + regex, exists := regexMap[pattern] + if !exists { + panic(fmt.Sprintf("regex pattern not found: %s", pattern)) + } + return regex +} From 1b558e39f6268554c27e176446e4a39491d5e6fa Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:20:30 +0800 Subject: [PATCH 2/6] fix: escape backslashes in regex patterns for proper compilation --- agent/utils/re/re.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/utils/re/re.go b/agent/utils/re/re.go index 24fb043d6f09..7233bee2504e 100644 --- a/agent/utils/re/re.go +++ b/agent/utils/re/re.go @@ -12,7 +12,7 @@ const ( DiskKeyValuePattern = `([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)` FirewalldForwardPattern = `^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$` IptablesNatListPattern = `^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$` - ValidatorNamePattern = `^[a-zA-Z\u4e00-\u9fa5]{1}[a-zA-Z0-9_\u4e00-\u9fa5]{0,30}$` + ValidatorNamePattern = "^[a-zA-Z\\u4e00-\\u9fa5]{1}[a-zA-Z0-9_\\u4e00-\\u9fa5]{0,30}$" ValidatorIPPattern = `^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$` DomainPattern = `^([\w\p{Han}\-\*]{1,100}\.){1,10}([\w\p{Han}\-]{1,24}|[\w\p{Han}\-]{1,24}\.[\w\p{Han}\-]{1,24})(:\d{1,5})?$` ProxyCacheZonePattern = `keys_zone=proxy_cache_zone_of_[\w.]+:(\d+)([kmgt]?)` @@ -27,7 +27,7 @@ const ( PhpAssignmentPattern = `^\s*([a-z_]+)\s*=\s*(.*)$` DurationWithOptionalUnitPattern = `^(\d+)([smhdw]?)$` MysqlGroupPattern = `\[*\]` - AnsiEscapePattern = `\x1b\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>` + AnsiEscapePattern = "\\x1b\\[[0-9;?]*[A-Za-z]|\\x1b=|\\x1b>" RecycleBinFilePattern = `_1p_file_1p_(.+)_p_(\d+)_(\d+)` ) From 6daff09277fbaa3e6060a914dcb60d1f2e862d4b Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:23:17 +0800 Subject: [PATCH 3/6] fix: update regex patterns for ValidatorName and AnsiEscape to use raw string literals --- agent/utils/re/re.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/utils/re/re.go b/agent/utils/re/re.go index 7233bee2504e..43171cdb0cf6 100644 --- a/agent/utils/re/re.go +++ b/agent/utils/re/re.go @@ -12,7 +12,7 @@ const ( DiskKeyValuePattern = `([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)` FirewalldForwardPattern = `^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$` IptablesNatListPattern = `^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$` - ValidatorNamePattern = "^[a-zA-Z\\u4e00-\\u9fa5]{1}[a-zA-Z0-9_\\u4e00-\\u9fa5]{0,30}$" + ValidatorNamePattern = `^[a-zA-Z\p{Han}]{1}[a-zA-Z0-9_\p{Han}]{0,30}$` ValidatorIPPattern = `^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$` DomainPattern = `^([\w\p{Han}\-\*]{1,100}\.){1,10}([\w\p{Han}\-]{1,24}|[\w\p{Han}\-]{1,24}\.[\w\p{Han}\-]{1,24})(:\d{1,5})?$` ProxyCacheZonePattern = `keys_zone=proxy_cache_zone_of_[\w.]+:(\d+)([kmgt]?)` @@ -27,7 +27,7 @@ const ( PhpAssignmentPattern = `^\s*([a-z_]+)\s*=\s*(.*)$` DurationWithOptionalUnitPattern = `^(\d+)([smhdw]?)$` MysqlGroupPattern = `\[*\]` - AnsiEscapePattern = "\\x1b\\[[0-9;?]*[A-Za-z]|\\x1b=|\\x1b>" + AnsiEscapePattern = "\x1b\\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>" RecycleBinFilePattern = `_1p_file_1p_(.+)_p_(\d+)_(\d+)` ) From 926d0a18dbe7adc9656175f621c80e5e985e7be3 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:48:49 +0800 Subject: [PATCH 4/6] refactor: rename InitRegex to Init for clarity and consistency --- agent/server/server.go | 5 +++-- agent/utils/re/re.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/agent/server/server.go b/agent/server/server.go index 030d975c971c..80df10e1a0f8 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -4,11 +4,12 @@ import ( "crypto/tls" "crypto/x509" "fmt" - "github.com/gin-gonic/gin" "net" "net/http" "os" + "github.com/gin-gonic/gin" + "github.com/1Panel-dev/1Panel/agent/app/repo" "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/cron" @@ -31,7 +32,7 @@ import ( ) func Start() { - re.InitRegex() + re.Init() viper.Init() dir.Init() log.Init() diff --git a/agent/utils/re/re.go b/agent/utils/re/re.go index 43171cdb0cf6..d2532440468e 100644 --- a/agent/utils/re/re.go +++ b/agent/utils/re/re.go @@ -35,7 +35,7 @@ var regexMap = make(map[string]*regexp.Regexp) // InitRegex compiles all regex patterns and stores them in the map. // This function should be called once at program startup. -func InitRegex() { +func Init() { patterns := []string{ NumberAlphaPattern, ComposeDisallowedCharsPattern, From 784e85e5c1ddbeef388f24b21ab56381928123e0 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Tue, 11 Nov 2025 10:43:05 +0800 Subject: [PATCH 5/6] refactor: remove unused regex and update natListRegex initialization in iptables --- agent/utils/firewall/client/iptables/common.go | 5 ----- agent/utils/firewall/client/iptables/forward.go | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/agent/utils/firewall/client/iptables/common.go b/agent/utils/firewall/client/iptables/common.go index 742c0f1cc1d7..b96fada1a45e 100644 --- a/agent/utils/firewall/client/iptables/common.go +++ b/agent/utils/firewall/client/iptables/common.go @@ -2,7 +2,6 @@ package iptables import ( "fmt" - "regexp" "strconv" "strings" "time" @@ -31,10 +30,6 @@ const ( AllowSSH = "-p tcp --dport ssh -j ACCEPT" ) -var ( - natListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`) -) - const ( ACCEPT = "ACCEPT" DROP = "DROP" diff --git a/agent/utils/firewall/client/iptables/forward.go b/agent/utils/firewall/client/iptables/forward.go index bfb80ca0a31f..9925e5e80c0f 100644 --- a/agent/utils/firewall/client/iptables/forward.go +++ b/agent/utils/firewall/client/iptables/forward.go @@ -3,6 +3,8 @@ package iptables import ( "fmt" "strings" + + "github.com/1Panel-dev/1Panel/agent/utils/re" ) func AddForward(protocol, srcPort, dest, destPort, iface string, save bool) error { @@ -69,7 +71,7 @@ func ListForward(chain ...string) ([]IptablesNatInfo, error) { if err != nil { return nil, err } - + natListRegex := re.GetRegex(re.IptablesNatListPattern) var forwardList []IptablesNatInfo for _, line := range strings.Split(stdout, "\n") { line = strings.TrimFunc(line, func(r rune) bool { From 611fb01b60f3117012f31ea1b6634a5388043777 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:00:01 +0800 Subject: [PATCH 6/6] feat: add regex registration for iptables patterns and refactor regex usage in firewall client --- agent/init/firewall/firwall.go | 3 +++ agent/utils/firewall/client/iptables.go | 10 +++------- agent/utils/firewall/client/iptables/common.go | 5 +++++ agent/utils/re/re.go | 6 ++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/agent/init/firewall/firwall.go b/agent/init/firewall/firwall.go index e1d1ab9e9d51..59981b6eae39 100644 --- a/agent/init/firewall/firwall.go +++ b/agent/init/firewall/firwall.go @@ -3,6 +3,7 @@ package firewall import ( "github.com/1Panel-dev/1Panel/agent/utils/firewall" "github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) func Init() { @@ -10,6 +11,8 @@ func Init() { if err != nil { return } + re.RegisterRegex(iptables.Chian1PanelBasicPortPattern) + re.RegisterRegex(iptables.Chain1PanelBasicAddressPattern) clientName := client.Name() if clientName == "ufw" || clientName == "iptables" { _ = iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelForward, iptables.ForwardFileName) diff --git a/agent/utils/firewall/client/iptables.go b/agent/utils/firewall/client/iptables.go index cb1718a88754..a2e7b82983a7 100644 --- a/agent/utils/firewall/client/iptables.go +++ b/agent/utils/firewall/client/iptables.go @@ -2,7 +2,6 @@ package client import ( "fmt" - "regexp" "strconv" "strings" @@ -10,11 +9,9 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) -var portRuleRegex = regexp.MustCompile(`-A\s+INPUT\s+-p\s+(\w+)(?:\s+-m\s+\w+)*\s+--dport\s+(\d+(?::\d+)?)\s+-j\s+(\w+)`) -var addressRuleRegex = regexp.MustCompile(`-A\s+(INPUT|OUTPUT)\s+-s\s+(\S+)\s+-j\s+(\w+)`) - type Iptables struct{} func NewIptables() (*Iptables, error) { @@ -70,7 +67,7 @@ func (i *Iptables) ListPort() ([]FireInfo, error) { var datas []FireInfo lines := strings.Split(stdout, "\n") - chainPortRegex := regexp.MustCompile(fmt.Sprintf(`-A\s+%s\s+(?:-s\s+(\S+)\s+)?-p\s+(\w+)(?:\s+-m\s+\w+)*\s+--dport\s+(\d+(?::\d+)?)\s+-j\s+(\w+)`, iptables.Chain1PanelBasic)) + chainPortRegex := re.GetRegex(iptables.Chian1PanelBasicPortPattern) for _, line := range lines { line = strings.TrimSpace(line) if !strings.HasPrefix(line, fmt.Sprintf("-A %s", iptables.Chain1PanelBasic)) { @@ -111,8 +108,7 @@ func (i *Iptables) ListAddress() ([]FireInfo, error) { lines := strings.Split(stdout, "\n") addressMap := make(map[string]FireInfo) - chainAddressRegex := regexp.MustCompile(fmt.Sprintf(`-A\s+%s\s+(?:-s\s+(\S+)|(?:-d\s+(\S+)))?\s+-j\s+(\w+)`, iptables.Chain1PanelBasic)) - + chainAddressRegex := re.GetRegex(iptables.Chain1PanelBasicAddressPattern) for _, line := range lines { line = strings.TrimSpace(line) if !strings.HasPrefix(line, fmt.Sprintf("-A %s", iptables.Chain1PanelBasic)) { diff --git a/agent/utils/firewall/client/iptables/common.go b/agent/utils/firewall/client/iptables/common.go index b96fada1a45e..f932ea1a1af9 100644 --- a/agent/utils/firewall/client/iptables/common.go +++ b/agent/utils/firewall/client/iptables/common.go @@ -42,6 +42,11 @@ const ( NatTab = "nat" ) +var ( + Chain1PanelBasicAddressPattern = fmt.Sprintf(`-A\s+%s\s+(?:-s\s+(\S+)|(?:-d\s+(\S+)))?\s+-j\s+(\w+)`, Chain1PanelBasic) + Chian1PanelBasicPortPattern = fmt.Sprintf(`-A\s+%s\s+(?:-s\s+(\S+)\s+)?-p\s+(\w+)(?:\s+-m\s+\w+)*\s+--dport\s+(\d+(?::\d+)?)\s+-j\s+(\w+)`, Chain1PanelBasic) +) + func RunWithStd(tab, rule string) (string, error) { cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(20*time.Second)) stdout, err := cmdMgr.RunWithStdoutBashCf("%s iptables -t %s %s", cmd.SudoHandleCmd(), tab, rule) diff --git a/agent/utils/re/re.go b/agent/utils/re/re.go index d2532440468e..be5540b5e9ab 100644 --- a/agent/utils/re/re.go +++ b/agent/utils/re/re.go @@ -76,3 +76,9 @@ func GetRegex(pattern string) *regexp.Regexp { } return regex } + +// RegisterRegex registers a regex pattern and stores it in the map. +// This function should be called once at program startup. +func RegisterRegex(pattern string) { + regexMap[pattern] = regexp.MustCompile(pattern) +}