mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-05 20:40:16 -08:00
134 lines
3.8 KiB
Go
134 lines
3.8 KiB
Go
package plugin
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/samber/lo"
|
|
"golang.org/x/xerrors"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/downloader"
|
|
"github.com/aquasecurity/trivy/pkg/log"
|
|
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
|
|
)
|
|
|
|
const indexURL = "https://aquasecurity.github.io/trivy-plugin-index/v1/index.yaml"
|
|
|
|
type Index struct {
|
|
Version int `yaml:"version"`
|
|
Plugins []struct {
|
|
Name string `yaml:"name"`
|
|
Version string `yaml:"version"`
|
|
Maintainer string `yaml:"maintainer"`
|
|
Summary string `yaml:"summary"`
|
|
Repository string `yaml:"repository"`
|
|
Output bool `yaml:"output"`
|
|
} `yaml:"plugins"`
|
|
}
|
|
|
|
func (m *Manager) Update(ctx context.Context, opts Options) error {
|
|
m.logger.InfoContext(ctx, "Updating the plugin index...", log.String("url", m.indexURL))
|
|
|
|
// Download the index file to a temporary directory and copy it to the plugins directory,
|
|
// to avoid removing installed plugins.
|
|
tempDir, err := downloader.DownloadToTempDir(ctx, m.indexURL, downloader.Options{Insecure: opts.Insecure})
|
|
if err != nil {
|
|
return xerrors.Errorf("unable to download the plugin index: %w", err)
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Dir(m.indexPath), fs.ModePerm)
|
|
if err != nil {
|
|
return xerrors.Errorf("failed to create plugin index dir: %w", err)
|
|
}
|
|
|
|
_, err = fsutils.CopyFile(filepath.Join(tempDir, "index.yaml"), m.indexPath)
|
|
if err != nil {
|
|
return xerrors.Errorf("unable to copy the plugin index file: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *Manager) Search(ctx context.Context, keyword string) error {
|
|
index, err := m.loadIndex()
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
m.logger.ErrorContext(ctx, "The plugin index is not found. Please run 'trivy plugin update' to download the index.")
|
|
return xerrors.Errorf("plugin index not found: %w", err)
|
|
} else if err != nil {
|
|
return xerrors.Errorf("unable to load the plugin index: %w", err)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
buf.WriteString(fmt.Sprintf("%-20s %-60s %-20s %s\n", "NAME", "DESCRIPTION", "MAINTAINER", "OUTPUT"))
|
|
for _, p := range index.Plugins {
|
|
if keyword == "" || strings.Contains(p.Name, keyword) || strings.Contains(p.Summary, keyword) {
|
|
s := fmt.Sprintf("%-20s %-60s %-20s %s\n", truncateString(p.Name, 20),
|
|
truncateString(p.Summary, 60), truncateString(p.Maintainer, 20),
|
|
lo.Ternary(p.Output, " ✓", ""))
|
|
buf.WriteString(s)
|
|
}
|
|
}
|
|
|
|
if _, err = fmt.Fprint(m.w, buf.String()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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, opts Options) string {
|
|
// If the index file does not exist, download it first.
|
|
if !fsutils.FileExists(m.indexPath) {
|
|
if err := m.Update(ctx, opts); err != nil {
|
|
m.logger.ErrorContext(ctx, "Failed to update the plugin index", log.Err(err))
|
|
return name
|
|
}
|
|
}
|
|
|
|
index, err := m.loadIndex()
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
m.logger.WarnContext(ctx, "The plugin index is not found. Please run 'trivy plugin update' to download the index.")
|
|
return name
|
|
} else if err != nil {
|
|
m.logger.ErrorContext(ctx, "Unable to load the plugin index", log.Err(err))
|
|
return name
|
|
}
|
|
|
|
for _, p := range index.Plugins {
|
|
if p.Name == name {
|
|
return p.Repository
|
|
}
|
|
}
|
|
return name
|
|
}
|
|
|
|
func (m *Manager) loadIndex() (*Index, error) {
|
|
f, err := os.Open(m.indexPath)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("unable to open the index file: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
var index Index
|
|
if err = yaml.NewDecoder(f).Decode(&index); err != nil {
|
|
return nil, xerrors.Errorf("unable to decode the index file: %w", err)
|
|
}
|
|
|
|
return &index, nil
|
|
}
|
|
|
|
func truncateString(str string, num int) string {
|
|
if len(str) <= num {
|
|
return str
|
|
}
|
|
return str[:num-3] + "..."
|
|
}
|