first commit

This commit is contained in:
Ar1ste1a
2025-05-14 22:00:38 -04:00
commit a4dffe61bf
27 changed files with 4742 additions and 0 deletions
+656
View File
@@ -0,0 +1,656 @@
package cmd
import (
"dehasher/internal/export"
"dehasher/internal/files"
"dehasher/internal/pretty"
"dehasher/internal/sqlite"
"encoding/json"
"fmt"
"github.com/spf13/cobra"
"go.uber.org/zap"
"strings"
"time"
)
var (
// DB command flags
dbPath string
// DB query command flags
usernameDBQuery string
emailDBQuery string
ipDBQuery string
passwordDBQuery string
hashDBQuery string
nameDBQuery string
vinDBQuery string
licensePlateDBQuery string
addressDBQuery string
phoneDBQuery string
socialDBQuery string
cryptoCurrencyAddressDBQuery string
domainDBQuery string
limitResultsDB int
exactMatchDBQuery bool
outputFormatDB string
nonEmptyFieldsDBQuery string
displayFieldsDBQuery string
tableTypeDBQuery string
// DB runs command flags
startDateDBRuns string
endDateDBRuns string
containsQueryDBRuns string
lastXRunsDBRuns int
// DB command
dbCmd = &cobra.Command{
Use: "db",
Short: "Database operations for Dehasher",
Long: `Perform database operations like export, import, and query on the local Dehasher database.`,
}
)
func init() {
// Add subcommands to db command
dbCmd.AddCommand(dbExportCmd)
dbCmd.AddCommand(dbQueryCmd)
dbCmd.AddCommand(dbRunsCmd)
dbCmd.AddCommand(dbCredsCmd)
// Add flags specific to db command
dbCmd.PersistentFlags().StringVarP(&dbPath, "db-path", "D", "", "Path to database (default: ~/.local/share/Dehasher/dehashed.db)")
// Add flags specific to db query command
dbQueryCmd.Flags().StringVarP(&usernameDBQuery, "username", "u", "", "Filter by username")
dbQueryCmd.Flags().StringVarP(&emailDBQuery, "email", "e", "", "Filter by email")
dbQueryCmd.Flags().StringVarP(&ipDBQuery, "ip", "i", "", "Filter by IP address")
dbQueryCmd.Flags().StringVarP(&passwordDBQuery, "password", "p", "", "Filter by password")
dbQueryCmd.Flags().StringVarP(&hashDBQuery, "hash", "H", "", "Filter by hashed password")
dbQueryCmd.Flags().StringVarP(&nameDBQuery, "name", "n", "", "Filter by name")
dbQueryCmd.Flags().StringVarP(&vinDBQuery, "vin", "v", "", "Filter by VIN")
dbQueryCmd.Flags().StringVarP(&licensePlateDBQuery, "license", "L", "", "Filter by license plate")
dbQueryCmd.Flags().StringVarP(&addressDBQuery, "address", "a", "", "Filter by address")
dbQueryCmd.Flags().StringVarP(&phoneDBQuery, "phone", "P", "", "Filter by phone number")
dbQueryCmd.Flags().StringVarP(&socialDBQuery, "social", "s", "", "Filter by social media handle")
dbQueryCmd.Flags().StringVarP(&cryptoCurrencyAddressDBQuery, "crypto", "c", "", "Filter by cryptocurrency address")
dbQueryCmd.Flags().StringVarP(&domainDBQuery, "domain", "d", "", "Filter by domain/URL")
dbQueryCmd.Flags().IntVarP(&limitResultsDB, "limit", "l", 100, "Limit number of results")
dbQueryCmd.Flags().BoolVarP(&exactMatchDBQuery, "exact", "x", false, "Use exact matching instead of partial matching")
dbQueryCmd.Flags().StringVarP(&outputFormatDB, "format", "f", "table", "Output format (json, table, simple)")
dbQueryCmd.Flags().StringVar(&nonEmptyFieldsDBQuery, "non-empty", "", "Filter for non-empty fields (comma-separated list, e.g., 'password,email')")
dbQueryCmd.Flags().StringVar(&displayFieldsDBQuery, "display", "", "Fields to display in output (comma-separated list, e.g., 'username,email,password')")
dbQueryCmd.Flags().StringVarP(&tableTypeDBQuery, "table", "t", "results", "Table to query (results, runs, creds)")
// Add flags specific to db runs command
dbRunsCmd.Flags().StringVarP(&startDateDBRuns, "start-date", "s", "", "Start date for filtering runs (format: YYYY-MM-DD)")
dbRunsCmd.Flags().StringVarP(&endDateDBRuns, "end-date", "e", "", "End date for filtering runs (format: YYYY-MM-DD)")
dbRunsCmd.Flags().StringVarP(&containsQueryDBRuns, "contains", "c", "", "Filter runs containing this query string")
dbRunsCmd.Flags().IntVarP(&lastXRunsDBRuns, "last", "x", 0, "Show the last X runs")
dbRunsCmd.Flags().IntVarP(&limitResultsDB, "limit", "l", 100, "Limit number of results")
dbRunsCmd.Flags().StringVarP(&outputFormatDB, "format", "f", "table", "Output format (json, table, simple)")
// Add flags specific to db creds command
dbCredsCmd.Flags().StringVarP(&usernameDBQuery, "username", "u", "", "Filter by username")
dbCredsCmd.Flags().StringVarP(&emailDBQuery, "email", "e", "", "Filter by email")
dbCredsCmd.Flags().StringVarP(&passwordDBQuery, "password", "p", "", "Filter by password")
dbCredsCmd.Flags().IntVarP(&limitResultsDB, "limit", "l", 100, "Limit number of results")
dbCredsCmd.Flags().BoolVarP(&exactMatchDBQuery, "exact", "x", false, "Use exact matching instead of partial matching")
dbCredsCmd.Flags().StringVarP(&outputFormatDB, "format", "f", "table", "Output format (json, table, simple)")
dbCredsCmd.Flags().StringVar(&nonEmptyFieldsDBQuery, "non-empty", "", "Filter for non-empty fields (comma-separated list, e.g., 'password,email')")
dbCredsCmd.Flags().StringVar(&displayFieldsDBQuery, "display", "", "Fields to display in output (comma-separated list, e.g., 'username,email,password')")
}
// DB export command
var dbExportCmd = &cobra.Command{
Use: "export",
Short: "Export database to file",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Exporting database...")
// Create DBOptions with the provided parameters
options := &sqlite.DBOptions{
Username: usernameDBQuery,
Email: emailDBQuery,
IPAddress: ipDBQuery,
Password: passwordDBQuery,
HashedPassword: hashDBQuery,
Name: nameDBQuery,
Limit: limitResultsDB,
ExactMatch: exactMatchDBQuery,
}
// Parse non-empty fields if provided
if nonEmptyFieldsDBQuery != "" {
options.NonEmptyFields = strings.Split(nonEmptyFieldsDBQuery, ",")
}
// Parse display fields if provided
if displayFieldsDBQuery != "" {
options.DisplayFields = strings.Split(displayFieldsDBQuery, ",")
}
// Check if at least one search parameter is provided
if options.Username == "" && options.Email == "" && options.IPAddress == "" &&
options.Password == "" && options.HashedPassword == "" && options.Name == "" &&
len(options.NonEmptyFields) == 0 {
fmt.Println("Error: At least one search parameter is required.")
cmd.Help()
return
}
// Get the count of matching results
count, err := sqlite.GetResultsCount(options)
if err != nil {
fmt.Printf("Error counting results: %v\n", err)
return
}
// Query the database
results, err := sqlite.QueryResults(options)
if err != nil {
fmt.Printf("Error querying database: %v\n", err)
return
}
dhResults := sqlite.DehashedResults{Results: results}
fmt.Printf("Found %d results (showing %d):\n", count, len(results))
// Output results based on format
ft := files.GetFileType(outputFormatDB)
err = export.WriteToFile(dhResults, "dehasher_export", ft)
if err != nil {
zap.L().Error("write_to_file",
zap.String("message", "failed to write to file"),
zap.Error(err),
)
fmt.Printf("Error writing to file: %v\n", err)
return
}
fmt.Printf("Exported successfully to file: dehasher_export%s\n", ft.Extension())
},
}
// DB query command
var dbQueryCmd = &cobra.Command{
Use: "query",
Short: "Query local database",
Long: `Query the local database for previously run dehasher queries based on various parameters.`,
Run: func(cmd *cobra.Command, args []string) {
// Determine which table to query based on the tableTypeDBQuery parameter
switch tableTypeDBQuery {
case "results":
queryResultsTable(cmd)
case "runs":
queryRunsTable()
case "creds":
queryCredsTable(cmd)
default:
fmt.Printf("Error: Unknown table type '%s'. Valid options are: results, runs, creds\n", tableTypeDBQuery)
cmd.Help()
return
}
},
}
func queryRunsTable() {
// Parse date strings to time.Time
var startDate, endDate time.Time
var err error
if startDateDBRuns != "" {
startDate, err = time.Parse("2006-01-02", startDateDBRuns)
if err != nil {
fmt.Printf("Error parsing start date: %v\n", err)
return
}
}
if endDateDBRuns != "" {
endDate, err = time.Parse("2006-01-02", endDateDBRuns)
if err != nil {
fmt.Printf("Error parsing end date: %v\n", err)
return
}
// Set end date to end of day
endDate = endDate.Add(24*time.Hour - time.Second)
}
// Get the count of matching runs
count, err := sqlite.GetRunsCount(lastXRunsDBRuns, startDate, endDate, containsQueryDBRuns)
if err != nil {
fmt.Printf("Error counting runs: %v\n", err)
return
}
// Query the database
runs, err := sqlite.QueryRuns(limitResultsDB, lastXRunsDBRuns, startDate, endDate, containsQueryDBRuns)
if err != nil {
fmt.Printf("Error querying runs: %v\n", err)
return
}
displayRunsResults(count, runs)
}
func displayRunsResults(count int64, runs []sqlite.QueryOptions) {
// Display the results
fmt.Printf("Found %d runs (showing %d):\n", count, len(runs))
if len(runs) == 0 {
fmt.Println("No runs found.")
return
}
// Output results based on format
switch outputFormatDB {
case "json":
data, err := json.MarshalIndent(runs, "", " ")
if err != nil {
fmt.Printf("Error formatting results: %v\n", err)
return
}
fmt.Println(string(data))
case "table":
// Define headers and rows for the table
headers := []string{"ID", "Created At", "Max Records", "Username Query", "Email Query", "IP Query", "Password Query", "Hash Query", "Name Query", "Domain Query"}
rows := make([][]string, len(runs))
for i, run := range runs {
rows[i] = []string{
fmt.Sprintf("%d", run.ID),
run.CreatedAt.Format("2006-01-02 15:04:05"),
fmt.Sprintf("%d", run.MaxRecords),
truncate(run.UsernameQuery, 20),
truncate(run.EmailQuery, 20),
truncate(run.IpQuery, 20),
truncate(run.PassQuery, 20),
truncate(run.HashQuery, 20),
truncate(run.NameQuery, 20),
truncate(run.DomainQuery, 20),
}
}
pretty.Table(headers, rows)
default:
// Simple output
for _, run := range runs {
fmt.Printf("Run ID: %d\n", run.ID)
fmt.Printf(" Created At: %s\n", run.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Printf(" Max Records: %d\n", run.MaxRecords)
fmt.Printf(" Max Requests: %d\n", run.MaxRequests)
fmt.Printf(" Starting Page: %d\n", run.StartingPage)
fmt.Printf(" Output Format: %s\n", run.OutputFormat.String())
fmt.Printf(" Output File: %s\n", run.OutputFile)
fmt.Printf(" Regex Match: %t\n", run.RegexMatch)
fmt.Printf(" Wildcard Match: %t\n", run.WildcardMatch)
fmt.Printf(" Username Query: %s\n", run.UsernameQuery)
fmt.Printf(" Email Query: %s\n", run.EmailQuery)
fmt.Printf(" IP Query: %s\n", run.IpQuery)
fmt.Printf(" Password Query: %s\n", run.PassQuery)
fmt.Printf(" Hash Query: %s\n", run.HashQuery)
fmt.Printf(" Name Query: %s\n", run.NameQuery)
fmt.Printf(" Domain Query: %s\n", run.DomainQuery)
fmt.Printf(" VIN Query: %s\n", run.VinQuery)
fmt.Printf(" License Plate Query: %s\n", run.LicensePlateQuery)
fmt.Printf(" Address Query: %s\n", run.AddressQuery)
fmt.Printf(" Phone Query: %s\n", run.PhoneQuery)
fmt.Printf(" Social Query: %s\n", run.SocialQuery)
fmt.Printf(" Crypto Address Query: %s\n", run.CryptoAddressQuery)
fmt.Printf(" Print Balance: %t\n", run.PrintBalance)
fmt.Printf(" Creds Only: %t\n", run.CredsOnly)
fmt.Println()
}
}
}
// queryResultsTable queries the results table
func queryResultsTable(cmd *cobra.Command) {
// Create DBOptions with the provided parameters
options := &sqlite.DBOptions{
Username: usernameDBQuery,
Email: emailDBQuery,
IPAddress: ipDBQuery,
Password: passwordDBQuery,
HashedPassword: hashDBQuery,
Name: nameDBQuery,
Vin: vinDBQuery,
LicensePlate: licensePlateDBQuery,
Address: addressDBQuery,
Phone: phoneDBQuery,
Social: socialDBQuery,
CryptoCurrencyAddress: cryptoCurrencyAddressDBQuery,
Domain: domainDBQuery,
Limit: limitResultsDB,
ExactMatch: exactMatchDBQuery,
}
// Parse non-empty fields if provided
if nonEmptyFieldsDBQuery != "" {
options.NonEmptyFields = strings.Split(nonEmptyFieldsDBQuery, ",")
}
// Parse display fields if provided
if displayFieldsDBQuery != "" {
options.DisplayFields = strings.Split(displayFieldsDBQuery, ",")
}
// Check if at least one search parameter is provided
if options.Empty() {
fmt.Println("Error: At least one search parameter is required.")
cmd.Help()
return
}
// Get the count of matching results
count, err := sqlite.GetResultsCount(options)
if err != nil {
fmt.Printf("Error counting results: %v\n", err)
return
}
// Query the database
results, err := sqlite.QueryResults(options)
if err != nil {
fmt.Printf("Error querying database: %v\n", err)
return
}
// Display the results
displayResultsTable(count, results, options)
}
// displayResultsTable displays the results from the results table
func displayResultsTable(count int64, results []sqlite.Result, options *sqlite.DBOptions) {
// Display the results
fmt.Printf("Found %d results (showing %d):\n", count, len(results))
if len(results) == 0 {
fmt.Println("No results found.")
return
}
// Output results based on format
switch outputFormatDB {
case "json":
data, err := json.MarshalIndent(results, "", " ")
if err != nil {
fmt.Printf("Error formatting results: %v\n", err)
return
}
fmt.Println(string(data))
case "table":
// Determine which fields to display
type FieldInfo struct {
Name string
Width int
Getter func(result sqlite.Result) string
}
// Define all available fields
allFields := []FieldInfo{
{"Username", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Username), 20) }},
{"Email", 30, func(r sqlite.Result) string { return truncate(arrayToString(r.Email), 30) }},
{"IP Address", 15, func(r sqlite.Result) string { return truncate(arrayToString(r.IpAddress), 15) }},
{"Password", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Password), 20) }},
{"Hashed Password", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.HashedPassword), 20) }},
{"Name", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Name), 20) }},
{"VIN", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Vin), 20) }},
{"License Plate", 15, func(r sqlite.Result) string { return truncate(arrayToString(r.LicensePlate), 15) }},
{"Address", 30, func(r sqlite.Result) string { return truncate(arrayToString(r.Address), 30) }},
{"Phone", 15, func(r sqlite.Result) string { return truncate(arrayToString(r.Phone), 15) }},
{"Social", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.Social), 20) }},
{"Crypto Address", 20, func(r sqlite.Result) string { return truncate(arrayToString(r.CryptoCurrencyAddress), 20) }},
{"Domain/URL", 30, func(r sqlite.Result) string { return truncate(arrayToString(r.Url), 30) }},
}
// Select fields to display
var fieldsToDisplay []FieldInfo
var headers []string
if len(options.DisplayFields) > 0 {
// Use specified fields
for _, fieldName := range options.DisplayFields {
fieldName = strings.ToLower(strings.TrimSpace(fieldName))
for _, field := range allFields {
if strings.ToLower(field.Name) == fieldName ||
(fieldName == "ip" && strings.ToLower(field.Name) == "ip address") ||
(fieldName == "hash" && strings.ToLower(field.Name) == "hashed password") ||
(fieldName == "license" && strings.ToLower(field.Name) == "license plate") ||
(fieldName == "crypto" && strings.ToLower(field.Name) == "crypto address") ||
(fieldName == "url" && strings.ToLower(field.Name) == "domain/url") {
fieldsToDisplay = append(fieldsToDisplay, field)
headers = append(headers, field.Name)
break
}
}
}
} else {
// Default fields (first 6)
fieldsToDisplay = allFields[:6]
}
var rows [][]string
for _, result := range results {
rowValues := []string{}
for _, field := range fieldsToDisplay {
rowValues = append(rowValues, field.Getter(result))
}
rows = append(rows, rowValues)
}
pretty.Table(headers, rows)
default:
// Simple output
for i, result := range results {
fmt.Printf("Result %d:\n", i+1)
// Determine which fields to display
if len(options.DisplayFields) > 0 {
// Display only specified fields
for _, field := range options.DisplayFields {
field = strings.ToLower(strings.TrimSpace(field))
switch field {
case "username":
fmt.Printf(" Username: %s\n", result.Username)
case "email":
fmt.Printf(" Email: %s\n", result.Email)
case "ip", "ipaddress", "ip_address":
fmt.Printf(" IP Address: %s\n", result.IpAddress)
case "password":
fmt.Printf(" Password: %s\n", result.Password)
case "hash", "hashed_password":
fmt.Printf(" Hashed Password: %s\n", result.HashedPassword)
case "name":
fmt.Printf(" Name: %s\n", result.Name)
case "vin":
fmt.Printf(" VIN: %s\n", result.Vin)
case "license", "license_plate":
fmt.Printf(" License Plate: %s\n", result.LicensePlate)
case "address":
fmt.Printf(" Address: %s\n", result.Address)
case "phone":
fmt.Printf(" Phone: %s\n", result.Phone)
case "social":
fmt.Printf(" Social: %s\n", result.Social)
case "crypto", "cryptocurrency_address":
fmt.Printf(" Crypto Address: %s\n", result.CryptoCurrencyAddress)
case "domain", "url":
fmt.Printf(" Domain/URL: %s\n", result.Url)
}
}
} else {
// Display default fields
fmt.Printf(" Username: %s\n", result.Username)
fmt.Printf(" Email: %s\n", result.Email)
fmt.Printf(" IP Address: %s\n", result.IpAddress)
fmt.Printf(" Password: %s\n", result.Password)
fmt.Printf(" Hashed Password: %s\n", result.HashedPassword)
fmt.Printf(" Name: %s\n", result.Name)
}
fmt.Println()
}
}
}
// truncate truncates a string to the specified length and adds ellipsis if needed
func truncate(s string, length int) string {
if len(s) <= length {
return s
}
return s[:length-3] + "..."
}
func arrayToString(a []string) string {
return strings.Join(a, ", ")
}
// DB runs command
var dbRunsCmd = &cobra.Command{
Use: "runs",
Short: "Query previous query runs",
Long: `Query the database for previous query runs (QueryOptions) based on date range and query content.`,
Run: func(cmd *cobra.Command, args []string) {
// Call queryRunsTable directly
queryRunsTable()
},
}
// DB creds command
var dbCredsCmd = &cobra.Command{
Use: "creds",
Short: "Query credentials",
Long: `Query the database for credentials based on username, email, and password.`,
Run: func(cmd *cobra.Command, args []string) {
// Call queryCredsTable directly
queryCredsTable(cmd)
},
}
// queryCredsTable queries the credentials table
func queryCredsTable(cmd *cobra.Command) {
// Create DBOptions with the provided parameters
options := &sqlite.DBOptions{
Username: usernameDBQuery,
Email: emailDBQuery,
Password: passwordDBQuery,
Limit: limitResultsDB,
ExactMatch: exactMatchDBQuery,
}
// Parse non-empty fields if provided
if nonEmptyFieldsDBQuery != "" {
options.NonEmptyFields = strings.Split(nonEmptyFieldsDBQuery, ",")
}
// Parse display fields if provided
if displayFieldsDBQuery != "" {
options.DisplayFields = strings.Split(displayFieldsDBQuery, ",")
}
// Check if at least one search parameter is provided
if options.Username == "" && options.Email == "" && options.Password == "" && len(options.NonEmptyFields) == 0 {
fmt.Println("Error: At least one search parameter is required.")
cmd.Help()
return
}
// Get the count of matching credentials
count, err := sqlite.GetCredsCount(options)
if err != nil {
fmt.Printf("Error counting credentials: %v\n", err)
return
}
// Query the database
creds, err := sqlite.QueryCreds(options)
if err != nil {
fmt.Printf("Error querying credentials: %v\n", err)
return
}
// Display the results
displayCredsResults(count, creds)
}
// displayCredsResults displays the results from the creds table
func displayCredsResults(count int64, creds []sqlite.Creds) {
// Display the results
fmt.Printf("Found %d credentials (showing %d):\n", count, len(creds))
if len(creds) == 0 {
fmt.Println("No credentials found.")
return
}
// Output results based on format
switch outputFormatDB {
case "json":
data, err := json.MarshalIndent(creds, "", " ")
if err != nil {
fmt.Printf("Error formatting results: %v\n", err)
return
}
fmt.Println(string(data))
case "table":
// Define all available fields
type FieldInfo struct {
Name string
Getter func(cred sqlite.Creds) string
}
allFields := []FieldInfo{
{"ID", func(c sqlite.Creds) string { return fmt.Sprintf("%d", c.ID) }},
{"Created At", func(c sqlite.Creds) string { return c.CreatedAt.Format("2006-01-02 15:04:05") }},
{"Email", func(c sqlite.Creds) string { return c.Email }},
{"Username", func(c sqlite.Creds) string { return c.Username }},
{"Password", func(c sqlite.Creds) string { return c.Password }},
}
// Select fields to display
var fieldsToDisplay []FieldInfo
var headers []string
if len(displayFieldsDBQuery) > 0 {
// Use specified display fields
displayFields := strings.Split(displayFieldsDBQuery, ",")
for _, fieldName := range displayFields {
fieldName = strings.ToLower(strings.TrimSpace(fieldName))
for _, field := range allFields {
if strings.ToLower(field.Name) == fieldName {
fieldsToDisplay = append(fieldsToDisplay, field)
headers = append(headers, field.Name)
break
}
}
}
} else {
// Default fields
fieldsToDisplay = allFields
for _, field := range fieldsToDisplay {
headers = append(headers, field.Name)
}
}
// Create rows
rows := make([][]string, len(creds))
for i, cred := range creds {
rowValues := []string{}
for _, field := range fieldsToDisplay {
rowValues = append(rowValues, field.Getter(cred))
}
rows[i] = rowValues
}
pretty.Table(headers, rows)
default:
// Simple output
for _, cred := range creds {
fmt.Printf("Credential ID: %d\n", cred.ID)
fmt.Printf(" Created At: %s\n", cred.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Printf(" Email: %s\n", cred.Email)
fmt.Printf(" Username: %s\n", cred.Username)
fmt.Printf(" Password: %s\n", cred.Password)
fmt.Println()
}
}
}
+137
View File
@@ -0,0 +1,137 @@
package cmd
import (
"dehasher/internal/badger"
"dehasher/internal/query"
"dehasher/internal/sqlite"
"fmt"
"github.com/spf13/cobra"
)
var (
// Query command flags
maxRecords int
maxRequests int
startingPage int
credsOnly bool
printBalance bool
regexMatch bool
wildcardMatch bool
outputFormat string
outputFile string
usernameQuery string
emailQuery string
ipQuery string
passwordQuery string
hashQuery string
nameQuery string
domainQuery string
vinQuery string
licensePlateQuery string
addressQuery string
phoneQuery string
socialQuery string
cryptoCurrencyAddressQuery string
// Query command
queryCmd = &cobra.Command{
Use: "query",
Short: "Query the Dehashed API",
Long: `Query the Dehashed API for emails, usernames, passwords, hashes, IP addresses, and names.`,
Run: func(cmd *cobra.Command, args []string) {
// Check if API key and email are provided
key := apiKey
email := apiEmail
// If not provided as flags, try to get from stored values
if key == "" {
key = getStoredApiKey()
}
if email == "" {
email = getStoredApiEmail()
}
// Validate credentials
if key == "" || email == "" {
fmt.Println("API key and email are required. Use --key and --email flags or set them with set-key and set-email commands.")
return
}
// Create new QueryOptions
queryOptions := sqlite.NewQueryOptions(
maxRecords,
maxRequests,
startingPage,
outputFormat,
outputFile,
usernameQuery,
emailQuery,
ipQuery,
passwordQuery,
hashQuery,
nameQuery,
domainQuery,
vinQuery,
licensePlateQuery,
addressQuery,
phoneQuery,
socialQuery,
cryptoCurrencyAddressQuery,
regexMatch,
wildcardMatch,
printBalance,
credsOnly,
)
// Create new Dehasher
dehasher := query.NewDehasher(queryOptions)
dehasher.SetClientCredentials(
key,
)
// Start querying
dehasher.Start()
fmt.Println("\n[*] Completing Process")
sqlite.StoreQueryOptions(queryOptions)
},
}
)
func init() {
// Add flags specific to query command
queryCmd.Flags().IntVarP(&maxRecords, "max-records", "m", 30000, "Maximum amount of records to return")
queryCmd.Flags().IntVarP(&maxRequests, "max-requests", "r", -1, "Maximum number of requests to make")
queryCmd.Flags().IntVarP(&startingPage, "starting-page", "s", 1, "Starting page for requests")
queryCmd.Flags().BoolVarP(&printBalance, "print-balance", "b", false, "Print remaining balance after requests")
queryCmd.Flags().BoolVarP(&regexMatch, "regex-match", "R", false, "Use regex matching on query fields")
queryCmd.Flags().BoolVarP(&wildcardMatch, "wildcard-match", "W", false, "Use wildcard matching on query fields (Use ? to replace a single character, and * for multiple characters)")
queryCmd.Flags().BoolVarP(&credsOnly, "creds-only", "C", false, "Return credentials only")
queryCmd.Flags().StringVarP(&outputFormat, "format", "f", "json", "Output format (json, yaml, xml, txt)")
queryCmd.Flags().StringVarP(&outputFile, "output", "o", "query", "File to output results to including extension")
queryCmd.Flags().StringVarP(&usernameQuery, "username", "U", "", "Username query")
queryCmd.Flags().StringVarP(&emailQuery, "email-query", "E", "", "Email query")
queryCmd.Flags().StringVarP(&ipQuery, "ip", "I", "", "IP address query")
queryCmd.Flags().StringVarP(&domainQuery, "domain", "D", "", "Domain query")
queryCmd.Flags().StringVarP(&passwordQuery, "password", "P", "", "Password query")
queryCmd.Flags().StringVarP(&vinQuery, "vin", "V", "", "VIN query")
queryCmd.Flags().StringVarP(&licensePlateQuery, "license", "L", "", "License plate query")
queryCmd.Flags().StringVarP(&addressQuery, "address", "A", "", "Address query")
queryCmd.Flags().StringVarP(&phoneQuery, "phone", "M", "", "Phone query")
queryCmd.Flags().StringVarP(&socialQuery, "social", "S", "", "Social query")
queryCmd.Flags().StringVarP(&cryptoCurrencyAddressQuery, "crypto", "B", "", "Crypto currency address query")
queryCmd.Flags().StringVarP(&hashQuery, "hash", "Q", "", "Hashed password query")
queryCmd.Flags().StringVarP(&nameQuery, "name", "N", "", "Name query")
// Add mutually exclusive flags to exact match and regex match
queryCmd.MarkFlagsMutuallyExclusive("regex-match", "wildcard-match")
}
// Helper functions to get stored API credentials
func getStoredApiKey() string {
return badger.GetKey()
}
func getStoredApiEmail() string {
return badger.GetEmail()
}
+126
View File
@@ -0,0 +1,126 @@
package cmd
import (
"dehasher/internal/badger"
"fmt"
"github.com/spf13/cobra"
"go.uber.org/zap"
"os"
)
var (
// Global Flags
apiKey string
apiEmail string
// rootCmd is the base command for the CLI.
rootCmd = &cobra.Command{
Use: "dehasher",
Short: `Dehasher is a cli tool for querying query.`,
Long: fmt.Sprintf(
"%s\n%s",
`
______ _______ _______ _______ _______ _______
( __ \ ( ____ \|\ /|( ___ )( ____ \|\ /|( ____ \( ____ )
| ( \ )| ( \/| ) ( || ( ) || ( \/| ) ( || ( \/| ( )|
| | ) || (__ | (___) || (___) || (_____ | (___) || (__ | (____)|
| | | || __) | ___ || ___ |(_____ )| ___ || __) | __)
| | ) || ( | ( ) || ( ) | ) || ( ) || ( | (\ (
| (__/ )| (____/\| ) ( || ) ( |/\____) || ) ( || (____/\| ) \ \__
(______/ (_______/|/ \||/ \|\_______)|/ \|(_______/|/ \__/
An Ar1ste1a Project
`,
`––•–√\/––√\/––•––––•–√\/––√\/––•––––•–√\/––√\/––•––√\/––•––––•–√\/––√\/––•––
Dehasher can query the query API for:
- Emails - Usernames - Password
- Hashes - IP Addresses - Names
- VINs - License Plates - Addresses
- Phones - Social Media - Crypto Currency Addresses
Dehasher supports:
- Regex Matching
- Exact Matching
––•–√\/––√\/––•––––•–√\/––√\/––•––––•–√\/––√\/––•––√\/––•––––•–√\/––√\/––•––
`,
),
Version: "v1.0",
}
)
// Execute adds all child commands to the root command and sets flags appropriately.
func Execute() {
if err := rootCmd.Execute(); err != nil {
zap.L().Fatal("execute_root_command",
zap.String("message", "failed to execute root command"),
zap.Error(err),
)
fmt.Printf("[!] %v", err)
os.Exit(1)
}
}
func init() {
// Hide the default help command
rootCmd.CompletionOptions.HiddenDefaultCmd = true
// Add global flags for API key and email
rootCmd.PersistentFlags().StringVarP(&apiKey, "key", "k", "", "API Key for authentication")
// Add subcommands
rootCmd.AddCommand(dbCmd)
rootCmd.AddCommand(queryCmd)
rootCmd.AddCommand(setKeyCmd)
rootCmd.AddCommand(setEmailCmd)
}
// Command to set API key
var setKeyCmd = &cobra.Command{
Use: "set-key [key]",
Short: "Set and store API key",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
key := args[0]
// Store key in badger DB
err := storeApiKey(key)
if err != nil {
fmt.Printf("Error storing API key: %v\n", err)
return
}
fmt.Println("API key stored successfully")
},
}
// Command to set API email
var setEmailCmd = &cobra.Command{
Use: "set-email [email]",
Short: "Set and store API email",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
email := args[0]
// Store email in badger DB
err := storeApiEmail(email)
if err != nil {
fmt.Printf("Error storing API email: %v\n", err)
return
}
fmt.Println("API email stored successfully")
},
}
// Helper functions to store API credentials
func storeApiKey(key string) error {
err := badger.StoreKey(key)
if err != nil {
fmt.Printf("Error storing API key: %v\n", err)
return err
}
return nil
}
func storeApiEmail(email string) error {
err := badger.StoreEmail(email)
if err != nil {
fmt.Printf("Error storing API email: %v\n", err)
return err
}
return nil
}
+303
View File
@@ -0,0 +1,303 @@
package cmd
import (
"dehasher/internal/sqlite"
"dehasher/internal/whois"
"fmt"
"github.com/spf13/cobra"
"go.uber.org/zap"
"strings"
)
var (
// WHOIS command flags
whoisDomain string
whoisIPAddress string
whoisMXAddress string
whoisNSAddress string
whoisInclude string
whoisExclude string
whoisReverseType string
whoisOutputFormat string
whoisShowCredits bool
whoisHistory bool
whoisSubdomainScan bool
// WHOIS command
whoisCmd = &cobra.Command{
Use: "whois",
Short: "Dehashed WHOIS lookups and reverse WHOIS searches",
Long: `Perform WHOIS lookups, history searches, reverse WHOIS searches, IP lookups, MX lookups, NS lookups, and subdomain scans.`,
Run: func(cmd *cobra.Command, args []string) {
// Check if API key is provided
key := apiKey
// If not provided as flag, try to get from stored value
if key == "" {
key = getStoredApiKey()
}
// Validate credentials
if key == "" {
fmt.Println("API key is required. Use --key flag or set it with set-key command.")
return
}
// Show credits if requested
if whoisShowCredits {
fmt.Println("[*] Getting WHOIS credits...")
credits, err := whois.GetWHOISCredits(key)
if err != nil {
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois credits"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS credits: %v\n", err)
return
}
fmt.Printf("WHOIS Credits: %d\n", credits.WhoisCredits)
return
}
// Check if domain is provided for history and subdomain scan
if whoisHistory || whoisSubdomainScan {
if whoisDomain == "" {
fmt.Println("Domain is required for history and subdomain scan.")
return
}
}
// Determine which operation to perform based on flags
if whoisDomain != "" {
fmt.Println("[*] Performing WHOIS lookup...")
// Domain lookup
result, err := whois.WhoisSearch(whoisDomain, key)
if err != nil {
zap.L().Error("whois_search",
zap.String("message", "failed to perform whois search"),
zap.Error(err),
)
fmt.Printf("Error performing WHOIS lookup: %v\n", err)
return
}
// Fix the output format to use proper formatting
fmt.Printf("WHOIS Lookup Result:\n%+v\n", result.Data.WhoisRecord)
// Store the record
err = sqlite.StoreWhoisRecord(result.Data.WhoisRecord)
if err != nil {
zap.L().Error("store_whois_record",
zap.String("message", "failed to store whois record"),
zap.Error(err),
)
fmt.Printf("Error storing WHOIS record: %v\n", err)
// Continue execution even if storage fails
}
if whoisHistory {
fmt.Println("[*] Performing WHOIS history search...")
// Perform history search
history, err := whois.WhoisHistory(whoisDomain, key)
if err != nil {
zap.L().Error("whois_history",
zap.String("message", "failed to perform whois history lookup"),
zap.Error(err),
)
fmt.Printf("Error performing WHOIS history lookup: %v\n", err)
} else {
fmt.Println("\nWHOIS History:")
fmt.Println(history)
}
err = sqlite.StoreHistoryRecord(history.Data.Records)
if err != nil {
zap.L().Error("store_history_record",
zap.String("message", "failed to store history record"),
zap.Error(err),
)
fmt.Printf("Error storing WHOIS history record: %v\n", err)
}
}
// Perform subdomain scan
if whoisSubdomainScan {
fmt.Println("[*] Performing WHOIS subdomain scan...")
subdomains, err := whois.WhoisSubdomainScan(whoisDomain, key)
if err != nil {
zap.L().Error("whois_subdomain_scan",
zap.String("message", "failed to perform subdomain scan"),
zap.Error(err),
)
fmt.Printf("Error performing subdomain scan: %v\n", err)
} else {
fmt.Println("\nSubdomain Scan:")
fmt.Println(subdomains)
}
err = sqlite.StoreSubdomainRecord(subdomains.Data.Result.Records)
if err != nil {
zap.L().Error("store_subdomain_record",
zap.String("message", "failed to store subdomain record"),
zap.Error(err),
)
fmt.Printf("Error storing WHOIS subdomain record: %v\n", err)
}
}
// Get credits
credits, err := whois.GetWHOISCredits(key)
if err != nil {
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois credits"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS credits: %v\n", err)
return
}
fmt.Printf("\nWHOIS Credits Remaining: %d\n", credits.WhoisCredits)
return
}
if whoisIPAddress != "" {
fmt.Println("[*] Performing reverse IP lookup...")
// IP lookup
result, err := whois.WhoisIP(whoisIPAddress, key)
if err != nil {
zap.L().Error("whois_ip",
zap.String("message", "failed to perform ip lookup"),
zap.Error(err),
)
fmt.Printf("Error performing IP lookup: %v\n", err)
return
}
fmt.Println("IP Lookup Result:")
fmt.Println(string(result))
// Get credits
credits, err := whois.GetWHOISCredits(key)
if err != nil {
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois credits"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS credits: %v\n", err)
return
}
fmt.Printf("\nWHOIS Credits Remaining: %d\n", credits.WhoisCredits)
return
}
if whoisMXAddress != "" {
fmt.Println("[*] Performing reverse MX lookup...")
// MX lookup
result, err := whois.WhoisMX(whoisMXAddress, key)
if err != nil {
zap.L().Error("whois_mx",
zap.String("message", "failed to perform mx lookup"),
zap.Error(err),
)
fmt.Printf("Error performing MX lookup: %v\n", err)
return
}
// todo unmarshal mx lookup
fmt.Println("MX Lookup Result:")
fmt.Println(result)
// Get credits
credits, err := whois.GetWHOISCredits(key)
if err != nil {
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois credits"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS credits: %v\n", err)
return
}
fmt.Printf("\nWHOIS Credits Remaining: %d\n", credits.WhoisCredits)
return
}
if whoisNSAddress != "" {
fmt.Println("[*] Performing reverse NS lookup...")
// NS lookup
result, err := whois.WhoisNS(whoisNSAddress, key)
if err != nil {
zap.L().Error("whois_ns",
zap.String("message", "failed to perform ns lookup"),
zap.Error(err),
)
fmt.Printf("Error performing NS lookup: %v\n", err)
return
}
fmt.Println("NS Lookup Result:")
fmt.Println(result)
// Get credits
credits, err := whois.GetWHOISCredits(key)
if err != nil {
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois credits"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS credits: %v\n", err)
return
}
fmt.Printf("\nWHOIS Credits Remaining: %d\n", credits.WhoisCredits)
return
}
if whoisInclude != "" || whoisExclude != "" {
// Reverse WHOIS
includeTerms := []string{}
if whoisInclude != "" {
includeTerms = strings.Split(whoisInclude, ",")
}
excludeTerms := []string{}
if whoisExclude != "" {
excludeTerms = strings.Split(whoisExclude, ",")
}
if whoisReverseType == "" {
whoisReverseType = "registrant"
}
fmt.Println("[*] Performing reverse WHOIS lookup...")
result, err := whois.ReverseWHOIS(includeTerms, excludeTerms, whoisReverseType, key)
if err != nil {
fmt.Printf("Error performing reverse WHOIS: %v\n", err)
return
}
fmt.Println("Reverse WHOIS Result:")
fmt.Println(result)
return
}
// If no specific operation was requested
cmd.Help()
},
}
)
func init() {
// Add whois command to root command
rootCmd.AddCommand(whoisCmd)
// Add flags specific to whois command
whoisCmd.Flags().StringVarP(&whoisDomain, "domain", "d", "", "Domain for WHOIS lookup, history search, and subdomain scan")
whoisCmd.Flags().StringVarP(&whoisIPAddress, "ip", "i", "", "IP address for reverse IP lookup")
whoisCmd.Flags().StringVarP(&whoisMXAddress, "mx", "m", "", "MX address for reverse MX lookup")
whoisCmd.Flags().StringVarP(&whoisNSAddress, "ns", "n", "", "NS address for reverse NS lookup")
whoisCmd.Flags().StringVarP(&whoisInclude, "include", "I", "", "Terms to include in reverse WHOIS search (comma-separated)")
whoisCmd.Flags().StringVarP(&whoisExclude, "exclude", "E", "", "Terms to exclude in reverse WHOIS search (comma-separated)")
whoisCmd.Flags().StringVarP(&whoisReverseType, "type", "t", "registrant", "Type of reverse WHOIS search (registrant, email, organization, address, phone)")
whoisCmd.Flags().StringVarP(&whoisOutputFormat, "format", "f", "text", "Output format (text, json)")
whoisCmd.Flags().BoolVarP(&whoisShowCredits, "credits", "c", false, "Show remaining WHOIS credits")
whoisCmd.Flags().BoolVarP(&whoisHistory, "history", "H", false, "Perform WHOIS history search [25 Credits]")
whoisCmd.Flags().BoolVarP(&whoisSubdomainScan, "subdomains", "s", false, "Perform WHOIS subdomain scan")
// Add API key flag
whoisCmd.Flags().StringVarP(&apiKey, "key", "k", "", "Dehashed API key")
}