Path: blob/dev/pkg/protocols/common/contextargs/contextargs.go
2072 views
package contextargs12import (3"context"4"net/http/cookiejar"5"strings"6"sync/atomic"78"github.com/projectdiscovery/gologger"9mapsutil "github.com/projectdiscovery/utils/maps"10sliceutil "github.com/projectdiscovery/utils/slice"11stringsutil "github.com/projectdiscovery/utils/strings"12urlutil "github.com/projectdiscovery/utils/url"13)1415var (16// reservedPorts contains list of reserved ports for non-network requests in nuclei17reservedPorts = []string{"80", "443", "8080", "8443", "8081", "53"}18)1920// Context implements a shared context struct to share information across multiple templates within a workflow21type Context struct {22ctx context.Context2324// Meta is the target for the executor25MetaInput *MetaInput2627// CookieJar shared within workflow's http templates28CookieJar *cookiejar.Jar2930// Args is a workflow shared key-value store31args *mapsutil.SyncLockMap[string, interface{}]32}3334// Create a new contextargs instance35func New(ctx context.Context) *Context {36return NewWithInput(ctx, "")37}3839// NewWithMetaInput creates a new contextargs instance with meta input40func NewWithMetaInput(ctx context.Context, input *MetaInput) *Context {41n := New(ctx)42n.MetaInput = input43return n44}4546// Create a new contextargs instance with input string47func NewWithInput(ctx context.Context, input string) *Context {48jar, err := cookiejar.New(nil)49if err != nil {50gologger.Error().Msgf("contextargs: could not create cookie jar: %s\n", err)51}52metaInput := NewMetaInput()53metaInput.Input = input54return &Context{55ctx: ctx,56MetaInput: metaInput,57CookieJar: jar,58args: &mapsutil.SyncLockMap[string, interface{}]{59Map: make(map[string]interface{}),60ReadOnly: atomic.Bool{},61},62}63}6465// Context returns the context of the current contextargs66func (ctx *Context) Context() context.Context {67return ctx.ctx68}6970// Set the specific key-value pair71func (ctx *Context) Set(key string, value interface{}) {72_ = ctx.args.Set(key, value)73}7475func (ctx *Context) hasArgs() bool {76return !ctx.args.IsEmpty()77}7879// Merge the key-value pairs80func (ctx *Context) Merge(args map[string]interface{}) {81_ = ctx.args.Merge(args)82}8384// Add the specific key-value pair85func (ctx *Context) Add(key string, v interface{}) {86values, ok := ctx.args.Get(key)87if !ok {88ctx.Set(key, v)89}9091// If the key exists, append the value to the existing value92switch v := v.(type) {93case []string:94if values, ok := values.([]string); ok {95values = append(values, v...)96ctx.Set(key, values)97}98case string:99if values, ok := values.(string); ok {100tmp := []string{values, v}101ctx.Set(key, tmp)102}103default:104values, _ := ctx.Get(key)105ctx.Set(key, []interface{}{values, v})106}107}108109// UseNetworkPort updates input with required/default network port for that template110// but is ignored if input/target contains non-http ports like 80,8080,8081 etc111func (ctx *Context) UseNetworkPort(port string, excludePorts string) error {112ignorePorts := reservedPorts113if excludePorts != "" {114// TODO: add support for service names like http,https,ssh etc once https://github.com/projectdiscovery/netdb is ready115ignorePorts = sliceutil.Dedupe(strings.Split(excludePorts, ","))116}117if port == "" {118// if template does not contain port, do nothing119return nil120}121target, err := urlutil.Parse(ctx.MetaInput.Input)122if err != nil {123return err124}125inputPort := target.Port()126if inputPort == "" || stringsutil.EqualFoldAny(inputPort, ignorePorts...) {127// replace port with networkPort128target.UpdatePort(port)129ctx.MetaInput.Input = target.Host130}131return nil132}133134// Port returns the port of the target135func (ctx *Context) Port() string {136target, err := urlutil.Parse(ctx.MetaInput.Input)137if err != nil {138return ""139}140return target.Port()141}142143// Get the value with specific key if exists144func (ctx *Context) Get(key string) (interface{}, bool) {145if !ctx.hasArgs() {146return nil, false147}148149return ctx.args.Get(key)150}151152func (ctx *Context) GetAll() map[string]interface{} {153if !ctx.hasArgs() {154return nil155}156157return ctx.args.Clone().Map158}159160func (ctx *Context) ForEach(f func(string, interface{})) {161_ = ctx.args.Iterate(func(k string, v interface{}) error {162f(k, v)163return nil164})165}166167// Has check if the key exists168func (ctx *Context) Has(key string) bool {169return ctx.hasArgs() && ctx.args.Has(key)170}171172func (ctx *Context) HasArgs() bool {173return !ctx.args.IsEmpty()174}175176func (ctx *Context) Clone() *Context {177newCtx := &Context{178ctx: ctx.ctx,179MetaInput: ctx.MetaInput.Clone(),180args: ctx.args.Clone(),181CookieJar: ctx.CookieJar,182}183return newCtx184}185186// GetCopyIfHostOutdated returns a new contextargs if the host is outdated187func GetCopyIfHostOutdated(ctx *Context, url string) *Context {188if ctx.MetaInput.Input == "" {189newctx := ctx.Clone()190newctx.MetaInput.Input = url191return newctx192}193orig, _ := urlutil.Parse(ctx.MetaInput.Input)194newURL, _ := urlutil.Parse(url)195if orig != nil && newURL != nil && orig.Host != newURL.Host {196newCtx := ctx.Clone()197newCtx.MetaInput.Input = newURL.Host198return newCtx199}200return ctx201}202203204