From 5d74eb4e16f08a690cd1713349ff8ffaefc7fd9e Mon Sep 17 00:00:00 2001 From: GeMing <1328922121@qq.com> Date: Tue, 11 Feb 2025 20:59:47 +0800 Subject: [PATCH 1/2] chore: optimize --- app/common/middleware/api_debug_logger.go | 19 ++++--- .../middleware/recorder/log_recorder.go | 54 +++++++++++++++---- cmd/server/server.go | 2 +- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/app/common/middleware/api_debug_logger.go b/app/common/middleware/api_debug_logger.go index d677ed7..108f175 100644 --- a/app/common/middleware/api_debug_logger.go +++ b/app/common/middleware/api_debug_logger.go @@ -1,12 +1,11 @@ package middleware import ( - "bytes" "fmt" "github.com/MarchGe/go-admin-server/app/common/constant" + "github.com/MarchGe/go-admin-server/app/common/middleware/recorder" "github.com/gin-gonic/gin" "github.com/gobwas/glob" - "io" "log/slog" "path" "time" @@ -19,30 +18,34 @@ func initDebugPatterns(contextPath string) { contextPath + constant.Swagger + "/**", contextPath + "/terminal/ws", contextPath + "/terminal/ws/ssh/*", - contextPath + "/devops/app/upload", - contextPath + "/devops/explorer/upload", } } func ApiDebugLogger() gin.HandlerFunc { return func(c *gin.Context) { if ignoredDebug(c.Request.URL.Path) { + slog.Debug("==> Request info: ", slog.String("url", c.Request.URL.String())) c.Next() return } start := time.Now() - requestBodyBytes, _ := io.ReadAll(c.Request.Body) - c.Request.Body = io.NopCloser(bytes.NewReader(requestBodyBytes)) + requestBodyBytes, bodyIgnored := recorder.GetBodyContent(c) c.Next() end := time.Now() delay := end.Sub(start) - slog.Debug("RestApiInOutParameters", + var bodyLogAttr slog.Attr + if bodyIgnored { + bodyLogAttr = slog.Bool("bodyIgnored", true) + } else { + bodyLogAttr = slog.String("requestBody", string(requestBodyBytes)) + } + slog.Debug("==> Request info: ", slog.String("requestId", c.GetString(constant.RequestId)), slog.String("clientIp", c.ClientIP()), slog.String("method", c.Request.Method), slog.String("path", c.Request.URL.Path), slog.Any("query", c.Request.URL.Query()), - slog.String("requestBody", string(requestBodyBytes)), + bodyLogAttr, slog.Duration("duration", delay), ) } diff --git a/app/common/middleware/recorder/log_recorder.go b/app/common/middleware/recorder/log_recorder.go index d885aad..a36635a 100644 --- a/app/common/middleware/recorder/log_recorder.go +++ b/app/common/middleware/recorder/log_recorder.go @@ -13,10 +13,13 @@ import ( "github.com/gin-gonic/gin" "io" "log/slog" + "strconv" "strings" "time" ) +var logContentExceedLimit = 1024 + func RecordLoginLog(c *gin.Context, userId int64) { if !config.GetConfig().Log.LoginLog { return @@ -86,12 +89,23 @@ func RecordOpLog(opTarget string, v ...any) gin.HandlerFunc { log.Body = "[private]" } else { log.Query = c.Request.URL.Query().Encode() - if len(log.Query) > 255 { + if len(log.Query) > logContentExceedLimit { log.Query = "[too long ignored]" } - log.Body = getBodyParams(c) - if len(log.Body) > 500 { + contentLengthStr := c.GetHeader("Content-Length") + var contentLength int + if contentLengthStr != "" { + contentLength, _ = strconv.Atoi(contentLengthStr) + } + if contentLength > logContentExceedLimit { log.Body = "[too long ignored]" + } else { + body, ignored := GetBodyContent(c) + if ignored { + log.Body = "[ignored]" + } else { + log.Body = string(body) + } } } log.CreateTime = time.Now() @@ -124,12 +138,23 @@ func RecordExceptionLog(c *gin.Context, errString string) { } log.Path = c.Request.URL.Path log.Query = c.Request.URL.Query().Encode() - if len(log.Query) > 255 { + if len(log.Query) > logContentExceedLimit { log.Query = "[too long ignored]" } - log.Body = getBodyParams(c) - if len(log.Body) > 500 { + contentLengthStr := c.GetHeader("Content-Length") + var contentLength int + if contentLengthStr != "" { + contentLength, _ = strconv.Atoi(contentLengthStr) + } + if contentLength > logContentExceedLimit { log.Body = "[too long ignored]" + } else { + body, ignored := GetBodyContent(c) + if ignored { + log.Body = "[ignored]" + } else { + log.Body = string(body) + } } log.Error = errString log.CreateTime = time.Now() @@ -139,10 +164,19 @@ func RecordExceptionLog(c *gin.Context, errString string) { } } -func getBodyParams(c *gin.Context) string { - requestBodyBytes, _ := io.ReadAll(c.Request.Body) - c.Request.Body = io.NopCloser(bytes.NewReader(requestBodyBytes)) - return string(requestBodyBytes) +func GetBodyContent(c *gin.Context) (body []byte, bodyIgnored bool) { + contentType := c.GetHeader("Content-Type") + var requestBodyBytes []byte + var ignoreBody = true + if strings.Contains(contentType, "application/json") || strings.Contains(contentType, "application/x-www-form-urlencoded") || strings.Contains(contentType, "text/plain") { + requestBodyBytes, _ = io.ReadAll(c.Request.Body) + c.Request.Body = io.NopCloser(bytes.NewReader(requestBodyBytes)) + ignoreBody = false + } + if ignoreBody { + return nil, true + } + return requestBodyBytes, false } func parseMethod(method string) string { diff --git a/cmd/server/server.go b/cmd/server/server.go index 636d471..3e1d1cd 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -303,7 +303,7 @@ func getEngine() *gin.Engine { engine := gin.New() engine. - Use(middleware.ApiDebugLogger()). + //Use(middleware.ApiDebugLogger()). Use(middleware.GlobalErrHandler()). Use(middleware.SetRequestId()). Use(middleware.SetSession(&cfg.Cookie)). -- Gitee From ef82a40f3c41b6a0cbf7a35d933cdff10eb08d10 Mon Sep 17 00:00:00 2001 From: GeMing <1328922121@qq.com> Date: Wed, 12 Feb 2025 13:19:08 +0800 Subject: [PATCH 2/2] chore: optimize --- app/admin/apis/devops/app.go | 1 + app/admin/apis/devops/explorer.go | 26 ++++++++++++ app/admin/apis/devops/explorer_sftp.go | 4 ++ app/test/admin/apis/devops/explorer_test.go | 46 +++++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 app/test/admin/apis/devops/explorer_test.go diff --git a/app/admin/apis/devops/app.go b/app/admin/apis/devops/app.go index a95dffd..427f561 100644 --- a/app/admin/apis/devops/app.go +++ b/app/admin/apis/devops/app.go @@ -140,6 +140,7 @@ func (a *AppApi) UploadPkg(c *gin.Context) { if err != nil { E.PanicErr(err) } + file.Filename = CleanFilename(file.Filename) if len([]rune(file.Filename)) > req.AppPkgFileNameMaxLength { R.Fail(c, "文件名太长", http.StatusBadRequest) return diff --git a/app/admin/apis/devops/explorer.go b/app/admin/apis/devops/explorer.go index a2ab142..cc6cfe2 100644 --- a/app/admin/apis/devops/explorer.go +++ b/app/admin/apis/devops/explorer.go @@ -12,6 +12,7 @@ import ( "net/http" "os" "path" + "regexp" "sort" "strings" ) @@ -74,6 +75,27 @@ func (a *ExplorerApi) DeleteEntry(c *gin.Context) { R.Success(c, nil) } +func FilenameCheck(filename string) error { + rgx := regexp.MustCompile("[^\\w.\\-@~]") + invalidStr := rgx.FindString(filename) + if invalidStr == "" { + if strings.HasPrefix(filename, "-") { + return E.Message("文件名不能以\"-\"开头") + } + return nil + } + return E.Message("不能包含特殊字符: " + invalidStr) +} + +func CleanFilename(filename string) string { + rgx := regexp.MustCompile("[^\\w.\\-@~]") + result := rgx.ReplaceAllString(filename, "_") + if strings.HasPrefix(filename, "-") { + result = strings.Replace(result, "-", "_", 1) + } + return result +} + // Upload godoc // // @Summary 上传文件 @@ -94,6 +116,7 @@ func (a *ExplorerApi) Upload(c *gin.Context) { if err != nil { E.PanicErr(err) } + file.Filename = CleanFilename(file.Filename) filePath := path.Clean(dir) + "/" + file.Filename if err = c.SaveUploadedFile(file, filePath); err != nil { if errors.Is(err, os.ErrPermission) { @@ -179,6 +202,9 @@ func (a *ExplorerApi) Rename(c *gin.Context) { if err := c.ShouldBindJSON(&body); err != nil { E.PanicErr(err) } + if err := FilenameCheck(body.NewName); err != nil { + E.PanicErr(err) + } oldPath := path.Clean(body.Dir + "/" + body.OldName) newPath := path.Clean(body.Dir + "/" + body.NewName) if oldPath == newPath { diff --git a/app/admin/apis/devops/explorer_sftp.go b/app/admin/apis/devops/explorer_sftp.go index b0a3702..5448838 100644 --- a/app/admin/apis/devops/explorer_sftp.go +++ b/app/admin/apis/devops/explorer_sftp.go @@ -113,6 +113,7 @@ func (a *ExplorerSftpApi) Upload(c *gin.Context) { if err != nil { E.PanicErr(err) } + file.Filename = CleanFilename(file.Filename) sHostId := c.PostForm("hostId") hostId, err := strconv.Atoi(sHostId) if err != nil { @@ -212,6 +213,9 @@ func (a *ExplorerSftpApi) Rename(c *gin.Context) { if err := c.ShouldBindJSON(&body); err != nil { E.PanicErr(err) } + if err := FilenameCheck(body.NewName); err != nil { + E.PanicErr(err) + } host := a.getHost(body.HostId) if err := a.explorerSftpService.Rename(&body, host); err != nil { E.PanicErr(err) diff --git a/app/test/admin/apis/devops/explorer_test.go b/app/test/admin/apis/devops/explorer_test.go new file mode 100644 index 0000000..d2a4cc3 --- /dev/null +++ b/app/test/admin/apis/devops/explorer_test.go @@ -0,0 +1,46 @@ +package devops + +import ( + devops2 "github.com/MarchGe/go-admin-server/app/admin/apis/devops" + "testing" +) + +func TestFilenameCheck(t *testing.T) { + tests := []struct { + FileName string + Description string + ExpectedPass bool + }{ + {FileName: "-xxx.doc", Description: "测试文件名不能以“-”开头", ExpectedPass: false}, + {FileName: "xxx-.doc", Description: "测试文件名非开头可以包含“-”", ExpectedPass: true}, + {FileName: "xx!#x.doc", Description: "测试文件名不能包含特殊字符", ExpectedPass: false}, + } + for _, test := range tests { + err := devops2.FilenameCheck(test.FileName) + if (test.ExpectedPass && err == nil) || (!test.ExpectedPass && err != nil) { + t.Logf("\n-----\n用例描述:%s\n文件名: %s 预期:%t 结果:%s 原因:%v", test.Description, test.FileName, test.ExpectedPass, "一致", err) + } else { + t.Errorf("\n-----\n用例描述:%s\n文件名: %s 预期:%t 结果:%s 原因:%v", test.Description, test.FileName, test.ExpectedPass, "不一致", err) + } + } +} + +func TestCleanFilename(t *testing.T) { + tests := []struct { + FileName string + Description string + ExpectedOutput string + }{ + {FileName: "-xxx.doc", Description: "测试文件名不能以“-”开头", ExpectedOutput: "_xxx.doc"}, + {FileName: "xxx-.doc", Description: "测试文件名非开头可以包含“-”", ExpectedOutput: "xxx-.doc"}, + {FileName: "%xx!#x.d@%oc&", Description: "测试文件名不能包含特殊字符", ExpectedOutput: "_xx__x.d@_oc_"}, + } + for _, test := range tests { + fileName := devops2.CleanFilename(test.FileName) + if test.ExpectedOutput == fileName { + t.Logf("\n-----\n用例描述:%s\n文件名: %s 预期:%s 输出:%s 结果:%s", test.Description, test.FileName, test.ExpectedOutput, fileName, "一致") + } else { + t.Errorf("\n-----\n用例描述:%s\n文件名: %s 预期:%s 输出:%s 结果:%s", test.Description, test.FileName, test.ExpectedOutput, fileName, "不一致") + } + } +} -- Gitee