diff --git a/cmd/cariddi/main.go b/cmd/cariddi/main.go index fc4d9bc..5481860 100644 --- a/cmd/cariddi/main.go +++ b/cmd/cariddi/main.go @@ -90,6 +90,12 @@ func main() { StoreResp: flags.StoreResp, } + config.OutputDir = output.CariddiOutputFolder + if flags.StoredRespDir != "" { + config.OutputDir = flags.StoredRespDir + config.StoreResp = true + } + // Read the targets from standard input. targets := input.ScanTargets() @@ -118,18 +124,18 @@ func main() { // Create output files if needed (txt / html). config.Txt = "" if flags.TXTout != "" { - config.Txt = fileUtils.CreateOutputFile(flags.TXTout, "results", "txt") + config.Txt = fileUtils.CreateOutputFile(flags.TXTout, "results", "txt", config.OutputDir) } var ResultHTML = "" if flags.HTMLout != "" { - ResultHTML = fileUtils.CreateOutputFile(flags.HTMLout, "", "html") + ResultHTML = fileUtils.CreateOutputFile(flags.HTMLout, "", "html", config.OutputDir) output.BannerHTML(ResultHTML) output.HeaderHTML("Results", ResultHTML) } if config.StoreResp { - fileUtils.CreateIndexOutputFile("index.responses.txt") + fileUtils.CreateIndexOutputFile("index.responses.txt", config.OutputDir) } // Read headers if needed @@ -167,13 +173,13 @@ func main() { // IF TXT OUTPUT > if flags.TXTout != "" { output.TxtOutput(flags, finalResults, finalSecret, finalEndpoints, - finalExtensions, finalErrors, finalInfos) + finalExtensions, finalErrors, finalInfos, config.OutputDir) } // IF HTML OUTPUT > if flags.HTMLout != "" { output.HTMLOutput(flags, ResultHTML, finalResults, finalSecret, - finalEndpoints, finalExtensions, finalErrors, finalInfos) + finalEndpoints, finalExtensions, finalErrors, finalInfos, config.OutputDir) } // If needed print secrets. diff --git a/internal/file/file.go b/internal/file/file.go index a0ed364..498b131 100644 --- a/internal/file/file.go +++ b/internal/file/file.go @@ -44,9 +44,9 @@ const ( // CreateOutputFolder creates the output folder // If it fails exits with an error message. -func CreateOutputFolder() { +func CreateOutputFolder(outputDir string) { // Create a folder/directory at a full qualified path - err := os.Mkdir("output-cariddi", Permission0755) + err := os.MkdirAll(outputDir, Permission0755) if err != nil { fmt.Println("Can't create output folder.") os.Exit(1) @@ -56,9 +56,9 @@ func CreateOutputFolder() { // CreateHostOutputFolder creates the host output folder // for the HTTP responses. // If it fails exits with an error message. -func CreateHostOutputFolder(host string) { +func CreateHostOutputFolder(host string, outputDir string) { // Create a folder/directory at a full qualified path - err := os.MkdirAll(filepath.Join("output-cariddi", host), Permission0755) + err := os.MkdirAll(filepath.Join(outputDir, host), Permission0755) if err != nil { fmt.Println("Can't create host output folder.") os.Exit(1) @@ -71,21 +71,21 @@ func CreateHostOutputFolder(host string) { // already exists, if yes asks the user if cariddi has to overwrite it; // if no cariddi creates it. // Whenever an instruction fails, it exits with an error message. -func CreateOutputFile(target string, subcommand string, format string) string { +func CreateOutputFile(target string, subcommand string, format string, outputDir string) string { target = ReplaceBadCharacterOutput(target) var filename string if subcommand != "" { - filename = filepath.Join("output-cariddi", target+"."+subcommand+"."+format) + filename = filepath.Join(outputDir, target+"."+subcommand+"."+format) } else { - filename = filepath.Join("output-cariddi", target+"."+format) + filename = filepath.Join(outputDir, target+"."+format) } _, err := os.Stat(filename) if os.IsNotExist(err) { - if _, err := os.Stat("output-cariddi/"); os.IsNotExist(err) { - CreateOutputFolder() + if _, err := os.Stat(fmt.Sprintf("%s/", outputDir)); os.IsNotExist(err) { + CreateOutputFolder(outputDir) } // If the file doesn't exist, create it. f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, Permission0644) @@ -119,15 +119,15 @@ func CreateOutputFile(target string, subcommand string, format string) string { // It creates the output folder if needed, then checks if the index output file // already exists, if no cariddi creates it. // Whenever an instruction fails, it exits with an error message. -func CreateIndexOutputFile(filename string) { +func CreateIndexOutputFile(filename string, outputDir string) { _, err := os.Stat(filename) if os.IsNotExist(err) { - if _, err := os.Stat("output-cariddi/"); os.IsNotExist(err) { - CreateOutputFolder() + if _, err := os.Stat(fmt.Sprintf("%s/", outputDir)); os.IsNotExist(err) { + CreateOutputFolder(outputDir) } // If the file doesn't exist, create it. - filename = filepath.Join("output-cariddi", filename) + filename = filepath.Join(outputDir, filename) f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, Permission0644) if err != nil { @@ -161,6 +161,7 @@ func ReadFile(inputFile string) []string { for scanner.Scan() { text = append(text, scanner.Text()) } + file.Close() return text diff --git a/pkg/crawler/colly.go b/pkg/crawler/colly.go index 4585f36..5319fb3 100644 --- a/pkg/crawler/colly.go +++ b/pkg/crawler/colly.go @@ -134,8 +134,12 @@ func New(scan *Scan) *Results { fmt.Println(r.Request.URL) } + var outputPath string + if scan.StoreResp { - err := output.StoreHTTPResponse(r) + var err error + outputPath, err = output.StoreHTTPResponse(r, scan.OutputDir) + if err != nil { log.Println(err) } @@ -193,7 +197,7 @@ func New(scan *Scan) *Results { if scan.JSON { jsonOutput, err := output.GetJSONString( - r, secrets, parameters, filetype, errors, infos, + r, secrets, parameters, filetype, errors, infos, outputPath, ) if err == nil { diff --git a/pkg/crawler/options.go b/pkg/crawler/options.go index 73ab694..af636c9 100644 --- a/pkg/crawler/options.go +++ b/pkg/crawler/options.go @@ -59,6 +59,7 @@ type Scan struct { FileType int Headers map[string]string StoreResp bool + OutputDir string // Settings Concurrency int diff --git a/pkg/crawler/useragents.go b/pkg/crawler/useragents.go index ada2079..9d02ab6 100644 --- a/pkg/crawler/useragents.go +++ b/pkg/crawler/useragents.go @@ -35,6 +35,10 @@ import ( "time" ) +const ( + maxRandomValue = 100 +) + // genOsString generates a random OS string for a User Agent. func genOsString() string { source := rand.NewSource(time.Now().UnixNano()) @@ -151,7 +155,7 @@ func GenerateRandomUserAgent() string { source := rand.NewSource(time.Now().UnixNano()) rng := rand.New(source) - decision := rng.Intn(100) + decision := rng.Intn(maxRandomValue) var ua string if decision%2 == 0 { diff --git a/pkg/input/check.go b/pkg/input/check.go index e5f6f08..38be87b 100644 --- a/pkg/input/check.go +++ b/pkg/input/check.go @@ -29,6 +29,7 @@ package input import ( "fmt" "os" + "path/filepath" "strings" fileUtils "github.com/edoardottt/cariddi/internal/file" @@ -47,6 +48,33 @@ func CheckOutputFile(input string) bool { return true } +// IsValidOutputPath checks if the directory is valid and, if created, cleans it up. +func CheckOutputPath(outputPath string) bool { + // Convert to absolute path if necessary + absPath, err := filepath.Abs(outputPath) + if err != nil { + return false + } + + // Check if the directory already exists + _, err = os.Stat(absPath) + if err == nil { + // Directory exists, so it's valid, no need to delete + return true + } + + // If the directory does not exist, try to create it + err = os.MkdirAll(absPath, os.ModePerm) + if err != nil { + return false + } + + // Since we created the directory, clean it up + defer os.RemoveAll(absPath) + + return true +} + // CheckFlags checks the flags taken as input. func CheckFlags(flags Input) { if flags.TXTout != "" { @@ -127,4 +155,11 @@ func CheckFlags(flags Input) { fmt.Println(" - cat urls | cariddi -headersfile headers.txt") os.Exit(1) } + + if flags.StoredRespDir != "" { + if !CheckOutputPath(flags.StoredRespDir) { + fmt.Println("Validation failed for srd flag; there may be errors creating the output directory.") + os.Exit(1) + } + } } diff --git a/pkg/input/check_test.go b/pkg/input/check_test.go new file mode 100644 index 0000000..a497b01 --- /dev/null +++ b/pkg/input/check_test.go @@ -0,0 +1,55 @@ +package input_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/edoardottt/cariddi/pkg/input" +) + +// TestCheckOutputPath tests the CheckOutputPath function for valid and invalid cases. +func TestCheckOutputPath(t *testing.T) { + // Create a temporary directory for testing + tmpDir, err := os.MkdirTemp("", "testdir") + defer os.RemoveAll(tmpDir) // Cleanup after test + + if err != nil { + t.Fatalf("Failed to create temporary directory: %v", err) + } + + // Valid case: Existing directory + if !input.CheckOutputPath(tmpDir) { + t.Errorf("CheckOutputPath(%s) = false; want true", tmpDir) + } + + // Verify that the existing directory is still present after check + _, err = os.Stat(tmpDir) + if err != nil { + t.Errorf("Existing directory %s should still be present after CheckOutputPath, but got error: %v", tmpDir, err) + } + + // Cross-platform invalid cases + invalidPaths := []string{ + // Null character is invalid on all platforms + filepath.Join(tmpDir, "invalid\000path"), + } + + for _, invalidPath := range invalidPaths { + if input.CheckOutputPath(invalidPath) { + t.Errorf("CheckOutputPath(%s) = true; want false", invalidPath) + } + } + + // Valid case: New directory creation and cleanup + newDir := filepath.Join(tmpDir, "newdir") + if !input.CheckOutputPath(newDir) { + t.Errorf("CheckOutputPath(%s) = false; want true", newDir) + } + + // After check, the directory should be removed + _, err = os.Stat(newDir) + if err == nil || !os.IsNotExist(err) { + t.Errorf("CheckOutputPath should remove the directory, but it still exists: %s", newDir) + } +} diff --git a/pkg/input/flags.go b/pkg/input/flags.go index 3848d7f..e9edb19 100644 --- a/pkg/input/flags.go +++ b/pkg/input/flags.go @@ -94,6 +94,8 @@ type Input struct { UserAgent string // StoreResp stores HTTP responses. StoreResp bool + // StoredRespDir stores HTTP responses to the directory provided. + StoredRespDir string } // ScanFlag defines all the options taken @@ -142,6 +144,8 @@ func ScanFlag() Input { storeRespPtr := flag.Bool("sr", false, "Store HTTP responses.") + storedRespDirPtr := flag.String("srd", "", "Stores HTTP responses to the directory provided.") + flag.Parse() result := Input{ @@ -173,6 +177,7 @@ func ScanFlag() Input { *debugPtr, *userAgentPtr, *storeRespPtr, + *storedRespDirPtr, } return result diff --git a/pkg/output/jsonl.go b/pkg/output/jsonl.go index e54d7d4..79b6ecf 100644 --- a/pkg/output/jsonl.go +++ b/pkg/output/jsonl.go @@ -44,6 +44,7 @@ type JSONData struct { ContentType string `json:"content_type,omitempty"` ContentLength int `json:"content_length,omitempty"` Matches *MatcherResults `json:"matches,omitempty"` + OutputPath string `json:"output_path,omitempty"` // Host string `json:"host"` # TODO: Available in Colly 2.x } @@ -67,6 +68,7 @@ func GetJSONString( filetype *scanner.FileType, errors []scanner.ErrorMatched, infos []scanner.InfoMatched, + outputPath string, ) ([]byte, error) { // Parse response headers headers := r.Headers @@ -136,6 +138,7 @@ func GetJSONString( ContentType: contentType, ContentLength: contentLength, Matches: matcherResults, + OutputPath: outputPath, // Host: "", // TODO } diff --git a/pkg/output/jsonl_test.go b/pkg/output/jsonl_test.go index 83a5683..fd8c387 100644 --- a/pkg/output/jsonl_test.go +++ b/pkg/output/jsonl_test.go @@ -113,6 +113,7 @@ func TestJSONOutput(t *testing.T) { errors []scanner.ErrorMatched infos []scanner.InfoMatched want string + outputPath string }{ { name: "test_all_findings", @@ -122,7 +123,8 @@ func TestJSONOutput(t *testing.T) { filetype: filetype, errors: errors, infos: infos, - want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"filetype":{"extension":"pdf","severity":7},"parameters":[{"name":"id","attacks":[]}],"errors":[{"name":"MySQL error","match":"it is a MySQL error happening"}],"infos":[{"name":"info1","match":"its my pleasure to inform you on this great day"}],"secrets":[{"name":"mysecret","match":"it's a random day for my secret regex to be found"}]}}`, //nolint:lll + want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"filetype":{"extension":"pdf","severity":7},"parameters":[{"name":"id","attacks":[]}],"errors":[{"name":"MySQL error","match":"it is a MySQL error happening"}],"infos":[{"name":"info1","match":"its my pleasure to inform you on this great day"}],"secrets":[{"name":"mysecret","match":"it's a random day for my secret regex to be found"}]},"output_path":"C:\\testDir1\\testDir2"}`, //nolint:lll + outputPath: "C:\\testDir1\\testDir2", }, { name: "test_all_findings_nocontent", @@ -132,7 +134,8 @@ func TestJSONOutput(t *testing.T) { filetype: filetype, errors: errors, infos: infos, - want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"matches":{"filetype":{"extension":"pdf","severity":7},"parameters":[{"name":"id","attacks":[]}],"errors":[{"name":"MySQL error","match":"it is a MySQL error happening"}],"infos":[{"name":"info1","match":"its my pleasure to inform you on this great day"}],"secrets":[{"name":"mysecret","match":"it's a random day for my secret regex to be found"}]}}`, //nolint:lll + want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"matches":{"filetype":{"extension":"pdf","severity":7},"parameters":[{"name":"id","attacks":[]}],"errors":[{"name":"MySQL error","match":"it is a MySQL error happening"}],"infos":[{"name":"info1","match":"its my pleasure to inform you on this great day"}],"secrets":[{"name":"mysecret","match":"it's a random day for my secret regex to be found"}]},"output_path":"C:\\testDir1\\testDir2"}`, //nolint:lll + outputPath: "C:\\testDir1\\testDir2", }, { name: "test_no_findings", @@ -142,7 +145,8 @@ func TestJSONOutput(t *testing.T) { filetype: &scanner.FileType{}, errors: []scanner.ErrorMatched{}, infos: []scanner.InfoMatched{}, - want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128}`, //nolint: all + want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"output_path":"C:\\testDir1\\testDir2"}`, //nolint: all + outputPath: "C:\\testDir1\\testDir2", }, { name: "test_only_secrets", @@ -152,7 +156,8 @@ func TestJSONOutput(t *testing.T) { filetype: &scanner.FileType{}, errors: []scanner.ErrorMatched{}, infos: []scanner.InfoMatched{}, - want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"secrets":[{"name":"mysecret","match":"it's a random day for my secret regex to be found"}]}}`, //nolint:lll + want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"secrets":[{"name":"mysecret","match":"it's a random day for my secret regex to be found"}]},"output_path":"C:\\testDir1\\testDir2"}`, //nolint:lll + outputPath: "C:\\testDir1\\testDir2", }, { name: "test_only_params", @@ -162,7 +167,8 @@ func TestJSONOutput(t *testing.T) { filetype: &scanner.FileType{}, errors: []scanner.ErrorMatched{}, infos: []scanner.InfoMatched{}, - want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"parameters":[{"name":"id","attacks":[]}]}}`, //nolint:lll + want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"parameters":[{"name":"id","attacks":[]}]},"output_path":"C:\\testDir1\\testDir2"}`, //nolint:lll + outputPath: "C:\\testDir1\\testDir2", }, { name: "test_only_errors", @@ -172,7 +178,8 @@ func TestJSONOutput(t *testing.T) { filetype: &scanner.FileType{}, errors: errors, infos: []scanner.InfoMatched{}, - want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"errors":[{"name":"MySQL error","match":"it is a MySQL error happening"}]}}`, //nolint:lll + want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"errors":[{"name":"MySQL error","match":"it is a MySQL error happening"}]},"output_path":"C:\\testDir1\\testDir2"}`, //nolint:lll + outputPath: "C:\\testDir1\\testDir2", }, { name: "test_only_infos", @@ -182,13 +189,25 @@ func TestJSONOutput(t *testing.T) { filetype: &scanner.FileType{}, errors: []scanner.ErrorMatched{}, infos: infos, + want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"infos":[{"name":"info1","match":"its my pleasure to inform you on this great day"}]},"output_path":"C:\\testDir1\\testDir2"}`, //nolint:lll + outputPath: "C:\\testDir1\\testDir2", + }, + { + name: "test_no_outputPath", + r: resp, + secrets: []scanner.SecretMatched{}, + parameters: []scanner.Parameter{}, + filetype: &scanner.FileType{}, + errors: []scanner.ErrorMatched{}, + infos: infos, want: `{"url":"http://test.com.pdf?id=5","method":"GET","status_code":200,"words":1,"lines":1,"content_type":"application/pdf","content_length":128,"matches":{"infos":[{"name":"info1","match":"its my pleasure to inform you on this great day"}]}}`, //nolint:lll + outputPath: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got, _ := output.GetJSONString(tt.r, tt.secrets, tt.parameters, tt.filetype, tt.errors, tt.infos); !reflect.DeepEqual(string(got), tt.want) { //nolint:lll + if got, _ := output.GetJSONString(tt.r, tt.secrets, tt.parameters, tt.filetype, tt.errors, tt.infos, tt.outputPath); !reflect.DeepEqual(string(got), tt.want) { //nolint:lll t.Errorf("GetJSONString\n%v", string(got)) t.Errorf("want\n%v", tt.want) } diff --git a/pkg/output/output.go b/pkg/output/output.go index ba1b12f..619703b 100644 --- a/pkg/output/output.go +++ b/pkg/output/output.go @@ -51,25 +51,25 @@ func PrintSimpleOutput(out []string) { // Actually it manages everything related to TXT output. func TxtOutput(flags input.Input, finalResults []string, finalSecret []scanner.SecretMatched, finalEndpoints []scanner.EndpointMatched, finalExtensions []scanner.FileTypeMatched, - finalErrors []scanner.ErrorMatched, finalInfos []scanner.InfoMatched) { - exists, err := fileUtils.ElementExists(CariddiOutputFolder) + finalErrors []scanner.ErrorMatched, finalInfos []scanner.InfoMatched, outputDir string) { + exists, err := fileUtils.ElementExists(outputDir) if err != nil { fmt.Println("Error while creating the output directory.") os.Exit(1) } if !exists { - fileUtils.CreateOutputFolder() + fileUtils.CreateOutputFolder(outputDir) } - ResultFilename := fileUtils.CreateOutputFile(flags.TXTout, "results", "txt") + ResultFilename := fileUtils.CreateOutputFile(flags.TXTout, "results", "txt", outputDir) for _, elem := range finalResults { AppendOutputToTxt(elem, ResultFilename) } // if secrets flag enabled save also secrets if flags.Secrets { - SecretFilename := fileUtils.CreateOutputFile(flags.TXTout, "secrets", "txt") + SecretFilename := fileUtils.CreateOutputFile(flags.TXTout, "secrets", "txt", outputDir) for _, elem := range finalSecret { AppendOutputToTxt(fmt.Sprintf("%s - %s in %s", elem.Secret.Name, elem.Match, elem.URL), SecretFilename) } @@ -77,7 +77,7 @@ func TxtOutput(flags input.Input, finalResults []string, finalSecret []scanner.S // if endpoints flag enabled save also endpoints if flags.Endpoints { - EndpointFilename := fileUtils.CreateOutputFile(flags.TXTout, "endpoints", "txt") + EndpointFilename := fileUtils.CreateOutputFile(flags.TXTout, "endpoints", "txt", outputDir) for _, elem := range finalEndpoints { for _, parameter := range elem.Parameters { @@ -96,7 +96,7 @@ func TxtOutput(flags input.Input, finalResults []string, finalSecret []scanner.S // if extensions flag enabled save also secrets if 1 <= flags.Extensions && flags.Extensions <= 7 { - ExtensionsFilename := fileUtils.CreateOutputFile(flags.TXTout, "extensions", "txt") + ExtensionsFilename := fileUtils.CreateOutputFile(flags.TXTout, "extensions", "txt", outputDir) for _, elem := range finalExtensions { AppendOutputToTxt(fmt.Sprintf("%s in %s", elem.Filetype.Extension, elem.URL), ExtensionsFilename) } @@ -104,7 +104,7 @@ func TxtOutput(flags input.Input, finalResults []string, finalSecret []scanner.S // if errors flag enabled save also errors if flags.Errors { - ErrorsFilename := fileUtils.CreateOutputFile(flags.TXTout, "errors", "txt") + ErrorsFilename := fileUtils.CreateOutputFile(flags.TXTout, "errors", "txt", outputDir) for _, elem := range finalErrors { AppendOutputToTxt(fmt.Sprintf("%s - %s in %s", elem.Error.ErrorName, elem.Match, elem.URL), ErrorsFilename) } @@ -112,7 +112,7 @@ func TxtOutput(flags input.Input, finalResults []string, finalSecret []scanner.S // if info flag enabled save also infos if flags.Info { - InfosFilename := fileUtils.CreateOutputFile(flags.TXTout, "info", "txt") + InfosFilename := fileUtils.CreateOutputFile(flags.TXTout, "info", "txt", outputDir) for _, elem := range finalInfos { AppendOutputToTxt(fmt.Sprintf("%s - %s in %s", elem.Info.Name, elem.Match, elem.URL), InfosFilename) } @@ -123,8 +123,8 @@ func TxtOutput(flags input.Input, finalResults []string, finalSecret []scanner.S // Actually it manages everything related to HTML output. func HTMLOutput(flags input.Input, resultFilename string, finalResults []string, finalSecret []scanner.SecretMatched, finalEndpoints []scanner.EndpointMatched, finalExtensions []scanner.FileTypeMatched, - finalErrors []scanner.ErrorMatched, finalInfos []scanner.InfoMatched) { - exists, err := fileUtils.ElementExists(CariddiOutputFolder) + finalErrors []scanner.ErrorMatched, finalInfos []scanner.InfoMatched, outputDir string) { + exists, err := fileUtils.ElementExists(outputDir) if err != nil { fmt.Println("Error while creating the output directory.") @@ -132,7 +132,7 @@ func HTMLOutput(flags input.Input, resultFilename string, finalResults []string, } if !exists { - fileUtils.CreateOutputFolder() + fileUtils.CreateOutputFolder(outputDir) } HeaderHTML("Results found", resultFilename) diff --git a/pkg/output/responses.go b/pkg/output/responses.go index fc3decb..81efa40 100644 --- a/pkg/output/responses.go +++ b/pkg/output/responses.go @@ -118,8 +118,8 @@ func getResponseFileName(folder, url string) string { // UpdateIndex updates the index file with the // correct information linking to HTTP responses files. // If it fails returns an error. -func UpdateIndex(resp *colly.Response) error { - index, err := os.OpenFile(filepath.Join(CariddiOutputFolder, index), +func UpdateIndex(resp *colly.Response, outputDir string) error { + index, err := os.OpenFile(filepath.Join(outputDir, index), os.O_APPEND|os.O_WRONLY, fileUtils.Permission0644) if err != nil { @@ -130,7 +130,7 @@ func UpdateIndex(resp *colly.Response) error { builder := &bytes.Buffer{} - builder.WriteString(getResponseFileName(filepath.Join(CariddiOutputFolder, resp.Request.URL.Host), + builder.WriteString(getResponseFileName(filepath.Join(outputDir, resp.Request.URL.Host), resp.Request.URL.String())) builder.WriteRune(' ') builder.WriteString(resp.Request.URL.String()) @@ -148,27 +148,27 @@ func UpdateIndex(resp *colly.Response) error { // WriteHTTPResponse creates an HTTP response output file and // writes the HTTP response inside it. // If it fails returns an error. -func WriteHTTPResponse(inputURL *url.URL, response []byte) error { - file := getResponseFileName(filepath.Join(CariddiOutputFolder, inputURL.Host), inputURL.String()) +func WriteHTTPResponse(inputURL *url.URL, response []byte, outputDir string) (string, error) { + file := getResponseFileName(filepath.Join(outputDir, inputURL.Host), inputURL.String()) outFile, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY, fileUtils.Permission0644) if err != nil { - return err + return file, err } if _, writeErr := outFile.Write(response); writeErr != nil { - return ErrHTTPResp + return file, ErrHTTPResp } - return nil + return file, nil } // StoreHTTPResponse stores an HTTP response in a file. // If it fails returns an error. -func StoreHTTPResponse(r *colly.Response) error { - fileUtils.CreateHostOutputFolder(r.Request.URL.Host) +func StoreHTTPResponse(r *colly.Response, outputDir string) (string, error) { + fileUtils.CreateHostOutputFolder(r.Request.URL.Host, outputDir) - err := UpdateIndex(r) + err := UpdateIndex(r, outputDir) if err != nil { log.Println(err) } @@ -178,10 +178,10 @@ func StoreHTTPResponse(r *colly.Response) error { log.Println(err) } - err = WriteHTTPResponse(r.Request.URL, response) + outputPath, err := WriteHTTPResponse(r.Request.URL, response, outputDir) if err != nil { log.Println(err) } - return nil + return outputPath, nil }