Skip to content

Commit

Permalink
fix(plugin): respect --insecure (#7022)
Browse files Browse the repository at this point in the history
Signed-off-by: knqyf263 <knqyf263@gmail.com>
  • Loading branch information
knqyf263 authored Jun 26, 2024
1 parent 8d618e4 commit 3d02a31
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 19 deletions.
25 changes: 20 additions & 5 deletions pkg/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Use "{{.CommandPath}} [command] --help" for more information about a command.{{e

// NewApp is the factory method to return Trivy CLI
func NewApp() *cobra.Command {
cobra.EnableTraverseRunHooks = true // To execute persistent pre-run hooks from all parents.
globalFlags := flag.NewGlobalFlagGroup()
rootCmd := NewRootCommand(globalFlags)
rootCmd.AddGroup(
Expand Down Expand Up @@ -89,7 +90,7 @@ func NewApp() *cobra.Command {
NewServerCommand(globalFlags),
NewConfigCommand(globalFlags),
NewConvertCommand(globalFlags),
NewPluginCommand(),
NewPluginCommand(globalFlags),
NewModuleCommand(globalFlags),
NewKubernetesCommand(globalFlags),
NewSBOMCommand(globalFlags),
Expand Down Expand Up @@ -719,14 +720,25 @@ func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
return cmd
}

func NewPluginCommand() *cobra.Command {
func NewPluginCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
var pluginOptions flag.Options
pluginFlags := &flag.Flags{
GlobalFlagGroup: globalFlags,
}
cmd := &cobra.Command{
Use: "plugin subcommand",
Aliases: []string{"p"},
GroupID: groupManagement,
Short: "Manage plugins",
SilenceErrors: true,
SilenceUsage: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
pluginOptions, err = pluginFlags.ToOptions(args)
if err != nil {
return err
}
return nil
},
}
cmd.AddCommand(
&cobra.Command{
Expand All @@ -746,7 +758,7 @@ func NewPluginCommand() *cobra.Command {
DisableFlagsInUseLine: true,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if _, err := plugin.Install(cmd.Context(), args[0], plugin.Options{}); err != nil {
if _, err := plugin.Install(cmd.Context(), args[0], plugin.Options{Insecure: pluginOptions.Insecure}); err != nil {
return xerrors.Errorf("plugin install error: %w", err)
}
return nil
Expand Down Expand Up @@ -805,7 +817,10 @@ func NewPluginCommand() *cobra.Command {
Short: "Run a plugin on the fly",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return plugin.Run(cmd.Context(), args[0], plugin.Options{Args: args[1:]})
return plugin.Run(cmd.Context(), args[0], plugin.Options{
Args: args[1:],
Insecure: pluginOptions.Insecure,
})
},
},
&cobra.Command{
Expand All @@ -816,7 +831,7 @@ func NewPluginCommand() *cobra.Command {
SilenceUsage: true,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
if err := plugin.Update(cmd.Context()); err != nil {
if err := plugin.Update(cmd.Context(), plugin.Options{Insecure: pluginOptions.Insecure}); err != nil {
return xerrors.Errorf("plugin update error: %w", err)
}
return nil
Expand Down
19 changes: 15 additions & 4 deletions pkg/downloader/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
)

// DownloadToTempDir downloads the configured source to a temp dir.
func DownloadToTempDir(ctx context.Context, url string) (string, error) {
tempDir, err := os.MkdirTemp("", "trivy-plugin")
func DownloadToTempDir(ctx context.Context, url string, insecure bool) (string, error) {
tempDir, err := os.MkdirTemp("", "trivy-download")
if err != nil {
return "", xerrors.Errorf("failed to create a temp dir: %w", err)
}
Expand All @@ -21,26 +21,37 @@ func DownloadToTempDir(ctx context.Context, url string) (string, error) {
return "", xerrors.Errorf("unable to get the current dir: %w", err)
}

if err = Download(ctx, url, tempDir, pwd); err != nil {
if err = Download(ctx, url, tempDir, pwd, insecure); err != nil {
return "", xerrors.Errorf("download error: %w", err)
}

return tempDir, nil
}

// Download downloads the configured source to the destination.
func Download(ctx context.Context, src, dst, pwd string) error {
func Download(ctx context.Context, src, dst, pwd string, insecure bool) error {
// go-getter doesn't allow the dst directory already exists if the src is directory.
_ = os.RemoveAll(dst)

var opts []getter.ClientOption
if insecure {
opts = append(opts, getter.WithInsecure())
}

// Clone the global map so that it will not be accessed concurrently.
getters := maps.Clone(getter.Getters)

// Overwrite the file getter so that a file will be copied
getters["file"] = &getter.FileGetter{Copy: true}

// Since "httpGetter" is a global pointer and the state is shared,
// once it is executed without "WithInsecure()",
// it cannot enable WithInsecure() afterwards because its state is preserved.
// cf. https://github.com/hashicorp/go-getter/blob/5a63fd9c0d5b8da8a6805e8c283f46f0dacb30b3/get.go#L63-L65
httpGetter := &getter.HttpGetter{Netrc: true}
getters["http"] = httpGetter
getters["https"] = httpGetter

// Build the client
client := &getter.Client{
Ctx: ctx,
Expand Down
61 changes: 61 additions & 0 deletions pkg/downloader/downloader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package downloader_test

import (
"context"
"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/aquasecurity/trivy/pkg/downloader"
)

func TestDownload(t *testing.T) {
// Set up a test server with a self-signed certificate
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("test content"))
require.NoError(t, err)
}))
defer server.Close()

tests := []struct {
name string
insecure bool
wantErr bool
}{
{
"Secure (should fail)",
false,
true,
},
{
"Insecure (should succeed)",
true,
false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set up the destination path
dst := t.TempDir()

// Execute the download
err := downloader.Download(context.Background(), server.URL, dst, "", tt.insecure)

if tt.wantErr {
assert.Error(t, err)
return
}
require.NoError(t, err)

// Check the content of the downloaded file
content, err := os.ReadFile(dst)
require.NoError(t, err)
assert.Equal(t, "test content", string(content))
})
}
}
3 changes: 2 additions & 1 deletion pkg/oci/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir s
}

// Decompress the downloaded file if it is compressed and copy it into the dst
if err = downloader.Download(ctx, f.Name(), dir, dir); err != nil {
// NOTE: it's local copying, the insecure option doesn't matter.
if err = downloader.Download(ctx, f.Name(), dir, dir, false); err != nil {
return xerrors.Errorf("download error: %w", err)
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/plugin/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ type Index struct {
} `yaml:"plugins"`
}

func (m *Manager) Update(ctx context.Context) error {
func (m *Manager) Update(ctx context.Context, opts Options) error {
m.logger.InfoContext(ctx, "Updating the plugin index...", log.String("url", m.indexURL))
if err := downloader.Download(ctx, m.indexURL, filepath.Dir(m.indexPath), ""); err != nil {
if err := downloader.Download(ctx, m.indexURL, filepath.Dir(m.indexPath), "", opts.Insecure); err != nil {
return xerrors.Errorf("unable to download the plugin index: %w", err)
}
return nil
Expand Down Expand Up @@ -69,10 +69,10 @@ func (m *Manager) Search(ctx context.Context, keyword string) error {

// tryIndex returns the repository URL if the plugin name is found in the index.
// Otherwise, it returns the input name.
func (m *Manager) tryIndex(ctx context.Context, name string) string {
func (m *Manager) tryIndex(ctx context.Context, name string, opts Options) string {
// If the index file does not exist, download it first.
if !fsutils.FileExists(m.indexPath) {
if err := m.Update(ctx); err != nil {
if err := m.Update(ctx, opts); err != nil {
m.logger.ErrorContext(ctx, "Failed to update the plugin index", log.Err(err))
return name
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/plugin/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestManager_Update(t *testing.T) {
t.Cleanup(ts.Close)

manager := plugin.NewManager(plugin.WithIndexURL(ts.URL + "/index.yaml"))
err := manager.Update(context.Background())
err := manager.Update(context.Background(), plugin.Options{})
require.NoError(t, err)

indexPath := filepath.Join(tempDir, ".trivy", "plugins", "index.yaml")
Expand Down
6 changes: 3 additions & 3 deletions pkg/plugin/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ func Upgrade(ctx context.Context, names []string) error { return defaultManager(
func Uninstall(ctx context.Context, name string) error { return defaultManager().Uninstall(ctx, name) }
func Information(name string) error { return defaultManager().Information(name) }
func List(ctx context.Context) error { return defaultManager().List(ctx) }
func Update(ctx context.Context) error { return defaultManager().Update(ctx) }
func Update(ctx context.Context, opts Options) error { return defaultManager().Update(ctx, opts) }
func Search(ctx context.Context, keyword string) error { return defaultManager().Search(ctx, keyword) }

// Install installs a plugin
func (m *Manager) Install(ctx context.Context, arg string, opts Options) (Plugin, error) {
input := m.parseArg(ctx, arg)
input.name = m.tryIndex(ctx, input.name)
input.name = m.tryIndex(ctx, input.name, opts)

// If the plugin is already installed, it skips installing the plugin.
if p, installed := m.isInstalled(ctx, input.name, input.version); installed {
Expand All @@ -111,7 +111,7 @@ func (m *Manager) Install(ctx context.Context, arg string, opts Options) (Plugin
}

func (m *Manager) install(ctx context.Context, src string, opts Options) (Plugin, error) {
tempDir, err := downloader.DownloadToTempDir(ctx, src)
tempDir, err := downloader.DownloadToTempDir(ctx, src, opts.Insecure)
if err != nil {
return Plugin{}, xerrors.Errorf("download failed: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type Options struct {
Args []string
Stdin io.Reader // For output plugin
Platform ftypes.Platform
Insecure bool
}

func (p *Plugin) Cmd(ctx context.Context, opts Options) (*exec.Cmd, error) {
Expand Down Expand Up @@ -154,7 +155,7 @@ func (p *Plugin) install(ctx context.Context, dst, pwd string, opts Options) err
p.Installed.Platform = lo.FromPtr(platform.Selector)

log.DebugContext(ctx, "Downloading the execution file...", log.String("uri", platform.URI))
if err = downloader.Download(ctx, platform.URI, dst, pwd); err != nil {
if err = downloader.Download(ctx, platform.URI, dst, pwd, opts.Insecure); err != nil {
return xerrors.Errorf("unable to download the execution file (%s): %w", platform.URI, err)
}
return nil
Expand Down

0 comments on commit 3d02a31

Please sign in to comment.