Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature to send HTTP Response for Reported Vulnerabilities #343

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The agent version.
agentVersion=1.6.0
jsonVersion=1.2.9
jsonVersion=1.2.10
# Updated exposed NR APM API version.
nrAPIVersion=8.12.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityExcepti
import com.newrelic.api.agent.security.utils.logging.LogLevel

import java.lang
import java.util.{HashMap => JavaHashMap}
import scala.concurrent.{ExecutionContext, Future}
import scala.runtime.AbstractFunction1

class AkkaResponseHelper extends AbstractFunction1[HttpResponse, HttpResponse] {
Expand All @@ -25,7 +27,9 @@ class AkkaResponseHelper extends AbstractFunction1[HttpResponse, HttpResponse] {
val stringResponse = new lang.StringBuilder()
val isLockAquired = GenericHelper.acquireLockIfPossible(AkkaCoreUtils.NR_SEC_CUSTOM_ATTRIB_NAME);
stringResponse.append(httpResponse.entity.asInstanceOf[HttpEntity.Strict].getData().decodeString("utf-8"))
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken())
val headers = new JavaHashMap[String, String]()
httpResponse.headers.foreach(header => headers.put(header.name(), header.value()))
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), headers, httpResponse.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken())
} catch {
case t: NewRelicSecurityException =>
NewRelicSecurity.getAgent.log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, AkkaCoreUtils.AKKA_HTTP_10_0_0, t.getMessage), t, classOf[AkkaCoreUtils].getName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.newrelic.api.agent.security.schema.policy.AgentPolicy;
import com.newrelic.api.agent.security.utils.logging.LogLevel;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
Expand All @@ -33,18 +34,18 @@ public static boolean acquireServletLockIfPossible() {
return GenericHelper.acquireLockIfPossible(VulnerabilityCaseType.REFLECTED_XSS, NR_SEC_CUSTOM_ATTRIB_NAME);
}

public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder responseBody, String contentType, String className, String methodName, Token token) {
if(NewRelicSecurity.getAgent().getIastDetectionCategory().getRxssEnabled()){
return;
}
public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder responseBody, String contentType, HashMap<String, String> headers, int responseCode, String className, String methodName, Token token) {
try {
token.linkAndExpire();
ServletHelper.executeBeforeExitingTransaction();
// ServletHelper.executeBeforeExitingTransaction();
if(!isServletLockAcquired || !NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class))){
return;
}
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseContentType(contentType);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseBody(responseBody);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setContentType(contentType);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setHeaders(headers);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setBody(responseBody);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setStatusCode(responseCode);

LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest());

