Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plugin): respect --insecure #7022

Merged
merged 2 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions pkg/commands/app.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC we need to add insecure option for run flag.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Added 7b1eda6

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