diff --git a/providers/network/config/config.go b/providers/network/config/config.go index f9445077de..ddd2347395 100644 --- a/providers/network/config/config.go +++ b/providers/network/config/config.go @@ -6,6 +6,7 @@ package config import ( "go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory" "go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin" + "go.mondoo.com/cnquery/v11/providers/network/connection" "go.mondoo.com/cnquery/v11/providers/network/provider" ) @@ -42,6 +43,12 @@ var Config = plugin.Provider{ Default: "", Desc: "Disable TLS/SSL verification", }, + { + Long: connection.OPTION_FOLLOW_REDIRECTS, + Type: plugin.FlagType_Bool, + Default: "", + Desc: "Follow HTTP redirects", + }, }, }, }, diff --git a/providers/network/connection/connection.go b/providers/network/connection/connection.go index 625e947797..398df40ab7 100644 --- a/providers/network/connection/connection.go +++ b/providers/network/connection/connection.go @@ -13,11 +13,16 @@ import ( "go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin" ) +const ( + OPTION_FOLLOW_REDIRECTS = "follow-redirects" +) + type HostConnection struct { plugin.Connection - Conf *inventory.Config - asset *inventory.Asset - httpClient *http.Client + Conf *inventory.Config + FollowRedirects bool + asset *inventory.Asset + transport *http.Transport } func NewHostConnection(id uint32, asset *inventory.Asset, conf *inventory.Config) *HostConnection { @@ -40,11 +45,17 @@ func NewHostConnection(id uint32, asset *inventory.Asset, conf *inventory.Config } } + var followRedirects bool + if followRedirectsStr, ok := conf.Options[OPTION_FOLLOW_REDIRECTS]; ok { + followRedirects = followRedirectsStr == "true" + } + return &HostConnection{ - Connection: plugin.NewConnection(id, asset), - Conf: conf, - asset: asset, - httpClient: &http.Client{Transport: transport}, + Connection: plugin.NewConnection(id, asset), + Conf: conf, + asset: asset, + transport: transport, + FollowRedirects: followRedirects, } } @@ -63,6 +74,15 @@ func (p *HostConnection) FQDN() string { return p.Conf.Host } -func (p *HostConnection) Client() *http.Client { - return p.httpClient +func (p *HostConnection) Client(followRedirects bool) *http.Client { + c := &http.Client{ + Transport: p.transport, + } + + if !followRedirects { + c.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + } + return c } diff --git a/providers/network/provider/provider.go b/providers/network/provider/provider.go index 0139157509..6e5060edb3 100644 --- a/providers/network/provider/provider.go +++ b/providers/network/provider/provider.go @@ -7,6 +7,7 @@ import ( "context" "errors" "net/url" + "strconv" "strings" "go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory" @@ -44,6 +45,11 @@ func (s *Service) ParseCLI(req *plugin.ParseCLIReq) (*plugin.ParseCLIRes, error) insecure, _ = found.RawData().Value.(bool) } + options := map[string]string{} + if found, ok := req.Flags[connection.OPTION_FOLLOW_REDIRECTS]; ok { + options[connection.OPTION_FOLLOW_REDIRECTS] = strconv.FormatBool(found.RawData().Value.(bool)) + } + asset := inventory.Asset{ Connections: []*inventory.Config{{ Type: "host", @@ -52,6 +58,7 @@ func (s *Service) ParseCLI(req *plugin.ParseCLIReq) (*plugin.ParseCLIRes, error) Path: path, Runtime: scheme, Insecure: insecure, + Options: options, }}, } diff --git a/providers/network/resources/http.go b/providers/network/resources/http.go index 90d7d88f30..e23b87e492 100644 --- a/providers/network/resources/http.go +++ b/providers/network/resources/http.go @@ -75,13 +75,18 @@ func initHttpGet(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[str return nil, nil, err } args["url"] = llx.ResourceData(url, "url") + args["followRedirects"] = llx.BoolData(conn.FollowRedirects) + } + + if _, ok := args["followRedirects"]; !ok { + args["followRedirects"] = llx.BoolData(false) } return args, nil, nil } func (x *mqlHttpGet) id() (string, error) { - return x.Url.Data.__id, nil + return strings.Join([]string{x.Url.Data.__id, strconv.FormatBool(x.FollowRedirects.Data)}, ";"), nil } func (x *mqlHttpGet) do() error { @@ -97,7 +102,7 @@ func (x *mqlHttpGet) do() error { } conn := x.MqlRuntime.Connection.(*connection.HostConnection) - resp, err := conn.Client().Get(x.Url.Data.String.Data) + resp, err := conn.Client(x.FollowRedirects.Data).Get(x.Url.Data.String.Data) x.resp.State = plugin.StateIsSet x.resp.Data = resp x.resp.Error = err diff --git a/providers/network/resources/network.lr b/providers/network/resources/network.lr index 8053585cd1..bae7477329 100644 --- a/providers/network/resources/network.lr +++ b/providers/network/resources/network.lr @@ -19,9 +19,11 @@ http {} // HTTP GET requests http.get @defaults("url statusCode") { - init(rawUrl string) + init(rawUrl string, followRedirects bool) // URL for this request url url + // Follow redirects + followRedirects bool // Header returned from this request header() http.header // Status returned from this request diff --git a/providers/network/resources/network.lr.go b/providers/network/resources/network.lr.go index 08acfb3333..2e631f6de2 100644 --- a/providers/network/resources/network.lr.go +++ b/providers/network/resources/network.lr.go @@ -198,6 +198,9 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "http.get.url": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlHttpGet).GetUrl()).ToDataRes(types.Resource("url")) }, + "http.get.followRedirects": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlHttpGet).GetFollowRedirects()).ToDataRes(types.Bool) + }, "http.get.header": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlHttpGet).GetHeader()).ToDataRes(types.Resource("http.header")) }, @@ -676,6 +679,10 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlHttpGet).Url, ok = plugin.RawToTValue[*mqlUrl](v.Value, v.Error) return }, + "http.get.followRedirects": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlHttpGet).FollowRedirects, ok = plugin.RawToTValue[bool](v.Value, v.Error) + return + }, "http.get.header": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlHttpGet).Header, ok = plugin.RawToTValue[*mqlHttpHeader](v.Value, v.Error) return @@ -1476,6 +1483,7 @@ type mqlHttpGet struct { __id string mqlHttpGetInternal Url plugin.TValue[*mqlUrl] + FollowRedirects plugin.TValue[bool] Header plugin.TValue[*mqlHttpHeader] StatusCode plugin.TValue[int64] Version plugin.TValue[string] @@ -1523,6 +1531,10 @@ func (c *mqlHttpGet) GetUrl() *plugin.TValue[*mqlUrl] { return &c.Url } +func (c *mqlHttpGet) GetFollowRedirects() *plugin.TValue[bool] { + return &c.FollowRedirects +} + func (c *mqlHttpGet) GetHeader() *plugin.TValue[*mqlHttpHeader] { return plugin.GetOrCompute[*mqlHttpHeader](&c.Header, func() (*mqlHttpHeader, error) { if c.MqlRuntime.HasRecording { diff --git a/providers/network/resources/network.lr.manifest.yaml b/providers/network/resources/network.lr.manifest.yaml index 53d9c94635..ae7db9fb74 100755 --- a/providers/network/resources/network.lr.manifest.yaml +++ b/providers/network/resources/network.lr.manifest.yaml @@ -91,6 +91,7 @@ resources: http.get: fields: body: {} + followRedirects: {} header: {} statusCode: {} url: {} diff --git a/providers/network/resources/url.go b/providers/network/resources/url.go index 074f89021a..c01d6aa336 100644 --- a/providers/network/resources/url.go +++ b/providers/network/resources/url.go @@ -82,7 +82,8 @@ func (x *mqlUrl) string() (string, error) { } host := x.Host.Data - if x.Port.Data != 0 { + isStandardPort := x.Port.Data == 80 && x.Scheme.Data == "http" || x.Port.Data == 443 && x.Scheme.Data == "https" + if x.Port.Data != 0 && !isStandardPort { host += ":" + strconv.Itoa(int(x.Port.Data)) }