github.com/wolfi-dev/wolfictl@v0.16.11/pkg/cli/advisory_export.go (about) 1 package cli 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "strings" 8 9 "github.com/google/osv-scanner/pkg/models" 10 "github.com/spf13/cobra" 11 12 "github.com/chainguard-dev/clog" 13 14 "github.com/wolfi-dev/wolfictl/pkg/advisory" 15 "github.com/wolfi-dev/wolfictl/pkg/configs" 16 v2 "github.com/wolfi-dev/wolfictl/pkg/configs/advisory/v2" 17 rwos "github.com/wolfi-dev/wolfictl/pkg/configs/rwfs/os" 18 "github.com/wolfi-dev/wolfictl/pkg/distro" 19 ) 20 21 func cmdAdvisoryExport() *cobra.Command { 22 p := &exportParams{} 23 cmd := &cobra.Command{ 24 Use: "export", 25 Short: "Export advisory data (experimental)", 26 SilenceErrors: true, 27 Args: cobra.NoArgs, 28 Hidden: true, 29 30 PreRunE: func(cmd *cobra.Command, _ []string) error { 31 ctx := cmd.Context() 32 33 if p.format == OutputOSV { 34 if _, err := os.Stat(p.outputLocation); os.IsNotExist(err) { 35 clog.FromContext(ctx).Errorf("directory %s does not exist, please create that first", p.outputLocation) 36 return err 37 } 38 } 39 40 return nil 41 }, 42 43 RunE: func(cmd *cobra.Command, _ []string) error { 44 if len(p.advisoriesRepoDirs) == 0 { 45 if p.doNotDetectDistro { 46 return fmt.Errorf("no advisories repo dir specified") 47 } 48 49 d, err := distro.Detect() 50 if err != nil { 51 return fmt.Errorf("no advisories repo dir specified, and distro auto-detection failed: %w", err) 52 } 53 54 p.advisoriesRepoDirs = append(p.advisoriesRepoDirs, d.Local.AdvisoriesRepo.Dir) 55 _, _ = fmt.Fprint(os.Stderr, renderDetectedDistro(d)) 56 } 57 58 indices := make([]*configs.Index[v2.Document], 0, len(p.advisoriesRepoDirs)) 59 for _, dir := range p.advisoriesRepoDirs { 60 advisoryFsys := rwos.DirFS(dir) 61 index, err := v2.NewIndex(cmd.Context(), advisoryFsys) 62 if err != nil { 63 return fmt.Errorf("unable to index advisory configs for directory %q: %w", dir, err) 64 } 65 66 indices = append(indices, index) 67 } 68 69 opts := advisory.ExportOptions{ 70 AdvisoryDocIndices: indices, 71 Ecosystem: models.Ecosystem(p.ecosystem), 72 } 73 74 var export io.Reader 75 var err error 76 switch p.format { 77 case OutputYAML: 78 export, err = advisory.ExportYAML(opts) 79 case OutputCSV: 80 export, err = advisory.ExportCSV(opts) 81 case OutputOSV: 82 err = advisory.ExportOSV(opts, p.outputLocation) 83 default: 84 return fmt.Errorf("unrecognized format: %q. Valid formats are: [%s]", p.format, strings.Join([]string{OutputYAML, OutputCSV, OutputOSV}, ", ")) 85 } 86 if err != nil { 87 return fmt.Errorf("unable to export advisory data: %w", err) 88 } 89 90 if p.format != OutputOSV { 91 var outputFile *os.File 92 if p.outputLocation == "" { 93 outputFile = os.Stdout 94 } else { 95 outputFile, err = os.Create(p.outputLocation) 96 if err != nil { 97 return fmt.Errorf("unable to create output file: %w", err) 98 } 99 defer outputFile.Close() 100 } 101 102 _, err = io.Copy(outputFile, export) 103 if err != nil { 104 return fmt.Errorf("unable to export data to specified location: %w", err) 105 } 106 } 107 108 return nil 109 }, 110 } 111 112 p.addFlagsTo(cmd) 113 return cmd 114 } 115 116 type exportParams struct { 117 doNotDetectDistro bool 118 advisoriesRepoDirs []string 119 outputLocation string 120 // format controls how commands will produce their output. 121 format string 122 ecosystem string 123 } 124 125 const ( 126 // OutputYAML YAML output. 127 OutputYAML = "yaml" 128 // OutputCSV CSV output. 129 OutputCSV = "csv" 130 // OutputOSV OSV output. 131 OutputOSV = "osv" 132 ) 133 134 func (p *exportParams) addFlagsTo(cmd *cobra.Command) { 135 addNoDistroDetectionFlag(&p.doNotDetectDistro, cmd) 136 137 cmd.Flags().StringSliceVarP(&p.advisoriesRepoDirs, "advisories-repo-dir", "a", nil, "directory containing an advisories repository") 138 cmd.Flags().StringVarP(&p.outputLocation, "output", "o", "", "output location (default: stdout). In case using OSV format this will be the output directory.") 139 cmd.Flags().StringVarP(&p.format, "format", "f", OutputCSV, fmt.Sprintf("Output format. One of: [%s]", strings.Join([]string{OutputYAML, OutputCSV, OutputOSV}, ", "))) 140 cmd.Flags().StringVarP(&p.ecosystem, "ecosystem", "e", "Wolfi", fmt.Sprintf("Ecosystem format. One of: [%s]", strings.Join([]string{"Wolfi", "Chainguard"}, ", "))) 141 }