diff --git a/internal/security_utils/global_utils.go b/internal/security_utils/global_utils.go index 157fd16..11d2fd7 100644 --- a/internal/security_utils/global_utils.go +++ b/internal/security_utils/global_utils.go @@ -3,18 +3,14 @@ package security_utils -import "net/http" - const MaxReadBodyLen = 300000 type Info_req struct { - ResponseBody string - ResponseHeader http.Header - ResponseContentType string GrpcBody []interface{} ReqTraceData string RequestIdentifier NrRequestIdentifier Request RequestInfo + Response ResponseInfo VulnerabilityDetails VulnerabilityDetails ReflectedMetaData ReflectedMetaData ParentID string @@ -30,7 +26,11 @@ type ReflectedMetaData struct { } type ResponseInfo struct { - ContentType string `json:"contentType"` + ContentType string `json:"contentType"` + StatusCode int `json:"statusCode"` + Headers map[string]string `json:"headers"` + Body string `json:"body"` + HeadersMap map[string][]string `json:"-"` } type RequestInfo struct { diff --git a/internal/security_utils/xss_validation.go b/internal/security_utils/xss_validation.go index 401e7ad..8055061 100644 --- a/internal/security_utils/xss_validation.go +++ b/internal/security_utils/xss_validation.go @@ -344,7 +344,7 @@ func decodeRequestData(rq *Info_req) []string { func decodeResponseData(rq *Info_req) []string { var processedData []string - decodedBodyValue := processURLEncodedDataForXSS(rq.ResponseBody) + decodedBodyValue := processURLEncodedDataForXSS(rq.Response.Body) processedData = append(processedData, decodedBodyValue...) for _, st := range decodedBodyValue { newprocessedBody := safeDecode(st) diff --git a/security_config/global_config.go b/security_config/global_config.go index 1557a2e..5e08fdd 100644 --- a/security_config/global_config.go +++ b/security_config/global_config.go @@ -191,6 +191,12 @@ func (info *Info_struct) ScanScheduleSchedule() string { return info.security.ScanSchedule.Schedule } +func (info *Info_struct) ReportHttpResponseBody() bool { + info.securityMutex.Lock() + defer info.securityMutex.Unlock() + return info.security.ScanControllers.ReportHttpResponseBody +} + func (info *Info_struct) SecurityHomePath() string { return info.security.SecurityHomePath } diff --git a/security_config/limits.go b/security_config/limits.go index 80c83a4..de924d0 100644 --- a/security_config/limits.go +++ b/security_config/limits.go @@ -3,4 +3,5 @@ package security_config const ( MaxStackTraceFrames = 100 ValidatorDefaultEndpoint = "wss://csec.nr-data.net" + HttpResponseBodyLimit = 500 * 1000 //500kb ) diff --git a/security_config/secure_config.go b/security_config/secure_config.go index 77a41fe..6652c6f 100644 --- a/security_config/secure_config.go +++ b/security_config/secure_config.go @@ -52,7 +52,8 @@ type Security struct { AllowIastSampleCollection bool `yaml:"always_sample_traces"` } `yaml:"scan_schedule"` ScanControllers struct { - IastScanRequestRateLimit int `yaml:"iast_scan_request_rate_limit"` + IastScanRequestRateLimit int `yaml:"iast_scan_request_rate_limit"` + ReportHttpResponseBody bool `yaml:"report_http_response_body: true"` } `yaml:"scan_controllers"` } diff --git a/security_event_generation/event_generation.go b/security_event_generation/event_generation.go index b4ef20b..8aa0aad 100644 --- a/security_event_generation/event_generation.go +++ b/security_event_generation/event_generation.go @@ -323,7 +323,7 @@ func SendVulnerableEvent(req *secUtils.Info_req, category, eventCategory string, tmp_event.VulnerabilityDetails = vulnerabilityDetails tmp_event.ApplicationIdentifiers = getApplicationIdentifiers("Event") tmp_event.EventGenerationTime = strconv.FormatInt(time.Now().Unix()*1000, 10) - tmp_event.HTTPResponse = secUtils.ResponseInfo{ContentType: req.ResponseContentType} + tmp_event.HTTPResponse = req.Response tmp_event.MetaData.AppServerInfo.ApplicationDirectory = secConfig.GlobalInfo.EnvironmentInfo.Wd tmp_event.MetaData.AppServerInfo.ServerBaseDirectory = secConfig.GlobalInfo.EnvironmentInfo.Wd tmp_event.MetaData.SkipScanParameters = secConfig.GlobalInfo.SkipIastScanParameters() @@ -395,6 +395,25 @@ func SendVulnerableEvent(req *secUtils.Info_req, category, eventCategory string, } +func SendResponseEvent(req *secUtils.Info_req) { + + if req != nil && secUtils.CaseInsensitiveEquals(req.RequestIdentifier.NextStage, "VULNERABLE") { + + var tmp_event SecHttpResponse + tmp_event.ApplicationIdentifiers = getApplicationIdentifiers("sec-http-response") + tmp_event.HTTPRequest = nil + tmp_event.HTTPResponse = req.Response + tmp_event.TraceId = req.TraceId + tmp_event.IsIASTRequest = true + tmp_event.LinkingMetadata = copyMap(secConfig.GlobalInfo.MetaData.GetLinkingMetadata()) + tmp_event.LinkingMetadata["trace.id"] = req.TraceId + _, err := sendEvent(tmp_event, "", "sec-http-response") + if err != nil { + logger.Errorln(err) + } + } +} + func SendExitEvent(eventTracker *secUtils.EventTracker, requestIdentifier string) { var tmp_event Exitevent diff --git a/security_event_generation/event_generation_utils.go b/security_event_generation/event_generation_utils.go index 846ce2c..88f84d9 100644 --- a/security_event_generation/event_generation_utils.go +++ b/security_event_generation/event_generation_utils.go @@ -166,6 +166,14 @@ type ApplicationIdentifiers struct { LinkingMetadata map[string]string `json:"linkingMetadata"` } +type SecHttpResponse struct { + ApplicationIdentifiers + TraceId string `json:"traceId"` + HTTPResponse secUtils.ResponseInfo `json:"httpResponse"` + HTTPRequest interface{} `json:"httpRequest"` + IsIASTRequest bool `json:"isIASTRequest"` +} + type FuzzFailBean struct { ApplicationIdentifiers FuzzHeader string `json:"fuzzHeader"` diff --git a/security_intercept/intercept.go b/security_intercept/intercept.go index 661ff6c..8604d8b 100644 --- a/security_intercept/intercept.go +++ b/security_intercept/intercept.go @@ -328,7 +328,7 @@ func TraceIncommingRequest(url, host string, hdrMap map[string][]string, method infoReq.Request.URI = getRequestUri(url) infoReq.TraceId = traceId - if reqtype == "gRPC" { + if reqtype == "gRPC" { infoReq.Request.IsGRPC = true } @@ -336,18 +336,27 @@ func TraceIncommingRequest(url, host string, hdrMap map[string][]string, method } func associateResponseBody(body string) { - r := secConfig.Secure.GetRequest() - if r != nil { - r.ResponseBody = r.ResponseBody + body - secConfig.Secure.CalculateOutboundApiId() + if secConfig.GlobalInfo.ReportHttpResponseBody() { + r := secConfig.Secure.GetRequest() + if r != nil { + if l := len(r.Response.Body); len(body) <= secConfig.HttpResponseBodyLimit-l { + r.Response.Body = r.Response.Body + body + } else if secConfig.HttpResponseBodyLimit-l > 1 { + end := secConfig.HttpResponseBodyLimit - l + r.Response.Body = r.Response.Body + body[:end-1] + } + secConfig.Secure.CalculateOutboundApiId() + } } + } func associateResponseHeader(header http.Header) { r := secConfig.Secure.GetRequest() if r != nil { - r.ResponseHeader = header - r.ResponseContentType = header.Get("content-type") + r.Response.Headers = ToOneValueMap(header) + r.Response.HeadersMap = header + r.Response.ContentType = header.Get("content-type") } } @@ -452,8 +461,9 @@ func traceResponseOperations() { r := secConfig.Secure.GetRequest() if r != nil { - checkSecureCookies(r.ResponseHeader) + checkSecureCookies(r.Response.HeadersMap) xssCheck(r) + eventGeneration.SendResponseEvent(r) } } @@ -484,9 +494,9 @@ func checkSecureCookies(responseHeader http.Header) { } func xssCheck(r *secUtils.Info_req) { - if !IsRxssDisabled() && r.ResponseBody != "" { + if !IsRxssDisabled() && r.Response.Body != "" { - contentType := r.ResponseContentType + contentType := r.Response.ContentType if !secUtils.IsContentTypeSupported(contentType) { SendLogMessage(SKIP_RXSS_EVENT+contentType, "XssCheck", "SEVERE") logger.Debugln(SKIP_RXSS_EVENT, contentType) @@ -494,14 +504,14 @@ func xssCheck(r *secUtils.Info_req) { } // Double check befor rxss event validation becouse in some case we don't have contentType in response header. - cType := http.DetectContentType([]byte(r.ResponseBody)) + cType := http.DetectContentType([]byte(r.Response.Body)) if !secUtils.IsContentTypeSupported(cType) { SendLogMessage(SKIP_RXSS_EVENT+cType, "XssCheck", "SEVERE") logger.Debugln(SKIP_RXSS_EVENT, cType) return } - if r.ResponseContentType == "" { - r.ResponseContentType = cType + if r.Response.ContentType == "" { + r.Response.ContentType = cType } out := secUtils.CheckForReflectedXSS(r) @@ -512,7 +522,7 @@ func xssCheck(r *secUtils.Info_req) { } else { var arg []string arg = append(arg, out) - arg = append(arg, r.ResponseBody) + arg = append(arg, r.Response.Body) secConfig.Secure.SendEvent("REFLECTED_XSS", "REFLECTED_XSS", arg) } }