From 88720dfdc93a5857ab153ef599ae87823d6a721d Mon Sep 17 00:00:00 2001 From: Dave Walker Date: Sun, 19 Apr 2020 10:11:15 +0100 Subject: [PATCH] Support outputting all file formats concurrently (#218) * Support outputting all file formats concurrently Previously ffuf would allow outputting to a single file-format, ie Markdown (md), json or HTML. It was not possible to output in multiple formats in the same execution. This change allows specifying an output (-of) of "all", which means that the output filename (-o) is used, but the appropriate suffix is added. As an example, ... -of all -o output/report Will output: - output/report.json - output/report.html - output/report.csv - ... etc Fixes ffuf/ffuf#215 Signed-off-by: Dave Walker (Daviey) * Updated Changelog and added myself to CONTRIBUTORS Signed-off-by: Dave Walker (Daviey) * Fix file extension for 'ecsv' when 'all' is used .. And fix CONTRIBUTORS.md to be alphabetical Signed-off-by: Dave Walker (Daviey) --- CHANGELOG.md | 1 + main.go | 4 +-- pkg/output/stdout.go | 62 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99b39bc..035ae36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Changed behaviour of `-maxtime`, can now be used for entire process. - A new flag `-ignore-body` so ffuf does not fetch the response content. Default value=false. - Added the wordlists to the header information. + - Added support to output "all" formats (specify the path/filename sans file extension and ffuf will add the appropriate suffix for the filetype) - Changed - Added tls renegotiation flag to fix #193 in http.Client diff --git a/main.go b/main.go index a894990..ab01a92 100644 --- a/main.go +++ b/main.go @@ -101,7 +101,7 @@ func main() { flag.StringVar(&opts.requestProto, "request-proto", "https", "Protocol to use along with raw request") flag.StringVar(&conf.Method, "X", "GET", "HTTP method to use") flag.StringVar(&conf.OutputFile, "o", "", "Write output to file") - flag.StringVar(&opts.outputFormat, "of", "json", "Output file format. Available formats: json, ejson, html, md, csv, ecsv") + flag.StringVar(&opts.outputFormat, "of", "json", "Output file format. Available formats: json, ejson, html, md, csv, ecsv (or, 'all' for all formats)") flag.StringVar(&conf.OutputDirectory, "od", "", "Directory path to store matched results to.") flag.BoolVar(&conf.IgnoreBody, "ignore-body", false, "Do not fetch the response content.") flag.BoolVar(&conf.Quiet, "s", false, "Do not print additional information (silent mode)") @@ -430,7 +430,7 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error { //Check the output file format option if conf.OutputFile != "" { //No need to check / error out if output file isn't defined - outputFormats := []string{"json", "ejson", "html", "md", "csv", "ecsv"} + outputFormats := []string{"all", "json", "ejson", "html", "md", "csv", "ecsv"} found := false for _, f := range outputFormats { if f == parseOpts.outputFormat { diff --git a/pkg/output/stdout.go b/pkg/output/stdout.go index b7730e1..87c3af6 100644 --- a/pkg/output/stdout.go +++ b/pkg/output/stdout.go @@ -83,7 +83,16 @@ func (s *Stdoutput) Banner() error { // Output file info if len(s.config.OutputFile) > 0 { - printOption([]byte("Output file"), []byte(s.config.OutputFile)) + + // Use filename as specified by user + OutputFile := s.config.OutputFile + + if s.config.OutputFormat == "all" { + // Actually... append all extensions + OutputFile += ".{json,ejson,html,md,csv,ecsv}" + } + + printOption([]byte("Output file"), []byte(OutputFile)) printOption([]byte("File format"), []byte(s.config.OutputFormat)) } @@ -196,10 +205,59 @@ func (s *Stdoutput) Warning(warnstring string) { } } +func (s *Stdoutput) writeToAll(config *ffuf.Config, res []Result) error { + var err error + var BaseFilename string = s.config.OutputFile + + // Go through each type of write, adding + // the suffix to each output file. + + s.config.OutputFile = BaseFilename + ".json" + err = writeJSON(s.config, s.Results) + if err != nil { + s.Error(fmt.Sprintf("%s", err)) + } + + s.config.OutputFile = BaseFilename + ".ejson" + err = writeEJSON(s.config, s.Results) + if err != nil { + s.Error(fmt.Sprintf("%s", err)) + } + + s.config.OutputFile = BaseFilename + ".html" + err = writeHTML(s.config, s.Results) + if err != nil { + s.Error(fmt.Sprintf("%s", err)) + } + + s.config.OutputFile = BaseFilename + ".md" + err = writeMarkdown(s.config, s.Results) + if err != nil { + s.Error(fmt.Sprintf("%s", err)) + } + + s.config.OutputFile = BaseFilename + ".csv" + err = writeCSV(s.config, s.Results, false) + if err != nil { + s.Error(fmt.Sprintf("%s", err)) + } + + s.config.OutputFile = BaseFilename + ".ecsv" + err = writeCSV(s.config, s.Results, true) + if err != nil { + s.Error(fmt.Sprintf("%s", err)) + } + + return nil + +} + func (s *Stdoutput) Finalize() error { var err error if s.config.OutputFile != "" { - if s.config.OutputFormat == "json" { + if s.config.OutputFormat == "all" { + err = s.writeToAll(s.config, s.Results) + } else if s.config.OutputFormat == "json" { err = writeJSON(s.config, s.Results) } else if s.config.OutputFormat == "ejson" { err = writeEJSON(s.config, s.Results)