From da16ec0e7f321066998ef4714a80be04b7c9ae81 Mon Sep 17 00:00:00 2001 From: xyzeva Date: Sat, 22 Jun 2024 12:09:50 +0300 Subject: [PATCH] feat: implement api mode --- pkg/config/config.go | 32 ++++++++------- pkg/scan/dirlist.go | 21 ++++++++-- pkg/scan/dnslist.go | 16 +++++--- pkg/scan/dork.go | 22 +++++++++-- pkg/scan/git.go | 14 +++++-- pkg/scan/js/scan.go | 50 ++++++++++++++--------- pkg/scan/js/supabase.go | 32 +++++++++------ pkg/scan/nuclei.go | 12 ++++-- pkg/scan/ports.go | 10 +++-- sif.go | 87 ++++++++++++++++++++++++++++++++++------- 10 files changed, 212 insertions(+), 84 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 845fcfd..b3b6cf3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -8,21 +8,22 @@ import ( ) type Settings struct { - Dirlist string - Dnslist string - Debug bool - LogDir string - NoScan bool - Ports string - Dorking bool - Git bool - Whois bool - Threads int - Nuclei bool - Timeout time.Duration - URLs goflags.StringSlice - File string - ApiMode bool + Dirlist string + Dnslist string + Debug bool + LogDir string + NoScan bool + Ports string + Dorking bool + Git bool + Whois bool + Threads int + Nuclei bool + JavaScript bool + Timeout time.Duration + URLs goflags.StringSlice + File string + ApiMode bool } const ( @@ -60,6 +61,7 @@ func Parse() *Settings { flagSet.BoolVar(&settings.Nuclei, "nuclei", false, "Enable scanning using nuclei templates"), flagSet.BoolVar(&settings.NoScan, "noscan", false, "Do not perform base URL (robots.txt, etc) scanning"), flagSet.BoolVar(&settings.Whois, "whois", false, "Enable WHOIS lookup"), + flagSet.BoolVar(&settings.JavaScript, "js", false, "Enable JavaScript scans"), ) flagSet.CreateGroup("runtime", "Runtime", diff --git a/pkg/scan/dirlist.go b/pkg/scan/dirlist.go index a24f5d1..fd6d09c 100644 --- a/pkg/scan/dirlist.go +++ b/pkg/scan/dirlist.go @@ -22,7 +22,12 @@ const ( bigFile = "directory-list-2.3-big.txt" ) -func Dirlist(size string, url string, timeout time.Duration, threads int, logdir string) { +type DirectoryResult struct { + Url string `json:"url"` + StatusCode int `json:"status_code"` +} + +func Dirlist(size string, url string, timeout time.Duration, threads int, logdir string) ([]DirectoryResult, error) { fmt.Println(styles.Separator.Render("πŸ“‚ Starting " + styles.Status.Render("directory fuzzing") + "...")) @@ -31,7 +36,7 @@ func Dirlist(size string, url string, timeout time.Duration, threads int, logdir if logdir != "" { if err := logger.WriteHeader(sanitizedURL, logdir, size+" directory fuzzing"); err != nil { log.Errorf("Error creating log file: %v", err) - return + return nil, err } } @@ -55,7 +60,7 @@ func Dirlist(size string, url string, timeout time.Duration, threads int, logdir resp, err := http.Get(list) if err != nil { log.Errorf("Error downloading directory list: %s", err) - return + return nil, err } defer resp.Body.Close() var directories []string @@ -71,6 +76,8 @@ func Dirlist(size string, url string, timeout time.Duration, threads int, logdir var wg sync.WaitGroup wg.Add(threads) + + results := []DirectoryResult{} for thread := 0; thread < threads; thread++ { go func(thread int) { defer wg.Done() @@ -93,9 +100,17 @@ func Dirlist(size string, url string, timeout time.Duration, threads int, logdir if logdir != "" { logger.Write(sanitizedURL, logdir, fmt.Sprintf("%s [%s]\n", strconv.Itoa(resp.StatusCode), directory)) } + + result := DirectoryResult{ + Url: resp.Request.URL.String(), + StatusCode: resp.StatusCode, + } + results = append(results, result) } } }(thread) } wg.Wait() + + return results, nil } diff --git a/pkg/scan/dnslist.go b/pkg/scan/dnslist.go index d6f3a6a..3c4d858 100644 --- a/pkg/scan/dnslist.go +++ b/pkg/scan/dnslist.go @@ -21,7 +21,7 @@ const ( dnsBigFile = "subdomains-10000.txt" ) -func Dnslist(size string, url string, timeout time.Duration, threads int, logdir string) { +func Dnslist(size string, url string, timeout time.Duration, threads int, logdir string) ([]string, error) { fmt.Println(styles.Separator.Render("πŸ“‘ Starting " + styles.Status.Render("DNS fuzzing") + "...")) @@ -45,7 +45,7 @@ func Dnslist(size string, url string, timeout time.Duration, threads int, logdir resp, err := http.Get(list) if err != nil { log.Errorf("Error downloading DNS list: %s", err) - return + return nil, err } defer resp.Body.Close() var dns []string @@ -60,7 +60,7 @@ func Dnslist(size string, url string, timeout time.Duration, threads int, logdir if logdir != "" { if err := logger.WriteHeader(sanitizedURL, logdir, size+" subdomain fuzzing"); err != nil { log.Errorf("Error creating log file: %v", err) - return + return nil, err } } @@ -70,6 +70,8 @@ func Dnslist(size string, url string, timeout time.Duration, threads int, logdir var wg sync.WaitGroup wg.Add(threads) + + urls := []string{} for thread := 0; thread < threads; thread++ { go func(thread int) { defer wg.Done() @@ -80,10 +82,11 @@ func Dnslist(size string, url string, timeout time.Duration, threads int, logdir } log.Debugf("Looking up: %s", domain) - _, err := client.Get("http://" + domain + "." + sanitizedURL) + resp, err := client.Get("http://" + domain + "." + sanitizedURL) if err != nil { log.Debugf("Error %s: %s", domain, err) } else { + urls = append(urls, resp.Request.URL.String()) dnslog.Infof("%s %s.%s", styles.Status.Render("[http]"), styles.Highlight.Render(domain), sanitizedURL) if logdir != "" { @@ -97,10 +100,11 @@ func Dnslist(size string, url string, timeout time.Duration, threads int, logdir } } - _, err = client.Get("https://" + domain + "." + sanitizedURL) + resp, err = client.Get("https://" + domain + "." + sanitizedURL) if err != nil { log.Debugf("Error %s: %s", domain, err) } else { + urls = append(urls, resp.Request.URL.String()) dnslog.Infof("%s %s.%s", styles.Status.Render("[https]"), styles.Highlight.Render(domain), sanitizedURL) if logdir != "" { logger.Write(sanitizedURL, logdir, fmt.Sprintf("[https] %s.%s\n", domain, sanitizedURL)) @@ -110,4 +114,6 @@ func Dnslist(size string, url string, timeout time.Duration, threads int, logdir }(thread) } wg.Wait() + + return urls, nil } diff --git a/pkg/scan/dork.go b/pkg/scan/dork.go index 23b9bb3..9e7c14b 100644 --- a/pkg/scan/dork.go +++ b/pkg/scan/dork.go @@ -21,7 +21,12 @@ const ( dorkFile = "dork.txt" ) -func Dork(url string, timeout time.Duration, threads int, logdir string) { +type DorkResult struct { + Url string `json:"url"` + Count int `json:"count"` +} + +func Dork(url string, timeout time.Duration, threads int, logdir string) ([]DorkResult, error) { fmt.Println(styles.Separator.Render("πŸ€“ Starting " + styles.Status.Render("URL Dorking") + "...")) @@ -30,7 +35,7 @@ func Dork(url string, timeout time.Duration, threads int, logdir string) { if logdir != "" { if err := logger.WriteHeader(sanitizedURL, logdir, "URL dorking"); err != nil { log.Errorf("Error creating log file: %v", err) - return + return nil, err } } @@ -43,7 +48,7 @@ func Dork(url string, timeout time.Duration, threads int, logdir string) { resp, err := http.Get(dorkURL + dorkFile) if err != nil { log.Errorf("Error downloading dork list: %s", err) - return + return nil, err } defer resp.Body.Close() var dorks []string @@ -56,6 +61,8 @@ func Dork(url string, timeout time.Duration, threads int, logdir string) { // util.InitProgressBar() var wg sync.WaitGroup wg.Add(threads) + + dorkResults := []DorkResult{} for thread := 0; thread < threads; thread++ { go func(thread int) { defer wg.Done() @@ -71,9 +78,18 @@ func Dork(url string, timeout time.Duration, threads int, logdir string) { if logdir != "" { logger.Write(sanitizedURL, logdir, fmt.Sprintf("%s dork results found for dork [%s]\n", strconv.Itoa(len(results)), dork)) } + + result := DorkResult{ + Url: dork, + Count: len(results), + } + + dorkResults = append(dorkResults, result) } } }(thread) } wg.Wait() + + return dorkResults, nil } diff --git a/pkg/scan/git.go b/pkg/scan/git.go index bbf5b6b..ead1b2e 100644 --- a/pkg/scan/git.go +++ b/pkg/scan/git.go @@ -20,7 +20,7 @@ const ( gitFile = "git.txt" ) -func Git(url string, timeout time.Duration, threads int, logdir string) { +func Git(url string, timeout time.Duration, threads int, logdir string) ([]string, error) { fmt.Println(styles.Separator.Render("🌿 Starting " + styles.Status.Render("git repository scanning") + "...")) @@ -29,7 +29,7 @@ func Git(url string, timeout time.Duration, threads int, logdir string) { if logdir != "" { if err := logger.WriteHeader(sanitizedURL, logdir, "git directory fuzzing"); err != nil { log.Errorf("Error creating log file: %v", err) - return + return nil, err } } @@ -42,7 +42,7 @@ func Git(url string, timeout time.Duration, threads int, logdir string) { resp, err := http.Get(gitURL + gitFile) if err != nil { log.Errorf("Error downloading git list: %s", err) - return + return nil, err } defer resp.Body.Close() var gitUrls []string @@ -59,6 +59,8 @@ func Git(url string, timeout time.Duration, threads int, logdir string) { var wg sync.WaitGroup wg.Add(threads) + + foundUrls := []string{} for thread := 0; thread < threads; thread++ { go func(thread int) { defer wg.Done() @@ -74,15 +76,19 @@ func Git(url string, timeout time.Duration, threads int, logdir string) { log.Debugf("Error %s: %s", repourl, err) } - if resp.StatusCode != 404 { + if resp.StatusCode == 200 && !strings.HasPrefix(resp.Header.Get("Content-Type"), "text/html") { // log url, directory, and status code gitlog.Infof("%s git found at [%s]", styles.Status.Render(strconv.Itoa(resp.StatusCode)), styles.Highlight.Render(repourl)) if logdir != "" { logger.Write(sanitizedURL, logdir, fmt.Sprintf("%s git found at [%s]\n", strconv.Itoa(resp.StatusCode), repourl)) } + + foundUrls = append(foundUrls, resp.Request.URL.String()) } } }(thread) } wg.Wait() + + return foundUrls, nil } diff --git a/pkg/scan/js/scan.go b/pkg/scan/js/scan.go index cdef29c..319471d 100644 --- a/pkg/scan/js/scan.go +++ b/pkg/scan/js/scan.go @@ -2,10 +2,10 @@ package js import ( "bufio" - "encoding/json" "fmt" "io" "net/http" + "os" "slices" "strings" "time" @@ -16,15 +16,24 @@ import ( urlutil "github.com/projectdiscovery/utils/url" ) -func JavascriptScan(url string, timeout time.Duration, threads int, logdir string) { +type JavascriptScanResult struct { + SupabaseResults []supabaseScanResult `json:"supabase_results"` + FoundEnvironmentVars map[string]string `json:"environment_variables"` +} + +func JavascriptScan(url string, timeout time.Duration, threads int, logdir string) (*JavascriptScanResult, error) { + jslog := log.NewWithOptions(os.Stderr, log.Options{ + Prefix: "🚧 JavaScript", + }).With("url", url) + baseUrl, err := urlutil.Parse(url) if err != nil { - return + return nil, err } resp, err := http.Get(url) if err != nil { fmt.Println(err) - return + return nil, err } defer resp.Body.Close() @@ -37,13 +46,13 @@ func JavascriptScan(url string, timeout time.Duration, threads int, logdir strin doc, err := htmlquery.Parse(strings.NewReader(html)) if err != nil { - return + return nil, err } var scripts []string nodes, err := htmlquery.QueryAll(doc, "//script/@src") if err != nil { - return + return nil, err } for _, node := range nodes { var src = htmlquery.InnerText(node) @@ -61,9 +70,10 @@ func JavascriptScan(url string, timeout time.Duration, threads int, logdir strin for _, script := range scripts { if strings.Contains(script, "/_buildManifest.js") { + jslog.Infof("Detected Next.JS pages router! Getting all scripts from %s", script) nextScripts, err := frameworks.GetPagesRouterScripts(script) if err != nil { - return + return nil, err } for _, nextScript := range nextScripts { @@ -75,10 +85,11 @@ func JavascriptScan(url string, timeout time.Duration, threads int, logdir strin } } - log.Debugf("Got all scripts: %s, now running scans on them", scripts) + jslog.Infof("Got %d scripts, now running scans on them", len(scripts)) + var supabaseResults []supabaseScanResult for _, script := range scripts { - log.Debugf("Scanning %s", script) + jslog.Infof("Scanning %s", script) resp, err := http.Get(script) if err != nil { fmt.Println(err) @@ -92,19 +103,22 @@ func JavascriptScan(url string, timeout time.Duration, threads int, logdir strin } content := string(bodyBytes) - supabaseResults, err := ScanSupabase(content) + jslog.Infof("Running supabase scanner on %s", script) + scriptSupabaseResults, err := ScanSupabase(content, script) if err != nil { - log.Debugf("Error while scanning supabase: %s", err) + jslog.Errorf("Error while scanning supabase: %s", err) } - if supabaseResults != nil { - marshalled, err := json.Marshal(supabaseResults) - if err != nil { - continue - } - - log.Debugf("Supabase results: %s", marshalled) + if scriptSupabaseResults != nil { + supabaseResults = append(supabaseResults, scriptSupabaseResults...) } } + + result := JavascriptScanResult{ + SupabaseResults: supabaseResults, + FoundEnvironmentVars: map[string]string{}, + } + + return &result, nil } diff --git a/pkg/scan/js/supabase.go b/pkg/scan/js/supabase.go index 69760dc..6c5be54 100644 --- a/pkg/scan/js/supabase.go +++ b/pkg/scan/js/supabase.go @@ -10,6 +10,7 @@ import ( "io" "math" "net/http" + "os" "regexp" "slices" "strconv" @@ -92,7 +93,10 @@ func GetSupabaseJsonResponse(projectId string, path string, apikey string, auth return data.(map[string]interface{}), nil } -func ScanSupabase(jsContent string) ([]supabaseScanResult, error) { +func ScanSupabase(jsContent string, jsUrl string) ([]supabaseScanResult, error) { + supabaselog := log.NewWithOptions(os.Stderr, log.Options{ + Prefix: "🚧 JavaScript > Supabase ⚑️", + }).With("url", jsUrl) jwtRegex, err := regexp.Compile("[\"|'|`](ey[A-Za-z0-9_-]{2,}(?:\\.[A-Za-z0-9_-]{2,}){2})[\"|'|`]") @@ -118,15 +122,15 @@ func ScanSupabase(jsContent string) ([]supabaseScanResult, error) { decoded, err := base64.RawStdEncoding.DecodeString(body) if err != nil { - log.Debugf("Failed to decode JWT %s: %s", body, err) + supabaselog.Debugf("Failed to decode JWT %s: %s", body, err) continue } - log.Debugf("JWT body: %s", decoded) + supabaselog.Debugf("JWT body: %s", decoded) var supabaseJwt *supabaseJwtBody err = json.Unmarshal([]byte(decoded), &supabaseJwt) if err != nil { - log.Debugf("Failed to json parse JWT %s: %s", jwt, err) + supabaselog.Debugf("Failed to json parse JWT %s: %s", jwt, err) continue } @@ -134,22 +138,22 @@ func ScanSupabase(jsContent string) ([]supabaseScanResult, error) { continue } - log.Debugf("Found valid supabase project %s with role %s", *supabaseJwt.ProjectId, *supabaseJwt.Role) + supabaselog.Infof("Found valid supabase project %s with role %s", *supabaseJwt.ProjectId, *supabaseJwt.Role) client := http.Client{} req, err := http.NewRequest("POST", "https://"+*supabaseJwt.ProjectId+".supabase.co/auth/v1/signup", bytes.NewBufferString(`{"email":"automated`+strconv.Itoa(int(time.Now().Unix()))+`@sif.sh","password":"automatedacct"}`)) if err != nil { - log.Debugf("1 %s", err) + supabaselog.Errorf("Error while creating HTTP req for creating user: %s", err) + continue } req.Header.Set("apikey", jwt) resp, err := client.Do(req) if err != nil { - log.Debugf("2 %s", err) - + supabaselog.Errorf("Error while sending request to create user: %s", err) + continue } - log.Debugf("%d", resp.StatusCode) var auth string if resp.StatusCode == http.StatusOK { body, err := io.ReadAll(resp.Body) @@ -165,7 +169,7 @@ func ScanSupabase(jsContent string) ([]supabaseScanResult, error) { } auth = data["access_token"].(string) - log.Debugf("Created account with JWT %s", auth) + supabaselog.Infof("Created account with JWT %s", auth) } var collections = []supabaseCollection{} @@ -199,11 +203,13 @@ func ScanSupabase(jsContent string) ([]supabaseScanResult, error) { } samples := sampleObj["array"].([]interface{}) - - for _, sample := range samples { - log.Debugf("%s", sample) + marshalled, err := json.Marshal(samples) + if err != nil { + supabaselog.Errorf("Failed to marshal sample data for %s: %s", k, err) } + supabaselog.Infof("Got sample (1000 entries) for collection %s: %s", k, string(marshalled)) + limitedSample := samples[0:int(math.Min(float64(len(samples)), 10))] collection := supabaseCollection{ diff --git a/pkg/scan/nuclei.go b/pkg/scan/nuclei.go index ffac6fc..8303708 100644 --- a/pkg/scan/nuclei.go +++ b/pkg/scan/nuclei.go @@ -30,7 +30,7 @@ import ( "github.com/projectdiscovery/ratelimit" ) -func Nuclei(url string, timeout time.Duration, threads int, logdir string) { +func Nuclei(url string, timeout time.Duration, threads int, logdir string) ([]output.ResultEvent, error) { fmt.Println(styles.Separator.Render("βš›οΈ Starting " + styles.Status.Render("nuclei template scanning") + "...")) sanitizedURL := strings.Split(url, "://")[1] @@ -50,12 +50,14 @@ func Nuclei(url string, timeout time.Duration, threads int, logdir string) { config.DefaultConfig.SetTemplatesDir(pwd) catalog := disk.NewCatalog(pwd) + results := []output.ResultEvent{} // Custom output outputWriter := testutils.NewMockOutputWriter() outputWriter.WriteCallback = func(event *output.ResultEvent) { if event.Matched != "" { nucleilog.Infof(format.FormatLine(event)) + results = append(results, *event) // TODO: metasploit } } @@ -70,7 +72,7 @@ func Nuclei(url string, timeout time.Duration, threads int, logdir string) { interactOpts := interactsh.DefaultOptions(outputWriter, reportingClient, progressClient) interactClient, err := interactsh.New(interactOpts) if err != nil { - log.Fatalf("Could not create interact client: %s\n", err) + return nil, err } defer interactClient.Close() @@ -92,13 +94,13 @@ func Nuclei(url string, timeout time.Duration, threads int, logdir string) { workflowLoader, err := parsers.NewLoader(&executorOpts) if err != nil { - nucleilog.Fatalf("Could not create workflow loader: %s\n", err) + return nil, err } executorOpts.WorkflowLoader = workflowLoader store, err := loader.New(loader.NewConfig(options, catalog, executorOpts)) if err != nil { - nucleilog.Fatalf("Could not create loader client: %s\n", err) + return nil, err } store.Load() @@ -107,4 +109,6 @@ func Nuclei(url string, timeout time.Duration, threads int, logdir string) { _ = engine.Execute(store.Templates(), input) engine.WorkPool().Wait() + + return results, nil } diff --git a/pkg/scan/ports.go b/pkg/scan/ports.go index da0b0e8..9eaff28 100644 --- a/pkg/scan/ports.go +++ b/pkg/scan/ports.go @@ -18,14 +18,14 @@ import ( const commonPorts = "https://raw.githubusercontent.com/dropalldatabases/sif-runtime/main/ports/top-ports.txt" -func Ports(scope string, url string, timeout time.Duration, threads int, logdir string) { - fmt.Println(styles.Separator.Render("πŸšͺ Starting " + styles.Status.Render("port scanning") + "...")) +func Ports(scope string, url string, timeout time.Duration, threads int, logdir string) ([]string, error) { + log.Printf(styles.Separator.Render("πŸšͺ Starting " + styles.Status.Render("port scanning") + "...")) sanitizedURL := strings.Split(url, "://")[1] if logdir != "" { if err := logger.WriteHeader(sanitizedURL, logdir, scope+" port scanning"); err != nil { log.Errorf("Error creating log file: %v", err) - return + return nil, err } } @@ -41,7 +41,7 @@ func Ports(scope string, url string, timeout time.Duration, threads int, logdir resp, err := http.Get(commonPorts) if err != nil { log.Errorf("Error downloading ports list: %s", err) - return + return nil, err } defer resp.Body.Close() scanner := bufio.NewScanner(resp.Body) @@ -89,4 +89,6 @@ func Ports(scope string, url string, timeout time.Duration, threads int, logdir } else { portlog.Error("Found no open ports") } + + return openPorts, nil } diff --git a/sif.go b/sif.go index 35005c9..02d9bd6 100644 --- a/sif.go +++ b/sif.go @@ -2,6 +2,7 @@ package sif import ( "bufio" + "encoding/json" "errors" "fmt" "os" @@ -12,8 +13,7 @@ import ( "github.com/dropalldatabases/sif/pkg/config" "github.com/dropalldatabases/sif/pkg/logger" "github.com/dropalldatabases/sif/pkg/scan" - "github.com/dropalldatabases/sif/pkg/scan/js" - "github.com/dropalldatabases/sif/pkg/utils" + jsscan "github.com/dropalldatabases/sif/pkg/scan/js" ) // App is a client instance. It is first initialised using New and then ran @@ -24,6 +24,16 @@ type App struct { logFiles []string } +type UrlResult struct { + Url string `json:"url"` + Results []ModuleResult +} + +type ModuleResult struct { + Id string `json:"id"` + Data interface{} `json:"data"` +} + // New creates a new App struct by parsing the configuration options, // figuring out the targets from list or file, etc. // @@ -68,6 +78,10 @@ func (app *App) Run() error { log.SetLevel(log.DebugLevel) } + if app.settings.ApiMode { + log.SetLevel(5) + } + if app.settings.LogDir != "" { if err := logger.Init(app.settings.LogDir); err != nil { return err @@ -81,6 +95,8 @@ func (app *App) Run() error { log.Infof("πŸ“‘Starting scan on %s...", url) + moduleResults := []ModuleResult{} + if app.settings.LogDir != "" { if err := logger.CreateFile(&app.logFiles, url, app.settings.LogDir); err != nil { return err @@ -92,15 +108,30 @@ func (app *App) Run() error { } if app.settings.Dirlist != "none" { - scan.Dirlist(app.settings.Dirlist, url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + result, err := scan.Dirlist(app.settings.Dirlist, url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + if err != nil { + log.Errorf("Error while running directory scan: %s", err) + } else { + moduleResults = append(moduleResults, ModuleResult{"dirlist", result}) + } } if app.settings.Dnslist != "none" { - scan.Dnslist(app.settings.Dnslist, url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + result, err := scan.Dnslist(app.settings.Dnslist, url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + if err != nil { + log.Errorf("Error while running dns scan: %s", err) + } else { + moduleResults = append(moduleResults, ModuleResult{"dnslist", result}) + } } if app.settings.Ports != "none" { - scan.Ports(app.settings.Ports, url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + result, err := scan.Ports(app.settings.Ports, url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + if err != nil { + log.Errorf("Error while running port scan: %s", err) + } else { + moduleResults = append(moduleResults, ModuleResult{"portscan", result}) + } } if app.settings.Whois { @@ -109,26 +140,52 @@ func (app *App) Run() error { // func Git(url string, timeout time.Duration, threads int, logdir string) if app.settings.Git { - scan.Git(url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + result, err := scan.Git(url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + if err != nil { + log.Errorf("Error while running Git module: %s", err) + } else { + moduleResults = append(moduleResults, ModuleResult{"git", result}) + } } if app.settings.Nuclei { - scan.Nuclei(url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + result, err := scan.Nuclei(url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + if err != nil { + log.Errorf("Error while running Nuclei module: %s", err) + } else { + moduleResults = append(moduleResults, ModuleResult{"nuclei", result}) + } } - js.JavascriptScan(url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + if app.settings.JavaScript { + result, err := jsscan.JavascriptScan(url, app.settings.Timeout, app.settings.Threads, app.settings.LogDir) + if err != nil { + log.Errorf("Error while running JS module: %s", err) + } else { + moduleResults = append(moduleResults, ModuleResult{"js", result}) + } + } if app.settings.ApiMode { - utils.ReturnApiOutput() - } + result := UrlResult{ + Url: url, + Results: moduleResults, + } - // TODO: WHOIS + marshalled, err := json.Marshal(result) + if err != nil { + log.Fatalf("failed to marshal result: %s", err) + } + fmt.Println(string(marshalled)) + } } - if app.settings.LogDir != "" { - fmt.Println(styles.Box.Render(fmt.Sprintf("🌿 All scans completed!\nπŸ“‚ Output saved to files: %s\n", strings.Join(app.logFiles, ", ")))) - } else { - fmt.Println(styles.Box.Render(fmt.Sprintf("🌿 All scans completed!\n"))) + if !app.settings.ApiMode { + if app.settings.LogDir != "" { + fmt.Println(styles.Box.Render(fmt.Sprintf("🌿 All scans completed!\nπŸ“‚ Output saved to files: %s\n", strings.Join(app.logFiles, ", ")))) + } else { + fmt.Println(styles.Box.Render(fmt.Sprintf("🌿 All scans completed!\n"))) + } } return nil