package cmd import ( "bufio" "fmt" "log" "os" "github.com/spf13/cobra" "shimon/internal/shimon" ) var ( domainsPath string outFile string prefix string regexp string suffix string timeout int ulimit int64 workers int rootCmd = &cobra.Command{ Use: "shimon", Short: "Load a file containing domains then return a list of domains matching a provided regular expression", Run: rootRun, } ) // Initialize flags for cobra func init() { rootCmd.PersistentFlags().StringVarP(&domainsPath, "domains-file", "f", "", "Path to the input file containing domains (Example: './domains.txt')") rootCmd.MarkPersistentFlagRequired("file") rootCmd.PersistentFlags().StringVarP(&outFile, "output-file", "o", "results.txt", "File to output matching domains to") rootCmd.PersistentFlags().StringVarP(&prefix, "prefix", "p", "https://", "Prefix for each domain") rootCmd.PersistentFlags().StringVarP(®exp, "regexp", "r", "", "Regular expression for fingerprinting (Example '[g|G]oogle'") rootCmd.MarkPersistentFlagRequired("regexp") rootCmd.PersistentFlags().StringVarP(&suffix, "suffix", "s", "/", "Suffix for each domain (Example: '/index/login.php'") rootCmd.PersistentFlags().IntVarP(&timeout, "timeout", "t", 30, "Timeout (in seconds) for each GET request") rootCmd.PersistentFlags().Int64VarP(&ulimit, "ulimit", "u", 1024, "Maximum number of open files allowed by 'ulimit -n'") rootCmd.PersistentFlags().IntVarP(&workers, "workers", "w", 1000, "Number of workers to create") } // Load all domains (separated by newline) from a file into a slice of strings func loadDomains(filePath string) ([]string, error) { file, err := os.Open(filePath) if err != nil { return []string{}, err } defer file.Close() // Ensure files is closed at the end of the function domains := []string{} buf := bufio.NewScanner(file) for buf.Scan() { domains = append(domains, buf.Text()) } if err := buf.Err(); err != nil { return []string{}, err } return domains, nil } // Function for the root command (shimon) func rootRun(cmd *cobra.Command, args []string) { domains, err := loadDomains(domainsPath) if err != nil { log.Fatalf(err.Error()) } shimon := shimon.ShiMon{ Domains: domains, Prefix: prefix, RegExp: regexp, Suffix: suffix, Timeout: timeout, Ulimit: ulimit, Workers: workers, } // Create results file for writing resultsFile, err := os.Create(outFile) if err != nil { log.Fatal(err) } // Create a buffered writer to write to this file (allows for partial writes) writer := bufio.NewWriter(resultsFile) // Start scan and receive matches then perform a hard exit if it errors matches, err := shimon.Scan(writer) if err != nil { log.Fatalf(err.Error()) } fmt.Println("\nResults:") for _, match := range matches { fmt.Println(" ", match) } fmt.Println("\nSaving results to:", outFile) } // Execute executes the root command. func Execute() error { return rootCmd.Execute() }