package cmd import ( "fmt" "time" "github.com/spf13/cobra" "go.uber.org/zap" "hub.krkn.tech/KrakenTech/crowsnest/internal/badger" "hub.krkn.tech/KrakenTech/crowsnest/internal/debug" "hub.krkn.tech/KrakenTech/crowsnest/internal/export" "hub.krkn.tech/KrakenTech/crowsnest/internal/files" hunter "hub.krkn.tech/KrakenTech/crowsnest/internal/hunter.io" "hub.krkn.tech/KrakenTech/crowsnest/internal/pretty" "hub.krkn.tech/KrakenTech/crowsnest/internal/sqlite" ) func init() { // Add hunter command to root command rootCmd.AddCommand(hunterCmd) // Add flags specific to hunter command hunterCmd.Flags().StringVarP(&hunterDomain, "domain", "d", "", "Domain to query") hunterCmd.Flags().StringVarP(&hunterEmail, "email", "e", "", "Email to query") hunterCmd.Flags().StringVarP(&hunterFirstName, "first-name", "F", "", "First name to query") hunterCmd.Flags().StringVarP(&hunterLastName, "last-name", "L", "", "Last name to query") hunterCmd.Flags().BoolVarP(&hunterDomainSearch, "domain-search", "D", false, "Search for domain") hunterCmd.Flags().BoolVarP(&hunterEmailFind, "email-find", "E", false, "Find emails for user using domain, first name, and last name") hunterCmd.Flags().BoolVarP(&hunterEmailVerify, "email-verify", "V", false, "Verify email") hunterCmd.Flags().BoolVarP(&hunterCompanyEnrichmentDomain, "company-enrichment", "C", false, "Company enrichment for domain") hunterCmd.Flags().BoolVarP(&hunterPersonEnrichmentEmail, "person-enrichment", "P", false, "Person enrichment for email") hunterCmd.Flags().BoolVarP(&hunterCombinedEnrichmentEmail, "combined-enrichment", "B", false, "Combined Company and Person enrichment for email") hunterCmd.Flags().StringVarP(&hunterOutputFormat, "format", "f", "json", "Output format (json, yaml, xml, txt)") hunterCmd.Flags().StringVarP(&hunterOutputFile, "output", "o", "hunter", "File to output results to including extension") // Add mutually exclusive flags to hunter command hunterCmd.MarkFlagsMutuallyExclusive("email-find") } var ( // Hunter Commands Flags hunterDomain string hunterEmail string hunterFirstName string hunterLastName string hunterDomainSearch bool hunterEmailFind bool hunterEmailVerify bool hunterCompanyEnrichmentDomain bool hunterPersonEnrichmentEmail bool hunterCombinedEnrichmentEmail bool hunterOutputFormat string hunterOutputFile string hunterCmd = &cobra.Command{ Use: "hunter", Short: "Hunter.io API interaction", Long: `Interact with the Hunter.io API for email and domain information.`, Run: func(cmd *cobra.Command, args []string) { if debugGlobal { debug.PrintInfo("debug mode enabled") zap.L().Info("hunter_debug", zap.String("message", "debug mode enabled"), ) } // Flag Checks if !hunterFlagCheck() { return } if hunterOutputFile == "" { if debugGlobal { debug.PrintInfo("output file not specified, using default") } hunterOutputFile = "hunter_" + time.Now().Format("05_04_05") } if hunterOutputFormat == "" { if debugGlobal { debug.PrintInfo("output format not specified, using default") } hunterOutputFormat = "json" } fType := files.GetFileType(hunterOutputFormat) if fType == files.UNKNOWN { fmt.Println("[!] Error: Invalid output format. Must be 'json', 'xml', 'yaml', or 'txt'.") return } if debugGlobal { debug.PrintInfo("using output format: " + hunterOutputFormat) } fmt.Println("[*] Hunter.io API interaction [Beta]") h := hunter.NewHunterIO(getHunterApiKey(), debugGlobal) if hunterDomainSearch { fmt.Println("[*] Performing domain search search...") result, err := h.DomainSearch(hunterDomain) if err != nil { if debugGlobal { debug.PrintInfo("failed to perform domain search") debug.PrintError(err) } zap.L().Error("hunter_domain_search", zap.String("message", "failed to perform domain search"), zap.Error(err), ) fmt.Printf("Error performing domain search: %v\n", err) return } // Store the users discovered var creds []sqlite.User for _, email := range result.Emails { creds = append(creds, sqlite.User{Email: email.Value}) } err = sqlite.StoreUsers(creds) if err != nil { if debugGlobal { debug.PrintInfo("failed to store hunter domain search") debug.PrintError(err) } zap.L().Error("store_hunter_domain_search", zap.String("message", "failed to store hunter domain search"), zap.Error(err), ) fmt.Printf("Error storing Hunter.io Domain Search Result: %v\n", err) } // Write Hunter.io Domain Search Result to file fmt.Printf("[*] Writing Hunter.io Domain Search Result to file: %s%s\n", hunterOutputFile, fType.Extension()) err = export.WriteIStringToFile(result, hunterOutputFile, fType) if err != nil { if debugGlobal { debug.PrintInfo("failed to write hunter domain search to file") debug.PrintError(err) } zap.L().Error("write_hunter_domain_search", zap.String("message", "failed to write hunter domain search to file"), zap.Error(err), ) fmt.Printf("Error writing Hunter.io Domain Search Result to file: %v\n", err) } // Pretty Print Hunter.io Domain Search Result fmt.Println("Domain Search Result:") pretty.HunterDomainTree(hunterDomain, result) return } if hunterEmailFind { fmt.Println("[*] Performing email find search...") result, err := h.EmailFinder(hunterDomain, hunterFirstName, hunterLastName) if err != nil { if debugGlobal { debug.PrintInfo("failed to perform email find") debug.PrintError(err) } zap.L().Error("hunter_email_find", zap.String("message", "failed to perform email find"), zap.Error(err), ) fmt.Printf("Error performing email find: %v\n", err) return } // Write Hunter.io Email Finder Result to file fmt.Printf("[*] Writing Hunter.io Email Finder Result to file: %s%s\n", hunterOutputFile, fType.Extension()) err = export.WriteIStringToFile(result, hunterOutputFile, fType) if err != nil { if debugGlobal { debug.PrintInfo("failed to write hunter email find to file") debug.PrintError(err) } zap.L().Error("write_hunter_email_find", zap.String("message", "failed to write hunter email find to file"), zap.Error(err), ) fmt.Printf("Error writing Hunter.io Email Finder Result to file: %v\n", err) } fmt.Println("Email Find Result:") var ( headers = []string{"Email", "Score", "Domain", "Accept All", "Position", "Twitter", "Linkedin", "Phone Number", "Company", "Verification"} rows [][]string ) rows = append(rows, []string{ result.Email, fmt.Sprintf("%d", result.Score), result.Domain, fmt.Sprintf("%t", result.AcceptAll), result.Position, result.Twitter, result.LinkedinURL, result.PhoneNumber, result.Company, fmt.Sprintf("%v", result.Verification), }) pretty.Table(headers, rows) return } if hunterEmailVerify { fmt.Println("[*] Performing email verification search...") result, err := h.EmailVerification(hunterEmail) if err != nil { if debugGlobal { debug.PrintInfo("failed to perform email verification") debug.PrintError(err) } zap.L().Error("hunter_email_verification", zap.String("message", "failed to perform email verification"), zap.Error(err), ) fmt.Printf("Error performing email verification: %v\n", err) return } // Write Hunter.io Email Verification Result to file fmt.Printf("[*] Writing Hunter.io Email Verification Result to file: %s%s\n", hunterOutputFile, fType.Extension()) err = export.WriteIStringToFile(result, hunterOutputFile, fType) if err != nil { if debugGlobal { debug.PrintInfo("failed to write hunter email verification to file") debug.PrintError(err) } zap.L().Error("write_hunter_email_verification", zap.String("message", "failed to write hunter email verification to file"), zap.Error(err), ) fmt.Printf("Error writing Hunter.io Email Verification Result to file: %v\n", err) } // Pretty Print Hunter.io Email Verification Result var ( headers = []string{"Email", "Result", "Score", "Disposable", "MX Records", "SMTP Server", "SMTP Check"} rows [][]string ) rows = append(rows, []string{ result.Email, result.Result, fmt.Sprintf("%d", result.Score), fmt.Sprintf("%t", result.Disposable), fmt.Sprintf("%t", result.MXRecords), fmt.Sprintf("%t", result.SMTPServer), fmt.Sprintf("%t", result.SMTPCheck), }) fmt.Println("Email Verification Result:") pretty.Table(headers, rows) return } if hunterCompanyEnrichmentDomain { fmt.Println("[*] Performing company enrichment search...") result, err := h.CompanyEnrichment(hunterDomain) if err != nil { if debugGlobal { debug.PrintInfo("failed to perform company enrichment") debug.PrintError(err) } zap.L().Error("hunter_company_enrichment", zap.String("message", "failed to perform company enrichment"), zap.Error(err), ) fmt.Printf("Error performing company enrichment: %v\n", err) return } // Write to file fmt.Printf("[*] Writing Hunter.io Company Enrichment Result to file: %s%s\n", hunterOutputFile, fType.Extension()) err = export.WriteIStringToFile(result, hunterOutputFile, fType) if err != nil { if debugGlobal { debug.PrintInfo("failed to write hunter company enrichment to file") debug.PrintError(err) } zap.L().Error("write_hunter_company_enrichment", zap.String("message", "failed to write hunter company enrichment to file"), zap.Error(err), ) fmt.Printf("Error writing Hunter.io Company Enrichment Result to file: %v\n", err) } // Pretty Print Hunter.io Company Enrichment Result fmt.Println("Company Enrichment Result:") pretty.HunterCompanyEnrichmentTree(hunterDomain, result) return } if hunterPersonEnrichmentEmail { fmt.Println("[*] Performing person enrichment search...") result, err := h.PersonEnrichment(hunterEmail) if err != nil { if debugGlobal { debug.PrintInfo("failed to perform person enrichment") debug.PrintError(err) } zap.L().Error("hunter_person_enrichment", zap.String("message", "failed to perform person enrichment"), zap.Error(err), ) fmt.Printf("Error performing person enrichment: %v\n", err) return } // Write to file fmt.Printf("[*] Writing Hunter.io Person Enrichment Result to file: %s%s\n", hunterOutputFile, fType.Extension()) err = export.WriteIStringToFile(result, hunterOutputFile, fType) if err != nil { if debugGlobal { debug.PrintInfo("failed to write hunter person enrichment to file") debug.PrintError(err) } zap.L().Error("write_hunter_person_enrichment", zap.String("message", "failed to write hunter person enrichment to file"), zap.Error(err), ) fmt.Printf("Error writing Hunter.io Person Enrichment Result to file: %v\n", err) } // Pretty Print Hunter.io Person Enrichment Result fmt.Println("Person Enrichment Result:") pretty.HunterPersonEnrichmentTree(hunterEmail, result) return } if hunterCombinedEnrichmentEmail { fmt.Println("[*] Performing combined enrichment search...") result, err := h.CombinedEnrichment(hunterEmail) if err != nil { if debugGlobal { debug.PrintInfo("failed to perform combined enrichment") debug.PrintError(err) } zap.L().Error("hunter_combined_enrichment", zap.String("message", "failed to perform combined enrichment"), zap.Error(err), ) fmt.Printf("Error performing combined enrichment: %v\n", err) return } // Write to file fmt.Printf("[*] Writing Hunter.io Combined Enrichment Result to file: %s%s\n", hunterOutputFile, fType.Extension()) err = export.WriteIStringToFile(result, hunterOutputFile, fType) if err != nil { if debugGlobal { debug.PrintInfo("failed to write hunter combined enrichment to file") debug.PrintError(err) } zap.L().Error("write_hunter_combined_enrichment", zap.String("message", "failed to write hunter combined enrichment to file"), zap.Error(err), ) fmt.Printf("Error writing Hunter.io Combined Enrichment Result to file: %v\n", err) } fmt.Println("Combined Enrichment Result:") pretty.HunterCombinedEnrichmentTree(hunterEmail, result) return } }, } ) func hunterFlagCheck() bool { if debugGlobal { debug.PrintInfo("checking flags") } var optionSet bool if hunterDomainSearch { if hunterDomain == "" { fmt.Println("Domain is required for domain search") return false } optionSet = true } if hunterEmailVerify { if hunterEmail == "" { fmt.Println("Email is required for email verification") return false } optionSet = true } if hunterCompanyEnrichmentDomain { if hunterDomain == "" { fmt.Println("Domain is required for company enrichment") return false } optionSet = true } if hunterPersonEnrichmentEmail { if hunterEmail == "" { fmt.Println("Email is required for person enrichment") return false } optionSet = true } if hunterCombinedEnrichmentEmail { if hunterEmail == "" { fmt.Println("Email is required for combined enrichment") return false } optionSet = true } if hunterEmailFind { if hunterFirstName == "" || hunterLastName == "" { fmt.Println("First name and last name are required for email find") return false } if hunterDomain == "" { fmt.Println("Domain is required for email find") return false } optionSet = true } if !optionSet { fmt.Println("[!] No options selected") return false } return true } // Helper functions to get stored API credentials func getHunterApiKey() string { return badger.GetHunterKey() }