Plugins: Unexport PluginDir field from PluginDTO (#59190)

* unexport pluginDir from dto

* more err checks

* tidy

* fix tests

* fix dboard file tests

* fix import

* fix tests

* apply PR feedback

* combine interfaces

* fix logs and clean up test

* filepath clean

* use fs.File

* rm explicit type
This commit is contained in:
Will Browne
2022-12-02 12:46:55 +00:00
committed by GitHub
parent 6d1bcd9f40
commit 76233f9997
15 changed files with 223 additions and 286 deletions

View File

@ -1,12 +1,13 @@
package api
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"path"
"path/filepath"
"runtime"
@ -16,7 +17,6 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
@ -28,6 +28,7 @@ import (
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/pluginsettings"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
@ -309,42 +310,25 @@ func (hs *HTTPServer) getPluginAssets(c *models.ReqContext) {
}
// prepend slash for cleaning relative paths
requestedFile := filepath.Clean(filepath.Join("/", web.Params(c.Req)["*"]))
rel, err := filepath.Rel("/", requestedFile)
requestedFile, err := util.CleanRelativePath(web.Params(c.Req)["*"])
if err != nil {
// slash is prepended above therefore this is not expected to fail
c.JsonApiErr(500, "Failed to get the relative path", err)
c.JsonApiErr(500, "Failed to clean relative file path", err)
return
}
if !plugin.IncludedInSignature(rel) {
hs.log.Warn("Access to requested plugin file will be forbidden in upcoming Grafana versions as the file "+
"is not included in the plugin signature", "file", requestedFile)
}
absPluginDir, err := filepath.Abs(plugin.PluginDir)
f, err := plugin.File(requestedFile)
if err != nil {
c.JsonApiErr(500, "Failed to get plugin absolute path", nil)
return
}
pluginFilePath := filepath.Join(absPluginDir, rel)
// It's safe to ignore gosec warning G304 since we already clean the requested file path and subsequently
// use this with a prefix of the plugin's directory, which is set during plugin loading
// nolint:gosec
f, err := os.Open(pluginFilePath)
if err != nil {
if os.IsNotExist(err) {
c.JsonApiErr(404, "Plugin file not found", err)
if errors.Is(err, plugins.ErrFileNotExist) {
c.JsonApiErr(404, "Plugin file not found", nil)
return
}
c.JsonApiErr(500, "Could not open plugin file", err)
return
}
defer func() {
if err := f.Close(); err != nil {
hs.log.Error("Failed to close file", "err", err)
if err = f.Close(); err != nil {
hs.log.Error("Failed to close plugin file", "err", err)
}
}()
@ -360,7 +344,16 @@ func (hs *HTTPServer) getPluginAssets(c *models.ReqContext) {
c.Resp.Header().Set("Cache-Control", "public, max-age=3600")
}
http.ServeContent(c.Resp, c.Req, pluginFilePath, fi.ModTime(), f)
if rs, ok := f.(io.ReadSeeker); ok {
http.ServeContent(c.Resp, c.Req, requestedFile, fi.ModTime(), rs)
} else {
b, err := io.ReadAll(f)
if err != nil {
c.JsonApiErr(500, "Plugin file exists but could not read", err)
return
}
http.ServeContent(c.Resp, c.Req, requestedFile, fi.ModTime(), bytes.NewReader(b))
}
}
// CheckHealth returns the health of a plugin.
@ -496,34 +489,24 @@ func (hs *HTTPServer) pluginMarkdown(ctx context.Context, pluginId string, name
return nil, plugins.NotFoundError{PluginID: pluginId}
}
// nolint:gosec
// We can ignore the gosec G304 warning since we have cleaned the requested file path and subsequently
// use this with a prefix of the plugin's directory, which is set during plugin loading
path := filepath.Join(plugin.PluginDir, mdFilepath(strings.ToUpper(name)))
exists, err := fs.Exists(path)
md, err := plugin.File(mdFilepath(strings.ToUpper(name)))
if err != nil {
return nil, err
}
if !exists {
path = filepath.Join(plugin.PluginDir, mdFilepath(strings.ToLower(name)))
md, err = plugin.File(mdFilepath(strings.ToUpper(name)))
if err != nil {
return make([]byte, 0), nil
}
}
defer func() {
if err = md.Close(); err != nil {
hs.log.Error("Failed to close plugin markdown file", "err", err)
}
}()
exists, err = fs.Exists(path)
d, err := io.ReadAll(md)
if err != nil {
return nil, err
}
if !exists {
return make([]byte, 0), nil
}
// nolint:gosec
// We can ignore the gosec G304 warning since we have cleaned the requested file path and subsequently
// use this with a prefix of the plugin's directory, which is set during plugin loading
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return data, nil
return d, nil
}
func mdFilepath(mdFilename string) string {