From 33d86e41c2d7a84908573dbad4b7953bf9f7af5e Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Thu, 29 Dec 2022 12:46:41 +0100 Subject: [PATCH] Only serve image files on ./image This is an addition to the existing XSS fix in the previous commit. --- api/application.go | 15 ++++++++++----- router/router.go | 23 +++++++++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/api/application.go b/api/application.go index 736330ee..142809cc 100644 --- a/api/application.go +++ b/api/application.go @@ -328,11 +328,7 @@ func (a *ApplicationAPI) UploadApplicationImage(ctx *gin.Context) { } ext := filepath.Ext(file.Filename) - - switch ext { - case ".gif", ".png", ".jpg", ".jpeg": - // ok - default: + if !ValidApplicationImageExt(ext) { ctx.AbortWithError(400, errors.New("invalid file extension")) return } @@ -391,3 +387,12 @@ func generateNonExistingImageName(imgDir string, gen func() string) string { } } } + +func ValidApplicationImageExt(ext string) bool { + switch ext { + case ".gif", ".png", ".jpg", ".jpeg": + return true + default: + return false + } +} diff --git a/router/router.go b/router/router.go index 59cf78c3..14405f6d 100644 --- a/router/router.go +++ b/router/router.go @@ -2,6 +2,8 @@ package router import ( "fmt" + "net/http" + "path/filepath" "regexp" "time" @@ -14,7 +16,7 @@ import ( "github.com/gotify/server/v2/config" "github.com/gotify/server/v2/database" "github.com/gotify/server/v2/docs" - "github.com/gotify/server/v2/error" + gerror "github.com/gotify/server/v2/error" "github.com/gotify/server/v2/model" "github.com/gotify/server/v2/plugin" "github.com/gotify/server/v2/ui" @@ -24,8 +26,8 @@ import ( func Create(db *database.GormDatabase, vInfo *model.VersionInfo, conf *config.Configuration) (*gin.Engine, func()) { g := gin.New() - g.Use(gin.LoggerWithFormatter(logFormatter), gin.Recovery(), error.Handler(), location.Default()) - g.NoRoute(error.NotFound()) + g.Use(gin.LoggerWithFormatter(logFormatter), gin.Recovery(), gerror.Handler(), location.Default()) + g.NoRoute(gerror.NotFound()) streamHandler := stream.New(time.Duration(conf.Server.Stream.PingPeriodSeconds)*time.Second, 15*time.Second, conf.Server.Stream.AllowedOrigins) authentication := auth.Auth{DB: db} @@ -61,7 +63,8 @@ func Create(db *database.GormDatabase, vInfo *model.VersionInfo, conf *config.Co g.GET("/health", healthHandler.Health) g.GET("/swagger", docs.Serve) - g.Static("/image", conf.UploadedImagesDir) + g.StaticFS("/image", &onlyImageFS{inner: gin.Dir(conf.UploadedImagesDir, false)}) + g.GET("/docs", docs.UI) g.Use(func(ctx *gin.Context) { @@ -194,3 +197,15 @@ func logFormatter(param gin.LogFormatterParams) string { param.ErrorMessage, ) } + +type onlyImageFS struct { + inner http.FileSystem +} + +func (fs *onlyImageFS) Open(name string) (http.File, error) { + ext := filepath.Ext(name) + if !api.ValidApplicationImageExt(ext) { + return nil, fmt.Errorf("invalid file") + } + return fs.inner.Open(name) +}