Skip to content

Feature to send HTTP Response for Reported Vulnerabilities #343

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

Merged
merged 46 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7bd7d3b
Initial changes for proof backed deserialization attack detection
AnupamJuniwal Sep 6, 2023
a8bff23
Merge branch 'develop' into task/deserialisation_improvement_poc
AnupamJuniwal Sep 7, 2023
cac032e
This contains code cleanup and multiple fixes as required to gather m…
AnupamJuniwal Sep 13, 2023
81345c0
Merge branch 'develop' into task/deserialisation_improvement_poc
AnupamJuniwal Sep 26, 2023
9fff3b7
Code cleanup
AnupamJuniwal Sep 26, 2023
c17d614
This contains changes for deserializationInfo to be managed with one …
AnupamJuniwal Oct 18, 2023
58fa096
minor fix for NPE handling
AnupamJuniwal Oct 30, 2023
f58a5c8
Merge branch 'develop' into task/deserialisation_improvement_poc
AnupamJuniwal Nov 21, 2023
4cd631f
Refactoring for java deserialization hook
AnupamJuniwal Nov 27, 2023
70acca1
Add Transaction start and finish listeners
lovesh-ap Oct 4, 2024
7f46937
Merge branch 'refs/heads/fix/trace-id-reporting/NR-321827' into featu…
lovesh-ap Oct 16, 2024
53561c7
implementation for the feature to send http response for vulnerabilities
lovesh-ap Oct 17, 2024
3239c17
Functionality to never disable http response hooks, disable event gen…
lovesh-ap Oct 24, 2024
4998e08
Rename field value iastRequest to isIASTRequest
lovesh-ap Oct 25, 2024
f2b0c71
Trim response body to 500KB if more
lovesh-ap Nov 14, 2024
cac35d4
trim response body for RXSS and change json name for http response event
lovesh-ap Nov 16, 2024
10692ed
Updating isEmpty condition in HttpResponseEvent
IshikaDawda Dec 5, 2024
ee8d3f3
Added todo note for object deserialiation
AnupamJuniwal Jan 13, 2025
93d10f0
Merge branch 'refs/heads/main' into task/deserialisation_improvement_poc
lovesh-ap Jan 13, 2025
12559be
Deserialization POC without reflection
lovesh-ap Jan 15, 2025
c20e8bb
Merge branch 'refs/heads/main' into feature/include-http-response
IshikaDawda Jan 15, 2025
80edf6a
Update invokes of HttpResponse methods
IshikaDawda Jan 15, 2025
1e115e0
Merge branch 'refs/heads/main' into task/deserialisation_improvement_poc
lovesh-ap Feb 4, 2025
d72a9ac
change parameter schema for Deserialization event
lovesh-ap Feb 7, 2025
bfb2acd
Bump json version to 1.2.11
IshikaDawda Feb 11, 2025
e17fc7a
Pipeline to Buffer and Process Events with Incomplete HTTP Requests
lovesh-ap Feb 13, 2025
3d16620
Merge branch 'refs/heads/main' into feature/include-http-response
lovesh-ap Feb 13, 2025
40577b2
Merge branch 'refs/heads/feature/include-http-response' into feature/…
lovesh-ap Feb 13, 2025
fd3d572
Prodcutisation of DeserializationInfo
lovesh-ap Feb 24, 2025
4995a34
Merge pull request #383 from newrelic/feature/poc/local-event-buffer
lovesh-ap Feb 25, 2025
9ef161e
Merge branch 'refs/heads/feature/include-http-response' into task/des…
lovesh-ap Feb 25, 2025
4577300
Add UNSAFE_DESERIALIZATION as a new category
lovesh-ap Feb 26, 2025
71b445f
Merge branch 'refs/heads/main' into feature/include-http-response
lovesh-ap Mar 2, 2025
90f0324
Introduce Deserialization Vulnerability detection and
lovesh-ap Apr 11, 2025
6540e78
Deserialization event size reduction
lovesh-ap Apr 11, 2025
f077121
update json version to 1.2.11
lovesh-ap Apr 11, 2025
8e00a41
Remove deserialization context and add category reflection to Skip Scan
lovesh-ap Apr 14, 2025
27391e2
Remove hard check on deserialization value
lovesh-ap Apr 15, 2025
ef528f6
Remove unused Class and sysouts
lovesh-ap Apr 15, 2025
381d49a
Generate all reflection events
lovesh-ap Apr 16, 2025
aaa48a6
Log level change for debugging
lovesh-ap Apr 21, 2025
d1cf30b
Changelogs for 1.7.0 release
lovesh-ap Apr 21, 2025
4370bd2
Disable httpResponse sending
IshikaDawda Apr 23, 2025
9f1d5eb
Merge branch 'refs/heads/main' into feature/include-http-response
IshikaDawda Apr 23, 2025
d78ca37
Merge branch 'refs/heads/feature/include-http-response' into task/des…
IshikaDawda Apr 23, 2025
c1299e0
Merge pull request #395 from newrelic/task/deserialisation_improvemen…
k2himanshu Apr 23, 2025
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
16 changes: 16 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ Noteworthy changes to the agent are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.7.0] - TBD
### Adds
- [PR-395](https://github.com/newrelic/csec-java-agent/pull/395) **Support for Deserialization Vulnerability Detection**: Implemented mechanisms to detect vulnerabilities arising from unsafe deserialization processes.
- [PR-395](https://github.com/newrelic/csec-java-agent/pull/395) **Support for Vulnerability Detection of Remote Code Invocation via Reflection**: Enhanced capability to identify security risks associated with remote code execution through reflection.
- [PR-343](https://github.com/newrelic/csec-java-agent/pull/343) **HTTP Response Handling for Vulnerabilities**: Developed the functionality to send HTTP responses for detected vulnerabilities directly to the UI.

### Changes
- [PR-343](https://github.com/newrelic/csec-java-agent/pull/343) **Trimmed Response Body**: Updated the response handling logic to trim response bodies to a maximum of 500KB when larger. This optimization aids in performance and resource conservation.
- [PR-396](https://github.com/newrelic/csec-java-agent/pull/396) Upgraded _commons-io:commons-io_ from version 2.7 to 2.14.0
- [PR-403](https://github.com/newrelic/csec-java-agent/pull/403) GraphQL Supported Version Range: Restricted the supported version range for GraphQL due to the release of a new version on April 7th, 2025

### Fixes
- [PR-372](https://github.com/newrelic/csec-java-agent/pull/372) **Repeat IAST Request Relay Commands**: Reconfigured logic to repeat IAST control commands until the endpoint is confirmed.


## [1.6.1] - 2025-3-1
### Adds
- [PR-309](https://github.com/newrelic/csec-java-agent/pull/309) Introduced API Endpoint detection for Resin Server. [NR-293077](https://new-relic.atlassian.net/browse/NR-293077)
Expand All @@ -17,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [PR-364](https://github.com/newrelic/csec-java-agent/pull/364) Modified HealthCheck to include the iastTestIdentifier and adjusted WebSocket headers to send instance-count only when its value is greater than zero. [NR-347851](https://new-relic.atlassian.net/browse/NR-347851)
- [PR-349](https://github.com/newrelic/csec-java-agent/pull/349) Enhanced the process for rolling over log files, allowing for specific prefixes and suffixes. [NR-337016](https://new-relic.atlassian.net/browse/NR-337016)


## [1.6.0] - 2024-12-16
### Adds
- [PR-329](https://github.com/newrelic/csec-java-agent/pull/329) Apache Pekko Server Support: The security agent now supports Apache Pekko Server version 1.0.0 and newer, compatible with Scala 2.13 and above. [NR-308780](https://new-relic.atlassian.net/browse/NR-308780), [NR-308781](https://new-relic.atlassian.net/browse/NR-308781), [NR-308791](https://new-relic.atlassian.net/browse/NR-308791), [NR-308792](https://new-relic.atlassian.net/browse/NR-308792) [NR-308782](https://new-relic.atlassian.net/browse/NR-308782)
Expand Down
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.1
jsonVersion=1.2.10
jsonVersion=1.2.11
# 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(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(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(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")
}
}
Loading
Loading