diff --git a/Changelog.md b/Changelog.md index cc571be..32775eb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,25 @@ # Changelog +## [v0.6.0] - 2024-15-01 +### Changes +* Added exclusion based filtering of RXSS events. +* Added ws headers NR-CSEC-ENTITY-GUID and NR-CSEC-ENTITY-NAME. +* Added Support for PUT, PATCH and DELETE http requests type. NR-175410 +* Added Support for FastHttp framework. +* Implemented API to send important logs to Security Engine. +* Added support for warning messages in case of missing security wrappers +* Updated jsonVersion to 1.1.1 in security events. +* Updated example/test application directory. +* Updated unit test-cases for mongo. +* Updated file access hook and sent absolute file path. +### Changes +* Incorrect query type for mongo findAndModify case. +* Fixed empty complete request ID for lastleg . +* Incorrect server protocol in case of grpc. +* Nil query for sql prepared statement for MAC environment. +* Fixed for NPE in case of outbound request. + + ## [v0.5.1] - 2023-11-16 ### Bug Fixes * Added required changes for backward compatibility with APM agent. diff --git a/README.md b/README.md index 5c0eaba..afb7e90 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ For the latest version of the agent, Go 1.17+ is required. * labstack/echo * julienschmidt/httprouter * micro/go-micro +* valyala/fasthttp ### Databases diff --git a/examples/sample-vulnerable-application/README.md b/examples/sample-vulnerable-application/README.md new file mode 100644 index 0000000..a4dfab0 --- /dev/null +++ b/examples/sample-vulnerable-application/README.md @@ -0,0 +1,28 @@ +#### examples/sample-vulnerable-application +sample-vulnerable-application is a vulnerable web application designed for demo and reference. +#### WARNING! +--- +sample-vulnerable-application is a vulnerable web application. + +**Use it for demo purposes only, run it only on test environment.** + +#### Setup +``` +git clone https://github.com/newrelic/csec-go-agent.git +cd examples/sample-vulnerable-application + +``` +#### Install dependency packages + +``` +go mod init test +go mod download +``` + +#### Run application +``` +go run main.go +``` + +#### Accessing the application : +The application can be accessed at `http://HOST_MACHINE_IP:8000` \ No newline at end of file diff --git a/examples/server/main.go b/examples/sample-vulnerable-application/main.go similarity index 95% rename from examples/server/main.go rename to examples/sample-vulnerable-application/main.go index 8541724..757cd4a 100644 --- a/examples/server/main.go +++ b/examples/sample-vulnerable-application/main.go @@ -1,6 +1,6 @@ // Copyright 2023 New Relic Corporation. All rights reserved. // SPDX-License-Identifier: New Relic Pre-Release - +// warning! sample-vulnerable-application is a vulnerable web application.Use it for demo purposes only, run it only on test environment package main import ( diff --git a/instrumentation/csec_antchfx_htmlquery/go.mod b/instrumentation/csec_antchfx_htmlquery/go.mod index 26eff96..ccc9691 100644 --- a/instrumentation/csec_antchfx_htmlquery/go.mod +++ b/instrumentation/csec_antchfx_htmlquery/go.mod @@ -3,5 +3,5 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_antchfx_htmlquery go 1.17 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 ) diff --git a/instrumentation/csec_antchfx_jsonquery/go.mod b/instrumentation/csec_antchfx_jsonquery/go.mod index 76a339e..5e74eec 100644 --- a/instrumentation/csec_antchfx_jsonquery/go.mod +++ b/instrumentation/csec_antchfx_jsonquery/go.mod @@ -3,5 +3,5 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_antchfx_jsonquery go 1.17 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 ) diff --git a/instrumentation/csec_antchfx_xmlquery/go.mod b/instrumentation/csec_antchfx_xmlquery/go.mod index 1140204..83c00ef 100644 --- a/instrumentation/csec_antchfx_xmlquery/go.mod +++ b/instrumentation/csec_antchfx_xmlquery/go.mod @@ -3,5 +3,5 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_antchfx_xmlquery go 1.17 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 ) diff --git a/instrumentation/csec_antchfx_xpath/go.mod b/instrumentation/csec_antchfx_xpath/go.mod index dfef5dd..3244225 100644 --- a/instrumentation/csec_antchfx_xpath/go.mod +++ b/instrumentation/csec_antchfx_xpath/go.mod @@ -3,5 +3,5 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_antchfx_xpath go 1.16 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 ) diff --git a/instrumentation/csec_augustoroman_v8/go.mod b/instrumentation/csec_augustoroman_v8/go.mod index 77484a4..6344a2b 100644 --- a/instrumentation/csec_augustoroman_v8/go.mod +++ b/instrumentation/csec_augustoroman_v8/go.mod @@ -3,5 +3,5 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_augustoroman_v8 go 1.17 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 ) diff --git a/instrumentation/csec_grpc/go.mod b/instrumentation/csec_grpc/go.mod index 1fbe3bf..aae9c97 100644 --- a/instrumentation/csec_grpc/go.mod +++ b/instrumentation/csec_grpc/go.mod @@ -3,7 +3,7 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_grpc go 1.17 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 google.golang.org/grpc v1.58.3 google.golang.org/protobuf v1.31.0 github.com/golang/protobuf v1.5.3 diff --git a/instrumentation/csec_grpc/main.go b/instrumentation/csec_grpc/main.go index 7eb3d90..48a5bb3 100644 --- a/instrumentation/csec_grpc/main.go +++ b/instrumentation/csec_grpc/main.go @@ -51,11 +51,12 @@ func (k *SecGrpcServe) secServe(l net.Listener) error { } func init() { + secIntercept.InitGrpsFuzzRestClient(SecGrpcFuzz{}) + if !secIntercept.IsAgentInitializedForHook() || secIntercept.IsForceDisable() || !secIntercept.IsHookingoIsSupported() { return } e := secIntercept.HookWrapInterface((*grpc.Server).Serve, (*SecGrpcServe).secServe, (*SecGrpcServe).secServe_s) secIntercept.IsHookedLog("(*grpc.Server).Serve", e) - secIntercept.InitGrpsFuzzRestClient(SecGrpcFuzz{}) } diff --git a/instrumentation/csec_grpc/sec_grpc_fuzz.go b/instrumentation/csec_grpc/sec_grpc_fuzz.go index 7310002..4770196 100644 --- a/instrumentation/csec_grpc/sec_grpc_fuzz.go +++ b/instrumentation/csec_grpc/sec_grpc_fuzz.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + secUtils "github.com/newrelic/csec-go-agent/internal/security_utils" secConfig "github.com/newrelic/csec-go-agent/security_config" secevent "github.com/newrelic/csec-go-agent/security_event_generation" sechandler "github.com/newrelic/csec-go-agent/security_handlers" @@ -24,11 +25,12 @@ type SecGrpcFuzz struct { func (grpcFuzz SecGrpcFuzz) ExecuteFuzzRequest(fuzzRequest *sechandler.FuzzRequrestHandler, caseType string, fuzzId string) { fuzzRequestID := fmt.Sprintf("%v", fuzzRequest.Headers[secIntercept.NR_CSEC_FUZZ_REQUEST_ID]) - + sechandler.FuzzHandler.AppendCompletedRequestIds(fuzzId, "") var grpcBody []interface{} err := json.Unmarshal([]byte(fuzzRequest.Body), &grpcBody) if err != nil { logger.Debugln("ERROR: error in unmarshal gRPC body : ", err.Error(), fuzzRequest.Body) + secIntercept.SendLogMessage("ERROR: error in unmarshal gRPC body : "+err.Error(), "csec_grpc") secevent.SendFuzzFailEvent(fuzzRequestID) return } @@ -55,6 +57,7 @@ func (grpcFuzz SecGrpcFuzz) ExecuteFuzzRequest(fuzzRequest *sechandler.FuzzRequr if err != nil { logger.Errorln("ERROR: Failed to create fuzz client : ", secConfig.GlobalInfo.ApplicationInfo.ServerIp, gPort, err.Error()) + secIntercept.SendLogMessage("ERROR: Failed to create fuzz client : "+secConfig.GlobalInfo.ApplicationInfo.ServerIp+gPort+err.Error(), "csec_grpc") secevent.SendFuzzFailEvent(fuzzRequestID) } @@ -63,7 +66,6 @@ func (grpcFuzz SecGrpcFuzz) ExecuteFuzzRequest(fuzzRequest *sechandler.FuzzRequr url = url[1:] } - sechandler.FuzzHandler.AppendCompletedRequestIds(fuzzId, "") tmp := fmt.Sprintf("%s: %s", "nr-csec-parent-id", fuzzId) headers = append(headers, tmp) @@ -89,7 +91,7 @@ func (grpcFuzz SecGrpcFuzz) ExecuteFuzzRequest(fuzzRequest *sechandler.FuzzRequr } func getFuzzClient(protocol, url, serverName string) (*grpc.ClientConn, error) { - if protocol == "https" { + if secUtils.CaseInsensitiveEquals(protocol, "https") { return getHttpsClient(url, serverName) } else { return getHttpClient(url) diff --git a/instrumentation/csec_ldap_v3/go.mod b/instrumentation/csec_ldap_v3/go.mod index b4e2093..4df2d8f 100644 --- a/instrumentation/csec_ldap_v3/go.mod +++ b/instrumentation/csec_ldap_v3/go.mod @@ -2,5 +2,5 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_ldap_v3 go 1.17 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 ) \ No newline at end of file diff --git a/instrumentation/csec_mongodb_mongo/csec_mongodb_mongo_test.go b/instrumentation/csec_mongodb_mongo/csec_mongodb_mongo_test.go index 2a732a9..02e7b0b 100644 --- a/instrumentation/csec_mongodb_mongo/csec_mongodb_mongo_test.go +++ b/instrumentation/csec_mongodb_mongo/csec_mongodb_mongo_test.go @@ -33,7 +33,6 @@ func TestMongoInsertOneHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -63,7 +62,6 @@ func TestMongoInsertManyHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -92,7 +90,6 @@ func TestMongoFindHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -130,7 +127,6 @@ func TestMongoFindOneHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -163,7 +159,6 @@ func TestMongoFindOneAndReplaceHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -192,7 +187,6 @@ func TestMongoFindOneAndUpdateHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -222,7 +216,6 @@ func TestMongoFindOneAndDeleteHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -251,7 +244,6 @@ func TestMongoUpdateOneHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -280,7 +272,6 @@ func TestMongoUpdateManyHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -310,7 +301,6 @@ func TestMongoReplaceOneHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -340,7 +330,6 @@ func TestMongoDeleteOneHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll @@ -369,7 +358,6 @@ func TestMongoDeleteManyHook(t *testing.T) { secConfig.RegisterListener() mt := ConnectMongoDB(t) - defer mt.Close() mt.Run("success", func(mt *mtest.T) { userCollection = mt.Coll diff --git a/instrumentation/csec_mongodb_mongo/go.mod b/instrumentation/csec_mongodb_mongo/go.mod index 269b585..58bab84 100644 --- a/instrumentation/csec_mongodb_mongo/go.mod +++ b/instrumentation/csec_mongodb_mongo/go.mod @@ -2,5 +2,5 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_mongodb_mongo go 1.16 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 ) \ No newline at end of file diff --git a/instrumentation/csec_robertkrimen_otto/go.mod b/instrumentation/csec_robertkrimen_otto/go.mod index 8d412ef..23b2d56 100644 --- a/instrumentation/csec_robertkrimen_otto/go.mod +++ b/instrumentation/csec_robertkrimen_otto/go.mod @@ -2,5 +2,5 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_robertkrimen_otto go 1.17 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 ) \ No newline at end of file diff --git a/instrumentation/csec_valyala_fasthttp/go.mod b/instrumentation/csec_valyala_fasthttp/go.mod index b6453b5..1bc5b3f 100644 --- a/instrumentation/csec_valyala_fasthttp/go.mod +++ b/instrumentation/csec_valyala_fasthttp/go.mod @@ -3,5 +3,5 @@ module github.com/newrelic/csec-go-agent/instrumentation/csec_valyala_fasthttp go 1.17 require ( - github.com/newrelic/csec-go-agent v0.5.1 + github.com/newrelic/csec-go-agent v0.6.0 ) \ No newline at end of file diff --git a/internal/security_logs/initLogging.go b/internal/security_logs/initLogging.go index 49e4b37..2381f92 100644 --- a/internal/security_logs/initLogging.go +++ b/internal/security_logs/initLogging.go @@ -12,7 +12,7 @@ var initLogger = DefaultLogger(true) func init_initLogger(initlogFileName, logFilepath string, pid int) { - rotateFileHook, writer, err := NewRotateFileHook(RotateFileConfig{ + rotateFileHook, writer, isDefault, _ := NewRotateFileHook(RotateFileConfig{ Filename: filepath.Join(logFilepath, initlogFileName), Filepath: logFilepath, MaxSize: 50, // megabytes @@ -20,7 +20,7 @@ func init_initLogger(initlogFileName, logFilepath string, pid int) { BaseLogFilename: initlogFileName, }) - UpdateLogger(writer, "INFO", pid, initLogger, rotateFileHook, err) + UpdateLogger(writer, "INFO", pid, initLogger, rotateFileHook, isDefault) } func InitLogger() *logFile { diff --git a/internal/security_logs/logger.go b/internal/security_logs/logger.go index 0d7049b..9a02e77 100644 --- a/internal/security_logs/logger.go +++ b/internal/security_logs/logger.go @@ -46,10 +46,12 @@ func DefaultLogger(iscache1 bool) *logFile { func (f *logFile) fire(level string, msg ...interface{}) { logm := fmt.Sprintln(msg...) - if level == "ERROR" { errLevel := fmt.Sprintf("\x1b[%dm%s\x1b[0m", 31, "ERROR") logm = fmt.Sprintf(" [%s] %s", errLevel, logm) + } else if level == "WARN" { + errLevel := fmt.Sprintf("\x1b[%dm%s\x1b[0m", 33, "WARN") + logm = fmt.Sprintf(" [%s] %s", errLevel, logm) } else { logm = fmt.Sprintf(" [%s] %s", level, logm) } @@ -104,6 +106,6 @@ func (f *logFile) cleanCache() { f.cache = make([]interface{}, 0) } -func (f *logFile) IsDebug() bool{ +func (f *logFile) IsDebug() bool { return f.isDebugMode -} \ No newline at end of file +} diff --git a/internal/security_logs/logging.go b/internal/security_logs/logging.go index c306a15..64efb1c 100644 --- a/internal/security_logs/logging.go +++ b/internal/security_logs/logging.go @@ -16,9 +16,9 @@ var agentLogger = DefaultLogger(false) var isInitilized = false var errorBuffer = secUtils.NewCring(5) -func Init(logFileName, initlogFileName, logFilepath string, pid int) { +func Init(logFileName, initlogFileName, logFilepath string, pid int) error { isInitilized = true - rotateFileHook, writer, err := NewRotateFileHook(RotateFileConfig{ + rotateFileHook, writer, isDefault, err := NewRotateFileHook(RotateFileConfig{ Filename: filepath.Join(logFilepath, logFileName), Filepath: logFilepath, MaxSize: 50, // megabytes @@ -26,9 +26,10 @@ func Init(logFileName, initlogFileName, logFilepath string, pid int) { BaseLogFilename: logFileName, }) - UpdateLogger(writer, "INFO", pid, agentLogger, rotateFileHook, err) + UpdateLogger(writer, "INFO", pid, agentLogger, rotateFileHook, isDefault) init_initLogger(initlogFileName, logFilepath, pid) + return err } func SetLogLevel(level string) { diff --git a/internal/security_logs/rotateFileHook.go b/internal/security_logs/rotateFileHook.go index 52589f4..c3662c9 100644 --- a/internal/security_logs/rotateFileHook.go +++ b/internal/security_logs/rotateFileHook.go @@ -57,20 +57,20 @@ func (config *RotateFileConfig) createLogDir() (io.Writer, error) { } -func NewRotateFileHook(config RotateFileConfig) (*RotateFileHook, io.Writer, bool) { +func NewRotateFileHook(config RotateFileConfig) (*RotateFileHook, io.Writer, bool, error) { logfile, err := config.createLogDir() - idDefault := false + isDefault := false if err != nil { fmt.Println(err) logfile = os.Stdout - idDefault = true + isDefault = true } hook := RotateFileHook{ Config: config, } - return &hook, logfile, idDefault + return &hook, logfile, isDefault, err } func (hook *RotateFileHook) Fire(logMessege, mode string, isDefault bool) string { diff --git a/internal/security_utils/config.go b/internal/security_utils/config.go index 707714b..c0796c6 100644 --- a/internal/security_utils/config.go +++ b/internal/security_utils/config.go @@ -4,8 +4,8 @@ package security_utils const ( - CollectorVersion = "0.5.1" - JsonVersion = "1.1.0" + CollectorVersion = "0.6.0" + JsonVersion = "1.1.1" CollectorType = "GOLANG" - BuildNumber = "154" + BuildNumber = "155" ) diff --git a/internal/security_utils/cring.go b/internal/security_utils/cring.go index c8c77cc..62d4b21 100644 --- a/internal/security_utils/cring.go +++ b/internal/security_utils/cring.go @@ -115,7 +115,7 @@ func (cr *Cring) ForceInsert(astr string) { return } -func (cr *Cring) Get() interface{} { +func (cr *Cring) Get() []interface{} { return cr.data } diff --git a/internal/security_utils/global_utils.go b/internal/security_utils/global_utils.go index 9e182e1..d16e267 100644 --- a/internal/security_utils/global_utils.go +++ b/internal/security_utils/global_utils.go @@ -3,10 +3,13 @@ package security_utils +import "net/http" + const MaxReadBodyLen = 300000 type Info_req struct { ResponseBody string + ResponseHeader http.Header ResponseContentType string GrpcByte [][]byte GrpcBody []interface{} @@ -27,6 +30,10 @@ type ReflectedMetaData struct { GrcpMessageVersion string `json:"grcpMessageVersion"` } +type ResponseInfo struct { + ContentType string `json:"contentType"` +} + type RequestInfo struct { Body string `json:"body"` Headers map[string]string `json:"headers"` diff --git a/internal/security_utils/string_utils.go b/internal/security_utils/string_utils.go index a868d38..2117d2b 100644 --- a/internal/security_utils/string_utils.go +++ b/internal/security_utils/string_utils.go @@ -42,6 +42,15 @@ func ContainsInArray(s []string, substr string) bool { return false } +func StartWithInArray(s []string, substr string) bool { + for arr := range s { + if strings.HasPrefix(substr, s[arr]) { + return true + } + } + return false +} + func CheckGrpcByte(a [][]byte, b []byte) bool { for x := range a { // In Grpc Handling first 5 bytes used to identify length of data stream. diff --git a/internal/security_utils/xss_validation.go b/internal/security_utils/xss_validation.go index aaa25d4..1e0212e 100644 --- a/internal/security_utils/xss_validation.go +++ b/internal/security_utils/xss_validation.go @@ -46,27 +46,52 @@ const ( ) func IsContentTypeSupported(type1 string) bool { - supportedContentType := []string{ - "text/css", - "text/csv", - "text/html", - "text/javascript", - "application/json", - "application/ld+json", - "text/javascript", - "application/vnd.oasis.opendocument.text", - "application/x-httpd-php", - "application/rtf", - "image/svg+xml", - "text/plain", - "application/xhtml+xml", - "application/xml", - "multipart/form-data", - "application/x-www-form-urlencoded", + unSupportedMediaType := []string{ + "video/", + "image/", + "font/", + "audio/", + } + + unSupportedContentType := []string{ + "application/zip", + "application/epub+zip", + "application/gzip", + "application/java-archive", + "application/msword", "application/octet-stream", + "application/ogg", + "application/pdf", + "application/rtf", + "application/vnd.amazon.ebook", + "application/vnd.apple.installer+xml", + "application/vnd.ms-excel", + "application/vnd.ms-fontobject", + "application/vnd.ms-powerpoint", + "application/vnd.oasis.opendocument.presentation", + "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.oasis.opendocument.text", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.rar", + "application/vnd.visio", + "application/x-7z-compressed", + "application/x-abiword", + "application/x-bzip", + "application/x-bzip2", + "application/x-cdf", + "application/x-freearc", + "application/x-tar", + "application/zip", + "text/calendar", + } + + if StartWithInArray(unSupportedMediaType, strings.ToLower(type1)) { + return false } - return ContainsInArray(supportedContentType, strings.ToLower(type1)) + return !ContainsInArray(unSupportedContentType, strings.ToLower(type1)) } var tagNameRegex = regexp.MustCompile(`(?ims)<([a-zA-Z_\\-]+[0-9]*|!--)`) diff --git a/security_config/global_config.go b/security_config/global_config.go index 487dbe7..62dfd40 100644 --- a/security_config/global_config.go +++ b/security_config/global_config.go @@ -135,9 +135,22 @@ type metaData struct { accountID string agentRunId string entityGuid string + entityName string sync.Mutex } +func (m *metaData) GetEntityName() string { + m.Lock() + defer m.Unlock() + return m.entityName +} + +func (m *metaData) SetEntityName(value string) { + m.Lock() + defer m.Unlock() + m.entityName = value +} + func (m *metaData) GetEntityGuid() string { m.Lock() defer m.Unlock() diff --git a/security_config/secure_config.go b/security_config/secure_config.go index 59eab96..6372c1b 100644 --- a/security_config/secure_config.go +++ b/security_config/secure_config.go @@ -21,7 +21,7 @@ type Security struct { Enabled bool `json:"enabled"` } `json:"deserialization"` } `json:"detection"` - SecurityHomePath string // SecurityHomePath not part of user config file default is pwd + SecurityHomePath string `json:"-"` // SecurityHomePath not part of user config file default is pwd Request struct { BodyLimit int `yaml:"body_limit"` } `yaml:"request"` diff --git a/security_event_generation/event_generation.go b/security_event_generation/event_generation.go index 6036f05..c2a8714 100644 --- a/security_event_generation/event_generation.go +++ b/security_event_generation/event_generation.go @@ -36,11 +36,13 @@ var ( HcBuffer *secUtils.Cring logger = logging.GetLogger("hook") removeChannel = make(chan string) + errorBuffer = secUtils.NewCring(5) ) func InitHcScheduler() { logging.EndStage("5", "Security agent components started") SendSecHealthCheck() + sendBufferLogMessage() t := time.NewTicker(5 * time.Minute) for { select { @@ -261,7 +263,7 @@ func SendVulnerableEvent(req *secUtils.Info_req, category string, args interface tmp_event.EventGenerationTime = strconv.FormatInt(time.Now().Unix()*1000, 10) tmp_event.BlockingProcessingTime = "1" tmp_event.HTTPRequest = req.Request - + tmp_event.HTTPResponse = secUtils.ResponseInfo{ContentType: req.ResponseContentType} if req.Request.BodyReader.GetBody != nil { tmp_event.HTTPRequest.Body = string(req.Request.BodyReader.GetBody()) } @@ -372,6 +374,38 @@ func IASTDataRequest(batchSize int, completedRequestIds interface{}, pendingRequ } } +func sendBufferLogMessage() { + i := errorBuffer.Remove() + logger.Debugln("sendBufferLogMessage") + for i != nil { + if secConfig.SecureWS != nil { + if logger.IsDebug() { + logger.Debugln("ready to send : ", string(i.([]byte))) + } + (secConfig.SecureWS).RegisterEvent(i.([]byte), "", "LogMessage") + } + i = errorBuffer.Remove() + } +} + +func SendLogMessage(log, caller string) { + var tmp_event LogMessage + + tmp_event.ApplicationUUID = secConfig.GlobalInfo.ApplicationInfo.GetAppUUID() + tmp_event.JSONName = "critical-messages" + tmp_event.Timestamp = time.Now().Unix() * 1000 + tmp_event.Level = "SEVERE" + tmp_event.Message = log + tmp_event.Caller = caller + tmp_event.Exception = Exception{Message: log} + tmp_event.ThreadName = caller + tmp_event.LinkingMetadata = secConfig.GlobalInfo.MetaData.GetLinkingMetadata() + _, err := sendEvent(tmp_event, "", "LogMessage") + if err != nil { + logger.Debugln("ERROR: ", err.Error()) + } +} + func sendEvent(event interface{}, eventID, eventType string) ([]byte, error) { event_json, err := json.Marshal(event) if err != nil { @@ -385,8 +419,12 @@ func sendEvent(event interface{}, eventID, eventType string) ([]byte, error) { (secConfig.SecureWS).RegisterEvent(event_json, eventID, eventType) return event_json, nil } else { - logger.Errorln("websocket not configured to send event") - return event_json, errors.New("websocket not configured to send event") + if eventType == "LogMessage" { + errorBuffer.Add(event_json) + } else { + logger.Errorln("websocket not initialized to send event", string(event_json)) + } + return event_json, errors.New("websocket not initialized to send event") } } diff --git a/security_event_generation/event_generation_utils.go b/security_event_generation/event_generation_utils.go index 25b5878..2b538d2 100644 --- a/security_event_generation/event_generation_utils.go +++ b/security_event_generation/event_generation_utils.go @@ -43,18 +43,19 @@ const statusTemplate = "Snapshot timestamp: : %s \n" + type eventJson struct { ApplicationIdentifiers - Parameters interface{} `json:"parameters"` - EventGenerationTime string `json:"eventGenerationTime"` - HTTPRequest secUtils.RequestInfo `json:"httpRequest"` - ID string `json:"id"` - CaseType string `json:"caseType"` - EventCategory string `json:"eventCategory"` - MetaData metaData `json:"metaData"` - BlockingProcessingTime string `json:"blockingProcessingTime"` - IsAPIBlocked bool `json:"isAPIBlocked"` - IsIASTEnable bool `json:"isIASTEnable"` - ParentId string `json:"parentId"` - IsIASTRequest bool `json:"isIASTRequest"` + Parameters interface{} `json:"parameters"` + EventGenerationTime string `json:"eventGenerationTime"` + HTTPRequest secUtils.RequestInfo `json:"httpRequest"` + HTTPResponse secUtils.ResponseInfo `json:"httpResponse"` + ID string `json:"id"` + CaseType string `json:"caseType"` + EventCategory string `json:"eventCategory"` + MetaData metaData `json:"metaData"` + BlockingProcessingTime string `json:"blockingProcessingTime"` + IsAPIBlocked bool `json:"isAPIBlocked"` + IsIASTEnable bool `json:"isIASTEnable"` + ParentId string `json:"parentId"` + IsIASTRequest bool `json:"isIASTRequest"` secUtils.VulnerabilityDetails } @@ -133,7 +134,7 @@ type Exitevent struct { ApplicationIdentifiers ExecutionId string `json:"executionId"` CaseType string `json:"caseType"` - RequestIdentifier string `json:"RequestIdentifier"` + RequestIdentifier string `json:"k2RequestIdentifier"` } type ApplicationIdentifiers struct { @@ -178,6 +179,24 @@ type Urlmappings struct { Handler string `json:"handler"` } +type LogMessage struct { + JSONName string `json:"jsonName"` + ApplicationUUID string `json:"applicationUUID"` + Timestamp int64 `json:"timestamp"` + Level string `json:"level"` + Message string `json:"message"` + Caller string `json:"caller"` + Exception Exception `json:"exception"` + ThreadName string `json:"threadName"` + LinkingMetadata interface{} `json:"linkingMetadata"` +} + +type Exception struct { + Message string `json:"message"` + Cause interface{} `json:"cause"` + StackTrace []string `json:"stackTrace"` +} + //status utils function ///// @@ -208,17 +227,20 @@ func populateStatusLogs(service, process map[string]interface{}) { statusFilePath := filepath.Join(secConfig.GlobalInfo.SecurityHomePath(), "nr-security-home", "logs", "snapshots") err := os.MkdirAll(statusFilePath, os.ModePerm) if err != nil { + SendLogMessage(err.Error(), "populateStatusLogs") logger.Errorln(err) return } err = os.Chmod(statusFilePath, 0777) if err != nil { + SendLogMessage(err.Error(), "populateStatusLogs") logger.Errorln(err) return } statusFilePath1 := filepath.Join(statusFilePath, fmt.Sprintf("go-security-collector-status-%s.log", secConfig.GlobalInfo.ApplicationInfo.GetAppUUID())) f, err := os.OpenFile(statusFilePath1, os.O_RDWR|os.O_CREATE, 0777) if err != nil { + SendLogMessage(err.Error(), "populateStatusLogs") logger.Errorln(err) return } diff --git a/security_handlers/control_command_handler.go b/security_handlers/control_command_handler.go index 38fcd7e..fd95646 100644 --- a/security_handlers/control_command_handler.go +++ b/security_handlers/control_command_handler.go @@ -49,7 +49,7 @@ func parseControlCommand(arg []byte) (error, bool) { if err != nil { logger.Errorln("Unable to unmarshall cc ", err) - return errors.New("unable to unmarshall cc "), false + return errors.New("unable to unmarshall cc " + err.Error()), false } logger.Debugln("Received control command", cc.ControlCommand) diff --git a/security_handlers/fuzz_request_handler.go b/security_handlers/fuzz_request_handler.go index 00fe315..abc5053 100644 --- a/security_handlers/fuzz_request_handler.go +++ b/security_handlers/fuzz_request_handler.go @@ -35,14 +35,18 @@ func (fTask *FuzzTask) Run() { if fTask.fuzzRequrestHandler.IsGRPC { if FuzzHandler.grpsFuzzRestClient == nil { + eventGeneration.SendLogMessage("gRPC rest client not initialised", "security_handlers") logger.Errorln("gRPC rest client not initialised") + FuzzHandler.AppendCompletedRequestIds(fTask.requestID, "") } else { FuzzHandler.grpsFuzzRestClient.ExecuteFuzzRequest(fTask.fuzzRequrestHandler, fTask.caseType, fTask.requestID) FuzzHandler.RemovePendingRequestIds(fTask.requestID) } } else { if FuzzHandler.httpFuzzRestClient == nil { + eventGeneration.SendLogMessage("http rest client not initialised", "security_handlers") logger.Errorln("http rest client not initialised") + FuzzHandler.AppendCompletedRequestIds(fTask.requestID, "") } else { FuzzHandler.httpFuzzRestClient.ExecuteFuzzRequest(fTask.fuzzRequrestHandler, fTask.caseType, fTask.requestID) FuzzHandler.RemovePendingRequestIds(fTask.requestID) diff --git a/security_handlers/web_socket.go b/security_handlers/web_socket.go index 8ccf083..f1d3048 100644 --- a/security_handlers/web_socket.go +++ b/security_handlers/web_socket.go @@ -219,7 +219,7 @@ func (ws *websocket) closeWs() { func (ws *websocket) RegisterEvent(s []byte, eventID string, eventType string) { increaseEventProcessed(eventType) - if !ws.isWsConnected() { + if !ws.isWsConnected() && eventType != "LogMessage" { increaseEventDropCount(eventType) logger.Debugln("Drop event WS not connected or Reconnecting", len(ws.eventBuffer), cap(ws.eventBuffer)) return @@ -362,6 +362,7 @@ func readThread(ws *websocket) { } err, _ = parseControlCommand(buf) if err != nil { + eventGeneration.SendLogMessage("Unable to unmarshall control command"+err.Error(), "security_handlers") logger.Errorln("Unable to unmarshall cc ", err) } } @@ -372,7 +373,7 @@ func getConnectionHeader() http.Header { return http.Header{ "NR-CSEC-CONNECTION-TYPE": []string{"LANGUAGE_COLLECTOR"}, "NR-LICENSE-KEY": []string{secConfig.GlobalInfo.ApplicationInfo.GetApiAccessorToken()}, - "NR-AGENT-RUN-TOKEN": []string{secConfig.GlobalInfo.MetaData.GetAccountID()}, + "NR-AGENT-RUN-TOKEN": []string{secConfig.GlobalInfo.MetaData.GetAgentRunId()}, "NR-CSEC-VERSION": []string{secUtils.CollectorVersion}, "NR-CSEC-COLLECTOR-TYPE": []string{secUtils.CollectorType}, "NR-CSEC-MODE": []string{secConfig.GlobalInfo.SecurityMode()}, @@ -382,6 +383,7 @@ func getConnectionHeader() http.Header { "NR-ACCOUNT-ID": []string{secConfig.GlobalInfo.MetaData.GetAccountID()}, "NR-CSEC-IAST-DATA-TRANSFER-MODE": []string{"PULL"}, "NR-CSEC-ENTITY-GUID": []string{secConfig.GlobalInfo.MetaData.GetEntityGuid()}, + "NR-CSEC-ENTITY-NAME": []string{secConfig.GlobalInfo.MetaData.GetEntityName()}, } } diff --git a/security_initialization.go b/security_initialization.go index 733300b..d15ace7 100644 --- a/security_initialization.go +++ b/security_initialization.go @@ -14,6 +14,7 @@ import ( logging "github.com/newrelic/csec-go-agent/internal/security_logs" secUtils "github.com/newrelic/csec-go-agent/internal/security_utils" secConfig "github.com/newrelic/csec-go-agent/security_config" + secIntercept "github.com/newrelic/csec-go-agent/security_intercept" ) var logger logging.Logger @@ -40,13 +41,15 @@ func initLogger(logFilePath string, isDebugLog bool) { if isDebugLog { logLevel = "DEBUG" } - logging.Init(LOG_FILE, INIT_LOG_FILE, logFilePath, os.Getpid()) + err := logging.Init(LOG_FILE, INIT_LOG_FILE, logFilePath, os.Getpid()) + if err != nil { + secIntercept.SendLogMessage(err.Error(), "logging") + } logging.SetLogLevel(logLevel) logger = logging.GetLogger("Init") } func initApplicationInfo(appName string) { - secConfig.GlobalInfo.ApplicationInfo.SetAppUUID(secUtils.GetUniqueUUID()) secConfig.GlobalInfo.ApplicationInfo.SetAppName(appName) secConfig.GlobalInfo.ApplicationInfo.SetPid(secUtils.IntToString(os.Getpid())) binaryPath, err := os.Executable() @@ -111,6 +114,7 @@ func initSecurityAgent(applicationName, licenseKey string, isDebugLog bool, secu if secConfig.GlobalInfo.IsForceDisable() { return } + secConfig.GlobalInfo.ApplicationInfo.SetAppUUID(secUtils.GetUniqueUUID()) secConfig.GlobalInfo.SetSecurity(securityAgentConfig) secConfig.GlobalInfo.ApplicationInfo.SetApiAccessorToken(licenseKey) secConfig.GlobalInfo.SetSecurityHomePath(secUtils.GetCurrentWorkingDir()) diff --git a/security_instrumentation/sec_httpfuzz.go b/security_instrumentation/sec_httpfuzz.go index 4ff5c96..e73e847 100644 --- a/security_instrumentation/sec_httpfuzz.go +++ b/security_instrumentation/sec_httpfuzz.go @@ -13,6 +13,7 @@ import ( "sync" "time" + secUtils "github.com/newrelic/csec-go-agent/internal/security_utils" secConfig "github.com/newrelic/csec-go-agent/security_config" eventGeneration "github.com/newrelic/csec-go-agent/security_event_generation" sechandler "github.com/newrelic/csec-go-agent/security_handlers" @@ -59,7 +60,7 @@ func (httpFuzz SecHttpFuzz) ExecuteFuzzRequest(fuzzRequest *sechandler.FuzzRequr fuzzRequestURL := secConfig.GlobalInfo.ApplicationInfo.GetServerIp() + applicationPort + fuzzRequest.Url var fuzzRequestClient *http.Client - if fuzzRequest.Protocol == "https" { + if secUtils.CaseInsensitiveEquals(fuzzRequest.Protocol, "https") { fuzzRequestURL = HTTPS + fuzzRequestURL fuzzRequestClient = getHttpsClient() fuzzRequestClient.Transport = getTransport(fuzzRequest.ServerName) @@ -67,21 +68,15 @@ func (httpFuzz SecHttpFuzz) ExecuteFuzzRequest(fuzzRequest *sechandler.FuzzRequr fuzzRequestURL = HTTP + fuzzRequestURL fuzzRequestClient = getHttpClient() } - + sechandler.FuzzHandler.AppendCompletedRequestIds(fuzzId, "") var req *http.Request = nil var err error = nil - switch fuzzRequest.Method { - - case GET: - req, err = http.NewRequest(GET, fuzzRequestURL, nil) - case POST: - req, err = http.NewRequest(POST, fuzzRequestURL, strings.NewReader(fuzzRequest.Body)) - default: - logger.Errorln("Unimplemented request type", fuzzRequest.Method) - } + req, err = http.NewRequest(fuzzRequest.Method, fuzzRequestURL, strings.NewReader(fuzzRequest.Body)) if req == nil || err != nil { eventGeneration.SendFuzzFailEvent(fuzzRequestID) + logger.Infoln("ERROR: request type", fuzzRequest.Method, err) + secIntercept.SendLogMessage(err.Error(), "security_instrumentation") return } req.URL.RawQuery = req.URL.Query().Encode() @@ -90,7 +85,6 @@ func (httpFuzz SecHttpFuzz) ExecuteFuzzRequest(fuzzRequest *sechandler.FuzzRequr value := fmt.Sprintf("%v", headerValue) req.Header.Set(headerKey, value) } - sechandler.FuzzHandler.AppendCompletedRequestIds(fuzzId, "") req.Header.Set("Content-Type", fuzzRequest.ContentType) req.Header.Set("nr-csec-parent-id", fuzzId) @@ -100,6 +94,7 @@ func (httpFuzz SecHttpFuzz) ExecuteFuzzRequest(fuzzRequest *sechandler.FuzzRequr response, err := fuzzRequestClient.Do(req) if err != nil { logger.Debugln("ERROR: fuzz request fail : ", fuzzRequestID, err.Error()) + secIntercept.SendLogMessage("fuzz request fail :"+err.Error(), "security_instrumentation") eventGeneration.SendFuzzFailEvent(fuzzRequestID) } else { defer response.Body.Close() diff --git a/security_instrumentation/sec_instrumentation_init.go b/security_instrumentation/sec_instrumentation_init.go index d3c4977..15addf1 100644 --- a/security_instrumentation/sec_instrumentation_init.go +++ b/security_instrumentation/sec_instrumentation_init.go @@ -4,6 +4,7 @@ package security_instrumentation import ( + "fmt" "runtime/debug" logging "github.com/newrelic/csec-go-agent/internal/security_logs" @@ -16,16 +17,16 @@ const id = "github.com/newrelic/csec-go-agent" var ( constant = map[string]string{ - "github.com/go-ldap/ldap/v3": id + "/instrumentation/csec_ldap_v3", - "github.com/mongo-driver/mongo": id + "/instrumentation/csec_mongodb_mongo", - "github.com/robertkrimen/otto": id + "/instrumentation/csec_robertkrimen_otto", - "github.com/augustoroman/v8": id + "/instrumentation/csec_augustoroman_v8", - "github.com/antchfx/xpath": id + "/instrumentation/csec_antchfx_xpath", - "github.com/antchfx/xmlquery": id + "/instrumentation/csec_antchfx_xmlquery", - "github.com/antchfx/jsonquery": id + "/instrumentation/csec_antchfx_jsonquery", - "github.com/antchfx/htmlquery": id + "/instrumentation/csec_antchfx_htmlquery", - "google.golang.org/grpc": id + "/instrumentation/csec_grpc", - "github.com/valyala/fasthttp": id + "/instrumentation/csec_valyala_fasthttp", + "github.com/go-ldap/ldap/v3": id + "/instrumentation/csec_ldap_v3", + "go.mongodb.org/mongo-driver/mongo": id + "/instrumentation/csec_mongodb_mongo", + "github.com/robertkrimen/otto": id + "/instrumentation/csec_robertkrimen_otto", + "github.com/augustoroman/v8": id + "/instrumentation/csec_augustoroman_v8", + "github.com/antchfx/xpath": id + "/instrumentation/csec_antchfx_xpath", + "github.com/antchfx/xmlquery": id + "/instrumentation/csec_antchfx_xmlquery", + "github.com/antchfx/jsonquery": id + "/instrumentation/csec_antchfx_jsonquery", + "github.com/antchfx/htmlquery": id + "/instrumentation/csec_antchfx_htmlquery", + "google.golang.org/grpc": id + "/instrumentation/csec_grpc", + "github.com/valyala/fasthttp": id + "/instrumentation/csec_valyala_fasthttp", } ) @@ -40,9 +41,9 @@ func init() { if secIntercept.IsForceDisable() { return } - + locateImports() if secIntercept.IsHookingoIsSupported() { - //locateImports() + locateImports() secIntercept.InitSyms() init_hooks() } @@ -74,7 +75,7 @@ func locateImports() { buildInfo, ok := debug.ReadBuildInfo() // ReadBuildInfo returns the build information embedded in the running binary if buildInfo == nil || !ok { - logger.Infoln("No import found, Please make sure binary built with module support") + logger.Debugln("No import found, Please make sure binary built with module support") return } dependencieMap := make(map[string]string, 0) @@ -84,7 +85,8 @@ func locateImports() { for wrapper, secWrapper := range constant { if _, ok := dependencieMap[wrapper]; ok { if _, ok := dependencieMap[secWrapper]; !ok { - logging.PrintWarnlog("Suggested Sec protect imports :" + secWrapper + " for: " + wrapper) + printlogs := fmt.Sprintf("Warning : Your application seems to be using package %s. Please make sure you import %s package to enable security for package %s.", wrapper, secWrapper, wrapper) + logging.PrintWarnlog(printlogs) } } } diff --git a/security_intercept/intercept.go b/security_intercept/intercept.go index 7df9c24..8c69a66 100644 --- a/security_intercept/intercept.go +++ b/security_intercept/intercept.go @@ -97,7 +97,12 @@ func TraceFileOperation(fname string, flag int, isFileOpen bool) *secUtils.Event } var args []string - args = append(args, fname) + absolutePath, err := fileAbs(fname) + if err != nil { + args = append(args, fname) + } else { + args = append(args, absolutePath) + } if isFileOpen && isFileModified(flag) && fileInApplicationPath(fname) && fileExecByExtension(fname) { return secConfig.Secure.SendEvent("FILE_INTEGRITY", args) } else { @@ -345,7 +350,7 @@ func TraceIncommingRequest(url, host string, hdrMap map[string][]string, method secConfig.Secure.AssociateInboundRequest(infoReq) } -func AssociateResponseBody(body, contentType string) { +func AssociateResponseBody(body, contentType string, header http.Header) { if !isAgentInitialized() { return } @@ -353,6 +358,7 @@ func AssociateResponseBody(body, contentType string) { if r != nil { r.ResponseBody = r.ResponseBody + body r.ResponseContentType = contentType + r.ResponseHeader = header secConfig.Secure.CalculateOutboundApiId() } } @@ -504,43 +510,45 @@ func DissociateInboundRequest() { } func XssCheck() { - if IsRXSSDisable() { - return - } if !isAgentInitialized() { return } r := secConfig.Secure.GetRequest() - if r != nil && r.ResponseBody != "" { - if r.ResponseContentType != "" && !secUtils.IsContentTypeSupported(r.ResponseContentType) { - logger.Debugln("No need to send RXSS event ContentType not supported for rxss event validation", r.ResponseContentType) - return - } + if r != nil { + if r.ResponseBody != "" && !IsRXSSDisable() { - // Double check befor rxss event validation becouse in some case we don't have contentType in response header. - cType := http.DetectContentType([]byte(r.ResponseBody)) - if !secUtils.IsContentTypeSupported(cType) { - logger.Debugln("No need to send RXSS event ContentType not supported for rxss event validation", cType) - return - } - if r.ResponseContentType != "" { - r.ResponseContentType = cType - } + if r.ResponseContentType != "" && !secUtils.IsContentTypeSupported(r.ResponseContentType) { + SendLogMessage("No need to send RXSS event ContentType not supported for rxss event validation "+r.ResponseContentType, "XssCheck") + logger.Debugln("No need to send RXSS event ContentType not supported for rxss event validation", r.ResponseContentType) + return + } - out := secUtils.CheckForReflectedXSS(r) - logger.Debugln("CheckForReflectedXSS out value is : ", out) + // Double check befor rxss event validation becouse in some case we don't have contentType in response header. + cType := http.DetectContentType([]byte(r.ResponseBody)) + if !secUtils.IsContentTypeSupported(cType) { + SendLogMessage("No need to send RXSS event ContentType not supported for rxss event validation "+cType, "XssCheck") + logger.Debugln("No need to send RXSS event ContentType not supported for rxss event validation", cType) + return + } + if r.ResponseContentType == "" { + r.ResponseContentType = cType + } - if len(out) == 0 && !secConfig.GlobalInfo.IsIASTEnable() { - logger.Debugln("No need to send xss event as not attack and dynamic scanning is false") - } else { - logger.Debugln("return value of reflected xss string : ", out) - var arg []string - arg = append(arg, out) - arg = append(arg, r.ResponseBody) - secConfig.Secure.SendEvent("REFLECTED_XSS", arg) + out := secUtils.CheckForReflectedXSS(r) + logger.Debugln("CheckForReflectedXSS out value is : ", out) + + if len(out) == 0 && !secConfig.GlobalInfo.IsIASTEnable() { + logger.Debugln("No need to send xss event as not attack and dynamic scanning is false") + } else { + logger.Debugln("return value of reflected xss string : ", out) + var arg []string + arg = append(arg, out) + arg = append(arg, r.ResponseBody) + secConfig.Secure.SendEvent("REFLECTED_XSS", arg) + } + logger.Debugln("Called check for reflected XSS" + out) } - logger.Debugln("Called check for reflected XSS" + out) } } @@ -666,7 +674,10 @@ func UpdateLinkData(linkingMetadata map[string]string) { if ok { secConfig.GlobalInfo.MetaData.SetEntityGuid(entityGuid) } - + entityname, ok := linkingMetadata["entity.name"] + if ok { + secConfig.GlobalInfo.MetaData.SetEntityName(entityname) + } if !IsDisable() && !IsForceDisable() { go secWs.InitializeWsConnecton() } @@ -683,6 +694,10 @@ func UpdateLinkData(linkingMetadata map[string]string) { } +func SendLogMessage(message, caller string) { + eventGeneration.SendLogMessage(message, caller) +} + // security_api handlers func SendEvent(caseType string, data ...interface{}) interface{} { @@ -735,7 +750,7 @@ func SendEvent(caseType string, data ...interface{}) interface{} { func inboundcallHandler(request interface{}) { r, ok := request.(webRequestv2) - if !ok { + if !ok || r == nil { inboundcallHandlerv1(request) return } @@ -755,7 +770,8 @@ func inboundcallHandler(request interface{}) { // merge inboundcallHandler and inboundcallHandlerv1 in the next major release(v1.0.0) func inboundcallHandlerv1(request interface{}) { r, ok := request.(webRequest) - if !ok { + if !ok || r == nil { + SendLogMessage("ERROR: Request is not a type of webRequest and webRequestv2 ", "security_intercept") logger.Errorln("request is not a type of webRequest and webRequestv2 ") return } @@ -777,7 +793,7 @@ func outboundcallHandler(req interface{}) *secUtils.EventTracker { return nil } r, ok := req.(*http.Request) - if !ok || r.URL.String() == "" { + if !ok || r == nil || r.URL == nil || r.URL.String() == "" { return nil } var args []interface{} @@ -797,12 +813,10 @@ func httpresponseHandler(data ...interface{}) { return } contentType := "" - if hdr, ok := header.(http.Header); ok { - for key, values := range hdr { - if secUtils.CaseInsensitiveEquals(key, "content-type") { - contentType = strings.Join(values, ",") - } - } + responseHeader := http.Header{} + if hdr, ok := header.(http.Header); ok && hdr != nil { + contentType = hdr.Get("content-type") + responseHeader = hdr } if contentType != "" && !secUtils.IsContentTypeSupported(contentType) { @@ -818,7 +832,7 @@ func httpresponseHandler(data ...interface{}) { return } - AssociateResponseBody(responseBody, contentType) + AssociateResponseBody(responseBody, contentType, responseHeader) } } @@ -902,19 +916,25 @@ func mongoHandler(data ...interface{}) *secUtils.EventTracker { if !isAgentInitialized() { return nil } + if len(data) < 2 { + return nil + } queryType, ok := data[1].(string) if (ok && queryType == "delete") || !secConfig.GlobalInfo.InstrumentationData.TraceHooksApplied.Mongo { - + if secUtils.CaseInsensitiveEquals(queryType, "findAndModify") { + queryType = "update" + } var arg11 []interface{} var arg12 []interface{} var jsonMap map[string]interface{} arg, ok := data[0].([]byte) - if !ok { + if !ok || arg == nil { return nil } err := json.Unmarshal(arg, &jsonMap) if err != nil { + SendLogMessage("error in Unmarshal mongo arg"+err.Error(), "mongoHandler") logger.Errorln("error in Unmarshal mongo arg", err) return nil } @@ -928,10 +948,12 @@ func mongoHandler(data ...interface{}) *secUtils.EventTracker { } return nil } - func DistributedTraceHeaders(hdrs *http.Request, secureAgentevent interface{}) { - if secureAgentevent != nil { - secEvent := secureAgentevent.(*secUtils.EventTracker) + if secureAgentevent != nil && hdrs != nil { + secEvent, ok := secureAgentevent.(*secUtils.EventTracker) + if !ok || secEvent == nil { + return + } key, value := GetTraceHeader(secEvent) if key != "" { hdrs.Header.Add(key, value) diff --git a/security_intercept/intercept_utils.go b/security_intercept/intercept_utils.go index ba0dea2..3d932b9 100644 --- a/security_intercept/intercept_utils.go +++ b/security_intercept/intercept_utils.go @@ -133,6 +133,18 @@ func fileExecByExtension(fn string) bool { return false } +// fileAbs return the file absolute path without clean process +func fileAbs(path string) (string, error) { + if filepath.IsAbs(path) { + return path, nil + } + wd, err := os.Getwd() + if err != nil { + return "", err + } + return strings.Join([]string{wd, path}, string(os.PathSeparator)), nil +} + // // fileExecByExtension is used to check opened file is elf binary or not // func fileIsBinaryExec(fn string) bool { // f, err := os.Open(fn)