diff --git a/v3/integrations/nrecho-v3/nrecho.go b/v3/integrations/nrecho-v3/nrecho.go index ad5f862b8..8125265b6 100644 --- a/v3/integrations/nrecho-v3/nrecho.go +++ b/v3/integrations/nrecho-v3/nrecho.go @@ -51,9 +51,7 @@ func transactionName(c echo.Context) string { // e := echo.New() // // Add the nrecho middleware before other middlewares or routes: // e.Use(nrecho.Middleware(app)) -// func Middleware(app *newrelic.Application) func(echo.HandlerFunc) echo.HandlerFunc { - if nil == app { return func(next echo.HandlerFunc) echo.HandlerFunc { return next @@ -93,3 +91,24 @@ func Middleware(app *newrelic.Application) func(echo.HandlerFunc) echo.HandlerFu } } } + +// WrapRouter extracts API endpoints from the echo instance passed to it +// which is used to detect application URL mapping(api-endpoints) for provable security. +// In this version of the integration, this wrapper is only necessary if you are using the New Relic security agent integration [https://github.com/newrelic/go-agent/tree/master/v3/integrations/nrsecurityagent], +// but it may be enhanced to provide additional functionality in future releases. +// e := echo.New() +// .... +// .... +// .... +// +// nrecho.WrapRouter(e) +// + +func WrapRouter(engine *echo.Echo) { + if engine != nil && newrelic.IsSecurityAgentPresent() { + router := engine.Routes() + for _, r := range router { + newrelic.GetSecurityAgentInterface().SendEvent("API_END_POINTS", r.Path, r.Method, r.Name) + } + } +} diff --git a/v3/integrations/nrecho-v4/nrecho.go b/v3/integrations/nrecho-v4/nrecho.go index 4c3b8cd18..d768c7a76 100644 --- a/v3/integrations/nrecho-v4/nrecho.go +++ b/v3/integrations/nrecho-v4/nrecho.go @@ -71,7 +71,6 @@ func WithSkipper(skipper Skipper) ConfigOption { // e := echo.New() // // Add the nrecho middleware before other middlewares or routes: // e.Use(nrecho.MiddlewareWithConfig(nrecho.Config{App: app})) -// func Middleware(app *newrelic.Application, opts ...ConfigOption) func(echo.HandlerFunc) echo.HandlerFunc { if app == nil { return func(next echo.HandlerFunc) echo.HandlerFunc { @@ -131,3 +130,24 @@ func Middleware(app *newrelic.Application, opts ...ConfigOption) func(echo.Handl } } } + +// WrapRouter extracts API endpoints from the echo instance passed to it +// which is used to detect application URL mapping(api-endpoints) for provable security. +// In this version of the integration, this wrapper is only necessary if you are using the New Relic security agent integration [https://github.com/newrelic/go-agent/tree/master/v3/integrations/nrsecurityagent], +// but it may be enhanced to provide additional functionality in future releases. +// e := echo.New() +// .... +// .... +// .... +// +// nrecho.WrapRouter(e) +// + +func WrapRouter(engine *echo.Echo) { + if engine != nil && newrelic.IsSecurityAgentPresent() { + router := engine.Routes() + for _, r := range router { + newrelic.GetSecurityAgentInterface().SendEvent("API_END_POINTS", r.Path, r.Method, r.Name) + } + } +} diff --git a/v3/integrations/nrfasthttp/instrumentation.go b/v3/integrations/nrfasthttp/instrumentation.go index abefb8460..f05a8b770 100644 --- a/v3/integrations/nrfasthttp/instrumentation.go +++ b/v3/integrations/nrfasthttp/instrumentation.go @@ -50,7 +50,9 @@ func WrapHandle(app *newrelic.Application, pattern string, handler fasthttp.Requ if app == nil { return pattern, handler } - + if newrelic.IsSecurityAgentPresent() { + newrelic.GetSecurityAgentInterface().SendEvent("API_END_POINTS", pattern, "*", internal.HandlerName(handler)) + } // add the wrapped function to the trace options as the source code reference point // (but only if we know we're collecting CLM for this transaction and the user didn't already // specify a different code location explicitly). diff --git a/v3/integrations/nrgin/nrgin.go b/v3/integrations/nrgin/nrgin.go index 88ef11b24..e2943466b 100644 --- a/v3/integrations/nrgin/nrgin.go +++ b/v3/integrations/nrgin/nrgin.go @@ -144,6 +144,26 @@ func MiddlewareHandlerTxnNames(app *newrelic.Application) gin.HandlerFunc { return middleware(app, false) } +// WrapRouter extracts API endpoints from the router instance passed to it +// which is used to detect application URL mapping(api-endpoints) for provable security. +// In this version of the integration, this wrapper is only necessary if you are using the New Relic security agent integration [https://github.com/newrelic/go-agent/tree/master/v3/integrations/nrsecurityagent], +// but it may be enhanced to provide additional functionality in future releases. +// router := gin.Default() +// .... +// .... +// .... +// +// nrgin.WrapRouter(router) +// + +func WrapRouter(engine *gin.Engine) { + if engine != nil && newrelic.IsSecurityAgentPresent() { + router := engine.Routes() + for _, r := range router { + newrelic.GetSecurityAgentInterface().SendEvent("API_END_POINTS", r.Path, r.Method, internal.HandlerName(r.HandlerFunc)) + } + } +} func middleware(app *newrelic.Application, useNewNames bool) gin.HandlerFunc { return func(c *gin.Context) { if app != nil { diff --git a/v3/integrations/nrgorilla/nrgorilla.go b/v3/integrations/nrgorilla/nrgorilla.go index dd2088a25..a8425d0a3 100644 --- a/v3/integrations/nrgorilla/nrgorilla.go +++ b/v3/integrations/nrgorilla/nrgorilla.go @@ -111,3 +111,35 @@ func Middleware(app *newrelic.Application) mux.MiddlewareFunc { }) } } + +// WrapRouter extracts API endpoints from the router object passed to it +// which is used to detect application URL mapping(api-endpoints) for provable security. +// In this version of the integration, this wrapper is only necessary if you are using the New Relic security agent integration [https://github.com/newrelic/go-agent/tree/master/v3/integrations/nrsecurityagent], +// but it may be enhanced to provide additional functionality in future releases. +// r := mux.NewRouter() +// .... +// .... +// .... +// +// nrgorilla.WrapRouter(router) +// + +func WrapRouter(router *mux.Router) { + if router != nil && newrelic.IsSecurityAgentPresent() { + router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { + path, err1 := route.GetPathTemplate() + if err1 != nil { + return nil + } + methods, _ := route.GetMethods() + if len(methods) == 0 { + newrelic.GetSecurityAgentInterface().SendEvent("API_END_POINTS", path, "*", internal.HandlerName(route.GetHandler())) + } else { + for _, method := range methods { + newrelic.GetSecurityAgentInterface().SendEvent("API_END_POINTS", path, method, internal.HandlerName(route.GetHandler())) + } + } + return nil + }) + } +} diff --git a/v3/integrations/nrhttprouter/nrhttprouter.go b/v3/integrations/nrhttprouter/nrhttprouter.go index 75cb73ff4..d1df356eb 100644 --- a/v3/integrations/nrhttprouter/nrhttprouter.go +++ b/v3/integrations/nrhttprouter/nrhttprouter.go @@ -8,33 +8,33 @@ // httprouter.Router. Use an *nrhttprouter.Router in place of your // *httprouter.Router. Example: // -// package main +// package main // -// import ( -// "fmt" -// "net/http" -// "os" +// import ( +// "fmt" +// "net/http" +// "os" // -// "github.com/julienschmidt/httprouter" -// newrelic "github.com/newrelic/go-agent/v3/newrelic" -// "github.com/newrelic/go-agent/v3/integrations/nrhttprouter" -// ) +// "github.com/julienschmidt/httprouter" +// newrelic "github.com/newrelic/go-agent/v3/newrelic" +// "github.com/newrelic/go-agent/v3/integrations/nrhttprouter" +// ) // -// func main() { -// cfg := newrelic.NewConfig("httprouter App", os.Getenv("NEW_RELIC_LICENSE_KEY")) -// app, _ := newrelic.NewApplication(cfg) +// func main() { +// cfg := newrelic.NewConfig("httprouter App", os.Getenv("NEW_RELIC_LICENSE_KEY")) +// app, _ := newrelic.NewApplication(cfg) // -// // Create the Router replacement: -// router := nrhttprouter.New(app) +// // Create the Router replacement: +// router := nrhttprouter.New(app) // -// router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { -// w.Write([]byte("welcome\n")) -// }) -// router.GET("/hello/:name", (w http.ResponseWriter, r *http.Request, ps httprouter.Params) { -// w.Write([]byte(fmt.Sprintf("hello %s\n", ps.ByName("name")))) -// }) -// http.ListenAndServe(":8000", router) -// } +// router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { +// w.Write([]byte("welcome\n")) +// }) +// router.GET("/hello/:name", (w http.ResponseWriter, r *http.Request, ps httprouter.Params) { +// w.Write([]byte(fmt.Sprintf("hello %s\n", ps.ByName("name")))) +// }) +// http.ListenAndServe(":8000", router) +// } // // Runnable example: https://github.com/newrelic/go-agent/tree/master/v3/integrations/nrhttprouter/example/main.go package nrhttprouter @@ -84,6 +84,9 @@ func (r *Router) handle(method string, path string, original httprouter.Handle) } } r.Router.Handle(method, path, handle) + if newrelic.IsSecurityAgentPresent() { + newrelic.GetSecurityAgentInterface().SendEvent("API_END_POINTS", path, method, internal.HandlerName(original)) + } } // DELETE replaces httprouter.Router.DELETE. diff --git a/v3/integrations/nrsecurityagent/go.mod b/v3/integrations/nrsecurityagent/go.mod index 8e419fe8d..26f12cd40 100644 --- a/v3/integrations/nrsecurityagent/go.mod +++ b/v3/integrations/nrsecurityagent/go.mod @@ -3,11 +3,10 @@ module github.com/newrelic/go-agent/v3/integrations/nrsecurityagent go 1.19 require ( - github.com/newrelic/csec-go-agent v1.0.0 - github.com/newrelic/go-agent/v3 v3.31.0 + github.com/newrelic/csec-go-agent v1.1.0 + github.com/newrelic/go-agent/v3 v3.30.0 github.com/newrelic/go-agent/v3/integrations/nrsqlite3 v1.2.0 gopkg.in/yaml.v2 v2.4.0 ) - replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/internal/utilities.go b/v3/internal/utilities.go index e6c78e5ca..d57538b9a 100644 --- a/v3/internal/utilities.go +++ b/v3/internal/utilities.go @@ -7,6 +7,8 @@ import ( "bytes" "encoding/json" "fmt" + "reflect" + "runtime" "time" ) @@ -26,3 +28,17 @@ func CompactJSONString(js string) string { } return buf.String() } + +// HandlerName return name of a function. +func HandlerName(h interface{}) string { + if h == nil { + return "" + } + t := reflect.ValueOf(h).Type() + if t.Kind() == reflect.Func { + if pointer := runtime.FuncForPC(reflect.ValueOf(h).Pointer()); pointer != nil { + return pointer.Name() + } + } + return "" +} diff --git a/v3/newrelic/instrumentation.go b/v3/newrelic/instrumentation.go index d0ffd7379..b58db6ccc 100644 --- a/v3/newrelic/instrumentation.go +++ b/v3/newrelic/instrumentation.go @@ -5,6 +5,8 @@ package newrelic import ( "net/http" + + "github.com/newrelic/go-agent/v3/internal" ) // instrumentation.go contains helpers built on the lower level api. @@ -42,6 +44,10 @@ func WrapHandle(app *Application, pattern string, handler http.Handler, options // specify a different code location explicitly). cache := NewCachedCodeLocation() + if IsSecurityAgentPresent() { + secureAgent.SendEvent("API_END_POINTS", pattern, "*", internal.HandlerName(handler)) + } + return pattern, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var tOptions *traceOptSet var txnOptionList []TraceOption diff --git a/v3/newrelic/secure_agent.go b/v3/newrelic/secure_agent.go index 6c0057c10..ae0ce723f 100644 --- a/v3/newrelic/secure_agent.go +++ b/v3/newrelic/secure_agent.go @@ -10,7 +10,7 @@ import ( // calls in this case, effectively removing the hooks from the running agent. // // If the nrsecureagent integration was initialized, it will register a real securityAgent -// value in the securityAgent varialble instead, thus "activating" the hooks. +// value in the securityAgent variable instead, thus "activating" the hooks. var secureAgent securityAgent = noOpSecurityAgent{} // GetSecurityAgentInterface returns the securityAgent value @@ -20,7 +20,7 @@ var secureAgent securityAgent = noOpSecurityAgent{} // // Packages which need to make calls to secureAgent's methods // may obtain the secureAgent value by calling this function. -// This avoids exposing the variable itself so it's not +// This avoids exposing the variable itself, so it's not // writable externally and also sets up for the future if this // ends up not being a global variable later. func GetSecurityAgentInterface() securityAgent {