diff --git a/.github/workflows/publish-release-to-maven.yml b/.github/workflows/publish-release-to-maven.yml index 3a2deecbf..1aae7301b 100644 --- a/.github/workflows/publish-release-to-maven.yml +++ b/.github/workflows/publish-release-to-maven.yml @@ -12,8 +12,8 @@ jobs: with: apm-repo: 'k2io/newrelic-java-agent' apm-source-ref: 'csec-dev' - csec-run-unittest: 'true' - csec-run-instrumentation-verify: 'true' + csec-run-unittest: 'false' + csec-run-instrumentation-verify: 'false' is-release: 'true' version-suffix: '' slack-notify: 'true' \ No newline at end of file diff --git a/Changelog.md b/Changelog.md index 4a1b5233d..3ec4c627e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,33 @@ 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.4.1] - 2024-8-14 +### Adds +- [PR-296](https://github.com/newrelic/csec-java-agent/pull/296) Apache Solr Support: The security agent now also supports Apache Solr Version 4.0.0 and above. [NR-288599](https://new-relic.atlassian.net/browse/NR-288599) +- [PR-275](https://github.com/newrelic/csec-java-agent/pull/275) The maximum permissible size for a request body for scan will be set at 500KB. [NR-174195](https://new-relic.atlassian.net/browse/NR-174195) +- [PR-306](https://github.com/newrelic/csec-java-agent/pull/306) Add csec prefix to all instrumentation Jar, this resolves CVE flagged by third party scanners on our instrumentation JARs. [NR-289249](https://new-relic.atlassian.net/browse/NR-289249) +- [PR-303](https://github.com/newrelic/csec-java-agent/pull/303) Honour OFF Flag, Handle Boolean values for config log_level. [NR-293102](https://new-relic.atlassian.net/browse/NR-293102) +- [PR-299](https://github.com/newrelic/csec-java-agent/pull/299) Support Authentication capabilities for Proxy Settings. [NR-283945](https://new-relic.atlassian.net/browse/NR-283945) +- [PR-313](https://github.com/newrelic/csec-java-agent/pull/313) Processing of the security agent will persist even if the creation of the security home directory encounters an issue. [NR-297206](https://new-relic.atlassian.net/browse/NR-297206) +- [PR-277](https://github.com/newrelic/csec-java-agent/pull/277) Improve Management of Log file size and its count. [NR-272900](https://new-relic.atlassian.net/browse/NR-272900) +- [PR-314](https://github.com/newrelic/csec-java-agent/pull/314) Report error to Error Inbox upon connection failure to Security Engine. [NR-299700](https://new-relic.atlassian.net/browse/NR-299700) +- [PR-316](https://github.com/newrelic/csec-java-agent/pull/316) Detailed IAST Scan metric reporting via HealthCheck. [NR-267166](https://new-relic.atlassian.net/browse/NR-267166) +- [PR-302](https://github.com/newrelic/csec-java-agent/pull/302) Detect API Endpoint of the Application for Vertx Framework. [NR-287771](https://new-relic.atlassian.net/browse/NR-287771) +- [PR-293](https://github.com/newrelic/csec-java-agent/pull/293), [PR-284](https://github.com/newrelic/csec-java-agent/pull/284), [PR-302](https://github.com/newrelic/csec-java-agent/pull/302) Detect route of an incoming request for mule server, play framework and Vertx Framework. [NR-283915](https://new-relic.atlassian.net/browse/NR-283915), [NR-265915](https://new-relic.atlassian.net/browse/NR-265915), [NR-287771](https://new-relic.atlassian.net/browse/NR-287771) + +### Changes +- [PR-265](https://github.com/newrelic/csec-java-agent/pull/265) Improve Secure Cookie event reporting to provide detailed vulnerability. [NR-273609](https://new-relic.atlassian.net/browse/NR-273609) +- [PR-283](https://github.com/newrelic/csec-java-agent/pull/283) Update IAST Header Parsing Minimum Expected Length Set to 8. [NR-282647](https://new-relic.atlassian.net/browse/NR-282647) +- [PR-308](https://github.com/newrelic/csec-java-agent/pull/308) Remove jackson-dataformat-properties to address [CVE-2023-3894](https://www.cve.org/CVERecord?id=CVE-2023-3894) and exclude transitive dependency junit to address [CVE-2020-15250](https://www.cve.org/CVERecord?id=CVE-2020-15250) [NR-295033](https://new-relic.atlassian.net/browse/NR-295033) + +### Fixes +- [PR-292](https://github.com/newrelic/csec-java-agent/pull/292) Fix for ClassNotFoundException observed in glassfish server [NR-262453](https://new-relic.atlassian.net/browse/NR-262453) +- [PR-286](https://github.com/newrelic/csec-java-agent/pull/286) Detect correct user class in Netty Reactor Server [NR-253551](https://new-relic.atlassian.net/browse/NR-253551) +- [PR-317](https://github.com/newrelic/csec-java-agent/pull/317) Add a workaround for an issue where New Relic Security Agent breaks the gRPC endpoints [#130](https://github.com/newrelic/csec-java-agent/issues/310). [NR-299709](https://new-relic.atlassian.net/browse/NR-299709) + +### Deprecations +- Status File Used for Debugging: This feature has been deprecated. All debugging capabilities have been moved to either Init Logging or [Error Inbox](https://docs.newrelic.com/docs/errors-inbox/errors-inbox/) and will be removed in a future agent release. [NR-293966](https://new-relic.atlassian.net/browse/NR-293966) + ## [1.4.0] - 2024-6-24 ### Changes - Json Version bump to 1.2.3 due to [NR-254157](https://new-relic.atlassian.net/browse/NR-254157) implementation. @@ -16,8 +43,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [PR-256](https://github.com/newrelic/csec-java-agent/pull/256), [PR-259](https://github.com/newrelic/csec-java-agent/pull/259), [PR-258](https://github.com/newrelic/csec-java-agent/pull/258) Feature to detect route of an incoming request for Jax-RS and Spring Framework. [NR-265913](https://new-relic.atlassian.net/browse/NR-265913), [NR-261653](https://new-relic.atlassian.net/browse/NR-261653), [NR-273605](https://new-relic.atlassian.net/browse/NR-273605) - [PR-126](https://github.com/newrelic/csec-java-agent/pull/126), [PR-127](https://github.com/newrelic/csec-java-agent/pull/127), [PR-128](https://github.com/newrelic/csec-java-agent/pull/128), [PR-129](https://github.com/newrelic/csec-java-agent/pull/129) Jedis Support : The security agent now also supports Jedis Version 1.4.0 and above. [NR-174176](https://new-relic.atlassian.net/browse/NR-174176) - [PR-287](https://github.com/newrelic/csec-java-agent/pull/287) Support for Proxy Settings for Connecting to the Security Engine, with known limitation of missing Authentication capabilities. + ### Fixes -- [PR-255](https://github.com/newrelic/csec-java-agent/pull/255) Handle InvalidPathException thrown by Paths.get method [NR-262452](https://new-relic.atlassian.net/browse/) +- [PR-255](https://github.com/newrelic/csec-java-agent/pull/255) Handle InvalidPathException thrown by Paths.get method [NR-262452](https://new-relic.atlassian.net/browse/NR-262452) - [PR-216](https://github.com/newrelic/csec-java-agent/pull/216) Extract Server Configuration to resolve IAST localhost connection with application for Glassfish Server. [NR-223808](https://new-relic.atlassian.net/browse/NR-223808) - [PR-214](https://github.com/newrelic/csec-java-agent/pull/214) Extract Server Configuration to resolve IAST localhost connection with application for Weblogic Server. [NR-223809](https://new-relic.atlassian.net/browse/NR-223809) - [PR-242](https://github.com/newrelic/csec-java-agent/pull/242) Fix for User Class detection in Play Framework [NR-264101](https://new-relic.atlassian.net/browse/NR-264101) diff --git a/gradle.properties b/gradle.properties index 4cc8c40f4..9971d5819 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # The agent version. -agentVersion=1.4.0 +agentVersion=1.4.1 jsonVersion=1.2.5 # Updated exposed NR APM API version. nrAPIVersion=8.12.0 diff --git a/instrumentation-security/akka-http-2.11_10.0.0/src/main/scala/akka/http/scaladsl/server/CsecAkkaHttpContextFunction.scala b/instrumentation-security/akka-http-2.11_10.0.0/src/main/scala/akka/http/scaladsl/server/CsecAkkaHttpContextFunction.scala index 4a974e768..2ebd36c77 100644 --- a/instrumentation-security/akka-http-2.11_10.0.0/src/main/scala/akka/http/scaladsl/server/CsecAkkaHttpContextFunction.scala +++ b/instrumentation-security/akka-http-2.11_10.0.0/src/main/scala/akka/http/scaladsl/server/CsecAkkaHttpContextFunction.scala @@ -8,6 +8,7 @@ package akka.http.scaladsl.server import akka.Done +import akka.http.scaladsl.model.HttpEntity import akka.stream.javadsl.Source import akka.stream.scaladsl.Sink import akka.util.ByteString @@ -55,15 +56,17 @@ class CsecContextWrapper(original: Function1[RequestContext, Future[RouteResult] override def apply(ctx: RequestContext): Future[RouteResult] = { try { - var httpRequest = ctx.request; + val httpRequest = ctx.request; val body: lang.StringBuilder = new lang.StringBuilder(); val dataBytes: Source[ByteString, AnyRef] = httpRequest.entity.getDataBytes() val isLockAquired = AkkaCoreUtils.acquireServletLockIfPossible(); - val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => - val chunk = byteString.utf8String - body.append(chunk) + if (!httpRequest.entity.isInstanceOf[HttpEntity.Chunked]) { + val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => + val chunk = byteString.utf8String + body.append(chunk) + } + val processingResult: Future[Done] = dataBytes.runWith(sink, ctx.materializer) } - val processingResult: Future[Done] = dataBytes.runWith(sink, ctx.materializer) AkkaCoreUtils.preProcessHttpRequest(isLockAquired, httpRequest, body, NewRelic.getAgent.getTransaction.getToken); original.apply(ctx) } catch { diff --git a/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala b/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala index f1d3534c5..76bef8435 100644 --- a/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala +++ b/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala @@ -8,7 +8,7 @@ package akka.http.scaladsl import akka.Done -import akka.http.scaladsl.model.{HttpRequest, HttpResponse} +import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse} import akka.stream.Materializer import akka.stream.javadsl.Source import akka.stream.scaladsl.Sink @@ -26,11 +26,14 @@ class AkkaAsyncRequestHandler(handler: HttpRequest ⇒ Future[HttpResponse])(imp val body: lang.StringBuilder = new lang.StringBuilder(); val dataBytes: Source[ByteString, AnyRef] = param.entity.getDataBytes() val isLockAquired = AkkaCoreUtils.acquireServletLockIfPossible(); - val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => - val chunk = byteString.utf8String - body.append(chunk) + if (!param.entity.isInstanceOf[HttpEntity.Chunked]) { + val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => + val chunk = byteString.utf8String + body.append(chunk) + } + val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) } - val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) + AkkaCoreUtils.preProcessHttpRequest(isLockAquired, param, body, NewRelic.getAgent.getTransaction.getToken); val futureResponse: Future[HttpResponse] = handler.apply(param) futureResponse.flatMap(ResponseFutureHelper.wrapResponseAsync(NewRelic.getAgent.getTransaction.getToken, materializer)) diff --git a/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala b/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala index 18dbe8b67..bb1f64a44 100644 --- a/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala +++ b/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala @@ -8,7 +8,7 @@ package akka.http.scaladsl import akka.Done -import akka.http.scaladsl.model.{HttpRequest, HttpResponse} +import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse} import akka.stream.Materializer import akka.stream.javadsl.Source import akka.stream.scaladsl.Sink @@ -26,11 +26,14 @@ class AkkaSyncRequestHandler(handler: HttpRequest ⇒ HttpResponse)(implicit mat val body: lang.StringBuilder = new lang.StringBuilder(); val dataBytes: Source[ByteString, AnyRef] = param.entity.getDataBytes() val isLockAquired = AkkaCoreUtils.acquireServletLockIfPossible(); - val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => - val chunk = byteString.utf8String - body.append(chunk) + + if (!param.entity.isInstanceOf[HttpEntity.Chunked]) { + val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => + val chunk = byteString.utf8String + body.append(chunk) + } + val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) } - val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) AkkaCoreUtils.preProcessHttpRequest(isLockAquired, param, body, NewRelic.getAgent.getTransaction.getToken); val response: HttpResponse = handler.apply(param) ResponseFutureHelper.wrapResponseSync(response, materializer) diff --git a/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala b/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala index f1d3534c5..d6d0370cd 100644 --- a/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala +++ b/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala @@ -8,7 +8,7 @@ package akka.http.scaladsl import akka.Done -import akka.http.scaladsl.model.{HttpRequest, HttpResponse} +import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse} import akka.stream.Materializer import akka.stream.javadsl.Source import akka.stream.scaladsl.Sink @@ -26,11 +26,13 @@ class AkkaAsyncRequestHandler(handler: HttpRequest ⇒ Future[HttpResponse])(imp val body: lang.StringBuilder = new lang.StringBuilder(); val dataBytes: Source[ByteString, AnyRef] = param.entity.getDataBytes() val isLockAquired = AkkaCoreUtils.acquireServletLockIfPossible(); - val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => - val chunk = byteString.utf8String - body.append(chunk) + if (!param.entity.isInstanceOf[HttpEntity.Chunked]) { + val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => + val chunk = byteString.utf8String + body.append(chunk) + } + val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) } - val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) AkkaCoreUtils.preProcessHttpRequest(isLockAquired, param, body, NewRelic.getAgent.getTransaction.getToken); val futureResponse: Future[HttpResponse] = handler.apply(param) futureResponse.flatMap(ResponseFutureHelper.wrapResponseAsync(NewRelic.getAgent.getTransaction.getToken, materializer)) diff --git a/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala b/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala index 18dbe8b67..f8b63323c 100644 --- a/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala +++ b/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala @@ -8,7 +8,7 @@ package akka.http.scaladsl import akka.Done -import akka.http.scaladsl.model.{HttpRequest, HttpResponse} +import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse} import akka.stream.Materializer import akka.stream.javadsl.Source import akka.stream.scaladsl.Sink @@ -26,11 +26,13 @@ class AkkaSyncRequestHandler(handler: HttpRequest ⇒ HttpResponse)(implicit mat val body: lang.StringBuilder = new lang.StringBuilder(); val dataBytes: Source[ByteString, AnyRef] = param.entity.getDataBytes() val isLockAquired = AkkaCoreUtils.acquireServletLockIfPossible(); - val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => - val chunk = byteString.utf8String - body.append(chunk) + if (!param.entity.isInstanceOf[HttpEntity.Chunked]) { + val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => + val chunk = byteString.utf8String + body.append(chunk) + } + val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) } - val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) AkkaCoreUtils.preProcessHttpRequest(isLockAquired, param, body, NewRelic.getAgent.getTransaction.getToken); val response: HttpResponse = handler.apply(param) ResponseFutureHelper.wrapResponseSync(response, materializer) diff --git a/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala b/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala index f1d3534c5..d6d0370cd 100644 --- a/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala +++ b/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala @@ -8,7 +8,7 @@ package akka.http.scaladsl import akka.Done -import akka.http.scaladsl.model.{HttpRequest, HttpResponse} +import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse} import akka.stream.Materializer import akka.stream.javadsl.Source import akka.stream.scaladsl.Sink @@ -26,11 +26,13 @@ class AkkaAsyncRequestHandler(handler: HttpRequest ⇒ Future[HttpResponse])(imp val body: lang.StringBuilder = new lang.StringBuilder(); val dataBytes: Source[ByteString, AnyRef] = param.entity.getDataBytes() val isLockAquired = AkkaCoreUtils.acquireServletLockIfPossible(); - val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => - val chunk = byteString.utf8String - body.append(chunk) + if (!param.entity.isInstanceOf[HttpEntity.Chunked]) { + val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => + val chunk = byteString.utf8String + body.append(chunk) + } + val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) } - val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) AkkaCoreUtils.preProcessHttpRequest(isLockAquired, param, body, NewRelic.getAgent.getTransaction.getToken); val futureResponse: Future[HttpResponse] = handler.apply(param) futureResponse.flatMap(ResponseFutureHelper.wrapResponseAsync(NewRelic.getAgent.getTransaction.getToken, materializer)) diff --git a/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java b/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java index 0a0d4fb68..1f32330c9 100644 --- a/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java +++ b/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java @@ -61,7 +61,7 @@ public static boolean acquireServletLockIfPossible() { return false; } - public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder responseBody, String contentType, String className, String methodName, Token token) { + public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder responseBody, String contentType, int responseCode, String className, String methodName, Token token) { try { token.linkAndExpire(); if(!isServletLockAcquired || !NewRelicSecurity.isHookProcessingActive()){ @@ -69,6 +69,7 @@ public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringB } NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseContentType(contentType); NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseBody(responseBody); + NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseCode(responseCode); LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest()); if(!ServletHelper.isResponseContentTypeExcluded(NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().getResponseContentType())) { diff --git a/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala b/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala index 18dbe8b67..f8b63323c 100644 --- a/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala +++ b/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala @@ -8,7 +8,7 @@ package akka.http.scaladsl import akka.Done -import akka.http.scaladsl.model.{HttpRequest, HttpResponse} +import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse} import akka.stream.Materializer import akka.stream.javadsl.Source import akka.stream.scaladsl.Sink @@ -26,11 +26,13 @@ class AkkaSyncRequestHandler(handler: HttpRequest ⇒ HttpResponse)(implicit mat val body: lang.StringBuilder = new lang.StringBuilder(); val dataBytes: Source[ByteString, AnyRef] = param.entity.getDataBytes() val isLockAquired = AkkaCoreUtils.acquireServletLockIfPossible(); - val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => - val chunk = byteString.utf8String - body.append(chunk) + if (!param.entity.isInstanceOf[HttpEntity.Chunked]) { + val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => + val chunk = byteString.utf8String + body.append(chunk) + } + val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) } - val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) AkkaCoreUtils.preProcessHttpRequest(isLockAquired, param, body, NewRelic.getAgent.getTransaction.getToken); val response: HttpResponse = handler.apply(param) ResponseFutureHelper.wrapResponseSync(response, materializer) diff --git a/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/ResponseFutureHelper.scala b/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/ResponseFutureHelper.scala index 1e9b6e179..60a80405f 100644 --- a/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/ResponseFutureHelper.scala +++ b/instrumentation-security/akka-http-core-2.13_10.1.8/src/main/scala/akka/http/scaladsl/ResponseFutureHelper.scala @@ -41,7 +41,7 @@ object ResponseFutureHelper { processingResult.onComplete { _ => { token.linkAndExpire() - AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, response.entity.contentType.toString(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken) + AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, response.entity.contentType.toString(), response.status.intValue(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken) } } @@ -68,7 +68,7 @@ object ResponseFutureHelper { } val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) - AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), this.getClass.getName, "apply", NewRelic.getAgent.getTransaction.getToken()) + AkkaCoreUtils.postProcessHttpRequest(isLockAquired, stringResponse, httpResponse.entity.contentType.toString(), 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) diff --git a/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala b/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala index e73c15e44..922fe6070 100644 --- a/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala +++ b/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaAsyncRequestHandler.scala @@ -8,7 +8,7 @@ package akka.http.scaladsl import akka.Done -import akka.http.scaladsl.model.{HttpRequest, HttpResponse} +import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse} import akka.stream.Materializer import akka.stream.javadsl.Source import akka.stream.scaladsl.Sink @@ -26,11 +26,14 @@ class AkkaAsyncRequestHandler(handler: HttpRequest ⇒ Future[HttpResponse])(imp val body: lang.StringBuilder = new lang.StringBuilder(); val dataBytes: Source[ByteString, AnyRef] = param.entity.getDataBytes() val isLockAquired = AkkaCoreUtils.acquireServletLockIfPossible(); - val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => - val chunk = byteString.utf8String - body.append(chunk) + + if (!param.entity.isInstanceOf[HttpEntity.Chunked]) { + val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => + val chunk = byteString.utf8String + body.append(chunk) + } + val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) } - val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) AkkaCoreUtils.preProcessHttpRequest(isLockAquired, param, body, NewRelic.getAgent.getTransaction.getToken); val futureResponse: Future[HttpResponse] = handler.apply(param) futureResponse.flatMap(ResponseFutureHelper.wrapResponseAsync(NewRelic.getAgent.getTransaction.getToken, materializer)) diff --git a/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala b/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala index 6e3cbf634..d0f99f329 100644 --- a/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala +++ b/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaSyncRequestHandler.scala @@ -8,7 +8,7 @@ package akka.http.scaladsl import akka.Done -import akka.http.scaladsl.model.{HttpRequest, HttpResponse} +import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse} import akka.stream.Materializer import akka.stream.javadsl.Source import akka.stream.scaladsl.Sink @@ -27,11 +27,14 @@ class AkkaSyncRequestHandler(handler: HttpRequest ⇒ HttpResponse)(implicit mat val body: lang.StringBuilder = new lang.StringBuilder(); val dataBytes: Source[ByteString, AnyRef] = param.entity.getDataBytes() val isLockAquired = AkkaCoreUtils.acquireServletLockIfPossible(); - val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => - val chunk = byteString.utf8String - body.append(chunk) + + if (!param.entity.isInstanceOf[HttpEntity.Chunked]) { + val sink: Sink[ByteString, Future[Done]] = Sink.foreach[ByteString] { byteString => + val chunk = byteString.utf8String + body.append(chunk) + } + val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) } - val processingResult: Future[Done] = dataBytes.runWith(sink, materializer) AkkaCoreUtils.preProcessHttpRequest(isLockAquired, param, body, NewRelic.getAgent.getTransaction.getToken); val response: HttpResponse = handler.apply(param) ResponseFutureHelper.wrapResponseSync(response, materializer) diff --git a/instrumentation-security/javax-jndi/src/main/java/javax/naming/JNDIUtils.java b/instrumentation-security/javax-jndi/src/main/java/javax/naming/JNDIUtils.java index b4c4576ce..5aa298c1a 100644 --- a/instrumentation-security/javax-jndi/src/main/java/javax/naming/JNDIUtils.java +++ b/instrumentation-security/javax-jndi/src/main/java/javax/naming/JNDIUtils.java @@ -8,6 +8,7 @@ import com.newrelic.api.agent.security.utils.logging.LogLevel; import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -35,7 +36,10 @@ public static AbstractOperation handleJNDIHook(String name, String methodName, S NewRelicSecurity.getAgent().registerOperation(operation); return operation; } - } catch (Exception ignored) { + } catch (URISyntaxException ignored) { + // Ignoring URISyntaxException + } + catch (Exception ignored) { NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, JAVAX_JNDI, ignored.getMessage()), ignored, JNDIUtils.class.getName()); NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE , String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, JAVAX_JNDI, ignored.getMessage()), ignored, JNDIUtils.class.getName()); } diff --git a/instrumentation-security/mule-3.6/src/main/java/com/newrelic/agent/security/instrumentation/mule36/MuleHelper.java b/instrumentation-security/mule-3.6/src/main/java/com/newrelic/agent/security/instrumentation/mule36/MuleHelper.java index 3a79f35d7..dd226db05 100644 --- a/instrumentation-security/mule-3.6/src/main/java/com/newrelic/agent/security/instrumentation/mule36/MuleHelper.java +++ b/instrumentation-security/mule-3.6/src/main/java/com/newrelic/agent/security/instrumentation/mule36/MuleHelper.java @@ -7,7 +7,9 @@ import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.security.schema.Framework; import com.newrelic.api.agent.security.schema.policy.AgentPolicy; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import org.mule.api.processor.MessageProcessor; import org.mule.module.http.api.HttpHeaders; import org.mule.module.http.api.listener.HttpListener; @@ -29,6 +31,7 @@ public class MuleHelper { private static final String EMPTY = ""; public static final String LIBRARY_NAME = "MULE-SERVER"; private static final Map handlerMap = new HashMap<>(); + public static final String MULE_3_6 = "MULE-3.6"; public static void processHttpRequestHeader(HttpRequest httpRequest, com.newrelic.api.agent.security.schema.HttpRequest securityRequest @@ -114,10 +117,24 @@ public static void gatherURLMappings(HttpListener messageSource, List getHandlerMap() { return handlerMap; } + + // route detection + public static void setRequestRoute(String listenerPath) { + if (NewRelicSecurity.isHookProcessingActive()) { + try { + NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().setRoute(listenerPath); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFramework(Framework.MULE); + } catch (Exception e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_ROUTE_FOR_INCOMING_REQUEST, MULE_3_6, e.getMessage()), e, MuleHelper.class.getName()); + } + } + } } diff --git a/instrumentation-security/mule-3.6/src/main/java/org/mule/module/http/internal/listener/HttpRequestToMuleEvent_Instrumentation.java b/instrumentation-security/mule-3.6/src/main/java/org/mule/module/http/internal/listener/HttpRequestToMuleEvent_Instrumentation.java index ca9faf1b5..d5c2ac80c 100644 --- a/instrumentation-security/mule-3.6/src/main/java/org/mule/module/http/internal/listener/HttpRequestToMuleEvent_Instrumentation.java +++ b/instrumentation-security/mule-3.6/src/main/java/org/mule/module/http/internal/listener/HttpRequestToMuleEvent_Instrumentation.java @@ -9,6 +9,7 @@ import com.newrelic.api.agent.security.schema.SecurityMetaData; import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -24,6 +25,7 @@ public static MuleEvent transform(final HttpRequestContext requestContext, final { boolean isLockAcquired = acquireLockIfPossible(requestContext.hashCode()); MuleEvent event; + MuleHelper.setRequestRoute(listenerPath); if (isLockAcquired) { preprocessSecurityHook(requestContext); } @@ -82,7 +84,9 @@ private static void preprocessSecurityHook(HttpRequestContext requestContext) { // TODO: need to update UserClassEntity ServletHelper.registerUserLevelCode(MuleHelper.LIBRARY_NAME); securityRequest.setRequestParsed(true); - } catch (Throwable ignored){} + } catch (Throwable ignored){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, MuleHelper.MULE_3_6, ignored.getMessage()), ignored, HttpRequestToMuleEvent_Instrumentation.class.getName()); + } } private static void postProcessSecurityHook() { @@ -107,9 +111,11 @@ private static void postProcessSecurityHook() { ServletHelper.tmpFileCleanUp(NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getTempFiles()); } catch (Throwable e) { if(e instanceof NewRelicSecurityException){ - e.printStackTrace(); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, MuleHelper.MULE_3_6, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); throw e; } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, MuleHelper.MULE_3_6, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE , String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, MuleHelper.MULE_3_6, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); } } diff --git a/instrumentation-security/mule-3.6/src/main/java/org/mule/module/http/internal/listener/async/RequestHandler_Instrumentation.java b/instrumentation-security/mule-3.6/src/main/java/org/mule/module/http/internal/listener/async/RequestHandler_Instrumentation.java index 3ebd77e15..da85e3095 100644 --- a/instrumentation-security/mule-3.6/src/main/java/org/mule/module/http/internal/listener/async/RequestHandler_Instrumentation.java +++ b/instrumentation-security/mule-3.6/src/main/java/org/mule/module/http/internal/listener/async/RequestHandler_Instrumentation.java @@ -9,11 +9,13 @@ import com.newrelic.api.agent.security.schema.SecurityMetaData; import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import org.mule.module.http.internal.domain.request.HttpRequest; import org.mule.module.http.internal.domain.request.HttpRequestContext; +import org.mule.module.http.internal.listener.HttpRequestToMuleEvent_Instrumentation; @Weave(type = MatchType.Interface, originalName = "org.mule.module.http.internal.listener.async.RequestHandler") public class RequestHandler_Instrumentation { @@ -76,7 +78,9 @@ private void preprocessSecurityHook(HttpRequestContext requestContext) { // TODO: need to update UserClassEntity ServletHelper.registerUserLevelCode(MuleHelper.LIBRARY_NAME); securityRequest.setRequestParsed(true); - } catch (Throwable ignored){} + } catch (Throwable ignored){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, MuleHelper.MULE_3_6, ignored.getMessage()), ignored, HttpRequestToMuleEvent_Instrumentation.class.getName()); + } } private void postProcessSecurityHook() { @@ -101,9 +105,11 @@ private void postProcessSecurityHook() { ServletHelper.tmpFileCleanUp(NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getTempFiles()); } catch (Throwable e) { if(e instanceof NewRelicSecurityException){ - e.printStackTrace(); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, MuleHelper.MULE_3_6, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); throw e; } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, MuleHelper.MULE_3_6, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE , String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, MuleHelper.MULE_3_6, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); } } diff --git a/instrumentation-security/mule-3.7/src/main/java/com/newrelic/agent/security/instrumentation/mule37/MuleHelper.java b/instrumentation-security/mule-3.7/src/main/java/com/newrelic/agent/security/instrumentation/mule37/MuleHelper.java index afc9c97ba..a1b00a487 100644 --- a/instrumentation-security/mule-3.7/src/main/java/com/newrelic/agent/security/instrumentation/mule37/MuleHelper.java +++ b/instrumentation-security/mule-3.7/src/main/java/com/newrelic/agent/security/instrumentation/mule37/MuleHelper.java @@ -7,11 +7,14 @@ import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.security.schema.Framework; import com.newrelic.api.agent.security.schema.policy.AgentPolicy; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import org.mule.api.processor.MessageProcessor; import org.mule.module.http.api.HttpHeaders; import org.mule.module.http.api.listener.HttpListener; import org.mule.module.http.internal.domain.request.HttpRequest; +import org.mule.module.http.internal.listener.ListenerPath; import org.mule.processor.InvokerMessageProcessor; import java.util.HashMap; @@ -28,6 +31,7 @@ public class MuleHelper { private static final String EMPTY = ""; public static final String LIBRARY_NAME = "MULE-SERVER"; private static final Map handlerMap = new HashMap<>(); + public static final String MULE_3_7 = "MULE-3.7"; public static void processHttpRequestHeader(HttpRequest httpRequest, com.newrelic.api.agent.security.schema.HttpRequest securityRequest) { for (String headerName : httpRequest.getHeaderNames()) { @@ -108,10 +112,23 @@ public static void gatherURLMappings(HttpListener messageSource, List getHandlerMap() { return handlerMap; } + + public static void setRequestRoute(ListenerPath listenerPath) { + if (NewRelicSecurity.isHookProcessingActive()) { + try { + NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().setRoute(listenerPath.getResolvedPath()); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFramework(Framework.MULE); + } catch (Exception e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_ROUTE_FOR_INCOMING_REQUEST, MULE_3_7, e.getMessage()), e, MuleHelper.class.getName()); + } + } + } } diff --git a/instrumentation-security/mule-3.7/src/main/java/org/mule/module/http/internal/listener/HttpRequestToMuleEvent_Instrumentation.java b/instrumentation-security/mule-3.7/src/main/java/org/mule/module/http/internal/listener/HttpRequestToMuleEvent_Instrumentation.java index 521808b1a..62c6c4175 100644 --- a/instrumentation-security/mule-3.7/src/main/java/org/mule/module/http/internal/listener/HttpRequestToMuleEvent_Instrumentation.java +++ b/instrumentation-security/mule-3.7/src/main/java/org/mule/module/http/internal/listener/HttpRequestToMuleEvent_Instrumentation.java @@ -9,6 +9,7 @@ import com.newrelic.api.agent.security.schema.SecurityMetaData; import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -24,6 +25,7 @@ public static MuleEvent transform(final HttpRequestContext requestContext, final { boolean isLockAcquired = acquireLockIfPossible(requestContext.hashCode()); MuleEvent event; + MuleHelper.setRequestRoute(listenerPath); if (isLockAcquired) { preprocessSecurityHook(requestContext); } @@ -82,7 +84,9 @@ private static void preprocessSecurityHook(HttpRequestContext requestContext) { // TODO: need to update UserClassEntity ServletHelper.registerUserLevelCode(MuleHelper.LIBRARY_NAME); securityRequest.setRequestParsed(true); - } catch (Throwable ignored){} + } catch (Throwable ignored){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, MuleHelper.MULE_3_7, ignored.getMessage()), ignored, HttpRequestToMuleEvent_Instrumentation.class.getName()); + } } private static void postProcessSecurityHook() { @@ -107,9 +111,11 @@ private static void postProcessSecurityHook() { ServletHelper.tmpFileCleanUp(NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getTempFiles()); } catch (Throwable e) { if(e instanceof NewRelicSecurityException){ - e.printStackTrace(); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, MuleHelper.MULE_3_7, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); throw e; } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, MuleHelper.MULE_3_7, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE , String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, MuleHelper.MULE_3_7, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); } } diff --git a/instrumentation-security/mule-3.7/src/main/java/org/mule/module/http/internal/listener/async/RequestHandler_Instrumentation.java b/instrumentation-security/mule-3.7/src/main/java/org/mule/module/http/internal/listener/async/RequestHandler_Instrumentation.java index 0d1c0d15f..4a9f526ac 100644 --- a/instrumentation-security/mule-3.7/src/main/java/org/mule/module/http/internal/listener/async/RequestHandler_Instrumentation.java +++ b/instrumentation-security/mule-3.7/src/main/java/org/mule/module/http/internal/listener/async/RequestHandler_Instrumentation.java @@ -9,14 +9,17 @@ import com.newrelic.api.agent.security.schema.SecurityMetaData; import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import org.mule.module.http.internal.domain.request.HttpRequest; import org.mule.module.http.internal.domain.request.HttpRequestContext; +import org.mule.module.http.internal.listener.HttpRequestToMuleEvent_Instrumentation; @Weave(type = MatchType.Interface, originalName = "org.mule.module.http.internal.listener.async.RequestHandler") public class RequestHandler_Instrumentation { + public void handleRequest(HttpRequestContext requestContext, HttpResponseReadyCallback responseCallback) { boolean isLockAcquired = acquireLockIfPossible(requestContext.hashCode()); if (isLockAcquired) { @@ -76,7 +79,9 @@ private void preprocessSecurityHook(HttpRequestContext requestContext) { // TODO: need to update UserClassEntity ServletHelper.registerUserLevelCode(MuleHelper.LIBRARY_NAME); securityRequest.setRequestParsed(true); - } catch (Throwable ignored){} + } catch (Throwable ignored){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, MuleHelper.MULE_3_7, ignored.getMessage()), ignored, HttpRequestToMuleEvent_Instrumentation.class.getName()); + } } private void postProcessSecurityHook() { @@ -101,9 +106,11 @@ private void postProcessSecurityHook() { ServletHelper.tmpFileCleanUp(NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getTempFiles()); } catch (Throwable e) { if(e instanceof NewRelicSecurityException){ - e.printStackTrace(); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, MuleHelper.MULE_3_7, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); throw e; } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, MuleHelper.MULE_3_7, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE , String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, MuleHelper.MULE_3_7, e.getMessage()), e, HttpRequestToMuleEvent_Instrumentation.class.getName()); } } diff --git a/instrumentation-security/play-2.13_2.7/src/main/scala/com/newrelic/agent/security/instrumentation/play2_13/GeneratedRouter_Instrumentation.scala b/instrumentation-security/play-2.13_2.7/src/main/scala/com/newrelic/agent/security/instrumentation/play2_13/GeneratedRouter_Instrumentation.scala index 2bddaae88..4dabe9884 100644 --- a/instrumentation-security/play-2.13_2.7/src/main/scala/com/newrelic/agent/security/instrumentation/play2_13/GeneratedRouter_Instrumentation.scala +++ b/instrumentation-security/play-2.13_2.7/src/main/scala/com/newrelic/agent/security/instrumentation/play2_13/GeneratedRouter_Instrumentation.scala @@ -1,7 +1,7 @@ package com.newrelic.agent.security.instrumentation.play2_13 import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper -import com.newrelic.api.agent.security.schema.ApplicationURLMapping +import com.newrelic.api.agent.security.schema.{ApplicationURLMapping, StringUtils} import com.newrelic.api.agent.weaver.{MatchType, Weave, Weaver} import play.api.routing.HandlerDef import play.core.routing.{HandlerInvoker, HandlerInvokerFactory} @@ -24,7 +24,8 @@ abstract class GeneratedRouter_Instrumentation { val iterator = documentation.iterator while (iterator.hasNext) { val doc = iterator.next - URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(doc._1, doc._2, doc._3)) + val handler = StringUtils.substringBeforeLast(doc._3, StringUtils.DOT_DELIMITER) + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(doc._1, doc._2, handler)) } } } diff --git a/instrumentation-security/play-2.4/src/main/scala/com/newrelic/agent/security/instrumentation/play24/GeneratedRouter_Instrumentation.scala b/instrumentation-security/play-2.4/src/main/scala/com/newrelic/agent/security/instrumentation/play24/GeneratedRouter_Instrumentation.scala index b0d071e24..3a69e0ce6 100644 --- a/instrumentation-security/play-2.4/src/main/scala/com/newrelic/agent/security/instrumentation/play24/GeneratedRouter_Instrumentation.scala +++ b/instrumentation-security/play-2.4/src/main/scala/com/newrelic/agent/security/instrumentation/play24/GeneratedRouter_Instrumentation.scala @@ -1,7 +1,7 @@ package com.newrelic.agent.security.instrumentation.play24 import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper -import com.newrelic.api.agent.security.schema.ApplicationURLMapping +import com.newrelic.api.agent.security.schema.{ApplicationURLMapping, StringUtils} import com.newrelic.api.agent.weaver.{MatchType, Weave, Weaver} import play.core.routing.{HandlerDef, HandlerInvoker} @@ -22,7 +22,8 @@ abstract class GeneratedRouter_Instrumentation { val iterator = documentation.iterator while (iterator.hasNext) { val doc = iterator.next - URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(doc._1, doc._2, doc._3)) + val handler = StringUtils.substringBeforeLast(doc._3, StringUtils.DOT_DELIMITER) + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(doc._1, doc._2, handler)) } } } diff --git a/instrumentation-security/play-2.6/src/main/scala/com/newrelic/agent/security/instrumentation/play26/GeneratedRouter_Instrumentation.scala b/instrumentation-security/play-2.6/src/main/scala/com/newrelic/agent/security/instrumentation/play26/GeneratedRouter_Instrumentation.scala index 079fae659..3d67607aa 100644 --- a/instrumentation-security/play-2.6/src/main/scala/com/newrelic/agent/security/instrumentation/play26/GeneratedRouter_Instrumentation.scala +++ b/instrumentation-security/play-2.6/src/main/scala/com/newrelic/agent/security/instrumentation/play26/GeneratedRouter_Instrumentation.scala @@ -1,7 +1,7 @@ package com.newrelic.agent.security.instrumentation.play26 import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper -import com.newrelic.api.agent.security.schema.ApplicationURLMapping +import com.newrelic.api.agent.security.schema.{ApplicationURLMapping, StringUtils} import com.newrelic.api.agent.weaver.{MatchType, Weave, Weaver} import play.api.routing.HandlerDef import play.core.routing.HandlerInvoker @@ -23,7 +23,8 @@ abstract class GeneratedRouter_Instrumentation { val iterator = documentation.iterator while (iterator.hasNext) { val doc = iterator.next - URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(doc._1, doc._2, doc._3)) + val handler = StringUtils.substringBeforeLast(doc._3, StringUtils.DOT_DELIMITER) + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(doc._1, doc._2, handler)) } } } diff --git a/instrumentation-security/resteasy-2.2/src/main/java/com/newrelic/agent/security/instrumentation/resteasy2/RestEasyHelper.java b/instrumentation-security/resteasy-2.2/src/main/java/com/newrelic/agent/security/instrumentation/resteasy2/RestEasyHelper.java index b7f94a8c6..43efac8a5 100644 --- a/instrumentation-security/resteasy-2.2/src/main/java/com/newrelic/agent/security/instrumentation/resteasy2/RestEasyHelper.java +++ b/instrumentation-security/resteasy-2.2/src/main/java/com/newrelic/agent/security/instrumentation/resteasy2/RestEasyHelper.java @@ -6,19 +6,31 @@ import com.newrelic.api.agent.security.schema.ApplicationURLMapping; import com.newrelic.api.agent.security.schema.StringUtils; import com.newrelic.api.agent.security.utils.logging.LogLevel; +import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import org.jboss.resteasy.core.ResourceInvoker; import org.jboss.resteasy.core.ResourceLocator; import org.jboss.resteasy.core.ResourceMethod; +import java.util.Collections; +import java.util.List; + public class RestEasyHelper { - private static final String WILDCARD = "*"; public static final String RESTEASY_22 = "RESTEASY-2.2"; + public static final String RESTEASY_SUB_RESOURCE_LIST = "SUB_RESOURCE_LIST"; public static final String ROUTE_DETECTION_COMPLETED = "ROUTE_DETECTION_COMPLETED"; public static void gatherUrlMappings(String path, ResourceInvoker invoker) { try{ + List subResourceList = Collections.emptyList(); + if (NewRelicSecurity.isHookProcessingActive()) { + subResourceList = NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(RESTEASY_SUB_RESOURCE_LIST, List.class); + } if(invoker instanceof ResourceMethod) { ResourceMethod methodInvoker = (ResourceMethod) invoker; + if (subResourceList != null && !subResourceList.isEmpty() && subResourceList.contains(methodInvoker.getResourceClass().getName())){ + return; + } String handler = methodInvoker.getResourceClass().getName(); for (String httpMethod: methodInvoker.getHttpMethods()){ @@ -28,13 +40,16 @@ public static void gatherUrlMappings(String path, ResourceInvoker invoker) { // case of SubResources else if(invoker instanceof ResourceLocator) { ResourceLocator locatorInvoker = (ResourceLocator) invoker; + if (subResourceList != null && !subResourceList.isEmpty() && subResourceList.contains(locatorInvoker.getMethod().getDeclaringClass().getName())){ + return; + } String handler = locatorInvoker.getMethod().getDeclaringClass().getName(); - String finalPath = StringUtils.appendIfMissing(path, StringUtils.SEPARATOR) + WILDCARD; + String finalPath = StringUtils.appendIfMissing(path, StringUtils.SEPARATOR) + URLMappingsHelper.WILDCARD; - URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, finalPath, handler)); + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(URLMappingsHelper.WILDCARD, finalPath, handler)); } } catch (Exception ignored){ - NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_ROUTE_FOR_INCOMING_REQUEST, RESTEASY_22, ignored.getMessage()), ignored, RestEasyHelper.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, RESTEASY_22, ignored.getMessage()), ignored, RestEasyHelper.class.getName()); } } } diff --git a/instrumentation-security/resteasy-2.2/src/main/java/org/jboss/resteasy/util/GetRestful_Instrumentation.java b/instrumentation-security/resteasy-2.2/src/main/java/org/jboss/resteasy/util/GetRestful_Instrumentation.java new file mode 100644 index 000000000..53fbadcf7 --- /dev/null +++ b/instrumentation-security/resteasy-2.2/src/main/java/org/jboss/resteasy/util/GetRestful_Instrumentation.java @@ -0,0 +1,29 @@ +package org.jboss.resteasy.util; + +import com.newrelic.agent.security.instrumentation.resteasy2.RestEasyHelper; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +import java.util.ArrayList; +import java.util.List; + +@Weave(originalName = "org.jboss.resteasy.util.GetRestful") +public class GetRestful_Instrumentation { + + private static boolean hasJAXRSAnnotations(Class c){ + boolean result = Weaver.callOriginal(); + if (NewRelicSecurity.isHookProcessingActive() && Boolean.TRUE.equals(result)){ + SecurityMetaData metaData = NewRelicSecurity.getAgent().getSecurityMetaData(); + List subResourceList = metaData.getCustomAttribute(RestEasyHelper.RESTEASY_SUB_RESOURCE_LIST, List.class); + if (subResourceList == null) { + subResourceList = new ArrayList<>(); + } + subResourceList.add(c.getName()); + metaData.addCustomAttribute(RestEasyHelper.RESTEASY_SUB_RESOURCE_LIST, subResourceList); + } + return result; + } + +} diff --git a/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java b/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java index 8c10e6923..772d0ed51 100644 --- a/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java +++ b/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java @@ -4,12 +4,15 @@ import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.security.schema.Framework; +import com.newrelic.api.agent.security.schema.SecurityMetaData; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Set; @@ -34,7 +37,7 @@ public static void addMappings() { } @Test - public void testAPIEndpointDetection() { + public void testAPIEndpointDetection() throws Exception { methodMapping.addMapping(new TestMappings()); Set mappings = URLMappingsHelper.getApplicationURLMappings(); @@ -42,9 +45,22 @@ public void testAPIEndpointDetection() { for (ApplicationURLMapping mapping: mappings) { Assert.assertNotNull(mapping); + // Assertions for URL Mappings assertMapping(mapping); + + // Assertions for Route Detection + assertRouteDetection(mapping); } + } + + private void assertRouteDetection(ApplicationURLMapping mapping) throws Exception { + methodMapping.handleRequest(new DummyRequest(mapping.getPath(), mapping.getMethod())); + SecurityMetaData metaData = SecurityInstrumentationTestRunner.getIntrospector().getSecurityMetaData(); + Assert.assertFalse(metaData.getRequest().getRoute().isEmpty()); + Assert.assertEquals(mapping.getPath(), metaData.getRequest().getRoute()); + + Assert.assertEquals(Framework.SPRING_WEB_MVC.name(), metaData.getMetaData().getFramework()); } private void assertMapping(ApplicationURLMapping actualMapping) { @@ -60,4 +76,7 @@ class TestHandlerMethodMapping extends RequestMappingHandlerMapping { public void addMapping(Object handler) { super.detectHandlerMethods(handler); } + public void handleRequest(HttpServletRequest request) throws Exception { + super.getHandlerInternal(request); + } } diff --git a/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/DummyRequest.java b/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/DummyRequest.java new file mode 100644 index 000000000..7c1eb6248 --- /dev/null +++ b/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/DummyRequest.java @@ -0,0 +1,328 @@ +package com.nr.agent.security.instrumentation.spring.webmvc; + +import com.newrelic.api.agent.security.schema.StringUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpUpgradeHandler; +import javax.servlet.http.Part; + +public class DummyRequest implements HttpServletRequest { + protected String queryString = StringUtils.EMPTY; + protected String pathInfo; + protected String servletPath = StringUtils.SEPARATOR; + private String method; + private static Enumeration dummyEnum = new Enumeration() { + public boolean hasMoreElements() { + return false; + } + + public String nextElement() { + return null; + } + }; + + public DummyRequest(String pathInfo, String method) { + this.pathInfo = pathInfo; + this.method = method; + } + + public String getContextPath() { + return null; + } + + public ServletRequest getRequest() { + return this; + } + + public String getQueryString() { + return this.queryString; + } + + public String getPathInfo() { + return this.pathInfo; + } + + public String getServletPath() { + return this.servletPath; + } + + public String getMethod() { + return this.method; + } + + public Object getAttribute(String name) { + return pathInfo; + } + + public Enumeration getAttributeNames() { + return null; + } + + public String getCharacterEncoding() { + return null; + } + + public int getContentLength() { + return -1; + } + + public long getContentLengthLong() { + return -1L; + } + + public String getContentType() { + return null; + } + + public ServletInputStream getInputStream() throws IOException { + return null; + } + + public Locale getLocale() { + return null; + } + + public Enumeration getLocales() { + return null; + } + + public String getProtocol() { + return null; + } + + public BufferedReader getReader() throws IOException { + return null; + } + + public String getRealPath(String path) { + return null; + } + + public String getRemoteAddr() { + return null; + } + + public String getRemoteHost() { + return null; + } + + public String getScheme() { + return null; + } + + public String getServerName() { + return null; + } + + public int getServerPort() { + return -1; + } + + public boolean isSecure() { + return false; + } + + public void removeAttribute(String name) { + } + + public void setAttribute(String name, Object value) { + } + + public void setCharacterEncoding(String enc) throws UnsupportedEncodingException { + } + + public String getParameter(String name) { + return null; + } + + public Map getParameterMap() { + return null; + } + + public Enumeration getParameterNames() { + return dummyEnum; + } + + public String[] getParameterValues(String name) { + return null; + } + + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + public String getAuthType() { + return null; + } + + public Cookie[] getCookies() { + return null; + } + + public long getDateHeader(String name) { + return -1L; + } + + public String getHeader(String name) { + return null; + } + + public Enumeration getHeaders(String name) { + return null; + } + + public Enumeration getHeaderNames() { + return null; + } + + public int getIntHeader(String name) { + return -1; + } + + public String getPathTranslated() { + return null; + } + + public String getRemoteUser() { + return null; + } + + public String getRequestedSessionId() { + return null; + } + + public String getRequestURI() { + return null; + } + + public StringBuffer getRequestURL() { + return null; + } + + public HttpSession getSession() { + return null; + } + + public HttpSession getSession(boolean create) { + return null; + } + + public String changeSessionId() { + return null; + } + + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + public boolean isRequestedSessionIdFromURL() { + return false; + } + + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + public boolean isRequestedSessionIdValid() { + return false; + } + + public void setRequestedSessionCookiePath(String cookiePath) { + } + + public boolean isUserInRole(String role) { + return false; + } + + public Principal getUserPrincipal() { + return null; + } + + public String getLocalAddr() { + return null; + } + + public String getLocalName() { + return null; + } + + public int getLocalPort() { + return -1; + } + + public int getRemotePort() { + return -1; + } + + public DispatcherType getDispatcherType() { + return null; + } + + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { + return null; + } + + public boolean isAsyncStarted() { + return false; + } + + public boolean isAsyncSupported() { + return false; + } + + public AsyncContext getAsyncContext() { + return null; + } + + public Collection getParts() { + return null; + } + + public Part getPart(String name) { + return null; + } + + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return false; + } + + public void login(String username, String password) throws ServletException { + } + + public void logout() throws ServletException { + + } + + public T upgrade(Class handlerClass) { + return null; + } + + public ServletContext getServletContext() { + return null; + } + +} + diff --git a/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java b/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java index d1b94dc24..31b25d089 100644 --- a/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java +++ b/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java @@ -4,12 +4,15 @@ import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.security.schema.Framework; +import com.newrelic.api.agent.security.schema.SecurityMetaData; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Set; @@ -34,7 +37,7 @@ public static void addMappings() { } @Test - public void testAPIEndpointDetection() { + public void testAPIEndpointDetection() throws Exception { methodMapping.addMapping(new TestMappings()); Set mappings = URLMappingsHelper.getApplicationURLMappings(); @@ -42,9 +45,22 @@ public void testAPIEndpointDetection() { for (ApplicationURLMapping mapping: mappings) { Assert.assertNotNull(mapping); + // Assertions for URL Mappings assertMapping(mapping); + + // Assertions for Route Detection + assertRouteDetection(mapping); } + } + + private void assertRouteDetection(ApplicationURLMapping mapping) throws Exception { + methodMapping.handleRequest(new DummyRequest(mapping.getPath(), mapping.getMethod())); + SecurityMetaData metaData = SecurityInstrumentationTestRunner.getIntrospector().getSecurityMetaData(); + Assert.assertFalse(metaData.getRequest().getRoute().isEmpty()); + Assert.assertEquals(mapping.getPath(), metaData.getRequest().getRoute()); + + Assert.assertEquals(Framework.SPRING_WEB_MVC.name(), metaData.getMetaData().getFramework()); } private void assertMapping(ApplicationURLMapping actualMapping) { @@ -60,4 +76,8 @@ class TestHandlerMethodMapping extends RequestMappingHandlerMapping { public void addMapping(Object handler) { super.detectHandlerMethods(handler); } + + public void handleRequest(HttpServletRequest request) throws Exception { + super.getHandlerInternal(request); + } } \ No newline at end of file diff --git a/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/DummyRequest.java b/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/DummyRequest.java new file mode 100644 index 000000000..69c965f96 --- /dev/null +++ b/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/DummyRequest.java @@ -0,0 +1,328 @@ +package com.nr.agent.security.instrumentation.spring.webmvc; + +import com.newrelic.api.agent.security.schema.StringUtils; + +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpUpgradeHandler; +import javax.servlet.http.Part; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; + +public class DummyRequest implements HttpServletRequest { + protected String queryString = StringUtils.EMPTY; + protected String pathInfo; + protected String servletPath = StringUtils.SEPARATOR; + private String method; + private static Enumeration dummyEnum = new Enumeration() { + public boolean hasMoreElements() { + return false; + } + + public String nextElement() { + return null; + } + }; + + public DummyRequest(String pathInfo, String method) { + this.pathInfo = pathInfo; + this.method = method; + } + + public String getContextPath() { + return null; + } + + public ServletRequest getRequest() { + return this; + } + + public String getQueryString() { + return this.queryString; + } + + public String getPathInfo() { + return this.pathInfo; + } + + public String getServletPath() { + return this.servletPath; + } + + public String getMethod() { + return this.method; + } + + public Object getAttribute(String name) { + return pathInfo; + } + + public Enumeration getAttributeNames() { + return null; + } + + public String getCharacterEncoding() { + return null; + } + + public int getContentLength() { + return -1; + } + + public long getContentLengthLong() { + return -1L; + } + + public String getContentType() { + return null; + } + + public ServletInputStream getInputStream() throws IOException { + return null; + } + + public Locale getLocale() { + return null; + } + + public Enumeration getLocales() { + return null; + } + + public String getProtocol() { + return null; + } + + public BufferedReader getReader() throws IOException { + return null; + } + + public String getRealPath(String path) { + return null; + } + + public String getRemoteAddr() { + return null; + } + + public String getRemoteHost() { + return null; + } + + public String getScheme() { + return null; + } + + public String getServerName() { + return null; + } + + public int getServerPort() { + return -1; + } + + public boolean isSecure() { + return false; + } + + public void removeAttribute(String name) { + } + + public void setAttribute(String name, Object value) { + } + + public void setCharacterEncoding(String enc) throws UnsupportedEncodingException { + } + + public String getParameter(String name) { + return null; + } + + public Map getParameterMap() { + return null; + } + + public Enumeration getParameterNames() { + return dummyEnum; + } + + public String[] getParameterValues(String name) { + return null; + } + + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + public String getAuthType() { + return null; + } + + public Cookie[] getCookies() { + return null; + } + + public long getDateHeader(String name) { + return -1L; + } + + public String getHeader(String name) { + return null; + } + + public Enumeration getHeaders(String name) { + return null; + } + + public Enumeration getHeaderNames() { + return null; + } + + public int getIntHeader(String name) { + return -1; + } + + public String getPathTranslated() { + return null; + } + + public String getRemoteUser() { + return null; + } + + public String getRequestedSessionId() { + return null; + } + + public String getRequestURI() { + return null; + } + + public StringBuffer getRequestURL() { + return null; + } + + public HttpSession getSession() { + return null; + } + + public HttpSession getSession(boolean create) { + return null; + } + + public String changeSessionId() { + return null; + } + + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + public boolean isRequestedSessionIdFromURL() { + return false; + } + + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + public boolean isRequestedSessionIdValid() { + return false; + } + + public void setRequestedSessionCookiePath(String cookiePath) { + } + + public boolean isUserInRole(String role) { + return false; + } + + public Principal getUserPrincipal() { + return null; + } + + public String getLocalAddr() { + return null; + } + + public String getLocalName() { + return null; + } + + public int getLocalPort() { + return -1; + } + + public int getRemotePort() { + return -1; + } + + public DispatcherType getDispatcherType() { + return null; + } + + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { + return null; + } + + public boolean isAsyncStarted() { + return false; + } + + public boolean isAsyncSupported() { + return false; + } + + public AsyncContext getAsyncContext() { + return null; + } + + public Collection getParts() { + return null; + } + + public Part getPart(String name) { + return null; + } + + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return false; + } + + public void login(String username, String password) throws ServletException { + } + + public void logout() throws ServletException { + + } + + public T upgrade(Class handlerClass) { + return null; + } + + public ServletContext getServletContext() { + return null; + } + +} + diff --git a/instrumentation-security/spring-webmvc-6.0.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java b/instrumentation-security/spring-webmvc-6.0.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java index d1b94dc24..168b08ae3 100644 --- a/instrumentation-security/spring-webmvc-6.0.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java +++ b/instrumentation-security/spring-webmvc-6.0.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java @@ -4,6 +4,9 @@ import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.security.schema.Framework; +import com.newrelic.api.agent.security.schema.SecurityMetaData; +import jakarta.servlet.http.HttpServletRequest; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -34,7 +37,7 @@ public static void addMappings() { } @Test - public void testAPIEndpointDetection() { + public void testAPIEndpointDetection() throws Exception { methodMapping.addMapping(new TestMappings()); Set mappings = URLMappingsHelper.getApplicationURLMappings(); @@ -42,9 +45,22 @@ public void testAPIEndpointDetection() { for (ApplicationURLMapping mapping: mappings) { Assert.assertNotNull(mapping); + // Assertions for URL Mappings assertMapping(mapping); + + // Assertions for Route Detection + assertRouteDetection(mapping); } + } + + private void assertRouteDetection(ApplicationURLMapping mapping) throws Exception { + methodMapping.handleRequest(new DummyRequest(mapping.getPath(), mapping.getMethod())); + SecurityMetaData metaData = SecurityInstrumentationTestRunner.getIntrospector().getSecurityMetaData(); + Assert.assertFalse(metaData.getRequest().getRoute().isEmpty()); + Assert.assertEquals(mapping.getPath(), metaData.getRequest().getRoute()); + + Assert.assertEquals(Framework.SPRING_WEB_MVC.name(), metaData.getMetaData().getFramework()); } private void assertMapping(ApplicationURLMapping actualMapping) { @@ -60,4 +76,8 @@ class TestHandlerMethodMapping extends RequestMappingHandlerMapping { public void addMapping(Object handler) { super.detectHandlerMethods(handler); } + + public void handleRequest(HttpServletRequest request) throws Exception { + super.getHandlerInternal(request); + } } \ No newline at end of file diff --git a/instrumentation-security/spring-webmvc-6.0.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/DummyRequest.java b/instrumentation-security/spring-webmvc-6.0.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/DummyRequest.java new file mode 100644 index 000000000..a68695bbb --- /dev/null +++ b/instrumentation-security/spring-webmvc-6.0.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/DummyRequest.java @@ -0,0 +1,380 @@ +package com.nr.agent.security.instrumentation.spring.webmvc; + +import com.newrelic.api.agent.security.schema.StringUtils; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.DispatcherType; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import jakarta.servlet.http.HttpUpgradeHandler; +import jakarta.servlet.http.Part; +import org.springframework.http.server.PathContainer; +import org.springframework.http.server.RequestPath; +import org.springframework.web.util.ServletRequestPathUtils; +import org.springframework.web.util.UrlPathHelper; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class DummyRequest implements HttpServletRequest { + protected String queryString = StringUtils.EMPTY; + protected String pathInfo; + protected String servletPath = StringUtils.SEPARATOR; + private String method; + private static Enumeration dummyEnum = new Enumeration() { + public boolean hasMoreElements() { + return false; + } + + public String nextElement() { + return null; + } + }; + + public DummyRequest(String pathInfo, String method) { + this.pathInfo = pathInfo; + this.method = method; + } + + public String getContextPath() { + return null; + } + + public String getQueryString() { + return this.queryString; + } + + public String getPathInfo() { + return this.pathInfo; + } + + public String getServletPath() { + return this.servletPath; + } + + public String getMethod() { + return this.method; + } + + public Object getAttribute(String name) { + if (name.equals(ServletRequestPathUtils.class.getName() + ".PATH")) { + return new RequestPath() { + @Override + public PathContainer contextPath() { + return new PathContainer() { + @Override + public String value() { + return pathInfo; + } + + @Override + public List elements() { + return Collections.emptyList(); + } + }; + } + + @Override + public PathContainer pathWithinApplication() { + return new PathContainer() { + @Override + public String value() { + return pathInfo; + } + + @Override + public List elements() { + return Collections.emptyList(); + } + }; + } + + @Override + public RequestPath modifyContextPath(String contextPath) { + return this; + } + + @Override + public String value() { + return ""; + } + + @Override + public List elements() { + return Collections.emptyList(); + } + }; + } else if (name.equals(UrlPathHelper.class.getName() + ".PATH") || name.equals("jakarta.servlet.include.request_uri")){ + return pathInfo; + } else { + return null; + } + } + + public Enumeration getAttributeNames() { + return null; + } + + public String getCharacterEncoding() { + return null; + } + + public int getContentLength() { + return -1; + } + + public long getContentLengthLong() { + return -1L; + } + + public String getContentType() { + return null; + } + + public ServletInputStream getInputStream() throws IOException { + return null; + } + + public Locale getLocale() { + return null; + } + + public Enumeration getLocales() { + return null; + } + + public String getProtocol() { + return null; + } + + public BufferedReader getReader() throws IOException { + return null; + } + + public String getRealPath(String path) { + return null; + } + + public String getRemoteAddr() { + return null; + } + + public String getRemoteHost() { + return null; + } + + public String getScheme() { + return null; + } + + public String getServerName() { + return null; + } + + public int getServerPort() { + return -1; + } + + public boolean isSecure() { + return false; + } + + public void removeAttribute(String name) { + } + + public void setAttribute(String name, Object value) { + } + + public void setCharacterEncoding(String enc) throws UnsupportedEncodingException { + } + + public String getParameter(String name) { + return null; + } + + public Map getParameterMap() { + return null; + } + + public Enumeration getParameterNames() { + return dummyEnum; + } + + public String[] getParameterValues(String name) { + return null; + } + + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + public String getAuthType() { + return null; + } + + public Cookie[] getCookies() { + return null; + } + + public long getDateHeader(String name) { + return -1L; + } + + public String getHeader(String name) { + return null; + } + + public Enumeration getHeaders(String name) { + return null; + } + + public Enumeration getHeaderNames() { + return null; + } + + public int getIntHeader(String name) { + return -1; + } + + public String getPathTranslated() { + return null; + } + + public String getRemoteUser() { + return null; + } + + public String getRequestedSessionId() { + return null; + } + + public String getRequestURI() { + return null; + } + + public StringBuffer getRequestURL() { + return null; + } + + public HttpSession getSession() { + return null; + } + + public HttpSession getSession(boolean create) { + return null; + } + + public String changeSessionId() { + return null; + } + + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + public boolean isRequestedSessionIdFromURL() { + return false; + } + + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + public boolean isRequestedSessionIdValid() { + return false; + } + + public void setRequestedSessionCookiePath(String cookiePath) { + } + + public boolean isUserInRole(String role) { + return false; + } + + public Principal getUserPrincipal() { + return null; + } + + public String getLocalAddr() { + return null; + } + + public String getLocalName() { + return null; + } + + public int getLocalPort() { + return -1; + } + + public int getRemotePort() { + return -1; + } + + public DispatcherType getDispatcherType() { + return null; + } + + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { + return null; + } + + public boolean isAsyncStarted() { + return false; + } + + public boolean isAsyncSupported() { + return false; + } + + public AsyncContext getAsyncContext() { + return null; + } + + public Collection getParts() { + return null; + } + + public Part getPart(String name) { + return null; + } + + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return false; + } + + public void login(String username, String password) throws ServletException { + } + + public void logout() throws ServletException {} + + public T upgrade(Class handlerClass) { + return null; + } + + public ServletContext getServletContext() { + return null; + } + +} + diff --git a/instrumentation-security/vertx-core-3.3.0/build.gradle b/instrumentation-security/vertx-core-3.3.0/build.gradle index 3f9e79eca..bea94f57b 100644 --- a/instrumentation-security/vertx-core-3.3.0/build.gradle +++ b/instrumentation-security/vertx-core-3.3.0/build.gradle @@ -3,6 +3,7 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") implementation("io.vertx:vertx-core:3.3.0") + implementation("io.vertx:vertx-web:3.3.0") } jar { @@ -10,7 +11,9 @@ jar { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-core:[3.3.0,3.4.0)' + passesOnly ('io.vertx:vertx-core:[3.3.0,3.4.0)') { + implementation("io.vertx:vertx-web:3.3.0") + } excludeRegex '.*(milestone|CR|Beta)[0-9]*' } diff --git a/instrumentation-security/vertx-core-3.3.0/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java b/instrumentation-security/vertx-core-3.3.0/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java new file mode 100644 index 000000000..39c730e82 --- /dev/null +++ b/instrumentation-security/vertx-core-3.3.0/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java @@ -0,0 +1,25 @@ +package io.vertx.core.http; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.http.HttpServer", type = MatchType.Interface) +public class HttpServer_Instrumentation { + + public HttpServer_Instrumentation requestHandler(Handler handler){ + HttpServer_Instrumentation server = Weaver.callOriginal(); + try { + VertxApiEndpointUtils.getInstance().generateAPIEndpoints(handler.hashCode()); + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, "VERTX-CORE-3.3.0", e.getMessage()), e, VertxApiEndpointUtils.class.getName()); + } + return server; + } + +} diff --git a/instrumentation-security/vertx-core-3.4.0/build.gradle b/instrumentation-security/vertx-core-3.4.0/build.gradle index 25d8d3d69..a65c8ff23 100644 --- a/instrumentation-security/vertx-core-3.4.0/build.gradle +++ b/instrumentation-security/vertx-core-3.4.0/build.gradle @@ -3,6 +3,7 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") implementation("io.vertx:vertx-core:3.4.0") + implementation("io.vertx:vertx-web:3.4.0") testImplementation('io.vertx:vertx-web-client:3.4.0') } @@ -11,7 +12,9 @@ jar { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-core:[3.4.0,3.7.1)' + passesOnly ('io.vertx:vertx-core:[3.4.0,3.7.1)'){ + implementation("io.vertx:vertx-web:3.4.0") + } excludeRegex '.*CR[0-9]*' excludeRegex '.*-milestone[0-9]' excludeRegex '.*Beta[0-9]' diff --git a/instrumentation-security/vertx-core-3.4.0/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java b/instrumentation-security/vertx-core-3.4.0/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java new file mode 100644 index 000000000..b7fbb5b67 --- /dev/null +++ b/instrumentation-security/vertx-core-3.4.0/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java @@ -0,0 +1,26 @@ +package io.vertx.core.http; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; + + +@Weave(originalName = "io.vertx.core.http.HttpServer", type = MatchType.Interface) +public class HttpServer_Instrumentation { + + public HttpServer_Instrumentation requestHandler(Handler handler){ + HttpServer_Instrumentation server = Weaver.callOriginal(); + try { + VertxApiEndpointUtils.getInstance().generateAPIEndpoints(handler.hashCode()); + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, "VERTX-CORE-3.4.0", e.getMessage()), e, VertxApiEndpointUtils.class.getName()); + } + return server; + } + +} diff --git a/instrumentation-security/vertx-core-3.7.1/build.gradle b/instrumentation-security/vertx-core-3.7.1/build.gradle index d5ee37175..3b1b8db62 100644 --- a/instrumentation-security/vertx-core-3.7.1/build.gradle +++ b/instrumentation-security/vertx-core-3.7.1/build.gradle @@ -9,11 +9,14 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") implementation("io.vertx:vertx-core:3.7.1") + implementation("io.vertx:vertx-web:3.7.1") testImplementation('io.vertx:vertx-web-client:3.7.1') } verifyInstrumentation { - passesOnly 'io.vertx:vertx-core:[3.7.1,4.0.0.Beta1)' + passesOnly ('io.vertx:vertx-core:[3.7.1,4.0.0.Beta1)') { + implementation('io.vertx:vertx-web:3.7.1') + } excludeRegex '.*CR[0-9]*' excludeRegex '.*-milestone[0-9]' } diff --git a/instrumentation-security/vertx-core-3.7.1/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java b/instrumentation-security/vertx-core-3.7.1/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java new file mode 100644 index 000000000..52435cc6f --- /dev/null +++ b/instrumentation-security/vertx-core-3.7.1/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java @@ -0,0 +1,25 @@ +package io.vertx.core.http; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.http.HttpServer", type = MatchType.Interface) +public class HttpServer_Instrumentation { + + public HttpServer_Instrumentation requestHandler(Handler handler){ + HttpServer_Instrumentation server = Weaver.callOriginal(); + try { + VertxApiEndpointUtils.getInstance().generateAPIEndpoints(handler.hashCode()); + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, "VERTX-CORE-3.7.1", e.getMessage()), e, VertxApiEndpointUtils.class.getName()); + } + return server; + } + +} diff --git a/instrumentation-security/vertx-core-4.0.0/build.gradle b/instrumentation-security/vertx-core-4.0.0/build.gradle index 50226b02a..8f8012a66 100644 --- a/instrumentation-security/vertx-core-4.0.0/build.gradle +++ b/instrumentation-security/vertx-core-4.0.0/build.gradle @@ -3,6 +3,7 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") implementation("io.vertx:vertx-core:4.0.0") + implementation("io.vertx:vertx-web:4.0.0") testImplementation('io.vertx:vertx-web-client:4.0.0') } @@ -13,7 +14,9 @@ jar { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-core:[4.0.0,)' + passesOnly ('io.vertx:vertx-core:[4.0.0,)') { + implementation('io.vertx:vertx-web:4.0.0') + } excludeRegex '.*CR[0-9]*' excludeRegex '.*-milestone[0-9]' excludeRegex '.*Beta[0-9]' diff --git a/instrumentation-security/vertx-core-4.0.0/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java b/instrumentation-security/vertx-core-4.0.0/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java new file mode 100644 index 000000000..d030b540a --- /dev/null +++ b/instrumentation-security/vertx-core-4.0.0/src/main/java/io/vertx/core/http/HttpServer_Instrumentation.java @@ -0,0 +1,26 @@ +package io.vertx.core.http; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; + + + +@Weave(originalName = "io.vertx.core.http.HttpServer", type = MatchType.Interface) +public class HttpServer_Instrumentation { + + public HttpServer_Instrumentation requestHandler(Handler handler){ + HttpServer_Instrumentation server = Weaver.callOriginal(); + try { + VertxApiEndpointUtils.getInstance().generateAPIEndpoints(handler.hashCode()); + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, "VERTX-CORE-4.0.0", e.getMessage()), e, VertxApiEndpointUtils.class.getName()); + } + return server; + } +} diff --git a/instrumentation-security/vertx-web-3.2.0/src/main/java/io/vertx/ext/web/Router_Instrumentation.java b/instrumentation-security/vertx-web-3.2.0/src/main/java/io/vertx/ext/web/Router_Instrumentation.java new file mode 100644 index 000000000..b89e88acc --- /dev/null +++ b/instrumentation-security/vertx-web-3.2.0/src/main/java/io/vertx/ext/web/Router_Instrumentation.java @@ -0,0 +1,25 @@ +package io.vertx.ext.web; + +import com.newrelic.api.agent.security.instrumentation.helpers.ThreadLocalLockHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "io.vertx.ext.web.Router", type = MatchType.Interface) +public class Router_Instrumentation { + + public Router_Instrumentation mountSubRouter(String mountPoint, Router subRouter) { + Router_Instrumentation result; + boolean isLockAcquired = ThreadLocalLockHelper.acquireLock(); + try { + result = Weaver.callOriginal(); + } finally { + if (isLockAcquired) { + ThreadLocalLockHelper.releaseLock(); + } + } + VertxApiEndpointUtils.getInstance().resolveSubRoutes(this.hashCode(), subRouter.hashCode(), mountPoint); + return result; + } +} diff --git a/instrumentation-security/vertx-web-3.2.0/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java b/instrumentation-security/vertx-web-3.2.0/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java index 9217471b8..7135ea14d 100644 --- a/instrumentation-security/vertx-web-3.2.0/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java +++ b/instrumentation-security/vertx-web-3.2.0/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java @@ -1,15 +1,80 @@ package io.vertx.ext.web.impl; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.Route; import io.vertx.ext.web.RoutingContext; +import java.util.regex.Pattern; + @Weave(originalName = "io.vertx.ext.web.impl.RouteImpl") -public class RouteImpl_Instrumentation { +public abstract class RouteImpl_Instrumentation { + + private final RouterImpl router = Weaver.callOriginal(); + + private String path = Weaver.callOriginal(); + + private Pattern pattern = Weaver.callOriginal(); + + RouteImpl_Instrumentation(RouterImpl router, int order){ + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, null, null); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, HttpMethod method, String path) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), path, null, method.name()); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, String path) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), path, null, null); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, HttpMethod method, String regex, boolean bregex) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, regex, method.name()); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, String regex, boolean bregex) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, regex, null); + } synchronized void handleContext(RoutingContext context) { + try { + VertxApiEndpointUtils.getInstance().generateAPIEndpointsIfNotPresent(this.hashCode()); + VertxApiEndpointUtils.getInstance().routeDetection(path, pattern); + } catch (Exception e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_ROUTE_FOR_INCOMING_REQUEST, "VERTX-WEB-3.2.0", e.getMessage()), e, this.getClass().getName()); + } ServletHelper.registerUserLevelCode("vertx-web"); Weaver.callOriginal(); } + + public synchronized Route method(HttpMethod method) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, null, method.name()); + return route; + } + + public synchronized Route path(String path) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), path, null, null); + return route; + } + + public synchronized Route pathRegex(String regex) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, regex, null); + return route; + } + + public synchronized Route handler(Handler contextHandler){ + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addHandlerClass(router.hashCode(), this.hashCode(), contextHandler.getClass().getName()); + return route; + } } diff --git a/instrumentation-security/vertx-web-3.5.1/src/main/java/io/vertx/ext/web/Router_Instrumentation.java b/instrumentation-security/vertx-web-3.5.1/src/main/java/io/vertx/ext/web/Router_Instrumentation.java new file mode 100644 index 000000000..6d788d82e --- /dev/null +++ b/instrumentation-security/vertx-web-3.5.1/src/main/java/io/vertx/ext/web/Router_Instrumentation.java @@ -0,0 +1,25 @@ +package io.vertx.ext.web; + +import com.newrelic.api.agent.security.instrumentation.helpers.ThreadLocalLockHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "io.vertx.ext.web.Router", type = MatchType.Interface) +public class Router_Instrumentation { + + public Router_Instrumentation mountSubRouter(String mountPoint, Router subRouter) { + Router_Instrumentation result; + boolean isLockAcquired = ThreadLocalLockHelper.acquireLock(); + try { + result = Weaver.callOriginal(); + } finally { + if (isLockAcquired) { + ThreadLocalLockHelper.releaseLock(); + } + } + VertxApiEndpointUtils.getInstance().resolveSubRoutes(this.hashCode(), subRouter.hashCode(), mountPoint); + return result; + } +} \ No newline at end of file diff --git a/instrumentation-security/vertx-web-3.5.1/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java b/instrumentation-security/vertx-web-3.5.1/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java index f69d47e2d..683a6cfdb 100644 --- a/instrumentation-security/vertx-web-3.5.1/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java +++ b/instrumentation-security/vertx-web-3.5.1/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java @@ -1,14 +1,81 @@ package io.vertx.ext.web.impl; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.security.schema.Framework; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.RoutingContext; + +import java.util.regex.Pattern; @Weave(originalName = "io.vertx.ext.web.impl.RouteImpl") public class RouteImpl_Instrumentation { + private final RouterImpl router = Weaver.callOriginal(); + + private String path = Weaver.callOriginal(); + + private Pattern pattern = Weaver.callOriginal(); + + RouteImpl_Instrumentation(RouterImpl router, int order){ + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, null, null); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, HttpMethod method, String path) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), path, null, method.name()); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, String path) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), path, null, null); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, HttpMethod method, String regex, boolean bregex) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, regex, method.name()); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, String regex, boolean bregex) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, regex, null); + } + void handleContext(RoutingContextImplBase context) { + try { + VertxApiEndpointUtils.getInstance().generateAPIEndpointsIfNotPresent(this.hashCode()); + VertxApiEndpointUtils.getInstance().routeDetection(path, pattern); + } catch (Exception e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_ROUTE_FOR_INCOMING_REQUEST, "VERTX-WEB-3.5.1", e.getMessage()), e, this.getClass().getName()); + } ServletHelper.registerUserLevelCode("vertx-web"); Weaver.callOriginal(); } + + public synchronized Route method(HttpMethod method) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, null, method.name()); + return route; + } + + public synchronized Route path(String path) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), path, null, null); + return route; + } + + public synchronized Route pathRegex(String regex) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, regex, null); + return route; + } + + public synchronized Route handler(Handler contextHandler){ + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addHandlerClass(router.hashCode(), this.hashCode(), contextHandler.getClass().getName()); + return route; + } } diff --git a/instrumentation-security/vertx-web-3.8.3/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java b/instrumentation-security/vertx-web-3.8.3/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java new file mode 100644 index 000000000..04026fe3c --- /dev/null +++ b/instrumentation-security/vertx-web-3.8.3/src/main/java/io/vertx/ext/web/impl/RouteImpl_Instrumentation.java @@ -0,0 +1,70 @@ +package io.vertx.ext.web.impl; + +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +@Weave(originalName = "io.vertx.ext.web.impl.RouteImpl", type = MatchType.ExactClass) +public class RouteImpl_Instrumentation { + + private final RouterImpl router = Weaver.callOriginal(); + + private volatile RouteState state = Weaver.callOriginal(); + + RouteImpl_Instrumentation(RouterImpl router, int order){ + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, null, null); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, HttpMethod method, String path) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), path, null, method.name()); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, String path) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), path, null, null); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, HttpMethod method, String regex, boolean bregex) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, regex, method.name()); + } + + RouteImpl_Instrumentation(RouterImpl router, int order, String regex, boolean bregex) { + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, regex, null); + } + + public synchronized Route method(HttpMethod method) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, null, method.name()); + return route; + } + + public synchronized Route path(String path) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), path, null, null); + return route; + } + + public synchronized Route pathRegex(String regex) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addRouteImpl(router.hashCode(), this.hashCode(), null, regex, null); + return route; + } + + public synchronized Route handler(Handler contextHandler){ + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().addHandlerClass(router.hashCode(), this.hashCode(), contextHandler.getClass().getName()); + return route; + } + + public synchronized Route subRouter(Router subRouter) { + Route route = Weaver.callOriginal(); + VertxApiEndpointUtils.getInstance().removeRouteImpl(router.hashCode(), this.hashCode()); + VertxApiEndpointUtils.getInstance().resolveSubRoutes(router.hashCode(), subRouter.hashCode(), VertxApiEndpointUtils.getInstance().getPath(state.getPath(), state.getPattern())); + return route; + } +} diff --git a/instrumentation-security/vertx-web-3.8.3/src/main/java/io/vertx/ext/web/impl/RouteState_Instrumentation.java b/instrumentation-security/vertx-web-3.8.3/src/main/java/io/vertx/ext/web/impl/RouteState_Instrumentation.java index acb7d0c98..92a1fae66 100644 --- a/instrumentation-security/vertx-web-3.8.3/src/main/java/io/vertx/ext/web/impl/RouteState_Instrumentation.java +++ b/instrumentation-security/vertx-web-3.8.3/src/main/java/io/vertx/ext/web/impl/RouteState_Instrumentation.java @@ -1,13 +1,36 @@ package io.vertx.ext.web.impl; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.VertxApiEndpointUtils; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import java.util.regex.Pattern; + @Weave(originalName = "io.vertx.ext.web.impl.RouteState") abstract class RouteState_Instrumentation { + + private final RouteImpl route = Weaver.callOriginal(); + void handleContext(RoutingContextImplBase context){ + try { + VertxApiEndpointUtils.getInstance().generateAPIEndpointsIfNotPresent(route.hashCode()); + VertxApiEndpointUtils.getInstance().routeDetection(getPath(), getPattern()); + } catch (Exception e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_ROUTE_FOR_INCOMING_REQUEST, "VERTX-WEB-3.8.3", e.getMessage()), e, this.getClass().getName()); + } ServletHelper.registerUserLevelCode("vertx-web"); Weaver.callOriginal(); } + + public String getPath() { + return Weaver.callOriginal(); + } + + public Pattern getPattern() { + return Weaver.callOriginal(); + } } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/AgentConfig.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/AgentConfig.java index d2216244e..84bda35e3 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/AgentConfig.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/AgentConfig.java @@ -44,7 +44,7 @@ public class AgentConfig { private boolean isNRSecurityEnabled; - private static final FileLoggerThreadPool logger = FileLoggerThreadPool.getInstance(); + private static FileLoggerThreadPool logger; private OSVariables osVariables; @@ -64,6 +64,8 @@ public void instantiate(){ groupName = applyRequiredGroup(); //Instantiation call please do not move or repeat this. osVariables = OsVariablesInstance.instantiate().getOsVariables(); + + logger = FileLoggerThreadPool.getInstance(); // Set required LogLevel logLevel = applyRequiredLogLevel(); } @@ -177,6 +179,9 @@ public void setConfig(CollectorConfig config) { } public void createSnapshotDirectory() throws IOException { + if (osVariables.getSnapshotDir() == null){ + return; + } Path snapshotDir = Paths.get(osVariables.getSnapshotDir()); // Remove any file with this name from target. if (!snapshotDir.toFile().isDirectory()) { diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/os/OsVariablesInstance.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/os/OsVariablesInstance.java index 1e8d5c30f..86964a2a6 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/os/OsVariablesInstance.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/os/OsVariablesInstance.java @@ -29,8 +29,8 @@ private OsVariablesInstance() { if(StringUtils.isNotBlank(AgentConfig.getInstance().getSecurityHome())) { osVariables.setLogDirectory(Paths.get(AgentConfig.getInstance().getSecurityHome(), LOGS).toString()); osVariables.setTmpDirectory(Paths.get(AgentConfig.getInstance().getSecurityHome(), TMP, LANGUAGE_AGENT, AgentInfo.getInstance().getApplicationUUID()).toString()); + osVariables.setSnapshotDir(Paths.get(osVariables.getLogDirectory(), SNAPSHOTS).toString()); } - osVariables.setSnapshotDir(Paths.get(osVariables.getLogDirectory(), SNAPSHOTS).toString()); // osVariables.setPolicyConfigPath(Paths.get(k2root.toString(), CONFIG, LANGUAGE_AGENT).toString()); if (SystemUtils.IS_OS_LINUX) { diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/controlcommand/ControlCommandProcessor.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/controlcommand/ControlCommandProcessor.java index 828eef61b..293943e48 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/controlcommand/ControlCommandProcessor.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/controlcommand/ControlCommandProcessor.java @@ -115,6 +115,7 @@ public void run() { case IntCodeControlCommand.UNSUPPORTED_AGENT: logger.log(LogLevel.SEVERE, controlCommand.getArguments().get(0), ControlCommandProcessor.class.getSimpleName()); + NewRelic.noticeError("Incompatible New Relic Security Agent: " + controlCommand.getArguments().get(0), true); System.err.println(controlCommand.getArguments().get(0)); NewRelic.getAgent().getLogger().log(Level.SEVERE, controlCommand.getArguments().get(0)); InstrumentationUtils.shutdownLogic(true); diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/logging/HealthCheckScheduleThread.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/logging/HealthCheckScheduleThread.java index 71208cc35..308d513da 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/logging/HealthCheckScheduleThread.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/logging/HealthCheckScheduleThread.java @@ -142,6 +142,9 @@ private void writeStatusLogFile(JAHealthCheck sendJaHealthCheck) { if(writerHealthCheck == null){ writerHealthCheck = AgentInfo.getInstance().getJaHealthCheck(); } + if (osVariables.getSnapshotDir() == null){ + return; + } File statusLog = new File(osVariables.getSnapshotDir(), String.format(K_2_AGENT_STATUS_LOG, AgentInfo.getInstance().getApplicationUUID())); try { FileUtils.deleteQuietly(statusLog); diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/websocket/WSClient.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/websocket/WSClient.java index 8f24f3f8b..338fb464d 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/websocket/WSClient.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/websocket/WSClient.java @@ -89,6 +89,7 @@ private SSLContext createSSLContext() throws Exception { } catch (Exception e) { logger.log(LogLevel.SEVERE, "Unable to generate ca certificate. Verify the certificate format. Will not process further certs.", e, WSClient.class.getName()); + NewRelic.noticeError(new SecurityNoticeError("New Relic Security Agent is unable to generate CA Certificate. Verify the certificate format. Will not process further certs.", e), noticeErrorCustomParameters, true); break; } } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java b/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java index 535aa27b5..f0241ac02 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java @@ -323,16 +323,18 @@ public void registerOperation(AbstractOperation operation) { // boolean blockNeeded = checkIfBlockingNeeded(operation.getApiID()); // securityMetaData.getMetaData().setApiBlocked(blockNeeded); HttpRequest request = securityMetaData.getRequest(); -// if (StringUtils.isEmpty(request.getRoute())){ Framework frameWork = Framework.UNKNOWN; if(!securityMetaData.getFuzzRequestIdentifier().getK2Request() && StringUtils.isNotBlank(securityMetaData.getMetaData().getFramework())) { frameWork = Framework.valueOf(securityMetaData.getMetaData().getFramework()); } if (!securityMetaData.getFuzzRequestIdentifier().getK2Request() && StringUtils.isEmpty(request.getRoute())){ - request.setRoute(getEndpointRoute(StringUtils.substringBefore(request.getUrl(), "?"), frameWork), true); - logger.log(LogLevel.FINEST,"Route detection using Application Endpoint", this.getClass().getName()); + String route = getEndpointRoute(StringUtils.substringBefore(request.getUrl(), "?"), frameWork); + if (route != null) { + request.setRoute(route); + logger.log(LogLevel.FINEST,"Route detection using Application Endpoint", this.getClass().getName()); + } } -// } + if (needToGenerateEvent(operation.getApiID())) { DispatcherPool.getInstance().dispatchEvent(operation, securityMetaData); if (!firstEventProcessed.get()) { @@ -358,7 +360,7 @@ private String getEndpointRoute(String uri, Framework framework){ private String getEndpointRoute(String uri) { List uriSegments = URLMappingsHelper.getSegments(uri); if (uriSegments.isEmpty()){ - return StringUtils.EMPTY; + return null; } for (RouteSegments routeSegments : URLMappingsHelper.getRouteSegments()) { int uriSegIdx = 0; @@ -384,7 +386,7 @@ private String getEndpointRoute(String uri) { } } } - return StringUtils.EMPTY; + return null; } private int jumpRoute(List value, int i1, List uriSegments, int i) { @@ -881,13 +883,21 @@ public void retransformUninstrumentedClass(Class classToRetransform) { @Override public String decryptAndVerify(String encryptedData, String hashVerifier) { - String decryptedData = EncryptorUtils.decrypt(AgentInfo.getInstance().getLinkingMetadata().get(INRSettingsKey.NR_ENTITY_GUID), encryptedData); - if(EncryptorUtils.verifyHashData(hashVerifier, decryptedData)) { - return decryptedData; - } else { - NewRelic.getAgent().getLogger().log(Level.WARNING, String.format("Agent data decryption verifier fails on data : %s hash : %s", encryptedData, hashVerifier), Agent.class.getName()); - return null; + boolean lockAcquired = ThreadLocalLockHelper.acquireLock(); + try { + if (lockAcquired) { + String decryptedData = EncryptorUtils.decrypt(AgentInfo.getInstance().getLinkingMetadata().get(INRSettingsKey.NR_ENTITY_GUID), encryptedData); + if (EncryptorUtils.verifyHashData(hashVerifier, decryptedData)) { + return decryptedData; + } else { + NewRelic.getAgent().getLogger().log(Level.WARNING, String.format("Agent data decryption verifier fails on data : %s hash : %s", encryptedData, hashVerifier), Agent.class.getName()); + return null; + } + } + } finally { + ThreadLocalLockHelper.releaseLock(); } + return null; } @Override diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/URLMappingsHelper.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/URLMappingsHelper.java index a71fce012..9a045a8e2 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/URLMappingsHelper.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/URLMappingsHelper.java @@ -31,8 +31,14 @@ public class URLMappingsHelper { add("org.eclipse.jetty.ee8.jsp.JettyJspServlet"); add("org.eclipse.jetty.ee8.servlet.DefaultServlet"); add("org.eclipse.jetty.servlet.NoJspServlet"); - add("javax.faces.webapp.FacesServlet"); add("org.apache.cxf.transport.servlet.CXFServlet"); + add("javax.faces.webapp.FacesServlet"); + add("jakarta.faces.webapp.FacesServlet"); + add("weblogic.servlet.JSPServlet"); + add("weblogic.servlet.FileServlet"); + add("weblogic.management.rest.JerseyServlet"); + add("com.caucho.jsp.XtpServlet"); + add("com.caucho.jsp.JspServlet"); }}; public static Set getApplicationURLMappings() { diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/VertxApiEndpointUtils.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/VertxApiEndpointUtils.java new file mode 100644 index 000000000..47de7398a --- /dev/null +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/VertxApiEndpointUtils.java @@ -0,0 +1,179 @@ +package com.newrelic.api.agent.security.instrumentation.helpers; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.security.schema.Framework; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.security.schema.helper.VertxRoute; +import com.newrelic.api.agent.security.utils.logging.LogLevel; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +public class VertxApiEndpointUtils { + + private static final class InstanceHolder { + static final VertxApiEndpointUtils instance = new VertxApiEndpointUtils(); + } + private final String VERTX_FRAMEWORK = "VERTX-FRAMEWORK"; + + public static VertxApiEndpointUtils getInstance() { + return InstanceHolder.instance; + } + + private VertxApiEndpointUtils() {} + + private final Map> routes = new ConcurrentHashMap<>(); + + private void clear(){ + routes.clear(); + } + + private void addRouteImpl(int routerHashCode, int routeHashCode) throws Exception { + boolean isLockAcquired = ThreadLocalLockHelper.acquireLock(); + try { + if (isLockAcquired) { + VertxRoute route = new VertxRoute(routerHashCode, routeHashCode); + if (!routes.containsKey(routerHashCode)) { + routes.put(routerHashCode, new ConcurrentHashMap<>()); + } + routes.get(routerHashCode).put(routeHashCode, route); + } + } finally { + if (isLockAcquired) { + ThreadLocalLockHelper.releaseLock(); + } + } + } + + public void addRouteImpl(int routerHashCode, int routeHashCode, String path, String pattern, String method){ + try { + addRouteImpl(routerHashCode, routeHashCode); + if (!routes.containsKey(routerHashCode)){ + return; + } + VertxRoute route = routes.get(routerHashCode).get(routeHashCode); + if (route == null){ + return; + } + if (path != null) { + route.setPath(path); + } + if (pattern != null) { + route.setPattern(pattern); + } + if (method != null) { + route.getMethods().add(method); + } + } catch (Exception e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, VERTX_FRAMEWORK, e.getMessage()), e, VertxApiEndpointUtils.class.getName()); + } + } + + public void addHandlerClass(int routerHashCode, int routeHashCode, String handlerName){ + try { + if (!routes.containsKey(routerHashCode)){ + return; + } + VertxRoute route = routes.get(routerHashCode).get(routeHashCode); + if (route == null || !Objects.isNull(route.getHandlerName())){ + return; + } + route.setHandlerName(handlerName); + } catch (Exception e) {} + } + + public void resolveSubRoutes(int parentRouterHashCode, int childRouterHashCode, String path){ + try { + if (path == null || !routes.containsKey(childRouterHashCode) || !routes.containsKey(parentRouterHashCode)){ + return; + } + + for (Map.Entry vertxRoute : routes.get(childRouterHashCode).entrySet()) { + VertxRoute route = vertxRoute.getValue(); + if (StringUtils.equalsAny(route.getHandlerName(), "io.vertx.ext.web.handler.impl.BodyHandlerImpl", "io.vertx.ext.web.handler.BodyHandler")){ + continue; + } + String subRoutePath = getPath(route.getPath(), route.getPattern()); + route.setPath(StringUtils.removeEnd(path, StringUtils.SEPARATOR) + StringUtils.prependIfMissing(subRoutePath, StringUtils.SEPARATOR)); + route.setRouterHashCode(parentRouterHashCode); + routes.get(parentRouterHashCode).put(route.getRouteHashCode(), route); + } + } catch (Exception e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, VERTX_FRAMEWORK, e.getMessage()), e, VertxApiEndpointUtils.class.getName()); + } + } + + public void generateAPIEndpoints(int routerHashCode){ + if (!routes.containsKey(routerHashCode)){ + return; + } + for (Map.Entry vertxRoute : routes.get(routerHashCode).entrySet()){ + VertxRoute routeImpl = vertxRoute.getValue(); + String handlerName = routeImpl.getHandlerName(); + if (StringUtils.equalsAny(handlerName, "io.vertx.ext.web.handler.impl.BodyHandlerImpl", "io.vertx.ext.web.handler.BodyHandler")){ + continue; + } + if (handlerName != null){ + handlerName = StringUtils.substringBefore(routeImpl.getHandlerName(), StringUtils.SEPARATOR); + } + if (routeImpl.getMethods().isEmpty()){ + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(URLMappingsHelper.WILDCARD, getPath(routeImpl.getPath(), routeImpl.getPattern()), handlerName)); + } + for (String method : routeImpl.getMethods()) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(method, getPath(routeImpl.getPath(), routeImpl.getPattern()), handlerName)); + } + } + clear(); + } + + public void removeRouteImpl(int routerHashCode, int routeHashCode){ + try { + if (routes.containsKey(routerHashCode) && routes.get(routerHashCode).containsKey(routeHashCode)){ + VertxRoute route = routes.get(routerHashCode).remove(routeHashCode); + if (route != null && !URLMappingsHelper.getApplicationURLMappings().isEmpty()){ + URLMappingsHelper.getApplicationURLMappings().remove(new ApplicationURLMapping(URLMappingsHelper.WILDCARD, getPath(route.getPath(), route.getPattern()))); + } + } + } catch (Exception e) {} + } + + public String getPath(String path, Object pattern) { + if (path != null){ + return path; + } + if (pattern instanceof Pattern) { + return ((Pattern) pattern).pattern(); + } + if (pattern instanceof String) { + return (String) pattern; + } + return URLMappingsHelper.subResourceSegment; + } + + public void routeDetection(String path, Pattern pattern) { + if (NewRelicSecurity.isHookProcessingActive()){ + String route = StringUtils.EMPTY; + if (path != null){ + route = path; + } else if (pattern != null){ + route = pattern.pattern(); + } + if (URLMappingsHelper.getSegmentCount(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().getRoute()) == URLMappingsHelper.getSegmentCount(route)) { + return; + } + NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().setRoute(route, Objects.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().getFramework(), Framework.SERVLET.name())); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFramework(Framework.VERTX); + } + } + + public void generateAPIEndpointsIfNotPresent(int routeHashCode) { + for (Map.Entry> routesSet : routes.entrySet()) { + if (routesSet.getValue().containsKey(routeHashCode)) { + generateAPIEndpoints(routesSet.getKey()); + } + } + } +} diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/StringUtils.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/StringUtils.java index 82808436d..e9f8e89b3 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/StringUtils.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/StringUtils.java @@ -12,8 +12,12 @@ public class StringUtils { public static final String LF = "\n"; public static final int INDEX_NOT_FOUND = -1; public static final String COMMA_DELIMETER = ","; + + public static final String DOT_DELIMITER = "."; + public static final String SEPARATOR = "/"; + /** *

