From eacfbc86b5f7ba08c4be1bdf14a3d552aeb1874b Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Sun, 21 Sep 2025 17:39:30 +0200 Subject: [PATCH 1/4] security fix: Command built from user-controlled sources CWE-78 https://cwe.mitre.org/data/definitions/78.html https://owasp.org/www-community/attacks/Command_Injection --- config/config.go | 10 +++++----- main.go | 2 +- web/service/server.go | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/config/config.go b/config/config.go index c9a3e83c..17c9a77f 100644 --- a/config/config.go +++ b/config/config.go @@ -23,11 +23,11 @@ type LogLevel string // Logging level constants const ( - Debug LogLevel = "debug" - Info LogLevel = "info" - Notice LogLevel = "notice" - Warn LogLevel = "warn" - Error LogLevel = "error" + Debug LogLevel = "debug" + Info LogLevel = "info" + Notice LogLevel = "notice" + Warning LogLevel = "warning" + Error LogLevel = "error" ) // GetVersion returns the version string of the 3x-ui application. diff --git a/main.go b/main.go index 119dc4d9..8ab8b13f 100644 --- a/main.go +++ b/main.go @@ -35,7 +35,7 @@ func runWebServer() { logger.InitLogger(logging.INFO) case config.Notice: logger.InitLogger(logging.NOTICE) - case config.Warn: + case config.Warning: logger.InitLogger(logging.WARNING) case config.Error: logger.InitLogger(logging.ERROR) diff --git a/web/service/server.go b/web/service/server.go index 9fe42e2c..5fea423b 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -697,14 +697,39 @@ func (s *ServerService) GetLogs(count string, level string, syslog string) []str var lines []string if syslog == "true" { - cmdArgs := []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", count, "-p", level} - // Run the command - cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) + // Check if running on Windows - journalctl is not available + if runtime.GOOS == "windows" { + return []string{"Syslog is not supported on Windows. Please use application logs instead by unchecking the 'Syslog' option."} + } + + // Validate and sanitize count parameter + countInt, err := strconv.Atoi(count) + if err != nil || countInt < 1 || countInt > 10000 { + return []string{"Invalid count parameter - must be a number between 1 and 10000"} + } + + // Validate level parameter - only allow valid syslog levels + validLevels := map[string]bool{ + "0": true, "emerg": true, + "1": true, "alert": true, + "2": true, "crit": true, + "3": true, "err": true, + "4": true, "warning": true, + "5": true, "notice": true, + "6": true, "info": true, + "7": true, "debug": true, + } + if !validLevels[level] { + return []string{"Invalid level parameter - must be a valid syslog level"} + } + + // Use hardcoded command with validated parameters + cmd := exec.Command("journalctl", "-u", "x-ui", "--no-pager", "-n", strconv.Itoa(countInt), "-p", level) var out bytes.Buffer cmd.Stdout = &out - err := cmd.Run() + err = cmd.Run() if err != nil { - return []string{"Failed to run journalctl command!"} + return []string{"Failed to run journalctl command! Make sure systemd is available and x-ui service is registered."} } lines = strings.Split(out.String(), "\n") } else { From 9f024b9e6a5c5a8d7adbac36fa2f8e38a29455f0 Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Sun, 21 Sep 2025 17:47:01 +0200 Subject: [PATCH 2/4] security fix: Workflow with permissions CWE-275 --- .github/workflows/docker.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 0e460d24..9ec4c870 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,4 +1,7 @@ name: Release 3X-UI for Docker +permissions: + contents: read + packages: write on: workflow_dispatch: push: From e64e6327ef4cfda8f612c98882fe649c02918ac7 Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Sun, 21 Sep 2025 17:52:18 +0200 Subject: [PATCH 3/4] security fix: Uncontrolled data used in path expression --- web/service/server.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/web/service/server.go b/web/service/server.go index 5fea423b..a268a13e 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -1008,7 +1008,19 @@ func (s *ServerService) UpdateGeofile(fileName string) error { {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat", "geoip_RU.dat"}, {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat", "geosite_RU.dat"}, } - + // Strict allowlist check to avoid writing uncontrolled files + if fileName != "" { + isAllowed := false + for _, file := range files { + if fileName == file.FileName { + isAllowed = true + break + } + } + if !isAllowed { + return common.NewErrorf("Invalid geofile name: %s", fileName) + } + } downloadFile := func(url, destPath string) error { resp, err := http.Get(url) if err != nil { From ae79b43cdb1fdcec772e9c411bb81243cae1de0a Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Sun, 21 Sep 2025 17:59:17 +0200 Subject: [PATCH 4/4] security fix: Use of insufficient randomness as the key of a cryptographic algorithm --- util/random/random.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/util/random/random.go b/util/random/random.go index 9610e26c..c746df63 100644 --- a/util/random/random.go +++ b/util/random/random.go @@ -2,7 +2,8 @@ package random import ( - "math/rand" + "crypto/rand" + "math/big" ) var ( @@ -40,12 +41,21 @@ func init() { func Seq(n int) string { runes := make([]rune, n) for i := 0; i < n; i++ { - runes[i] = allSeq[rand.Intn(len(allSeq))] + idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(allSeq)))) + if err != nil { + panic("crypto/rand failed: " + err.Error()) + } + runes[i] = allSeq[idx.Int64()] } return string(runes) } // Num generates a random integer between 0 and n-1. func Num(n int) int { - return rand.Intn(n) + bn := big.NewInt(int64(n)) + r, err := rand.Int(rand.Reader, bn) + if err != nil { + panic("crypto/rand failed: " + err.Error()) + } + return int(r.Int64()) }