From 5e74a3993bc6a85d7d001e1bbfe76048768277eb Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Mon, 29 May 2017 22:10:36 +0200 Subject: [PATCH] fix(api): add restrictions for the files served by the API (#903) --- api/errors.go | 1 + api/http/handler/file.go | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/api/errors.go b/api/errors.go index ad19b05d4..e22dbc01d 100644 --- a/api/errors.go +++ b/api/errors.go @@ -4,6 +4,7 @@ package portainer const ( ErrUnauthorized = Error("Unauthorized") ErrResourceAccessDenied = Error("Access denied to resource") + ErrResourceNotFound = Error("Unable to find resource") ErrUnsupportedDockerAPI = Error("Unsupported Docker API response") ErrMissingSecurityContext = Error("Unable to find security details in request context") ) diff --git a/api/http/handler/file.go b/api/http/handler/file.go index 008275932..488a3968e 100644 --- a/api/http/handler/file.go +++ b/api/http/handler/file.go @@ -1,19 +1,36 @@ package handler import ( + "os" + + "github.com/portainer/portainer" + httperror "github.com/portainer/portainer/http/error" + + "log" "net/http" + "path" "strings" ) // FileHandler represents an HTTP API handler for managing static files. type FileHandler struct { http.Handler + Logger *log.Logger + allowedDirectories map[string]bool } // NewFileHandler returns a new instance of FileHandler. func NewFileHandler(assetPath string) *FileHandler { h := &FileHandler{ Handler: http.FileServer(http.Dir(assetPath)), + Logger: log.New(os.Stderr, "", log.LstdFlags), + allowedDirectories: map[string]bool{ + "/": true, + "/css": true, + "/js": true, + "/images": true, + "/fonts": true, + }, } return h } @@ -27,11 +44,18 @@ func isHTML(acceptContent []string) bool { return false } -func (fileHandler *FileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (handler *FileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + requestDirectory := path.Dir(r.URL.Path) + if !handler.allowedDirectories[requestDirectory] { + httperror.WriteErrorResponse(w, portainer.ErrResourceNotFound, http.StatusNotFound, handler.Logger) + return + } + if !isHTML(r.Header["Accept"]) { w.Header().Set("Cache-Control", "max-age=31536000") } else { w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") } - fileHandler.Handler.ServeHTTP(w, r) + + handler.Handler.ServeHTTP(w, r) }