RXSSOperation rxssOperation = new RXSSOperation(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,18 @@ public static boolean acquireServletLockIfPossible() {
return GenericHelper.acquireLockIfPossible(VulnerabilityCaseType.REFLECTED_XSS, NR_SEC_CUSTOM_ATTRIB_NAME);
}

public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder response, String contentType, int responseCode, String className, String methodName, Token token) {
public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder response, String contentType, HashMap<String, String> headers, int responseCode, String className, String methodName, Token token) {
try {
if(NewRelicSecurity.getAgent().getIastDetectionCategory().getRxssEnabled()){
return;
}
token.linkAndExpire();

if(!isServletLockAcquired || !NewRelicSecurity.isHookProcessingActive()){
return;
}
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseContentType(contentType);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseBody(response);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseCode(responseCode);
ServletHelper.executeBeforeExitingTransaction();

NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setContentType(contentType);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setHeaders(headers);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setBody(response);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setStatusCode(responseCode);
// ServletHelper.executeBeforeExitingTransaction();
LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest());

if(!ServletHelper.isResponseContentTypeExcluded(NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().getResponseContentType())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import akka.stream.scaladsl.Sink
import akka.util.ByteString
import com.newrelic.api.agent.security.NewRelicSecurity
import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper
import com.newrelic.api.agent.security.schema.StringUtils
import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException
import com.newrelic.api.agent.security.utils.logging.LogLevel
import com.newrelic.api.agent.{NewRelic, Token}

import java.lang
import java.util.{HashMap => JavaHashMap}
import scala.concurrent.{ExecutionContext, Future}

object ResponseFutureHelper {
Expand All @@ -41,7 +41,9 @@ object ResponseFutureHelper {
processingResult.onComplete {
_ => {
token.linkAndExpire()
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, response.entity.contentType.toString(), response.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken)
val headers = new JavaHashMap[String, String]()
response.headers.foreach(header => headers.put(header.name(), header.value()))
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, response.entity.contentType.toString(), headers, response.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken)
}
}

Expand All @@ -67,8 +69,9 @@ object ResponseFutureHelper {
stringResponse.append(chunk)
}
val processingResult: Future[Done] = dataBytes.runWith(sink, materializer)

AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), httpResponse.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken())
val headers = new JavaHashMap[String, String]()
httpResponse.headers.foreach(header => headers.put(header.name(), header.value()))
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), headers, httpResponse.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken())
} catch {
case t: NewRelicSecurityException =>
NewRelicSecurity.getAgent.log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, AkkaCoreUtils.AKKA_HTTP_CORE_10_0, t.getMessage), t, classOf[AkkaCoreUtils].getName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,15 +233,15 @@ class AkkaHttpCoreTest {

Assert.assertFalse("response should not be empty", operation.getResponse.isEmpty)
Assert.assertEquals("Invalid response content-type.", operation.getResponse.getResponseContentType, contentType)
Assert.assertEquals("Invalid responseBody.", operation.getResponse.getResponseBody.toString, responseBody)
Assert.assertEquals("Invalid responseBody.", operation.getResponse.getBody.toString, responseBody)
}
private def assertMetaData(metaData: SecurityMetaData): Unit = {
Assert.assertFalse("response should not be empty", metaData.getResponse.isEmpty)
Assert.assertEquals("Invalid response content-type.", metaData.getRequest.getContentType, contentType)
Assert.assertEquals("Invalid responseBody.", metaData.getRequest.getBody.toString, requestBody)
Assert.assertFalse("response should not be empty", metaData.getRequest.isEmpty)
Assert.assertEquals("Invalid response content-type.", metaData.getResponse.getResponseContentType, contentType)
Assert.assertEquals("Invalid responseBody.", metaData.getResponse.getResponseBody.toString, responseBody)
Assert.assertEquals("Invalid responseBody.", metaData.getResponse.getBody.toString, responseBody)
Assert.assertEquals("Invalid protocol.", metaData.getRequest.getProtocol, "http")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,18 @@ public static boolean acquireServletLockIfPossible() {
return GenericHelper.acquireLockIfPossible(VulnerabilityCaseType.REFLECTED_XSS, NR_SEC_CUSTOM_ATTRIB_NAME);
}

public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder responseBody, String contentType, int responseCode, String className, String methodName, Token token) {
public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder responseBody, String contentType, HashMap<String, String> headers, int responseCode, String className, String methodName, Token token) {
try {
if(NewRelicSecurity.getAgent().getIastDetectionCategory().getRxssEnabled()){
return;
}
token.linkAndExpire();

if(!isServletLockAcquired || !NewRelicSecurity.isHookProcessingActive()){
return;
}
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseContentType(contentType);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseBody(responseBody);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseCode(responseCode);
ServletHelper.executeBeforeExitingTransaction();
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setContentType(contentType);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setBody(responseBody);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setStatusCode(responseCode);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setHeaders(headers);
// ServletHelper.executeBeforeExitingTransaction();

LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import akka.stream.scaladsl.Sink
import akka.util.ByteString
import com.newrelic.api.agent.security.NewRelicSecurity
import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper
import com.newrelic.api.agent.security.schema.StringUtils
import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException
import com.newrelic.api.agent.security.utils.logging.LogLevel
import com.newrelic.api.agent.{NewRelic, Token}

import java.lang
import java.util.{HashMap => JavaHashMap}
import scala.concurrent.{ExecutionContext, Future}

object ResponseFutureHelper {
Expand All @@ -41,7 +41,9 @@ object ResponseFutureHelper {
processingResult.onComplete {
_ => {
token.linkAndExpire()
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, response.entity.contentType.toString(), response.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken)
val headers = new JavaHashMap[String, String]()
response.headers.foreach(header => headers.put(header.name(), header.value()))
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, response.entity.contentType.toString(), headers, response.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken)
}
}

Expand All @@ -67,8 +69,9 @@ object ResponseFutureHelper {
stringResponse.append(chunk)
}
val processingResult: Future[Done] = dataBytes.runWith(sink, materializer)

AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), httpResponse.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken())
val headers = new JavaHashMap[String, String]()
httpResponse.headers.foreach(header => headers.put(header.name(), header.value()))
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), headers, httpResponse.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken())
} catch {
case t: NewRelicSecurityException =>
NewRelicSecurity.getAgent.log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, AkkaCoreUtils.AKKA_HTTP_CORE_10_0_11, t.getMessage), t, classOf[AkkaCoreUtils].getName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,15 +233,15 @@ class AkkaHttpCoreTest {

Assert.assertFalse("response should not be empty", operation.getResponse.isEmpty)
Assert.assertEquals("Invalid response content-type.", operation.getResponse.getResponseContentType, contentType)
Assert.assertEquals("Invalid responseBody.", operation.getResponse.getResponseBody.toString, responseBody)
Assert.assertEquals("Invalid responseBody.", operation.getResponse.getBody.toString, responseBody)
}
private def assertMetaData(metaData: SecurityMetaData): Unit = {
Assert.assertFalse("response should not be empty", metaData.getResponse.isEmpty)
Assert.assertEquals("Invalid response content-type.", metaData.getRequest.getContentType, contentType)
Assert.assertEquals("Invalid responseBody.", metaData.getRequest.getBody.toString, requestBody)
Assert.assertFalse("response should not be empty", metaData.getRequest.isEmpty)
Assert.assertEquals("Invalid response content-type.", metaData.getResponse.getResponseContentType, contentType)
Assert.assertEquals("Invalid responseBody.", metaData.getResponse.getResponseBody.toString, responseBody)
Assert.assertEquals("Invalid responseBody.", metaData.getResponse.getBody.toString, responseBody)
Assert.assertEquals("Invalid protocol.", metaData.getRequest.getProtocol, "http")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,16 @@ public static boolean acquireServletLockIfPossible() {
return GenericHelper.acquireLockIfPossible(VulnerabilityCaseType.REFLECTED_XSS, NR_SEC_CUSTOM_ATTRIB_NAME);
}

public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder responseBody, String contentType, int responseCode, String className, String methodName, Token token) {
public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder responseBody, String contentType, Map<String, String> headers, int responseCode, String className, String methodName, Token token) {
try {
if(NewRelicSecurity.getAgent().getIastDetectionCategory().getRxssEnabled()){
return;
}
token.linkAndExpire();
if(!isServletLockAcquired || !NewRelicSecurity.isHookProcessingActive()){
return;
}
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseContentType(contentType);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseBody(responseBody);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseCode(responseCode);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setContentType(contentType);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setBody(responseBody);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setStatusCode(responseCode);
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setHeaders(headers);
LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest());

if(!ServletHelper.isResponseContentTypeExcluded(NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().getResponseContentType())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import akka.stream.scaladsl.Sink
import akka.util.ByteString
import com.newrelic.api.agent.security.NewRelicSecurity
import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper
import com.newrelic.api.agent.security.schema.StringUtils
import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException
import com.newrelic.api.agent.security.utils.logging.LogLevel
import com.newrelic.api.agent.{NewRelic, Token}

import java.lang
import java.util.{HashMap => JavaHashMap}
import scala.concurrent.{ExecutionContext, Future}

object ResponseFutureHelper {
Expand All @@ -41,7 +41,9 @@ object ResponseFutureHelper {
processingResult.onComplete {
_ => {
token.linkAndExpire()
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, response.entity.contentType.toString(), response.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken)
val headers = new JavaHashMap[String, String]()
response.headers.foreach(header => headers.put(header.name(), header.value()))
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, response.entity.contentType.toString(), headers, response.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken)
}
}

Expand All @@ -67,8 +69,9 @@ object ResponseFutureHelper {
stringResponse.append(chunk)
}
val processingResult: Future[Done] = dataBytes.runWith(sink, materializer)

AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), httpResponse.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken)
val headers = new JavaHashMap[String, String]()
httpResponse.headers.foreach(header => headers.put(header.name(), header.value()))
AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), headers, httpResponse.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken)
} catch {
case t: NewRelicSecurityException =>
NewRelicSecurity.getAgent.log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, AkkaCoreUtils.AKKA_HTTP_CORE_10_0_11, t.getMessage), t, classOf[AkkaCoreUtils].getName)
Expand Down
Loading
Loading