diff --git a/docs/docs/plugin/user-guide.md b/docs/docs/plugin/user-guide.md index f26f741f7e..a02cd66433 100644 --- a/docs/docs/plugin/user-guide.md +++ b/docs/docs/plugin/user-guide.md @@ -40,8 +40,6 @@ $ trivy plugin install referrer This command will download the plugin and install it in the plugin cache. - - Trivy adheres to the XDG specification, so the location depends on whether XDG_DATA_HOME is set. Trivy will now search XDG_DATA_HOME for the location of the Trivy plugins cache. The preference order is as follows: @@ -55,7 +53,10 @@ Furthermore, it is possible to download plugins that are not registered in the i $ trivy plugin install github.com/aquasecurity/trivy-plugin-kubectl ``` ```bash -$ trivy plugin install myplugin.tar.gz +$ trivy plugin install https://github.com/aquasecurity/trivy-plugin-kubectl/archive/refs/heads/main.zip +``` +```bash +$ trivy plugin install ./myplugin.tar.gz ``` If the plugin's Git repository is [properly tagged](./developer-guide.md#tagging-plugin-repositories), you can specify the version to install like this: diff --git a/pkg/plugin/manager.go b/pkg/plugin/manager.go index 91dd30d368..949c87525b 100644 --- a/pkg/plugin/manager.go +++ b/pkg/plugin/manager.go @@ -116,6 +116,14 @@ func (m *Manager) install(ctx context.Context, src string, opts Options) (Plugin } defer os.RemoveAll(tempDir) + if entries, err := os.ReadDir(tempDir); err != nil { + return Plugin{}, xerrors.Errorf("failed to read %s: %w", tempDir, err) + } else if len(entries) == 1 && entries[0].IsDir() { + // A single directory may be contained within an archive file. + // e.g. https://github.com/aquasecurity/trivy-plugin-referrer/archive/refs/heads/main.zip + tempDir = filepath.Join(tempDir, entries[0].Name()) + } + m.logger.DebugContext(ctx, "Loading the plugin metadata...") plugin, err := m.loadMetadata(tempDir) if err != nil { diff --git a/pkg/plugin/manager_unix_test.go b/pkg/plugin/manager_unix_test.go index fca1c8fac2..0250a80d79 100644 --- a/pkg/plugin/manager_unix_test.go +++ b/pkg/plugin/manager_unix_test.go @@ -63,12 +63,17 @@ func modifyManifest(t *testing.T, worktree, version string) { } func TestManager_Install(t *testing.T) { - gs := setupGitRepository(t, "test_plugin", "testdata/test_plugin") + gs := setupGitRepository(t, "test_plugin", "testdata/test_plugin/test_plugin") t.Cleanup(gs.Close) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { zr := zip.NewWriter(w) - require.NoError(t, zr.AddFS(os.DirFS("testdata/test_plugin"))) + switch r.URL.Path { + case "/test_plugin.zip": + require.NoError(t, zr.AddFS(os.DirFS("testdata/test_plugin/test_plugin"))) + case "/test_nested.zip": + require.NoError(t, zr.AddFS(os.DirFS("testdata/test_plugin"))) + } require.NoError(t, zr.Close()) })) t.Cleanup(ts.Close) @@ -119,6 +124,13 @@ func TestManager_Install(t *testing.T) { wantFile: ".trivy/plugins/test_plugin/test.sh", wantLogs: fmt.Sprintf(wantLogs, ts.URL+"/test_plugin.zip", "0.2.0"), }, + { + name: "nested archive", + pluginName: ts.URL + "/test_nested.zip", + want: wantPlugin, + wantFile: ".trivy/plugins/test_plugin/test.sh", + wantLogs: fmt.Sprintf(wantLogs, ts.URL+"/test_nested.zip", "0.2.0"), + }, { name: "local path", pluginName: "testdata/test_plugin", diff --git a/pkg/plugin/testdata/test_plugin/plugin.yaml b/pkg/plugin/testdata/test_plugin/test_plugin/plugin.yaml similarity index 100% rename from pkg/plugin/testdata/test_plugin/plugin.yaml rename to pkg/plugin/testdata/test_plugin/test_plugin/plugin.yaml diff --git a/pkg/plugin/testdata/test_plugin/test.sh b/pkg/plugin/testdata/test_plugin/test_plugin/test.sh similarity index 100% rename from pkg/plugin/testdata/test_plugin/test.sh rename to pkg/plugin/testdata/test_plugin/test_plugin/test.sh