mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-22 07:10:41 -08:00
fix(plugin): respect --insecure (#7022)
Signed-off-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
@@ -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(
|
||||
@@ -89,7 +90,7 @@ func NewApp() *cobra.Command {
|
||||
NewServerCommand(globalFlags),
|
||||
NewConfigCommand(globalFlags),
|
||||
NewConvertCommand(globalFlags),
|
||||
NewPluginCommand(),
|
||||
NewPluginCommand(globalFlags),
|
||||
NewModuleCommand(globalFlags),
|
||||
NewKubernetesCommand(globalFlags),
|
||||
NewSBOMCommand(globalFlags),
|
||||
@@ -719,7 +720,11 @@ 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"},
|
||||
@@ -727,6 +732,13 @@ func NewPluginCommand() *cobra.Command {
|
||||
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{
|
||||
@@ -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
|
||||
@@ -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{
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -21,7 +21,7 @@ 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)
|
||||
}
|
||||
|
||||
@@ -29,11 +29,14 @@ func DownloadToTempDir(ctx context.Context, url string) (string, error) {
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -41,6 +44,14 @@ func Download(ctx context.Context, src, dst, pwd string) error {
|
||||
// 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,
|
||||
|
||||
61
pkg/downloader/downloader_test.go
Normal file
61
pkg/downloader/downloader_test.go
Normal file
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 {
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user