Commit a515c544 by Will Browne Committed by GitHub

Plugins: Add support for signature manifest V2 (#29240)

* add support for signing manifest v2

* add log and fix var name

* shorten comment

* improve comment

* remove unnecessary param

* improve naming

* reformat

* rename var

* refactor test

* remove unnecessary assert

* simplify test requirements

* add more test cases

* address feedback

* revert naming

* flip tracking missing

* fix check

* Trigger Build
parent e2351f79
......@@ -19,25 +19,29 @@ type PluginSetting struct {
JsonData map[string]interface{} `json:"jsonData"`
DefaultNavUrl string `json:"defaultNavUrl"`
LatestVersion string `json:"latestVersion"`
HasUpdate bool `json:"hasUpdate"`
State plugins.PluginState `json:"state"`
Signature plugins.PluginSignature `json:"signature"`
LatestVersion string `json:"latestVersion"`
HasUpdate bool `json:"hasUpdate"`
State plugins.PluginState `json:"state"`
Signature plugins.PluginSignatureStatus `json:"signature"`
SignatureType plugins.PluginSignatureType `json:"signatureType"`
SignatureOrg string `json:"signatureOrg"`
}
type PluginListItem struct {
Name string `json:"name"`
Type string `json:"type"`
Id string `json:"id"`
Enabled bool `json:"enabled"`
Pinned bool `json:"pinned"`
Info *plugins.PluginInfo `json:"info"`
LatestVersion string `json:"latestVersion"`
HasUpdate bool `json:"hasUpdate"`
DefaultNavUrl string `json:"defaultNavUrl"`
Category string `json:"category"`
State plugins.PluginState `json:"state"`
Signature plugins.PluginSignature `json:"signature"`
Name string `json:"name"`
Type string `json:"type"`
Id string `json:"id"`
Enabled bool `json:"enabled"`
Pinned bool `json:"pinned"`
Info *plugins.PluginInfo `json:"info"`
LatestVersion string `json:"latestVersion"`
HasUpdate bool `json:"hasUpdate"`
DefaultNavUrl string `json:"defaultNavUrl"`
Category string `json:"category"`
State plugins.PluginState `json:"state"`
Signature plugins.PluginSignatureStatus `json:"signature"`
SignatureType plugins.PluginSignatureType `json:"signatureType"`
SignatureOrg string `json:"signatureOrg"`
}
type PluginList []PluginListItem
......
......@@ -110,6 +110,8 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) Response {
DefaultNavUrl: pluginDef.DefaultNavUrl,
State: pluginDef.State,
Signature: pluginDef.Signature,
SignatureType: pluginDef.SignatureType,
SignatureOrg: pluginDef.SignatureOrg,
}
if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
......@@ -162,6 +164,8 @@ func GetPluginSettingByID(c *models.ReqContext) Response {
HasUpdate: def.GrafanaNetHasUpdate,
State: def.State,
Signature: def.Signature,
SignatureType: def.SignatureType,
SignatureOrg: def.SignatureOrg,
}
query := models.GetPluginSettingByIdQuery{PluginId: pluginID, OrgId: c.OrgId}
......
......@@ -8,10 +8,13 @@ import (
"errors"
"io"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
"golang.org/x/crypto/openpgp"
......@@ -51,6 +54,17 @@ type pluginManifest struct {
KeyID string `json:"keyId"`
Time int64 `json:"time"`
Files map[string]string `json:"files"`
// V2 supported fields
ManifestVersion string `json:"manifestVersion"`
SignatureType PluginSignatureType `json:"signatureType"`
SignedByOrg string `json:"signedByOrg"`
SignedByOrgName string `json:"signedByOrgName"`
RootURLs []string `json:"rootUrls"`
}
func (m *pluginManifest) isV2() bool {
return strings.HasPrefix(m.ManifestVersion, "2.")
}
// readPluginManifest attempts to read and verify the plugin manifest
......@@ -83,7 +97,7 @@ func readPluginManifest(body []byte) (*pluginManifest, error) {
}
// getPluginSignatureState returns the signature state for a plugin.
func getPluginSignatureState(log log.Logger, plugin *PluginBase) PluginSignature {
func getPluginSignatureState(log log.Logger, plugin *PluginBase) (PluginSignatureState, error) {
log.Debug("Getting signature state of plugin", "plugin", plugin.Id, "isBackend", plugin.Backend)
manifestPath := filepath.Join(plugin.PluginDir, "MANIFEST.txt")
......@@ -93,20 +107,58 @@ func getPluginSignatureState(log log.Logger, plugin *PluginBase) PluginSignature
byteValue, err := ioutil.ReadFile(manifestPath)
if err != nil || len(byteValue) < 10 {
log.Debug("Plugin is unsigned", "id", plugin.Id)
return PluginSignatureUnsigned
return PluginSignatureState{
Status: pluginSignatureUnsigned,
}, nil
}
manifest, err := readPluginManifest(byteValue)
if err != nil {
log.Debug("Plugin signature invalid", "id", plugin.Id)
return PluginSignatureInvalid
return PluginSignatureState{
Status: pluginSignatureInvalid,
}, nil
}
// Make sure the versions all match
if manifest.Plugin != plugin.Id || manifest.Version != plugin.Info.Version {
return PluginSignatureModified
return PluginSignatureState{
Status: pluginSignatureModified,
}, nil
}
// Validate that private is running within defined root URLs
if manifest.SignatureType == privateType {
appURL, err := url.Parse(setting.AppUrl)
if err != nil {
return PluginSignatureState{}, err
}
foundMatch := false
for _, u := range manifest.RootURLs {
rootURL, err := url.Parse(u)
if err != nil {
log.Warn("Could not parse plugin root URL", "plugin", plugin.Id, "rootUrl", rootURL)
return PluginSignatureState{}, err
}
if rootURL.Scheme == appURL.Scheme &&
rootURL.Host == appURL.Host &&
rootURL.RequestURI() == appURL.RequestURI() {
foundMatch = true
break
}
}
if !foundMatch {
log.Warn("Could not find root URL that matches running application URL", "plugin", plugin.Id, "appUrl", appURL, "rootUrls", manifest.RootURLs)
return PluginSignatureState{
Status: pluginSignatureInvalid,
}, nil
}
}
manifestFiles := make(map[string]bool, len(manifest.Files))
// Verify the manifest contents
log.Debug("Verifying contents of plugin manifest", "plugin", plugin.Id)
for p, hash := range manifest.Files {
......@@ -118,7 +170,10 @@ func getPluginSignatureState(log log.Logger, plugin *PluginBase) PluginSignature
// on the manifest file for a plugin and not user input.
f, err := os.Open(fp)
if err != nil {
return PluginSignatureModified
log.Warn("Plugin file listed in the manifest was not found", "plugin", plugin.Id, "filename", p, "dir", plugin.PluginDir)
return PluginSignatureState{
Status: pluginSignatureModified,
}, nil
}
defer func() {
if err := f.Close(); err != nil {
......@@ -129,16 +184,42 @@ func getPluginSignatureState(log log.Logger, plugin *PluginBase) PluginSignature
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
log.Warn("Couldn't read plugin file", "plugin", plugin.Id, "filename", fp)
return PluginSignatureModified
return PluginSignatureState{
Status: pluginSignatureModified,
}, nil
}
sum := hex.EncodeToString(h.Sum(nil))
if sum != hash {
log.Warn("Plugin file's signature has been modified versus manifest", "plugin", plugin.Id, "filename", fp)
return PluginSignatureModified
return PluginSignatureState{
Status: pluginSignatureModified,
}, nil
}
manifestFiles[p] = true
}
if manifest.isV2() {
// Track files missing from the manifest
var unsignedFiles []string
for _, f := range plugin.Files {
if _, exists := manifestFiles[f]; !exists {
unsignedFiles = append(unsignedFiles, f)
}
}
if len(unsignedFiles) > 0 {
log.Warn("The following files were not included in the signature", "plugin", plugin.Id, "files", unsignedFiles)
return PluginSignatureState{
Status: pluginSignatureModified,
}, nil
}
}
// Everything OK
log.Debug("Plugin signature valid", "id", plugin.Id)
return PluginSignatureValid
return PluginSignatureState{
Status: pluginSignatureValid,
Type: manifest.SignatureType,
SigningOrg: manifest.SignedByOrgName,
}, nil
}
package plugins
import (
"sort"
"strings"
"testing"
......@@ -46,6 +47,13 @@ NR7DnB0CCQHO+4FlSPtXFTzNepoc+CytQyDAeOLMLmf2Tqhk2YShk+G/YlVX
require.NoError(t, err)
require.NotNil(t, manifest)
assert.Equal(t, "grafana-googlesheets-datasource", manifest.Plugin)
assert.Equal(t, "1.0.0-dev", manifest.Version)
assert.Equal(t, int64(1586817677115), manifest.Time)
assert.Equal(t, "7e4d0c6a708866e7", manifest.KeyID)
expectedFiles := []string{"LICENSE", "README.md", "gfx_sheets_darwin_amd64", "gfx_sheets_linux_amd64",
"gfx_sheets_windows_amd64.exe", "module.js", "module.js.LICENSE.txt", "module.js.map", "plugin.json",
}
assert.Equal(t, expectedFiles, fileList(manifest))
})
t.Run("invalid manifest", func(t *testing.T) {
......@@ -54,3 +62,61 @@ NR7DnB0CCQHO+4FlSPtXFTzNepoc+CytQyDAeOLMLmf2Tqhk2YShk+G/YlVX
require.Error(t, err)
})
}
func TestReadPluginManifestV2(t *testing.T) {
txt := `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
{
"manifestVersion": "2.0.0",
"signatureType": "private",
"signedByOrg": "willbrowne",
"signedByOrgName": "Will Browne",
"rootUrls": [
"http://localhost:3000/"
],
"plugin": "test",
"version": "1.0.0",
"time": 1605807018050,
"keyId": "7e4d0c6a708866e7",
"files": {
"plugin.json": "2bb467c0bfd6c454551419efe475b8bf8573734e73c7bab52b14842adb62886f"
}
}
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
wqIEARMKAAYFAl+2q6oACgkQfk0ManCIZudmzwIJAXWz58cd/91rTXszKPnE
xbVEvERCbjKTtPBQBNQyqEvV+Ig3MuBSNOVy2SOGrMsdbS6lONgvgt4Cm+iS
wV+vYifkAgkBJtg/9DMB7/iX5O0h49CtSltcpfBFXlGqIeOwRac/yENzRzAA
khdr/tZ1PDgRxMqB/u+Vtbpl0xSxgblnrDOYMSI=
=rLIE
-----END PGP SIGNATURE-----`
t.Run("valid manifest", func(t *testing.T) {
manifest, err := readPluginManifest([]byte(txt))
require.NoError(t, err)
require.NotNil(t, manifest)
assert.Equal(t, "test", manifest.Plugin)
assert.Equal(t, "1.0.0", manifest.Version)
assert.Equal(t, int64(1605807018050), manifest.Time)
assert.Equal(t, "7e4d0c6a708866e7", manifest.KeyID)
assert.Equal(t, "2.0.0", manifest.ManifestVersion)
assert.Equal(t, privateType, manifest.SignatureType)
assert.Equal(t, "willbrowne", manifest.SignedByOrg)
assert.Equal(t, "Will Browne", manifest.SignedByOrgName)
assert.Equal(t, []string{"http://localhost:3000/"}, manifest.RootURLs)
assert.Equal(t, []string{"plugin.json"}, fileList(manifest))
})
}
func fileList(manifest *pluginManifest) []string {
var keys []string
for k := range manifest.Files {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
......@@ -21,14 +21,27 @@ var (
PluginStateAlpha PluginState = "alpha"
)
type PluginSignature string
type PluginSignatureState struct {
Status PluginSignatureStatus
Type PluginSignatureType
SigningOrg string
}
type PluginSignatureStatus string
const (
pluginSignatureInternal PluginSignatureStatus = "internal" // core plugin, no signature
pluginSignatureValid PluginSignatureStatus = "valid" // signed and accurate MANIFEST
pluginSignatureInvalid PluginSignatureStatus = "invalid" // invalid signature
pluginSignatureModified PluginSignatureStatus = "modified" // valid signature, but content mismatch
pluginSignatureUnsigned PluginSignatureStatus = "unsigned" // no MANIFEST file
)
type PluginSignatureType string
const (
PluginSignatureInternal PluginSignature = "internal" // core plugin, no signature
PluginSignatureValid PluginSignature = "valid" // signed and accurate MANIFEST
PluginSignatureInvalid PluginSignature = "invalid" // invalid signature
PluginSignatureModified PluginSignature = "modified" // valid signature, but content mismatch
PluginSignatureUnsigned PluginSignature = "unsigned" // no MANIFEST file
grafanaType PluginSignatureType = "grafana"
privateType PluginSignatureType = "private"
)
type PluginNotFoundError struct {
......@@ -62,25 +75,28 @@ type PluginLoader interface {
// PluginBase is the base plugin type.
type PluginBase struct {
Type string `json:"type"`
Name string `json:"name"`
Id string `json:"id"`
Info PluginInfo `json:"info"`
Dependencies PluginDependencies `json:"dependencies"`
Includes []*PluginInclude `json:"includes"`
Module string `json:"module"`
BaseUrl string `json:"baseUrl"`
Category string `json:"category"`
HideFromList bool `json:"hideFromList,omitempty"`
Preload bool `json:"preload"`
State PluginState `json:"state,omitempty"`
Signature PluginSignature `json:"signature"`
Backend bool `json:"backend"`
IncludedInAppId string `json:"-"`
PluginDir string `json:"-"`
DefaultNavUrl string `json:"-"`
IsCorePlugin bool `json:"-"`
Type string `json:"type"`
Name string `json:"name"`
Id string `json:"id"`
Info PluginInfo `json:"info"`
Dependencies PluginDependencies `json:"dependencies"`
Includes []*PluginInclude `json:"includes"`
Module string `json:"module"`
BaseUrl string `json:"baseUrl"`
Category string `json:"category"`
HideFromList bool `json:"hideFromList,omitempty"`
Preload bool `json:"preload"`
State PluginState `json:"state,omitempty"`
Signature PluginSignatureStatus `json:"signature"`
Backend bool `json:"backend"`
IncludedInAppId string `json:"-"`
PluginDir string `json:"-"`
DefaultNavUrl string `json:"-"`
IsCorePlugin bool `json:"-"`
Files []string `json:"-"`
SignatureType PluginSignatureType `json:"-"`
SignatureOrg string `json:"-"`
GrafanaNetVersion string `json:"-"`
GrafanaNetHasUpdate bool `json:"-"`
......@@ -114,6 +130,8 @@ func (pb *PluginBase) registerPlugin(base *PluginBase) error {
// Copy relevant fields from the base
pb.PluginDir = base.PluginDir
pb.Signature = base.Signature
pb.SignatureType = base.SignatureType
pb.SignatureOrg = base.SignatureOrg
Plugins[pb.Id] = pb
return nil
......
......@@ -145,7 +145,7 @@ func (pm *PluginManager) Init() error {
for _, p := range Plugins {
if p.IsCorePlugin {
p.Signature = PluginSignatureInternal
p.Signature = pluginSignatureInternal
} else {
metrics.SetPluginBuildInformation(p.Id, p.Type, p.Info.Version)
}
......@@ -370,7 +370,20 @@ func (s *PluginScanner) loadPlugin(pluginJSONFilePath string) error {
}
pluginCommon.PluginDir = filepath.Dir(pluginJSONFilePath)
pluginCommon.Signature = getPluginSignatureState(s.log, &pluginCommon)
pluginCommon.Files, err = collectPluginFilesWithin(pluginCommon.PluginDir)
if err != nil {
s.log.Warn("Could not collect plugin file information in directory", "pluginID", pluginCommon.Id, "dir", pluginCommon.PluginDir)
return err
}
signatureState, err := getPluginSignatureState(s.log, &pluginCommon)
if err != nil {
s.log.Warn("Could not get plugin signature state", "pluginID", pluginCommon.Id, "err", err)
return err
}
pluginCommon.Signature = signatureState.Status
pluginCommon.SignatureType = signatureState.Type
pluginCommon.SignatureOrg = signatureState.SigningOrg
s.plugins[currentDir] = &pluginCommon
......@@ -383,21 +396,21 @@ func (*PluginScanner) IsBackendOnlyPlugin(pluginType string) bool {
// validateSignature validates a plugin's signature.
func (s *PluginScanner) validateSignature(plugin *PluginBase) *PluginError {
if plugin.Signature == PluginSignatureValid {
if plugin.Signature == pluginSignatureValid {
s.log.Debug("Plugin has valid signature", "id", plugin.Id)
return nil
}
if plugin.Root != nil {
// If a descendant plugin with invalid signature, set signature to that of root
if plugin.IsCorePlugin || plugin.Signature == PluginSignatureInternal {
if plugin.IsCorePlugin || plugin.Signature == pluginSignatureInternal {
s.log.Debug("Not setting descendant plugin's signature to that of root since it's core or internal",
"plugin", plugin.Id, "signature", plugin.Signature, "isCore", plugin.IsCorePlugin)
} else {
s.log.Debug("Setting descendant plugin's signature to that of root", "plugin", plugin.Id,
"root", plugin.Root.Id, "signature", plugin.Signature, "rootSignature", plugin.Root.Signature)
plugin.Signature = plugin.Root.Signature
if plugin.Signature == PluginSignatureValid {
if plugin.Signature == pluginSignatureValid {
s.log.Debug("Plugin has valid signature (inherited from root)", "id", plugin.Id)
return nil
}
......@@ -414,7 +427,7 @@ func (s *PluginScanner) validateSignature(plugin *PluginBase) *PluginError {
}
switch plugin.Signature {
case PluginSignatureUnsigned:
case pluginSignatureUnsigned:
if allowed := s.allowUnsigned(plugin); !allowed {
s.log.Debug("Plugin is unsigned", "id", plugin.Id)
s.errors = append(s.errors, fmt.Errorf("plugin %q is unsigned", plugin.Id))
......@@ -425,13 +438,13 @@ func (s *PluginScanner) validateSignature(plugin *PluginBase) *PluginError {
s.log.Warn("Running an unsigned backend plugin", "pluginID", plugin.Id, "pluginDir",
plugin.PluginDir)
return nil
case PluginSignatureInvalid:
case pluginSignatureInvalid:
s.log.Debug("Plugin %q has an invalid signature", plugin.Id)
s.errors = append(s.errors, fmt.Errorf("plugin %q has an invalid signature", plugin.Id))
return &PluginError{
ErrorCode: signatureInvalid,
}
case PluginSignatureModified:
case pluginSignatureModified:
s.log.Debug("Plugin %q has a modified signature", plugin.Id)
s.errors = append(s.errors, fmt.Errorf("plugin %q's signature has been modified", plugin.Id))
return &PluginError{
......@@ -506,3 +519,23 @@ func GetPluginMarkdown(pluginId string, name string) ([]byte, error) {
}
return data, nil
}
// gets plugin filenames that require verification for plugin signing
func collectPluginFilesWithin(rootDir string) ([]string, error) {
var files []string
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && info.Name() != "MANIFEST.txt" {
file, err := filepath.Rel(rootDir, path)
if err != nil {
return err
}
files = append(files, file)
}
return nil
})
return files, err
}
......@@ -89,12 +89,12 @@ func TestPluginManager_Init(t *testing.T) {
assert.Empty(t, pm.scanningErrors)
})
t.Run("With external back-end plugin with invalid signature", func(t *testing.T) {
t.Run("With external back-end plugin with invalid v1 signature", func(t *testing.T) {
origPluginsPath := setting.PluginsPath
t.Cleanup(func() {
setting.PluginsPath = origPluginsPath
})
setting.PluginsPath = "testdata/invalid-signature"
setting.PluginsPath = "testdata/invalid-v1-signature"
pm := &PluginManager{
Cfg: &setting.Cfg{},
......@@ -158,6 +158,123 @@ func TestPluginManager_Init(t *testing.T) {
assert.Len(t, pm.scanningErrors, 1)
assert.True(t, errors.Is(pm.scanningErrors[0], duplicatePluginError{}))
})
t.Run("With external back-end plugin with valid v2 signature", func(t *testing.T) {
origPluginsPath := setting.PluginsPath
t.Cleanup(func() {
setting.PluginsPath = origPluginsPath
})
setting.PluginsPath = "testdata/valid-v2-signature"
pm := &PluginManager{
Cfg: &setting.Cfg{},
BackendPluginManager: &fakeBackendPluginManager{},
}
err := pm.Init()
require.NoError(t, err)
require.Empty(t, pm.scanningErrors)
pluginId := "test"
assert.NotNil(t, Plugins[pluginId])
assert.Equal(t, "datasource", Plugins[pluginId].Type)
assert.Equal(t, "Test", Plugins[pluginId].Name)
assert.Equal(t, pluginId, Plugins[pluginId].Id)
assert.Equal(t, "1.0.0", Plugins[pluginId].Info.Version)
assert.Equal(t, pluginSignatureValid, Plugins[pluginId].Signature)
assert.Equal(t, grafanaType, Plugins[pluginId].SignatureType)
assert.Equal(t, "Grafana Labs", Plugins[pluginId].SignatureOrg)
assert.False(t, Plugins[pluginId].IsCorePlugin)
})
t.Run("With back-end plugin with invalid v2 private signature (mismatched root URL)", func(t *testing.T) {
origAppURL := setting.AppUrl
origPluginsPath := setting.PluginsPath
t.Cleanup(func() {
setting.AppUrl = origAppURL
setting.PluginsPath = origPluginsPath
})
setting.AppUrl = "http://localhost:1234"
setting.PluginsPath = "testdata/valid-v2-pvt-signature"
pm := &PluginManager{
Cfg: &setting.Cfg{},
}
err := pm.Init()
require.NoError(t, err)
assert.Equal(t, []error{fmt.Errorf(`plugin "test" has an invalid signature`)}, pm.scanningErrors)
assert.Nil(t, Plugins[("test")])
})
t.Run("With back-end plugin with valid v2 private signature", func(t *testing.T) {
origAppURL := setting.AppUrl
origPluginsPath := setting.PluginsPath
t.Cleanup(func() {
setting.AppUrl = origAppURL
setting.PluginsPath = origPluginsPath
})
setting.AppUrl = "http://localhost:3000/"
setting.PluginsPath = "testdata/valid-v2-pvt-signature"
pm := &PluginManager{
Cfg: &setting.Cfg{},
BackendPluginManager: &fakeBackendPluginManager{},
}
err := pm.Init()
require.NoError(t, err)
require.Empty(t, pm.scanningErrors)
pluginId := "test"
assert.NotNil(t, Plugins[pluginId])
assert.Equal(t, "datasource", Plugins[pluginId].Type)
assert.Equal(t, "Test", Plugins[pluginId].Name)
assert.Equal(t, pluginId, Plugins[pluginId].Id)
assert.Equal(t, "1.0.0", Plugins[pluginId].Info.Version)
assert.Equal(t, pluginSignatureValid, Plugins[pluginId].Signature)
assert.Equal(t, privateType, Plugins[pluginId].SignatureType)
assert.Equal(t, "Will Browne", Plugins[pluginId].SignatureOrg)
assert.False(t, Plugins[pluginId].IsCorePlugin)
})
t.Run("With back-end plugin with modified v2 signature (missing file from plugin dir)", func(t *testing.T) {
origAppURL := setting.AppUrl
origPluginsPath := setting.PluginsPath
t.Cleanup(func() {
setting.AppUrl = origAppURL
setting.PluginsPath = origPluginsPath
})
setting.AppUrl = "http://localhost:3000/"
setting.PluginsPath = "testdata/invalid-v2-signature"
pm := &PluginManager{
Cfg: &setting.Cfg{},
BackendPluginManager: &fakeBackendPluginManager{},
}
err := pm.Init()
require.NoError(t, err)
assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors)
assert.Nil(t, Plugins[("test")])
})
t.Run("With back-end plugin with modified v2 signature (unaccounted file in plugin dir)", func(t *testing.T) {
origAppURL := setting.AppUrl
origPluginsPath := setting.PluginsPath
t.Cleanup(func() {
setting.AppUrl = origAppURL
setting.PluginsPath = origPluginsPath
})
setting.AppUrl = "http://localhost:3000/"
setting.PluginsPath = "testdata/invalid-v2-signature-2"
pm := &PluginManager{
Cfg: &setting.Cfg{},
BackendPluginManager: &fakeBackendPluginManager{},
}
err := pm.Init()
require.NoError(t, err)
assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors)
assert.Nil(t, Plugins[("test")])
})
}
func TestPluginManager_IsBackendOnlyPlugin(t *testing.T) {
......
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
{
"manifestVersion": "2.0.0",
"signatureType": "grafana",
"signedByOrg": "grafana",
"signedByOrgName": "Grafana Labs",
"plugin": "test",
"version": "1.0.0",
"time": 1605807330546,
"keyId": "7e4d0c6a708866e7",
"files": {
"plugin.json": "2bb467c0bfd6c454551419efe475b8bf8573734e73c7bab52b14842adb62886f"
}
}
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
wqEEARMKAAYFAl+2rOIACgkQfk0ManCIZudNOwIJAT8FTzwnRFCSLTOaR3F3
2Fh96eRbghokXcQG9WqpQAg8ZiVfGXeWWRNtV+nuQ9VOZOTO0BovWLuMkym2
ci8ABpWOAgd46LkGn3Dd8XVnGmLI6UPqHAXflItOrCMRiGcYJn5PxP1aCz8h
D0JoNI9TIKrhMtM4voU3Qhf3mIOTHueuDNS48w==
=mu2j
-----END PGP SIGNATURE-----
{
"type": "datasource",
"name": "Test",
"id": "test",
"backend": true,
"executable": "test",
"state": "alpha",
"info": {
"version": "1.0.0",
"description": "Test",
"author": {
"name": "Will Browne",
"url": "https://willbrowne.com"
}
}
}
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
{
"manifestVersion": "2.0.0",
"signatureType": "grafana",
"signedByOrg": "grafana",
"signedByOrgName": "Grafana Labs",
"plugin": "test",
"version": "1.0.0",
"time": 1605809299800,
"keyId": "7e4d0c6a708866e7",
"files": {
"plugin.json": "2bb467c0bfd6c454551419efe475b8bf8573734e73c7bab52b14842adb62886f",
"veryImportantFile": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
}
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
wqIEARMKAAYFAl+2tJMACgkQfk0ManCIZueB0AIJAT/PWs226MaIu3eDZy4o
3UH/tIExyY4zR+VSBfTS+Gji5BcIRkIn7bhM1U40KDraDCvQOl3WetgqQkPd
wcSTJJocAgkBrsrxNz/Nl+vw/usre3Funj0hPVS/6NnJXwe6sVH+gAQfeddz
MzYTY/gcUVWp8Y7l/Hg44nry0PS3sr5LQ30w/FY=
=ev+T
-----END PGP SIGNATURE-----
{
"type": "datasource",
"name": "Test",
"id": "test",
"backend": true,
"executable": "test",
"state": "alpha",
"info": {
"version": "1.0.0",
"description": "Test",
"author": {
"name": "Will Browne",
"url": "https://willbrowne.com"
}
}
}
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
{
"manifestVersion": "2.0.0",
"signatureType": "private",
"signedByOrg": "willbrowne",
"signedByOrgName": "Will Browne",
"rootUrls": [
"http://localhost:3000/"
],
"plugin": "test",
"version": "1.0.0",
"time": 1605807018050,
"keyId": "7e4d0c6a708866e7",
"files": {
"plugin.json": "2bb467c0bfd6c454551419efe475b8bf8573734e73c7bab52b14842adb62886f"
}
}
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
wqIEARMKAAYFAl+2q6oACgkQfk0ManCIZudmzwIJAXWz58cd/91rTXszKPnE
xbVEvERCbjKTtPBQBNQyqEvV+Ig3MuBSNOVy2SOGrMsdbS6lONgvgt4Cm+iS
wV+vYifkAgkBJtg/9DMB7/iX5O0h49CtSltcpfBFXlGqIeOwRac/yENzRzAA
khdr/tZ1PDgRxMqB/u+Vtbpl0xSxgblnrDOYMSI=
=rLIE
-----END PGP SIGNATURE-----
{
"type": "datasource",
"name": "Test",
"id": "test",
"backend": true,
"executable": "test",
"state": "alpha",
"info": {
"version": "1.0.0",
"description": "Test",
"author": {
"name": "Will Browne",
"url": "https://willbrowne.com"
}
}
}
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
{
"manifestVersion": "2.0.0",
"signatureType": "grafana",
"signedByOrg": "grafana",
"signedByOrgName": "Grafana Labs",
"plugin": "test",
"version": "1.0.0",
"time": 1605807330546,
"keyId": "7e4d0c6a708866e7",
"files": {
"plugin.json": "2bb467c0bfd6c454551419efe475b8bf8573734e73c7bab52b14842adb62886f"
}
}
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
wqEEARMKAAYFAl+2rOIACgkQfk0ManCIZudNOwIJAT8FTzwnRFCSLTOaR3F3
2Fh96eRbghokXcQG9WqpQAg8ZiVfGXeWWRNtV+nuQ9VOZOTO0BovWLuMkym2
ci8ABpWOAgd46LkGn3Dd8XVnGmLI6UPqHAXflItOrCMRiGcYJn5PxP1aCz8h
D0JoNI9TIKrhMtM4voU3Qhf3mIOTHueuDNS48w==
=mu2j
-----END PGP SIGNATURE-----
{
"type": "datasource",
"name": "Test",
"id": "test",
"backend": true,
"executable": "test",
"state": "alpha",
"info": {
"version": "1.0.0",
"description": "Test",
"author": {
"name": "Will Browne",
"url": "https://willbrowne.com"
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment