diff --git a/cmd/event.go b/cmd/event.go index 72da219..23a5653 100644 --- a/cmd/event.go +++ b/cmd/event.go @@ -121,7 +121,7 @@ func listEvents(cmd *cobra.Command, args []string) { } //display event definitions //TODO: add support for different outputs and formats - format, err := formatter.New(eventFormatFlag, outputFlag, cmd) + format, err := formatter.New(eventFormatFlag, eventOutputFlag, cmd) if err != nil { cmd.PrintErrln("Error creating formatter: ", err) return @@ -145,7 +145,7 @@ func getEventDescriptions(cmd *cobra.Command, args []string) { } //display event definitions //TODO: add support for different outputs and formats - format, err := formatter.New(eventFormatFlag, outputFlag, cmd) + format, err := formatter.New(eventFormatFlag, eventOutputFlag, cmd) if err != nil { cmd.PrintErrln("Error creating formatter: ", err) return diff --git a/cmd/root.go b/cmd/root.go index 8ce70dc..c41f86e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -10,8 +10,7 @@ import ( "github.com/spf13/cobra" ) -var formatFlag string -var outputFlag string +// var outputFlag string var ( serverInfo client.ServerInfo = client.ServerInfo{ ConnectionType: client.PROTOCOL_UNIX, @@ -56,6 +55,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&serverInfo.UnixSocketPath, "socketPath", client.SOCKET, "Path of the unix socket") //tcp connection type flag rootCmd.PersistentFlags().StringVarP(&serverInfo.ADDR, "server", "s", client.DefaultIP+":"+client.DefaultPort, "The address and port of the Kubernetes API server") + //rootCmd.PersistentFlags().StringVarP(&outputFlag, "output", "o", "", "Specify the output file path (default is stdout)") //if empty stdout } diff --git a/cmd/stream.go b/cmd/stream.go index facb984..1aea77b 100644 --- a/cmd/stream.go +++ b/cmd/stream.go @@ -141,7 +141,7 @@ func stream(cmd *cobra.Command, args []string) { } //create formatter for output - format, err := formatter.New(streamFormatFlag, outputFlag, cmd) + format, err := formatter.New(streamFormatFlag, streamOutputFlag, cmd) if err != nil { cmd.PrintErrln("Error creating formatter: ", err) return diff --git a/pkg/cmd/formatter/TableFormatter.go b/pkg/cmd/formatter/TableFormatter.go index 4495bae..c2f176d 100644 --- a/pkg/cmd/formatter/TableFormatter.go +++ b/pkg/cmd/formatter/TableFormatter.go @@ -3,10 +3,12 @@ package formatter import ( "fmt" "os" + "path/filepath" "strings" "github.com/aquasecurity/table" pb "github.com/aquasecurity/tracee/api/v1beta1" + "github.com/spf13/cobra" ) func (f *Formatter) PrintSteamTableHeaders() { @@ -74,7 +76,7 @@ func getEventValue(ev *pb.EventValue) string { } func (f *Formatter) PrintEventListTable(response *pb.GetEventDefinitionsResponse) *table.Table { - tbl := table.New(os.Stdout) + tbl := createTable(f) tbl.SetHeaders("ID", "Name", "Version", "Tags") for _, event := range response.Definitions { // Check if the optional field Threat is set (non-nil) @@ -91,7 +93,7 @@ func (f *Formatter) PrintEventListTable(response *pb.GetEventDefinitionsResponse } func (f *Formatter) PrintEventDescriptionTable(response *pb.GetEventDefinitionsResponse) *table.Table { - tbl := table.New(os.Stdout) + tbl := createTable(f) tbl.SetHeaders("ID", "Name", "Version", "Tags", "Description") for _, event := range response.Definitions { // Check if the optional field Threat is set (non-nil) @@ -107,3 +109,36 @@ func (f *Formatter) PrintEventDescriptionTable(response *pb.GetEventDefinitionsR } return tbl } + +func createTable(f *Formatter) *table.Table { + if (f.Output != "") && (f.Output != "stdout") { + /// Validate the file path + if f.Output == "" || strings.TrimSpace(f.Output) == "" { + fmt.Errorf("Output file path is empty or invalid") + return nil + } + + // Ensure parent directories exist + dir := filepath.Dir(f.Output) + if err := os.MkdirAll(dir, 0755); err != nil { + fmt.Errorf("failed to create directories for output file: %v", err) + return nil + } + + // Create or open the file + file, err := os.Create(f.Output) + if err != nil { + fmt.Errorf("failed to open output file: %v", err) + return nil + } + tbl := table.New(file) + // Make sure to close the file after execution + f.CMD.PersistentPostRun = func(cmd *cobra.Command, args []string) { + file.Close() + } + return tbl + } else { + return table.New(os.Stdout) + } + +} diff --git a/pkg/cmd/formatter/formatter.go b/pkg/cmd/formatter/formatter.go index 8ece362..0bd0094 100644 --- a/pkg/cmd/formatter/formatter.go +++ b/pkg/cmd/formatter/formatter.go @@ -2,6 +2,9 @@ package formatter import ( "fmt" + "os" + "path/filepath" + "strings" "github.com/spf13/cobra" ) @@ -10,6 +13,8 @@ const ( FormatJSON = "json" FormatTable = "table" FormatGoTpl = "gotemplate" + + // DefaultOutput is the default output format ) // SupportedFormats is a slice of all supported format types @@ -24,7 +29,9 @@ type Formatter struct { func New(format string, output string, cmd *cobra.Command) (*Formatter, error) { if !containsFormat(format) { return nil, fmt.Errorf("format %s is not supported", format) - + } + if err := initOutput(cmd, output); err != nil { + return nil, err } return &Formatter{ Format: format, @@ -42,3 +49,35 @@ func containsFormat(format string) bool { } return false } +func initOutput(cmd *cobra.Command, output string) error { + if (output != "") && (output != "stdout") { + /// Validate the file path + if output == "" || strings.TrimSpace(output) == "" { + return fmt.Errorf("output file path is empty or invalid") + } + + // Ensure parent directories exist + dir := filepath.Dir(output) + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create directories for output file: %v", err) + } + + // Create or open the file + file, err := os.Create(output) + if err != nil { + return fmt.Errorf("failed to open output file: %v", err) + } + + cmd.SetOut(file) + cmd.SetErr(file) + // Make sure to close the file after execution + cmd.PersistentPostRun = func(cmd *cobra.Command, args []string) { + file.Close() + } + } else { + // If no file is specified, use stdout + cmd.SetOut(os.Stdout) + cmd.SetErr(os.Stderr) + } + return nil +}