Path: blob/dev/pkg/protocols/common/contextargs/metainput.go
2072 views
package contextargs12import (3"bytes"4"crypto/md5"5"fmt"6"net"7"strings"8"sync"910jsoniter "github.com/json-iterator/go"11"github.com/projectdiscovery/nuclei/v3/pkg/input/types"12urlutil "github.com/projectdiscovery/utils/url"13"github.com/segmentio/ksuid"14)1516// MetaInput represents a target with metadata (TODO: replace with https://github.com/projectdiscovery/metainput)17type MetaInput struct {18// Input represent the target19Input string `json:"input,omitempty"`20// CustomIP to use for connection21CustomIP string `json:"customIP,omitempty"`22// hash of the input23hash string `json:"-"`2425// ReqResp is the raw request for the input26ReqResp *types.RequestResponse `json:"raw-request,omitempty"`2728mu *sync.Mutex29}3031func NewMetaInput() *MetaInput {32return &MetaInput{mu: &sync.Mutex{}}33}3435func (metaInput *MetaInput) marshalToBuffer() (bytes.Buffer, error) {36var b bytes.Buffer37err := jsoniter.NewEncoder(&b).Encode(metaInput)38return b, err39}4041// Target returns the target of the metainput42func (metaInput *MetaInput) Target() string {43if metaInput.ReqResp != nil && metaInput.ReqResp.URL.URL != nil {44return metaInput.ReqResp.URL.String()45}46return metaInput.Input47}4849// URL returns request url50func (metaInput *MetaInput) URL() (*urlutil.URL, error) {51instance, err := urlutil.ParseAbsoluteURL(metaInput.Target(), false)52if err != nil {53return nil, err54}55return instance, nil56}5758// Port returns the port of the target59// if port is not present then empty string is returned60func (metaInput *MetaInput) Port() string {61target, err := urlutil.ParseAbsoluteURL(metaInput.Input, false)62if err != nil {63return ""64}65return target.Port()66}6768// Address return the remote address of target69// Note: it does not resolve the domain to ip70// it is meant to be used by hosterrorsCache if invalid metainput71// is provided then it uses a random ksuid as hostname to avoid skipping valid targets72func (metaInput *MetaInput) Address() string {73var hostname, port string74target, err := urlutil.ParseAbsoluteURL(metaInput.Target(), false)75if err != nil {76if metaInput.CustomIP == "" {77// since this is used in hosterrorscache we add a random id78// which will never be used to avoid skipping valid targets79hostname = fmt.Sprintf("invalid-%s", ksuid.New().String())80}81} else {82hostname = target.Hostname()83port = target.Port()84if port == "" {85switch target.Scheme {86case urlutil.HTTP:87port = "80"88case urlutil.HTTPS:89port = "443"90default:91port = "80"92}93}94}95if metaInput.CustomIP != "" {96hostname = metaInput.CustomIP97}98if port == "" {99if strings.HasPrefix(hostname, "http://") {100port = "80"101} else if strings.HasPrefix(hostname, "https://") {102port = "443"103}104}105return net.JoinHostPort(hostname, port)106}107108// ID returns a unique id/hash for metainput109func (metaInput *MetaInput) ID() string {110if metaInput.CustomIP != "" {111return fmt.Sprintf("%s-%s", metaInput.Input, metaInput.CustomIP)112}113if metaInput.ReqResp != nil {114return metaInput.ReqResp.ID()115}116return metaInput.Input117}118119func (metaInput *MetaInput) MarshalString() (string, error) {120b, err := metaInput.marshalToBuffer()121return b.String(), err122}123124func (metaInput *MetaInput) MustMarshalString() string {125marshaled, _ := metaInput.MarshalString()126return marshaled127}128129func (metaInput *MetaInput) MarshalBytes() ([]byte, error) {130b, err := metaInput.marshalToBuffer()131return b.Bytes(), err132}133134func (metaInput *MetaInput) MustMarshalBytes() []byte {135marshaled, _ := metaInput.MarshalBytes()136return marshaled137}138139func (metaInput *MetaInput) Unmarshal(data string) error {140return jsoniter.NewDecoder(strings.NewReader(data)).Decode(metaInput)141}142143func (metaInput *MetaInput) Clone() *MetaInput {144metaInput.mu.Lock()145defer metaInput.mu.Unlock()146147input := NewMetaInput()148input.Input = metaInput.Input149input.CustomIP = metaInput.CustomIP150input.hash = metaInput.hash151if metaInput.ReqResp != nil {152input.ReqResp = metaInput.ReqResp.Clone()153}154return input155}156157func (metaInput *MetaInput) PrettyPrint() string {158if metaInput.CustomIP != "" {159return fmt.Sprintf("%s [%s]", metaInput.Input, metaInput.CustomIP)160}161if metaInput.ReqResp != nil {162return fmt.Sprintf("%s [%s]", metaInput.ReqResp.URL.String(), metaInput.ReqResp.Request.Method)163}164return metaInput.Input165}166167// GetScanHash returns a unique hash that represents a scan by hashing (metainput + templateId)168func (metaInput *MetaInput) GetScanHash(templateId string) string {169// there may be some cases where metainput is changed ex: while executing self-contained template etc170// but that totally changes the scanID/hash so to avoid that we compute hash only once171// and reuse it for all subsequent calls172metaInput.mu.Lock()173defer metaInput.mu.Unlock()174175if metaInput.hash == "" {176var rawRequest string177if metaInput.ReqResp != nil {178rawRequest = metaInput.ReqResp.ID()179}180metaInput.hash = getMd5Hash(templateId + ":" + metaInput.Input + ":" + metaInput.CustomIP + rawRequest)181}182return metaInput.hash183}184185func getMd5Hash(data string) string {186bin := md5.Sum([]byte(data))187return string(bin[:])188}189190191