mirror of https://github.com/grafana/grafana
CLI: Allow relative symlinks in zip archives when installing plugins (#50537)
Earlier we only allowed symlinks in plugins starting with grafana- in zip archives when installing plugins using the grafana-cli. This changes so that symlinks in zip archives containing relative links to files in the zip archive are always allowed when installing plugins. The reasoning behind this is that Grafana per default doesn't load a plugin that has an invalid plugin signature meaning that any symlink must be included in the plugin signature manifest. Co-authored-by: Will Browne <will.browne@grafana.com> Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>pull/50878/head
parent
68691d7775
commit
b47ec36d0d
@ -1,268 +0,0 @@ |
||||
package commands |
||||
|
||||
import ( |
||||
"fmt" |
||||
"io" |
||||
"os" |
||||
"path/filepath" |
||||
"runtime" |
||||
"testing" |
||||
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands/commandstest" |
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models" |
||||
"github.com/stretchr/testify/assert" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestRemoveGitBuildFromName(t *testing.T) { |
||||
pluginName := "datasource-kairosdb" |
||||
|
||||
// The root directory should get renamed to the plugin name
|
||||
paths := map[string]string{ |
||||
"datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/": "datasource-kairosdb/", |
||||
"datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/README.md": "datasource-kairosdb/README.md", |
||||
"datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/partials/": "datasource-kairosdb/partials/", |
||||
"datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/partials/config.html": "datasource-kairosdb/partials/config.html", |
||||
} |
||||
for pth, exp := range paths { |
||||
name := removeGitBuildFromName(pluginName, pth) |
||||
assert.Equal(t, exp, name) |
||||
} |
||||
} |
||||
|
||||
func TestExtractFiles(t *testing.T) { |
||||
t.Run("Should preserve file permissions for plugin backend binaries for linux and darwin", func(t *testing.T) { |
||||
skipWindows(t) |
||||
pluginsDir := setupFakePluginsDir(t) |
||||
|
||||
archive := filepath.Join("testdata", "grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip") |
||||
err := extractFiles(archive, "grafana-simple-json-datasource", pluginsDir, false) |
||||
require.NoError(t, err) |
||||
|
||||
// File in zip has permissions 755
|
||||
fileInfo, err := os.Stat(filepath.Join(pluginsDir, "grafana-simple-json-datasource", |
||||
"simple-plugin_darwin_amd64")) |
||||
require.NoError(t, err) |
||||
assert.Equal(t, "-rwxr-xr-x", fileInfo.Mode().String()) |
||||
|
||||
// File in zip has permission 755
|
||||
fileInfo, err = os.Stat(pluginsDir + "/grafana-simple-json-datasource/simple-plugin_linux_amd64") |
||||
require.NoError(t, err) |
||||
assert.Equal(t, "-rwxr-xr-x", fileInfo.Mode().String()) |
||||
|
||||
// File in zip has permission 644
|
||||
fileInfo, err = os.Stat(pluginsDir + "/grafana-simple-json-datasource/simple-plugin_windows_amd64.exe") |
||||
require.NoError(t, err) |
||||
assert.Equal(t, "-rw-r--r--", fileInfo.Mode().String()) |
||||
|
||||
// File in zip has permission 755
|
||||
fileInfo, err = os.Stat(pluginsDir + "/grafana-simple-json-datasource/non-plugin-binary") |
||||
require.NoError(t, err) |
||||
assert.Equal(t, "-rwxr-xr-x", fileInfo.Mode().String()) |
||||
}) |
||||
|
||||
t.Run("Should ignore symlinks if not allowed", func(t *testing.T) { |
||||
pluginsDir := setupFakePluginsDir(t) |
||||
|
||||
err := extractFiles("testdata/plugin-with-symlink.zip", "plugin-with-symlink", pluginsDir, false) |
||||
require.NoError(t, err) |
||||
|
||||
_, err = os.Stat(pluginsDir + "/plugin-with-symlink/text.txt") |
||||
require.NoError(t, err) |
||||
_, err = os.Stat(pluginsDir + "/plugin-with-symlink/symlink_to_txt") |
||||
assert.Error(t, err) |
||||
}) |
||||
|
||||
t.Run("Should extract symlinks if allowed", func(t *testing.T) { |
||||
skipWindows(t) |
||||
pluginsDir := setupFakePluginsDir(t) |
||||
|
||||
err := extractFiles("testdata/plugin-with-symlink.zip", "plugin-with-symlink", pluginsDir, true) |
||||
require.NoError(t, err) |
||||
|
||||
_, err = os.Stat(pluginsDir + "/plugin-with-symlink/symlink_to_txt") |
||||
require.NoError(t, err) |
||||
}) |
||||
|
||||
t.Run("Should detect if archive members point outside of the destination directory", func(t *testing.T) { |
||||
pluginsDir := setupFakePluginsDir(t) |
||||
|
||||
err := extractFiles("testdata/plugin-with-parent-member.zip", "plugin-with-parent-member", |
||||
pluginsDir, true) |
||||
require.EqualError(t, err, fmt.Sprintf( |
||||
`archive member "../member.txt" tries to write outside of plugin directory: %q, this can be a security risk`, |
||||
pluginsDir, |
||||
)) |
||||
}) |
||||
|
||||
t.Run("Should detect if archive members are absolute", func(t *testing.T) { |
||||
pluginsDir := setupFakePluginsDir(t) |
||||
|
||||
err := extractFiles("testdata/plugin-with-absolute-member.zip", "plugin-with-absolute-member", |
||||
pluginsDir, true) |
||||
require.EqualError(t, err, fmt.Sprintf( |
||||
`archive member "/member.txt" tries to write outside of plugin directory: %q, this can be a security risk`, |
||||
pluginsDir, |
||||
)) |
||||
}) |
||||
} |
||||
|
||||
func TestInstallPluginCommand(t *testing.T) { |
||||
pluginsDir := setupFakePluginsDir(t) |
||||
c, err := commandstest.NewCliContext(map[string]string{"pluginsDir": pluginsDir}) |
||||
require.NoError(t, err) |
||||
|
||||
client := &commandstest.FakeGrafanaComClient{ |
||||
GetPluginFunc: func(pluginId, repoUrl string) (models.Plugin, error) { |
||||
require.Equal(t, "test-plugin-panel", pluginId) |
||||
plugin := models.Plugin{ |
||||
ID: "test-plugin-panel", |
||||
Category: "", |
||||
Versions: []models.Version{ |
||||
{ |
||||
Commit: "commit", |
||||
URL: "url", |
||||
Version: "1.0.0", |
||||
Arch: map[string]models.ArchMeta{ |
||||
fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH): { |
||||
SHA256: "test", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
return plugin, nil |
||||
}, |
||||
DownloadFileFunc: func(pluginName string, tmpFile *os.File, url string, checksum string) (err error) { |
||||
require.Equal(t, "test-plugin-panel", pluginName) |
||||
require.Equal(t, "/test-plugin-panel/versions/1.0.0/download", url) |
||||
require.Equal(t, "test", checksum) |
||||
f, err := os.Open("testdata/grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip") |
||||
require.NoError(t, err) |
||||
_, err = io.Copy(tmpFile, f) |
||||
require.NoError(t, err) |
||||
return nil |
||||
}, |
||||
} |
||||
|
||||
err = InstallPlugin("test-plugin-panel", "", c, client) |
||||
assert.NoError(t, err) |
||||
} |
||||
|
||||
func TestSelectVersion(t *testing.T) { |
||||
t.Run("Should return error when requested version does not exist", func(t *testing.T) { |
||||
_, err := SelectVersion( |
||||
makePluginWithVersions(versionArg{Version: "version"}), |
||||
"1.1.1", |
||||
) |
||||
assert.Error(t, err) |
||||
}) |
||||
|
||||
t.Run("Should return error when no version supports current arch", func(t *testing.T) { |
||||
_, err := SelectVersion( |
||||
makePluginWithVersions(versionArg{Version: "version", Arch: []string{"non-existent"}}), |
||||
"", |
||||
) |
||||
assert.Error(t, err) |
||||
}) |
||||
|
||||
t.Run("Should return error when requested version does not support current arch", func(t *testing.T) { |
||||
_, err := SelectVersion( |
||||
makePluginWithVersions( |
||||
versionArg{Version: "2.0.0"}, |
||||
versionArg{Version: "1.1.1", Arch: []string{"non-existent"}}, |
||||
), |
||||
"1.1.1", |
||||
) |
||||
assert.Error(t, err) |
||||
}) |
||||
|
||||
t.Run("Should return latest available for arch when no version specified", func(t *testing.T) { |
||||
ver, err := SelectVersion( |
||||
makePluginWithVersions( |
||||
versionArg{Version: "2.0.0", Arch: []string{"non-existent"}}, |
||||
versionArg{Version: "1.0.0"}, |
||||
), |
||||
"", |
||||
) |
||||
require.NoError(t, err) |
||||
assert.Equal(t, "1.0.0", ver.Version) |
||||
}) |
||||
|
||||
t.Run("Should return latest version when no version specified", func(t *testing.T) { |
||||
ver, err := SelectVersion( |
||||
makePluginWithVersions(versionArg{Version: "2.0.0"}, versionArg{Version: "1.0.0"}), |
||||
"", |
||||
) |
||||
require.NoError(t, err) |
||||
assert.Equal(t, "2.0.0", ver.Version) |
||||
}) |
||||
|
||||
t.Run("Should return requested version", func(t *testing.T) { |
||||
ver, err := SelectVersion( |
||||
makePluginWithVersions( |
||||
versionArg{Version: "2.0.0"}, |
||||
versionArg{Version: "1.0.0"}, |
||||
), |
||||
"1.0.0", |
||||
) |
||||
require.NoError(t, err) |
||||
assert.Equal(t, "1.0.0", ver.Version) |
||||
}) |
||||
} |
||||
|
||||
func setupFakePluginsDir(t *testing.T) string { |
||||
dirname := "testdata/fake-plugins-dir" |
||||
err := os.RemoveAll(dirname) |
||||
require.NoError(t, err) |
||||
|
||||
err = os.MkdirAll(dirname, 0750) |
||||
require.NoError(t, err) |
||||
t.Cleanup(func() { |
||||
err := os.RemoveAll(dirname) |
||||
assert.NoError(t, err) |
||||
}) |
||||
|
||||
dirname, err = filepath.Abs(dirname) |
||||
require.NoError(t, err) |
||||
|
||||
return dirname |
||||
} |
||||
|
||||
func skipWindows(t *testing.T) { |
||||
if runtime.GOOS == "windows" { |
||||
t.Skip("Skipping test on Windows") |
||||
} |
||||
} |
||||
|
||||
type versionArg struct { |
||||
Version string |
||||
Arch []string |
||||
} |
||||
|
||||
func makePluginWithVersions(versions ...versionArg) *models.Plugin { |
||||
plugin := &models.Plugin{ |
||||
ID: "", |
||||
Category: "", |
||||
Versions: []models.Version{}, |
||||
} |
||||
|
||||
for _, version := range versions { |
||||
ver := models.Version{ |
||||
Version: version.Version, |
||||
Commit: fmt.Sprintf("commit_%s", version.Version), |
||||
URL: fmt.Sprintf("url_%s", version.Version), |
||||
} |
||||
if version.Arch != nil { |
||||
ver.Arch = map[string]models.ArchMeta{} |
||||
for _, arch := range version.Arch { |
||||
ver.Arch[arch] = models.ArchMeta{ |
||||
SHA256: fmt.Sprintf("sha256_%s", arch), |
||||
} |
||||
} |
||||
} |
||||
plugin.Versions = append(plugin.Versions, ver) |
||||
} |
||||
|
||||
return plugin |
||||
} |
@ -0,0 +1,358 @@ |
||||
package installer |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"os" |
||||
"path/filepath" |
||||
"runtime" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestInstall(t *testing.T) { |
||||
testDir := "./testdata/tmpInstallPluginDir" |
||||
err := os.Mkdir(testDir, os.ModePerm) |
||||
require.NoError(t, err) |
||||
|
||||
t.Cleanup(func() { |
||||
err = os.RemoveAll(testDir) |
||||
require.NoError(t, err) |
||||
}) |
||||
|
||||
pluginID := "test-app" |
||||
|
||||
i := &Installer{log: &fakeLogger{}} |
||||
err = i.Install(context.Background(), pluginID, "", testDir, "./testdata/plugin-with-symlinks.zip", "") |
||||
require.NoError(t, err) |
||||
|
||||
// verify extracted contents
|
||||
files, err := ioutil.ReadDir(filepath.Join(testDir, pluginID)) |
||||
require.NoError(t, err) |
||||
require.Len(t, files, 6) |
||||
require.Equal(t, files[0].Name(), "MANIFEST.txt") |
||||
require.Equal(t, files[1].Name(), "dashboards") |
||||
require.Equal(t, files[2].Name(), "extra") |
||||
require.Equal(t, os.ModeSymlink, files[2].Mode()&os.ModeSymlink) |
||||
require.Equal(t, files[3].Name(), "plugin.json") |
||||
require.Equal(t, files[4].Name(), "symlink_to_txt") |
||||
require.Equal(t, os.ModeSymlink, files[4].Mode()&os.ModeSymlink) |
||||
require.Equal(t, files[5].Name(), "text.txt") |
||||
} |
||||
|
||||
func TestUninstall(t *testing.T) { |
||||
i := &Installer{log: &fakeLogger{}} |
||||
|
||||
pluginDir := t.TempDir() |
||||
pluginJSON := filepath.Join(pluginDir, "plugin.json") |
||||
_, err := os.Create(pluginJSON) |
||||
require.NoError(t, err) |
||||
|
||||
err = i.Uninstall(context.Background(), pluginDir) |
||||
require.NoError(t, err) |
||||
|
||||
_, err = os.Stat(pluginDir) |
||||
require.True(t, os.IsNotExist(err)) |
||||
|
||||
t.Run("Uninstall will search in nested dir folder for plugin.json", func(t *testing.T) { |
||||
pluginDistDir := filepath.Join(t.TempDir(), "dist") |
||||
err = os.Mkdir(pluginDistDir, os.ModePerm) |
||||
require.NoError(t, err) |
||||
pluginJSON = filepath.Join(pluginDistDir, "plugin.json") |
||||
_, err = os.Create(pluginJSON) |
||||
require.NoError(t, err) |
||||
|
||||
pluginDir = filepath.Dir(pluginDistDir) |
||||
|
||||
err = i.Uninstall(context.Background(), pluginDir) |
||||
require.NoError(t, err) |
||||
|
||||
_, err = os.Stat(pluginDir) |
||||
require.True(t, os.IsNotExist(err)) |
||||
}) |
||||
|
||||
t.Run("Uninstall will not delete folder if cannot recognize plugin structure", func(t *testing.T) { |
||||
pluginDir = t.TempDir() |
||||
err = i.Uninstall(context.Background(), pluginDir) |
||||
require.EqualError(t, err, fmt.Sprintf("tried to remove %s, but it doesn't seem to be a plugin", pluginDir)) |
||||
|
||||
_, err = os.Stat(pluginDir) |
||||
require.False(t, os.IsNotExist(err)) |
||||
}) |
||||
} |
||||
|
||||
func TestExtractFiles(t *testing.T) { |
||||
i := &Installer{log: &fakeLogger{}} |
||||
pluginsDir := setupFakePluginsDir(t) |
||||
|
||||
t.Run("Should preserve file permissions for plugin backend binaries for linux and darwin", func(t *testing.T) { |
||||
skipWindows(t) |
||||
|
||||
archive := filepath.Join("testdata", "grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip") |
||||
err := i.extractFiles(archive, "grafana-simple-json-datasource", pluginsDir) |
||||
require.NoError(t, err) |
||||
|
||||
// File in zip has permissions 755
|
||||
fileInfo, err := os.Stat(filepath.Join(pluginsDir, "grafana-simple-json-datasource", "simple-plugin_darwin_amd64")) |
||||
require.NoError(t, err) |
||||
require.Equal(t, "-rwxr-xr-x", fileInfo.Mode().String()) |
||||
|
||||
// File in zip has permission 755
|
||||
fileInfo, err = os.Stat(pluginsDir + "/grafana-simple-json-datasource/simple-plugin_linux_amd64") |
||||
require.NoError(t, err) |
||||
require.Equal(t, "-rwxr-xr-x", fileInfo.Mode().String()) |
||||
|
||||
// File in zip has permission 644
|
||||
fileInfo, err = os.Stat(pluginsDir + "/grafana-simple-json-datasource/simple-plugin_windows_amd64.exe") |
||||
require.NoError(t, err) |
||||
require.Equal(t, "-rw-r--r--", fileInfo.Mode().String()) |
||||
|
||||
// File in zip has permission 755
|
||||
fileInfo, err = os.Stat(pluginsDir + "/grafana-simple-json-datasource/non-plugin-binary") |
||||
require.NoError(t, err) |
||||
require.Equal(t, "-rwxr-xr-x", fileInfo.Mode().String()) |
||||
}) |
||||
|
||||
t.Run("Should extract file with relative symlink", func(t *testing.T) { |
||||
skipWindows(t) |
||||
|
||||
err := i.extractFiles("testdata/plugin-with-symlink.zip", "plugin-with-symlink", pluginsDir) |
||||
require.NoError(t, err) |
||||
|
||||
_, err = os.Stat(pluginsDir + "/plugin-with-symlink/symlink_to_txt") |
||||
require.NoError(t, err) |
||||
|
||||
target, err := filepath.EvalSymlinks(pluginsDir + "/plugin-with-symlink/symlink_to_txt") |
||||
require.NoError(t, err) |
||||
require.Equal(t, pluginsDir+"/plugin-with-symlink/text.txt", target) |
||||
}) |
||||
|
||||
t.Run("Should extract directory with relative symlink", func(t *testing.T) { |
||||
skipWindows(t) |
||||
|
||||
err := i.extractFiles("testdata/plugin-with-symlink-dir.zip", "plugin-with-symlink-dir", pluginsDir) |
||||
require.NoError(t, err) |
||||
|
||||
_, err = os.Stat(pluginsDir + "/plugin-with-symlink-dir/symlink_to_dir") |
||||
require.NoError(t, err) |
||||
|
||||
target, err := filepath.EvalSymlinks(pluginsDir + "/plugin-with-symlink-dir/symlink_to_dir") |
||||
require.NoError(t, err) |
||||
require.Equal(t, pluginsDir+"/plugin-with-symlink-dir/dir", target) |
||||
}) |
||||
|
||||
t.Run("Should not extract file with absolute symlink", func(t *testing.T) { |
||||
skipWindows(t) |
||||
|
||||
err := i.extractFiles("testdata/plugin-with-absolute-symlink.zip", "plugin-with-absolute-symlink", pluginsDir) |
||||
require.NoError(t, err) |
||||
|
||||
_, err = os.Stat(pluginsDir + "/plugin-with-absolute-symlink/test.txt") |
||||
require.True(t, os.IsNotExist(err)) |
||||
}) |
||||
|
||||
t.Run("Should not extract directory with absolute symlink", func(t *testing.T) { |
||||
skipWindows(t) |
||||
|
||||
err := i.extractFiles("testdata/plugin-with-absolute-symlink-dir.zip", "plugin-with-absolute-symlink-dir", pluginsDir) |
||||
require.NoError(t, err) |
||||
|
||||
_, err = os.Stat(pluginsDir + "/plugin-with-absolute-symlink-dir/target") |
||||
require.True(t, os.IsNotExist(err)) |
||||
}) |
||||
|
||||
t.Run("Should detect if archive members point outside of the destination directory", func(t *testing.T) { |
||||
err := i.extractFiles("testdata/plugin-with-parent-member.zip", "plugin-with-parent-member", pluginsDir) |
||||
require.EqualError(t, err, fmt.Sprintf( |
||||
`archive member "../member.txt" tries to write outside of plugin directory: %q, this can be a security risk`, |
||||
pluginsDir, |
||||
)) |
||||
}) |
||||
|
||||
t.Run("Should detect if archive members are absolute", func(t *testing.T) { |
||||
err := i.extractFiles("testdata/plugin-with-absolute-member.zip", "plugin-with-absolute-member", pluginsDir) |
||||
require.EqualError(t, err, fmt.Sprintf( |
||||
`archive member "/member.txt" tries to write outside of plugin directory: %q, this can be a security risk`, |
||||
pluginsDir, |
||||
)) |
||||
}) |
||||
} |
||||
|
||||
func TestSelectVersion(t *testing.T) { |
||||
i := &Installer{log: &fakeLogger{}} |
||||
|
||||
t.Run("Should return error when requested version does not exist", func(t *testing.T) { |
||||
_, err := i.selectVersion(createPlugin(versionArg{version: "version"}), "1.1.1") |
||||
require.Error(t, err) |
||||
}) |
||||
|
||||
t.Run("Should return error when no version supports current arch", func(t *testing.T) { |
||||
_, err := i.selectVersion(createPlugin(versionArg{version: "version", arch: []string{"non-existent"}}), "") |
||||
require.Error(t, err) |
||||
}) |
||||
|
||||
t.Run("Should return error when requested version does not support current arch", func(t *testing.T) { |
||||
_, err := i.selectVersion(createPlugin( |
||||
versionArg{version: "2.0.0"}, |
||||
versionArg{version: "1.1.1", arch: []string{"non-existent"}}, |
||||
), "1.1.1") |
||||
require.Error(t, err) |
||||
}) |
||||
|
||||
t.Run("Should return latest available for arch when no version specified", func(t *testing.T) { |
||||
ver, err := i.selectVersion(createPlugin( |
||||
versionArg{version: "2.0.0", arch: []string{"non-existent"}}, |
||||
versionArg{version: "1.0.0"}, |
||||
), "") |
||||
require.NoError(t, err) |
||||
require.Equal(t, "1.0.0", ver.Version) |
||||
}) |
||||
|
||||
t.Run("Should return latest version when no version specified", func(t *testing.T) { |
||||
ver, err := i.selectVersion(createPlugin(versionArg{version: "2.0.0"}, versionArg{version: "1.0.0"}), "") |
||||
require.NoError(t, err) |
||||
require.Equal(t, "2.0.0", ver.Version) |
||||
}) |
||||
|
||||
t.Run("Should return requested version", func(t *testing.T) { |
||||
ver, err := i.selectVersion(createPlugin(versionArg{version: "2.0.0"}, versionArg{version: "1.0.0"}), "1.0.0") |
||||
require.NoError(t, err) |
||||
require.Equal(t, "1.0.0", ver.Version) |
||||
}) |
||||
} |
||||
|
||||
func TestRemoveGitBuildFromName(t *testing.T) { |
||||
// The root directory should get renamed to the plugin name
|
||||
paths := map[string]string{ |
||||
"datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/": "datasource-kairosdb/", |
||||
"datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/README.md": "datasource-kairosdb/README.md", |
||||
"datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/partials/": "datasource-kairosdb/partials/", |
||||
"datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/partials/config.html": "datasource-kairosdb/partials/config.html", |
||||
} |
||||
for p, exp := range paths { |
||||
name := removeGitBuildFromName(p, "datasource-kairosdb") |
||||
require.Equal(t, exp, name) |
||||
} |
||||
} |
||||
|
||||
func TestIsSymlinkRelativeTo(t *testing.T) { |
||||
tcs := []struct { |
||||
desc string |
||||
basePath string |
||||
symlinkDestPath string |
||||
symlinkOrigPath string |
||||
expected bool |
||||
}{ |
||||
{ |
||||
desc: "Symbolic link pointing to relative file within basePath should return true", |
||||
basePath: "/dir", |
||||
symlinkDestPath: "test.txt", |
||||
symlinkOrigPath: "/dir/sub-dir/test1.txt", |
||||
expected: true, |
||||
}, |
||||
{ |
||||
desc: "Symbolic link pointing to relative file within basePath should return true", |
||||
basePath: "/dir", |
||||
symlinkDestPath: "test.txt", |
||||
symlinkOrigPath: "/dir/test1.txt", |
||||
expected: true, |
||||
}, |
||||
{ |
||||
desc: "Symbolic link pointing to relative file within basePath should return true", |
||||
basePath: "/dir", |
||||
symlinkDestPath: "../etc/test.txt", |
||||
symlinkOrigPath: "/dir/sub-dir/test1.txt", |
||||
expected: true, |
||||
}, |
||||
{ |
||||
desc: "Symbolic link pointing to absolute directory outside basePath should return false", |
||||
basePath: "/dir", |
||||
symlinkDestPath: "/etc/test.txt", |
||||
symlinkOrigPath: "/dir/sub-dir/test1.txt", |
||||
expected: false, |
||||
}, |
||||
{ |
||||
desc: "Symbolic link pointing to relative file outside basePath should return false", |
||||
basePath: "/dir", |
||||
symlinkDestPath: "../../etc/test.txt", |
||||
symlinkOrigPath: "/dir/sub-dir/test1.txt", |
||||
expected: false, |
||||
}, |
||||
} |
||||
|
||||
for _, tc := range tcs { |
||||
t.Run(tc.desc, func(t *testing.T) { |
||||
actual := isSymlinkRelativeTo(tc.basePath, tc.symlinkDestPath, tc.symlinkOrigPath) |
||||
require.Equal(t, tc.expected, actual) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func setupFakePluginsDir(t *testing.T) string { |
||||
dir := "testdata/fake-plugins-dir" |
||||
err := os.RemoveAll(dir) |
||||
require.NoError(t, err) |
||||
|
||||
err = os.MkdirAll(dir, 0750) |
||||
require.NoError(t, err) |
||||
t.Cleanup(func() { |
||||
err = os.RemoveAll(dir) |
||||
require.NoError(t, err) |
||||
}) |
||||
|
||||
dir, err = filepath.Abs(dir) |
||||
require.NoError(t, err) |
||||
|
||||
return dir |
||||
} |
||||
|
||||
func skipWindows(t *testing.T) { |
||||
if runtime.GOOS == "windows" { |
||||
t.Skip("Skipping test on Windows") |
||||
} |
||||
} |
||||
|
||||
type versionArg struct { |
||||
version string |
||||
arch []string |
||||
} |
||||
|
||||
func createPlugin(versions ...versionArg) *Plugin { |
||||
p := &Plugin{ |
||||
Versions: []Version{}, |
||||
} |
||||
|
||||
for _, version := range versions { |
||||
ver := Version{ |
||||
Version: version.version, |
||||
Commit: fmt.Sprintf("commit_%s", version.version), |
||||
URL: fmt.Sprintf("url_%s", version.version), |
||||
} |
||||
if version.arch != nil { |
||||
ver.Arch = map[string]ArchMeta{} |
||||
for _, arch := range version.arch { |
||||
ver.Arch[arch] = ArchMeta{ |
||||
SHA256: fmt.Sprintf("sha256_%s", arch), |
||||
} |
||||
} |
||||
} |
||||
p.Versions = append(p.Versions, ver) |
||||
} |
||||
|
||||
return p |
||||
} |
||||
|
||||
type fakeLogger struct{} |
||||
|
||||
func (f *fakeLogger) Successf(_ string, _ ...interface{}) {} |
||||
func (f *fakeLogger) Failuref(_ string, _ ...interface{}) {} |
||||
func (f *fakeLogger) Info(_ ...interface{}) {} |
||||
func (f *fakeLogger) Infof(_ string, _ ...interface{}) {} |
||||
func (f *fakeLogger) Debug(_ ...interface{}) {} |
||||
func (f *fakeLogger) Debugf(_ string, _ ...interface{}) {} |
||||
func (f *fakeLogger) Warn(_ ...interface{}) {} |
||||
func (f *fakeLogger) Warnf(_ string, _ ...interface{}) {} |
||||
func (f *fakeLogger) Error(_ ...interface{}) {} |
||||
func (f *fakeLogger) Errorf(_ string, _ ...interface{}) {} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue