summaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/breachforum.go24
-rw-r--r--cmd/bssid.go30
-rw-r--r--cmd/crawl.go16
-rw-r--r--cmd/dns.go77
-rw-r--r--cmd/email.go24
-rw-r--r--cmd/files.go36
-rw-r--r--cmd/moon.go68
-rw-r--r--cmd/root.go2
-rw-r--r--cmd/sl.go13
-rw-r--r--cmd/stdin.go98
-rw-r--r--cmd/telegram.go24
-rw-r--r--cmd/username.go45
12 files changed, 283 insertions, 174 deletions
diff --git a/cmd/breachforum.go b/cmd/breachforum.go
index 63254c1..229292a 100644
--- a/cmd/breachforum.go
+++ b/cmd/breachforum.go
@@ -11,7 +11,7 @@ var breachforumCmd = &cobra.Command{
Aliases: []string{"brf"},
Short: "Search BreachForum data",
Long: `Search breachdetect index for BreachForum messages and detections`,
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runBreachForumSearch,
}
@@ -26,15 +26,17 @@ func runBreachForumSearch(cmd *cobra.Command, args []string) error {
return err
}
- params := &models.BreachForumSearchParams{
- Search: args[0],
- }
- params.MaxHits, _ = cmd.Flags().GetInt("max_hits")
-
- response, err := c.SearchBreachForum(params)
- if err != nil {
- return err
- }
+ maxHits, _ := cmd.Flags().GetInt("max_hits")
- return formatter.FormatBreachForumResults(response, IsJSONOutput())
+ return forEachQuery(args, func(query string) error {
+ params := &models.BreachForumSearchParams{
+ Search: query,
+ MaxHits: maxHits,
+ }
+ response, err := c.SearchBreachForum(params)
+ if err != nil {
+ return err
+ }
+ return formatter.FormatBreachForumResults(response, IsJSONOutput())
+ })
}
diff --git a/cmd/bssid.go b/cmd/bssid.go
index 0cf751f..116c63a 100644
--- a/cmd/bssid.go
+++ b/cmd/bssid.go
@@ -11,7 +11,7 @@ var bssidCmd = &cobra.Command{
Aliases: []string{"bs"},
Short: "Lookup WiFi access point location by BSSID",
Long: `Lookup geographic location of a WiFi access point by its BSSID (MAC address) using Apple's location services`,
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runBSSIDLookup,
}
@@ -28,17 +28,21 @@ func runBSSIDLookup(cmd *cobra.Command, args []string) error {
return err
}
- params := &models.BSSIDParams{
- BSSID: args[0],
- }
- params.All, _ = cmd.Flags().GetBool("all")
- params.Google, _ = cmd.Flags().GetBool("google")
- params.OSM, _ = cmd.Flags().GetBool("osm")
-
- response, err := c.LookupBSSID(params)
- if err != nil {
- return err
- }
+ all, _ := cmd.Flags().GetBool("all")
+ google, _ := cmd.Flags().GetBool("google")
+ osm, _ := cmd.Flags().GetBool("osm")
- return formatter.FormatBSSIDResults(*response, IsJSONOutput())
+ return forEachQuery(args, func(bssid string) error {
+ params := &models.BSSIDParams{
+ BSSID: bssid,
+ All: all,
+ Google: google,
+ OSM: osm,
+ }
+ response, err := c.LookupBSSID(params)
+ if err != nil {
+ return err
+ }
+ return formatter.FormatBSSIDResults(*response, IsJSONOutput())
+ })
}
diff --git a/cmd/crawl.go b/cmd/crawl.go
index 2c9b719..5c5f78f 100644
--- a/cmd/crawl.go
+++ b/cmd/crawl.go
@@ -11,7 +11,7 @@ var crawlCmd = &cobra.Command{
Aliases: []string{"cw"},
Short: "Crawl domain",
Long: `Resolves a domain using httpx and crawls it using katana. Returns discovered links as plain text, one per line, streamed in real-time. Supports both http:// and https:// URLs.`,
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runCrawl,
}
@@ -28,14 +28,10 @@ func runCrawl(cmd *cobra.Command, args []string) error {
return err
}
- err = c.CrawlDomain(args[0], subdomains, func(line string) error {
- fmt.Println(line)
- return nil
+ return forEachQuery(args, func(domain string) error {
+ return c.CrawlDomain(domain, subdomains, func(line string) error {
+ fmt.Println(line)
+ return nil
+ })
})
-
- if err != nil {
- return err
- }
-
- return nil
}
diff --git a/cmd/dns.go b/cmd/dns.go
index dda2ddd..5fda64a 100644
--- a/cmd/dns.go
+++ b/cmd/dns.go
@@ -20,7 +20,7 @@ var dnsTLDCmd = &cobra.Command{
Aliases: []string{"t"},
Short: "Check NXDOMAIN for custom term against all TLDs",
Long: "Streams NDJSON results checking each TLD. For NXDOMAIN domains, returns status. For existing domains, runs httpx to get page title and tech stack.",
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runDNSTLDCheck,
}
@@ -29,12 +29,11 @@ var dnsSiteCmd = &cobra.Command{
Aliases: []string{"s"},
Short: "Check if a website URL has been reused",
Long: "Checks if a website URL has been reused across different domains",
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runDNSSiteCheck,
}
func runDNSTLDCheck(cmd *cobra.Command, args []string) error {
- term := args[0]
showOnly, _ := cmd.Flags().GetString("show-only")
c, err := newUnauthenticatedClient()
@@ -42,58 +41,54 @@ func runDNSTLDCheck(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to create client: %w", err)
}
- params := &models.DNSTLDParams{
- Term: term,
- ShowOnly: showOnly,
- }
-
- fmt.Printf("Checking TLDs for term: %s\n\n", term)
-
- err = c.CheckDNSTLDStream(params, func(result json.RawMessage) error {
- var domainResult models.DomainResult
- if err := json.Unmarshal(result, &domainResult); err != nil {
- return fmt.Errorf("failed to parse result: %w", err)
+ return forEachQuery(args, func(term string) error {
+ params := &models.DNSTLDParams{
+ Term: term,
+ ShowOnly: showOnly,
}
- output, err := formatter.FormatDNSResults(&domainResult, IsJSONOutput())
+ fmt.Printf("Checking TLDs for term: %s\n\n", term)
+
+ err := c.CheckDNSTLDStream(params, func(result json.RawMessage) error {
+ var domainResult models.DomainResult
+ if err := json.Unmarshal(result, &domainResult); err != nil {
+ return fmt.Errorf("failed to parse result: %w", err)
+ }
+ output, err := formatter.FormatDNSResults(&domainResult, IsJSONOutput())
+ if err != nil {
+ return err
+ }
+ printOutput(output)
+ return nil
+ })
if err != nil {
- return err
+ return fmt.Errorf("TLD check failed: %w", err)
}
- printOutput(output)
return nil
})
-
- if err != nil {
- return fmt.Errorf("TLD check failed: %w", err)
- }
-
- return nil
}
func runDNSSiteCheck(cmd *cobra.Command, args []string) error {
- siteURL := args[0]
-
c, err := newClient()
if err != nil {
return fmt.Errorf("failed to create client: %w", err)
}
- response, err := c.CheckDNSSite(siteURL)
- if err != nil {
- return fmt.Errorf("site check failed: %w", err)
- }
-
- if err := checkError(response.Error); err != nil {
- return err
- }
-
- output, err := formatter.FormatDNSSite(response, IsJSONOutput())
- if err != nil {
- return err
- }
-
- printOutput(output)
- return nil
+ return forEachQuery(args, func(siteURL string) error {
+ response, err := c.CheckDNSSite(siteURL)
+ if err != nil {
+ return fmt.Errorf("site check failed: %w", err)
+ }
+ if err := checkError(response.Error); err != nil {
+ return err
+ }
+ output, err := formatter.FormatDNSSite(response, IsJSONOutput())
+ if err != nil {
+ return err
+ }
+ printOutput(output)
+ return nil
+ })
}
func init() {
diff --git a/cmd/email.go b/cmd/email.go
index 85cc686..871c272 100644
--- a/cmd/email.go
+++ b/cmd/email.go
@@ -17,7 +17,7 @@ var verifyEmailCmd = &cobra.Command{
Aliases: []string{"v"},
Short: "Verify email address",
Long: `Performs comprehensive email verification including format validation, MX records check, SMTP verification, and disposable/webmail detection`,
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runVerifyEmail,
}
@@ -27,21 +27,19 @@ func init() {
}
func runVerifyEmail(cmd *cobra.Command, args []string) error {
- email := args[0]
-
c, err := newClient()
if err != nil {
return err
}
- response, err := c.VerifyEmail(email)
- if err != nil {
- return err
- }
-
- if err := checkError(response.Error); err != nil {
- return err
- }
-
- return formatter.FormatEmailResults(response, IsJSONOutput())
+ return forEachQuery(args, func(email string) error {
+ response, err := c.VerifyEmail(email)
+ if err != nil {
+ return err
+ }
+ if err := checkError(response.Error); err != nil {
+ return err
+ }
+ return formatter.FormatEmailResults(response, IsJSONOutput())
+ })
}
diff --git a/cmd/files.go b/cmd/files.go
index aee7d14..6b608ca 100644
--- a/cmd/files.go
+++ b/cmd/files.go
@@ -11,7 +11,7 @@ var filesCmd = &cobra.Command{
Aliases: []string{"f"},
Short: "Search open directory files",
Long: `Search for files in open directories using various filters (free OSINT endpoint)`,
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runFilesSearch,
}
@@ -30,19 +30,25 @@ func runFilesSearch(cmd *cobra.Command, args []string) error {
return err
}
- params := &models.OpenDirectorySearchParams{
- URL: args[0],
- }
- params.Filename, _ = cmd.Flags().GetString("filename")
- params.Extension, _ = cmd.Flags().GetString("extension")
- params.Exclude, _ = cmd.Flags().GetString("exclude")
- params.Size, _ = cmd.Flags().GetInt("size")
- params.From, _ = cmd.Flags().GetInt("from")
-
- response, err := c.SearchOpenDirectoryFiles(params)
- if err != nil {
- return err
- }
+ filename, _ := cmd.Flags().GetString("filename")
+ extension, _ := cmd.Flags().GetString("extension")
+ exclude, _ := cmd.Flags().GetString("exclude")
+ size, _ := cmd.Flags().GetInt("size")
+ from, _ := cmd.Flags().GetInt("from")
- return formatter.FormatFilesResults(*response, IsJSONOutput())
+ return forEachQuery(args, func(url string) error {
+ params := &models.OpenDirectorySearchParams{
+ URL: url,
+ Filename: filename,
+ Extension: extension,
+ Exclude: exclude,
+ Size: size,
+ From: from,
+ }
+ response, err := c.SearchOpenDirectoryFiles(params)
+ if err != nil {
+ return err
+ }
+ return formatter.FormatFilesResults(*response, IsJSONOutput())
+ })
}
diff --git a/cmd/moon.go b/cmd/moon.go
index 931f64c..f749435 100644
--- a/cmd/moon.go
+++ b/cmd/moon.go
@@ -13,7 +13,7 @@ var moonCmd = &cobra.Command{
Aliases: []string{"mn"},
Short: "Search moon logs",
Long: `Search moon logs with various filters. Requires admin API key.`,
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runMoon,
}
@@ -36,40 +36,46 @@ func runMoon(cmd *cobra.Command, args []string) error {
return err
}
- params := &models.MoonParams{
- Query: args[0],
- }
- params.Filename, _ = cmd.Flags().GetString("filename")
- params.MaxHits, _ = cmd.Flags().GetInt("max_hits")
+ filename, _ := cmd.Flags().GetString("filename")
+ maxHits, _ := cmd.Flags().GetInt("max_hits")
sortBy, _ := cmd.Flags().GetString("sort_by")
if sortBy != "" && sortBy != "ingest_timestamp" && sortBy != "date_posted" {
return fmt.Errorf("invalid sort_by value: must be 'ingest_timestamp' or 'date_posted'")
}
- params.SortBy = sortBy
- params.IngestStartDate, _ = cmd.Flags().GetString("ingest_start_date")
- params.IngestEndDate, _ = cmd.Flags().GetString("ingest_end_date")
- params.PostedStartDate, _ = cmd.Flags().GetString("posted_start_date")
- params.PostedEndDate, _ = cmd.Flags().GetString("posted_end_date")
- params.Format, _ = cmd.Flags().GetString("format")
+ ingestStart, _ := cmd.Flags().GetString("ingest_start_date")
+ ingestEnd, _ := cmd.Flags().GetString("ingest_end_date")
+ postedStart, _ := cmd.Flags().GetString("posted_start_date")
+ postedEnd, _ := cmd.Flags().GetString("posted_end_date")
+ format, _ := cmd.Flags().GetString("format")
- response, err := c.SearchMoonLogs(params)
- if err != nil {
- return err
- }
-
- if err := checkError(response.Error); err != nil {
- return err
- }
-
- if params.Format != "json" {
- fmt.Println(response.Message)
+ return forEachQuery(args, func(query string) error {
+ params := &models.MoonParams{
+ Query: query,
+ Filename: filename,
+ MaxHits: maxHits,
+ SortBy: sortBy,
+ IngestStartDate: ingestStart,
+ IngestEndDate: ingestEnd,
+ PostedStartDate: postedStart,
+ PostedEndDate: postedEnd,
+ Format: format,
+ }
+ response, err := c.SearchMoonLogs(params)
+ if err != nil {
+ return err
+ }
+ if err := checkError(response.Error); err != nil {
+ return err
+ }
+ if params.Format != "json" {
+ fmt.Println(response.Message)
+ return nil
+ }
+ output, err := formatter.FormatMoonResults(response, IsJSONOutput())
+ if err != nil {
+ return err
+ }
+ printOutput(output)
return nil
- }
-
- output, err := formatter.FormatMoonResults(response, IsJSONOutput())
- if err != nil {
- return err
- }
- printOutput(output)
- return nil
+ })
}
diff --git a/cmd/root.go b/cmd/root.go
index 292e96d..a5d407f 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -23,6 +23,8 @@ DB.org.ai CLI client`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return utils.CheckForUpdates(cmd)
},
+ SilenceUsage: true,
+ SilenceErrors: true,
}
func Execute() {
diff --git a/cmd/sl.go b/cmd/sl.go
index 2f28fc7..b55d458 100644
--- a/cmd/sl.go
+++ b/cmd/sl.go
@@ -3,6 +3,7 @@ package cmd
import (
"fmt"
+ "git.db.org.ai/dborg/internal/client"
"git.db.org.ai/dborg/internal/formatter"
"git.db.org.ai/dborg/internal/models"
"github.com/spf13/cobra"
@@ -11,8 +12,8 @@ import (
var slCmd = &cobra.Command{
Use: "sl [query]",
Short: "Search stealer logs",
- Long: `Search stealer logs with various filters`,
- Args: cobra.ExactArgs(1),
+ Long: `Search stealer logs with various filters. Accepts a query arg or newline-delimited queries on stdin.`,
+ Args: argsOrStdin(1),
RunE: runSLSearch,
}
@@ -34,8 +35,14 @@ func runSLSearch(cmd *cobra.Command, args []string) error {
return err
}
+ return forEachQuery(args, func(query string) error {
+ return runSLSearchOne(cmd, c, query)
+ })
+}
+
+func runSLSearchOne(cmd *cobra.Command, c *client.Client, query string) error {
params := &models.SLParams{
- Query: args[0],
+ Query: query,
}
params.Filename, _ = cmd.Flags().GetString("filename")
params.MaxHits, _ = cmd.Flags().GetInt("max_hits")
diff --git a/cmd/stdin.go b/cmd/stdin.go
new file mode 100644
index 0000000..e9240db
--- /dev/null
+++ b/cmd/stdin.go
@@ -0,0 +1,98 @@
+package cmd
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+
+ "git.db.org.ai/dborg/internal/utils"
+ "github.com/spf13/cobra"
+)
+
+func stdinPiped() bool {
+ fi, err := os.Stdin.Stat()
+ if err != nil {
+ return false
+ }
+ return (fi.Mode() & os.ModeCharDevice) == 0
+}
+
+func readStdinLines() ([]string, error) {
+ var lines []string
+ scanner := bufio.NewScanner(os.Stdin)
+ scanner.Buffer(make([]byte, 64*1024), 1024*1024)
+ for scanner.Scan() {
+ line := strings.TrimSpace(scanner.Text())
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+ lines = append(lines, line)
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, fmt.Errorf("failed to read stdin: %w", err)
+ }
+ return lines, nil
+}
+
+func argsOrStdin(n int) cobra.PositionalArgs {
+ return func(cmd *cobra.Command, args []string) error {
+ if len(args) == n {
+ return nil
+ }
+ if len(args) == 0 && stdinPiped() {
+ return nil
+ }
+ return fmt.Errorf("accepts %d arg(s), received %d", n, len(args))
+ }
+}
+
+func resolveQueries(args []string) ([]string, error) {
+ if len(args) > 0 {
+ return args, nil
+ }
+ if !stdinPiped() {
+ return nil, fmt.Errorf("no query provided")
+ }
+ lines, err := readStdinLines()
+ if err != nil {
+ return nil, err
+ }
+ if len(lines) == 0 {
+ return nil, fmt.Errorf("no queries read from stdin")
+ }
+ return lines, nil
+}
+
+func forEachQuery(args []string, fn func(query string) error) error {
+ queries, err := resolveQueries(args)
+ if err != nil {
+ return err
+ }
+
+ multi := len(queries) > 1
+ var firstErr error
+ for i, q := range queries {
+ if multi {
+ printQuerySeparator(i, q)
+ }
+ if err := fn(q); err != nil {
+ fmt.Fprintf(os.Stderr, "Error for %q: %v\n", q, err)
+ if firstErr == nil {
+ firstErr = err
+ }
+ }
+ }
+ return firstErr
+}
+
+func printQuerySeparator(i int, q string) {
+ if utils.IsTerminal() {
+ if i > 0 {
+ fmt.Println()
+ }
+ fmt.Printf("\033[1;36m━━━ %s ━━━\033[0m\n", q)
+ } else {
+ fmt.Printf("--- %s ---\n", q)
+ }
+}
diff --git a/cmd/telegram.go b/cmd/telegram.go
index 98092a6..23da10a 100644
--- a/cmd/telegram.go
+++ b/cmd/telegram.go
@@ -17,7 +17,7 @@ var phoneCmd = &cobra.Command{
Aliases: []string{"p"},
Short: "Get phone number for Telegram user",
Long: `Retrieves the phone number associated with a Telegram username (with @ prefix) or user ID`,
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runTelegramPhone,
}
@@ -27,21 +27,19 @@ func init() {
}
func runTelegramPhone(cmd *cobra.Command, args []string) error {
- identifier := args[0]
-
c, err := newClient()
if err != nil {
return err
}
- response, err := c.GetTelegramPhone(identifier)
- if err != nil {
- return err
- }
-
- if err := checkError(response.Error); err != nil {
- return err
- }
-
- return formatter.FormatTelegramResults(response, IsJSONOutput())
+ return forEachQuery(args, func(identifier string) error {
+ response, err := c.GetTelegramPhone(identifier)
+ if err != nil {
+ return err
+ }
+ if err := checkError(response.Error); err != nil {
+ return err
+ }
+ return formatter.FormatTelegramResults(response, IsJSONOutput())
+ })
}
diff --git a/cmd/username.go b/cmd/username.go
index ed51d2e..60da503 100644
--- a/cmd/username.go
+++ b/cmd/username.go
@@ -14,7 +14,7 @@ var usernameCmd = &cobra.Command{
Aliases: []string{"un"},
Short: "Check username availability across websites",
Long: `Check username availability across hundreds of websites using WhatsMyName dataset`,
- Args: cobra.ExactArgs(1),
+ Args: argsOrStdin(1),
RunE: runUsernameCheck,
}
@@ -31,30 +31,27 @@ func runUsernameCheck(cmd *cobra.Command, args []string) error {
return err
}
- params := &models.USRSXParams{
- Username: args[0],
- }
- params.Sites, _ = cmd.Flags().GetStringSlice("sites")
- params.Fuzzy, _ = cmd.Flags().GetBool("fuzzy")
- params.MaxTasks, _ = cmd.Flags().GetInt("max_tasks")
-
- err = c.CheckUsernameStream(params, func(result json.RawMessage) error {
- if IsJSONOutput() {
- fmt.Println(string(result))
- return nil
- }
+ sites, _ := cmd.Flags().GetStringSlice("sites")
+ fuzzy, _ := cmd.Flags().GetBool("fuzzy")
+ maxTasks, _ := cmd.Flags().GetInt("max_tasks")
- var siteResult models.SiteResult
- if err := json.Unmarshal(result, &siteResult); err != nil {
- return err
+ return forEachQuery(args, func(username string) error {
+ params := &models.USRSXParams{
+ Username: username,
+ Sites: sites,
+ Fuzzy: fuzzy,
+ MaxTasks: maxTasks,
}
-
- return formatter.FormatUsernameSiteResult(&siteResult)
+ return c.CheckUsernameStream(params, func(result json.RawMessage) error {
+ if IsJSONOutput() {
+ fmt.Println(string(result))
+ return nil
+ }
+ var siteResult models.SiteResult
+ if err := json.Unmarshal(result, &siteResult); err != nil {
+ return err
+ }
+ return formatter.FormatUsernameSiteResult(&siteResult)
+ })
})
-
- if err != nil {
- return err
- }
-
- return nil
}