/
githubmirror
/
trivy
Обзор
Документация
Войти
/
githubmirror
/
trivy
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
pkg/flag/report_flags.go
322 строки
10 KB
DmitriyLewen
fix(flag): validate template file extension (#10296)
18 мар 2026, 12:28
Не верифицирован
18 мар 2026, 12:28
20458b8
Код
Авторство
О чём код?
package flag import ( "path/filepath" "slices" "strings" "github.com/mattn/go-shellwords" "github.com/spf13/viper" "golang.org/x/xerrors" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/compliance/spec" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/result" "github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/utils/fsutils" xslices "github.com/aquasecurity/trivy/pkg/x/slices" xstrings "github.com/aquasecurity/trivy/pkg/x/strings" ) // e.g. config yaml: // // format: table // dependency-tree: true // severity: HIGH,CRITICAL var ( FormatFlag = Flag[string]{ Name: "format", ConfigName: "format", Shorthand: "f", Default: string(types.FormatTable), Values: xstrings.ToStringSlice(types.SupportedFormats), Usage: "format", TelemetrySafe: true, } ReportFormatFlag = Flag[string]{ Name: "report", ConfigName: "report", Default: "all", Values: []string{ "all", "summary", }, Usage: "specify a report format for the output", TelemetrySafe: true, } TemplateFlag = Flag[string]{ Name: "template", ConfigName: "template", Shorthand: "t", Usage: "output template (file path must have .tpl extension)", } DependencyTreeFlag = Flag[bool]{ Name: "dependency-tree", ConfigName: "dependency-tree", Usage: "[EXPERIMENTAL] show dependency origin tree of vulnerable packages", TelemetrySafe: true, } ListAllPkgsFlag = Flag[bool]{ Name: "list-all-pkgs", ConfigName: "list-all-pkgs", Default: true, Usage: "output all packages in the JSON report regardless of vulnerability", TelemetrySafe: true, } IgnoreFileFlag = Flag[string]{ Name: "ignorefile", ConfigName: "ignorefile", Default: result.DefaultIgnoreFile, Usage: "specify .trivyignore file", } IgnorePolicyFlag = Flag[string]{ Name: "ignore-policy", ConfigName: "ignore-policy", Usage: "specify the Rego file path to evaluate each vulnerability", } ExitCodeFlag = Flag[int]{ Name: "exit-code", ConfigName: "exit-code", Usage: "specify exit code when any security issues are found", TelemetrySafe: true, } ExitOnEOLFlag = Flag[int]{ Name: "exit-on-eol", ConfigName: "exit-on-eol", Usage: "exit with the specified code when the OS reaches end of service/life", TelemetrySafe: true, } OutputFlag = Flag[string]{ Name: "output", ConfigName: "output", Shorthand: "o", Usage: "output file name", } OutputPluginArgFlag = Flag[string]{ Name: "output-plugin-arg", ConfigName: "output-plugin-arg", Usage: "[EXPERIMENTAL] output plugin arguments", } SeverityFlag = Flag[[]string]{ Name: "severity", ConfigName: "severity", Shorthand: "s", Default: dbTypes.SeverityNames, Values: dbTypes.SeverityNames, Usage: "severities of security issues to be displayed", TelemetrySafe: true, } ComplianceFlag = Flag[string]{ Name: "compliance", ConfigName: "scan.compliance", Usage: "compliance report to generate", } ShowSuppressedFlag = Flag[bool]{ Name: "show-suppressed", ConfigName: "scan.show-suppressed", Usage: "[EXPERIMENTAL] show suppressed vulnerabilities", TelemetrySafe: true, } TableModeFlag = Flag[[]string]{ Name: "table-mode", ConfigName: "table-mode", Default: xstrings.ToStringSlice(types.SupportedTableModes), Values: xstrings.ToStringSlice(types.SupportedTableModes), Usage: "[EXPERIMENTAL] tables that will be displayed in 'table' format", } ) // ReportFlagGroup composes common printer flag structs // used for commands requiring reporting logic. type ReportFlagGroup struct { Format *Flag[string] ReportFormat *Flag[string] Template *Flag[string] DependencyTree *Flag[bool] ListAllPkgs *Flag[bool] IgnoreFile *Flag[string] IgnorePolicy *Flag[string] ExitCode *Flag[int] ExitOnEOL *Flag[int] Output *Flag[string] OutputPluginArg *Flag[string] Severity *Flag[[]string] Compliance *Flag[string] ShowSuppressed *Flag[bool] TableMode *Flag[[]string] } type ReportOptions struct { Format types.Format ReportFormat string Template string DependencyTree bool ListAllPkgs bool IgnoreFile string ExitCode int ExitOnEOL int IgnorePolicy string Output string OutputPluginArgs []string Severities []dbTypes.Severity Compliance spec.ComplianceSpec ShowSuppressed bool TableModes []types.TableMode } func NewReportFlagGroup() *ReportFlagGroup { return &ReportFlagGroup{ Format: FormatFlag.Clone(), ReportFormat: ReportFormatFlag.Clone(), Template: TemplateFlag.Clone(), DependencyTree: DependencyTreeFlag.Clone(), ListAllPkgs: ListAllPkgsFlag.Clone(), IgnoreFile: IgnoreFileFlag.Clone(), IgnorePolicy: IgnorePolicyFlag.Clone(), ExitCode: ExitCodeFlag.Clone(), ExitOnEOL: ExitOnEOLFlag.Clone(), Output: OutputFlag.Clone(), OutputPluginArg: OutputPluginArgFlag.Clone(), Severity: SeverityFlag.Clone(), Compliance: ComplianceFlag.Clone(), ShowSuppressed: ShowSuppressedFlag.Clone(), TableMode: TableModeFlag.Clone(), } } func (f *ReportFlagGroup) Name() string { return "Report" } func (f *ReportFlagGroup) Flags() []Flagger { return []Flagger{ f.Format, f.ReportFormat, f.Template, f.DependencyTree, f.ListAllPkgs, f.IgnoreFile, f.IgnorePolicy, f.ExitCode, f.ExitOnEOL, f.Output, f.OutputPluginArg, f.Severity, f.Compliance, f.ShowSuppressed, f.TableMode, } } func (f *ReportFlagGroup) ToOptions(opts *Options) error { format := types.Format(f.Format.Value()) template := f.Template.Value() dependencyTree := f.DependencyTree.Value() listAllPkgs := f.ListAllPkgs.Value() tableModes := f.TableMode.Value() if template != "" { switch format { case "": log.Warn("'--template' is ignored because '--format template' is not specified. Use '--template' option with '--format template' option.") case "template": // Validate template file extension if path, ok := strings.CutPrefix(template, "@"); ok { if filepath.Ext(path) != ".tpl" { return xerrors.Errorf("template file must have .tpl extension: %s", path) } } default: log.Warnf("'--template' is ignored because '--format %s' is specified. Use '--template' option with '--format template' option.", format) } } else if format == types.FormatTemplate { log.Warn("'--format template' is ignored because '--template' is not specified. Specify '--template' option when you use '--format template'.") } // "--list-all-pkgs" option is unavailable with other than "--format json". // If user explicitly specifies "--list-all-pkgs" with "--format table" or other formats, we should warn it. // We check if the flag was explicitly set by the user to avoid warning when using the default value. if f.ListAllPkgs.IsSet() && listAllPkgs && format != types.FormatJSON { log.Warn(`"--list-all-pkgs" is only valid for the JSON format, for other formats a list of packages is automatically included.`) } // "--dependency-tree" option is available only with "--format table". if dependencyTree { log.Info(`"--dependency-tree" only shows the dependents of vulnerable packages. ` + `Note that it is the reverse of the usual dependency tree, which shows the packages that depend on the vulnerable package. ` + `It supports limited package managers. Please see the document for the detail.`) if format != types.FormatTable { log.Warn(`"--dependency-tree" can be used only with "--format table".`) } } // "--table-mode" option is available only with "--format table". if viper.IsSet(TableModeFlag.ConfigName) && format != types.FormatTable { return xerrors.New(`"--table-mode" can be used only with "--format table".`) } cs, err := loadComplianceTypes(f.Compliance.Value()) if err != nil { return xerrors.Errorf("unable to load compliance spec: %w", err) } var outputPluginArgs []string if arg := f.OutputPluginArg.Value(); arg != "" { outputPluginArgs, err = shellwords.Parse(arg) if err != nil { return xerrors.Errorf("unable to parse output plugin argument: %w", err) } } if viper.IsSet(f.IgnoreFile.ConfigName) && !fsutils.FileExists(f.IgnoreFile.Value()) { return xerrors.Errorf("ignore file not found: %s", f.IgnoreFile.Value()) } opts.ReportOptions = ReportOptions{ Format: format, ReportFormat: f.ReportFormat.Value(), Template: template, DependencyTree: dependencyTree, ListAllPkgs: listAllPkgs, IgnoreFile: f.IgnoreFile.Value(), ExitCode: f.ExitCode.Value(), ExitOnEOL: f.ExitOnEOL.Value(), IgnorePolicy: f.IgnorePolicy.Value(), Output: f.Output.Value(), OutputPluginArgs: outputPluginArgs, Severities: toSeverity(f.Severity.Value()), Compliance: cs, ShowSuppressed: f.ShowSuppressed.Value(), TableModes: xstrings.ToTSlice[types.TableMode](tableModes), } return nil } func loadComplianceTypes(compliance string) (spec.ComplianceSpec, error) { if compliance != "" && !slices.Contains(types.SupportedCompliances, compliance) && !strings.HasPrefix(compliance, "@") { return spec.ComplianceSpec{}, xerrors.Errorf("unknown compliance : %v", compliance) } cs, err := spec.GetComplianceSpec(compliance, cache.DefaultDir()) if err != nil { return spec.ComplianceSpec{}, xerrors.Errorf("spec loading from file system error: %w", err) } return cs, nil } func toSeverity(severity []string) []dbTypes.Severity { if len(severity) == 0 { return nil } severities := xslices.Map(severity, func(s string) dbTypes.Severity { // Note that there is no need to check the error here // since the severity value is already validated in the flag parser. sev, _ := dbTypes.NewSeverity(s) return sev }) log.Debug("Parsed severities", log.Any("severities", severities)) return severities }