mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 20:12:22 +08:00
Plugins: Fs: Add option to access unallowed files in dev mode (#66492)
* Plugins: Fs: Add option to access unallowed files in dev mode * Plugins: Fs: allow accessing unallowed files only when in dev mode * Plugins: Fs: Add ProvideLocalFinder * Plugins: FS: Pass whole config in NewLocalFinder() * Plugins: FS: Add AllowListLocalFS * Plugins: FS: Fix some tests * Plugins: FS: Update tests * Plugins: FS: Removed dead code * Plugins: FS: Add tests for AllowListFS * Plugins: FS: Update comments * Plugins: FS: Use variadic arguments for allow list rather than map * Plugins: FS: Remove unnecessary log * Plugins: FS: Do not escape plugin root dir * Fix merge conflict * Plugins: FS: Update comments * Plugins: FS: PR review changes * Fix merge conflict * Fix tests * Cleanup * Fix flaky test * Changes from PR review * Lint * Add comment to LocalFS.Remove * Fix Windows * Renamed devMode to production
This commit is contained in:
@ -3,7 +3,6 @@ package loader
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
@ -28,13 +27,18 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var compareOpts = []cmp.Option{cmpopts.IgnoreFields(plugins.Plugin{}, "client", "log"), localFSComparer}
|
||||
var compareOpts = []cmp.Option{cmpopts.IgnoreFields(plugins.Plugin{}, "client", "log"), fsComparer}
|
||||
|
||||
var localFSComparer = cmp.Comparer(func(fs1 plugins.LocalFS, fs2 plugins.LocalFS) bool {
|
||||
fs1Files := fs1.Files()
|
||||
fs2Files := fs2.Files()
|
||||
var fsComparer = cmp.Comparer(func(fs1 plugins.FS, fs2 plugins.FS) bool {
|
||||
fs1Files, err := fs1.Files()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fs2Files, err := fs2.Files()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
finder.NewLocalFinder()
|
||||
sort.SliceStable(fs1Files, func(i, j int) bool {
|
||||
return fs1Files[i] < fs1Files[j]
|
||||
})
|
||||
@ -108,9 +112,8 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
Module: "app/plugins/datasource/cloudwatch/module",
|
||||
BaseURL: "public/app/plugins/datasource/cloudwatch",
|
||||
FS: plugins.NewLocalFS(
|
||||
filesInDir(t, filepath.Join(corePluginDir, "app/plugins/datasource/cloudwatch")),
|
||||
filepath.Join(corePluginDir, "app/plugins/datasource/cloudwatch")),
|
||||
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(corePluginDir, "app/plugins/datasource/cloudwatch")),
|
||||
Signature: plugins.SignatureInternal,
|
||||
Class: plugins.Core,
|
||||
},
|
||||
@ -147,12 +150,9 @@ func TestLoader_Load(t *testing.T) {
|
||||
Backend: true,
|
||||
State: "alpha",
|
||||
},
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: plugins.NewLocalFS(
|
||||
filesInDir(t, filepath.Join(parentDir, "testdata/valid-v2-signature/plugin/")),
|
||||
filepath.Join(parentDir, "testdata/valid-v2-signature/plugin/"),
|
||||
),
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/valid-v2-signature/plugin/")),
|
||||
Signature: "valid",
|
||||
SignatureType: plugins.GrafanaSignature,
|
||||
SignatureOrg: "Grafana Labs",
|
||||
@ -226,20 +226,10 @@ func TestLoader_Load(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Class: plugins.External,
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
FS: plugins.NewLocalFS(
|
||||
map[string]struct{}{
|
||||
filepath.Join(parentDir, "testdata/includes-symlinks", "/MANIFEST.txt"): {},
|
||||
filepath.Join(parentDir, "testdata/includes-symlinks", "dashboards/connections.json"): {},
|
||||
filepath.Join(parentDir, "testdata/includes-symlinks", "dashboards/extra/memory.json"): {},
|
||||
filepath.Join(parentDir, "testdata/includes-symlinks", "plugin.json"): {},
|
||||
filepath.Join(parentDir, "testdata/includes-symlinks", "symlink_to_txt"): {},
|
||||
filepath.Join(parentDir, "testdata/includes-symlinks", "text.txt"): {},
|
||||
},
|
||||
filepath.Join(parentDir, "testdata/includes-symlinks"),
|
||||
),
|
||||
Class: plugins.External,
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/includes-symlinks")),
|
||||
Signature: "valid",
|
||||
SignatureType: plugins.GrafanaSignature,
|
||||
SignatureOrg: "Grafana Labs",
|
||||
@ -276,13 +266,10 @@ func TestLoader_Load(t *testing.T) {
|
||||
Backend: true,
|
||||
State: plugins.AlphaRelease,
|
||||
},
|
||||
Class: plugins.External,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: plugins.NewLocalFS(
|
||||
filesInDir(t, filepath.Join(parentDir, "testdata/unsigned-datasource/plugin")),
|
||||
filepath.Join(parentDir, "testdata/unsigned-datasource/plugin"),
|
||||
),
|
||||
Class: plugins.External,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/unsigned-datasource/plugin")),
|
||||
Signature: "unsigned",
|
||||
},
|
||||
},
|
||||
@ -330,13 +317,10 @@ func TestLoader_Load(t *testing.T) {
|
||||
Backend: true,
|
||||
State: plugins.AlphaRelease,
|
||||
},
|
||||
Class: plugins.External,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: plugins.NewLocalFS(
|
||||
filesInDir(t, filepath.Join(parentDir, "testdata/unsigned-datasource/plugin")),
|
||||
filepath.Join(parentDir, "testdata/unsigned-datasource/plugin"),
|
||||
),
|
||||
Class: plugins.External,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/unsigned-datasource/plugin")),
|
||||
Signature: plugins.SignatureUnsigned,
|
||||
},
|
||||
},
|
||||
@ -440,14 +424,11 @@ func TestLoader_Load(t *testing.T) {
|
||||
Backend: false,
|
||||
},
|
||||
DefaultNavURL: "/plugins/test-app/page/root-page-react",
|
||||
FS: plugins.NewLocalFS(map[string]struct{}{
|
||||
filepath.Join(parentDir, "testdata/test-app-with-includes", "dashboards/memory.json"): {},
|
||||
filepath.Join(parentDir, "testdata/test-app-with-includes", "plugin.json"): {},
|
||||
}, filepath.Join(parentDir, "testdata/test-app-with-includes")),
|
||||
Class: plugins.External,
|
||||
Signature: plugins.SignatureUnsigned,
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/test-app-with-includes")),
|
||||
Class: plugins.External,
|
||||
Signature: plugins.SignatureUnsigned,
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -532,9 +513,7 @@ func TestLoader_Load_CustomSource(t *testing.T) {
|
||||
Plugins: []plugins.Dependency{},
|
||||
},
|
||||
},
|
||||
FS: plugins.NewLocalFS(map[string]struct{}{
|
||||
filepath.Join(parentDir, "testdata/cdn/plugin", "plugin.json"): {},
|
||||
}, filepath.Join(parentDir, "testdata/cdn/plugin")),
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/cdn/plugin")),
|
||||
Class: plugins.Bundled,
|
||||
Signature: plugins.SignatureValid,
|
||||
BaseURL: "plugin-cdn/grafana-worldmap-panel/0.3.3/public/plugins/grafana-worldmap-panel",
|
||||
@ -670,13 +649,10 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
|
||||
Executable: "test",
|
||||
State: plugins.AlphaRelease,
|
||||
},
|
||||
Class: plugins.External,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: plugins.NewLocalFS(map[string]struct{}{
|
||||
filepath.Join(parentDir, "testdata/valid-v2-pvt-signature/plugin/plugin.json"): {},
|
||||
filepath.Join(parentDir, "testdata/valid-v2-pvt-signature/plugin/MANIFEST.txt"): {},
|
||||
}, filepath.Join(parentDir, "testdata/valid-v2-pvt-signature/plugin")),
|
||||
Class: plugins.External,
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "testdata/valid-v2-pvt-signature/plugin")),
|
||||
Signature: "valid",
|
||||
SignatureType: plugins.PrivateSignature,
|
||||
SignatureOrg: "Will Browne",
|
||||
@ -795,10 +771,7 @@ func TestLoader_Load_RBACReady(t *testing.T) {
|
||||
},
|
||||
Backend: false,
|
||||
},
|
||||
FS: plugins.NewLocalFS(map[string]struct{}{
|
||||
filepath.Join(pluginDir, "plugin.json"): {},
|
||||
filepath.Join(pluginDir, "MANIFEST.txt"): {},
|
||||
}, pluginDir),
|
||||
FS: mustNewStaticFSForTests(t, pluginDir),
|
||||
Class: plugins.External,
|
||||
Signature: plugins.SignatureValid,
|
||||
SignatureType: plugins.PrivateSignature,
|
||||
@ -885,10 +858,7 @@ func TestLoader_Load_Signature_RootURL(t *testing.T) {
|
||||
Backend: true,
|
||||
Executable: "test",
|
||||
},
|
||||
FS: plugins.NewLocalFS(map[string]struct{}{
|
||||
filepath.Join(filepath.Join(parentDir, "/testdata/valid-v2-pvt-signature-root-url-uri/plugin"), "plugin.json"): {},
|
||||
filepath.Join(filepath.Join(parentDir, "/testdata/valid-v2-pvt-signature-root-url-uri/plugin"), "MANIFEST.txt"): {},
|
||||
}, filepath.Join(parentDir, "/testdata/valid-v2-pvt-signature-root-url-uri/plugin")),
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(parentDir, "/testdata/valid-v2-pvt-signature-root-url-uri/plugin")),
|
||||
Class: plugins.External,
|
||||
Signature: plugins.SignatureValid,
|
||||
SignatureType: plugins.PrivateSignature,
|
||||
@ -972,7 +942,7 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) {
|
||||
},
|
||||
Backend: false,
|
||||
},
|
||||
FS: plugins.NewLocalFS(filesInDir(t, pluginDir), pluginDir),
|
||||
FS: mustNewStaticFSForTests(t, pluginDir),
|
||||
Class: plugins.External,
|
||||
Signature: plugins.SignatureValid,
|
||||
SignatureType: plugins.GrafanaSignature,
|
||||
@ -1062,7 +1032,7 @@ func TestLoader_Load_SkipUninitializedPlugins(t *testing.T) {
|
||||
},
|
||||
Backend: false,
|
||||
},
|
||||
FS: plugins.NewLocalFS(filesInDir(t, pluginDir1), pluginDir1),
|
||||
FS: mustNewStaticFSForTests(t, pluginDir1),
|
||||
Class: plugins.External,
|
||||
Signature: plugins.SignatureValid,
|
||||
SignatureType: plugins.GrafanaSignature,
|
||||
@ -1137,10 +1107,9 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
},
|
||||
Backend: true,
|
||||
},
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: plugins.NewLocalFS(filesInDir(t, filepath.Join(rootDir, "testdata/nested-plugins/parent")),
|
||||
filepath.Join(rootDir, "testdata/nested-plugins/parent")),
|
||||
Module: "plugins/test-datasource/module",
|
||||
BaseURL: "public/plugins/test-datasource",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(rootDir, "testdata/nested-plugins/parent")),
|
||||
Signature: plugins.SignatureValid,
|
||||
SignatureType: plugins.GrafanaSignature,
|
||||
SignatureOrg: "Grafana Labs",
|
||||
@ -1170,10 +1139,9 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
Plugins: []plugins.Dependency{},
|
||||
},
|
||||
},
|
||||
Module: "plugins/test-panel/module",
|
||||
BaseURL: "public/plugins/test-panel",
|
||||
FS: plugins.NewLocalFS(filesInDir(t, filepath.Join(rootDir, "testdata/nested-plugins/parent/nested")),
|
||||
filepath.Join(rootDir, "testdata/nested-plugins/parent/nested")),
|
||||
Module: "plugins/test-panel/module",
|
||||
BaseURL: "public/plugins/test-panel",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(rootDir, "testdata/nested-plugins/parent/nested")),
|
||||
Signature: plugins.SignatureValid,
|
||||
SignatureType: plugins.GrafanaSignature,
|
||||
SignatureOrg: "Grafana Labs",
|
||||
@ -1310,10 +1278,9 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
},
|
||||
Backend: false,
|
||||
},
|
||||
Module: "plugins/myorgid-simple-app/module",
|
||||
BaseURL: "public/plugins/myorgid-simple-app",
|
||||
FS: plugins.NewLocalFS(filesInDir(t, filepath.Join(rootDir, "testdata/app-with-child/dist")),
|
||||
filepath.Join(rootDir, "testdata/app-with-child/dist")),
|
||||
Module: "plugins/myorgid-simple-app/module",
|
||||
BaseURL: "public/plugins/myorgid-simple-app",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(rootDir, "testdata/app-with-child/dist")),
|
||||
DefaultNavURL: "/plugins/myorgid-simple-app/page/root-page-react",
|
||||
Signature: plugins.SignatureValid,
|
||||
SignatureType: plugins.GrafanaSignature,
|
||||
@ -1349,10 +1316,9 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
Plugins: []plugins.Dependency{},
|
||||
},
|
||||
},
|
||||
Module: "plugins/myorgid-simple-app/child/module",
|
||||
BaseURL: "public/plugins/myorgid-simple-app",
|
||||
FS: plugins.NewLocalFS(filesInDir(t, filepath.Join(rootDir, "testdata/app-with-child/dist/child")),
|
||||
filepath.Join(rootDir, "testdata/app-with-child/dist/child")),
|
||||
Module: "plugins/myorgid-simple-app/child/module",
|
||||
BaseURL: "public/plugins/myorgid-simple-app",
|
||||
FS: mustNewStaticFSForTests(t, filepath.Join(rootDir, "testdata/app-with-child/dist/child")),
|
||||
IncludedInAppID: parent.ID,
|
||||
Signature: plugins.SignatureValid,
|
||||
SignatureType: plugins.GrafanaSignature,
|
||||
@ -1421,7 +1387,7 @@ func Test_setPathsBasedOnApp(t *testing.T) {
|
||||
func newLoader(cfg *config.Cfg, cbs ...func(loader *Loader)) *Loader {
|
||||
l := New(cfg, &fakes.FakeLicensingService{}, signature.NewUnsignedAuthorizer(cfg), fakes.NewFakePluginRegistry(),
|
||||
fakes.NewFakeBackendProcessProvider(), fakes.NewFakeProcessManager(), fakes.NewFakeRoleRegistry(),
|
||||
assetpath.ProvideService(pluginscdn.ProvideService(cfg)), finder.NewLocalFinder(),
|
||||
assetpath.ProvideService(pluginscdn.ProvideService(cfg)), finder.NewLocalFinder(cfg),
|
||||
signature.ProvideService(cfg, keystore.ProvideService(kvstore.NewFakeKVStore())))
|
||||
|
||||
for _, cb := range cbs {
|
||||
@ -1453,39 +1419,8 @@ func verifyState(t *testing.T, ps []*plugins.Plugin, reg *fakes.FakePluginRegist
|
||||
}
|
||||
}
|
||||
|
||||
func filesInDir(t *testing.T, dir string) map[string]struct{} {
|
||||
files, err := collectFilesWithin(dir)
|
||||
if err != nil {
|
||||
t.Logf("Could not collect plugin file info. Err: %v", err)
|
||||
return map[string]struct{}{}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func collectFilesWithin(dir string) (map[string]struct{}, error) {
|
||||
files := map[string]struct{}{}
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip directories
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// verify that file is within plugin directory
|
||||
//file, err := filepath.Rel(dir, path)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//if strings.HasPrefix(file, ".."+string(filepath.Separator)) {
|
||||
// return fmt.Errorf("file '%s' not inside of plugin directory", file)
|
||||
//}
|
||||
|
||||
files[path] = struct{}{}
|
||||
return nil
|
||||
})
|
||||
|
||||
return files, err
|
||||
func mustNewStaticFSForTests(t *testing.T, dir string) plugins.FS {
|
||||
sfs, err := plugins.NewStaticFS(plugins.NewLocalFS(dir))
|
||||
require.NoError(t, err)
|
||||
return sfs
|
||||
}
|
||||
|
Reference in New Issue
Block a user