Checks if a CharSequence is not empty (""), not null and not whitespace only.

* diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/helper/VertxRoute.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/helper/VertxRoute.java new file mode 100644 index 000000000..bbd1625c2 --- /dev/null +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/helper/VertxRoute.java @@ -0,0 +1,83 @@ +package com.newrelic.api.agent.security.schema.helper; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class VertxRoute { + + private int routerHashCode; + private int routeHashCode; + private String path; + private String pattern; + private Set methods; + private String handlerName; + + public VertxRoute(int routerHashCode, int routeHashCode) { + this.routerHashCode = routerHashCode; + this.routeHashCode = routeHashCode; + this.methods = new HashSet<>(); + } + + public int getRouterHashCode() { + return routerHashCode; + } + + public void setRouterHashCode(int routerHashCode) { + this.routerHashCode = routerHashCode; + } + + public int getRouteHashCode() { + return routeHashCode; + } + + public void setRouteHashCode(int routeHashCode) { + this.routeHashCode = routeHashCode; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public Set getMethods() { + return methods; + } + + public void setMethods(Set methods) { + this.methods = methods; + } + + public String getHandlerName() { + return handlerName; + } + + public void setHandlerName(String handlerName) { + this.handlerName = handlerName; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof VertxRoute)) { + return false; + } + return Objects.equals(routerHashCode, ((VertxRoute) obj).routerHashCode) && + Objects.equals(routeHashCode, ((VertxRoute) obj).routeHashCode); + } + + @Override + public int hashCode() { + return Objects.hash(routerHashCode, routeHashCode); + } +} diff --git a/settings.gradle b/settings.gradle index bc07ef644..b3b2cd194 100644 --- a/settings.gradle +++ b/settings.gradle @@ -209,6 +209,7 @@ include 'instrumentation:weblogic-12.2' include 'instrumentation:jedis-4.0.0' include 'instrumentation:jedis-3.0.0' include 'instrumentation:jedis-2.7.1_2.7.2' +include 'instrumentation:jedis-1.4.0' include 'instrumentation:solr-4.0.0' include 'instrumentation:solr-5.0.0' include 'instrumentation:solr-5.1.0'