From 5e3332e080c229d22545dd974ac52b6bf45fc48c Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 1 May 2025 23:25:27 -0600 Subject: [PATCH] chore: updated utility functions --- internal/urlx/url.go | 116 +++++++++++++++++++++++++++++++++++++++ internal/util/bmc.go | 6 +- internal/util/error.go | 4 +- internal/util/print.go | 25 +++++++++ internal/util/strings.go | 10 ++++ 5 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 internal/urlx/url.go create mode 100644 internal/util/print.go create mode 100644 internal/util/strings.go diff --git a/internal/urlx/url.go b/internal/urlx/url.go new file mode 100644 index 0000000..f3dc689 --- /dev/null +++ b/internal/urlx/url.go @@ -0,0 +1,116 @@ +package urlx + +import ( + "fmt" + "net/url" + "strings" + + "github.com/rs/zerolog/log" +) + +func Sanitize(uri string) (string, error) { + // URL sanitanization for host argument + parsedURI, err := url.ParseRequestURI(uri) + if err != nil { + return "", fmt.Errorf("failed to parse URI: %w", err) + } + // Remove any trailing slashes + parsedURI.Path = strings.TrimSuffix(parsedURI.Path, "/") + // Collapse any doubled slashes + parsedURI.Path = strings.ReplaceAll(parsedURI.Path, "//", "/") + return parsedURI.String(), nil +} + +// FormatHosts() takes a list of hosts and ports and builds full URLs in the +// form of scheme://host:port. If no scheme is provided, it will use "https" by +// default. +// +// Returns a 2D string slice where each slice contains URL host strings for each +// port. The intention is to have all of the URLs for a single host combined into +// a single slice to initiate one goroutine per host, but making request to multiple +// ports. +func FormatHosts(hosts []string, ports []int, scheme string, verbose bool) [][]string { + // format each positional arg as a complete URL + var formattedHosts [][]string + for _, host := range hosts { + uri, err := url.ParseRequestURI(host) + if err != nil { + if verbose { + log.Warn().Msgf("invalid URI parsed: %s", host) + } + continue + } + + // check if scheme is set, if not set it with flag or default value ('https' if flag is not set) + if uri.Scheme == "" { + if scheme != "" { + uri.Scheme = scheme + } else { + // hardcoded assumption + uri.Scheme = "https" + } + } + + // tidy up slashes and update arg with new value + uri.Path = strings.TrimSuffix(uri.Path, "/") + uri.Path = strings.ReplaceAll(uri.Path, "//", "/") + + // for hosts with unspecified ports, add ports to scan from flag + if uri.Port() == "" { + var tmp []string + for _, port := range ports { + uri.Host += fmt.Sprintf(":%d", port) + tmp = append(tmp, uri.String()) + } + formattedHosts = append(formattedHosts, tmp) + } else { + formattedHosts = append(formattedHosts, []string{uri.String()}) + } + + } + return formattedHosts +} + +// FormatIPs() takes a list of IP addresses and ports and builds full URLs in the +// form of scheme://host:port. If no scheme is provided, it will use "https" by +// default. +// +// Returns a 2D string slice where each slice contains URL host strings for each +// port. The intention is to have all of the URLs for a single host combined into +// a single slice to initiate one goroutine per host, but making request to multiple +// ports. +func FormatIPs(ips []string, ports []int, scheme string, verbose bool) [][]string { + // format each positional arg as a complete URL + var formattedHosts [][]string + for _, ip := range ips { + if scheme == "" { + scheme = "https" + } + // make an entirely new object since we're expecting just IPs + uri := &url.URL{ + Scheme: scheme, + Host: ip, + } + + // tidy up slashes and update arg with new value + uri.Path = strings.ReplaceAll(uri.Path, "//", "/") + uri.Path = strings.TrimSuffix(uri.Path, "/") + + // for hosts with unspecified ports, add ports to scan from flag + if uri.Port() == "" { + if len(ports) == 0 { + ports = append(ports, 443) + } + var tmp []string + for _, port := range ports { + uri.Host += fmt.Sprintf(":%d", port) + tmp = append(tmp, uri.String()) + } + formattedHosts = append(formattedHosts, tmp) + } else { + formattedHosts = append(formattedHosts, []string{uri.String()}) + } + + } + return formattedHosts +} diff --git a/internal/util/bmc.go b/internal/util/bmc.go index d2aaebf..6a85026 100644 --- a/internal/util/bmc.go +++ b/internal/util/bmc.go @@ -13,7 +13,7 @@ func GetBMCCredentials(store secrets.SecretStore, id string) bmc.BMCCredentials ) if id == "" { - log.Error().Msg("failed to get BMC credentials: id was empty") + log.Error().Msg("failed to get BMC credentials: ID was empty") return creds } @@ -36,11 +36,11 @@ func GetBMCCredentials(store secrets.SecretStore, id string) bmc.BMCCredentials log.Warn().Str("id", id).Err(err).Msg("no default credentials were set, they will be blank unless overridden by CLI flags") } else { // Default credentials found, use them. - log.Info().Str("id", id).Msg("default credentials found, using") + log.Info().Str("id", id).Msg("using default credentials found") creds = defaultSecret } } else { - log.Info().Str("id", id).Msg("specific credentials found, using") + log.Info().Str("id", id).Msg("using specific credentials found") } return creds diff --git a/internal/util/error.go b/internal/util/error.go index 0141ea7..85de514 100644 --- a/internal/util/error.go +++ b/internal/util/error.go @@ -1,6 +1,8 @@ package util -import "fmt" +import ( + "fmt" +) // FormatErrorList() is a wrapper function that unifies error list formatting // and makes printing error lists consistent. diff --git a/internal/util/print.go b/internal/util/print.go new file mode 100644 index 0000000..aca00de --- /dev/null +++ b/internal/util/print.go @@ -0,0 +1,25 @@ +package util + +import ( + "encoding/json" + "fmt" + + "github.com/rs/zerolog/log" + "gopkg.in/yaml.v2" +) + +func PrintJSON(data any) { + b, err := json.MarshalIndent(data, "", " ") + if err != nil { + log.Error().Err(err).Msgf("failed to marshal scanned results to JSON") + } + fmt.Println(string(b)) +} + +func PrintYAML(data any) { + b, err := yaml.Marshal(data) + if err != nil { + log.Error().Err(err).Msgf("failed to marshal scanned results to JSON") + } + fmt.Printf("%s\n", string(b)) +} diff --git a/internal/util/strings.go b/internal/util/strings.go new file mode 100644 index 0000000..93a046f --- /dev/null +++ b/internal/util/strings.go @@ -0,0 +1,10 @@ +package util + +import "strings" + +func TidyJSON(s string) string { + s = strings.ReplaceAll(s, "\n", "") + s = strings.ReplaceAll(s, "\t", "") + s = strings.ReplaceAll(s, " ", "") + return strings.ReplaceAll(s, "\"", "'") +}