- Added datawells subcommand

- Altered the request format to match the new api request structure
- Altered max results per page to reflect updated Dehashed API max (10000)
This commit is contained in:
Evan Hosinski
2026-04-07 09:09:12 -04:00
parent da53a787fe
commit 5c36b034b6
11 changed files with 499 additions and 63 deletions
+64 -5
View File
@@ -4,6 +4,8 @@ import (
"crowsnest/internal/badger"
"crowsnest/internal/debug"
"crowsnest/internal/dehashed"
"crowsnest/internal/files"
"crowsnest/internal/pretty"
"crowsnest/internal/sqlite"
"fmt"
"github.com/spf13/cobra"
@@ -13,19 +15,20 @@ import (
func init() {
// Add api command to root command
rootCmd.AddCommand(dehashedCmd)
dehashedCmd.AddCommand(dehashedDataWellsCmd)
// Add flags specific to api command
dehashedCmd.Flags().IntVarP(&maxRecords, "max-records", "m", 30000, "Maximum amount of records to return")
dehashedCmd.Flags().IntVarP(&maxRecords, "max-records", "m", 50000, "Maximum total records to return (max 50000)")
dehashedCmd.Flags().IntVarP(&maxRequests, "max-requests", "r", -1, "Maximum number of requests to make")
dehashedCmd.Flags().IntVarP(&startingPage, "starting-page", "s", 1, "Starting page for requests")
dehashedCmd.Flags().BoolVarP(&printBalance, "print-balance", "b", false, "Print remaining balance after requests")
dehashedCmd.Flags().BoolVarP(&regexMatch, "regex-match", "R", false, "Use regex matching on query fields")
dehashedCmd.Flags().BoolVarP(&wildcardMatch, "wildcard-match", "W", false, "Use wildcard matching on query fields (Use ? to replace a single character, and * for multiple characters)")
dehashedCmd.Flags().BoolVarP(&credsOnly, "creds-only", "C", false, "Return credentials only")
dehashedCmd.Flags().StringVarP(&outputFormat, "format", "f", "json", "Output format (json, yaml, xml, txt)")
dehashedCmd.Flags().StringVarP(&outputFile, "output", "o", "query", "File to output results to including extension")
dehashedCmd.Flags().StringVarP(&outputFormat, "format", "f", "json", "Output format (json, yaml, xml, txt, grep)")
dehashedCmd.Flags().StringVarP(&outputFile, "output", "o", "query", "File to output results to without extension")
dehashedCmd.Flags().StringVarP(&usernameQuery, "username", "U", "", "Username query")
dehashedCmd.Flags().StringVarP(&emailQuery, "email-query", "E", "", "HunterEmail query")
dehashedCmd.Flags().StringVarP(&emailQuery, "email-query", "E", "", "Email query")
dehashedCmd.Flags().StringVarP(&ipQuery, "ip", "I", "", "IP address query")
dehashedCmd.Flags().StringVarP(&domainQuery, "domain", "D", "", "Domain query")
dehashedCmd.Flags().StringVarP(&passwordQuery, "password", "P", "", "Password query")
@@ -40,6 +43,12 @@ func init() {
// Add mutually exclusive flags to wildcard match and regex match
dehashedCmd.MarkFlagsMutuallyExclusive("regex-match", "wildcard-match")
dehashedDataWellsCmd.Flags().IntVar(&dataWellsCount, "count", 20, "Number of data wells to return (20 or 50)")
dehashedDataWellsCmd.Flags().IntVarP(&dataWellsPage, "page", "p", 1, "Data wells page to request")
dehashedDataWellsCmd.Flags().StringVar(&dataWellsSort, "sort", "", "Sort data wells by added, name, date, or records; optionally suffix -ASC or -DESC")
dehashedDataWellsCmd.Flags().StringVarP(&dataWellsOutputFormat, "format", "f", "json", "Output format (json, yaml, xml, txt, grep)")
dehashedDataWellsCmd.Flags().StringVarP(&dataWellsOutputFile, "output", "o", "data_wells", "File to output data wells to without extension")
}
var (
@@ -66,6 +75,11 @@ var (
phoneQuery string
socialQuery string
cryptoCurrencyAddressQuery string
dataWellsCount int
dataWellsPage int
dataWellsSort string
dataWellsOutputFormat string
dataWellsOutputFile string
// Query command
dehashedCmd = &cobra.Command{
@@ -77,7 +91,7 @@ var (
// Validate credentials
if key == "" {
fmt.Println("API key is required. Set the key with the \"set-key\" command. [crowsnest set-key <api_key>]")
fmt.Println("API key is required. Set the key with the \"set dehashed\" command. [crowsnest set dehashed <api_key>]")
return
}
@@ -133,9 +147,54 @@ var (
}
},
}
dehashedDataWellsCmd = &cobra.Command{
Use: "data-wells",
Short: "List DeHashed data wells",
Long: `List DeHashed data wells. This endpoint is free and does not require a DeHashed API key or subscription.`,
Run: func(cmd *cobra.Command, args []string) {
client := dehashed.NewDehashedClientV2("", debugGlobal)
response, err := client.DataWells(dehashed.DataWellsRequest{
Count: dataWellsCount,
Page: dataWellsPage,
Sort: dataWellsSort,
})
if err != nil {
fmt.Printf("[!] Error querying data wells: %v\n", err)
return
}
fType := files.GetFileType(dataWellsOutputFormat)
if dataWellsOutputFile != "" {
fmt.Printf("[*] Writing data wells to file: %s%s\n", dataWellsOutputFile, fType.Extension())
if err := dehashed.WriteDataWellsToFile(response, dataWellsOutputFile, fType); err != nil {
fmt.Printf("[!] Error writing data wells to file: %v\n", err)
return
}
}
fmt.Printf("[+] Retrieved %d data wells (total: %d, next page: %t)\n", len(response.DataWells), response.Total, response.NextPage)
printDataWellsTable(response.DataWells)
},
}
)
// Helper functions to get stored API credentials
func getDehashedApiKey() string {
return badger.GetDehashedKey()
}
func printDataWellsTable(dataWells []dehashed.DataWell) {
headers := []string{"Name", "Date", "Records", "Sensitive", "Data"}
rows := make([][]string, 0, len(dataWells))
for _, well := range dataWells {
rows = append(rows, []string{
well.Name,
well.Date,
fmt.Sprintf("%d", well.Records),
fmt.Sprintf("%t", well.IsSensitive),
well.Data,
})
}
pretty.Table(headers, rows)
}
+1 -1
View File
@@ -25,7 +25,7 @@ func init() {
queryCmd.Flags().StringVarP(&dbQueryUserQuery, "user-query", "q", "", "User query to execute")
queryCmd.Flags().StringVarP(&dbQueryRawQuery, "raw-query", "r", "", "Raw SQL query to execute")
queryCmd.Flags().BoolVarP(&dbQueryListAll, "list-all", "a", false, "List all tables and their columns")
queryCmd.Flags().StringVarP(&dbQueryFormat, "format", "f", "json", "Output format (json, yaml, xml, txt)")
queryCmd.Flags().StringVarP(&dbQueryFormat, "format", "f", "json", "Output format (json, yaml, xml, txt, grep)")
queryCmd.Flags().StringVarP(&dbQueryFile, "file", "o", "query", "File to output results to")
// Add mutually exclusive flags to query and raw-query
+12 -4
View File
@@ -53,15 +53,23 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&debugGlobal, "debug", false, "Show debug information")
// Add subcommands
rootCmd.AddCommand(setDehashedKeyCmd)
rootCmd.AddCommand(setHunterKeyCmd)
rootCmd.AddCommand(setCmd)
rootCmd.AddCommand(setLocalDb)
rootCmd.AddCommand(buyMeCoffeeCmd)
setCmd.AddCommand(setDehashedKeyCmd)
setCmd.AddCommand(setHunterKeyCmd)
}
var setCmd = &cobra.Command{
Use: "set",
Short: "Set CrowsNest configuration values",
Long: "Set CrowsNest configuration values such as API keys.",
}
// Command to set API key
var setDehashedKeyCmd = &cobra.Command{
Use: "set-dehashed [key]",
Use: "dehashed [key]",
Short: "Set and store Dehashed.com API key",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
@@ -77,7 +85,7 @@ var setDehashedKeyCmd = &cobra.Command{
}
var setHunterKeyCmd = &cobra.Command{
Use: "set-hunter [key]",
Use: "hunter [key]",
Short: "Set and store Hunter.io API key",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
+1 -1
View File
@@ -59,7 +59,7 @@ var (
// Validate credentials
if key == "" {
fmt.Println("API key is required. Set the key with the \"set-key\" command. [crowsnest set-key <api_key>]")
fmt.Println("API key is required. Set the key with the \"set dehashed\" command. [crowsnest set dehashed <api_key>]")
return
}