package input import ( "net" "path/filepath" "strings" "github.com/projectdiscovery/hmap/store/hybrid" templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" fileutil "github.com/projectdiscovery/utils/file" "github.com/projectdiscovery/utils/ports" stringsutil "github.com/projectdiscovery/utils/strings" urlutil "github.com/projectdiscovery/utils/url" ) // Helper is a structure for helping with input transformation type Helper struct { InputsHTTP *hybrid.HybridMap } // NewHelper returns a new input helper instance func NewHelper() *Helper { helper := &Helper{} return helper } // Close closes the resources associated with input helper func (h *Helper) Close() error { var err error if h.InputsHTTP != nil { err = h.InputsHTTP.Close() } return err } // Transform transforms an input based on protocol type and returns // appropriate input based on it. func (h *Helper) Transform(input string, protocol templateTypes.ProtocolType) string { switch protocol { case templateTypes.DNSProtocol, templateTypes.WHOISProtocol: return h.convertInputToType(input, typeHostOnly, "") case templateTypes.FileProtocol, templateTypes.OfflineHTTPProtocol: return h.convertInputToType(input, typeFilepath, "") case templateTypes.HTTPProtocol, templateTypes.HeadlessProtocol: return h.convertInputToType(input, typeURL, "") case templateTypes.NetworkProtocol: return h.convertInputToType(input, typeHostWithOptionalPort, "") case templateTypes.WebsocketProtocol: return h.convertInputToType(input, typeWebsocket, "") case templateTypes.SSLProtocol: return h.convertInputToType(input, typeHostWithPort, "443") } return input } type inputType int const ( typeHostOnly inputType = iota + 1 typeHostWithPort typeHostWithOptionalPort typeURL typeFilepath typeWebsocket ) // convertInputToType converts an input based on an inputType. // Various formats are supported for inputs and their transformation func (h *Helper) convertInputToType(input string, inputType inputType, defaultPort string) string { isURL := strings.Contains(input, "://") uri, _ := urlutil.Parse(input) var host, port string if isURL && uri != nil { host, port, _ = net.SplitHostPort(uri.Host) } else { host, port, _ = net.SplitHostPort(input) } hasHost := host != "" hasPort := ports.IsValid(port) hasDefaultPort := ports.IsValid(defaultPort) switch inputType { case typeFilepath: // if it has ports most likely it's not a file if hasPort { return "" } if filepath.IsAbs(input) { return input } if absPath, _ := filepath.Abs(input); absPath != "" && fileutil.FileOrFolderExists(absPath) { return input } if _, err := filepath.Match(input, ""); err != filepath.ErrBadPattern && !isURL { return input } // if none of these satisfy the condition return empty return "" case typeHostOnly: if hasHost { return host } if isURL && uri != nil { return uri.Hostname() } return input case typeURL: if uri != nil && stringsutil.EqualFoldAny(uri.Scheme, "http", "https") { return input } if h.InputsHTTP != nil { if probed, ok := h.InputsHTTP.Get(input); ok { return string(probed) } } // try to parse it as absolute url and return if absUrl, err := urlutil.ParseAbsoluteURL(input, false); err == nil { return absUrl.String() } case typeHostWithPort, typeHostWithOptionalPort: if hasHost && hasPort { return net.JoinHostPort(host, port) } if uri != nil && !hasPort && uri.Scheme == "https" { return net.JoinHostPort(uri.Host, "443") } if hasDefaultPort { return net.JoinHostPort(input, defaultPort) } if inputType == typeHostWithOptionalPort { return input } case typeWebsocket: if uri != nil && stringsutil.EqualFoldAny(uri.Scheme, "ws", "wss") { return input } // empty if prefix is not given return "" } // do not return empty return input }