diff --git a/Changelog.md b/Changelog.md index 1cfb63cde..c137963d8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,35 @@ 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.3.0] - 2024-5-16 +### Changes +- [PR-186](https://github.com/newrelic/csec-java-agent/pull/186) Feature to detect API Endpoint of the Application [NR-222163](https://new-relic.atlassian.net/browse/NR-222163) +- [PR-132](https://github.com/newrelic/csec-java-agent/pull/132) JCache Support : The security agent now also supports jCache 1.0.0 and above [NR-175383](https://new-relic.atlassian.net/browse/NR-175383) +- [PR-193](https://github.com/newrelic/csec-java-agent/pull/193) Spray HTTP Server Support : The security agent now also supports Spray HTTP Server version 1.3.1 and above (with scala 2.11 and above) [NR-230246](https://new-relic.atlassian.net/browse/NR-230246), [NR-230248](https://new-relic.atlassian.net/browse/NR-230248) +- [PR-195](https://github.com/newrelic/csec-java-agent/pull/195) Spray Can Server Support : The security agent now also supports Spray Can Server version 1.3.1 and above (with scala 2.11 and above) [NR-230246](https://new-relic.atlassian.net/browse/NR-230246), [NR-230248](https://new-relic.atlassian.net/browse/NR-230248) +- [PR-194](https://github.com/newrelic/csec-java-agent/pull/194) Spray Client Support : The security agent now also supports Spray Client version 1.3.1 and above (with scala 2.11 and above) [NR-230243](https://new-relic.atlassian.net/browse/NR-230243), [NR-230245](https://new-relic.atlassian.net/browse/NR-230245) +- [PR-202](https://github.com/newrelic/csec-java-agent/pull/202) Netty Server support : The security agent now also supports Netty Server version 4.0.0.Final and above. [NR-234864](https://new-relic.atlassian.net/browse/NR-234864) +- [PR-220](https://github.com/newrelic/csec-java-agent/pull/220) Netty Reactor Server support : The security agent now also supports Netty Reactor Server version 0.7.0.RELEASE and above. [NR-249812](https://new-relic.atlassian.net/browse/NR-249812) +- [PR-239](https://github.com/newrelic/csec-java-agent/pull/239) Spring WebClient Support : The security agent now also supports Spring WebClient version 5.0.0.RELEASE and above. [NR-258894](https://new-relic.atlassian.net/browse/NR-258894), [NR-258895](https://new-relic.atlassian.net/browse/NR-258895) +- [PR-219](https://github.com/newrelic/csec-java-agent/pull/219) Enable functionality to scan NewRelic applications using `security.is_home_app` config, default value is false +- [PR-217](https://github.com/newrelic/csec-java-agent/pull/217) Revamp user class detection technique, use server level endpoints. [NR-211161](https://new-relic.atlassian.net/browse/NR-211161) +- Resin Support : The security agent now also supports resin server [NR-171577](https://new-relic.atlassian.net/browse/NR-171577) +- Anorm Support : The security agent now also supports Anorm Datastore version 2.0 to 2.5 [NR-171575](https://new-relic.atlassian.net/browse/NR-171575) + +### Fixes +- [PR-202](https://github.com/newrelic/csec-java-agent/pull/202) Extract Server Configuration to resolve IAST localhost connection with application for Netty server. [NR-238324](https://new-relic.atlassian.net/browse/NR-238324) +- [PR-237](https://github.com/newrelic/csec-java-agent/pull/237) Fix for Correct User Class Detection in Sun-Net-HttpServer [NR-254564](https://new-relic.atlassian.net/browse/NR-254564) +- [PR-243](https://github.com/newrelic/csec-java-agent/pull/243) Improvement in fallback mechanism for NR_CSEC_HOME [NR-260723](https://new-relic.atlassian.net/browse/NR-260723) +- [PR-248](https://github.com/newrelic/csec-java-agent/pull/248) Fix for Regression in File Integrity Event Generation [NR-267172](https://new-relic.atlassian.net/browse/NR-267172) +- [PR-249](https://github.com/newrelic/csec-java-agent/pull/249), [PR-244](https://github.com/newrelic/csec-java-agent/pull/244) Improvements in IAST Replay [NR-267169](https://new-relic.atlassian.net/browse/NR-267169), [NR-265208](https://new-relic.atlassian.net/browse/NR-265208) +- [PR-235](https://github.com/newrelic/csec-java-agent/pull/235) Fix for NullPointerException observed in JDBC-GENERIC [NR-232657](https://new-relic.atlassian.net/browse/NR-232657) +- [PR-226](https://github.com/newrelic/csec-java-agent/pull/226) Fix for NoClassDefFoundError observed in JAVAX-JNDI Instrumentation [NR-254566](https://new-relic.atlassian.net/browse/NR-254566) +- [PR-225](https://github.com/newrelic/csec-java-agent/pull/225) Fix for FileAlreadyExistException observed in IAST Replay [NR-254565](https://new-relic.atlassian.net/browse/NR-254565) +- [PR-222](https://github.com/newrelic/csec-java-agent/pull/222) Exclude Milestone Release for Jax-RS, due to release of version 4.0.0-M2 on 9th March 2024 [NR-256459](https://new-relic.atlassian.net/browse/NR-256459) +- [PR-232](https://github.com/newrelic/csec-java-agent/pull/232) Exclude Latest Release version 12.7.0 for mssql-jdbc released on 08th April 2024 [NR-256461](https://new-relic.atlassian.net/browse/NR-256461) +- [PR-247](https://github.com/newrelic/csec-java-agent/pull/247) Exclude Latest Release version 1.7.14 for Rhino-JS-Engine released on 29th April 2024 [NR-265206](https://new-relic.atlassian.net/browse/NR-265206) +- [PR-219](https://github.com/newrelic/csec-java-agent/pull/219) Fixed an issue where lambda functions were causing class circularity errors [NR-239192](https://new-relic.atlassian.net/browse/NR-239192) + ## [1.2.1] - 2024-4-19 ### Fixes - [NR-259467](https://new-relic.atlassian.net/browse/NR-259467) Fix issue of nested event generation from CSEC's agent itself [PR-230](https://github.com/newrelic/csec-java-agent/pull/230) diff --git a/README.md b/README.md index c1c196271..106f9ba31 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,12 @@ The agent automatically instruments the following frameworks. - Mule ESB 3.6 to 3.9.x - gRPC 1.4.0 to latest** - Jersey 2.0 to latest +- Akka Server 10.0 to latest (with scala 2.11 and above) +- Spray Can 1.3.1 to latest (with scala 2.11 and above) +- Akka HTTP Server 10.0 to latest (with scala 2.11 and above) +- Spray HTTP 1.3.1 to latest (with scala 2.11 and above) +- Netty Server 4.0.0.Final to latest +- Netty Reactor Server 0.7.0.RELEASE to latest ** IAST for **gRPC** requires the dependency [protobuf-java-util](https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util) for IAST request replay. @@ -66,6 +72,9 @@ The agent automatically instruments the following HTTP clients and messaging ser - Xalan XPATH 2.1.0 to latest - Async Http Client from 2.0 to latest - Ning Async HTTP Client 1.0.0 to latest +- Akka Client 10.0 to latest (with scala 2.11 and above) +- Spray Can Client 1.3.1 to latest (with scala 2.11 and above) +- Spring WebClient 5.0.0.RELEASE to latest ### Datastores diff --git a/gradle.properties b/gradle.properties index c054d02e8..e0be6fcc8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # The agent version. -agentVersion=1.2.1 +agentVersion=1.3.0 jsonVersion=1.2.1 # Updated exposed NR APM API version. nrAPIVersion=8.4.0 diff --git a/instrumentation-security/akka-http-2.11_10.0.0/src/main/scala/akka/http/scaladsl/server/AkkaCoreUtils.java b/instrumentation-security/akka-http-2.11_10.0.0/src/main/scala/akka/http/scaladsl/server/AkkaCoreUtils.java index 5aa9083d8..b1a26e7bd 100644 --- a/instrumentation-security/akka-http-2.11_10.0.0/src/main/scala/akka/http/scaladsl/server/AkkaCoreUtils.java +++ b/instrumentation-security/akka-http-2.11_10.0.0/src/main/scala/akka/http/scaladsl/server/AkkaCoreUtils.java @@ -5,6 +5,7 @@ import com.newrelic.api.agent.Token; import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; @@ -61,7 +62,7 @@ public static boolean acquireServletLockIfPossible() { public static void postProcessHttpRequest(Boolean isServletLockAcquired, StringBuilder responseBody, String contentType, String className, String methodName, Token token) { try { token.linkAndExpire(); - if(!isServletLockAcquired || !NewRelicSecurity.isHookProcessingActive()){ + if(!isServletLockAcquired || !NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class))){ return; } NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseContentType(contentType); @@ -123,7 +124,7 @@ public static void preProcessHttpRequest (Boolean isServletLockAcquired, HttpReq try { queryString = httpRequest.getUri().rawQueryString().get(); } catch (NoSuchElementException ignored) { - // ignore NoSuchElementException – there is no value present in rawQueryString + // ignore NoSuchElementException there is no value present in rawQueryString } finally { if (queryString != null && !queryString.trim().isEmpty()) { securityRequest.setUrl(securityRequest.getUrl() + QUESTION_MARK + queryString); @@ -180,6 +181,9 @@ public static void processHttpRequestHeader(HttpRequest request, com.newrelic.ap } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeader(headerKey).get().value()); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = nextHeader.value(); if (headerFullValue != null && !headerFullValue.trim().isEmpty()) { diff --git a/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java b/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java index 4099208c1..284d64b7a 100644 --- a/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java +++ b/instrumentation-security/akka-http-core-10.0/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java @@ -5,6 +5,7 @@ import com.newrelic.api.agent.Token; import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; @@ -183,6 +184,9 @@ public static void processHttpRequestHeader(HttpRequest request, com.newrelic.ap } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeader(headerKey).get().value()); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = nextHeader.value(); if (headerFullValue != null && !headerFullValue.trim().isEmpty()) { diff --git a/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java b/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java index ff826809a..0a0d4fb68 100644 --- a/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java +++ b/instrumentation-security/akka-http-core-2.11_10.0.11/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java @@ -5,6 +5,7 @@ import com.newrelic.api.agent.Token; import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; @@ -183,6 +184,9 @@ public static void processHttpRequestHeader(HttpRequest request, com.newrelic.ap } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeader(headerKey).get().value()); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = nextHeader.value(); if (headerFullValue != null && !headerFullValue.trim().isEmpty()) { diff --git a/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java b/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java index 9aa227014..1f3e651c8 100644 --- a/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java +++ b/instrumentation-security/akka-http-core-2.13_10.2.0/src/main/scala/akka/http/scaladsl/AkkaCoreUtils.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.Token; import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; @@ -193,6 +194,9 @@ public static void processHttpRequestHeader(HttpRequest request, com.newrelic.ap } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeader(headerKey).get().value()); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = nextHeader.value(); if (headerFullValue != null && !headerFullValue.trim().isEmpty()) { diff --git a/instrumentation-security/apache-struts2/src/main/java/com/nr/instrumentation/security/apache/struts2/StrutsHelper.java b/instrumentation-security/apache-struts2/src/main/java/com/newrelic/agent/security/instrumentation/apache/struts2/StrutsHelper.java similarity index 95% rename from instrumentation-security/apache-struts2/src/main/java/com/nr/instrumentation/security/apache/struts2/StrutsHelper.java rename to instrumentation-security/apache-struts2/src/main/java/com/newrelic/agent/security/instrumentation/apache/struts2/StrutsHelper.java index a36fee7cd..bca7db4a9 100644 --- a/instrumentation-security/apache-struts2/src/main/java/com/nr/instrumentation/security/apache/struts2/StrutsHelper.java +++ b/instrumentation-security/apache-struts2/src/main/java/com/newrelic/agent/security/instrumentation/apache/struts2/StrutsHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.apache.struts2; +package com.newrelic.agent.security.instrumentation.apache.struts2; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; import com.newrelic.api.agent.security.instrumentation.helpers.*; diff --git a/instrumentation-security/apache-struts2/src/main/java/com/opensymphony/xwork2/config/Configuration_Instrumentation.java b/instrumentation-security/apache-struts2/src/main/java/com/opensymphony/xwork2/config/Configuration_Instrumentation.java index 136f3ea81..62d09202e 100644 --- a/instrumentation-security/apache-struts2/src/main/java/com/opensymphony/xwork2/config/Configuration_Instrumentation.java +++ b/instrumentation-security/apache-struts2/src/main/java/com/opensymphony/xwork2/config/Configuration_Instrumentation.java @@ -3,7 +3,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.security.apache.struts2.StrutsHelper; +import com.newrelic.agent.security.instrumentation.apache.struts2.StrutsHelper; import java.util.List; @Weave(type = MatchType.Interface, originalName = "com.opensymphony.xwork2.config.Configuration") diff --git a/instrumentation-security/apache-tomcat-10/build.gradle b/instrumentation-security/apache-tomcat-10/build.gradle new file mode 100644 index 000000000..337c0de09 --- /dev/null +++ b/instrumentation-security/apache-tomcat-10/build.gradle @@ -0,0 +1,22 @@ +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("org.apache.tomcat.embed:tomcat-embed-core:10.0.0") + implementation("org.apache.tomcat:tomcat-juli:10.0.0") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.apache-tomcat-10' } +} + +verifyInstrumentation { + passesOnly('org.apache.tomcat.embed:tomcat-embed-core:[10.0.0-M1,)') + fails('org.apache.tomcat.embed:tomcat-embed-core:[7.0.0,10.0.0-M1)') + excludeRegex '.*-(b|gfa|beta|RC)[0-9]*' +} + +site { + title 'Tomcat' + type 'Appserver' +} \ No newline at end of file diff --git a/instrumentation-security/apache-tomcat-10/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat10/HttpServletHelper.java b/instrumentation-security/apache-tomcat-10/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat10/HttpServletHelper.java new file mode 100644 index 000000000..632f1c55d --- /dev/null +++ b/instrumentation-security/apache-tomcat-10/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat10/HttpServletHelper.java @@ -0,0 +1,53 @@ +package com.newrelic.agent.security.instrumentation.apache.tomcat10; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.security.utils.logging.LogLevel; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletRegistration; +import java.util.Collection; +import java.util.Map; + +public class HttpServletHelper { + private static final String EMPTY = ""; + private static final String WILDCARD = "*"; + private static final String SEPARATOR = "/"; + public static final String APACHE_TOMCAT_10 = "APACHE-TOMCAT-10"; + + public static void gatherURLMappings(ServletContext servletContext) { + try { + Map servletRegistrations = servletContext.getServletRegistrations(); + getJSPMappings(servletContext, SEPARATOR); + + for (ServletRegistration servletRegistration : servletRegistrations.values()) { + for (String mapping : servletRegistration.getMappings()) { + String path = (mapping.startsWith(SEPARATOR) ? EMPTY : SEPARATOR) + mapping; + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, path, servletRegistration.getClassName())); + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, APACHE_TOMCAT_10, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } + + private static void getJSPMappings(ServletContext servletContext, String dir) { + try { + if(dir.endsWith(SEPARATOR)){ + Collection resourcePaths = servletContext.getResourcePaths(dir); + for (String path : resourcePaths) { + if(path.endsWith(SEPARATOR)) { + getJSPMappings(servletContext, path); + } + else if(path.endsWith(".jsp") || path.endsWith(".jspx") || path.endsWith(".JSP") || path.endsWith(".JSPX")) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, (path.startsWith(SEPARATOR) ? EMPTY : SEPARATOR) + path)); + } + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, APACHE_TOMCAT_10, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } +} diff --git a/instrumentation-security/apache-tomcat-10/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat10/StandardContext_Instrumentation.java b/instrumentation-security/apache-tomcat-10/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat10/StandardContext_Instrumentation.java new file mode 100644 index 000000000..d49785a9b --- /dev/null +++ b/instrumentation-security/apache-tomcat-10/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat10/StandardContext_Instrumentation.java @@ -0,0 +1,22 @@ +package com.newrelic.agent.security.instrumentation.apache.tomcat10; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import org.apache.catalina.LifecycleException; + +import jakarta.servlet.ServletContext; + +@Weave(type = MatchType.ExactClass, originalName = "org.apache.catalina.core.StandardContext") +public abstract class StandardContext_Instrumentation { + + public abstract ServletContext getServletContext(); + + protected synchronized void startInternal() throws LifecycleException { + try { + Weaver.callOriginal(); + } finally { + HttpServletHelper.gatherURLMappings(getServletContext()); + } + } +} diff --git a/instrumentation-security/apache-tomcat-10/src/test/java/com/nr/agent/security/instrumentation/tomcat10/APIEndpointTest.java b/instrumentation-security/apache-tomcat-10/src/test/java/com/nr/agent/security/instrumentation/tomcat10/APIEndpointTest.java new file mode 100644 index 000000000..e27acd60a --- /dev/null +++ b/instrumentation-security/apache-tomcat-10/src/test/java/com/nr/agent/security/instrumentation/tomcat10/APIEndpointTest.java @@ -0,0 +1,69 @@ +package com.nr.agent.security.instrumentation.tomcat10; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import org.apache.catalina.servlets.DefaultServlet; +import org.junit.Assert; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"com.newrelic.agent.security.instrumentation.apache.tomcat10" }) +public class APIEndpointTest { + @ClassRule + public static HttpServletServer server = new HttpServletServer(); + + private final Map expectedMappings = new HashMap<>(); + + @Before + public void setupEndpoints() { + expectedMappings.put("/servlet/*", HttpServletServer.class.getName()+"$1"); + expectedMappings.put("/index.jsp", null); + } + + @Test + public void testAPIEndpoint() throws Exception { + service(); + + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertNotNull(mappings); + Assert.assertEquals(2, mappings.size()); + for (ApplicationURLMapping mapping : mappings) { + assertMappings(mapping); + } + } + + private void assertMappings(ApplicationURLMapping actualMapping) { + Assert.assertNotNull(actualMapping); + + Assert.assertNotNull(actualMapping.getPath()); + String path = actualMapping.getPath(); + String handler = expectedMappings.get(path); + + Assert.assertNotNull(actualMapping.getMethod()); + + Assert.assertEquals(handler, actualMapping.getHandler()); + Assert.assertEquals("*", actualMapping.getMethod()); + } + @Trace(dispatcher = true) + private void service() throws IOException, URISyntaxException { + URL u = server.getEndPoint("").toURL(); + HttpURLConnection conn = (HttpURLConnection) u.openConnection(); + conn.setRequestProperty("content-type", "text/plain; charset=utf-8"); + conn.connect(); + conn.getResponseCode(); + } +} diff --git a/instrumentation-security/apache-tomcat-10/src/test/java/com/nr/agent/security/instrumentation/tomcat10/HttpServletServer.java b/instrumentation-security/apache-tomcat-10/src/test/java/com/nr/agent/security/instrumentation/tomcat10/HttpServletServer.java new file mode 100644 index 000000000..dfeac726a --- /dev/null +++ b/instrumentation-security/apache-tomcat-10/src/test/java/com/nr/agent/security/instrumentation/tomcat10/HttpServletServer.java @@ -0,0 +1,106 @@ +package com.nr.agent.security.instrumentation.tomcat10; + +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.junit.rules.ExternalResource; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URI; +import java.net.URISyntaxException; + +public class HttpServletServer extends ExternalResource { + + private final int port; + private Tomcat server; + private final String webappDirLocation = "./src/test/webapp/"; + private File tmp = new File(webappDirLocation); + public HttpServletServer() { + this.port = getRandomPort(); + } + + @Override + protected void before() throws Throwable { + startServer(); + } + + @Override + protected void after() { + stop(); + } + + private static int getRandomPort() { + try (ServerSocket socket = new ServerSocket(0)){ + return socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port "); + } + } + + private void startServer () throws Exception { + TomcatURLStreamHandlerFactory.disable(); + createFile(); + + server = new Tomcat(); + server.setPort(port); + + server.setBaseDir(webappDirLocation); + server.addWebapp("", tmp.getAbsolutePath()); + + Context context = server.addContext("/tmp", tmp.getAbsolutePath()); + Tomcat.addServlet(context, "servlet", new HttpServlet() { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + System.out.println("get API handler"); + super.doGet(req, resp); + } + }); + context.setAddWebinfClassesResources(true); + context.addWelcomeFile("/index.jsp"); + context.addServletMappingDecoded("/servlet/*","servlet"); + + server.getConnector(); + server.start(); + } + + public URI getEndPoint(String path) throws URISyntaxException { + return new URI("http://localhost:" + port + "/" + path); + } + private void createFile() { + File indexFile = new File(webappDirLocation + "index.jsp"); + try { + if (tmp.mkdir() && indexFile.createNewFile()) { + BufferedWriter writer = new BufferedWriter(new FileWriter(indexFile)); + writer.append("Hello World!"); + writer.flush(); + writer.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private void stop() { + if (server.getServer() != null && server.getServer().getState() != LifecycleState.DESTROYED) { + try { + if (server.getServer().getState() != LifecycleState.STOPPED) { + server.stop(); + } + server.destroy(); + FileUtils.forceDelete(tmp); + } catch (Exception e) { + e.printStackTrace(); + } + } + tmp = null; + } +} \ No newline at end of file diff --git a/instrumentation-security/apache-tomcat-7/build.gradle b/instrumentation-security/apache-tomcat-7/build.gradle new file mode 100644 index 000000000..8614e74a0 --- /dev/null +++ b/instrumentation-security/apache-tomcat-7/build.gradle @@ -0,0 +1,22 @@ +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("org.apache.tomcat.embed:tomcat-embed-core:8.0.1") + implementation("org.apache.tomcat:tomcat-juli:8.0.1") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.apache-tomcat-7' } +} + +verifyInstrumentation { + passesOnly 'org.apache.tomcat.embed:tomcat-embed-core:[7.0.0,10.0.0-M1)' + fails('org.apache.tomcat.embed:tomcat-embed-core:[10.0.0,)') + excludeRegex '.*-(b|gfa|beta|RC)[0-9]*' +} + +site { + title 'Tomcat' + type 'Appserver' +} \ No newline at end of file diff --git a/instrumentation-security/apache-tomcat-7/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat7/HttpServletHelper.java b/instrumentation-security/apache-tomcat-7/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat7/HttpServletHelper.java new file mode 100644 index 000000000..133a3112d --- /dev/null +++ b/instrumentation-security/apache-tomcat-7/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat7/HttpServletHelper.java @@ -0,0 +1,53 @@ +package com.newrelic.agent.security.instrumentation.apache.tomcat7; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.security.utils.logging.LogLevel; + +import javax.servlet.ServletContext; +import javax.servlet.ServletRegistration; +import java.util.Collection; +import java.util.Map; + +public class HttpServletHelper { + private static final String EMPTY = ""; + private static final String WILDCARD = "*"; + private static final String SEPARATOR = "/"; + public static final String APACHE_TOMCAT_7 = "APACHE-TOMCAT-7"; + + public static void gatherURLMappings(ServletContext servletContext) { + try { + Map servletRegistrations = servletContext.getServletRegistrations(); + getJSPMappings(servletContext, SEPARATOR); + + for (ServletRegistration servletRegistration : servletRegistrations.values()) { + for (String mapping : servletRegistration.getMappings()) { + String path = (mapping.startsWith(SEPARATOR) ? EMPTY : SEPARATOR) + mapping; + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, path, servletRegistration.getClassName())); + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, APACHE_TOMCAT_7, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } + + private static void getJSPMappings(ServletContext servletContext, String dir) { + try { + if(dir.endsWith(SEPARATOR)){ + Collection resourcePaths = servletContext.getResourcePaths(dir); + for (String path : resourcePaths) { + if(path.endsWith(SEPARATOR)) { + getJSPMappings(servletContext, path); + } + else if(path.endsWith(".jsp") || path.endsWith(".jspx") || path.endsWith(".JSP") || path.endsWith(".JSPX")) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, (path.startsWith(SEPARATOR) ? EMPTY : SEPARATOR) + path)); + } + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, APACHE_TOMCAT_7, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } +} diff --git a/instrumentation-security/apache-tomcat-7/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat7/StandardContext_Instrumentation.java b/instrumentation-security/apache-tomcat-7/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat7/StandardContext_Instrumentation.java new file mode 100644 index 000000000..61faec528 --- /dev/null +++ b/instrumentation-security/apache-tomcat-7/src/main/java/com/newrelic/agent/security/instrumentation/apache/tomcat7/StandardContext_Instrumentation.java @@ -0,0 +1,22 @@ +package com.newrelic.agent.security.instrumentation.apache.tomcat7; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import org.apache.catalina.LifecycleException; + +import javax.servlet.ServletContext; + +@Weave(type = MatchType.ExactClass, originalName = "org.apache.catalina.core.StandardContext") +public abstract class StandardContext_Instrumentation { + + public abstract ServletContext getServletContext(); + + protected synchronized void startInternal() throws LifecycleException { + try { + Weaver.callOriginal(); + } finally { + HttpServletHelper.gatherURLMappings(getServletContext()); + } + } +} diff --git a/instrumentation-security/apache-tomcat-7/src/test/java/com/nr/agent/security/instrumentation/tomcat7/APIEndpointTest.java b/instrumentation-security/apache-tomcat-7/src/test/java/com/nr/agent/security/instrumentation/tomcat7/APIEndpointTest.java new file mode 100644 index 000000000..338994b59 --- /dev/null +++ b/instrumentation-security/apache-tomcat-7/src/test/java/com/nr/agent/security/instrumentation/tomcat7/APIEndpointTest.java @@ -0,0 +1,69 @@ +package com.nr.agent.security.instrumentation.tomcat7; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import org.apache.catalina.servlets.DefaultServlet; +import org.junit.Assert; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"com.newrelic.agent.security.instrumentation.apache.tomcat7" }) +public class APIEndpointTest { + @ClassRule + public static HttpServletServer server = new HttpServletServer(); + + private final Map expectedMappings = new HashMap<>(); + + @Before + public void setupEndpoints() { + expectedMappings.put("/servlet/*", HttpServletServer.class.getName()+"$1"); + expectedMappings.put("/index.jsp", null); + } + + @Test + public void testAPIEndpoint() throws Exception { + service(); + + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertNotNull(mappings); + Assert.assertEquals(2, mappings.size()); + for (ApplicationURLMapping mapping : mappings) { + assertMappings(mapping); + } + } + + private void assertMappings(ApplicationURLMapping actualMapping) { + Assert.assertNotNull(actualMapping); + + Assert.assertNotNull(actualMapping.getPath()); + String path = actualMapping.getPath(); + String handler = expectedMappings.get(path); + + Assert.assertNotNull(actualMapping.getMethod()); + + Assert.assertEquals(handler, actualMapping.getHandler()); + Assert.assertEquals("*", actualMapping.getMethod()); + } + @Trace(dispatcher = true) + private void service() throws IOException, URISyntaxException { + URL u = server.getEndPoint("").toURL(); + HttpURLConnection conn = (HttpURLConnection) u.openConnection(); + conn.setRequestProperty("content-type", "text/plain; charset=utf-8"); + conn.connect(); + conn.getResponseCode(); + } +} diff --git a/instrumentation-security/apache-tomcat-7/src/test/java/com/nr/agent/security/instrumentation/tomcat7/HttpServletServer.java b/instrumentation-security/apache-tomcat-7/src/test/java/com/nr/agent/security/instrumentation/tomcat7/HttpServletServer.java new file mode 100644 index 000000000..e355d051d --- /dev/null +++ b/instrumentation-security/apache-tomcat-7/src/test/java/com/nr/agent/security/instrumentation/tomcat7/HttpServletServer.java @@ -0,0 +1,106 @@ +package com.nr.agent.security.instrumentation.tomcat7; + +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.junit.rules.ExternalResource; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URI; +import java.net.URISyntaxException; + +public class HttpServletServer extends ExternalResource { + + private final int port; + private Tomcat server; + private final String webappDirLocation = "./src/test/webapp/"; + private File tmp = new File(webappDirLocation); + public HttpServletServer() { + this.port = getRandomPort(); + } + + @Override + protected void before() throws Throwable { + startServer(); + } + + @Override + protected void after() { + stop(); + } + + private static int getRandomPort() { + try (ServerSocket socket = new ServerSocket(0)){ + return socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port "); + } + } + + private void startServer () throws Exception { + TomcatURLStreamHandlerFactory.disable(); + createFile(); + + server = new Tomcat(); + server.setPort(port); + + server.setBaseDir(webappDirLocation); + server.addWebapp("", tmp.getAbsolutePath()); + + Context context = server.addContext("/tmp", tmp.getAbsolutePath()); + Tomcat.addServlet(context, "servlet", new HttpServlet() { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + System.out.println("get API handler"); + super.doGet(req, resp); + } + }); + context.setAddWebinfClassesResources(true); + context.addWelcomeFile("/index.jsp"); + context.addServletMapping("/servlet/*","servlet"); + + server.getConnector(); + server.start(); + } + + public URI getEndPoint(String path) throws URISyntaxException { + return new URI("http://localhost:" + port + "/" + path); + } + private void createFile() { + File indexFile = new File(webappDirLocation + "index.jsp"); + try { + if (tmp.mkdir() && indexFile.createNewFile()) { + BufferedWriter writer = new BufferedWriter(new FileWriter(indexFile)); + writer.append("Hello World!"); + writer.flush(); + writer.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private void stop() { + if (server.getServer() != null && server.getServer().getState() != LifecycleState.DESTROYED) { + try { + if (server.getServer().getState() != LifecycleState.STOPPED) { + server.stop(); + } + server.destroy(); + FileUtils.forceDelete(tmp); + } catch (Exception e) { + e.printStackTrace(); + } + } + tmp = null; + } +} \ No newline at end of file diff --git a/instrumentation-security/apache-wicket-6.4/src/main/java/com/nr/instrumentation/security/apache/wicket6/WicketHelper.java b/instrumentation-security/apache-wicket-6.4/src/main/java/com/newrelic/agent/security/instrumentation/apache/wicket6/WicketHelper.java similarity index 92% rename from instrumentation-security/apache-wicket-6.4/src/main/java/com/nr/instrumentation/security/apache/wicket6/WicketHelper.java rename to instrumentation-security/apache-wicket-6.4/src/main/java/com/newrelic/agent/security/instrumentation/apache/wicket6/WicketHelper.java index 5439f2565..8a79ce975 100644 --- a/instrumentation-security/apache-wicket-6.4/src/main/java/com/nr/instrumentation/security/apache/wicket6/WicketHelper.java +++ b/instrumentation-security/apache-wicket-6.4/src/main/java/com/newrelic/agent/security/instrumentation/apache/wicket6/WicketHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.apache.wicket6; +package com.newrelic.agent.security.instrumentation.apache.wicket6; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; diff --git a/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/MountMapper_Instrumentation.java b/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/MountMapper_Instrumentation.java index 0f73a6cf9..ed8f77cad 100644 --- a/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/MountMapper_Instrumentation.java +++ b/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/MountMapper_Instrumentation.java @@ -2,7 +2,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; -import com.nr.instrumentation.security.apache.wicket6.WicketHelper; +import com.newrelic.agent.security.instrumentation.apache.wicket6.WicketHelper; import org.apache.wicket.request.IRequestMapper; @Weave(type = MatchType.ExactClass, originalName = "org.apache.wicket.request.mapper.mount.MountMapper") diff --git a/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/MountedMapper_Instrumentation.java b/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/MountedMapper_Instrumentation.java index 1c065a47c..2f9f1aba0 100644 --- a/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/MountedMapper_Instrumentation.java +++ b/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/MountedMapper_Instrumentation.java @@ -2,7 +2,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; -import com.nr.instrumentation.security.apache.wicket6.WicketHelper; +import com.newrelic.agent.security.instrumentation.apache.wicket6.WicketHelper; import org.apache.wicket.request.component.IRequestablePage; import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; import org.apache.wicket.util.IProvider; diff --git a/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/PackageMapper_Instrumentation.java b/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/PackageMapper_Instrumentation.java index 88f7c1af5..a114120c2 100644 --- a/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/PackageMapper_Instrumentation.java +++ b/instrumentation-security/apache-wicket-6.4/src/main/java/org/apache/wicket/request/mapper/mount/PackageMapper_Instrumentation.java @@ -2,7 +2,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; -import com.nr.instrumentation.security.apache.wicket6.WicketHelper; +import com.newrelic.agent.security.instrumentation.apache.wicket6.WicketHelper; import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; import org.apache.wicket.util.lang.PackageName; diff --git a/instrumentation-security/apache-wicket-7.0/src/main/java/com/nr/instrumentation/security/apache/wicket7/WicketHelper.java b/instrumentation-security/apache-wicket-7.0/src/main/java/com/newrelic/agent/security/instrumentation/apache/wicket7/WicketHelper.java similarity index 92% rename from instrumentation-security/apache-wicket-7.0/src/main/java/com/nr/instrumentation/security/apache/wicket7/WicketHelper.java rename to instrumentation-security/apache-wicket-7.0/src/main/java/com/newrelic/agent/security/instrumentation/apache/wicket7/WicketHelper.java index 651761e99..954fb676b 100644 --- a/instrumentation-security/apache-wicket-7.0/src/main/java/com/nr/instrumentation/security/apache/wicket7/WicketHelper.java +++ b/instrumentation-security/apache-wicket-7.0/src/main/java/com/newrelic/agent/security/instrumentation/apache/wicket7/WicketHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.apache.wicket7; +package com.newrelic.agent.security.instrumentation.apache.wicket7; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; diff --git a/instrumentation-security/apache-wicket-7.0/src/main/java/org/apache/wicket/core/request/mapper/MountedMapper_Instrumentation.java b/instrumentation-security/apache-wicket-7.0/src/main/java/org/apache/wicket/core/request/mapper/MountedMapper_Instrumentation.java index d8a9ea7ca..61aea584c 100644 --- a/instrumentation-security/apache-wicket-7.0/src/main/java/org/apache/wicket/core/request/mapper/MountedMapper_Instrumentation.java +++ b/instrumentation-security/apache-wicket-7.0/src/main/java/org/apache/wicket/core/request/mapper/MountedMapper_Instrumentation.java @@ -2,7 +2,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; -import com.nr.instrumentation.security.apache.wicket7.WicketHelper; +import com.newrelic.agent.security.instrumentation.apache.wicket7.WicketHelper; import org.apache.wicket.request.component.IRequestablePage; import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; import org.apache.wicket.util.IProvider; diff --git a/instrumentation-security/apache-wicket-7.0/src/main/java/org/apache/wicket/core/request/mapper/PackageMapper_Instrumentation.java b/instrumentation-security/apache-wicket-7.0/src/main/java/org/apache/wicket/core/request/mapper/PackageMapper_Instrumentation.java index 648807be1..d440ec102 100644 --- a/instrumentation-security/apache-wicket-7.0/src/main/java/org/apache/wicket/core/request/mapper/PackageMapper_Instrumentation.java +++ b/instrumentation-security/apache-wicket-7.0/src/main/java/org/apache/wicket/core/request/mapper/PackageMapper_Instrumentation.java @@ -2,7 +2,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; -import com.nr.instrumentation.security.apache.wicket7.WicketHelper; +import com.newrelic.agent.security.instrumentation.apache.wicket7.WicketHelper; import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; import org.apache.wicket.util.lang.PackageName; diff --git a/instrumentation-security/apache-wicket-8.0/src/main/java/com/nr/instrumentation/security/apache/wicket8/WicketHelper.java b/instrumentation-security/apache-wicket-8.0/src/main/java/com/newrelic/agent/security/instrumentation/apache/wicket8/WicketHelper.java similarity index 92% rename from instrumentation-security/apache-wicket-8.0/src/main/java/com/nr/instrumentation/security/apache/wicket8/WicketHelper.java rename to instrumentation-security/apache-wicket-8.0/src/main/java/com/newrelic/agent/security/instrumentation/apache/wicket8/WicketHelper.java index 376109337..9510e5958 100644 --- a/instrumentation-security/apache-wicket-8.0/src/main/java/com/nr/instrumentation/security/apache/wicket8/WicketHelper.java +++ b/instrumentation-security/apache-wicket-8.0/src/main/java/com/newrelic/agent/security/instrumentation/apache/wicket8/WicketHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.apache.wicket8; +package com.newrelic.agent.security.instrumentation.apache.wicket8; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; diff --git a/instrumentation-security/apache-wicket-8.0/src/main/java/org/apache/wicket/core/request/mapper/MountedMapper_Instrumentation.java b/instrumentation-security/apache-wicket-8.0/src/main/java/org/apache/wicket/core/request/mapper/MountedMapper_Instrumentation.java index e0f31197a..08e64a5b6 100644 --- a/instrumentation-security/apache-wicket-8.0/src/main/java/org/apache/wicket/core/request/mapper/MountedMapper_Instrumentation.java +++ b/instrumentation-security/apache-wicket-8.0/src/main/java/org/apache/wicket/core/request/mapper/MountedMapper_Instrumentation.java @@ -2,7 +2,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; -import com.nr.instrumentation.security.apache.wicket8.WicketHelper; +import com.newrelic.agent.security.instrumentation.apache.wicket8.WicketHelper; import org.apache.wicket.request.component.IRequestablePage; import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; diff --git a/instrumentation-security/apache-wicket-8.0/src/main/java/org/apache/wicket/core/request/mapper/PackageMapper_Instrumentation.java b/instrumentation-security/apache-wicket-8.0/src/main/java/org/apache/wicket/core/request/mapper/PackageMapper_Instrumentation.java index 873adf232..5c2d9d9b4 100644 --- a/instrumentation-security/apache-wicket-8.0/src/main/java/org/apache/wicket/core/request/mapper/PackageMapper_Instrumentation.java +++ b/instrumentation-security/apache-wicket-8.0/src/main/java/org/apache/wicket/core/request/mapper/PackageMapper_Instrumentation.java @@ -2,7 +2,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; -import com.nr.instrumentation.security.apache.wicket8.WicketHelper; +import com.newrelic.agent.security.instrumentation.apache.wicket8.WicketHelper; import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; import org.apache.wicket.util.lang.PackageName; diff --git a/instrumentation-security/async-http-client-2.0.0/src/main/java/com/newrelic/agent/security/instrumentation/org/asynchttpclient/AsynchttpHelper.java b/instrumentation-security/async-http-client-2.0.0/src/main/java/com/newrelic/agent/security/instrumentation/org/asynchttpclient/AsynchttpHelper.java index 54ff15338..94062e622 100644 --- a/instrumentation-security/async-http-client-2.0.0/src/main/java/com/newrelic/agent/security/instrumentation/org/asynchttpclient/AsynchttpHelper.java +++ b/instrumentation-security/async-http-client-2.0.0/src/main/java/com/newrelic/agent/security/instrumentation/org/asynchttpclient/AsynchttpHelper.java @@ -70,6 +70,7 @@ public static AbstractOperation preprocessSecurityHook(String url, String classN SSRFOperation operation = new SSRFOperation(url, className, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); return operation; } catch (Throwable e) { diff --git a/instrumentation-security/cxf-jaxrs/build.gradle b/instrumentation-security/cxf-jaxrs/build.gradle new file mode 100644 index 000000000..e131a5522 --- /dev/null +++ b/instrumentation-security/cxf-jaxrs/build.gradle @@ -0,0 +1,23 @@ +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation('org.apache.cxf:cxf-rt-frontend-jaxrs:2.1.4') + + implementation('org.apache.cxf:cxf-rt-transports-http-jetty:2.1.4') +} + + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.cxf-jaxrs' } +} + + +verifyInstrumentation { + passesOnly 'org.apache.cxf:cxf-rt-frontend-jaxrs:[2.1.4,)' +} + +site { + title 'CXF' + type 'Framework' +} \ No newline at end of file diff --git a/instrumentation-security/cxf-jaxrs/src/main/java/com/newrelic/agent/java/security/cxf/jaxrs/CXFHelper.java b/instrumentation-security/cxf-jaxrs/src/main/java/com/newrelic/agent/java/security/cxf/jaxrs/CXFHelper.java new file mode 100644 index 000000000..129337df7 --- /dev/null +++ b/instrumentation-security/cxf-jaxrs/src/main/java/com/newrelic/agent/java/security/cxf/jaxrs/CXFHelper.java @@ -0,0 +1,51 @@ +package com.newrelic.agent.java.security.cxf.jaxrs; + +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.MethodDispatcher; +import org.apache.cxf.jaxrs.model.OperationResourceInfo; + +import java.util.List; + +public class CXFHelper { + private static final String EMPTY = ""; + private static final String SEPARATOR = "/"; + private static final String WILDCARD = "*"; + + public static void gatherURLMapping(List classResourceInfo) { + for (ClassResourceInfo classResource: classResourceInfo){ + resources(classResource.getURITemplate().getValue(), classResource); + } + } + + private static void resources(String segment, ClassResourceInfo classResourceInfo) { + MethodDispatcher methodDispatcher = classResourceInfo.getMethodDispatcher(); + for(OperationResourceInfo method: methodDispatcher.getOperationResourceInfos()) { + String segment1 = method.getURITemplate().getValue(); + String path = segment + ((segment.endsWith(SEPARATOR) || segment1.startsWith(SEPARATOR)) ? EMPTY : SEPARATOR) + segment1; + + // http-method is null, then it can be a sub-resource + if (method.getHttpMethod() == null){ + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping( + WILDCARD, + path + (path.endsWith(SEPARATOR) ? EMPTY : SEPARATOR) + WILDCARD, + classResourceInfo.getResourceClass().getName() + )); + } else { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping( + method.getHttpMethod(), + path, + classResourceInfo.getResourceClass().getName() + )); + } + + } + // for sub-resources + for (ClassResourceInfo classResource: classResourceInfo.getSubResources()){ + String segment1 = classResource.getURITemplate().getValue(); + String path = segment + ((segment.endsWith(SEPARATOR) || segment1.startsWith(SEPARATOR)) ? EMPTY : SEPARATOR) + segment1; + resources(path, classResource); + } + } +} diff --git a/instrumentation-security/cxf-jaxrs/src/main/java/com/newrelic/agent/java/security/cxf/jaxrs/JAXRSServerFactoryBean.java b/instrumentation-security/cxf-jaxrs/src/main/java/com/newrelic/agent/java/security/cxf/jaxrs/JAXRSServerFactoryBean.java new file mode 100644 index 000000000..76c9421cf --- /dev/null +++ b/instrumentation-security/cxf-jaxrs/src/main/java/com/newrelic/agent/java/security/cxf/jaxrs/JAXRSServerFactoryBean.java @@ -0,0 +1,22 @@ +package com.newrelic.agent.java.security.cxf.jaxrs; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean; + +@Weave(type = MatchType.ExactClass, originalName = "org.apache.cxf.jaxrs.JAXRSServerFactoryBean") +public abstract class JAXRSServerFactoryBean { + public abstract JAXRSServiceFactoryBean getServiceFactory(); + + public Server create() { + try { + return Weaver.callOriginal(); + } finally { + CXFHelper.gatherURLMapping(getServiceFactory().getClassResourceInfo()); + } + } + + +} diff --git a/instrumentation-security/cxf-jaxrs/src/test/java/com/nr/agent/security/instrumentation/cxf/jaxrs/APIEndpointTest.java b/instrumentation-security/cxf-jaxrs/src/test/java/com/nr/agent/security/instrumentation/cxf/jaxrs/APIEndpointTest.java new file mode 100644 index 000000000..b4268abed --- /dev/null +++ b/instrumentation-security/cxf-jaxrs/src/test/java/com/nr/agent/security/instrumentation/cxf/jaxrs/APIEndpointTest.java @@ -0,0 +1,79 @@ +package com.nr.agent.security.instrumentation.cxf.jaxrs; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +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.nr.agent.security.instrumentation.cxf.jaxrs.app.CustomerLocatorResource; +import com.nr.agent.security.instrumentation.cxf.jaxrs.app.TestMapping; +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.jaxrs.JAXRSBindingFactory; +import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; +import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Iterator; +import java.util.Set; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "com.newrelic.agent.java.security.cxf.jaxrs") +public class APIEndpointTest { + private final String handler = TestMapping.class.getName(); + + @Test + public void testAPIEndpoint() { + startServer(); + + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertEquals(5, mappings.size()); + + Iterator mapping = mappings.iterator(); + String path = "/users/"; + + Assert.assertTrue(mapping.hasNext()); + assertMapping("*", "/customers/orders/*", CustomerLocatorResource.class.getName(), mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("POST", path, handler, mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("GET", path, handler, mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("PUT", path, handler, mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("GET", path +"count", handler, mapping.next()); + + } + + private void assertMapping(String method, String path, String handler, ApplicationURLMapping actualMapping) { + Assert.assertEquals(method, actualMapping.getMethod()); + Assert.assertEquals(path, actualMapping.getPath()); + Assert.assertEquals(handler, actualMapping.getHandler()); + } + + private void startServer() { + JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); + + sf.setResourceClasses(CustomerLocatorResource.class, TestMapping.class); + + sf.setBindingFactory(new JAXRSBindingFactory()); + + sf.setAddress("http://localhost:" + getRandomPort()); + Server myServer = sf.create(); + myServer.start(); + } + + private int getRandomPort() { + try (ServerSocket socket = new ServerSocket(0)){ + return socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port"); + } + } +} diff --git a/instrumentation-security/cxf-jaxrs/src/test/java/com/nr/agent/security/instrumentation/cxf/jaxrs/app/CustomerLocatorResource.java b/instrumentation-security/cxf-jaxrs/src/test/java/com/nr/agent/security/instrumentation/cxf/jaxrs/app/CustomerLocatorResource.java new file mode 100644 index 000000000..65e129133 --- /dev/null +++ b/instrumentation-security/cxf-jaxrs/src/test/java/com/nr/agent/security/instrumentation/cxf/jaxrs/app/CustomerLocatorResource.java @@ -0,0 +1,22 @@ +package com.nr.agent.security.instrumentation.cxf.jaxrs.app; + +import javax.ws.rs.Path; + +@Path("/customers") +public class CustomerLocatorResource { + protected OrdersSubResource ordersSubResource = new OrdersSubResource(); + + @Path("orders") + public Object getOrders() { + return ordersSubResource; + } +} +class OrdersSubResource { + protected TestMapping idType = new TestMapping(); + + @Path("getStuff") + public Object getById() { + return idType; + } + +} diff --git a/instrumentation-security/cxf-jaxrs/src/test/java/com/nr/agent/security/instrumentation/cxf/jaxrs/app/TestMapping.java b/instrumentation-security/cxf-jaxrs/src/test/java/com/nr/agent/security/instrumentation/cxf/jaxrs/app/TestMapping.java new file mode 100644 index 000000000..76c4e964a --- /dev/null +++ b/instrumentation-security/cxf-jaxrs/src/test/java/com/nr/agent/security/instrumentation/cxf/jaxrs/app/TestMapping.java @@ -0,0 +1,32 @@ +package com.nr.agent.security.instrumentation.cxf.jaxrs.app; + +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; + +@Path("users") +public class TestMapping { + @PUT + public String putIt() { + return "Put it!"; + } + + @POST + public String postIt() { + return "Post it!"; + } + + @GET + public String getIt() { + return "Get it!"; + } + + @Path("count") + @GET + public String pathIt() { + return "path it!"; + } +} + + diff --git a/instrumentation-security/dynamodb-1.11.390/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_390/DynamoDBUtil.java b/instrumentation-security/dynamodb-1.11.390/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_390/DynamoDBUtil.java index dbc891aa9..2766ce945 100644 --- a/instrumentation-security/dynamodb-1.11.390/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_390/DynamoDBUtil.java +++ b/instrumentation-security/dynamodb-1.11.390/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_390/DynamoDBUtil.java @@ -59,6 +59,7 @@ public static AbstractOperation processDynamoDBRequest(Request yRequest, operation = checkAndGenerateOperation(request, requests, klassName); if (operation!=null) { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } } diff --git a/instrumentation-security/dynamodb-1.11.453/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_453/DynamoDBUtil.java b/instrumentation-security/dynamodb-1.11.453/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_453/DynamoDBUtil.java index 9ad67c982..d1d2bb4ac 100644 --- a/instrumentation-security/dynamodb-1.11.453/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_453/DynamoDBUtil.java +++ b/instrumentation-security/dynamodb-1.11.453/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_453/DynamoDBUtil.java @@ -59,6 +59,7 @@ public static AbstractOperation processDynamoDBRequest(Request yRequest, operation = checkAndGenerateOperation(request, requests, klassName); if (operation!=null) { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } } diff --git a/instrumentation-security/dynamodb-1.11.459/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_459/DynamoDBUtil.java b/instrumentation-security/dynamodb-1.11.459/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_459/DynamoDBUtil.java index 3f9d5e78a..352b50a1c 100644 --- a/instrumentation-security/dynamodb-1.11.459/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_459/DynamoDBUtil.java +++ b/instrumentation-security/dynamodb-1.11.459/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_459/DynamoDBUtil.java @@ -68,6 +68,7 @@ public static AbstractOperation processDynamoDBRequest(Request yRequest, operation = checkAndGenerateOperation(request, requests, klassName); if (operation!=null) { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } } diff --git a/instrumentation-security/dynamodb-1.11.80/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_80/DynamoDBUtil.java b/instrumentation-security/dynamodb-1.11.80/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_80/DynamoDBUtil.java index 2a0d30947..13e1a39d8 100644 --- a/instrumentation-security/dynamodb-1.11.80/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_80/DynamoDBUtil.java +++ b/instrumentation-security/dynamodb-1.11.80/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_1_11_80/DynamoDBUtil.java @@ -59,6 +59,7 @@ public static AbstractOperation processDynamoDBRequest(Request yRequest, operation = checkAndGenerateOperation(request, requests, klassName); if (operation!=null) { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } } diff --git a/instrumentation-security/dynamodb-2.1.0/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_210/DynamoDBUtil.java b/instrumentation-security/dynamodb-2.1.0/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_210/DynamoDBUtil.java index c7aa03c91..cab84ab53 100644 --- a/instrumentation-security/dynamodb-2.1.0/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_210/DynamoDBUtil.java +++ b/instrumentation-security/dynamodb-2.1.0/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_210/DynamoDBUtil.java @@ -62,6 +62,7 @@ public static AbstractO operation = checkAndGenerateOperation(request, requests, klassName); if (operation!=null) { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } } diff --git a/instrumentation-security/dynamodb-2.1.2/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_212/DynamoDBUtil.java b/instrumentation-security/dynamodb-2.1.2/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_212/DynamoDBUtil.java index 1626924e1..1f85651cc 100644 --- a/instrumentation-security/dynamodb-2.1.2/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_212/DynamoDBUtil.java +++ b/instrumentation-security/dynamodb-2.1.2/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_212/DynamoDBUtil.java @@ -71,6 +71,7 @@ public static AbstractO operation = checkAndGenerateOperation(request, requests, klassName); if (operation!=null) { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } } diff --git a/instrumentation-security/dynamodb-2.15.34/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_215/DynamoDBUtil.java b/instrumentation-security/dynamodb-2.15.34/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_215/DynamoDBUtil.java index a16b957fa..2e26b9bbe 100644 --- a/instrumentation-security/dynamodb-2.15.34/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_215/DynamoDBUtil.java +++ b/instrumentation-security/dynamodb-2.15.34/src/main/java/com/newrelic/agent/security/instrumentation/dynamodb_215/DynamoDBUtil.java @@ -76,6 +76,7 @@ public static AbstractO operation = checkAndGenerateOperation(request, requests, klassName); if (operation!=null) { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } } diff --git a/instrumentation-security/grails-1.3/src/main/java/com/nr/instrumentation/security/grails13/GrailsHelper.java b/instrumentation-security/grails-1.3/src/main/java/com/newrelic/agent/security/instrumentation/grails13/GrailsHelper.java similarity index 90% rename from instrumentation-security/grails-1.3/src/main/java/com/nr/instrumentation/security/grails13/GrailsHelper.java rename to instrumentation-security/grails-1.3/src/main/java/com/newrelic/agent/security/instrumentation/grails13/GrailsHelper.java index ad89554f9..f7883b7da 100644 --- a/instrumentation-security/grails-1.3/src/main/java/com/nr/instrumentation/security/grails13/GrailsHelper.java +++ b/instrumentation-security/grails-1.3/src/main/java/com/newrelic/agent/security/instrumentation/grails13/GrailsHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.grails13; +package com.newrelic.agent.security.instrumentation.grails13; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; diff --git a/instrumentation-security/grails-1.3/src/main/java/org/codehaus/groovy/grails/commons/DefaultGrailsController_Instrumentation.java b/instrumentation-security/grails-1.3/src/main/java/org/codehaus/groovy/grails/commons/DefaultGrailsController_Instrumentation.java index eae553ef7..e936a10c6 100644 --- a/instrumentation-security/grails-1.3/src/main/java/org/codehaus/groovy/grails/commons/DefaultGrailsController_Instrumentation.java +++ b/instrumentation-security/grails-1.3/src/main/java/org/codehaus/groovy/grails/commons/DefaultGrailsController_Instrumentation.java @@ -4,7 +4,7 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.WeaveAllConstructors; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.security.grails13.GrailsHelper; +import com.newrelic.agent.security.instrumentation.grails13.GrailsHelper; import java.util.Map; diff --git a/instrumentation-security/grails-2.0/src/main/java/com/nr/instrumentation/security/grails2/GrailsHelper.java b/instrumentation-security/grails-2.0/src/main/java/com/newrelic/agent/security/instrumentation/grails2/GrailsHelper.java similarity index 90% rename from instrumentation-security/grails-2.0/src/main/java/com/nr/instrumentation/security/grails2/GrailsHelper.java rename to instrumentation-security/grails-2.0/src/main/java/com/newrelic/agent/security/instrumentation/grails2/GrailsHelper.java index f0c56410c..93d1856c5 100644 --- a/instrumentation-security/grails-2.0/src/main/java/com/nr/instrumentation/security/grails2/GrailsHelper.java +++ b/instrumentation-security/grails-2.0/src/main/java/com/newrelic/agent/security/instrumentation/grails2/GrailsHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.grails2; +package com.newrelic.agent.security.instrumentation.grails2; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; diff --git a/instrumentation-security/grails-2.0/src/main/java/org/codehaus/groovy/grails/commons/DefaultGrailsController_Instrumentation.java b/instrumentation-security/grails-2.0/src/main/java/org/codehaus/groovy/grails/commons/DefaultGrailsController_Instrumentation.java index ae6902a92..8f88fb90b 100644 --- a/instrumentation-security/grails-2.0/src/main/java/org/codehaus/groovy/grails/commons/DefaultGrailsController_Instrumentation.java +++ b/instrumentation-security/grails-2.0/src/main/java/org/codehaus/groovy/grails/commons/DefaultGrailsController_Instrumentation.java @@ -3,7 +3,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.security.grails2.GrailsHelper; +import com.newrelic.agent.security.instrumentation.grails2.GrailsHelper; import java.util.Map; diff --git a/instrumentation-security/grails-3.0/src/main/java/com/nr/instrumentation/security/grails3/GrailsHelper.java b/instrumentation-security/grails-3.0/src/main/java/com/newrelic/agent/security/instrumentation/grails3/GrailsHelper.java similarity index 94% rename from instrumentation-security/grails-3.0/src/main/java/com/nr/instrumentation/security/grails3/GrailsHelper.java rename to instrumentation-security/grails-3.0/src/main/java/com/newrelic/agent/security/instrumentation/grails3/GrailsHelper.java index dbbae1fdd..64ab175ed 100644 --- a/instrumentation-security/grails-3.0/src/main/java/com/nr/instrumentation/security/grails3/GrailsHelper.java +++ b/instrumentation-security/grails-3.0/src/main/java/com/newrelic/agent/security/instrumentation/grails3/GrailsHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.grails3; +package com.newrelic.agent.security.instrumentation.grails3; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; diff --git a/instrumentation-security/grails-3.0/src/main/java/org/grails/core/DefaultGrailsController_Instrumentation.java b/instrumentation-security/grails-3.0/src/main/java/org/grails/core/DefaultGrailsController_Instrumentation.java index 91fe02fcd..a0f86b79e 100644 --- a/instrumentation-security/grails-3.0/src/main/java/org/grails/core/DefaultGrailsController_Instrumentation.java +++ b/instrumentation-security/grails-3.0/src/main/java/org/grails/core/DefaultGrailsController_Instrumentation.java @@ -4,7 +4,7 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.WeaveAllConstructors; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.security.grails3.GrailsHelper; +import com.newrelic.agent.security.instrumentation.grails3.GrailsHelper; import java.lang.reflect.Method; import java.util.Map; diff --git a/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/GrpcClientUtils.java b/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/GrpcClientUtils.java index 69be47f0c..1733cca6e 100644 --- a/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/GrpcClientUtils.java +++ b/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/GrpcClientUtils.java @@ -39,6 +39,7 @@ public static AbstractOperation preprocessSecurityHook(String uri, Metadata meta SSRFOperation operation = new SSRFOperation(uri, klass, METHOD_NAME_START); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); String iastHeader = NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getRaw(); if (iastHeader != null && !iastHeader.trim().isEmpty()) { diff --git a/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/GrpcServerUtils.java b/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/GrpcServerUtils.java index 9dd55f506..711c0c23d 100644 --- a/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/GrpcServerUtils.java +++ b/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/GrpcServerUtils.java @@ -2,10 +2,7 @@ import com.google.protobuf.Descriptors; import com.newrelic.api.agent.security.NewRelicSecurity; -import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.GrpcHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.*; import com.newrelic.api.agent.security.schema.AgentMetaData; import com.newrelic.api.agent.security.schema.HttpRequest; import com.newrelic.api.agent.security.schema.SecurityMetaData; @@ -188,6 +185,9 @@ public static void processGRPCRequestMetadata(Metadata metadata, HttpRequest sec NewRelicSecurity.getAgent().getSecurityMetaData().setFuzzRequestIdentifier(ServletHelper.parseFuzzRequestIdentifierHeader(metadata.get(Metadata.Key.of(headerKey, Metadata.ASCII_STRING_MARSHALLER)))); } else if (GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(GenericHelper.CSEC_PARENT_ID, metadata.get(Metadata.Key.of(headerKey, Metadata.ASCII_STRING_MARSHALLER))); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; String[] headerElements = metadata.get(Metadata.Key.of(headerKey, Metadata.ASCII_STRING_MARSHALLER)).split(";"); diff --git a/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/processor/GrpcRequestProcessor.java b/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/processor/GrpcRequestProcessor.java index b81f9444b..2964fb0bd 100644 --- a/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/processor/GrpcRequestProcessor.java +++ b/instrumentation-security/grpc-1.22.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1220/processor/GrpcRequestProcessor.java @@ -10,7 +10,7 @@ import java.util.concurrent.Future; public class GrpcRequestProcessor implements Callable { - public static final String CALL_FAILED_REQUEST_S_REASON = "Call failed : request %s reason : "; + public static final String CALL_FAILED_REQUEST_S_REASON = "Call failed : request %s reason : %s "; private ControlCommandDto controlCommandDto; private int repeatCount; private static final int MAX_REPETITION = 3; @@ -31,18 +31,18 @@ public static void executeGrpcRequest(ControlCommandDto controlCommandDto) { try { Object futureResult = future.get(); if (futureResult instanceof Throwable) { - NewRelicSecurity.getAgent().log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean()), (Throwable) futureResult, GrpcClient.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean(), ((Throwable) futureResult).getMessage()), (Throwable) futureResult, GrpcClient.class.getName()); NewRelicSecurity.getAgent().reportIncident(LogLevel.WARNING, - String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId()), + String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId(), ((Throwable) futureResult).getMessage()), (Throwable) futureResult, GrpcClient.class.getName()); GrpcClientRequestReplayHelper.getInstance().addFuzzFailEventToQueue(controlCommandDto.getRequestBean(), (Throwable) futureResult); } else { GrpcClientRequestReplayHelper.getInstance().getPendingIds().remove(controlCommandDto.getId()); } } catch (Throwable e) { - NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean()), e, GrpcRequestProcessor.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean(), e.getMessage()), e, GrpcRequestProcessor.class.getName()); NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, - String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId()), + String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId(), e.getMessage()), e, GrpcRequestProcessor.class.getName()); GrpcClientRequestReplayHelper.getInstance().addFuzzFailEventToQueue(controlCommandDto.getRequestBean(), e); } diff --git a/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/GrpcClientUtils.java b/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/GrpcClientUtils.java index cbc6b423e..8e6adaee6 100644 --- a/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/GrpcClientUtils.java +++ b/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/GrpcClientUtils.java @@ -39,6 +39,7 @@ public static AbstractOperation preprocessSecurityHook(String uri, Metadata meta SSRFOperation operation = new SSRFOperation(uri, klass, METHOD_NAME_START); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); String iastHeader = NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getRaw(); if (iastHeader != null && !iastHeader.trim().isEmpty()) { diff --git a/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/GrpcServerUtils.java b/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/GrpcServerUtils.java index 372aaa012..7e05eb8d5 100644 --- a/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/GrpcServerUtils.java +++ b/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/GrpcServerUtils.java @@ -2,10 +2,7 @@ import com.google.protobuf.Descriptors; import com.newrelic.api.agent.security.NewRelicSecurity; -import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.GrpcHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.*; import com.newrelic.api.agent.security.schema.AgentMetaData; import com.newrelic.api.agent.security.schema.HttpRequest; import com.newrelic.api.agent.security.schema.SecurityMetaData; @@ -192,6 +189,9 @@ public static void processGRPCRequestMetadata(Metadata metadata, HttpRequest sec NewRelicSecurity.getAgent().getSecurityMetaData().setFuzzRequestIdentifier(ServletHelper.parseFuzzRequestIdentifierHeader(metadata.get(Metadata.Key.of(headerKey, Metadata.ASCII_STRING_MARSHALLER)))); } else if (GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(GenericHelper.CSEC_PARENT_ID, metadata.get(Metadata.Key.of(headerKey, Metadata.ASCII_STRING_MARSHALLER))); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; String[] headerElements = metadata.get(Metadata.Key.of(headerKey, Metadata.ASCII_STRING_MARSHALLER)).split(";"); diff --git a/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/processor/GrpcRequestProcessor.java b/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/processor/GrpcRequestProcessor.java index 242b46e43..b42863147 100644 --- a/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/processor/GrpcRequestProcessor.java +++ b/instrumentation-security/grpc-1.4.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc140/processor/GrpcRequestProcessor.java @@ -10,7 +10,7 @@ import java.util.concurrent.Future; public class GrpcRequestProcessor implements Callable { - public static final String CALL_FAILED_REQUEST_S_REASON = "Call failed : request %s reason : "; + public static final String CALL_FAILED_REQUEST_S_REASON = "Call failed : request %s reason : %s "; private ControlCommandDto controlCommandDto; private int repeatCount; private static final int MAX_REPETITION = 3; @@ -31,18 +31,18 @@ public static void executeGrpcRequest(ControlCommandDto controlCommandDto) { try { Object futureResult = future.get(); if (futureResult instanceof Throwable) { - NewRelicSecurity.getAgent().log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean()), (Throwable) futureResult, GrpcClient.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean(), ((Throwable) futureResult).getMessage()), (Throwable) futureResult, GrpcClient.class.getName()); NewRelicSecurity.getAgent().reportIncident(LogLevel.WARNING, - String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId()), + String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId(), ((Throwable) futureResult).getMessage()), (Throwable) futureResult, GrpcClient.class.getName()); GrpcClientRequestReplayHelper.getInstance().addFuzzFailEventToQueue(controlCommandDto.getRequestBean(), (Throwable) futureResult); } else { GrpcClientRequestReplayHelper.getInstance().getPendingIds().remove(controlCommandDto.getId()); } } catch (Throwable e) { - NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean()), e, GrpcRequestProcessor.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean(), e.getMessage()), e, GrpcRequestProcessor.class.getName()); NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, - String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId()), + String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId(), e.getMessage()), e, GrpcRequestProcessor.class.getName()); GrpcClientRequestReplayHelper.getInstance().addFuzzFailEventToQueue(controlCommandDto.getRequestBean(), e); } diff --git a/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/GrpcClientUtils.java b/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/GrpcClientUtils.java index 2086ca745..30c9953b7 100644 --- a/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/GrpcClientUtils.java +++ b/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/GrpcClientUtils.java @@ -39,6 +39,7 @@ public static AbstractOperation preprocessSecurityHook(String uri, Metadata meta SSRFOperation operation = new SSRFOperation(uri, klass, METHOD_NAME_START); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); String iastHeader = NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getRaw(); if (iastHeader != null && !iastHeader.trim().isEmpty()) { diff --git a/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/GrpcServerUtils.java b/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/GrpcServerUtils.java index b60d767d3..92fe00a3d 100644 --- a/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/GrpcServerUtils.java +++ b/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/GrpcServerUtils.java @@ -2,10 +2,7 @@ import com.google.protobuf.Descriptors; import com.newrelic.api.agent.security.NewRelicSecurity; -import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.GrpcHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.*; import com.newrelic.api.agent.security.schema.AgentMetaData; import com.newrelic.api.agent.security.schema.HttpRequest; import com.newrelic.api.agent.security.schema.SecurityMetaData; @@ -186,6 +183,9 @@ public static void processGRPCRequestMetadata(Metadata metadata, HttpRequest sec NewRelicSecurity.getAgent().getSecurityMetaData().setFuzzRequestIdentifier(ServletHelper.parseFuzzRequestIdentifierHeader(metadata.get(Metadata.Key.of(headerKey, Metadata.ASCII_STRING_MARSHALLER)))); } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(GenericHelper.CSEC_PARENT_ID, metadata.get(Metadata.Key.of(headerKey, Metadata.ASCII_STRING_MARSHALLER))); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; String[] headerElements = metadata.get(Metadata.Key.of(headerKey, Metadata.ASCII_STRING_MARSHALLER)).split(";"); diff --git a/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/processor/GrpcRequestProcessor.java b/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/processor/GrpcRequestProcessor.java index f9321adad..8d232c7e6 100644 --- a/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/processor/GrpcRequestProcessor.java +++ b/instrumentation-security/grpc-1.40.0/src/main/java/com/newrelic/agent/security/instrumentation/grpc1400/processor/GrpcRequestProcessor.java @@ -10,7 +10,7 @@ import java.util.concurrent.Future; public class GrpcRequestProcessor implements Callable { - public static final String CALL_FAILED_REQUEST_S_REASON = "Call failed : request %s reason : "; + public static final String CALL_FAILED_REQUEST_S_REASON = "Call failed : request %s reason : %s "; private ControlCommandDto controlCommandDto; private int repeatCount; private static final int MAX_REPETITION = 3; @@ -31,18 +31,18 @@ public static void executeGrpcRequest(ControlCommandDto controlCommandDto) { try { Object futureResult = future.get(); if (futureResult instanceof Throwable) { - NewRelicSecurity.getAgent().log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean()), (Throwable) futureResult, GrpcClient.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean(), ((Throwable) futureResult).getMessage()), (Throwable) futureResult, GrpcClient.class.getName()); NewRelicSecurity.getAgent().reportIncident(LogLevel.WARNING, - String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId()), + String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId(), ((Throwable) futureResult).getMessage()), (Throwable) futureResult, GrpcClient.class.getName()); GrpcClientRequestReplayHelper.getInstance().addFuzzFailEventToQueue(controlCommandDto.getRequestBean(), (Throwable) futureResult); } else { GrpcClientRequestReplayHelper.getInstance().getPendingIds().remove(controlCommandDto.getId()); } } catch (Throwable e) { - NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean()), e, GrpcRequestProcessor.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getRequestBean(), e.getMessage()), e, GrpcRequestProcessor.class.getName()); NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, - String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId()), + String.format(CALL_FAILED_REQUEST_S_REASON, controlCommandDto.getId(), e.getMessage()), e, GrpcRequestProcessor.class.getName()); GrpcClientRequestReplayHelper.getInstance().addFuzzFailEventToQueue(controlCommandDto.getRequestBean(), e); } diff --git a/instrumentation-security/httpclient-5.0/src/main/java/com/newrelic/agent/security/instrumentation/httpclient50/SecurityHelper.java b/instrumentation-security/httpclient-5.0/src/main/java/com/newrelic/agent/security/instrumentation/httpclient50/SecurityHelper.java index fd9727b83..65de23af7 100644 --- a/instrumentation-security/httpclient-5.0/src/main/java/com/newrelic/agent/security/instrumentation/httpclient50/SecurityHelper.java +++ b/instrumentation-security/httpclient-5.0/src/main/java/com/newrelic/agent/security/instrumentation/httpclient50/SecurityHelper.java @@ -52,6 +52,7 @@ public static AbstractOperation preprocessSecurityHook(HttpRequest request, Stri SSRFOperation operation = new SSRFOperation(uri, className, methodName); try { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } finally { if (operation.getApiID() != null && !operation.getApiID().trim().isEmpty() && diff --git a/instrumentation-security/javax-jndi/src/main/java/javax/naming/Context_Instrumentation.java b/instrumentation-security/javax-jndi/src/main/java/javax/naming/Context_Instrumentation.java index 7b011bdcb..507e695cd 100644 --- a/instrumentation-security/javax-jndi/src/main/java/javax/naming/Context_Instrumentation.java +++ b/instrumentation-security/javax-jndi/src/main/java/javax/naming/Context_Instrumentation.java @@ -10,7 +10,6 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.agent.security.instrumentation.javax.jndi.JNDIUtils; import java.util.Enumeration; import java.util.List; diff --git a/instrumentation-security/javax-jndi/src/main/java/com/newrelic/agent/security/instrumentation/javax/jndi/JNDIUtils.java b/instrumentation-security/javax-jndi/src/main/java/javax/naming/JNDIUtils.java similarity index 97% rename from instrumentation-security/javax-jndi/src/main/java/com/newrelic/agent/security/instrumentation/javax/jndi/JNDIUtils.java rename to instrumentation-security/javax-jndi/src/main/java/javax/naming/JNDIUtils.java index a50035bfc..b4c4576ce 100644 --- a/instrumentation-security/javax-jndi/src/main/java/com/newrelic/agent/security/instrumentation/javax/jndi/JNDIUtils.java +++ b/instrumentation-security/javax-jndi/src/main/java/javax/naming/JNDIUtils.java @@ -1,4 +1,4 @@ -package com.newrelic.agent.security.instrumentation.javax.jndi; +package javax.naming; import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; diff --git a/instrumentation-security/javax-jndi/src/test/java/com/nr/agent/security/instrumentation/javax/jndi/ContextRmiTest.java b/instrumentation-security/javax-jndi/src/test/java/com/nr/agent/security/instrumentation/javax/jndi/ContextRmiTest.java index 6f0e71eae..a2d2a52ed 100644 --- a/instrumentation-security/javax-jndi/src/test/java/com/nr/agent/security/instrumentation/javax/jndi/ContextRmiTest.java +++ b/instrumentation-security/javax-jndi/src/test/java/com/nr/agent/security/instrumentation/javax/jndi/ContextRmiTest.java @@ -6,7 +6,7 @@ import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.SSRFOperation; -import com.newrelic.agent.security.instrumentation.javax.jndi.JNDIUtils; +import javax.naming.JNDIUtils; import org.junit.Assert; import org.junit.FixMethodOrder; import org.junit.Test; diff --git a/instrumentation-security/javax-jndi/src/test/java/com/nr/agent/security/instrumentation/javax/jndi/ContextTest.java b/instrumentation-security/javax-jndi/src/test/java/com/nr/agent/security/instrumentation/javax/jndi/ContextTest.java index b4295dc77..e21071234 100644 --- a/instrumentation-security/javax-jndi/src/test/java/com/nr/agent/security/instrumentation/javax/jndi/ContextTest.java +++ b/instrumentation-security/javax-jndi/src/test/java/com/nr/agent/security/instrumentation/javax/jndi/ContextTest.java @@ -6,7 +6,7 @@ import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.SSRFOperation; -import com.newrelic.agent.security.instrumentation.javax.jndi.JNDIUtils; +import javax.naming.JNDIUtils; import com.unboundid.ldap.sdk.LDAPException; import org.junit.Assert; import org.junit.BeforeClass; diff --git a/instrumentation-security/javax-ldap/src/main/java/javax/naming/directory/DirContext_Instrumentation.java b/instrumentation-security/javax-ldap/src/main/java/javax/naming/directory/DirContext_Instrumentation.java index 310909107..a2f263627 100644 --- a/instrumentation-security/javax-ldap/src/main/java/javax/naming/directory/DirContext_Instrumentation.java +++ b/instrumentation-security/javax-ldap/src/main/java/javax/naming/directory/DirContext_Instrumentation.java @@ -10,7 +10,6 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.agent.security.instrumentation.javax.ldap.LDAPUtils; import javax.naming.Context; import javax.naming.Name; diff --git a/instrumentation-security/javax-ldap/src/main/java/com/newrelic/agent/security/instrumentation/javax/ldap/LDAPUtils.java b/instrumentation-security/javax-ldap/src/main/java/javax/naming/directory/LDAPUtils.java similarity index 78% rename from instrumentation-security/javax-ldap/src/main/java/com/newrelic/agent/security/instrumentation/javax/ldap/LDAPUtils.java rename to instrumentation-security/javax-ldap/src/main/java/javax/naming/directory/LDAPUtils.java index 749dabd50..1da7ed112 100644 --- a/instrumentation-security/javax-ldap/src/main/java/com/newrelic/agent/security/instrumentation/javax/ldap/LDAPUtils.java +++ b/instrumentation-security/javax-ldap/src/main/java/javax/naming/directory/LDAPUtils.java @@ -1,4 +1,4 @@ -package com.newrelic.agent.security.instrumentation.javax.ldap; +package javax.naming.directory; public class LDAPUtils { diff --git a/instrumentation-security/javax-ldap/src/test/java/com/nr/agent/security/instrumentation/javax/ldap/DirContextTest.java b/instrumentation-security/javax-ldap/src/test/java/com/nr/agent/security/instrumentation/javax/ldap/DirContextTest.java index 4d50e493c..e5e9ea75b 100644 --- a/instrumentation-security/javax-ldap/src/test/java/com/nr/agent/security/instrumentation/javax/ldap/DirContextTest.java +++ b/instrumentation-security/javax-ldap/src/test/java/com/nr/agent/security/instrumentation/javax/ldap/DirContextTest.java @@ -31,7 +31,7 @@ import java.util.List; @RunWith(SecurityInstrumentationTestRunner.class) -@InstrumentationTestConfig(includePrefixes = { "javax.naming", "com.newrelic.agent.security.instrumentation.javax.ldap.LDAPUtils" } ) +@InstrumentationTestConfig(includePrefixes = { "javax.naming", "javax.naming.directory.LDAPUtils" } ) @FixMethodOrder(MethodSorters.NAME_ASCENDING) //FIXME: after instrumentation works @Ignore diff --git a/instrumentation-security/javax-xpath/src/main/java/com/newrelic/agent/security/instrumentation/xpath/javax/XPATHUtils.java b/instrumentation-security/javax-xpath/src/main/java/com/newrelic/agent/security/instrumentation/xpath/javax/XPATHUtils.java deleted file mode 100644 index 7f2b1ed9b..000000000 --- a/instrumentation-security/javax-xpath/src/main/java/com/newrelic/agent/security/instrumentation/xpath/javax/XPATHUtils.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.newrelic.agent.security.instrumentation.xpath.javax; - -public class XPATHUtils { - - public static final String NR_SEC_CUSTOM_ATTRIB_NAME = "XPATH_OPERATION_LOCK_JAVAXPATH-"; - public static final String METHOD_EVALUATE = "evaluate"; - - public static final String METHOD_EXECUTE = "execute"; - public static final Object JAVAX_XPATH = "JAVAX-XPATH"; -} diff --git a/instrumentation-security/javax-xpath/src/main/java/com/sun/org/apache/xpath/internal/XPath_Instrumentation.java b/instrumentation-security/javax-xpath/src/main/java/com/sun/org/apache/xpath/internal/XPath_Instrumentation.java index 7b335a93f..777896065 100644 --- a/instrumentation-security/javax-xpath/src/main/java/com/sun/org/apache/xpath/internal/XPath_Instrumentation.java +++ b/instrumentation-security/javax-xpath/src/main/java/com/sun/org/apache/xpath/internal/XPath_Instrumentation.java @@ -10,7 +10,6 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.agent.security.instrumentation.xpath.javax.XPATHUtils; import com.sun.org.apache.xml.internal.utils.PrefixResolver; import com.sun.org.apache.xpath.internal.objects.XObject; @@ -26,7 +25,7 @@ public XObject execute( boolean isLockAcquired = acquireLockIfPossible(); AbstractOperation operation = null; if(isLockAcquired) { - operation = preprocessSecurityHook(getPatternString(), XPATHUtils.METHOD_EXECUTE); + operation = preprocessSecurityHook(getPatternString(), "execute"); } XObject returnVal = null; @@ -49,7 +48,7 @@ public XObject execute( boolean isLockAcquired = acquireLockIfPossible(); AbstractOperation operation = null; if(isLockAcquired) { - operation = preprocessSecurityHook(getPatternString(), XPATHUtils.METHOD_EXECUTE); + operation = preprocessSecurityHook(getPatternString(), "execute"); } XObject returnVal = null; @@ -87,24 +86,24 @@ private AbstractOperation preprocessSecurityHook (String patternString, String m return xPathOperation; } catch (Throwable e) { if (e instanceof NewRelicSecurityException) { - NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, XPATHUtils.JAVAX_XPATH, e.getMessage()), e, this.getClass().getName()); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, "JAVAX-XPATH", e.getMessage()), e, this.getClass().getName()); throw e; } - NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, XPATHUtils.JAVAX_XPATH, e.getMessage()), e, this.getClass().getName()); - NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE , String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, XPATHUtils.JAVAX_XPATH, e.getMessage()), e, this.getClass().getName()); + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, "JAVAX-XPATH", e.getMessage()), e, this.getClass().getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE , String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, "JAVAX-XPATH", e.getMessage()), e, this.getClass().getName()); } return null; } private void releaseLock() { try { - GenericHelper.releaseLock(XPATHUtils.NR_SEC_CUSTOM_ATTRIB_NAME); + GenericHelper.releaseLock("XPATH_OPERATION_LOCK_JAVAXPATH-"); } catch (Throwable ignored) {} } private boolean acquireLockIfPossible() { try { - return GenericHelper.acquireLockIfPossible(XPATHUtils.NR_SEC_CUSTOM_ATTRIB_NAME); + return GenericHelper.acquireLockIfPossible("XPATH_OPERATION_LOCK_JAVAXPATH-"); } catch (Throwable ignored) {} return false; } diff --git a/instrumentation-security/javax-xpath/src/main/java/javax/xml/xpath/XPath_Instrumentation.java b/instrumentation-security/javax-xpath/src/main/java/javax/xml/xpath/XPath_Instrumentation.java index 04742b312..ce65bb97b 100644 --- a/instrumentation-security/javax-xpath/src/main/java/javax/xml/xpath/XPath_Instrumentation.java +++ b/instrumentation-security/javax-xpath/src/main/java/javax/xml/xpath/XPath_Instrumentation.java @@ -10,7 +10,6 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.agent.security.instrumentation.xpath.javax.XPATHUtils; import org.xml.sax.InputSource; import javax.xml.namespace.QName; @@ -23,7 +22,7 @@ public String evaluate(String expression, InputSource source) boolean isLockAcquired = acquireLockIfPossible(); AbstractOperation operation = null; if(isLockAcquired) { - operation = preprocessSecurityHook(expression, XPATHUtils.METHOD_EVALUATE); + operation = preprocessSecurityHook(expression, "evaluate"); } String returnVal = null; @@ -46,7 +45,7 @@ public Object evaluate( boolean isLockAcquired = acquireLockIfPossible(); AbstractOperation operation = null; if(isLockAcquired) { - operation = preprocessSecurityHook(expression, XPATHUtils.METHOD_EVALUATE); + operation = preprocessSecurityHook(expression, "evaluate"); } Object returnVal = null; @@ -66,7 +65,7 @@ public String evaluate(String expression, Object item) boolean isLockAcquired = acquireLockIfPossible(); AbstractOperation operation = null; if(isLockAcquired) { - operation = preprocessSecurityHook(expression, XPATHUtils.METHOD_EVALUATE); + operation = preprocessSecurityHook(expression, "evaluate"); } String returnVal = null; @@ -86,7 +85,7 @@ public Object evaluate(String expression, Object item, QName returnType) boolean isLockAcquired = acquireLockIfPossible(); AbstractOperation operation = null; if(isLockAcquired) { - operation = preprocessSecurityHook(expression, XPATHUtils.METHOD_EVALUATE); + operation = preprocessSecurityHook(expression, "evaluate"); } Object returnVal = null; @@ -110,7 +109,7 @@ private void registerExitOperation(boolean isProcessingAllowed, AbstractOperatio } NewRelicSecurity.getAgent().registerExitEvent(operation); } catch (Throwable ignored){ - NewRelicSecurity.getAgent().log(LogLevel.FINEST, String.format(GenericHelper.EXIT_OPERATION_EXCEPTION_MESSAGE, XPATHUtils.JAVAX_XPATH, ignored.getMessage()), ignored, this.getClass().getName()); + NewRelicSecurity.getAgent().log(LogLevel.FINEST, String.format(GenericHelper.EXIT_OPERATION_EXCEPTION_MESSAGE, "JAVAX-XPATH", ignored.getMessage()), ignored, this.getClass().getName()); } } @@ -126,24 +125,24 @@ private AbstractOperation preprocessSecurityHook (String patternString, String m return xPathOperation; } catch (Throwable e) { if (e instanceof NewRelicSecurityException) { - NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, XPATHUtils.JAVAX_XPATH, e.getMessage()), e, this.getClass().getName()); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, "JAVAX-XPATH", e.getMessage()), e, this.getClass().getName()); throw e; } - NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, XPATHUtils.JAVAX_XPATH, e.getMessage()), e, this.getClass().getName()); - NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE , String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, XPATHUtils.JAVAX_XPATH, e.getMessage()), e, this.getClass().getName()); + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, "JAVAX-XPATH", e.getMessage()), e, this.getClass().getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE , String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, "JAVAX-XPATH", e.getMessage()), e, this.getClass().getName()); } return null; } private void releaseLock() { try { - GenericHelper.releaseLock(XPATHUtils.NR_SEC_CUSTOM_ATTRIB_NAME); + GenericHelper.releaseLock("XPATH_OPERATION_LOCK_JAVAXPATH-"); } catch (Throwable ignored) {} } private boolean acquireLockIfPossible() { try { - return GenericHelper.acquireLockIfPossible(XPATHUtils.NR_SEC_CUSTOM_ATTRIB_NAME); + return GenericHelper.acquireLockIfPossible("XPATH_OPERATION_LOCK_JAVAXPATH-"); } catch (Throwable ignored) {} return false; } diff --git a/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/XPathTest.java b/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/XPathTest.java index 37701dd21..7089a53a0 100644 --- a/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/XPathTest.java +++ b/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/XPathTest.java @@ -6,7 +6,6 @@ import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.XPathOperation; -import com.newrelic.agent.security.instrumentation.xpath.javax.XPATHUtils; import com.newrelic.security.test.marker.Java17IncompatibleTest; import org.junit.Assert; import org.junit.FixMethodOrder; @@ -33,6 +32,8 @@ public class XPathTest { private final String XML_DOC = "src/test/resources/Customer.xml"; private final String EXPRESSION = "/Customers/Customer"; + public static final String METHOD_EVALUATE = "evaluate"; + @Test public void testEvaluate() throws Exception { InputSource source = new InputSource(XML_DOC); @@ -46,7 +47,7 @@ public void testEvaluate() throws Exception { XPathOperation operation = (XPathOperation) operations.get(0); Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.XPATH, operation.getCaseType()); - Assert.assertEquals("Invalid executed method name.", XPATHUtils.METHOD_EVALUATE, operation.getMethodName()); + Assert.assertEquals("Invalid executed method name.", METHOD_EVALUATE, operation.getMethodName()); Assert.assertEquals("Invalid expression", EXPRESSION, operation.getExpression()); } @@ -63,7 +64,7 @@ public void testEvaluate1() throws Exception { XPathOperation operation = (XPathOperation) operations.get(0); Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.XPATH, operation.getCaseType()); - Assert.assertEquals("Invalid executed method name.", XPATHUtils.METHOD_EVALUATE, operation.getMethodName()); + Assert.assertEquals("Invalid executed method name.", METHOD_EVALUATE, operation.getMethodName()); Assert.assertEquals("Invalid expression", EXPRESSION, operation.getExpression()); } @@ -80,7 +81,7 @@ public void testEvaluate2() throws Exception { XPathOperation operation = (XPathOperation) operations.get(0); Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.XPATH, operation.getCaseType()); - Assert.assertEquals("Invalid executed method name.", XPATHUtils.METHOD_EVALUATE, operation.getMethodName()); + Assert.assertEquals("Invalid executed method name.", METHOD_EVALUATE, operation.getMethodName()); Assert.assertEquals("Invalid expression", EXPRESSION, operation.getExpression()); } @@ -97,7 +98,7 @@ public void testEvaluate3() throws Exception { XPathOperation operation = (XPathOperation) operations.get(0); Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.XPATH, operation.getCaseType()); - Assert.assertEquals("Invalid executed method name.", XPATHUtils.METHOD_EVALUATE, operation.getMethodName()); + Assert.assertEquals("Invalid executed method name.", METHOD_EVALUATE, operation.getMethodName()); Assert.assertEquals("Invalid expression", EXPRESSION, operation.getExpression()); } @Test @@ -114,7 +115,7 @@ public void testCompile() throws Exception { XPathOperation operation = (XPathOperation) operations.get(0); Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.XPATH, operation.getCaseType()); - Assert.assertEquals("Invalid executed method name.", XPATHUtils.METHOD_EVALUATE, operation.getMethodName()); + Assert.assertEquals("Invalid executed method name.", METHOD_EVALUATE, operation.getMethodName()); Assert.assertEquals("Invalid expression", EXPRESSION, operation.getExpression()); } @@ -132,7 +133,7 @@ public void testCompile1() throws Exception { XPathOperation operation = (XPathOperation) operations.get(0); Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.XPATH, operation.getCaseType()); - Assert.assertEquals("Invalid executed method name.", XPATHUtils.METHOD_EVALUATE, operation.getMethodName()); + Assert.assertEquals("Invalid executed method name.", METHOD_EVALUATE, operation.getMethodName()); Assert.assertEquals("Invalid expression", EXPRESSION, operation.getExpression()); } @@ -150,7 +151,7 @@ public void testCompile2() throws Exception { XPathOperation operation = (XPathOperation) operations.get(0); Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.XPATH, operation.getCaseType()); - Assert.assertEquals("Invalid executed method name.", XPATHUtils.METHOD_EVALUATE, operation.getMethodName()); + Assert.assertEquals("Invalid executed method name.", METHOD_EVALUATE, operation.getMethodName()); Assert.assertEquals("Invalid expression", EXPRESSION, operation.getExpression()); } @@ -168,7 +169,7 @@ public void testCompile3() throws Exception { XPathOperation operation = (XPathOperation) operations.get(0); Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.XPATH, operation.getCaseType()); - Assert.assertEquals("Invalid executed method name.", XPATHUtils.METHOD_EVALUATE, operation.getMethodName()); + Assert.assertEquals("Invalid executed method name.", METHOD_EVALUATE, operation.getMethodName()); Assert.assertEquals("Invalid expression", EXPRESSION, operation.getExpression()); } } diff --git a/instrumentation-security/jax-rs-1.0/src/main/java/com/newrelic/agent/security/instrumentation/javax/ws/rs/api/JavaxWsRsApi_Instrumentation.java b/instrumentation-security/jax-rs-1.0/src/main/java/com/newrelic/agent/security/instrumentation/javax/ws/rs/api/JavaxWsRsApi_Instrumentation.java index b7211b1af..c18d2c446 100644 --- a/instrumentation-security/jax-rs-1.0/src/main/java/com/newrelic/agent/security/instrumentation/javax/ws/rs/api/JavaxWsRsApi_Instrumentation.java +++ b/instrumentation-security/jax-rs-1.0/src/main/java/com/newrelic/agent/security/instrumentation/javax/ws/rs/api/JavaxWsRsApi_Instrumentation.java @@ -20,5 +20,6 @@ public class JavaxWsRsApi_Instrumentation { @WeaveIntoAllMethods public static void preprocessSecurityHook() { ServletHelper.registerUserLevelCode("jax-rs"); + ServletHelper.setFoundAnnotedUserLevelServiceMethod(); } } diff --git a/instrumentation-security/jax-rs-1.0/src/main/java/com/newrelic/agent/security/instrumentation/javax/ws/rs/api/JavaxWsRsApi_Subresource_Instrumentation.java b/instrumentation-security/jax-rs-1.0/src/main/java/com/newrelic/agent/security/instrumentation/javax/ws/rs/api/JavaxWsRsApi_Subresource_Instrumentation.java index 15018dfc2..b05be8a40 100644 --- a/instrumentation-security/jax-rs-1.0/src/main/java/com/newrelic/agent/security/instrumentation/javax/ws/rs/api/JavaxWsRsApi_Subresource_Instrumentation.java +++ b/instrumentation-security/jax-rs-1.0/src/main/java/com/newrelic/agent/security/instrumentation/javax/ws/rs/api/JavaxWsRsApi_Subresource_Instrumentation.java @@ -34,5 +34,6 @@ public class JavaxWsRsApi_Subresource_Instrumentation { @WeaveIntoAllMethods public static void preprocessSecurityHook() { ServletHelper.registerUserLevelCode("jax-rs"); + ServletHelper.setFoundAnnotedUserLevelServiceMethod(); } } diff --git a/instrumentation-security/jax-rs-1.0/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/JavaxWsRsApiTest.java b/instrumentation-security/jax-rs-1.0/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/JavaxWsRsApiTest.java index 4146f3dff..d73b39901 100644 --- a/instrumentation-security/jax-rs-1.0/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/JavaxWsRsApiTest.java +++ b/instrumentation-security/jax-rs-1.0/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/JavaxWsRsApiTest.java @@ -21,6 +21,7 @@ public void testPut() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testPost() { @@ -31,6 +32,7 @@ public void testPost() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testGet() { @@ -41,6 +43,7 @@ public void testGet() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testDelete() { @@ -51,6 +54,7 @@ public void testDelete() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testHead() { @@ -60,6 +64,7 @@ public void testHead() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testOptions() { @@ -70,6 +75,7 @@ public void testOptions() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testPatch() { @@ -80,6 +86,7 @@ public void testPatch() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -91,5 +98,6 @@ public void testPath() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } } diff --git a/instrumentation-security/jax-rs-1.0/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/SubresourceTest.java b/instrumentation-security/jax-rs-1.0/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/SubresourceTest.java index 61befaaf5..a67197b37 100644 --- a/instrumentation-security/jax-rs-1.0/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/SubresourceTest.java +++ b/instrumentation-security/jax-rs-1.0/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/SubresourceTest.java @@ -40,6 +40,7 @@ public void testPost() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -55,6 +56,7 @@ public void testPath() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -67,6 +69,7 @@ public void testGet() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -82,6 +85,7 @@ public void testPut() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -94,6 +98,7 @@ public void testDelete() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -106,6 +111,7 @@ public void testHead() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -118,6 +124,7 @@ public void testOptions() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testPatch() { @@ -131,6 +138,7 @@ public void testPatch() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } diff --git a/instrumentation-security/jax-rs-3.0/src/main/java/com/newrelic/agent/security/instrumentation/jakarta/ws/rs/api/JakartaWsRsApi_Instrumentation.java b/instrumentation-security/jax-rs-3.0/src/main/java/com/newrelic/agent/security/instrumentation/jakarta/ws/rs/api/JakartaWsRsApi_Instrumentation.java index 1414c047f..d7f20c912 100644 --- a/instrumentation-security/jax-rs-3.0/src/main/java/com/newrelic/agent/security/instrumentation/jakarta/ws/rs/api/JakartaWsRsApi_Instrumentation.java +++ b/instrumentation-security/jax-rs-3.0/src/main/java/com/newrelic/agent/security/instrumentation/jakarta/ws/rs/api/JakartaWsRsApi_Instrumentation.java @@ -20,5 +20,6 @@ public class JakartaWsRsApi_Instrumentation { @WeaveIntoAllMethods public static void preprocessSecurityHook() { ServletHelper.registerUserLevelCode("jax-rs"); + ServletHelper.setFoundAnnotedUserLevelServiceMethod(); } } diff --git a/instrumentation-security/jax-rs-3.0/src/main/java/com/newrelic/agent/security/instrumentation/jakarta/ws/rs/api/JakartaWsRsApi_Subresource_Instrumentation.java b/instrumentation-security/jax-rs-3.0/src/main/java/com/newrelic/agent/security/instrumentation/jakarta/ws/rs/api/JakartaWsRsApi_Subresource_Instrumentation.java index 1b99cdf70..73f33dbbd 100644 --- a/instrumentation-security/jax-rs-3.0/src/main/java/com/newrelic/agent/security/instrumentation/jakarta/ws/rs/api/JakartaWsRsApi_Subresource_Instrumentation.java +++ b/instrumentation-security/jax-rs-3.0/src/main/java/com/newrelic/agent/security/instrumentation/jakarta/ws/rs/api/JakartaWsRsApi_Subresource_Instrumentation.java @@ -35,5 +35,6 @@ public class JakartaWsRsApi_Subresource_Instrumentation { @WeaveIntoAllMethods public static void preprocessSecurityHook() { ServletHelper.registerUserLevelCode("jax-rs"); + ServletHelper.setFoundAnnotedUserLevelServiceMethod(); } } diff --git a/instrumentation-security/jax-rs-3.0/src/test/java/com/nr/agent/security/instrumentation/jakarta/ws/rs/api/test/JakartaWsRsApiTest.java b/instrumentation-security/jax-rs-3.0/src/test/java/com/nr/agent/security/instrumentation/jakarta/ws/rs/api/test/JakartaWsRsApiTest.java index 25d0b1eba..33eb00eee 100644 --- a/instrumentation-security/jax-rs-3.0/src/test/java/com/nr/agent/security/instrumentation/jakarta/ws/rs/api/test/JakartaWsRsApiTest.java +++ b/instrumentation-security/jax-rs-3.0/src/test/java/com/nr/agent/security/instrumentation/jakarta/ws/rs/api/test/JakartaWsRsApiTest.java @@ -21,6 +21,7 @@ public void testPut() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testPost() { @@ -31,6 +32,7 @@ public void testPost() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testGet() { @@ -41,6 +43,7 @@ public void testGet() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testDelete() { @@ -51,6 +54,7 @@ public void testDelete() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testHead() { @@ -61,6 +65,7 @@ public void testHead() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testOptions() { @@ -71,6 +76,7 @@ public void testOptions() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testPatch() { @@ -81,6 +87,7 @@ public void testPatch() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testPath() { @@ -91,5 +98,6 @@ public void testPath() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } } diff --git a/instrumentation-security/jax-rs-3.0/src/test/java/com/nr/agent/security/instrumentation/jakarta/ws/rs/api/test/SubresourceTest.java b/instrumentation-security/jax-rs-3.0/src/test/java/com/nr/agent/security/instrumentation/jakarta/ws/rs/api/test/SubresourceTest.java index 3fc39262e..23f9bbb01 100644 --- a/instrumentation-security/jax-rs-3.0/src/test/java/com/nr/agent/security/instrumentation/jakarta/ws/rs/api/test/SubresourceTest.java +++ b/instrumentation-security/jax-rs-3.0/src/test/java/com/nr/agent/security/instrumentation/jakarta/ws/rs/api/test/SubresourceTest.java @@ -40,6 +40,7 @@ public void testPost() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testPath() { @@ -54,6 +55,7 @@ public void testPath() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -66,6 +68,7 @@ public void testGet() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -81,6 +84,7 @@ public void testPut() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -105,6 +109,7 @@ public void testHead() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -117,6 +122,7 @@ public void testOptions() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test public void testPatch() { @@ -131,6 +137,7 @@ public void testPatch() { Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Override diff --git a/instrumentation-security/jdbc-generic/src/main/java/java/sql/PreparedStatement_Instrumentation.java b/instrumentation-security/jdbc-generic/src/main/java/java/sql/PreparedStatement_Instrumentation.java index 0690ce8f6..083fc98da 100644 --- a/instrumentation-security/jdbc-generic/src/main/java/java/sql/PreparedStatement_Instrumentation.java +++ b/instrumentation-security/jdbc-generic/src/main/java/java/sql/PreparedStatement_Instrumentation.java @@ -22,6 +22,8 @@ import com.newrelic.api.agent.weaver.Weaver; import java.math.BigDecimal; +import java.net.URL; +import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; @@ -32,6 +34,9 @@ public abstract class PreparedStatement_Instrumentation { @NewField private Map params; + + @NewField + private Map objectParams; @NewField String preparedSql; @NewField @@ -236,6 +241,128 @@ public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { Weaver.callOriginal(); } + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + setParamValue(parameterIndex, xmlObject.getString()); + Weaver.callOriginal(); + } + + public void setRowId(int parameterIndex, RowId x) throws SQLException { + setParamValue(parameterIndex, x.toString()); + Weaver.callOriginal(); + } + + public void setURL(int parameterIndex, java.net.URL x) throws SQLException { + setParamValue(parameterIndex, x.toString()); + Weaver.callOriginal(); + } + + public void setArray (int parameterIndex, Array x) throws SQLException { + setObjectParams(parameterIndex, x.getArray()); + Weaver.callOriginal(); + } + public void setDate(int parameterIndex, java.sql.Date x, Calendar cal) throws SQLException { + setParamValue(parameterIndex, x); + Weaver.callOriginal(); + } + public void setTime(int parameterIndex, java.sql.Time x, Calendar cal) throws SQLException { + setParamValue(parameterIndex, x); + Weaver.callOriginal(); + } + public void setTimestamp(int parameterIndex, java.sql.Timestamp x, Calendar cal) throws SQLException { + setParamValue(parameterIndex, x); + Weaver.callOriginal(); + } + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + setParamValue(parameterIndex, "null"); + Weaver.callOriginal(); + } + public void setNString(int parameterIndex, String value) throws SQLException { + setParamValue(parameterIndex, value); + Weaver.callOriginal(); + } + + public void setObject(int parameterIndex, Object x) throws SQLException { + if(x instanceof Long || x instanceof Integer || x instanceof Double || + x instanceof Float || x instanceof Boolean || x instanceof Short || + x instanceof String || x instanceof byte[] || x instanceof Timestamp || + x instanceof Date || x instanceof BigDecimal || x instanceof Time) { + setParamValue(parameterIndex, x); + } else if (x instanceof SQLXML) { + setParamValue(parameterIndex, ((SQLXML) x).getString()); + } else if (x instanceof RowId || x instanceof URL) { + setParamValue(parameterIndex, x.toString()); + } else { + //TODO critical-message for inconvertible + setObjectParams(parameterIndex, x); + } + Weaver.callOriginal(); + } + + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + if(x instanceof Long || x instanceof Integer || x instanceof Double || + x instanceof Float || x instanceof Boolean || x instanceof Short || + x instanceof String || x instanceof byte[] || x instanceof Timestamp || + x instanceof Date || x instanceof BigDecimal || x instanceof Time) { + setParamValue(parameterIndex, x); + } else if (x instanceof SQLXML) { + setParamValue(parameterIndex, ((SQLXML) x).getString()); + } else if (x instanceof RowId || x instanceof URL) { + setParamValue(parameterIndex, x.toString()); + } else { + NewRelicSecurity.getAgent().log(LogLevel.FINEST, String.format("Instrumentation library: %s , Inconvertible for type : %s", JDBC_GENERIC, x.getClass()), this.getClass().getName()); + setObjectParams(parameterIndex, x); + } + Weaver.callOriginal(); + } + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + if(x instanceof Long || x instanceof Integer || x instanceof Double || + x instanceof Float || x instanceof Boolean || x instanceof Short || + x instanceof String || x instanceof byte[] || x instanceof Timestamp || + x instanceof Date || x instanceof BigDecimal || x instanceof Time) { + setParamValue(parameterIndex, x); + } else if (x instanceof SQLXML) { + setParamValue(parameterIndex, ((SQLXML) x).getString()); + } else if (x instanceof RowId || x instanceof URL) { + setParamValue(parameterIndex, x.toString()); + } else { + NewRelicSecurity.getAgent().log(LogLevel.FINEST, String.format("Instrumentation library: %s , Inconvertible for type : %s", JDBC_GENERIC, x.getClass()), this.getClass().getName()); + setObjectParams(parameterIndex, x); + } + Weaver.callOriginal(); + } + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + if(x instanceof Long || x instanceof Integer || x instanceof Double || + x instanceof Float || x instanceof Boolean || x instanceof Short || + x instanceof String || x instanceof byte[] || x instanceof Timestamp || + x instanceof Date || x instanceof BigDecimal || x instanceof Time) { + setParamValue(parameterIndex, x); + } else if (x instanceof SQLXML) { + setParamValue(parameterIndex, ((SQLXML) x).getString()); + } else if (x instanceof RowId || x instanceof URL) { + setParamValue(parameterIndex, x.toString()); + } else { + NewRelicSecurity.getAgent().log(LogLevel.FINEST, String.format("Instrumentation library: %s , Inconvertible for type : %s", JDBC_GENERIC, x.getClass()), this.getClass().getName()); + setObjectParams(parameterIndex, x); + } + Weaver.callOriginal(); + } + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + if(x instanceof Long || x instanceof Integer || x instanceof Double || + x instanceof Float || x instanceof Boolean || x instanceof Short || + x instanceof String || x instanceof byte[] || x instanceof Timestamp || + x instanceof Date || x instanceof BigDecimal || x instanceof Time) { + setParamValue(parameterIndex, x); + } else if (x instanceof SQLXML) { + setParamValue(parameterIndex, ((SQLXML) x).getString()); + } else if (x instanceof RowId || x instanceof URL) { + setParamValue(parameterIndex, x.toString()); + } else { + NewRelicSecurity.getAgent().log(LogLevel.FINEST, String.format("Instrumentation library: %s , Inconvertible for type : %s", JDBC_GENERIC, x.getClass()), this.getClass().getName()); + setObjectParams(parameterIndex, x); + } + Weaver.callOriginal(); + } + public void clearParameters() throws SQLException { if(params != null){ params.clear(); @@ -260,6 +387,13 @@ private void setParamValue(int index, byte[] value) { params.put(String.valueOf(index), new String(value)); } + private void setObjectParams(int index, Object data) { + if (objectParams == null) { + objectParams = new HashMap<>(); + } + + objectParams.put(String.valueOf(index), data); + } public void addBatch() throws SQLException { boolean isLockAcquired = acquireLockIfPossible(); SQLOperation sqlOperation = null; @@ -267,8 +401,15 @@ public void addBatch() throws SQLException { sqlOperation = new SQLOperation(this.getClass().getName(), JdbcHelper.METHOD_EXECUTE_BATCH); sqlOperation.setQuery(preparedSql); Map localParams = new HashMap<>(); - localParams.putAll(params); + if(params != null) { + localParams.putAll(params); + } sqlOperation.setParams(localParams); + Map localObjParams = new HashMap<>(); + if (objectParams != null) { + localObjParams.putAll(objectParams); + } + sqlOperation.setObjectParams(localObjParams); sqlOperation.setDbName(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(JDBCVendor.META_CONST_JDBC_VENDOR, String.class)); sqlOperation.setPreparedCall(true); if(batchSQLOperation==null) diff --git a/instrumentation-security/jersey-2.16/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java b/instrumentation-security/jersey-2.16/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java index 3d5b05569..a84a351bc 100644 --- a/instrumentation-security/jersey-2.16/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java +++ b/instrumentation-security/jersey-2.16/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java @@ -2,6 +2,7 @@ import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; @@ -151,6 +152,9 @@ public static void processHttpRequestHeader(ContainerRequest request, HttpReques } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, headerFullValue); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } for (String headerValue : header.getValue()) { diff --git a/instrumentation-security/jersey-2/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java b/instrumentation-security/jersey-2/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java index 86f4786b3..a52a7a084 100644 --- a/instrumentation-security/jersey-2/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java +++ b/instrumentation-security/jersey-2/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java @@ -2,6 +2,7 @@ import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; @@ -153,6 +154,9 @@ public static void processHttpRequestHeader(ContainerRequest request, HttpReques } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, headerFullValue); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } for (String headerValue : header.getValue()) { diff --git a/instrumentation-security/jersey-3/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java b/instrumentation-security/jersey-3/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java index 7bcd414f8..c43db4efe 100644 --- a/instrumentation-security/jersey-3/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java +++ b/instrumentation-security/jersey-3/src/main/java/com/newrelic/agent/security/instrumentation/jersey2/HttpRequestHelper.java @@ -2,6 +2,7 @@ import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; @@ -151,6 +152,9 @@ public static void processHttpRequestHeader(ContainerRequest request, HttpReques } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, headerFullValue); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } for (String headerValue : header.getValue()) { diff --git a/instrumentation-security/jersey/build.gradle b/instrumentation-security/jersey/build.gradle index e39469d3c..2a7535835 100644 --- a/instrumentation-security/jersey/build.gradle +++ b/instrumentation-security/jersey/build.gradle @@ -3,6 +3,10 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") implementation('org.glassfish.jersey.core:jersey-server:2.28') + testImplementation("org.glassfish.jersey.containers:jersey-container-servlet:2.28") + testImplementation("org.glassfish.jersey.test-framework:jersey-test-framework-core:2.28") + testImplementation("org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:2.28") + testImplementation("org.glassfish.jersey.inject:jersey-hk2:2.28") } diff --git a/instrumentation-security/jersey/src/main/java/com/nr/instrumentation/security/jersey/JerseyHelper.java b/instrumentation-security/jersey/src/main/java/com/newrelic/agent/security/instrumentation/jersey/JerseyHelper.java similarity index 86% rename from instrumentation-security/jersey/src/main/java/com/nr/instrumentation/security/jersey/JerseyHelper.java rename to instrumentation-security/jersey/src/main/java/com/newrelic/agent/security/instrumentation/jersey/JerseyHelper.java index 5855f7d64..8b6b88053 100644 --- a/instrumentation-security/jersey/src/main/java/com/nr/instrumentation/security/jersey/JerseyHelper.java +++ b/instrumentation-security/jersey/src/main/java/com/newrelic/agent/security/instrumentation/jersey/JerseyHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.jersey; +package com.newrelic.agent.security.instrumentation.jersey; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; import com.newrelic.api.agent.security.instrumentation.helpers.*; @@ -14,6 +14,8 @@ public class JerseyHelper { private static final String EMPTY = ""; private static final String WILDCARD = "*"; private static final String SEPARATOR = "/"; + public static final String ORG_GLASSFISH_JERSEY_SERVER_WADL = "org.glassfish.jersey.server.wadl"; + public static void gatherUrlMappings(ResourceModel resourceModel) { try { List resources = resourceModel.getResources(); @@ -56,7 +58,9 @@ private static void extractMappingsFromResources(List resources, Strin private static void addURLMappings(String url, String httpMethod, Set> handlerClasses) { for (Class handlerClass : handlerClasses) { - URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(httpMethod, url, handlerClass.getName())); + if (!handlerClass.getName().startsWith(ORG_GLASSFISH_JERSEY_SERVER_WADL)){ + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(httpMethod, url, handlerClass.getName())); + } } } } diff --git a/instrumentation-security/jersey/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext_Instrumentation.java b/instrumentation-security/jersey/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext_Instrumentation.java index 149fc5cca..ef6eb3234 100644 --- a/instrumentation-security/jersey/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext_Instrumentation.java +++ b/instrumentation-security/jersey/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext_Instrumentation.java @@ -3,7 +3,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.security.jersey.JerseyHelper; +import com.newrelic.agent.security.instrumentation.jersey.JerseyHelper; import org.glassfish.jersey.server.model.ResourceModel; diff --git a/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/CustomerLocatorResource.java b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/CustomerLocatorResource.java new file mode 100644 index 000000000..9da61e921 --- /dev/null +++ b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/CustomerLocatorResource.java @@ -0,0 +1,17 @@ +package com.nr.agent.security.instrumentation.javax.ws.rs.api.app; + +import javax.ws.rs.Path; + +@Path("/customers") +public class CustomerLocatorResource { + + protected OrdersSubResource ordersSubResource = new OrdersSubResource(); + + @Path("orders") + public Object getOrders() { + return ordersSubResource; + } + + +} + diff --git a/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/IdSubResource.java b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/IdSubResource.java new file mode 100644 index 000000000..f65be73fa --- /dev/null +++ b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/IdSubResource.java @@ -0,0 +1,18 @@ +package com.nr.agent.security.instrumentation.javax.ws.rs.api.app; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; + +public class IdSubResource { + + + @GET + @Path("{id}") + @Produces("application/json") + public String getById(@PathParam("id") int id) { + return "one"; + } + +} diff --git a/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/OrdersSubResource.java b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/OrdersSubResource.java new file mode 100644 index 000000000..421b221f9 --- /dev/null +++ b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/OrdersSubResource.java @@ -0,0 +1,14 @@ +package com.nr.agent.security.instrumentation.javax.ws.rs.api.app; + +import javax.ws.rs.Path; + +public class OrdersSubResource { + + protected IdSubResource idType = new IdSubResource(); + + @Path("getStuff") + public Object getById() { + return idType; + } + +} diff --git a/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/TestMapping.java b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/TestMapping.java new file mode 100644 index 000000000..3f070a56a --- /dev/null +++ b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/app/TestMapping.java @@ -0,0 +1,25 @@ +package com.nr.agent.security.instrumentation.javax.ws.rs.api.app; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; + +@Path("users") + public class TestMapping { + @PUT + @Consumes("application/json") + public String putIt() { + return "Put it!"; + } + + + @Path("count") + @GET + @Consumes("application/json") + public String pathIt() { + return "path it!"; + } + } + + diff --git a/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/SubresourceTest.java b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/SubresourceTest.java new file mode 100644 index 000000000..874b7f0eb --- /dev/null +++ b/instrumentation-security/jersey/src/test/java/com/nr/agent/security/instrumentation/javax/ws/rs/api/test/SubresourceTest.java @@ -0,0 +1,63 @@ +package com.nr.agent.security.instrumentation.javax.ws.rs.api.test; + +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.nr.agent.security.instrumentation.javax.ws.rs.api.app.CustomerLocatorResource; +import com.nr.agent.security.instrumentation.javax.ws.rs.api.app.IdSubResource; +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import com.nr.agent.security.instrumentation.javax.ws.rs.api.app.OrdersSubResource; +import com.nr.agent.security.instrumentation.javax.ws.rs.api.app.TestMapping; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.ws.rs.core.Application; +import java.util.Iterator; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "com.nr.instrumentation.security.jersey", "org.glassfish.jersey.server.internal" }) +@Ignore +public class SubresourceTest extends JerseyTest { + @BeforeClass + public static void bringUp() { + System.setProperty("jersey.config.test.container.port", "0"); + } + + @Test + public void testAPIEndpoints() { + target("/customers/orders/getStuff/1").request().get(); + + Iterator mappings = URLMappingsHelper.getApplicationURLMappings().iterator(); + + Assert.assertTrue(mappings.hasNext()); + assertMapping("/customers/orders/*", "*", CustomerLocatorResource.class.getName(), mappings.next()); + + Assert.assertTrue(mappings.hasNext()); + assertMapping("/users/count", "GET", TestMapping.class.getName(), mappings.next()); + + Assert.assertTrue(mappings.hasNext()); + assertMapping("/users", "PUT", TestMapping.class.getName(), mappings.next()); + + Assert.assertTrue(mappings.hasNext()); + assertMapping("/users", "OPTIONS", TestMapping.class.getName(), mappings.next()); + + Assert.assertTrue(mappings.hasNext()); + assertMapping("/users/count", "OPTIONS", TestMapping.class.getName(), mappings.next()); + } + + private void assertMapping(String path, String method, String handler, ApplicationURLMapping actualMapping){ + Assert.assertEquals(path, actualMapping.getPath()); + Assert.assertEquals(method, actualMapping.getMethod()); + Assert.assertEquals(handler, actualMapping.getHandler()); + } + + @Override + protected Application configure() { + return new ResourceConfig(CustomerLocatorResource.class, IdSubResource.class, OrdersSubResource.class, TestMapping.class); + } +} diff --git a/instrumentation-security/jetty-11/src/main/java/com/newrelic/agent/security/instrumentation/jetty11/ContextHandler_Instrumentation.java b/instrumentation-security/jetty-11/src/main/java/com/newrelic/agent/security/instrumentation/jetty11/ContextHandler_Instrumentation.java index 734ad779c..6745182db 100644 --- a/instrumentation-security/jetty-11/src/main/java/com/newrelic/agent/security/instrumentation/jetty11/ContextHandler_Instrumentation.java +++ b/instrumentation-security/jetty-11/src/main/java/com/newrelic/agent/security/instrumentation/jetty11/ContextHandler_Instrumentation.java @@ -6,9 +6,11 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.ContextHandler; @Weave(type = MatchType.ExactClass, originalName = "org.eclipse.jetty.server.handler.ContextHandler") -public class ContextHandler_Instrumentation { +public abstract class ContextHandler_Instrumentation { + public abstract ContextHandler.Context getServletContext(); public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) { boolean isServletLockAcquired = acquireServletLockIfPossible(); @@ -27,6 +29,13 @@ public void doHandle(String target, Request baseRequest, HttpServletRequest requ HttpServletHelper.SERVICE_METHOD_NAME); } } + protected void doStart() throws Exception { + try { + Weaver.callOriginal(); + } finally { + HttpServletHelper.gatherURLMappings(getServletContext()); + } + } private boolean acquireServletLockIfPossible() { try { diff --git a/instrumentation-security/jetty-11/src/main/java/com/newrelic/agent/security/instrumentation/jetty11/HttpServletHelper.java b/instrumentation-security/jetty-11/src/main/java/com/newrelic/agent/security/instrumentation/jetty11/HttpServletHelper.java index c414129f9..00c729bec 100644 --- a/instrumentation-security/jetty-11/src/main/java/com/newrelic/agent/security/instrumentation/jetty11/HttpServletHelper.java +++ b/instrumentation-security/jetty-11/src/main/java/com/newrelic/agent/security/instrumentation/jetty11/HttpServletHelper.java @@ -1,20 +1,22 @@ package com.newrelic.agent.security.instrumentation.jetty11; import com.newrelic.api.agent.security.NewRelicSecurity; -import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.*; import com.newrelic.api.agent.security.schema.AgentMetaData; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; import com.newrelic.api.agent.security.schema.HttpRequest; 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.schema.policy.AgentPolicy; import com.newrelic.api.agent.security.utils.logging.LogLevel; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletRegistration; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.util.Arrays; +import java.util.Collection; import java.util.Enumeration; import java.util.Map; @@ -28,6 +30,8 @@ public class HttpServletHelper { public static final String NR_SEC_CUSTOM_ATTRIB_NAME = "SERVLET_LOCK-"; public static final String JETTY_11 = "JETTY-11"; + public static final String WILDCARD = "*"; + public static final String SEPARATOR = "/"; public static void processHttpRequestHeader(HttpServletRequest request, HttpRequest securityRequest) { Enumeration headerNames = request.getHeaderNames(); @@ -51,6 +55,9 @@ public static void processHttpRequestHeader(HttpServletRequest request, HttpRequ } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeader(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; Enumeration headerElements = request.getHeaders(headerKey); @@ -193,4 +200,36 @@ public static void postProcessSecurityHook(HttpServletRequest request, HttpServl NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, JETTY_11, e.getMessage()), e, HttpServletHelper.class.getName()); } } + public static void gatherURLMappings(ServletContext servletContext) { + try { + Map servletRegistrations = servletContext.getServletRegistrations(); + getJSPMappings(servletContext, SEPARATOR); + + for (ServletRegistration servletReg : servletRegistrations.values()) { + for (String mapping : servletReg.getMappings()) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, mapping, servletReg.getClassName())); + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, JETTY_11, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } + + private static void getJSPMappings(ServletContext servletContext, String dir) { + try { + if(dir.endsWith(SEPARATOR)){ + Collection resourcePaths = servletContext.getResourcePaths(dir); + for (String path : resourcePaths) { + if(path.endsWith(SEPARATOR)) { + getJSPMappings(servletContext, path); + } + else if(path.endsWith(".jsp") || path.endsWith(".jspx") || path.endsWith(".JSP") || path.endsWith(".JSPX")) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, path)); + } + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, JETTY_11, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } } diff --git a/instrumentation-security/jetty-11/src/test/java/com/nr/agent/security/instrumentation/jetty11/ServerTest.java b/instrumentation-security/jetty-11/src/test/java/com/nr/agent/security/instrumentation/jetty11/ServerTest.java index 71a73dd60..f9087b94f 100644 --- a/instrumentation-security/jetty-11/src/test/java/com/nr/agent/security/instrumentation/jetty11/ServerTest.java +++ b/instrumentation-security/jetty-11/src/test/java/com/nr/agent/security/instrumentation/jetty11/ServerTest.java @@ -5,8 +5,10 @@ import com.newrelic.agent.security.introspec.SecurityIntrospector; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.AgentMetaData; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; import com.newrelic.agent.security.instrumentation.jetty11.HttpServletHelper; @@ -14,9 +16,11 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.junit.After; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,8 +33,10 @@ import java.net.HttpURLConnection; import java.net.ServerSocket; import java.net.URL; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -42,6 +48,11 @@ public class ServerTest { private Server server; + private static final Map actualMappings = new HashMap<>(); + @BeforeClass + public static void addMappings() { + actualMappings.put("/servlet/*", MyServlet.class.getName()); + } @After public void teardown() throws Exception { if (server.isRunning()) { @@ -184,21 +195,30 @@ public void testHandle3() throws Exception { Assert.assertEquals("Wrong Content-type detected", "text/plain", operation.getRequest().getContentType()); } + @Test + public void testAPIEndpoint () throws Exception { + start(); + + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertEquals(1, mappings.size()); + for (ApplicationURLMapping mapping : mappings) { + Assert.assertNotNull(mapping); + + Assert.assertNotNull(mapping.getHandler()); + Assert.assertNotNull(mapping.getPath()); + Assert.assertNotNull(mapping.getMethod()); + + Assert.assertEquals(actualMappings.get(mapping.getPath()), mapping.getHandler()); + Assert.assertEquals("*", mapping.getMethod()); + } + } private void start() throws Exception { server = new Server(PORT); - ServletHolder holder = new ServletHolder( - new HttpServlet() { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) { - resp.setContentType("text/plain;charset=utf-8"); - resp.setStatus(HttpServletResponse.SC_OK); - } - } - ); + ServletHolder holder = new ServletHolder(new MyServlet()); ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS); handler.setContextPath("/"); server.setHandler(handler); - handler.addServlet(holder, "/*"); + handler.addServlet(holder, "/servlet/*"); server.start(); } @@ -252,3 +272,10 @@ private static int getRandomPort() { } } } +class MyServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + resp.setContentType("text/plain;charset=utf-8"); + resp.setStatus(HttpServletResponse.SC_OK); + } +} \ No newline at end of file diff --git a/instrumentation-security/jetty-12/src/main/java/com/newrelic/agent/security/instrumentation/jetty12/server/HttpServletHelper.java b/instrumentation-security/jetty-12/src/main/java/com/newrelic/agent/security/instrumentation/jetty12/server/HttpServletHelper.java index a194d4807..930b540a3 100644 --- a/instrumentation-security/jetty-12/src/main/java/com/newrelic/agent/security/instrumentation/jetty12/server/HttpServletHelper.java +++ b/instrumentation-security/jetty-12/src/main/java/com/newrelic/agent/security/instrumentation/jetty12/server/HttpServletHelper.java @@ -2,6 +2,7 @@ import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; @@ -57,6 +58,9 @@ public static void processHttpRequestHeader(Request request, HttpRequest securit } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeaders().get(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; diff --git a/instrumentation-security/jetty-9/src/main/java/com/newrelic/agent/security/instrumentation/jetty9/ContextHandler_Instrumentation.java b/instrumentation-security/jetty-9/src/main/java/com/newrelic/agent/security/instrumentation/jetty9/ContextHandler_Instrumentation.java index 10e5b07ac..e8e17d5a6 100644 --- a/instrumentation-security/jetty-9/src/main/java/com/newrelic/agent/security/instrumentation/jetty9/ContextHandler_Instrumentation.java +++ b/instrumentation-security/jetty-9/src/main/java/com/newrelic/agent/security/instrumentation/jetty9/ContextHandler_Instrumentation.java @@ -4,12 +4,15 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.ContextHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Weave(type = MatchType.ExactClass, originalName = "org.eclipse.jetty.server.handler.ContextHandler") -public class ContextHandler_Instrumentation { +public abstract class ContextHandler_Instrumentation { + + public abstract ContextHandler.Context getServletContext(); public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) { boolean isServletLockAcquired = acquireServletLockIfPossible(); @@ -29,6 +32,14 @@ public void doHandle(String target, Request baseRequest, HttpServletRequest requ } } + protected void doStart() throws Exception { + try { + Weaver.callOriginal(); + } finally { + HttpServletHelper.gatherURLMappings(getServletContext()); + } + } + private boolean acquireServletLockIfPossible() { try { return HttpServletHelper.acquireServletLockIfPossible(); diff --git a/instrumentation-security/jetty-9/src/main/java/com/newrelic/agent/security/instrumentation/jetty9/HttpServletHelper.java b/instrumentation-security/jetty-9/src/main/java/com/newrelic/agent/security/instrumentation/jetty9/HttpServletHelper.java index 03ac12d08..dd3d81697 100644 --- a/instrumentation-security/jetty-9/src/main/java/com/newrelic/agent/security/instrumentation/jetty9/HttpServletHelper.java +++ b/instrumentation-security/jetty-9/src/main/java/com/newrelic/agent/security/instrumentation/jetty9/HttpServletHelper.java @@ -1,10 +1,9 @@ package com.newrelic.agent.security.instrumentation.jetty9; import com.newrelic.api.agent.security.NewRelicSecurity; -import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; -import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.*; import com.newrelic.api.agent.security.schema.AgentMetaData; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; import com.newrelic.api.agent.security.schema.HttpRequest; import com.newrelic.api.agent.security.schema.SecurityMetaData; import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; @@ -12,9 +11,12 @@ import com.newrelic.api.agent.security.schema.policy.AgentPolicy; import com.newrelic.api.agent.security.utils.logging.LogLevel; +import javax.servlet.ServletContext; +import javax.servlet.ServletRegistration; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; +import java.util.Collection; import java.util.Enumeration; import java.util.Map; @@ -27,6 +29,9 @@ public class HttpServletHelper { public static final String SERVICE_ASYNC_METHOD_NAME = "handleAsync"; public static final String NR_SEC_CUSTOM_ATTRIB_NAME = "SERVLET_LOCK-"; public static final String JETTY_9 = "JETTY-9"; + private static final String SEPARATOR = "/"; + private static final String WILDCARD = "*"; + public static void processHttpRequestHeader(HttpServletRequest request, HttpRequest securityRequest) { Enumeration headerNames = request.getHeaderNames(); @@ -50,6 +55,9 @@ public static void processHttpRequestHeader(HttpServletRequest request, HttpRequ } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeader(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; @@ -194,4 +202,36 @@ public static void postProcessSecurityHook(HttpServletRequest request, HttpServl NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, JETTY_9, e.getMessage()), e, HttpServletHelper.class.getName()); } } + public static void gatherURLMappings(ServletContext servletContext) { + try { + Map servletRegistrations = servletContext.getServletRegistrations(); + getJSPMappings(servletContext, SEPARATOR); + + for (ServletRegistration servletReg : servletRegistrations.values()) { + for (String mapping : servletReg.getMappings()) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, mapping, servletReg.getClassName())); + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, JETTY_9, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } + + private static void getJSPMappings(ServletContext servletContext, String dir) { + try { + if(dir.endsWith(SEPARATOR)){ + Collection resourcePaths = servletContext.getResourcePaths(dir); + for (String path : resourcePaths) { + if(path.endsWith(SEPARATOR)) { + getJSPMappings(servletContext, path); + } + else if(path.endsWith(".jsp") || path.endsWith(".jspx") || path.endsWith(".JSP") || path.endsWith(".JSPX")) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, path)); + } + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, JETTY_9, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } } diff --git a/instrumentation-security/jetty-9/src/test/java/com/nr/agent/security/instrumentation/jetty9/ServerTest.java b/instrumentation-security/jetty-9/src/test/java/com/nr/agent/security/instrumentation/jetty9/ServerTest.java index 4ff76d7cc..e7f236937 100644 --- a/instrumentation-security/jetty-9/src/test/java/com/nr/agent/security/instrumentation/jetty9/ServerTest.java +++ b/instrumentation-security/jetty-9/src/test/java/com/nr/agent/security/instrumentation/jetty9/ServerTest.java @@ -5,8 +5,10 @@ import com.newrelic.agent.security.introspec.SecurityIntrospector; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.AgentMetaData; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; import com.newrelic.agent.security.instrumentation.jetty9.HttpServletHelper; @@ -31,6 +33,7 @@ import java.net.URL; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -184,21 +187,30 @@ public void testHandle3() throws Exception { Assert.assertEquals("Wrong Content-type detected", "text/plain", operation.getRequest().getContentType()); } + @Test + public void testAPIEndpoint () throws Exception { + start(); + + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertEquals(1, mappings.size()); + + for (ApplicationURLMapping mapping : mappings) { + Assert.assertNotNull(mapping); + Assert.assertNotNull(mapping.getHandler()); + Assert.assertNotNull(mapping.getPath()); + Assert.assertNotNull(mapping.getMethod()); + Assert.assertEquals(MyServlet.class.getName(), mapping.getHandler()); + Assert.assertEquals("/servlet/*", mapping.getPath()); + Assert.assertEquals("*", mapping.getMethod()); + } + } private void start() throws Exception { server = new Server(PORT); - ServletHolder holder = new ServletHolder( - new HttpServlet() { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) { - resp.setContentType("text/plain;charset=utf-8"); - resp.setStatus(HttpServletResponse.SC_OK); - } - } - ); + ServletHolder holder = new ServletHolder(new MyServlet()); ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS); handler.setContextPath("/"); server.setHandler(handler); - handler.addServlet(holder, "/*"); + handler.addServlet(holder, "/servlet/*"); server.start(); } @@ -252,3 +264,11 @@ private static int getRandomPort() { } } } + +class MyServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + resp.setContentType("text/plain;charset=utf-8"); + resp.setStatus(HttpServletResponse.SC_OK); + } +} \ No newline at end of file diff --git a/instrumentation-security/mongodb-3.0/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java b/instrumentation-security/mongodb-3.0/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java index 321b7af27..eb6a3fe42 100644 --- a/instrumentation-security/mongodb-3.0/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java +++ b/instrumentation-security/mongodb-3.0/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java @@ -76,6 +76,7 @@ public static AbstractOperation recordMongoOperation(List command, } } operation = new NoSQLOperation(operations, typeOfOperation, klassName, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(4); NewRelicSecurity.getAgent().registerOperation(operation); } } catch (Throwable e) { @@ -137,6 +138,7 @@ public static AbstractOperation recordWriteRequest(List } } operation = new NoSQLOperation(operations, OP_WRITE, klassName, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(4); NewRelicSecurity.getAgent().registerOperation(operation); } } catch (Throwable e) { diff --git a/instrumentation-security/mongodb-3.6/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java b/instrumentation-security/mongodb-3.6/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java index 3dfece7fd..24894c897 100644 --- a/instrumentation-security/mongodb-3.6/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java +++ b/instrumentation-security/mongodb-3.6/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java @@ -88,6 +88,7 @@ public static AbstractOperation recordMongoOperation(List command, } } operation = new NoSQLOperation(operations, typeOfOperation, klassName, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(4); NewRelicSecurity.getAgent().registerOperation(operation); } } catch (Throwable e) { @@ -149,6 +150,7 @@ public static AbstractOperation recordWriteRequest(List } } operation = new NoSQLOperation(operations, OP_WRITE, klassName, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(4); NewRelicSecurity.getAgent().registerOperation(operation); } } catch (Throwable e) { diff --git a/instrumentation-security/mongodb-3.7/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java b/instrumentation-security/mongodb-3.7/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java index 83f0f7786..672fb2804 100644 --- a/instrumentation-security/mongodb-3.7/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java +++ b/instrumentation-security/mongodb-3.7/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java @@ -86,6 +86,7 @@ public static AbstractOperation recordMongoOperation(List command, } } operation = new NoSQLOperation(operations, typeOfOperation, klassName, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(4); NewRelicSecurity.getAgent().registerOperation(operation); } } catch (Throwable e) { @@ -147,6 +148,7 @@ public static AbstractOperation recordWriteRequest(List } } operation = new NoSQLOperation(operations, OP_WRITE, klassName, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(4); NewRelicSecurity.getAgent().registerOperation(operation); } } catch (Throwable e) { diff --git a/instrumentation-security/mongodb-3.8/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java b/instrumentation-security/mongodb-3.8/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java index 1f3918270..255c9612f 100644 --- a/instrumentation-security/mongodb-3.8/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java +++ b/instrumentation-security/mongodb-3.8/src/main/java/com/newrelic/agent/security/instrumentation/mongo/MongoUtil.java @@ -74,6 +74,7 @@ public static AbstractOperation recordMongoOperation(List command, } } operation = new NoSQLOperation(operations, typeOfOperation, klassName, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(4); NewRelicSecurity.getAgent().registerOperation(operation); } } catch (Throwable e) { @@ -135,6 +136,7 @@ public static AbstractOperation recordWriteRequest(List } } operation = new NoSQLOperation(operations, OP_WRITE, klassName, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(4); NewRelicSecurity.getAgent().registerOperation(operation); } } catch (Throwable e) { 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 3754b8e9e..3a79f35d7 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 @@ -2,12 +2,20 @@ import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +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.policy.AgentPolicy; +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.processor.InvokerMessageProcessor; +import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.mule.module.http.api.HttpHeaders.Names.X_FORWARDED_FOR; @@ -20,6 +28,7 @@ public class MuleHelper { public static final String HANDLE_REQUEST_METHOD = "handleRequest"; private static final String EMPTY = ""; public static final String LIBRARY_NAME = "MULE-SERVER"; + private static final Map handlerMap = new HashMap<>(); public static void processHttpRequestHeader(HttpRequest httpRequest, com.newrelic.api.agent.security.schema.HttpRequest securityRequest @@ -49,6 +58,9 @@ public static void processHttpRequestHeader(HttpRequest httpRequest, .getAgent() .getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, httpRequest.getHeaderValue(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; for (String headerValue : httpRequest.getHeaderValues(headerKey)) { @@ -88,4 +100,24 @@ public static String getNrSecCustomAttribName(int hashcode) { return MULE_LOCK_CUSTOM_ATTRIB_NAME + Thread.currentThread().getId() + hashcode; } + public static void gatherURLMappings(HttpListener messageSource, List messageProcessors) { + try { + String path = messageSource.getPath(); + String handlerClass = null; + for (MessageProcessor processor: messageProcessors){ + if (processor instanceof InvokerMessageProcessor) { + handlerClass = getHandlerMap().remove(processor.hashCode()); + } + } + for (String method : messageSource.getAllowedMethods()){ + if (handlerClass != null){ + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(method, path, handlerClass)); + } + } + }catch (Exception ignored){} + } + + public static Map getHandlerMap() { + return handlerMap; + } } diff --git a/instrumentation-security/mule-3.6/src/main/java/org/mule/construct/AbstractPipeline_Instrumentation.java b/instrumentation-security/mule-3.6/src/main/java/org/mule/construct/AbstractPipeline_Instrumentation.java new file mode 100644 index 000000000..34415ac26 --- /dev/null +++ b/instrumentation-security/mule-3.6/src/main/java/org/mule/construct/AbstractPipeline_Instrumentation.java @@ -0,0 +1,27 @@ +package org.mule.construct; + +import com.newrelic.agent.security.instrumentation.mule36.MuleHelper; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import org.mule.api.processor.MessageProcessor; +import org.mule.api.source.MessageSource; +import org.mule.module.http.api.listener.HttpListener; + +import java.util.List; + +@Weave(type = MatchType.BaseClass, originalName = "org.mule.construct.AbstractPipeline") +public abstract class AbstractPipeline_Instrumentation { + + public abstract List getMessageProcessors(); + public abstract MessageSource getMessageSource(); + protected void doInitialise(){ + try { + Weaver.callOriginal(); + } finally { + if (getMessageSource() instanceof HttpListener){ + MuleHelper.gatherURLMappings((HttpListener) getMessageSource(), getMessageProcessors()); + } + } + } +} diff --git a/instrumentation-security/mule-3.6/src/main/java/org/mule/processor/InvokerMessageProcessor_Instrumentation.java b/instrumentation-security/mule-3.6/src/main/java/org/mule/processor/InvokerMessageProcessor_Instrumentation.java new file mode 100644 index 000000000..3e2871407 --- /dev/null +++ b/instrumentation-security/mule-3.6/src/main/java/org/mule/processor/InvokerMessageProcessor_Instrumentation.java @@ -0,0 +1,20 @@ +package org.mule.processor; + +import com.newrelic.agent.security.instrumentation.mule36.MuleHelper; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import org.mule.api.lifecycle.InitialisationException; + +@Weave(originalName = "org.mule.processor.InvokerMessageProcessor", type = MatchType.ExactClass) +public class InvokerMessageProcessor_Instrumentation { + protected Object object = Weaver.callOriginal(); + + public void initialise() throws InitialisationException { + try { + Weaver.callOriginal(); + } finally { + MuleHelper.getHandlerMap().put(hashCode(), object.getClass().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 f05979db0..afc9c97ba 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 @@ -2,12 +2,20 @@ import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +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.policy.AgentPolicy; +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.processor.InvokerMessageProcessor; +import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.mule.module.http.api.HttpHeaders.Names.X_FORWARDED_FOR; @@ -19,6 +27,7 @@ public class MuleHelper { public static final String HANDLE_REQUEST_METHOD = "handleRequest"; private static final String EMPTY = ""; public static final String LIBRARY_NAME = "MULE-SERVER"; + private static final Map handlerMap = new HashMap<>(); public static void processHttpRequestHeader(HttpRequest httpRequest, com.newrelic.api.agent.security.schema.HttpRequest securityRequest) { for (String headerName : httpRequest.getHeaderNames()) { @@ -43,6 +52,9 @@ public static void processHttpRequestHeader(HttpRequest httpRequest, com.newreli } else if (GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, httpRequest.getHeaderValue(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; for (String headerValue : httpRequest.getHeaderValues(headerKey)) { @@ -82,4 +94,24 @@ public static String getNrSecCustomAttribName(int hashcode) { return MULE_LOCK_CUSTOM_ATTRIB_NAME + Thread.currentThread().getId() + hashcode; } + public static void gatherURLMappings(HttpListener messageSource, List messageProcessors) { + try { + String path = messageSource.getPath(); + String handlerClass = null; + for (MessageProcessor processor: messageProcessors){ + if (processor instanceof InvokerMessageProcessor) { + handlerClass = getHandlerMap().remove(processor.hashCode()); + } + } + for (String method : messageSource.getAllowedMethods()){ + if (handlerClass != null){ + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(method, path, handlerClass)); + } + } + } catch (Exception ignored){} + } + + public static Map getHandlerMap() { + return handlerMap; + } } diff --git a/instrumentation-security/mule-3.7/src/main/java/org/mule/construct/AbstractPipeline_Instrumentation.java b/instrumentation-security/mule-3.7/src/main/java/org/mule/construct/AbstractPipeline_Instrumentation.java new file mode 100644 index 000000000..4952d39c0 --- /dev/null +++ b/instrumentation-security/mule-3.7/src/main/java/org/mule/construct/AbstractPipeline_Instrumentation.java @@ -0,0 +1,27 @@ +package org.mule.construct; + +import com.newrelic.agent.security.instrumentation.mule37.MuleHelper; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import org.mule.api.processor.MessageProcessor; +import org.mule.api.source.MessageSource; +import org.mule.module.http.api.listener.HttpListener; + +import java.util.List; + +@Weave(type = MatchType.BaseClass, originalName = "org.mule.construct.AbstractPipeline") +public abstract class AbstractPipeline_Instrumentation { + + public abstract List getMessageProcessors(); + public abstract MessageSource getMessageSource(); + protected void doInitialise(){ + try { + Weaver.callOriginal(); + } finally { + if (getMessageSource() instanceof HttpListener){ + MuleHelper.gatherURLMappings((HttpListener) getMessageSource(), getMessageProcessors()); + } + } + } +} diff --git a/instrumentation-security/mule-3.7/src/main/java/org/mule/processor/InvokerMessageProcessor_Instrumentation.java b/instrumentation-security/mule-3.7/src/main/java/org/mule/processor/InvokerMessageProcessor_Instrumentation.java new file mode 100644 index 000000000..94c152174 --- /dev/null +++ b/instrumentation-security/mule-3.7/src/main/java/org/mule/processor/InvokerMessageProcessor_Instrumentation.java @@ -0,0 +1,20 @@ +package org.mule.processor; + +import com.newrelic.agent.security.instrumentation.mule37.MuleHelper; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import org.mule.api.lifecycle.InitialisationException; + +@Weave(originalName = "org.mule.processor.InvokerMessageProcessor", type = MatchType.ExactClass) +public class InvokerMessageProcessor_Instrumentation { + protected Object object = Weaver.callOriginal(); + + public void initialise() throws InitialisationException { + try { + Weaver.callOriginal(); + } finally { + MuleHelper.getHandlerMap().put(hashCode(), object.getClass().getName()); + } + } +} diff --git a/instrumentation-security/netty-4.0.0/build.gradle b/instrumentation-security/netty-4.0.0/build.gradle index 2bfc0e0d1..a1ee29085 100644 --- a/instrumentation-security/netty-4.0.0/build.gradle +++ b/instrumentation-security/netty-4.0.0/build.gradle @@ -12,7 +12,7 @@ jar { } verifyInstrumentation { - passesOnly 'io.netty:netty-all:[4.0.0.Final,5.0.0.Alpha1)' + passesOnly 'io.netty:netty-all:[4.0.0.Final,4.0.8.Final)' } site { diff --git a/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/bootstrap/AbstractBootstrap_Instrumentation.java b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/bootstrap/AbstractBootstrap_Instrumentation.java new file mode 100644 index 000000000..fceb3c7e9 --- /dev/null +++ b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/bootstrap/AbstractBootstrap_Instrumentation.java @@ -0,0 +1,31 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package security.io.netty400.bootstrap; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelFuture; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +@Weave(type = MatchType.ExactClass, originalName = "io.netty.bootstrap.AbstractBootstrap") +abstract class AbstractBootstrap_Instrumentation { + + @SuppressWarnings("unused") + private ChannelFuture doBind(final SocketAddress localAddress) { + if (localAddress instanceof InetSocketAddress) { + int port = ((InetSocketAddress) localAddress).getPort(); + NewRelicSecurity.getAgent().setApplicationConnectionConfig(port, "http"); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/ChannelInboundHandler_Instrumentation.java b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/ChannelInboundHandler_Instrumentation.java index 4fa642234..10c000dc3 100644 --- a/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/ChannelInboundHandler_Instrumentation.java +++ b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/ChannelInboundHandler_Instrumentation.java @@ -13,13 +13,18 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpRequest; import security.io.netty400.utils.NettyUtils; @Weave(type = MatchType.Interface, originalName = "io.netty.channel.ChannelInboundHandler") public abstract class ChannelInboundHandler_Instrumentation { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - boolean isLockAcquired = NettyUtils.acquireNettyLockIfPossible(); + boolean isLockAcquired = false; + if (msg instanceof HttpRequest || msg instanceof HttpContent){ + isLockAcquired = NettyUtils.acquireNettyLockIfPossible(NettyUtils.NR_SEC_NETTY_OPERATIONAL_LOCK); + } if (isLockAcquired) { NettyUtils.processSecurityRequest(ctx, msg, getClass().getName()); if (!StringUtils.startsWith(getClass().getName(), NettyUtils.IO_NETTY)) { @@ -30,7 +35,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception Weaver.callOriginal(); } finally { if (isLockAcquired) { - NettyUtils.releaseNettyLock(); + NettyUtils.releaseNettyLock(NettyUtils.NR_SEC_NETTY_OPERATIONAL_LOCK); } } } diff --git a/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/ChannelOutboundHandler_Instrumentation.java b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/ChannelOutboundHandler_Instrumentation.java index 5990decd6..6a93460f1 100644 --- a/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/ChannelOutboundHandler_Instrumentation.java +++ b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/ChannelOutboundHandler_Instrumentation.java @@ -12,13 +12,19 @@ import com.newrelic.api.agent.weaver.Weaver; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpRequest; import security.io.netty400.utils.NettyUtils; @Weave(type = MatchType.Interface, originalName = "io.netty.channel.ChannelOutboundHandler") public abstract class ChannelOutboundHandler_Instrumentation { public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - boolean isLockAcquired = NettyUtils.acquireNettyLockIfPossible(); + boolean isLockAcquired = false; + if (msg instanceof FullHttpResponse){ + isLockAcquired = NettyUtils.acquireNettyLockIfPossible(NettyUtils.NR_SEC_NETTY_OPERATIONAL_LOCK_OUTBOUND); + } if (isLockAcquired) { NettyUtils.processSecurityResponse(ctx, msg); NettyUtils.sendRXSSEvent(ctx, msg, getClass().getName(), NettyUtils.WRITE_METHOD_NAME); @@ -27,7 +33,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) Weaver.callOriginal(); } finally { if (isLockAcquired) { - NettyUtils.releaseNettyLock(); + NettyUtils.releaseNettyLock(NettyUtils.NR_SEC_NETTY_OPERATIONAL_LOCK_OUTBOUND); } } } diff --git a/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/SimpleChannelInboundHandler_Instrumentation.java b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/SimpleChannelInboundHandler_Instrumentation.java new file mode 100644 index 000000000..b82cf5af4 --- /dev/null +++ b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/channel/SimpleChannelInboundHandler_Instrumentation.java @@ -0,0 +1,23 @@ +package security.io.netty400.channel; + +import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelHandlerContext; +import security.io.netty400.utils.NettyUtils; + +@Weave(type = MatchType.BaseClass, originalName = "io.netty.channel.SimpleChannelInboundHandler") +public class SimpleChannelInboundHandler_Instrumentation { + + protected void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception { + try { + if (!StringUtils.startsWith(getClass().getName(), NettyUtils.IO_NETTY)) { + ServletHelper.registerUserLevelCode(NettyUtils.IO_NETTY); + } + } catch (Exception e){ + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/utils/NettyUtils.java b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/utils/NettyUtils.java index 40428c49c..3a34eac2e 100644 --- a/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/utils/NettyUtils.java +++ b/instrumentation-security/netty-4.0.0/src/main/java/security/io/netty400/utils/NettyUtils.java @@ -1,9 +1,8 @@ package security.io.netty400.utils; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Transaction; import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; @@ -12,9 +11,11 @@ import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; import com.newrelic.api.agent.security.schema.policy.AgentPolicy; +import com.newrelic.api.agent.security.utils.logging.LogLevel; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; @@ -25,23 +26,24 @@ import java.util.Set; public class NettyUtils { + public static final String NETTY_4_0_0 = "NETTY-4.0.0"; public static String NR_SEC_CUSTOM_ATTRIB_NAME = "NETTY-4.8-REQ-BODY-TRACKER"; - public static String NR_SEC_NETTY_OPERATIONAL_LOCK = "NR_SEC_NETTY_OPERATIONAL_LOCK"; + public static String NR_SEC_NETTY_OPERATIONAL_LOCK = "NR_SEC_NETTY_OPERATIONAL_LOCK_INBOUND"; + public static String NR_SEC_NETTY_OPERATIONAL_LOCK_OUTBOUND = "NR_SEC_NETTY_OPERATIONAL_LOCK_OUTBOUND"; private static final String X_FORWARDED_FOR = "x-forwarded-for"; private static final String EMPTY = ""; public static final String WRITE_METHOD_NAME = "write"; public static final String IO_NETTY = "io.netty."; + private static final String ERROR_GETTING_SERVER_PORT = "Instrumentation library: %s , error while getting server port %s"; + private static final String ERROR_PARSING_HTTP_RESPONSE_DATA = "Instrumentation library: %s , error while parsing HTTP response data : %s"; public static void processSecurityRequest(ChannelHandlerContext ctx, Object msg, String className) { try { - Transaction tx = NewRelic.getAgent().getTransaction(); - Object secMetaObj = tx.getSecurityMetaData(); + if (!NewRelicSecurity.isHookProcessingActive()) { + return; + } if (msg instanceof HttpRequest) { - if (!(secMetaObj instanceof SecurityMetaData) || - NewRelicSecurity.getAgent().getSecurityMetaData() == null) { - return; - } SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); com.newrelic.api.agent.security.schema.HttpRequest securityRequest = securityMetaData.getRequest(); @@ -56,16 +58,15 @@ public static void processSecurityRequest(ChannelHandlerContext ctx, Object msg, processHttpRequestHeader((HttpRequest)msg, securityRequest); securityMetaData.setTracingHeaderValue(getTraceHeader(securityRequest.getHeaders())); - securityRequest.setProtocol(((HttpRequest) msg).getProtocolVersion().protocolName()); + securityRequest.setProtocol(((HttpRequest) msg).getProtocolVersion().protocolName().toLowerCase()); securityRequest.setContentType(securityRequest.getHeaders().get("content-type")); - StackTraceElement[] stack = Thread.currentThread().getStackTrace(); - securityMetaData.getMetaData().setServiceTrace(Arrays.copyOfRange(stack, 2, stack.length)); - securityRequest.setRequestParsed(true); - } else if (msg instanceof HttpContent) { - if (!(secMetaObj instanceof SecurityMetaData) || - NewRelicSecurity.getAgent().getSecurityMetaData() == null) { - return; + if (!securityMetaData.getMetaData().isUserLevelServiceMethodEncountered(IO_NETTY)){ + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + securityMetaData.getMetaData().setServiceTrace(Arrays.copyOfRange(stack, 2, stack.length)); } + securityRequest.setRequestParsed(true); + } + if (msg instanceof HttpContent) { Integer reqBodyTrackerContextId = NewRelicSecurity.getAgent().getSecurityMetaData() .getCustomAttribute(NR_SEC_CUSTOM_ATTRIB_NAME, Integer.class); if (reqBodyTrackerContextId == null) { @@ -80,7 +81,7 @@ public static void processSecurityRequest(ChannelHandlerContext ctx, Object msg, } } } catch (Throwable ignored) { - ignored.printStackTrace(); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_PARSING_HTTP_REQUEST_DATA, NETTY_4_0_0, ignored.getMessage()), ignored, NettyUtils.class.getName()); } } @@ -91,7 +92,9 @@ private static void setServerPortDetails(com.newrelic.api.agent.security.schema. return; } securityRequest.setServerPort(Integer.parseInt(port)); - } catch (Throwable throwable) {} + } catch (Throwable throwable) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(ERROR_GETTING_SERVER_PORT, NETTY_4_0_0, throwable.getMessage()), throwable, NettyUtils.class.getName()); + } } private static void setClientAddressDetails(SecurityMetaData securityMetaData, String address) { @@ -128,6 +131,9 @@ public static void processHttpRequestHeader(HttpRequest request, com.newrelic.ap } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.headers().get(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; @@ -167,22 +173,16 @@ public static String getTraceHeader(Map headers) { public static void processSecurityResponse(ChannelHandlerContext ctx, Object msg) { try { - Transaction tx = NewRelic.getAgent().getTransaction(); - Object secMetaObj = tx.getSecurityMetaData(); - if (msg instanceof FullHttpResponse) { - if (!(secMetaObj instanceof SecurityMetaData) || - NewRelicSecurity.getAgent().getSecurityMetaData() == null) { - return; - } + if (NewRelicSecurity.isHookProcessingActive() && msg instanceof FullHttpResponse) { SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); com.newrelic.api.agent.security.schema.HttpResponse securityResponse = securityMetaData.getResponse(); processResponseHeaders((HttpResponse) msg, securityResponse); - securityResponse.setResponseContentType(((FullHttpResponse) msg).headers().get("content-type")); + securityResponse.setResponseContentType(((FullHttpResponse) msg).headers().get(HttpHeaders.Names.CONTENT_TYPE)); securityResponse.getResponseBody().append(((FullHttpResponse) msg).content().toString(StandardCharsets.UTF_8)); } } catch (Throwable e) { - e.printStackTrace(); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(ERROR_PARSING_HTTP_RESPONSE_DATA, NETTY_4_0_0, e.getMessage()), e, NettyUtils.class.getName()); } } @@ -203,9 +203,11 @@ public static void sendRXSSEvent(ChannelHandlerContext ctx, Object msg, String c 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, NETTY_4_0_0, e.getMessage()), e, NettyUtils.class.getName()); throw e; } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, NETTY_4_0_0, e.getMessage()), e, NettyUtils.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, NETTY_4_0_0, e.getMessage()), e, NettyUtils.class.getName()); } } @@ -217,34 +219,30 @@ private static void processResponseHeaders(HttpResponse response, com.newrelic.a } } - public static boolean isNettyLockAcquired() { + public static boolean isNettyLockAcquired(String operationLock) { try { return NewRelicSecurity.isHookProcessingActive() && - Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(getNrSecOperationalLockName(), Boolean.class)); + Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(operationLock + Thread.currentThread().getId(), Boolean.class)); } catch (Throwable ignored) {} return false; } - public static boolean acquireNettyLockIfPossible() { + public static boolean acquireNettyLockIfPossible(String operationLock) { try { if (NewRelicSecurity.isHookProcessingActive() && - !isNettyLockAcquired()) { - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(getNrSecOperationalLockName(), true); + !isNettyLockAcquired(operationLock)) { + NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(operationLock + Thread.currentThread().getId(), true); return true; } } catch (Throwable ignored){} return false; } - public static void releaseNettyLock() { + public static void releaseNettyLock(String operationLock) { try { if(NewRelicSecurity.isHookProcessingActive()) { - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(getNrSecOperationalLockName(), null); + NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(operationLock + Thread.currentThread().getId(), null); } } catch (Throwable ignored){} } - - private static String getNrSecOperationalLockName() { - return NR_SEC_NETTY_OPERATIONAL_LOCK + Thread.currentThread().getId(); - } } diff --git a/instrumentation-security/netty-4.0.0/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyServer.java b/instrumentation-security/netty-4.0.0/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyServer.java new file mode 100644 index 000000000..ea7a623be --- /dev/null +++ b/instrumentation-security/netty-4.0.0/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyServer.java @@ -0,0 +1,82 @@ +package com.nr.agent.security.instrumentation.netty400; + +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.util.CharsetUtil; +import org.junit.rules.ExternalResource; + +import java.net.MalformedURLException; +import java.net.URL; + +public class NettyServer extends ExternalResource { + private Channel channel; + private int PORT; + + @Override + protected void before() throws Throwable { + PORT = SecurityInstrumentationTestRunner.getIntrospector().getRandomPort(); + startServer(); + } + + @Override + protected void after() { + stopServer(); + } + private void startServer() throws InterruptedException { + ServerBootstrap b = new ServerBootstrap(); + b.group(new NioEventLoopGroup(),new NioEventLoopGroup()) + .channel(NioServerSocketChannel.class) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(new HttpServerCodec()); + pipeline.addLast(new HttpObjectAggregator(65536)); + pipeline.addLast(new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest o) throws Exception { + o.content(); + FullHttpResponse response = new DefaultFullHttpResponse( + HttpVersion.HTTP_1_0, + HttpResponseStatus.OK, + Unpooled.copiedBuffer("write data", CharsetUtil.UTF_8)); + HttpHeaders.addHeader(response, HttpHeaders.Names.CONTENT_TYPE, "text/html"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + }); + } + }); + + channel = b.bind(PORT).sync().channel(); + System.out.println("checking..."); + } + + private void stopServer(){ + if (channel.isActive() && channel.isOpen()){ + channel.close(); + } + } + public URL getEndPoint() throws MalformedURLException { + return new URL("http://localhost:" + PORT + "/"); + } +} diff --git a/instrumentation-security/netty-4.0.0/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyTest.java b/instrumentation-security/netty-4.0.0/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyTest.java index 4ceabeb60..23a4f9ed8 100644 --- a/instrumentation-security/netty-4.0.0/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyTest.java +++ b/instrumentation-security/netty-4.0.0/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyTest.java @@ -1,12 +1,9 @@ package com.nr.agent.security.instrumentation.netty400; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import com.newrelic.agent.security.introspec.InstrumentationTestConfig; import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; import com.newrelic.agent.security.introspec.SecurityIntrospector; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.HttpRequest; import com.newrelic.api.agent.security.schema.HttpResponse; @@ -27,84 +24,110 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import org.junit.Assert; +import org.junit.ClassRule; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import security.io.netty400.utils.NettyUtils; +import java.io.IOException; +import java.net.HttpURLConnection; import java.util.List; @RunWith(SecurityInstrumentationTestRunner.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @InstrumentationTestConfig(includePrefixes = {"security.io.netty400"}) public class NettyTest { + + @ClassRule + public static NettyServer server = new NettyServer(); + + private final String header = "text/html"; + @Test + public void testChannelRXSS() throws IOException { + connect(); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertFalse("No operations detected", operations.isEmpty()); + + RXSSOperation operation = (RXSSOperation) operations.get(0); + Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); + Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); + + HttpResponse response = introspector.getSecurityMetaData().getResponse(); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid response body", "write data", response.getResponseBody().toString()); + } @Test - public void testChannelRead() throws JsonProcessingException { + public void testChannelRead() { channelRead(); SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); List operations = introspector.getOperations(); - Assert.assertTrue("Operations detected", operations.size() == 0); + Assert.assertTrue("Operations detected", operations.isEmpty()); + HttpRequest request = introspector.getSecurityMetaData().getRequest(); - Assert.assertEquals("Invalid protocol", "HTTP", request.getProtocol()); + Assert.assertEquals("Invalid protocol", "http", request.getProtocol()); Assert.assertNotNull("No URL", request.getUrl()); - Assert.assertEquals("Invalid content-type", "text/html", request.getContentType()); - Assert.assertEquals("Invalid headers", "text/html", request.getHeaders().get("content-type")); - Assert.assertEquals("Invalid response body", "read data", request.getBody().toString()); + Assert.assertEquals("Invalid content-type", header, request.getContentType()); + Assert.assertEquals("Invalid headers", header, request.getHeaders().get("content-type")); + Assert.assertEquals("Invalid request body", "read data", request.getBody().toString()); } @Test - public void testWrite() throws JsonProcessingException { + public void testWrite() { write(); SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); List operations = introspector.getOperations(); - Assert.assertTrue("No operations detected", operations.size() > 0); + Assert.assertFalse("No operations detected", operations.isEmpty()); RXSSOperation operation = (RXSSOperation) operations.get(0); Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); HttpResponse response = introspector.getSecurityMetaData().getResponse(); - Assert.assertEquals("Invalid content-type body", "text/html", response.getResponseContentType()); - Assert.assertEquals("Invalid content-type body", "text/html", response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); Assert.assertEquals("Invalid response body", "write data", response.getResponseBody().toString()); } @Test - public void testWriteAndFlush() throws JsonProcessingException { + public void testWriteAndFlush() { writeAndFlush(); SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); List operations = introspector.getOperations(); - Assert.assertTrue("No operations detected", operations.size() > 0); + Assert.assertFalse("No operations detected", operations.isEmpty()); RXSSOperation operation = (RXSSOperation) operations.get(0); Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); HttpResponse response = introspector.getSecurityMetaData().getResponse(); - Assert.assertEquals("Invalid content-type body", "text/html", response.getResponseContentType()); - Assert.assertEquals("Invalid content-type body", "text/html", response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); Assert.assertEquals("Invalid response body", "write flush data", response.getResponseBody().toString()); } @Test - public void testWriteAndFlushPromise() throws JsonProcessingException { + public void testWriteAndFlushPromise() { writeAndFlushPromise(); SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); List operations = introspector.getOperations(); - Assert.assertTrue("No operations detected", operations.size() > 0); + Assert.assertFalse("No operations detected", operations.isEmpty()); RXSSOperation operation = (RXSSOperation) operations.get(0); Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); HttpResponse response = introspector.getSecurityMetaData().getResponse(); - Assert.assertEquals("Invalid content-type body", "text/html", response.getResponseContentType()); - Assert.assertEquals("Invalid content-type body", "text/html", response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); Assert.assertEquals("Invalid response body", "write flush promise data", response.getResponseBody().toString()); } @@ -114,24 +137,25 @@ public void testEncode() { SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); List operations = introspector.getOperations(); - Assert.assertTrue("No operations detected", operations.size() > 0); + Assert.assertFalse("No operations detected", operations.isEmpty()); RXSSOperation operation = (RXSSOperation) operations.get(0); Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); HttpResponse response = introspector.getSecurityMetaData().getResponse(); - Assert.assertEquals("Invalid content-type body", "text/html", response.getResponseContentType()); - Assert.assertEquals("Invalid content-type body", "text/html", response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); Assert.assertEquals("Invalid response body", "encode data", response.getResponseBody().toString()); } @Trace(dispatcher = true) private void channelRead() { EmbeddedChannel channel = new EmbeddedChannel(new ChannelInboundHandlerAdapter()); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/test"); - httpRequest.headers().add("content-type", "text/html"); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, "/test"); + httpRequest.headers().add("content-type", header); DefaultHttpContent httpContent = new DefaultHttpContent(Unpooled.wrappedBuffer("read data".getBytes())); +// httpRequest.content().writeBytes("read data".getBytes()); channel.writeInbound(httpRequest, httpContent); channel.read(); } @@ -140,7 +164,7 @@ private void channelRead() { private void write() { EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter()); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.ACCEPTED); - response.headers().add("content-type","text/html"); + response.headers().add("content-type", header); response.content().writeBytes("write data".getBytes()); channel.write(response); @@ -151,7 +175,7 @@ private void write() { private void writeAndFlush() { EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter()); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.ACCEPTED); - response.headers().add("content-type","text/html"); + response.headers().add("content-type", header); response.content().writeBytes("write flush data".getBytes()); channel.writeAndFlush(response); @@ -161,7 +185,7 @@ private void writeAndFlush() { private void writeAndFlushPromise() { EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter()); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.ACCEPTED); - response.headers().add("content-type","text/html"); + response.headers().add("content-type", header); response.content().writeBytes("write flush promise data".getBytes()); channel.writeAndFlush(response, channel.newPromise()); @@ -171,11 +195,22 @@ private void writeAndFlushPromise() { private void encode() { EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter()); channel.pipeline().addLast(new HttpResponseEncoder()); - FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.ACCEPTED); - response.headers().add("content-type","text/html"); + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.ACCEPTED); + response.headers().add("content-type", header); response.content().writeBytes("encode data".getBytes()); channel.write(response); channel.flush(); } + @Trace(dispatcher = true) + private void connect() throws IOException { + HttpURLConnection connection = (HttpURLConnection) server.getEndPoint().openConnection(); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + connection.setRequestProperty("content-type", header); + connection.getOutputStream().write("name=ishi".getBytes()); + + connection.connect(); + System.out.println(connection.getResponseCode()); + } } \ No newline at end of file diff --git a/instrumentation-security/netty-4.0.8/.snyk b/instrumentation-security/netty-4.0.8/.snyk new file mode 100644 index 000000000..588e3c335 --- /dev/null +++ b/instrumentation-security/netty-4.0.8/.snyk @@ -0,0 +1,30 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.25.0 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JAVA-IONETTY-559515: + - '*': + reason: Instrumentation false positive + expires: 2038-01-19T00:00:00.000Z + created: 2022-11-03T16:37:15.223Z + SNYK-JAVA-IONETTY-559516: + - '*': + reason: Instrumentation false positive + expires: 2038-01-19T00:00:00.000Z + created: 2022-11-03T16:37:19.008Z + SNYK-JAVA-IONETTY-73571: + - '*': + reason: Instrumentation false positive + expires: 2038-01-19T00:00:00.000Z + created: 2022-11-03T16:37:22.194Z + SNYK-JAVA-ORGSCALALANG-3032987: + - '*': + reason: Instrumentation false positive + expires: 2038-01-19T00:00:00.000Z + created: 2022-11-03T16:37:25.428Z + SNYK-JAVA-IONETTY-473214: + - '*': + reason: Instrumentation false positive + expires: 2038-01-19T00:00:00.000Z + created: 2022-11-11T14:10:14.681Z +patch: {} diff --git a/instrumentation-security/netty-4.0.8/build.gradle b/instrumentation-security/netty-4.0.8/build.gradle new file mode 100644 index 000000000..9dcbb5235 --- /dev/null +++ b/instrumentation-security/netty-4.0.8/build.gradle @@ -0,0 +1,21 @@ +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("io.netty:netty-all:4.0.8.Final") +} + +jar { + manifest { + attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.netty-4.0.8' + } +} + +verifyInstrumentation { + passesOnly 'io.netty:netty-all:[4.0.8.Final,5.0.0.Alpha1)' +} + +site { + title 'Netty' + type 'Appserver' +} \ No newline at end of file diff --git a/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/bootstrap/AbstractBootstrap_Instrumentation.java b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/bootstrap/AbstractBootstrap_Instrumentation.java new file mode 100644 index 000000000..2c9a7cb88 --- /dev/null +++ b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/bootstrap/AbstractBootstrap_Instrumentation.java @@ -0,0 +1,31 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package security.io.netty400.bootstrap; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelFuture; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +@Weave(type = MatchType.ExactClass, originalName = "io.netty.bootstrap.AbstractBootstrap") +public abstract class AbstractBootstrap_Instrumentation { + + @SuppressWarnings("unused") + private ChannelFuture doBind(final SocketAddress localAddress) { + if (localAddress instanceof InetSocketAddress) { + int port = ((InetSocketAddress) localAddress).getPort(); + NewRelicSecurity.getAgent().setApplicationConnectionConfig(port, "http"); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/ChannelHandler_Instrumentation.java b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/ChannelHandler_Instrumentation.java new file mode 100644 index 000000000..e12227199 --- /dev/null +++ b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/ChannelHandler_Instrumentation.java @@ -0,0 +1,44 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package security.io.netty400.channel; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelHandlerContext; + +@Weave(type = MatchType.Interface, originalName = "io.netty.channel.ChannelHandler") +public class ChannelHandler_Instrumentation { + + /* + * This is to solve a bug where the transaction is lost when spring webclient times out and throws an error + * using the io.netty.handler.timeout.ReadTimeoutHandler class from netty. + * + * Any extra handlers used by netty will now link a transaction if available. + * + * ----------------------------------- + * WARNING + * ----------------------------------- + * + * Netty has marked this method as deprecated since 4.1 + * + * If instrumentation verification fails for because of this class, + * then in the new instrumentation module try instrumenting the class: + * + * io.netty.channel.AbstractChannelHandlerContext + * + * and its method: + * + * static void invokeExceptionCaught(final AbstractChannelHandlerContext next, final Throwable cause) + * + * */ + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + // TODO : Negative RXSS case. Read response. + Weaver.callOriginal(); + } + +} diff --git a/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/ChannelInboundHandler_Instrumentation.java b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/ChannelInboundHandler_Instrumentation.java new file mode 100644 index 000000000..10c000dc3 --- /dev/null +++ b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/ChannelInboundHandler_Instrumentation.java @@ -0,0 +1,42 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package security.io.netty400.channel; + +import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpRequest; +import security.io.netty400.utils.NettyUtils; + +@Weave(type = MatchType.Interface, originalName = "io.netty.channel.ChannelInboundHandler") +public abstract class ChannelInboundHandler_Instrumentation { + + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + boolean isLockAcquired = false; + if (msg instanceof HttpRequest || msg instanceof HttpContent){ + isLockAcquired = NettyUtils.acquireNettyLockIfPossible(NettyUtils.NR_SEC_NETTY_OPERATIONAL_LOCK); + } + if (isLockAcquired) { + NettyUtils.processSecurityRequest(ctx, msg, getClass().getName()); + if (!StringUtils.startsWith(getClass().getName(), NettyUtils.IO_NETTY)) { + ServletHelper.registerUserLevelCode(NettyUtils.IO_NETTY); + } + } + try { + Weaver.callOriginal(); + } finally { + if (isLockAcquired) { + NettyUtils.releaseNettyLock(NettyUtils.NR_SEC_NETTY_OPERATIONAL_LOCK); + } + } + } +} diff --git a/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/ChannelOutboundHandler_Instrumentation.java b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/ChannelOutboundHandler_Instrumentation.java new file mode 100644 index 000000000..6a93460f1 --- /dev/null +++ b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/ChannelOutboundHandler_Instrumentation.java @@ -0,0 +1,40 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package security.io.netty400.channel; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpRequest; +import security.io.netty400.utils.NettyUtils; + +@Weave(type = MatchType.Interface, originalName = "io.netty.channel.ChannelOutboundHandler") +public abstract class ChannelOutboundHandler_Instrumentation { + + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + boolean isLockAcquired = false; + if (msg instanceof FullHttpResponse){ + isLockAcquired = NettyUtils.acquireNettyLockIfPossible(NettyUtils.NR_SEC_NETTY_OPERATIONAL_LOCK_OUTBOUND); + } + if (isLockAcquired) { + NettyUtils.processSecurityResponse(ctx, msg); + NettyUtils.sendRXSSEvent(ctx, msg, getClass().getName(), NettyUtils.WRITE_METHOD_NAME); + } + try { + Weaver.callOriginal(); + } finally { + if (isLockAcquired) { + NettyUtils.releaseNettyLock(NettyUtils.NR_SEC_NETTY_OPERATIONAL_LOCK_OUTBOUND); + } + } + } +} diff --git a/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/SimpleChannelInboundHandler_Instrumentation.java b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/SimpleChannelInboundHandler_Instrumentation.java new file mode 100644 index 000000000..b82cf5af4 --- /dev/null +++ b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/channel/SimpleChannelInboundHandler_Instrumentation.java @@ -0,0 +1,23 @@ +package security.io.netty400.channel; + +import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelHandlerContext; +import security.io.netty400.utils.NettyUtils; + +@Weave(type = MatchType.BaseClass, originalName = "io.netty.channel.SimpleChannelInboundHandler") +public class SimpleChannelInboundHandler_Instrumentation { + + protected void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception { + try { + if (!StringUtils.startsWith(getClass().getName(), NettyUtils.IO_NETTY)) { + ServletHelper.registerUserLevelCode(NettyUtils.IO_NETTY); + } + } catch (Exception e){ + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/handler/codec/http/HttpObjectEncoder_Instrumentation.java b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/handler/codec/http/HttpObjectEncoder_Instrumentation.java new file mode 100644 index 000000000..c4b1e1779 --- /dev/null +++ b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/handler/codec/http/HttpObjectEncoder_Instrumentation.java @@ -0,0 +1,26 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package security.io.netty400.handler.codec.http; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import io.netty.channel.ChannelHandlerContext; + +import java.util.List; + +@Weave(type = MatchType.BaseClass, originalName = "io.netty.handler.codec.http.HttpObjectEncoder") +public class HttpObjectEncoder_Instrumentation { + + // heading downstream + protected void encode(ChannelHandlerContext ctx, Object msg, List out) { + // TODO : Process response here + Weaver.callOriginal(); + } + +} diff --git a/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/utils/NettyUtils.java b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/utils/NettyUtils.java new file mode 100644 index 000000000..3a34eac2e --- /dev/null +++ b/instrumentation-security/netty-4.0.8/src/main/java/security/io/netty400/utils/NettyUtils.java @@ -0,0 +1,248 @@ +package security.io.netty400.utils; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; +import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.schema.AgentMetaData; +import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; +import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.api.agent.security.schema.policy.AgentPolicy; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class NettyUtils { + public static final String NETTY_4_0_0 = "NETTY-4.0.0"; + public static String NR_SEC_CUSTOM_ATTRIB_NAME = "NETTY-4.8-REQ-BODY-TRACKER"; + public static String NR_SEC_NETTY_OPERATIONAL_LOCK = "NR_SEC_NETTY_OPERATIONAL_LOCK_INBOUND"; + public static String NR_SEC_NETTY_OPERATIONAL_LOCK_OUTBOUND = "NR_SEC_NETTY_OPERATIONAL_LOCK_OUTBOUND"; + private static final String X_FORWARDED_FOR = "x-forwarded-for"; + private static final String EMPTY = ""; + public static final String WRITE_METHOD_NAME = "write"; + + public static final String IO_NETTY = "io.netty."; + private static final String ERROR_GETTING_SERVER_PORT = "Instrumentation library: %s , error while getting server port %s"; + private static final String ERROR_PARSING_HTTP_RESPONSE_DATA = "Instrumentation library: %s , error while parsing HTTP response data : %s"; + + public static void processSecurityRequest(ChannelHandlerContext ctx, Object msg, String className) { + try { + if (!NewRelicSecurity.isHookProcessingActive()) { + return; + } + if (msg instanceof HttpRequest) { + SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); + com.newrelic.api.agent.security.schema.HttpRequest securityRequest = + securityMetaData.getRequest(); + + if (!NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty() && securityRequest.isRequestParsed()) { + return; + } + securityRequest.setMethod(((HttpRequest) msg).getMethod().name()); + securityRequest.setUrl(((HttpRequest) msg).getUri()); + setClientAddressDetails(securityMetaData, ctx.channel().remoteAddress().toString()); + setServerPortDetails(securityRequest, ctx.channel().localAddress().toString()); + processHttpRequestHeader((HttpRequest)msg, securityRequest); + securityMetaData.setTracingHeaderValue(getTraceHeader(securityRequest.getHeaders())); + + securityRequest.setProtocol(((HttpRequest) msg).getProtocolVersion().protocolName().toLowerCase()); + securityRequest.setContentType(securityRequest.getHeaders().get("content-type")); + if (!securityMetaData.getMetaData().isUserLevelServiceMethodEncountered(IO_NETTY)){ + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + securityMetaData.getMetaData().setServiceTrace(Arrays.copyOfRange(stack, 2, stack.length)); + } + securityRequest.setRequestParsed(true); + } + if (msg instanceof HttpContent) { + Integer reqBodyTrackerContextId = NewRelicSecurity.getAgent().getSecurityMetaData() + .getCustomAttribute(NR_SEC_CUSTOM_ATTRIB_NAME, Integer.class); + if (reqBodyTrackerContextId == null) { + reqBodyTrackerContextId = ctx.hashCode(); + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(NR_SEC_CUSTOM_ATTRIB_NAME, reqBodyTrackerContextId); + } + if (reqBodyTrackerContextId.equals(ctx.hashCode())) { + com.newrelic.api.agent.security.schema.HttpRequest securityRequest = + NewRelicSecurity.getAgent().getSecurityMetaData().getRequest(); + securityRequest.getBody().append(((HttpContent) msg).content().toString(StandardCharsets.UTF_8)); + } + } + } catch (Throwable ignored) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_PARSING_HTTP_REQUEST_DATA, NETTY_4_0_0, ignored.getMessage()), ignored, NettyUtils.class.getName()); + } + } + + private static void setServerPortDetails(com.newrelic.api.agent.security.schema.HttpRequest securityRequest, String address) { + try { + String port = StringUtils.substringAfterLast(address, ":"); + if (StringUtils.isBlank(port)) { + return; + } + securityRequest.setServerPort(Integer.parseInt(port)); + } catch (Throwable throwable) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(ERROR_GETTING_SERVER_PORT, NETTY_4_0_0, throwable.getMessage()), throwable, NettyUtils.class.getName()); + } + } + + private static void setClientAddressDetails(SecurityMetaData securityMetaData, String address) { + if (StringUtils.isBlank(address)) { + return; + } + com.newrelic.api.agent.security.schema.HttpRequest securityRequest = securityMetaData.getRequest(); + address = StringUtils.replace(address, "/", ""); + securityRequest.setClientIP(StringUtils.substringBeforeLast(address, ":")); + securityRequest.setClientPort(StringUtils.substringAfterLast(address, ":")); + if (StringUtils.isNotBlank(securityRequest.getClientIP())) { + securityMetaData.getMetaData().getIps().add(securityRequest.getClientIP()); + } + } + + public static void processHttpRequestHeader(HttpRequest request, com.newrelic.api.agent.security.schema.HttpRequest securityRequest) { + Set headerNames = request.headers().names(); + for (String headerKey : headerNames) { + boolean takeNextValue = false; + if (headerKey != null) { + headerKey = headerKey.toLowerCase(); + } + AgentPolicy agentPolicy = NewRelicSecurity.getAgent().getCurrentPolicy(); + AgentMetaData agentMetaData = NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData(); + if (agentPolicy != null + && agentPolicy.getProtectionMode().getEnabled() + && agentPolicy.getProtectionMode().getIpBlocking().getEnabled() + && agentPolicy.getProtectionMode().getIpBlocking().getIpDetectViaXFF() + && X_FORWARDED_FOR.equals(headerKey)) { + takeNextValue = true; + } else if (ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID.equals(headerKey)) { + // TODO: May think of removing this intermediate obj and directly create K2 Identifier. + NewRelicSecurity.getAgent().getSecurityMetaData().setFuzzRequestIdentifier(ServletHelper.parseFuzzRequestIdentifierHeader(request.headers().get(headerKey))); + } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.headers().get(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); + } + + String headerFullValue = EMPTY; + List headerElements = request.headers().getAll(headerKey); + for (String headerValue : headerElements) { + if (headerValue != null && !headerValue.trim().isEmpty()) { + if (takeNextValue) { + agentMetaData.setClientDetectedFromXFF(true); + securityRequest.setClientIP(headerValue); + agentMetaData.getIps() + .add(securityRequest.getClientIP()); + securityRequest.setClientPort(EMPTY); + takeNextValue = false; + } + if (headerFullValue.trim().isEmpty()) { + headerFullValue = headerValue; + } else { + headerFullValue = String.join(";", headerFullValue, headerValue); + } + } + } + securityRequest.getHeaders().put(headerKey, headerFullValue); + } + + } + + public static String getTraceHeader(Map headers) { + String data = EMPTY; + if (headers.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER) || headers.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase())) { + data = headers.get(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER); + if (data == null || data.trim().isEmpty()) { + data = headers.get(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase()); + } + } + return data; + } + + public static void processSecurityResponse(ChannelHandlerContext ctx, Object msg) { + try { + if (NewRelicSecurity.isHookProcessingActive() && msg instanceof FullHttpResponse) { + SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); + com.newrelic.api.agent.security.schema.HttpResponse securityResponse = + securityMetaData.getResponse(); + processResponseHeaders((HttpResponse) msg, securityResponse); + securityResponse.setResponseContentType(((FullHttpResponse) msg).headers().get(HttpHeaders.Names.CONTENT_TYPE)); + securityResponse.getResponseBody().append(((FullHttpResponse) msg).content().toString(StandardCharsets.UTF_8)); + } + } catch (Throwable e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(ERROR_PARSING_HTTP_RESPONSE_DATA, NETTY_4_0_0, e.getMessage()), e, NettyUtils.class.getName()); + } + } + + public static void sendRXSSEvent(ChannelHandlerContext ctx, Object msg, String className, String methodName) { + try { + if (!NewRelicSecurity.isHookProcessingActive() || !(msg instanceof FullHttpResponse)) { + return; + } + //Add request URI hash to low severity event filter + LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest()); + + if(!ServletHelper.isResponseContentTypeExcluded(NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().getResponseContentType())) { + RXSSOperation rxssOperation = new RXSSOperation(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest(), + NewRelicSecurity.getAgent().getSecurityMetaData().getResponse(), + className, methodName); + NewRelicSecurity.getAgent().registerOperation(rxssOperation); + } + ServletHelper.tmpFileCleanUp(NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getTempFiles()); + } catch (Throwable e) { + if (e instanceof NewRelicSecurityException) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, NETTY_4_0_0, e.getMessage()), e, NettyUtils.class.getName()); + throw e; + } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, NETTY_4_0_0, e.getMessage()), e, NettyUtils.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, NETTY_4_0_0, e.getMessage()), e, NettyUtils.class.getName()); + } + } + + private static void processResponseHeaders(HttpResponse response, com.newrelic.api.agent.security.schema.HttpResponse securityResponse) { + for (Map.Entry entry : response.headers().entries()) { + String headerKey = entry.getKey().toLowerCase(); + String headerValue = entry.getValue(); + securityResponse.getHeaders().put(headerKey, headerValue); + } + } + + public static boolean isNettyLockAcquired(String operationLock) { + try { + return NewRelicSecurity.isHookProcessingActive() && + Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(operationLock + Thread.currentThread().getId(), Boolean.class)); + } catch (Throwable ignored) {} + return false; + } + + public static boolean acquireNettyLockIfPossible(String operationLock) { + try { + if (NewRelicSecurity.isHookProcessingActive() && + !isNettyLockAcquired(operationLock)) { + NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(operationLock + Thread.currentThread().getId(), true); + return true; + } + } catch (Throwable ignored){} + return false; + } + + public static void releaseNettyLock(String operationLock) { + try { + if(NewRelicSecurity.isHookProcessingActive()) { + NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(operationLock + Thread.currentThread().getId(), null); + } + } catch (Throwable ignored){} + } +} diff --git a/instrumentation-security/netty-4.0.8/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyServer.java b/instrumentation-security/netty-4.0.8/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyServer.java new file mode 100644 index 000000000..ea7a623be --- /dev/null +++ b/instrumentation-security/netty-4.0.8/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyServer.java @@ -0,0 +1,82 @@ +package com.nr.agent.security.instrumentation.netty400; + +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.util.CharsetUtil; +import org.junit.rules.ExternalResource; + +import java.net.MalformedURLException; +import java.net.URL; + +public class NettyServer extends ExternalResource { + private Channel channel; + private int PORT; + + @Override + protected void before() throws Throwable { + PORT = SecurityInstrumentationTestRunner.getIntrospector().getRandomPort(); + startServer(); + } + + @Override + protected void after() { + stopServer(); + } + private void startServer() throws InterruptedException { + ServerBootstrap b = new ServerBootstrap(); + b.group(new NioEventLoopGroup(),new NioEventLoopGroup()) + .channel(NioServerSocketChannel.class) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(new HttpServerCodec()); + pipeline.addLast(new HttpObjectAggregator(65536)); + pipeline.addLast(new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest o) throws Exception { + o.content(); + FullHttpResponse response = new DefaultFullHttpResponse( + HttpVersion.HTTP_1_0, + HttpResponseStatus.OK, + Unpooled.copiedBuffer("write data", CharsetUtil.UTF_8)); + HttpHeaders.addHeader(response, HttpHeaders.Names.CONTENT_TYPE, "text/html"); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + }); + } + }); + + channel = b.bind(PORT).sync().channel(); + System.out.println("checking..."); + } + + private void stopServer(){ + if (channel.isActive() && channel.isOpen()){ + channel.close(); + } + } + public URL getEndPoint() throws MalformedURLException { + return new URL("http://localhost:" + PORT + "/"); + } +} diff --git a/instrumentation-security/netty-4.0.8/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyTest.java b/instrumentation-security/netty-4.0.8/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyTest.java new file mode 100644 index 000000000..d04b4b722 --- /dev/null +++ b/instrumentation-security/netty-4.0.8/src/test/java/com/nr/agent/security/instrumentation/netty400/NettyTest.java @@ -0,0 +1,218 @@ +package com.nr.agent.security.instrumentation.netty400; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import com.newrelic.agent.security.introspec.SecurityIntrospector; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.schema.AbstractOperation; +import com.newrelic.api.agent.security.schema.HttpRequest; +import com.newrelic.api.agent.security.schema.HttpResponse; +import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; +import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelInboundHandlerAdapter; + +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.DefaultHttpContent; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseEncoder; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import security.io.netty400.utils.NettyUtils; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.List; + +@RunWith(SecurityInstrumentationTestRunner.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@InstrumentationTestConfig(includePrefixes = {"security.io.netty400"}) +public class NettyTest { + @ClassRule + public static NettyServer server = new NettyServer(); + + private final String header = "text/html"; + @Test + public void testChannelRXSS() throws IOException { + connect(); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertFalse("No operations detected", operations.isEmpty()); + + RXSSOperation operation = (RXSSOperation) operations.get(0); + Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); + Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); + + HttpResponse response = introspector.getSecurityMetaData().getResponse(); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid response body", "write data", response.getResponseBody().toString()); + } + @Test + public void testChannelRead() { + channelRead(); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertTrue("Operations detected", operations.isEmpty()); + + HttpRequest request = introspector.getSecurityMetaData().getRequest(); + Assert.assertEquals("Invalid protocol", "http", request.getProtocol()); + Assert.assertNotNull("No URL", request.getUrl()); + Assert.assertEquals("Invalid content-type", header, request.getContentType()); + Assert.assertEquals("Invalid headers", header, request.getHeaders().get("content-type")); + Assert.assertEquals("Invalid request body", "read data", request.getBody().toString()); + } + + @Test + public void testWrite() { + write(); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertFalse("No operations detected", operations.isEmpty()); + + RXSSOperation operation = (RXSSOperation) operations.get(0); + Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); + Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); + + HttpResponse response = introspector.getSecurityMetaData().getResponse(); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid response body", "write data", response.getResponseBody().toString()); + } + + @Test + public void testWriteAndFlush() { + writeAndFlush(); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertFalse("No operations detected", operations.isEmpty()); + + RXSSOperation operation = (RXSSOperation) operations.get(0); + Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); + Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); + + HttpResponse response = introspector.getSecurityMetaData().getResponse(); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid response body", "write flush data", response.getResponseBody().toString()); + } + + @Test + public void testWriteAndFlushPromise() { + writeAndFlushPromise(); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertFalse("No operations detected", operations.isEmpty()); + + RXSSOperation operation = (RXSSOperation) operations.get(0); + Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); + Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); + + HttpResponse response = introspector.getSecurityMetaData().getResponse(); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid response body", "write flush promise data", response.getResponseBody().toString()); + } + + @Test + public void testEncode() { + encode(); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertFalse("No operations detected", operations.isEmpty()); + + RXSSOperation operation = (RXSSOperation) operations.get(0); + Assert.assertEquals("Invalid executed method name", NettyUtils.WRITE_METHOD_NAME, operation.getMethodName()); + Assert.assertEquals("Invalid event category", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); + + HttpResponse response = introspector.getSecurityMetaData().getResponse(); + Assert.assertEquals("Invalid content-type body", header, response.getResponseContentType()); + Assert.assertEquals("Invalid content-type body", header, response.getHeaders().get("content-type")); + Assert.assertEquals("Invalid response body", "encode data", response.getResponseBody().toString()); + } + + @Trace(dispatcher = true) + private void channelRead() { + EmbeddedChannel channel = new EmbeddedChannel(new ChannelInboundHandlerAdapter()); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, "/test"); + httpRequest.headers().add("content-type", header); + DefaultHttpContent httpContent = new DefaultHttpContent(Unpooled.wrappedBuffer("read data".getBytes())); +// httpRequest.content().writeBytes("read data".getBytes()); + channel.writeInbound(httpRequest, httpContent); + channel.read(); + } + + @Trace(dispatcher = true) + private void write() { + EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter()); + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.ACCEPTED); + response.headers().add("content-type", header); + response.content().writeBytes("write data".getBytes()); + + channel.write(response); + channel.flush(); + } + + @Trace(dispatcher = true) + private void writeAndFlush() { + EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter()); + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.ACCEPTED); + response.headers().add("content-type", header); + response.content().writeBytes("write flush data".getBytes()); + + channel.writeAndFlush(response); + } + + @Trace(dispatcher = true) + private void writeAndFlushPromise() { + EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter()); + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.ACCEPTED); + response.headers().add("content-type", header); + response.content().writeBytes("write flush promise data".getBytes()); + + channel.writeAndFlush(response, channel.newPromise()); + } + + @Trace(dispatcher = true) + private void encode() { + EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter()); + channel.pipeline().addLast(new HttpResponseEncoder()); + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.ACCEPTED); + response.headers().add("content-type", header); + response.content().writeBytes("encode data".getBytes()); + + channel.write(response); + channel.flush(); + } + @Trace(dispatcher = true) + private void connect() throws IOException { + HttpURLConnection connection = (HttpURLConnection) server.getEndPoint().openConnection(); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + connection.setRequestProperty("content-type", header); + connection.getOutputStream().write("name=ishi".getBytes()); + + connection.connect(); + System.out.println(connection.getResponseCode()); + } +} \ No newline at end of file diff --git a/instrumentation-security/ning-async-http-client-1.0.0/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_0/NingHelper.java b/instrumentation-security/ning-async-http-client-1.0.0/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_0/NingHelper.java index 4d24a9b23..8711a0146 100644 --- a/instrumentation-security/ning-async-http-client-1.0.0/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_0/NingHelper.java +++ b/instrumentation-security/ning-async-http-client-1.0.0/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_0/NingHelper.java @@ -53,6 +53,7 @@ public static AbstractOperation preprocessSecurityHook(Request request, String u SSRFOperation operation = new SSRFOperation(uri, className, methodName); try { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } finally { if (operation.getApiID() != null && !operation.getApiID().trim().isEmpty() && diff --git a/instrumentation-security/ning-async-http-client-1.1.0/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_1/NingHelper.java b/instrumentation-security/ning-async-http-client-1.1.0/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_1/NingHelper.java index 208c5bd7a..c2d17e323 100644 --- a/instrumentation-security/ning-async-http-client-1.1.0/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_1/NingHelper.java +++ b/instrumentation-security/ning-async-http-client-1.1.0/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_1/NingHelper.java @@ -47,6 +47,7 @@ public static AbstractOperation preprocessSecurityHook(Request request, String u SSRFOperation operation = new SSRFOperation(uri, className, methodName); try { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } finally { if (operation.getApiID() != null && !operation.getApiID().trim().isEmpty() && diff --git a/instrumentation-security/ning-async-http-client-1.6.1/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_6_1/NingHelper.java b/instrumentation-security/ning-async-http-client-1.6.1/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_6_1/NingHelper.java index ad0595b86..c4ecbb2f2 100644 --- a/instrumentation-security/ning-async-http-client-1.6.1/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_6_1/NingHelper.java +++ b/instrumentation-security/ning-async-http-client-1.6.1/src/main/java/com/newrelic/agent/security/instrumentation/ning/http_1_6_1/NingHelper.java @@ -50,6 +50,7 @@ public static AbstractOperation preprocessSecurityHook(Request request, String u SSRFOperation operation = new SSRFOperation(uri, className, methodName); try { + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); } finally { if (operation.getApiID() != null && !operation.getApiID().trim().isEmpty() && diff --git a/instrumentation-security/okhttp-3.0.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp30/OkhttpHelper.java b/instrumentation-security/okhttp-3.0.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp30/OkhttpHelper.java index ce4ce93ed..21c25c193 100644 --- a/instrumentation-security/okhttp-3.0.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp30/OkhttpHelper.java +++ b/instrumentation-security/okhttp-3.0.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp30/OkhttpHelper.java @@ -69,6 +69,7 @@ public static AbstractOperation preprocessSecurityHook(String url, String classN SSRFOperation operation = new SSRFOperation(url, className, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(operation); return operation; } catch (Throwable e) { diff --git a/instrumentation-security/okhttp-3.5.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp35/OkhttpHelper.java b/instrumentation-security/okhttp-3.5.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp35/OkhttpHelper.java index 84820a805..ea4ee3192 100644 --- a/instrumentation-security/okhttp-3.5.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp35/OkhttpHelper.java +++ b/instrumentation-security/okhttp-3.5.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp35/OkhttpHelper.java @@ -67,6 +67,7 @@ public static AbstractOperation preprocessSecurityHook(String url, String classN return null; } SSRFOperation ssrfOperation = new SSRFOperation(url, className, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(ssrfOperation); return ssrfOperation; } catch (Throwable e) { diff --git a/instrumentation-security/okhttp-4.0.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp40/OkhttpHelper.java b/instrumentation-security/okhttp-4.0.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp40/OkhttpHelper.java index 753334036..7f2d936da 100644 --- a/instrumentation-security/okhttp-4.0.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp40/OkhttpHelper.java +++ b/instrumentation-security/okhttp-4.0.0/src/main/java/com/newrelic/agent/security/instrumentation/okhttp40/OkhttpHelper.java @@ -68,6 +68,7 @@ public static AbstractOperation preprocessSecurityHook(String url, String classN return null; } SSRFOperation ssrfOperation = new SSRFOperation(url, className, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(ssrfOperation); return ssrfOperation; } catch (Throwable e) { diff --git a/instrumentation-security/play-2.13_2.7/build.gradle b/instrumentation-security/play-2.13_2.7/build.gradle new file mode 100644 index 000000000..23b3cf874 --- /dev/null +++ b/instrumentation-security/play-2.13_2.7/build.gradle @@ -0,0 +1,79 @@ +import play.routes.compiler.InjectedRoutesGenerator$ +import play.routes.compiler.RoutesCompiler +import play.routes.compiler.RoutesCompiler$ + +apply plugin: 'scala' + +scala { + zincVersion = "1.7.1" +} +isScalaProjectEnabled(project, "scala-2.13") + +sourceSets.test.scala.srcDir "src/test/java" +sourceSets.test.java.srcDirs = [] + +compileJava.options.bootstrapClasspath = null + +buildscript { + dependencies { + classpath 'com.typesafe.play:routes-compiler_2.13:2.7.3' + } + repositories { + mavenCentral() + } +} + +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("org.scala-lang:scala-library:2.13.12") + implementation("com.typesafe.play:play_2.13:2.7.3") + testImplementation("com.typesafe.play:routes-compiler_2.13:2.7.3") + testImplementation("com.typesafe.play:play-test_2.13:2.7.3") + testImplementation("com.typesafe.play:play-akka-http-server_2.13:2.7.3") + testImplementation("com.typesafe.play:play-java_2.13:2.7.3") + testImplementation("jakarta.xml.ws:jakarta.xml.ws-api:2.3.3") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.play-2.13_2.7' } +} + +verifyInstrumentation { + passesOnly 'com.typesafe.play:play_2.13:[2.7.0-M1,)' + passesOnly 'org.playframework:play_2.13:[3.0.0-M1,)' + passesOnly 'org.playframework:play_3:[3.0.0-M1,)' + + // build snapshots + excludeRegex '.*-[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]{7}$' +} + +compileTestScala { + def routeFile = file("src/test/resources/conf/routes") + def generatedSourcesDir = layout.buildDirectory.dir("generated/scala") + + options.compilerArgs += '-proc:none' + + inputs.file(routeFile) + localState.register(generatedSourcesDir) + + // this manually compiles the conf/routes file into an Routes.scala file, which is subsequently read on startup by our test application + doFirst { + def RoutesCompiler.RoutesCompilerTask routesCompilerTask = new RoutesCompiler.RoutesCompilerTask( + routeFile, scala.collection.immutable.List$.MODULE$.empty().toSeq(), true, false, false) + RoutesCompiler$.MODULE$.compile(routesCompilerTask, InjectedRoutesGenerator$.MODULE$, generatedSourcesDir.get().asFile) + source generatedSourcesDir + scala.collection.immutable.List$.MODULE$.empty() + } +} + +clean { + // Clean up any residual generated Routes files to avoid duplicate classes + delete 'src/test/scala/router' +} + +site { + title 'Play' + type 'Appserver' +} \ No newline at end of file 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 new file mode 100644 index 000000000..31735382b --- /dev/null +++ b/instrumentation-security/play-2.13_2.7/src/main/scala/com/newrelic/agent/security/instrumentation/play2_13/GeneratedRouter_Instrumentation.scala @@ -0,0 +1,34 @@ +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.weaver.{MatchType, Weave, Weaver} +import play.api.routing.HandlerDef +import play.core.routing.{HandlerInvoker, HandlerInvokerFactory} + +import scala.jdk.CollectionConverters.SeqHasAsJava + + +@Weave(originalName = "play.core.routing.GeneratedRouter", `type` = MatchType.BaseClass) +abstract class GeneratedRouter_Instrumentation { + + def documentation: scala.collection.immutable.Seq[(String, String, String)] + + def createInvoker[T](fakeCall: => T, handlerDef: HandlerDef)(implicit hif: HandlerInvokerFactory[T]): HandlerInvoker[T] = { + try { + Weaver.callOriginal() + } finally { + gatherURLMappings() + } + } + + private def gatherURLMappings(): Unit = { + val iterator = documentation.asJava.iterator() + while (iterator.hasNext){ + val doc = iterator.next + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(doc._1, doc._2, doc._3)) + } + } +} + + diff --git a/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/APIEndpointTest.java b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/APIEndpointTest.java new file mode 100644 index 000000000..50d2e31b4 --- /dev/null +++ b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/APIEndpointTest.java @@ -0,0 +1,74 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play2_7; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +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.security.test.marker.Java17IncompatibleTest; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + + +@Category({ Java17IncompatibleTest.class }) +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "com.newrelic.agent.security.instrumentation.play2_13" }) +public class APIEndpointTest { + + @ClassRule + public static PlayApplicationServerRule serverRule = new PlayApplicationServerRule(); + + private static final Map expectedMappings = new HashMap<>(); + + @BeforeClass + public static void setupMappings() { + expectedMappings.put("/hello", SimpleJavaController.class.getName() + ".hello"); + expectedMappings.put("/scalaHello", SimpleScalaController.class.getName() + ".scalaHello"); + expectedMappings.put("/post", SimpleJavaController.class.getName() + ".post(data:String)"); + expectedMappings.put("/index", SimpleJavaController.class.getName() + ".index"); + expectedMappings.put("/simple", SimpleJavaController.class.getName() + ".simple"); + } + + @Test + public void testControllerActions() throws IOException { + HttpURLConnection conn = ((HttpURLConnection) serverRule.getEndpoint("/hello").openConnection()); + conn.connect(); + System.out.println(conn.getResponseCode()); + + Set actualMappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertNotNull(actualMappings); + Assert.assertEquals(5, actualMappings.size()); + for (ApplicationURLMapping actualMapping : actualMappings) { + assertMappings(actualMapping); + } + } + + private void assertMappings(ApplicationURLMapping actualMapping){ + Assert.assertNotNull(actualMapping.getPath()); + Assert.assertNotNull(actualMapping.getHandler()); + Assert.assertNotNull(actualMapping.getMethod()); + + String path = actualMapping.getPath(); + String handler = expectedMappings.get(path); + String method = !path.equals("/post") ? "GET" : "POST"; + + Assert.assertEquals(handler, actualMapping.getHandler()); + Assert.assertEquals(method, actualMapping.getMethod()); + } +} \ No newline at end of file diff --git a/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/PlayApplicationServerRule.java b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/PlayApplicationServerRule.java new file mode 100644 index 000000000..5d551f312 --- /dev/null +++ b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/PlayApplicationServerRule.java @@ -0,0 +1,60 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play2_7; + +import org.junit.rules.ExternalResource; +import play.Application; +import play.inject.guice.GuiceApplicationBuilder; +import play.test.Helpers; +import play.test.TestServer; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.URL; + +import static play.inject.Bindings.bind; + +public class PlayApplicationServerRule extends ExternalResource { + private TestServer server; + private int port; + + public PlayApplicationServerRule() { + this.port = getRandomPort(); + } + + @Override + protected void before() throws Throwable { + Application application = new GuiceApplicationBuilder() + .bindings( + bind(SimpleJavaController.class).toSelf().eagerly(), + bind(SimpleScalaController.class).toSelf().eagerly(), + bind(SimpleJavaAction.class).toSelf().eagerly()) + .build(); + + server = Helpers.testServer(port, application); + server.start(); + } + + @Override + protected void after() { + server.stop(); + } + + private int getRandomPort() { + try (ServerSocket socket = new ServerSocket(0)){ + return socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral PORT"); + } + } + + public URL getEndpoint(String path) throws MalformedURLException { + return new URL("http://localhost:" + port + path); + } +} diff --git a/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/SimpleJavaAction.java b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/SimpleJavaAction.java new file mode 100644 index 000000000..8f0b6f74b --- /dev/null +++ b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/SimpleJavaAction.java @@ -0,0 +1,61 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play2_7; + + +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; +import javax.inject.Inject; + +import play.Logger; +import play.libs.concurrent.Futures; +import play.libs.concurrent.HttpExecutionContext; +import play.mvc.Http; +import play.mvc.Result; +import play.mvc.Results; + +import static play.mvc.Http.Status.GATEWAY_TIMEOUT; + +public class SimpleJavaAction extends play.mvc.Action.Simple { + private final Logger.ALogger logger = Logger.of("application.SimpleJavaAction"); + + private final HttpExecutionContext ec; + private final Futures futures; + + @Inject + public SimpleJavaAction(HttpExecutionContext ec, Futures futures) { + this.ec = ec; + this.futures = futures; + } + + public CompletionStage call(Http.Request req) { + if (logger.isTraceEnabled()) { + logger.trace("call: req = " + req); + } + + return futures.timeout(doCall(req), 1L, TimeUnit.SECONDS).exceptionally(e -> + (Results.status(GATEWAY_TIMEOUT, "No"))); + } + + private CompletionStage doCall(Http.Request req) { + return delegate.call(req).handleAsync((result, e) -> { + if (e != null) { + if (e instanceof CompletionException) { + logger.error("Direct exception " + e.getMessage(), e); + return internalServerError(); + } else { + logger.error("Unknown exception " + e.getMessage(), e); + return internalServerError(); + } + } else { + return result; + } + }, ec.current()); + } +} \ No newline at end of file diff --git a/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/SimpleJavaController.java b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/SimpleJavaController.java new file mode 100644 index 000000000..67dd207ac --- /dev/null +++ b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/SimpleJavaController.java @@ -0,0 +1,39 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play2_7; + +import javax.inject.Inject; + +import play.libs.concurrent.HttpExecutionContext; +import play.mvc.Controller; +import play.mvc.Result; +import play.mvc.With; + +@With(SimpleJavaAction.class) +public class SimpleJavaController extends Controller { + private HttpExecutionContext ec; + + @Inject + public SimpleJavaController(HttpExecutionContext ec) { + this.ec = ec; + } + + public Result post(String data) { return ok("Received data: " + data).as("text/plain");} + public Result hello() { + return ok("hello world").as("text/plain"); + } + + public Result index() { + return ok("Index Page").as("text/plain"); + } + + public Result simple() { + return ok("Simple test").as("text/plain"); + } + +} diff --git a/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/SimpleScalaController.scala b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/SimpleScalaController.scala new file mode 100644 index 000000000..8f84ab42c --- /dev/null +++ b/instrumentation-security/play-2.13_2.7/src/test/java/com/nr/agent/security/instrumentation/play2_7/SimpleScalaController.scala @@ -0,0 +1,24 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play2_7 + +import play.api.mvc._ + +import javax.inject.Inject +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future + +class SimpleScalaController @Inject()(components: ControllerComponents) extends AbstractController(components) { + + def scalaHello = Action.async { + Future { + Ok("Scala says hello world") + } + } + +} diff --git a/instrumentation-security/play-2.13_2.7/src/test/resources/conf/routes b/instrumentation-security/play-2.13_2.7/src/test/resources/conf/routes new file mode 100644 index 000000000..530d9c58d --- /dev/null +++ b/instrumentation-security/play-2.13_2.7/src/test/resources/conf/routes @@ -0,0 +1,5 @@ +GET /hello com.nr.agent.security.instrumentation.play2_7.SimpleJavaController.hello +GET /index com.nr.agent.security.instrumentation.play2_7.SimpleJavaController.index +GET /simple com.nr.agent.security.instrumentation.play2_7.SimpleJavaController.simple +GET /scalaHello com.nr.agent.security.instrumentation.play2_7.SimpleScalaController.scalaHello +POST /post com.nr.agent.security.instrumentation.play2_7.SimpleJavaController.post(data: String) \ No newline at end of file diff --git a/instrumentation-security/play-2.4/build.gradle b/instrumentation-security/play-2.4/build.gradle new file mode 100644 index 000000000..2c3810bba --- /dev/null +++ b/instrumentation-security/play-2.4/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'scala' + +isScalaProjectEnabled(project, "scala-2.10") + +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("org.scala-lang:scala-library:2.11.12") + implementation("com.typesafe.play:play_2.11:2.4.0") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.play-2.4' } +} + +verifyInstrumentation { + passesOnly 'com.typesafe.play:play_2.11:[2.4.0-M3,2.6.0-M1)' + passesOnly 'com.typesafe.play:play_2.10:[2.4.0-M3,)' + + fails 'com.typesafe.play:play_2.10:2.3.10' + fails 'com.typesafe.play:play_2.11:2.3.10' + + // build snapshots + excludeRegex '.*-[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]{7}$' +} + +site { + title 'Play' + type 'Appserver' +} \ No newline at end of file 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 new file mode 100644 index 000000000..bd0f4b043 --- /dev/null +++ b/instrumentation-security/play-2.4/src/main/scala/com/newrelic/agent/security/instrumentation/play24/GeneratedRouter_Instrumentation.scala @@ -0,0 +1,33 @@ +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.weaver.{MatchType, Weave, Weaver} +import play.core.routing.{HandlerDef, HandlerInvoker, HandlerInvokerFactory} + +import scala.collection.JavaConverters.asJavaIterableConverter + + +@Weave(originalName = "play.core.routing.GeneratedRouter", `type` = MatchType.BaseClass) +abstract class GeneratedRouter_Instrumentation { + + def documentation: Seq[(String, String, String)] + + def createInvoker[T](fakeCall: => T, handlerDef: HandlerDef)(implicit hif: HandlerInvokerFactory[T]): HandlerInvoker[T] = { + try { + Weaver.callOriginal() + } finally { + gatherURLMappings() + } + } + + private def gatherURLMappings(): Unit = { + val iterator = documentation.asJava.iterator() + while (iterator.hasNext){ + val doc = iterator.next + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(doc._1, doc._2, doc._3)) + } + } +} + + diff --git a/instrumentation-security/play-2.6/build.gradle b/instrumentation-security/play-2.6/build.gradle new file mode 100644 index 000000000..b3fecafda --- /dev/null +++ b/instrumentation-security/play-2.6/build.gradle @@ -0,0 +1,76 @@ +import play.routes.compiler.InjectedRoutesGenerator$ +import play.routes.compiler.RoutesCompiler +import play.routes.compiler.RoutesCompiler$ + +apply plugin: 'scala' + +isScalaProjectEnabled(project, "scala-2.11") + +sourceSets.test.scala.srcDir "src/test/java" +sourceSets.test.java.srcDirs = [] + +compileJava.options.bootstrapClasspath = null + +buildscript { + dependencies { + classpath 'com.typesafe.play:routes-compiler_2.13:2.7.3' + } + repositories { + mavenCentral() + } +} + +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("org.scala-lang:scala-library:2.11.12") + implementation("com.typesafe.play:play_2.11:2.7.0") + testImplementation("com.typesafe.play:routes-compiler_2.11:2.7.0") + testImplementation("com.typesafe.play:play-test_2.11:2.7.0") + testImplementation("com.typesafe.play:play-akka-http-server_2.11:2.7.0") + testImplementation("com.typesafe.play:play-java_2.11:2.7.0") + testImplementation("jakarta.xml.ws:jakarta.xml.ws-api:2.3.3") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.play-2.6' } +} + +verifyInstrumentation { + passesOnly 'com.typesafe.play:play_2.12:[2.6.0-M1,)' + passesOnly 'com.typesafe.play:play_2.11:[2.6.0-M1,)' + fails 'com.typesafe.play:play_2.13:[2.7.0-M1,)' + + // build snapshots + excludeRegex '.*-[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]{7}$' +} + +compileTestScala { + def routeFile = file("src/test/resources/conf/routes") + def generatedSourcesDir = layout.buildDirectory.dir("generated/scala") + + options.compilerArgs += '-proc:none' + + inputs.file(routeFile) + localState.register(generatedSourcesDir) + + // this manually compiles the conf/routes file into an Routes.scala file, which is subsequently read on startup by our test application + doFirst { + def RoutesCompiler.RoutesCompilerTask routesCompilerTask = new RoutesCompiler.RoutesCompilerTask( + routeFile, scala.collection.immutable.List$.MODULE$.empty().toSeq(), true, false, false) + RoutesCompiler$.MODULE$.compile(routesCompilerTask, InjectedRoutesGenerator$.MODULE$, generatedSourcesDir.get().asFile) + source generatedSourcesDir + scala.collection.immutable.List$.MODULE$.empty() + } +} + +clean { + // Clean up any residual generated Routes files to avoid duplicate classes + delete 'src/test/scala/router' +} + +site { + title 'Play' + type 'Appserver' +} \ No newline at end of file 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 new file mode 100644 index 000000000..ca821c948 --- /dev/null +++ b/instrumentation-security/play-2.6/src/main/scala/com/newrelic/agent/security/instrumentation/play26/GeneratedRouter_Instrumentation.scala @@ -0,0 +1,33 @@ +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.weaver.{MatchType, Weave, Weaver} +import play.api.routing.HandlerDef +import play.core.routing.{HandlerInvoker, HandlerInvokerFactory} + +import scala.collection.JavaConverters.asJavaIterableConverter + +@Weave(originalName = "play.core.routing.GeneratedRouter", `type` = MatchType.BaseClass) +abstract class GeneratedRouter_Instrumentation { + + def documentation: Seq[(String, String, String)] + + def createInvoker[T](fakeCall: => T, handlerDef: HandlerDef)(implicit hif: HandlerInvokerFactory[T]): HandlerInvoker[T] = { + try { + Weaver.callOriginal() + } finally { + gatherURLMappings() + } + } + + private def gatherURLMappings(): Unit = { + val iterator = documentation.asJava.iterator() + while (iterator.hasNext){ + val doc = iterator.next + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(doc._1, doc._2, doc._3)) + } + } +} + + diff --git a/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/APIEndpointTest.java b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/APIEndpointTest.java new file mode 100644 index 000000000..1a1f0ca4e --- /dev/null +++ b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/APIEndpointTest.java @@ -0,0 +1,74 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play26; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +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.security.test.marker.Java17IncompatibleTest; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + + +@Category({ Java17IncompatibleTest.class }) +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "com.newrelic.agent.security.instrumentation.play26" }) +public class APIEndpointTest { + + @ClassRule + public static PlayApplicationServerRule serverRule = new PlayApplicationServerRule(); + + private static final Map expectedMappings = new HashMap<>(); + + @BeforeClass + public static void setupMappings() { + expectedMappings.put("/hello", SimpleJavaController.class.getName() + ".hello"); + expectedMappings.put("/scalaHello", SimpleScalaController.class.getName() + ".scalaHello"); + expectedMappings.put("/post", SimpleJavaController.class.getName() + ".post(data:String)"); + expectedMappings.put("/index", SimpleJavaController.class.getName() + ".index"); + expectedMappings.put("/simple", SimpleJavaController.class.getName() + ".simple"); + } + + @Test + public void testControllerActions() throws IOException { + HttpURLConnection conn = ((HttpURLConnection) serverRule.getEndpoint("/hello").openConnection()); + conn.connect(); + System.out.println(conn.getResponseCode()); + + Set actualMappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertNotNull(actualMappings); + Assert.assertEquals(5, actualMappings.size()); + for (ApplicationURLMapping actualMapping : actualMappings) { + assertMappings(actualMapping); + } + } + + private void assertMappings(ApplicationURLMapping actualMapping){ + Assert.assertNotNull(actualMapping.getPath()); + Assert.assertNotNull(actualMapping.getHandler()); + Assert.assertNotNull(actualMapping.getMethod()); + + String path = actualMapping.getPath(); + String handler = expectedMappings.get(path); + String method = !path.equals("/post") ? "GET" : "POST"; + + Assert.assertEquals(handler, actualMapping.getHandler()); + Assert.assertEquals(method, actualMapping.getMethod()); + } +} \ No newline at end of file diff --git a/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/PlayApplicationServerRule.java b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/PlayApplicationServerRule.java new file mode 100644 index 000000000..399f228e3 --- /dev/null +++ b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/PlayApplicationServerRule.java @@ -0,0 +1,60 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play26; + +import org.junit.rules.ExternalResource; +import play.Application; +import play.inject.guice.GuiceApplicationBuilder; +import play.test.Helpers; +import play.test.TestServer; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.URL; + +import static play.inject.Bindings.bind; + +public class PlayApplicationServerRule extends ExternalResource { + private TestServer server; + private int port; + + public PlayApplicationServerRule() { + this.port = getRandomPort(); + } + + @Override + protected void before() throws Throwable { + Application application = new GuiceApplicationBuilder() + .bindings( + bind(SimpleJavaController.class).toSelf().eagerly(), + bind(SimpleScalaController.class).toSelf().eagerly(), + bind(SimpleJavaAction.class).toSelf().eagerly()) + .build(); + + server = Helpers.testServer(port, application); + server.start(); + } + + @Override + protected void after() { + server.stop(); + } + + private int getRandomPort() { + try (ServerSocket socket = new ServerSocket(0)){ + return socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral PORT"); + } + } + + public URL getEndpoint(String path) throws MalformedURLException { + return new URL("http://localhost:" + port + path); + } +} diff --git a/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/SimpleJavaAction.java b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/SimpleJavaAction.java new file mode 100644 index 000000000..41919ca65 --- /dev/null +++ b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/SimpleJavaAction.java @@ -0,0 +1,61 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play26; + + +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; +import javax.inject.Inject; + +import play.Logger; +import play.libs.concurrent.Futures; +import play.libs.concurrent.HttpExecutionContext; +import play.mvc.Http; +import play.mvc.Result; +import play.mvc.Results; + +import static play.mvc.Http.Status.GATEWAY_TIMEOUT; + +public class SimpleJavaAction extends play.mvc.Action.Simple { + private final Logger.ALogger logger = Logger.of("application.SimpleJavaAction"); + + private final HttpExecutionContext ec; + private final Futures futures; + + @Inject + public SimpleJavaAction(HttpExecutionContext ec, Futures futures) { + this.ec = ec; + this.futures = futures; + } + + public CompletionStage call(Http.Request req) { + if (logger.isTraceEnabled()) { + logger.trace("call: req = " + req); + } + + return futures.timeout(doCall(req), 1L, TimeUnit.SECONDS).exceptionally(e -> + (Results.status(GATEWAY_TIMEOUT, "No"))); + } + + private CompletionStage doCall(Http.Request req) { + return delegate.call(req).handleAsync((result, e) -> { + if (e != null) { + if (e instanceof CompletionException) { + logger.error("Direct exception " + e.getMessage(), e); + return internalServerError(); + } else { + logger.error("Unknown exception " + e.getMessage(), e); + return internalServerError(); + } + } else { + return result; + } + }, ec.current()); + } +} \ No newline at end of file diff --git a/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/SimpleJavaController.java b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/SimpleJavaController.java new file mode 100644 index 000000000..6df3468e2 --- /dev/null +++ b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/SimpleJavaController.java @@ -0,0 +1,39 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play26; + +import javax.inject.Inject; + +import play.libs.concurrent.HttpExecutionContext; +import play.mvc.Controller; +import play.mvc.Result; +import play.mvc.With; + +@With(SimpleJavaAction.class) +public class SimpleJavaController extends Controller { + private HttpExecutionContext ec; + + @Inject + public SimpleJavaController(HttpExecutionContext ec) { + this.ec = ec; + } + + public Result post(String data) { return ok("Received data: " + data).as("text/plain");} + public Result hello() { + return ok("hello world").as("text/plain"); + } + + public Result index() { + return ok("Index Page").as("text/plain"); + } + + public Result simple() { + return ok("Simple test").as("text/plain"); + } + +} diff --git a/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/SimpleScalaController.scala b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/SimpleScalaController.scala new file mode 100644 index 000000000..4a9259baf --- /dev/null +++ b/instrumentation-security/play-2.6/src/test/java/com/nr/agent/security/instrumentation/play26/SimpleScalaController.scala @@ -0,0 +1,24 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.security.instrumentation.play26 + +import play.api.mvc._ + +import javax.inject.Inject +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future + +class SimpleScalaController @Inject()(components: ControllerComponents) extends AbstractController(components) { + + def scalaHello = Action.async { + Future { + Ok("Scala says hello world") + } + } + +} diff --git a/instrumentation-security/play-2.6/src/test/resources/conf/routes b/instrumentation-security/play-2.6/src/test/resources/conf/routes new file mode 100644 index 000000000..7af0d11ef --- /dev/null +++ b/instrumentation-security/play-2.6/src/test/resources/conf/routes @@ -0,0 +1,5 @@ +GET /hello com.nr.agent.security.instrumentation.play26.SimpleJavaController.hello +GET /index com.nr.agent.security.instrumentation.play26.SimpleJavaController.index +GET /simple com.nr.agent.security.instrumentation.play26.SimpleJavaController.simple +GET /scalaHello com.nr.agent.security.instrumentation.play26.SimpleScalaController.scalaHello +POST /post com.nr.agent.security.instrumentation.play26.SimpleJavaController.post(data: String) \ No newline at end of file diff --git a/instrumentation-security/resteasy-2.2/build.gradle b/instrumentation-security/resteasy-2.2/build.gradle index 1e74c9fb7..b224f25f4 100644 --- a/instrumentation-security/resteasy-2.2/build.gradle +++ b/instrumentation-security/resteasy-2.2/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("org.jboss.resteasy:resteasy-jaxrs:2.3.3.Final") + testImplementation('org.apache.tomcat.embed:tomcat-embed-core:9.0.62') } jar { diff --git a/instrumentation-security/resteasy-2.2/src/main/java/com/nr/instrumentation/security/resteasy2/RestEasyHelper.java b/instrumentation-security/resteasy-2.2/src/main/java/com/newrelic/agent/security/instrumentation/resteasy2/RestEasyHelper.java similarity index 96% rename from instrumentation-security/resteasy-2.2/src/main/java/com/nr/instrumentation/security/resteasy2/RestEasyHelper.java rename to instrumentation-security/resteasy-2.2/src/main/java/com/newrelic/agent/security/instrumentation/resteasy2/RestEasyHelper.java index 958816739..38bca27e3 100644 --- a/instrumentation-security/resteasy-2.2/src/main/java/com/nr/instrumentation/security/resteasy2/RestEasyHelper.java +++ b/instrumentation-security/resteasy-2.2/src/main/java/com/newrelic/agent/security/instrumentation/resteasy2/RestEasyHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.resteasy2; +package com.newrelic.agent.security.instrumentation.resteasy2; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; diff --git a/instrumentation-security/resteasy-2.2/src/main/java/org/jboss/resteasy/core/registry/RootSegment_Instrumentation.java b/instrumentation-security/resteasy-2.2/src/main/java/org/jboss/resteasy/core/registry/RootSegment_Instrumentation.java index 08e784376..bbd0be248 100644 --- a/instrumentation-security/resteasy-2.2/src/main/java/org/jboss/resteasy/core/registry/RootSegment_Instrumentation.java +++ b/instrumentation-security/resteasy-2.2/src/main/java/org/jboss/resteasy/core/registry/RootSegment_Instrumentation.java @@ -3,7 +3,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.security.resteasy2.RestEasyHelper; +import com.newrelic.agent.security.instrumentation.resteasy2.RestEasyHelper; import org.jboss.resteasy.core.ResourceInvoker; @Weave(type = MatchType.ExactClass, originalName = "org.jboss.resteasy.core.registry.RootSegment") diff --git a/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/app/CustomerLocatorResource.java b/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/app/CustomerLocatorResource.java new file mode 100644 index 000000000..40cedd202 --- /dev/null +++ b/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/app/CustomerLocatorResource.java @@ -0,0 +1,24 @@ +package com.nr.instrumentation.resteasy2_2.app; + +import javax.ws.rs.Path; + +@Path("/customers") +public class CustomerLocatorResource { + + protected OrdersSubResource ordersSubResource = new OrdersSubResource(); + + @Path("orders") + public Object getOrders() { + return ordersSubResource; + } + +} + +class OrdersSubResource { + protected TestMapping idType = new TestMapping(); + + @Path("getStuff") + public Object getById() { + return idType; + } +} \ No newline at end of file diff --git a/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/app/TestMapping.java b/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/app/TestMapping.java new file mode 100644 index 000000000..1f77bf530 --- /dev/null +++ b/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/app/TestMapping.java @@ -0,0 +1,28 @@ +package com.nr.instrumentation.resteasy2_2.app; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; + +@Path("users") +public class TestMapping { + @GET + public String getIt() { + return "Get it!"; + } + + @PUT + public String putIt() { + return "Put it!"; + } + + @Path("count") + @GET + @Consumes("application/json") + public String pathIt() { + return "path it!"; + } +} + + diff --git a/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/test/APIEndpointTest.java b/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/test/APIEndpointTest.java new file mode 100644 index 000000000..905abac5b --- /dev/null +++ b/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/test/APIEndpointTest.java @@ -0,0 +1,75 @@ +package com.nr.instrumentation.resteasy2_2.test; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.nr.instrumentation.resteasy2_2.app.CustomerLocatorResource; +import com.nr.instrumentation.resteasy2_2.app.TestMapping; +import org.apache.catalina.LifecycleException; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.lang.instrument.UnmodifiableClassException; +import java.net.HttpURLConnection; +import java.util.Iterator; +import java.util.Set; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"com.newrelic.agent.security.instrumentation.resteasy2", "org.jboss.resteasy.core.registry"}) +public class APIEndpointTest { + + private final String handler = TestMapping.class.getName(); + private final String path = "/users"; + + @BeforeClass + public static void startServer() throws LifecycleException, UnmodifiableClassException, ClassNotFoundException { + SecurityInstrumentationTestRunner.instrumentation.retransformClasses(Class.forName("org.jboss.resteasy.logging.Logger")); + TestApplication.startServer(); + } + + @AfterClass + public static void stopServer() { + TestApplication.stopServer(); + } + + @Test + public void testURLMappings() throws IOException { + service(); + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + + Assert.assertEquals(4, mappings.size()); + + Iterator mapping = mappings.iterator(); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("GET", path, handler, mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("GET", path +"/count", handler, mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("PUT", path, handler, mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("*", "/customers/orders/*", CustomerLocatorResource.class.getName(), mapping.next()); + } + + private void assertMapping(String method, String path, String handler, ApplicationURLMapping actualMapping) { + Assert.assertEquals(method, actualMapping.getMethod()); + Assert.assertEquals(path, actualMapping.getPath()); + Assert.assertEquals(handler, actualMapping.getHandler()); + } + + @Trace(dispatcher = true) + private void service() throws IOException { + HttpURLConnection conn = (HttpURLConnection) TestApplication.getEndPoint("users").openConnection(); + conn.connect(); + conn.getResponseCode(); + } +} diff --git a/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/test/TestApplication.java b/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/test/TestApplication.java new file mode 100644 index 000000000..9e1fe44ff --- /dev/null +++ b/instrumentation-security/resteasy-2.2/src/test/java/com/nr/instrumentation/resteasy2_2/test/TestApplication.java @@ -0,0 +1,92 @@ +package com.nr.instrumentation.resteasy2_2.test; + +import com.nr.instrumentation.resteasy2_2.app.CustomerLocatorResource; +import com.nr.instrumentation.resteasy2_2.app.TestMapping; +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher; +import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap; + +import javax.ws.rs.core.Application; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.URL; +import java.util.HashSet; +import java.util.Set; + +public class TestApplication extends Application { + + private final Set classes = new HashSet<>(); + private static Tomcat tomcat; + private static int port; + private static final File tmp = new File("./tmp"); + + public TestApplication() { + classes.add(new CustomerLocatorResource()); + classes.add(new TestMapping()); + } + + @Override + public Set getSingletons() { + return classes; + } + + public static void startServer() throws LifecycleException { + TomcatURLStreamHandlerFactory.disable(); + getRandomPort(); + + tomcat = new Tomcat(); + tomcat.setPort(port); + + String workingDir = tmp.getAbsolutePath(); + + tomcat.setBaseDir(workingDir); + tomcat.getHost().setAppBase(workingDir); + + Context context = tomcat.addContext("/api", workingDir); + context.addApplicationListener(ResteasyBootstrap.class.getName()); + + Tomcat.addServlet(context, "resteasy-servlet", new HttpServletDispatcher()); + context.addParameter("resteasy.scan", "true"); + context.addParameter(Application.class.getName(), TestApplication.class.getName()); + context.addServletMappingDecoded("/*", "resteasy-servlet"); + + final Connector connector = new Connector(); + connector.setPort(port); + + tomcat.getService().addConnector(connector); + tomcat.start(); + } + + public static void stopServer() { + if (tomcat.getServer() != null && tomcat.getServer().getState() != LifecycleState.DESTROYED) { + try { + if (tomcat.getServer().getState() != LifecycleState.STOPPED) { + tomcat.stop(); + } + tomcat.destroy(); + FileUtils.forceDelete(tmp); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public static URL getEndPoint(String path) throws MalformedURLException { + return new URL("http://localhost:" + port + "/api/" + path); + } + private static void getRandomPort() { + try (ServerSocket socket = new ServerSocket(0)){ + port = socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port "+ port); + } + } +} diff --git a/instrumentation-security/resteasy-3/build.gradle b/instrumentation-security/resteasy-3/build.gradle index 7f7c8100f..b0571720a 100644 --- a/instrumentation-security/resteasy-3/build.gradle +++ b/instrumentation-security/resteasy-3/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("org.jboss.resteasy:resteasy-jaxrs:3.0.0.Final") + testImplementation('org.apache.tomcat.embed:tomcat-embed-core:9.0.70') } jar { diff --git a/instrumentation-security/resteasy-3/src/main/java/com/nr/instrumentation/security/resteasy3/RestEasyHelper.java b/instrumentation-security/resteasy-3/src/main/java/com/newrelic/agent/security/instrumentation/resteasy3/RestEasyHelper.java similarity index 96% rename from instrumentation-security/resteasy-3/src/main/java/com/nr/instrumentation/security/resteasy3/RestEasyHelper.java rename to instrumentation-security/resteasy-3/src/main/java/com/newrelic/agent/security/instrumentation/resteasy3/RestEasyHelper.java index b79372d3d..f309a2e86 100644 --- a/instrumentation-security/resteasy-3/src/main/java/com/nr/instrumentation/security/resteasy3/RestEasyHelper.java +++ b/instrumentation-security/resteasy-3/src/main/java/com/newrelic/agent/security/instrumentation/resteasy3/RestEasyHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.resteasy3; +package com.newrelic.agent.security.instrumentation.resteasy3; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; diff --git a/instrumentation-security/resteasy-3/src/main/java/org/jboss/resteasy/core/registry/RootClassNode_Instrumentation.java b/instrumentation-security/resteasy-3/src/main/java/org/jboss/resteasy/core/registry/RootClassNode_Instrumentation.java index 82261a01f..58d8c90e6 100644 --- a/instrumentation-security/resteasy-3/src/main/java/org/jboss/resteasy/core/registry/RootClassNode_Instrumentation.java +++ b/instrumentation-security/resteasy-3/src/main/java/org/jboss/resteasy/core/registry/RootClassNode_Instrumentation.java @@ -3,7 +3,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.security.resteasy3.RestEasyHelper; +import com.newrelic.agent.security.instrumentation.resteasy3.RestEasyHelper; import org.jboss.resteasy.core.ResourceInvoker; @Weave(type = MatchType.ExactClass, originalName = "org.jboss.resteasy.core.registry.RootClassNode") diff --git a/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/app/CustomerLocatorResource.java b/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/app/CustomerLocatorResource.java new file mode 100644 index 000000000..bd29209df --- /dev/null +++ b/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/app/CustomerLocatorResource.java @@ -0,0 +1,24 @@ +package com.nr.instrumentation.resteasy3.app; + +import javax.ws.rs.Path; + +@Path("/customers") +public class CustomerLocatorResource { + + protected OrdersSubResource ordersSubResource = new OrdersSubResource(); + + @Path("orders") + public Object getOrders() { + return ordersSubResource; + } + +} + +class OrdersSubResource { + protected TestMapping idType = new TestMapping(); + + @Path("getStuff") + public Object getById() { + return idType; + } +} \ No newline at end of file diff --git a/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/app/TestMapping.java b/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/app/TestMapping.java new file mode 100644 index 000000000..88a116edd --- /dev/null +++ b/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/app/TestMapping.java @@ -0,0 +1,28 @@ +package com.nr.instrumentation.resteasy3.app; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; + +@Path("users") +public class TestMapping { + @GET + public String getIt() { + return "Get it!"; + } + + @PUT + public String putIt() { + return "Put it!"; + } + + @Path("count") + @GET + @Consumes("application/json") + public String pathIt() { + return "path it!"; + } +} + + diff --git a/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/test/APIEndpointTest.java b/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/test/APIEndpointTest.java new file mode 100644 index 000000000..c4b70025c --- /dev/null +++ b/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/test/APIEndpointTest.java @@ -0,0 +1,74 @@ +package com.nr.instrumentation.resteasy3.test; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.nr.instrumentation.resteasy3.app.CustomerLocatorResource; +import com.nr.instrumentation.resteasy3.app.TestMapping; +import org.apache.catalina.LifecycleException; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.lang.instrument.UnmodifiableClassException; +import java.net.HttpURLConnection; +import java.util.Iterator; +import java.util.Set; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"com.newrelic.agent.security.instrumentation.resteasy3", "org.jboss.resteasy.core.registry"}) +public class APIEndpointTest { + private final String handler = TestMapping.class.getName(); + private final String path = "/users"; + + @BeforeClass + public static void startServer() throws LifecycleException, UnmodifiableClassException, ClassNotFoundException { + SecurityInstrumentationTestRunner.instrumentation.retransformClasses(Class.forName("org.jboss.resteasy.logging.Logger")); + TestApplication.startServer(); + } + + @AfterClass + public static void stopServer() { + TestApplication.stopServer(); + } + + @Test + public void testURLMappings() throws IOException { + service(); + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + + Assert.assertEquals(4, mappings.size()); + + Iterator mapping = mappings.iterator(); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("*", "/customers/orders/*", CustomerLocatorResource.class.getName(), mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("GET", path, handler, mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("GET", path +"/count", handler, mapping.next()); + + Assert.assertTrue(mapping.hasNext()); + assertMapping("PUT", path, handler, mapping.next()); + } + + private void assertMapping(String method, String path, String handler, ApplicationURLMapping actualMapping) { + Assert.assertEquals(method, actualMapping.getMethod()); + Assert.assertEquals(path, actualMapping.getPath()); + Assert.assertEquals(handler, actualMapping.getHandler()); + } + + @Trace(dispatcher = true) + private void service() throws IOException { + HttpURLConnection conn = (HttpURLConnection)TestApplication.getEndPoint("customers/getStuff/users").openConnection(); + conn.connect(); + conn.getResponseCode(); + } +} diff --git a/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/test/TestApplication.java b/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/test/TestApplication.java new file mode 100644 index 000000000..32b9c5a4f --- /dev/null +++ b/instrumentation-security/resteasy-3/src/test/java/com/nr/instrumentation/resteasy3/test/TestApplication.java @@ -0,0 +1,92 @@ +package com.nr.instrumentation.resteasy3.test; + +import com.nr.instrumentation.resteasy3.app.CustomerLocatorResource; +import com.nr.instrumentation.resteasy3.app.TestMapping; +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher; +import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap; + +import javax.ws.rs.core.Application; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.URL; +import java.util.HashSet; +import java.util.Set; + +public class TestApplication extends Application { + + private final Set classes = new HashSet<>(); + private static Tomcat tomcat; + private static int port; + private static final File tmp = new File("./tmp"); + + public TestApplication() { + classes.add(new CustomerLocatorResource()); + classes.add(new TestMapping()); + } + + @Override + public Set getSingletons() { + return classes; + } + + public static void startServer() throws LifecycleException { + TomcatURLStreamHandlerFactory.disable(); + getRandomPort(); + + tomcat = new Tomcat(); + tomcat.setPort(port); + + String workingDir = tmp.getAbsolutePath(); + + tomcat.setBaseDir(workingDir); + tomcat.getHost().setAppBase(workingDir); + + Context context = tomcat.addContext("/api", workingDir); + context.addApplicationListener(ResteasyBootstrap.class.getName()); + + Tomcat.addServlet(context, "resteasy-servlet", new HttpServletDispatcher()); + context.addParameter("resteasy.scan", "true"); + context.addParameter(Application.class.getName(), TestApplication.class.getName()); + context.addServletMappingDecoded("/*", "resteasy-servlet"); + + final Connector connector = new Connector(); + connector.setPort(port); + + tomcat.getService().addConnector(connector); + tomcat.start(); + } + + public static void stopServer() { + if (tomcat.getServer() != null && tomcat.getServer().getState() != LifecycleState.DESTROYED) { + try { + if (tomcat.getServer().getState() != LifecycleState.STOPPED) { + tomcat.stop(); + } + tomcat.destroy(); + FileUtils.forceDelete(tmp); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public static URL getEndPoint(String path) throws MalformedURLException { + return new URL("http://localhost:" + port + "/api/" + path); + } + private static void getRandomPort() { + try (ServerSocket socket = new ServerSocket(0)){ + port = socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port "+ port); + } + } +} diff --git a/instrumentation-security/resteasy-4/src/main/java/com/nr/instrumentation/security/resteasy4/RestEasyHelper.java b/instrumentation-security/resteasy-4/src/main/java/com/newrelic/agent/security/instrumentation/resteasy4/RestEasyHelper.java similarity index 96% rename from instrumentation-security/resteasy-4/src/main/java/com/nr/instrumentation/security/resteasy4/RestEasyHelper.java rename to instrumentation-security/resteasy-4/src/main/java/com/newrelic/agent/security/instrumentation/resteasy4/RestEasyHelper.java index c7230d733..795b5633a 100644 --- a/instrumentation-security/resteasy-4/src/main/java/com/nr/instrumentation/security/resteasy4/RestEasyHelper.java +++ b/instrumentation-security/resteasy-4/src/main/java/com/newrelic/agent/security/instrumentation/resteasy4/RestEasyHelper.java @@ -1,4 +1,4 @@ -package com.nr.instrumentation.security.resteasy4; +package com.newrelic.agent.security.instrumentation.resteasy4; import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; import com.newrelic.api.agent.security.schema.ApplicationURLMapping; diff --git a/instrumentation-security/resteasy-4/src/main/java/org/jboss/resteasy/core/registry/RootClassNode_Instrumentation.java b/instrumentation-security/resteasy-4/src/main/java/org/jboss/resteasy/core/registry/RootClassNode_Instrumentation.java index 2a98b0b9f..e4ffd7440 100644 --- a/instrumentation-security/resteasy-4/src/main/java/org/jboss/resteasy/core/registry/RootClassNode_Instrumentation.java +++ b/instrumentation-security/resteasy-4/src/main/java/org/jboss/resteasy/core/registry/RootClassNode_Instrumentation.java @@ -3,7 +3,7 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.security.resteasy4.RestEasyHelper; +import com.newrelic.agent.security.instrumentation.resteasy4.RestEasyHelper; import org.jboss.resteasy.spi.ResourceInvoker; @Weave(type = MatchType.ExactClass, originalName = "org.jboss.resteasy.core.registry.RootClassNode") diff --git a/instrumentation-security/servlet-2.4/src/main/java/com/newrelic/agent/security/instrumentation/servlet24/HttpServletHelper.java b/instrumentation-security/servlet-2.4/src/main/java/com/newrelic/agent/security/instrumentation/servlet24/HttpServletHelper.java index b6ac6bbf7..aa6ceacd9 100644 --- a/instrumentation-security/servlet-2.4/src/main/java/com/newrelic/agent/security/instrumentation/servlet24/HttpServletHelper.java +++ b/instrumentation-security/servlet-2.4/src/main/java/com/newrelic/agent/security/instrumentation/servlet24/HttpServletHelper.java @@ -51,6 +51,9 @@ public static void processHttpRequestHeader(HttpServletRequest request, HttpRequ } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeader(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; Enumeration headerElements = request.getHeaders(headerKey); @@ -126,12 +129,11 @@ public static void gatherURLMappings(ServletContext servletContext) { for (ServletRegistration servletRegistration : servletRegistrations.values()) { for (String s : servletRegistration.getMappings()) { - URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, s)); + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, s, servletRegistration.getClassName())); } } } catch (Exception e){ - String message = "Instrumentation library: %s , error while getting app endpoints : %s"; - NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(message, SERVLET_2_4, e.getMessage()), e, HttpServletHelper.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, SERVLET_2_4, e.getMessage()), e, HttpServletHelper.class.getName()); } } @@ -149,8 +151,7 @@ else if(path.endsWith(".jsp") || path.endsWith(".jspx") || path.endsWith(".JSP") } } } catch (Exception e){ - String message = "Instrumentation library: %s , error while getting app endpoints : %s"; - NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(message, SERVLET_2_4, e.getMessage()), e, HttpServletHelper.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, SERVLET_2_4, e.getMessage()), e, HttpServletHelper.class.getName()); } } } diff --git a/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/FilterChain_Instrumentation.java b/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/FilterChain_Instrumentation.java index 1caf929a4..2e25a599f 100644 --- a/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/FilterChain_Instrumentation.java +++ b/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/FilterChain_Instrumentation.java @@ -91,7 +91,7 @@ private void preprocessSecurityHook(ServletRequest request, ServletResponse resp private void postProcessSecurityHook(ServletRequest request, ServletResponse response) { try { - if (!NewRelicSecurity.isHookProcessingActive() + if (!NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class)) ) { return; } diff --git a/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/Filter_Instrumentation.java b/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/Filter_Instrumentation.java index 2540b23f0..6acac817c 100644 --- a/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/Filter_Instrumentation.java +++ b/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/Filter_Instrumentation.java @@ -92,7 +92,7 @@ private void preprocessSecurityHook(ServletRequest request, ServletResponse resp private void postProcessSecurityHook(ServletRequest request, ServletResponse response) { try { - if (!NewRelicSecurity.isHookProcessingActive() + if (!NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class)) ) { return; } diff --git a/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/Servlet_Instrumentation.java b/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/Servlet_Instrumentation.java index 39c27b4d6..98f957d36 100644 --- a/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/Servlet_Instrumentation.java +++ b/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/Servlet_Instrumentation.java @@ -97,7 +97,7 @@ private void preprocessSecurityHook(ServletRequest_Instrumentation request, Serv private void postProcessSecurityHook(ServletRequest_Instrumentation request, ServletResponse_Instrumentation response) { try { - if (!NewRelicSecurity.isHookProcessingActive() + if (!NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class)) ) { return; } diff --git a/instrumentation-security/servlet-2.4/src/test/java/com/nr/agent/security/instrumentation/servlet24/HttpServletServer.java b/instrumentation-security/servlet-2.4/src/test/java/com/nr/agent/security/instrumentation/servlet24/HttpServletServer.java index c9ea9821f..e81e85dd9 100644 --- a/instrumentation-security/servlet-2.4/src/test/java/com/nr/agent/security/instrumentation/servlet24/HttpServletServer.java +++ b/instrumentation-security/servlet-2.4/src/test/java/com/nr/agent/security/instrumentation/servlet24/HttpServletServer.java @@ -9,12 +9,19 @@ import org.apache.tomcat.util.http.fileupload.FileUtils; import org.junit.rules.ExternalResource; +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.net.ServerSocket; import java.net.URI; import java.net.URISyntaxException; +import java.util.Collections; +import java.util.Set; @WebServlet("/*") public class HttpServletServer extends ExternalResource { @@ -64,8 +71,16 @@ private void startServer () throws Exception { Context context = server.addContext("", tmp.getAbsolutePath()); + context.addServletContainerInitializer(new ServletContainerInitializer() { + @Override + public void onStartup(Set> c, ServletContext ctx) throws ServletException { + System.out.println("testing..."); + } + }, Collections.emptySet()); + Tomcat.addServlet( context, "servlet" , servlet); context.addServletMappingDecoded("/*","servlet"); + context.addServletMappingDecoded("/test","servlet"); final Connector connector = new Connector(); connector.setPort(port); @@ -84,15 +99,12 @@ private void stop() { if (server.getServer().getState() != LifecycleState.STOPPED) { server.stop(); } - server.destroy(); - FileUtils.forceDelete(tmp); + FileUtils.forceDelete(tmp); } catch (Exception e) { e.printStackTrace(); } - } - tmp = null; } } \ No newline at end of file diff --git a/instrumentation-security/servlet-3.0/build.gradle b/instrumentation-security/servlet-3.0/build.gradle new file mode 100644 index 000000000..1f955ae64 --- /dev/null +++ b/instrumentation-security/servlet-3.0/build.gradle @@ -0,0 +1,24 @@ +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation('jakarta.servlet:jakarta.servlet-api:4.0.4') + + testImplementation('org.apache.tomcat.embed:tomcat-embed-core:9.0.70') +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.servlet-3.0' } +} + +verifyInstrumentation { + passesOnly 'jakarta.servlet:jakarta.servlet-api:[4.0.2,5.0.0-M1)' + passesOnly 'javax.servlet:javax.servlet-api:[0,)' + fails 'javax.servlet:servlet-api:[2.4,)' + exclude 'javax.servlet:servlet-api:2.4.public_draft' +} + +site { + title 'Servlet' + type 'Framework' +} diff --git a/instrumentation-security/servlet-3.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet30/HttpServletHelper.java b/instrumentation-security/servlet-3.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet30/HttpServletHelper.java new file mode 100644 index 000000000..5084a7634 --- /dev/null +++ b/instrumentation-security/servlet-3.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet30/HttpServletHelper.java @@ -0,0 +1,51 @@ +package com.newrelic.agent.security.instrumentation.servlet30; + + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.security.utils.logging.LogLevel; + +import javax.servlet.ServletContext; +import javax.servlet.ServletRegistration; +import java.util.Collection; +import java.util.Map; + +public class HttpServletHelper { + private static final String WILDCARD = "*"; + private static final String SEPARATOR = "/"; + public static final String SERVLET_3_0 = "SERVLET-3.0"; + public static void gatherURLMappings(ServletContext servletContext) { + try { + Map servletRegistrations = servletContext.getServletRegistrations(); + getJSPMappings(servletContext, SEPARATOR); + + for (ServletRegistration servletRegistration : servletRegistrations.values()) { + for (String s : servletRegistration.getMappings()) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, s, servletRegistration.getClassName())); + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, SERVLET_3_0, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } + + public static void getJSPMappings(ServletContext servletContext, String dir) { + try { + if(dir.endsWith(SEPARATOR)){ + Collection resourcePaths = servletContext.getResourcePaths(dir); + for (String path : resourcePaths) { + if(path.endsWith(SEPARATOR)) { + getJSPMappings(servletContext, path); + } + else if(path.endsWith(".jsp") || path.endsWith(".jspx") || path.endsWith(".JSP") || path.endsWith(".JSPX")) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, path)); + } + } + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, SERVLET_3_0, e.getMessage()), e, HttpServletHelper.class.getName()); + } + } +} diff --git a/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/ServletContainerInitializer_Instrumentation.java b/instrumentation-security/servlet-3.0/src/main/java/javax/servlet/ServletContainerInitializer_Instrumentation.java similarity index 61% rename from instrumentation-security/servlet-2.4/src/main/java/javax/servlet/ServletContainerInitializer_Instrumentation.java rename to instrumentation-security/servlet-3.0/src/main/java/javax/servlet/ServletContainerInitializer_Instrumentation.java index 90cd830ac..5956afcad 100644 --- a/instrumentation-security/servlet-2.4/src/main/java/javax/servlet/ServletContainerInitializer_Instrumentation.java +++ b/instrumentation-security/servlet-3.0/src/main/java/javax/servlet/ServletContainerInitializer_Instrumentation.java @@ -1,11 +1,12 @@ package javax.servlet; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.agent.security.instrumentation.servlet24.HttpServletHelper; - +import com.newrelic.agent.security.instrumentation.servlet30.HttpServletHelper; import java.util.Set; -//@Weave(type = MatchType.Interface, originalName = "javax.servlet.ServletContainerInitializer") +@Weave(type = MatchType.Interface, originalName = "javax.servlet.ServletContainerInitializer") public class ServletContainerInitializer_Instrumentation { public void onStartup(Set> c, ServletContext ctx) throws ServletException { try { diff --git a/instrumentation-security/servlet-3.0/src/test/java/com/nr/agent/security/instrumentation/servlet30/ApiEndpointTest.java b/instrumentation-security/servlet-3.0/src/test/java/com/nr/agent/security/instrumentation/servlet30/ApiEndpointTest.java new file mode 100644 index 000000000..ee2b16e78 --- /dev/null +++ b/instrumentation-security/servlet-3.0/src/test/java/com/nr/agent/security/instrumentation/servlet30/ApiEndpointTest.java @@ -0,0 +1,35 @@ +package com.nr.agent.security.instrumentation.servlet30; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +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 org.apache.catalina.servlets.DefaultServlet; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Iterator; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "javax.servlet", "com.newrelic.agent.security.instrumentation.servlet30" }) +public class ApiEndpointTest { + @ClassRule + public static HttpServletServer server = new HttpServletServer(); + + @Test + public void testURLMappings() { + String handler = MyServlet.class.getName(); + String method = "*"; + Iterator mappings = URLMappingsHelper.getApplicationURLMappings().iterator(); + + Assert.assertTrue("URL Mappings", mappings.hasNext()); + ApplicationURLMapping mapping1 = mappings.next(); + Assert.assertEquals("URL Mappings", new ApplicationURLMapping(method, "/*", handler), mapping1); + + Assert.assertTrue("URL Mappings", mappings.hasNext()); + ApplicationURLMapping mapping2 = mappings.next(); + Assert.assertEquals("URL Mappings", new ApplicationURLMapping(method, "/test", handler), mapping2); + } +} diff --git a/instrumentation-security/servlet-3.0/src/test/java/com/nr/agent/security/instrumentation/servlet30/HttpServletServer.java b/instrumentation-security/servlet-3.0/src/test/java/com/nr/agent/security/instrumentation/servlet30/HttpServletServer.java new file mode 100644 index 000000000..c577787a3 --- /dev/null +++ b/instrumentation-security/servlet-3.0/src/test/java/com/nr/agent/security/instrumentation/servlet30/HttpServletServer.java @@ -0,0 +1,100 @@ +package com.nr.agent.security.instrumentation.servlet30; + +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.servlets.DefaultServlet; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.junit.rules.ExternalResource; + +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Collections; +import java.util.Set; + +@WebServlet("/*") +public class HttpServletServer extends ExternalResource { + + private final int port; + private Tomcat server; + private File tmp; + public HttpServletServer() { + this.port = getRandomPort(); + } + + @Override + protected void before() throws Throwable { + startServer(); + } + + @Override + protected void after() { + stop(); + } + + private static int getRandomPort() { + try (ServerSocket socket = new ServerSocket(0);){ + return socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port "); + } + } + + private void startServer () throws Exception { + TomcatURLStreamHandlerFactory.disable(); + + server = new Tomcat(); + server.setPort(port); + + tmp = new File("./tmp"); + server.setBaseDir(tmp.getAbsolutePath()); + + Context context = server.addContext("", tmp.getAbsolutePath()); + context.addServletContainerInitializer(new ServletContainerInitializer() { + @Override + public void onStartup(Set> c, ServletContext ctx){ + System.out.println("testing..."); + } + }, Collections.emptySet()); + + Tomcat.addServlet(context, "servlet", new MyServlet()); + context.addServletMappingDecoded("/*","servlet"); + context.addServletMappingDecoded("/test","servlet"); + + final Connector connector = new Connector(); + connector.setPort(port); + server.getService().addConnector(connector); + + server.start(); + } + + private void stop() { + if (server.getServer() != null && server.getServer().getState() != LifecycleState.DESTROYED) { + try { + if (server.getServer().getState() != LifecycleState.STOPPED) { + server.stop(); + } + server.destroy(); + FileUtils.forceDelete(tmp); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} +class MyServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } +} \ No newline at end of file diff --git a/instrumentation-security/servlet-5.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet5/HttpServletHelper.java b/instrumentation-security/servlet-5.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet5/HttpServletHelper.java index aa32dba10..8c6278daa 100644 --- a/instrumentation-security/servlet-5.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet5/HttpServletHelper.java +++ b/instrumentation-security/servlet-5.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet5/HttpServletHelper.java @@ -50,6 +50,9 @@ public static void processHttpRequestHeader(HttpServletRequest request, HttpRequ } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeader(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; @@ -126,7 +129,7 @@ public static void gatherURLMappings(ServletContext servletContext) { for (ServletRegistration servletRegistration : servletRegistrations.values()) { for (String s : servletRegistration.getMappings()) { - URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, s)); + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, s, servletRegistration.getClassName())); } } } catch (Exception e){ diff --git a/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/FilterChain_Instrumentation.java b/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/FilterChain_Instrumentation.java index e3414be3c..9893b7ccf 100644 --- a/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/FilterChain_Instrumentation.java +++ b/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/FilterChain_Instrumentation.java @@ -90,7 +90,7 @@ private void preprocessSecurityHook(ServletRequest request, ServletResponse resp private void postProcessSecurityHook(ServletRequest request, ServletResponse response) { try { - if (!NewRelicSecurity.isHookProcessingActive() + if (!NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class)) ) { return; } diff --git a/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/Filter_Instrumentation.java b/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/Filter_Instrumentation.java index 54c81187e..cf4ddbeb9 100644 --- a/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/Filter_Instrumentation.java +++ b/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/Filter_Instrumentation.java @@ -92,7 +92,7 @@ private void preprocessSecurityHook(ServletRequest request, ServletResponse resp private void postProcessSecurityHook(ServletRequest request, ServletResponse response) { try { - if (!NewRelicSecurity.isHookProcessingActive() + if (!NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class)) ) { return; } diff --git a/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/ServletContainerInitializer_Instrumentation.java b/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/ServletContainerInitializer_Instrumentation.java index 0e3a888bc..65904cf96 100644 --- a/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/ServletContainerInitializer_Instrumentation.java +++ b/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/ServletContainerInitializer_Instrumentation.java @@ -1,10 +1,12 @@ package jakarta.servlet; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.newrelic.agent.security.instrumentation.servlet5.HttpServletHelper; import java.util.Set; -//@Weave(type = MatchType.Interface, originalName = "jakarta.servlet.ServletContainerInitializer") +@Weave(type = MatchType.Interface, originalName = "jakarta.servlet.ServletContainerInitializer") public class ServletContainerInitializer_Instrumentation { public void onStartup(Set> var1, ServletContext var2) throws ServletException { try { diff --git a/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/Servlet_Instrumentation.java b/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/Servlet_Instrumentation.java index 4ec1c91bd..01da21d98 100644 --- a/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/Servlet_Instrumentation.java +++ b/instrumentation-security/servlet-5.0/src/main/java/jakarta/servlet/Servlet_Instrumentation.java @@ -96,7 +96,7 @@ private void preprocessSecurityHook(ServletRequest_Instrumentation request, Serv private void postProcessSecurityHook(ServletRequest_Instrumentation request, ServletResponse_Instrumentation response) { try { - if (!NewRelicSecurity.isHookProcessingActive() + if (!NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class)) ) { return; } diff --git a/instrumentation-security/servlet-5.0/src/test/java/com/nr/agent/security/instrumentation/servlet5/HttpServletServer.java b/instrumentation-security/servlet-5.0/src/test/java/com/nr/agent/security/instrumentation/servlet5/HttpServletServer.java index 958f3eb0a..e45ae3c5d 100644 --- a/instrumentation-security/servlet-5.0/src/test/java/com/nr/agent/security/instrumentation/servlet5/HttpServletServer.java +++ b/instrumentation-security/servlet-5.0/src/test/java/com/nr/agent/security/instrumentation/servlet5/HttpServletServer.java @@ -1,5 +1,8 @@ package com.nr.agent.security.instrumentation.servlet5; +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import org.apache.catalina.Context; import org.apache.catalina.startup.Tomcat; @@ -14,6 +17,8 @@ import java.net.ServerSocket; import java.net.URI; import java.net.URISyntaxException; +import java.util.Collections; +import java.util.Set; @WebServlet("/*") public class HttpServletServer extends ExternalResource { @@ -59,8 +64,16 @@ private void startServer () throws Exception { server.setBaseDir(tmp.getAbsolutePath()); Context context = server.addContext("", tmp.getAbsolutePath()); + context.addServletContainerInitializer(new ServletContainerInitializer() { + @Override + public void onStartup(Set> c, ServletContext ctx) throws ServletException { + System.out.println("testing..."); + } + }, Collections.emptySet()); + Tomcat.addServlet( context, "servlet" , servlet); context.addServletMappingDecoded("/*","servlet"); + context.addServletMappingDecoded("/test","servlet"); final Connector connector = new Connector(); connector.setPort(port); diff --git a/instrumentation-security/servlet-5.0/src/test/java/com/nr/agent/security/instrumentation/servlet5/ServletContainerInitializerTest.java b/instrumentation-security/servlet-5.0/src/test/java/com/nr/agent/security/instrumentation/servlet5/ServletContainerInitializerTest.java new file mode 100644 index 000000000..125c81fba --- /dev/null +++ b/instrumentation-security/servlet-5.0/src/test/java/com/nr/agent/security/instrumentation/servlet5/ServletContainerInitializerTest.java @@ -0,0 +1,34 @@ +package com.nr.agent.security.instrumentation.servlet5; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +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 org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Iterator; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "jakarta.servlet", "com.newrelic.agent.security.instrumentation.servlet5" }) +public class ServletContainerInitializerTest { + @ClassRule + public static HttpServletServer server = new HttpServletServer(); + + @Test + public void testURLMappings() { + String handler = "com.nr.agent.security.instrumentation.servlet5.HttpTestServlet"; + String method = "*"; + Iterator mappings = URLMappingsHelper.getApplicationURLMappings().iterator(); + + Assert.assertTrue("URL Mappings", mappings.hasNext()); + ApplicationURLMapping mapping1 = mappings.next(); + Assert.assertEquals("URL Mappings", new ApplicationURLMapping(method, "/*", handler), mapping1); + + Assert.assertTrue("URL Mappings", mappings.hasNext()); + ApplicationURLMapping mapping2 = mappings.next(); + Assert.assertEquals("URL Mappings", new ApplicationURLMapping(method, "/test", handler), mapping2); + } +} diff --git a/instrumentation-security/servlet-6.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet6/HttpServletHelper.java b/instrumentation-security/servlet-6.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet6/HttpServletHelper.java index 479e09e03..847d8e440 100644 --- a/instrumentation-security/servlet-6.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet6/HttpServletHelper.java +++ b/instrumentation-security/servlet-6.0/src/main/java/com/newrelic/agent/security/instrumentation/servlet6/HttpServletHelper.java @@ -50,6 +50,9 @@ public static void processHttpRequestHeader(HttpServletRequest request, HttpRequ } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, request.getHeader(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; @@ -126,12 +129,11 @@ public static void gatherURLMappings(ServletContext servletContext) { for (ServletRegistration servletRegistration : servletRegistrations.values()) { for (String s : servletRegistration.getMappings()) { - URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, s)); + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, s, servletRegistration.getClassName())); } } } catch (Exception e){ - String message = "Instrumentation library: %s , error while getting app endpoints : %s"; - NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(message, SERVLET_6_0, e.getMessage()), e, HttpServletHelper.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, SERVLET_6_0, e.getMessage()), e, HttpServletHelper.class.getName()); } } @@ -149,8 +151,7 @@ else if(path.endsWith(".jsp") || path.endsWith(".jspx") || path.endsWith(".JSP") } } } catch (Exception e){ - String message = "Instrumentation library: %s , error while getting app endpoints : %s"; - NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(message, SERVLET_6_0, e.getMessage()), e, HttpServletHelper.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, SERVLET_6_0, e.getMessage()), e, HttpServletHelper.class.getName()); } } } diff --git a/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/FilterChain_Instrumentation.java b/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/FilterChain_Instrumentation.java index ba39efeb2..c7f7a3c45 100644 --- a/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/FilterChain_Instrumentation.java +++ b/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/FilterChain_Instrumentation.java @@ -91,7 +91,7 @@ private void preprocessSecurityHook(ServletRequest request, ServletResponse resp private void postProcessSecurityHook(ServletRequest request, ServletResponse response) { try { - if (!NewRelicSecurity.isHookProcessingActive() + if (!NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class)) ) { return; } diff --git a/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/Filter_Instrumentation.java b/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/Filter_Instrumentation.java index 59d8e0f63..d1d36a341 100644 --- a/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/Filter_Instrumentation.java +++ b/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/Filter_Instrumentation.java @@ -92,7 +92,7 @@ private void preprocessSecurityHook(ServletRequest request, ServletResponse resp private void postProcessSecurityHook(ServletRequest request, ServletResponse response) { try { - if (!NewRelicSecurity.isHookProcessingActive() + if (!NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class)) ) { return; } diff --git a/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/ServletContainerInitializer_Instrumentation.java b/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/ServletContainerInitializer_Instrumentation.java index fd0434a5d..099d3afe5 100644 --- a/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/ServletContainerInitializer_Instrumentation.java +++ b/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/ServletContainerInitializer_Instrumentation.java @@ -1,10 +1,12 @@ package jakarta.servlet; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.newrelic.agent.security.instrumentation.servlet6.HttpServletHelper; import java.util.Set; -//@Weave(type = MatchType.Interface, originalName = "jakarta.servlet.ServletContainerInitializer") +@Weave(type = MatchType.Interface, originalName = "jakarta.servlet.ServletContainerInitializer") public class ServletContainerInitializer_Instrumentation { public void onStartup(Set> var1, ServletContext var2) { try { diff --git a/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/Servlet_Instrumentation.java b/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/Servlet_Instrumentation.java index 46bc01e24..cc01075f3 100644 --- a/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/Servlet_Instrumentation.java +++ b/instrumentation-security/servlet-6.0/src/main/java/jakarta/servlet/Servlet_Instrumentation.java @@ -96,7 +96,7 @@ private void preprocessSecurityHook(ServletRequest_Instrumentation request, Serv private void postProcessSecurityHook(ServletRequest_Instrumentation request, ServletResponse_Instrumentation response) { try { - if (!NewRelicSecurity.isHookProcessingActive() + if (!NewRelicSecurity.isHookProcessingActive() || Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute("RXSS_PROCESSED", Boolean.class)) ) { return; } diff --git a/instrumentation-security/servlet-6.0/src/test/java/com/nr/agent/security/instrumentation/servlet6/HttpServletServer.java b/instrumentation-security/servlet-6.0/src/test/java/com/nr/agent/security/instrumentation/servlet6/HttpServletServer.java index 911f76bbb..f725baa32 100644 --- a/instrumentation-security/servlet-6.0/src/test/java/com/nr/agent/security/instrumentation/servlet6/HttpServletServer.java +++ b/instrumentation-security/servlet-6.0/src/test/java/com/nr/agent/security/instrumentation/servlet6/HttpServletServer.java @@ -1,5 +1,8 @@ package com.nr.agent.security.instrumentation.servlet6; +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import org.apache.catalina.Context; import org.apache.catalina.LifecycleState; @@ -15,6 +18,8 @@ import java.net.ServerSocket; import java.net.URI; import java.net.URISyntaxException; +import java.util.Collections; +import java.util.Set; @WebServlet("/*") public class HttpServletServer extends ExternalResource { @@ -59,8 +64,16 @@ private void startServer () throws Exception { server.setBaseDir(tmp.getAbsolutePath()); Context context = server.addContext("", tmp.getAbsolutePath()); + context.addServletContainerInitializer(new ServletContainerInitializer() { + @Override + public void onStartup(Set> c, ServletContext ctx) throws ServletException { + System.out.println("testing..."); + } + }, Collections.emptySet()); + Tomcat.addServlet( context, "servlet" , servlet); context.addServletMappingDecoded("/*","servlet"); + context.addServletMappingDecoded("/test","servlet"); final Connector connector = new Connector(); connector.setPort(port); diff --git a/instrumentation-security/servlet-6.0/src/test/java/com/nr/agent/security/instrumentation/servlet6/ServletContainerInitializerTest.java b/instrumentation-security/servlet-6.0/src/test/java/com/nr/agent/security/instrumentation/servlet6/ServletContainerInitializerTest.java new file mode 100644 index 000000000..04c224897 --- /dev/null +++ b/instrumentation-security/servlet-6.0/src/test/java/com/nr/agent/security/instrumentation/servlet6/ServletContainerInitializerTest.java @@ -0,0 +1,34 @@ +package com.nr.agent.security.instrumentation.servlet6; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +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 org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Iterator; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "jakarta.servlet", "com.newrelic.agent.security.instrumentation.servlet6" }) +public class ServletContainerInitializerTest { + @ClassRule + public static HttpServletServer server = new HttpServletServer(); + + @Test + public void testURLMappings() { + String handler = "com.nr.agent.security.instrumentation.servlet6.HttpTestServlet"; + String method = "*"; + Iterator mappings = URLMappingsHelper.getApplicationURLMappings().iterator(); + + Assert.assertTrue("URL Mappings", mappings.hasNext()); + ApplicationURLMapping mapping1 = mappings.next(); + Assert.assertEquals("URL Mappings", new ApplicationURLMapping(method, "/*", handler), mapping1); + + Assert.assertTrue("URL Mappings", mappings.hasNext()); + ApplicationURLMapping mapping2 = mappings.next(); + Assert.assertEquals("URL Mappings", new ApplicationURLMapping(method, "/test", handler), mapping2); + } +} diff --git a/instrumentation-security/spray-can-1.3.1/build.gradle b/instrumentation-security/spray-can-1.3.1/build.gradle new file mode 100644 index 000000000..777a3cbb1 --- /dev/null +++ b/instrumentation-security/spray-can-1.3.1/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'scala' + +isScalaProjectEnabled(project, "scala-2.10") + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.spray-can-1.3.1' } +} + +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("org.scala-lang:scala-library:2.10.7") + implementation("io.spray:spray-can_2.10:1.3.3") + implementation("com.typesafe.akka:akka-actor_2.10:2.3.14") +} + +verifyInstrumentation { + passesOnly('io.spray:spray-can_2.11:[1.3.1,)'){ + implementation("com.typesafe.akka:akka-actor_2.11:2.3.14") + } + passesOnly('io.spray:spray-can_2.10:[1.3.1,)'){ + implementation("com.typesafe.akka:akka-actor_2.10:2.3.14") + } +} + +site { + title 'Spray-can' + type 'Messaging' +} \ No newline at end of file diff --git a/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/Http_Instrumentation.java b/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/Http_Instrumentation.java new file mode 100644 index 000000000..8bde104f4 --- /dev/null +++ b/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/Http_Instrumentation.java @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package spray.can; + +import java.net.InetSocketAddress; + +import akka.actor.ActorRef; +import akka.io.Inet; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.weaver.Weave; +import scala.Option; +import scala.collection.immutable.Traversable; +import spray.can.server.ServerSettings; +import spray.io.ServerSSLEngineProvider; + +@Weave(originalName = "spray.can.Http") +public class Http_Instrumentation { + + @Weave(originalName = "spray.can.Http$Bind") + public static class Bind { + + public Bind(final ActorRef listener, final InetSocketAddress endpoint, final int backlog, + final Traversable options, final Option settings, + final ServerSSLEngineProvider sslEngineProvider) { + NewRelicSecurity.getAgent().setApplicationConnectionConfig(endpoint.getPort(), "http"); + } + + } + +} diff --git a/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/SprayHttpUtils.java b/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/SprayHttpUtils.java new file mode 100644 index 000000000..c1a134fd7 --- /dev/null +++ b/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/SprayHttpUtils.java @@ -0,0 +1,154 @@ +package spray.can; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; +import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.schema.AgentMetaData; +import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; +import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.api.agent.security.schema.policy.AgentPolicy; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import scala.collection.Iterator; +import scala.collection.immutable.List; +import spray.http.*; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Map; + +public class SprayHttpUtils { + + public static final String QUESTION_MARK = "?"; + + private static final String X_FORWARDED_FOR = "x-forwarded-for"; + public static final String SPRAY_CAN_1_3_1 = "SPRAY-CAN-1.3.1"; + + public static String getNrSecCustomAttribName() { + return "SPRAY-CAN-" + Thread.currentThread().getId(); + } + public static String getNrSecCustomAttribNameForResponse() { + return "SPRAY-CAN-RXSS" + Thread.currentThread().getId(); + } + + public static void preProcessRequestHook(HttpRequest request) { + try { + if (!NewRelicSecurity.isHookProcessingActive()) { + return; + } + SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); + + com.newrelic.api.agent.security.schema.HttpRequest securityRequest = securityMetaData.getRequest(); + if (securityRequest.isRequestParsed()) { + return; + } + + AgentMetaData securityAgentMetaData = securityMetaData.getMetaData(); + securityRequest.setMethod(request.method().name()); + securityRequest.setProtocol(request.uri().scheme()); + securityRequest.setUrl(processURL(request.uri())); + securityRequest.setServerPort(request.uri().effectivePort()); + processHttpRequestHeader(request.headers(), securityRequest); + + securityMetaData.setTracingHeaderValue(getTraceHeader(securityRequest.getHeaders())); + + if (!request.entity().isEmpty()) { + if (request.entity() instanceof HttpEntity.NonEmpty) { + securityRequest.setContentType(((HttpEntity.NonEmpty) request.entity()).contentType().value()); + } + securityRequest.setBody(new StringBuilder(request.entity().data().asString(StandardCharsets.UTF_8))); + } + + StackTraceElement[] trace = Thread.currentThread().getStackTrace(); + securityMetaData.getMetaData().setServiceTrace(Arrays.copyOfRange(trace, 2, trace.length)); + securityRequest.setRequestParsed(true); + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, SPRAY_CAN_1_3_1, e.getMessage()), e, SprayHttpUtils.class.getName()); + } + } + + public static String getTraceHeader(Map headers) { + String data = StringUtils.EMPTY; + if (headers.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER) || headers.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase())) { + data = headers.get(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER); + if (data == null || data.trim().isEmpty()) { + data = headers.get(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase()); + } + } + return data; + } + + private static void processHttpRequestHeader(List headers, com.newrelic.api.agent.security.schema.HttpRequest securityRequest) { + Iterator headerIterator = headers.iterator(); + while (headerIterator.hasNext()){ + HttpHeader element = headerIterator.next(); + String headerKey = element.lowercaseName(); + String headerValue = element.value(); + boolean takeNextValue = false; + AgentPolicy agentPolicy = NewRelicSecurity.getAgent().getCurrentPolicy(); + AgentMetaData agentMetaData = NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData(); + if (agentPolicy != null + && agentPolicy.getProtectionMode().getEnabled() + && agentPolicy.getProtectionMode().getIpBlocking().getEnabled() + && agentPolicy.getProtectionMode().getIpBlocking().getIpDetectViaXFF() + && X_FORWARDED_FOR.equals(headerKey)) { + takeNextValue = true; + } else if (ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID.equals(headerKey)) { + // TODO: May think of removing this intermediate obj and directly create K2 Identifier. + NewRelicSecurity.getAgent().getSecurityMetaData().setFuzzRequestIdentifier(ServletHelper.parseFuzzRequestIdentifierHeader(headerValue)); + } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, headerValue); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); + } + if (takeNextValue) { + agentMetaData.setClientDetectedFromXFF(true); + securityRequest.setClientIP(headerValue); + agentMetaData.getIps() + .add(securityRequest.getClientIP()); + } + securityRequest.getHeaders().put(headerKey, headerValue); + } + } + + private static String processURL(Uri uri) { + String path = uri.path().toString(); + String queryString = StringUtils.substringAfter(uri.toString(), QUESTION_MARK); + if(StringUtils.isBlank(queryString)){ + return path; + } else { + return path + QUESTION_MARK + queryString; + } + } + + public static void postProcessSecurityHook(HttpResponse httpResponse, String className, String methodName) { + try { + if (!NewRelicSecurity.isHookProcessingActive() + ) { + return; + } + //Add request URI hash to low severity event filter + LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest()); + + if(!ServletHelper.isResponseContentTypeExcluded(NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().getResponseContentType())) { + RXSSOperation rxssOperation = new RXSSOperation(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest(), + NewRelicSecurity.getAgent().getSecurityMetaData().getResponse(), + className, methodName); + NewRelicSecurity.getAgent().registerOperation(rxssOperation); + } + ServletHelper.tmpFileCleanUp(NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getTempFiles()); + } catch (Throwable e) { + if (e instanceof NewRelicSecurityException) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, SPRAY_CAN_1_3_1, e.getMessage()), e, SprayHttpUtils.class.getName()); + throw e; + } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, SPRAY_CAN_1_3_1, e.getMessage()), e, SprayHttpUtils.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, SPRAY_CAN_1_3_1, e.getMessage()), e, SprayHttpUtils.class.getName()); + } + } +} diff --git a/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/rendering/ResponseRendering_Instrumentation.java b/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/rendering/ResponseRendering_Instrumentation.java new file mode 100644 index 000000000..ccfccafa1 --- /dev/null +++ b/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/rendering/ResponseRendering_Instrumentation.java @@ -0,0 +1,44 @@ +package spray.can.rendering; + +import akka.event.LoggingAdapter; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import spray.can.SprayHttpUtils; +import spray.http.HttpEntity; +import spray.http.HttpResponse; +import spray.http.Rendering; + +import java.nio.charset.StandardCharsets; + +@Weave(originalName = "spray.can.rendering.ResponseRenderingComponent$class") +public class ResponseRendering_Instrumentation { + private static boolean renderResponse$1(ResponseRenderingComponent component, HttpResponse response, + Rendering rendering, ResponsePartRenderingContext context, LoggingAdapter adapter) { + + boolean isLockAcquired = GenericHelper.acquireLockIfPossible(SprayHttpUtils.getNrSecCustomAttribNameForResponse()); + try { + if (isLockAcquired && response.entity().nonEmpty()) { + NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseBody(new StringBuilder(response.entity().data().asString(StandardCharsets.UTF_8))); + if (response.entity() instanceof HttpEntity.NonEmpty) { + NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseContentType(((HttpEntity.NonEmpty) response.entity()).contentType().value()); + } + SprayHttpUtils.postProcessSecurityHook(response, ResponseRendering_Instrumentation.class.getName(), "renderResponse$1"); + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_PARSING_HTTP_RESPONSE, SprayHttpUtils.SPRAY_CAN_1_3_1, e.getMessage()), e, ResponseRendering_Instrumentation.class.getName()); + } + boolean result; + try { + result = Weaver.callOriginal(); + } finally { + if(isLockAcquired){ + GenericHelper.releaseLock(SprayHttpUtils.getNrSecCustomAttribNameForResponse()); + } + } + return result; + } + +} diff --git a/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/server/ServerFrontend_Instrumentation.java b/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/server/ServerFrontend_Instrumentation.java new file mode 100644 index 000000000..6dd9a3fd5 --- /dev/null +++ b/instrumentation-security/spray-can-1.3.1/src/main/scala/spray/can/server/ServerFrontend_Instrumentation.java @@ -0,0 +1,34 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package spray.can.server; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import spray.can.SprayHttpUtils; +import spray.http.HttpRequest; + +@Weave(originalName = "spray.can.server.ServerFrontend$$anon$2$$anon$1") +public class ServerFrontend_Instrumentation { + + public void spray$can$server$ServerFrontend$$anon$$anon$$openNewRequest(final HttpRequest request, + final boolean closeAfterResponseCompletion, final RequestState state) { + boolean isLockAcquired = GenericHelper.acquireLockIfPossible(SprayHttpUtils.getNrSecCustomAttribName()); + if (isLockAcquired) { + SprayHttpUtils.preProcessRequestHook(request); + } + try { + Weaver.callOriginal(); + } finally { + if(isLockAcquired){ + GenericHelper.releaseLock(SprayHttpUtils.getNrSecCustomAttribName()); + } + } + } +} diff --git a/instrumentation-security/spray-client/build.gradle b/instrumentation-security/spray-client/build.gradle new file mode 100644 index 000000000..f71ab7b45 --- /dev/null +++ b/instrumentation-security/spray-client/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'scala' + +isScalaProjectEnabled(project, "scala-2.10") + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.spray-client' } +} + +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("org.scala-lang:scala-library:2.10.7") + implementation("io.spray:spray-client_2.10:1.3.3") + testImplementation("com.typesafe.akka:akka-actor_2.10:2.3.14") + testImplementation("io.spray:spray-can_2.10:1.3.3") +} + +verifyInstrumentation { + passesOnly('io.spray:spray-client_2.11:[1.3.1,)') + passesOnly('io.spray:spray-client_2.10:[1.3.1,)') +} + +site { + title 'Spray-can client' + type 'Messaging' +} \ No newline at end of file diff --git a/instrumentation-security/spray-client/src/main/scala/com/newrelic/agent/security/instrumentation/spray/client/OutboundRequest.scala b/instrumentation-security/spray-client/src/main/scala/com/newrelic/agent/security/instrumentation/spray/client/OutboundRequest.scala new file mode 100644 index 000000000..fd0aaf790 --- /dev/null +++ b/instrumentation-security/spray-client/src/main/scala/com/newrelic/agent/security/instrumentation/spray/client/OutboundRequest.scala @@ -0,0 +1,25 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.newrelic.agent.security.instrumentation.spray.client + +import spray.http.{HttpHeaders, HttpRequest} + +/** + * Spray's HttpRequest is immutable so we have to create a copy with the new headers. + */ + +class OutboundRequest(request: HttpRequest) { + private var req: HttpRequest = request + + def setHeader(key: String, value: String): Unit = { + req = request.withHeaders(req.headers ++ List(HttpHeaders.RawHeader(key, value))) + } + def getRequest: HttpRequest = { + req + } +} \ No newline at end of file diff --git a/instrumentation-security/spray-client/src/main/scala/com/newrelic/agent/security/instrumentation/spray/client/SprayUtils.java b/instrumentation-security/spray-client/src/main/scala/com/newrelic/agent/security/instrumentation/spray/client/SprayUtils.java new file mode 100644 index 000000000..81344b72a --- /dev/null +++ b/instrumentation-security/spray-client/src/main/scala/com/newrelic/agent/security/instrumentation/spray/client/SprayUtils.java @@ -0,0 +1,10 @@ +package com.newrelic.agent.security.instrumentation.spray.client; + +public class SprayUtils { + private static final String NR_SEC_OPERATION_LOCK = "OPERATION_LOCK_SPRAY_CAN_CLIENT-"; + public static final String METHOD_SEND_RECEIVE = "sendReceive"; + public static final String SPRAY_CLIENT = "SPRAY-CLIENT"; + public static String getNrSecCustomAttribName() { + return NR_SEC_OPERATION_LOCK + Thread.currentThread().getId(); + } +} diff --git a/instrumentation-security/spray-client/src/main/scala/spray/client/SendReceive_Instrumentation.java b/instrumentation-security/spray-client/src/main/scala/spray/client/SendReceive_Instrumentation.java new file mode 100644 index 000000000..96f6ac28f --- /dev/null +++ b/instrumentation-security/spray-client/src/main/scala/spray/client/SendReceive_Instrumentation.java @@ -0,0 +1,134 @@ +package spray.client; + +import com.newrelic.agent.security.instrumentation.spray.client.OutboundRequest; +import com.newrelic.agent.security.instrumentation.spray.client.SprayUtils; +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.schema.AbstractOperation; +import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; +import com.newrelic.api.agent.security.schema.operation.SSRFOperation; +import com.newrelic.api.agent.security.utils.SSRFUtils; +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 scala.concurrent.Future; +import spray.http.HttpRequest; +import spray.http.HttpResponse; + +import java.net.URI; + +@Weave(type = MatchType.Interface, originalName = "spray.client.pipelining$$anonfun$sendReceive$1") +public class SendReceive_Instrumentation { + + public final Future apply(HttpRequest request) { + boolean isLockAcquired = acquireLockIfPossible(); + AbstractOperation operation = null; + // Preprocess Phase + if (isLockAcquired) { + operation = preprocessSecurityHook(request); + request = addSecurityHeaders(request, operation); + } + + Future returnCode; + try { + returnCode = Weaver.callOriginal(); + } finally { + if (isLockAcquired) { + releaseLock(); + } + } + registerExitOperation(isLockAcquired, operation); + return returnCode; + } + + private HttpRequest addSecurityHeaders(HttpRequest request, AbstractOperation operation) { + OutboundRequest outboundRequest = new OutboundRequest(request); + if (operation!=null) { + SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); + String iastHeader = NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getRaw(); + if (iastHeader != null && !iastHeader.trim().isEmpty()) { + outboundRequest.setHeader(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID, iastHeader); + } + String csecParentId = securityMetaData.getCustomAttribute(GenericHelper.CSEC_PARENT_ID, String.class); + if(StringUtils.isNotBlank(csecParentId)){ + outboundRequest.setHeader(GenericHelper.CSEC_PARENT_ID, csecParentId); + } + + try { + NewRelicSecurity.getAgent().registerOperation(operation); + } finally { + if (operation.getApiID() != null && !operation.getApiID().trim().isEmpty() && + operation.getExecutionId() != null && !operation.getExecutionId().trim().isEmpty()) { + // Add Security distributed tracing header + outboundRequest.setHeader(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER, + SSRFUtils.generateTracingHeaderValue(securityMetaData.getTracingHeaderValue(), + operation.getApiID(), operation.getExecutionId(), + NewRelicSecurity.getAgent().getAgentUUID())); + } + } + } + return outboundRequest.getRequest(); + } + private void releaseLock() { + try { + GenericHelper.releaseLock(SprayUtils.getNrSecCustomAttribName()); + } catch (Throwable ignored) { + } + } + + private boolean acquireLockIfPossible() { + try { + return GenericHelper.acquireLockIfPossible(SprayUtils.getNrSecCustomAttribName()); + } catch (Throwable ignored) { + } + return false; + } + + private AbstractOperation preprocessSecurityHook(HttpRequest httpRequest) { + try { + SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); + if (!NewRelicSecurity.isHookProcessingActive() || securityMetaData.getRequest().isEmpty()) { + return null; + } + + // Generate required URL + URI methodURI = null; + String uri = null; + try { + methodURI = new URI(httpRequest.uri().toString()); + uri = methodURI.toString(); + if (methodURI == null) { + return null; + } + } catch (Exception ignored){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.URI_EXCEPTION_MESSAGE, SprayUtils.SPRAY_CLIENT, ignored.getMessage()), ignored, this.getClass().getName()); + return null; + } + return new SSRFOperation(uri, this.getClass().getName(), SprayUtils.METHOD_SEND_RECEIVE); + } catch (Throwable e) { + if (e instanceof NewRelicSecurityException) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, SprayUtils.SPRAY_CLIENT, e.getMessage()), e, this.getClass().getName()); + throw e; + } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, SprayUtils.SPRAY_CLIENT, e.getMessage()), e, this.getClass().getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, SprayUtils.SPRAY_CLIENT, e.getMessage()), e, this.getClass().getName()); + } + return null; + } + private void registerExitOperation(boolean isProcessingAllowed, AbstractOperation operation) { + try { + if (operation == null || !isProcessingAllowed || !NewRelicSecurity.isHookProcessingActive() || NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty() + ) { + return; + } + NewRelicSecurity.getAgent().registerExitEvent(operation); + } catch (Throwable e) { + NewRelicSecurity.getAgent().log(LogLevel.FINEST, String.format(GenericHelper.EXIT_OPERATION_EXCEPTION_MESSAGE, SprayUtils.SPRAY_CLIENT, e.getMessage()), e, this.getClass().getName()); + } + } +} + diff --git a/instrumentation-security/spray-client/src/test/scala/com/newrelic/security/agent/spray/client/SprayCanClientTest.scala b/instrumentation-security/spray-client/src/test/scala/com/newrelic/security/agent/spray/client/SprayCanClientTest.scala new file mode 100644 index 000000000..36a056279 --- /dev/null +++ b/instrumentation-security/spray-client/src/test/scala/com/newrelic/security/agent/spray/client/SprayCanClientTest.scala @@ -0,0 +1,118 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.newrelic.security.agent.spray.client + +import akka.actor._ +import com.newrelic.agent.security.instrumentation.spray.client.SprayUtils +import com.newrelic.agent.security.introspec.internal.HttpServerLocator +import com.newrelic.agent.security.introspec.{HttpTestServer, InstrumentationTestConfig, SecurityInstrumentationTestRunner, SecurityIntrospector} +import com.newrelic.api.agent.security.instrumentation.helpers.{GenericHelper, ServletHelper} +import com.newrelic.api.agent.security.schema.VulnerabilityCaseType +import com.newrelic.api.agent.security.schema.operation.SSRFOperation +import com.newrelic.security.test.marker.{Java11IncompatibleTest, Java17IncompatibleTest} +import org.junit.experimental.categories.Category +import org.junit.runner.RunWith +import org.junit._ +import org.junit.runners.MethodSorters +import spray.client.pipelining +import spray.client.pipelining.Get + +import java.util.UUID +import scala.util.{Failure, Success} + +//// Not compatible with Java 11+ and Scala 2.13+ https://github.com/scala/bug/issues/12340 +@Category( Array(classOf[Java11IncompatibleTest], classOf[Java17IncompatibleTest] )) +@RunWith(classOf[SecurityInstrumentationTestRunner]) +@InstrumentationTestConfig(includePrefixes = Array("spray", "scala", "com.newrelic.agent.security.instrumentation")) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class SprayCanClientTest { + var server :HttpTestServer = HttpServerLocator.createAndStart() + implicit var system: ActorSystem = ActorSystem("spray-client") + val endpoint :String = server.getEndPoint.toString; + + @After + def after(): Unit = { + server.shutdown() + } + + @Test + def testSendReceive(): Unit = { + server.getHeaders.clear() + val introspector: SecurityIntrospector = SecurityInstrumentationTestRunner.getIntrospector + + requestApi() + + Assert.assertTrue("No operations detected", introspector.getOperations.size() > 0) + val operations: SSRFOperation = introspector.getOperations.get(0).asInstanceOf[SSRFOperation] + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operations.getCaseType) + Assert.assertEquals("Invalid method-name.", SprayUtils.METHOD_SEND_RECEIVE, operations.getMethodName) + Assert.assertEquals("Invalid ssrf arg.", endpoint, operations.getArg) + + val header: java.util.Map[String, String] = server.getHeaders + Assert.assertFalse(String.format("Found CSEC header: %s", ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID), header.containsKey(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID)) + Assert.assertFalse(String.format("Found CSEC header: %s", ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER), header.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER)) + Assert.assertFalse(String.format("Found CSEC header: %s", GenericHelper.CSEC_PARENT_ID), header.containsKey(GenericHelper.CSEC_PARENT_ID)) + } + + @Test + def testSendReceiveWithHeader(): Unit = { + val headerValue = String.valueOf(UUID.randomUUID) + val introspector: SecurityIntrospector = SecurityInstrumentationTestRunner.getIntrospector + introspector.setK2FuzzRequestId(headerValue) + introspector.setK2TracingData(headerValue) + introspector.setK2ParentId(headerValue) + + requestApi() + + + Assert.assertTrue("No operations detected", introspector.getOperations.size() > 0) + val operations: SSRFOperation = introspector.getOperations.get(0).asInstanceOf[SSRFOperation] + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operations.getCaseType) + Assert.assertEquals("Invalid method-name.", SprayUtils.METHOD_SEND_RECEIVE, operations.getMethodName) + Assert.assertEquals("Invalid ssrf arg.", endpoint, operations.getArg) + + val header: java.util.Map[String, String] = server.getHeaders + Assert.assertTrue(String.format("Missing CSEC header: %s", ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID), header.containsKey(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID)) + Assert.assertEquals( + String.format("Invalid CSEC header value for: %s", ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID), + headerValue, + header.get(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID) + ) + + Assert.assertTrue(String.format("Missing CSEC header: %s", ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER), header.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase)) + Assert.assertEquals( + String.format("Invalid CSEC header value for: %s", ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER), + String.format("%s;DUMMY_UUID/dummy-api-id/dummy-exec-id;", headerValue), + header.get(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase) + ) + Assert.assertTrue(String.format("Missing CSEC header: %s", GenericHelper.CSEC_PARENT_ID), header.containsKey(GenericHelper.CSEC_PARENT_ID)) + Assert.assertEquals( + String.format("Invalid CSEC header value for: %s", GenericHelper.CSEC_PARENT_ID), + headerValue, + header.get(GenericHelper.CSEC_PARENT_ID) + ) + } + + + def requestApi()(implicit system: ActorSystem): Unit = { + import system.dispatcher + val pipeline = pipelining.sendReceive + val responseFuture = pipeline {Get(endpoint)} + + responseFuture onComplete { + case Success(result) => + println("The API call was successful...: " + result) + system.shutdown() + + case Failure(error) => + println(error, "Couldn't get elevation") + system.shutdown() + } + system.awaitTermination + } +} diff --git a/instrumentation-security/spray-http-1.3.1/build.gradle b/instrumentation-security/spray-http-1.3.1/build.gradle new file mode 100644 index 000000000..14d61a161 --- /dev/null +++ b/instrumentation-security/spray-http-1.3.1/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'scala' + +isScalaProjectEnabled(project, "scala-2.10") + +sourceSets.test.scala.srcDir "src/test/java" +sourceSets.test.java.srcDirs = [] + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.spray-http-1.3.1' } +} + +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("org.scala-lang:scala-library:2.10.7") + implementation("io.spray:spray-routing_2.10:1.3.3") + implementation("com.typesafe.akka:akka-actor_2.10:2.3.14") + testImplementation("io.spray:spray-can_2.10:1.3.3") +} + +verifyInstrumentation { + passesOnly('io.spray:spray-routing_2.11:[1.3.1,)') { + implementation("com.typesafe.akka:akka-actor_2.11:2.3.14") + } + passesOnly('io.spray:spray-routing_2.10:[1.3.1,)') { + implementation("com.typesafe.akka:akka-actor_2.10:2.3.14") + } +} + +site { + title 'Spray-http' + type 'Messaging' +} \ No newline at end of file diff --git a/instrumentation-security/spray-http-1.3.1/src/main/scala/spray/SprayHttpUtils.java b/instrumentation-security/spray-http-1.3.1/src/main/scala/spray/SprayHttpUtils.java new file mode 100644 index 000000000..0ff49b0ce --- /dev/null +++ b/instrumentation-security/spray-http-1.3.1/src/main/scala/spray/SprayHttpUtils.java @@ -0,0 +1,154 @@ +package spray; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; +import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.schema.AgentMetaData; +import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; +import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.api.agent.security.schema.policy.AgentPolicy; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import scala.collection.Iterator; +import scala.collection.immutable.List; +import spray.http.*; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Map; + +public class SprayHttpUtils { + + public static final String QUESTION_MARK = "?"; + + private static final String X_FORWARDED_FOR = "x-forwarded-for"; + public static final String SPRAY_HTTP_1_3_1 = "SPRAY-HTTP-1.3.1"; + + public static String getNrSecCustomAttribName() { + return "SPRAY-HTTP-" + Thread.currentThread().getId(); + } + public static String getNrSecCustomAttribNameForResponse() { + return "SPRAY-HTTP-RXSS" + Thread.currentThread().getId(); + } + + public static void preProcessRequestHook(HttpRequest request) { + try { + if (!NewRelicSecurity.isHookProcessingActive()) { + return; + } + SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); + + com.newrelic.api.agent.security.schema.HttpRequest securityRequest = securityMetaData.getRequest(); + if (securityRequest.isRequestParsed()) { + return; + } + + AgentMetaData securityAgentMetaData = securityMetaData.getMetaData(); + securityRequest.setMethod(request.method().name()); + securityRequest.setProtocol(request.uri().scheme()); + securityRequest.setUrl(processURL(request.uri())); + securityRequest.setServerPort(request.uri().effectivePort()); + processHttpRequestHeader(request.headers(), securityRequest); + + securityMetaData.setTracingHeaderValue(getTraceHeader(securityRequest.getHeaders())); + + if (!request.entity().isEmpty()) { + if (request.entity() instanceof HttpEntity.NonEmpty) { + securityRequest.setContentType(((HttpEntity.NonEmpty) request.entity()).contentType().value()); + } + securityRequest.setBody(new StringBuilder(request.entity().data().asString(StandardCharsets.UTF_8))); + } + + StackTraceElement[] trace = Thread.currentThread().getStackTrace(); + securityMetaData.getMetaData().setServiceTrace(Arrays.copyOfRange(trace, 2, trace.length)); + securityRequest.setRequestParsed(true); + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, SPRAY_HTTP_1_3_1, e.getMessage()), e, SprayHttpUtils.class.getName()); + } + } + + public static String getTraceHeader(Map headers) { + String data = StringUtils.EMPTY; + if (headers.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER) || headers.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase())) { + data = headers.get(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER); + if (data == null || data.trim().isEmpty()) { + data = headers.get(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase()); + } + } + return data; + } + + private static void processHttpRequestHeader(List headers, com.newrelic.api.agent.security.schema.HttpRequest securityRequest) { + Iterator headerIterator = headers.iterator(); + while (headerIterator.hasNext()){ + HttpHeader element = headerIterator.next(); + String headerKey = element.lowercaseName(); + String headerValue = element.value(); + boolean takeNextValue = false; + AgentPolicy agentPolicy = NewRelicSecurity.getAgent().getCurrentPolicy(); + AgentMetaData agentMetaData = NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData(); + if (agentPolicy != null + && agentPolicy.getProtectionMode().getEnabled() + && agentPolicy.getProtectionMode().getIpBlocking().getEnabled() + && agentPolicy.getProtectionMode().getIpBlocking().getIpDetectViaXFF() + && X_FORWARDED_FOR.equals(headerKey)) { + takeNextValue = true; + } else if (ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID.equals(headerKey)) { + // TODO: May think of removing this intermediate obj and directly create K2 Identifier. + NewRelicSecurity.getAgent().getSecurityMetaData().setFuzzRequestIdentifier(ServletHelper.parseFuzzRequestIdentifierHeader(headerValue)); + } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, headerValue); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); + } + if (takeNextValue) { + agentMetaData.setClientDetectedFromXFF(true); + securityRequest.setClientIP(headerValue); + agentMetaData.getIps() + .add(securityRequest.getClientIP()); + } + securityRequest.getHeaders().put(headerKey, headerValue); + } + } + + private static String processURL(Uri uri) { + String path = uri.path().toString(); + String queryString = StringUtils.substringAfter(uri.toString(), QUESTION_MARK); + if(StringUtils.isBlank(queryString)){ + return path; + } else { + return path + QUESTION_MARK + queryString; + } + } + + public static void postProcessSecurityHook(HttpResponse httpResponse, String className, String methodName) { + try { + if (!NewRelicSecurity.isHookProcessingActive() + ) { + return; + } + //Add request URI hash to low severity event filter + LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest()); + + if(!ServletHelper.isResponseContentTypeExcluded(NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().getResponseContentType())) { + RXSSOperation rxssOperation = new RXSSOperation(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest(), + NewRelicSecurity.getAgent().getSecurityMetaData().getResponse(), + className, methodName); + NewRelicSecurity.getAgent().registerOperation(rxssOperation); + } + ServletHelper.tmpFileCleanUp(NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getTempFiles()); + } catch (Throwable e) { + if (e instanceof NewRelicSecurityException) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, SPRAY_HTTP_1_3_1, e.getMessage()), e, SprayHttpUtils.class.getName()); + throw e; + } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, SPRAY_HTTP_1_3_1, e.getMessage()), e, SprayHttpUtils.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, SPRAY_HTTP_1_3_1, e.getMessage()), e, SprayHttpUtils.class.getName()); + } + } +} diff --git a/instrumentation-security/spray-http-1.3.1/src/main/scala/spray/httpx/marshalling/SprayToResponseMarshallingContext.java b/instrumentation-security/spray-http-1.3.1/src/main/scala/spray/httpx/marshalling/SprayToResponseMarshallingContext.java new file mode 100644 index 000000000..41d03a37e --- /dev/null +++ b/instrumentation-security/spray-http-1.3.1/src/main/scala/spray/httpx/marshalling/SprayToResponseMarshallingContext.java @@ -0,0 +1,49 @@ + +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package spray.httpx.marshalling; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +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 spray.SprayHttpUtils; +import spray.http.HttpEntity; +import spray.http.HttpResponse; +import java.nio.charset.StandardCharsets; + +@Weave(type = MatchType.Interface, originalName = "spray.httpx.marshalling.ToResponseMarshallingContext") +public class SprayToResponseMarshallingContext { + + @Trace(async = true) + public void marshalTo(HttpResponse httpResponse) { + boolean isLockAcquired = GenericHelper.acquireLockIfPossible(SprayHttpUtils.getNrSecCustomAttribNameForResponse()); + try { + if (isLockAcquired && httpResponse.entity().nonEmpty()) { + NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseBody(new StringBuilder(httpResponse.entity().data().asString(StandardCharsets.UTF_8))); + if (httpResponse.entity() instanceof HttpEntity.NonEmpty) { + NewRelicSecurity.getAgent().getSecurityMetaData().getResponse().setResponseContentType(((HttpEntity.NonEmpty) httpResponse.entity()).contentType().value()); + } + SprayHttpUtils.postProcessSecurityHook(httpResponse, this.getClass().getName(), "marshalTo"); + } + } catch (Exception e){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_PARSING_HTTP_RESPONSE, SprayHttpUtils.SPRAY_HTTP_1_3_1, e.getMessage()), e, this.getClass().getName()); + } + try { + Weaver.callOriginal(); + } finally { + if(isLockAcquired){ + GenericHelper.releaseLock(SprayHttpUtils.getNrSecCustomAttribNameForResponse()); + } + } + } + +} diff --git a/instrumentation-security/spray-http-1.3.1/src/main/scala/spray/routing/SprayRoutingHttpServer.java b/instrumentation-security/spray-http-1.3.1/src/main/scala/spray/routing/SprayRoutingHttpServer.java new file mode 100644 index 000000000..fe4c0f476 --- /dev/null +++ b/instrumentation-security/spray-http-1.3.1/src/main/scala/spray/routing/SprayRoutingHttpServer.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package spray.routing; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +import scala.Function1; +import scala.PartialFunction; +import spray.SprayHttpUtils; + +@Weave(type = MatchType.ExactClass, originalName = "spray.routing.HttpServiceBase$class") +public class SprayRoutingHttpServer { + + public static final void runSealedRoute$1(final HttpServiceBase $this, final RequestContext ctx, final PartialFunction sealedExceptionHandler$1, final Function1 sealedRoute$1) { + boolean isLockAcquired = GenericHelper.acquireLockIfPossible(SprayHttpUtils.getNrSecCustomAttribName()); + if (isLockAcquired) { + SprayHttpUtils.preProcessRequestHook(ctx.request()); + } + try { + Weaver.callOriginal(); + } finally { + if(isLockAcquired){ + GenericHelper.releaseLock(SprayHttpUtils.getNrSecCustomAttribName()); + } + } + } + +} diff --git a/instrumentation-security/spray-http-1.3.1/src/test/java/com/nr/agent/security/instrumentation/spray/http/SprayTest.java b/instrumentation-security/spray-http-1.3.1/src/test/java/com/nr/agent/security/instrumentation/spray/http/SprayTest.java new file mode 100644 index 000000000..a96b77eda --- /dev/null +++ b/instrumentation-security/spray-http-1.3.1/src/test/java/com/nr/agent/security/instrumentation/spray/http/SprayTest.java @@ -0,0 +1,159 @@ +package com.nr.agent.security.instrumentation.spray.http; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import com.newrelic.agent.security.introspec.SecurityIntrospector; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.schema.AbstractOperation; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; +import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.ServerSocket; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "spray", "scala" }) +public class SprayTest { + + @ClassRule + public static HttpServer server = HttpServer$.MODULE$.apply(getRandomPort()); + private static int port; + + @Test + public void testGet() throws IOException { + makeRequest("GET", StringUtils.EMPTY); + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertNotNull(operations); + Assert.assertEquals(1, operations.size()); + + RXSSOperation operation = (RXSSOperation) operations.get(0); + Assert.assertNotNull("No target operation detected", operation); + Assert.assertEquals("Wrong case-type detected", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); + Assert.assertEquals("Wrong method name detected", "marshalTo", operation.getMethodName()); + + Assert.assertNotNull("Empty request detected", operation.getRequest()); + Assert.assertEquals("Wrong Protocol detected", "http", operation.getRequest().getProtocol()); + Assert.assertEquals("Wrong port detected", port, operation.getRequest().getServerPort()); + Assert.assertEquals("Wrong Content-type detected", StringUtils.EMPTY, operation.getRequest().getContentType()); + + Assert.assertNotNull("Empty response detected", operation.getResponse()); + Assert.assertEquals("Wrong port detected", "testing API", operation.getResponse().getResponseBody().toString()); + Assert.assertEquals("Wrong Content-type detected", "text/plain", operation.getResponse().getResponseContentType()); + + } + @Test + public void testPost() throws IOException { + makeRequest("POST", StringUtils.EMPTY); + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertNotNull(operations); + Assert.assertEquals(1, operations.size()); + + RXSSOperation operation = (RXSSOperation) operations.get(0); + Assert.assertNotNull("No target operation detected", operation); + Assert.assertEquals("Wrong case-type detected", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); + Assert.assertEquals("Wrong method name detected", "marshalTo", operation.getMethodName()); + + Assert.assertNotNull("Empty request detected", operation.getRequest()); + Assert.assertEquals("Wrong Protocol detected", "http", operation.getRequest().getProtocol()); + Assert.assertEquals("Wrong port detected", port, operation.getRequest().getServerPort()); + Assert.assertEquals("Wrong Content-type detected", "text/plain", operation.getRequest().getContentType()); + Assert.assertEquals("Wrong Content-type detected", "data", operation.getRequest().getBody().toString()); + + Assert.assertNotNull("Empty response detected", operation.getResponse()); + Assert.assertEquals("Wrong port detected", "testing API", operation.getResponse().getResponseBody().toString()); + Assert.assertEquals("Wrong Content-type detected", "text/plain", operation.getResponse().getResponseContentType()); + + } + + @Test + public void testWithCSECHeader() throws IOException { + String headerValue = String.valueOf(UUID.randomUUID()); + makeRequest("GET", headerValue); + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + List operations = introspector.getOperations(); + Assert.assertNotNull(operations); + Assert.assertEquals(1, operations.size()); + + RXSSOperation operation = (RXSSOperation) operations.get(0); + Assert.assertNotNull("No target operation detected", operation); + Assert.assertEquals("Wrong case-type detected", VulnerabilityCaseType.REFLECTED_XSS, operation.getCaseType()); + Assert.assertEquals("Wrong method name detected", "marshalTo", operation.getMethodName()); + + Assert.assertNotNull("Empty request detected", operation.getRequest()); + Assert.assertEquals("Wrong Protocol detected", "http", operation.getRequest().getProtocol()); + Assert.assertEquals("Wrong port detected", port, operation.getRequest().getServerPort()); + Assert.assertEquals("Wrong Content-type detected", StringUtils.EMPTY, operation.getRequest().getContentType()); + + Assert.assertNotNull("Empty response detected", operation.getResponse()); + Assert.assertEquals("Wrong port detected", "testing API", operation.getResponse().getResponseBody().toString()); + Assert.assertEquals("Wrong Content-type detected", "text/plain", operation.getResponse().getResponseContentType()); + + Map headers = operation.getRequest().getHeaders(); + Assert.assertTrue( + String.format("Missing header: %s", ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID), + headers.containsKey(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID) + ); + Assert.assertEquals( + String.format("Invalid header value for: %s", ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID), + headerValue, + headers.get(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID) + ); + Assert.assertTrue( + String.format("Missing header: %s", ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER), + headers.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase()) + ); + Assert.assertEquals( + String.format("Invalid header value for: %s", ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER), + headerValue, + headers.get(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase()) + ); + Assert.assertTrue( + String.format("Missing header: %s", GenericHelper.CSEC_PARENT_ID), + headers.containsKey(GenericHelper.CSEC_PARENT_ID.toLowerCase()) + ); + Assert.assertEquals( + String.format("Invalid header value for: %s", GenericHelper.CSEC_PARENT_ID), + headerValue, + headers.get(GenericHelper.CSEC_PARENT_ID.toLowerCase()) + ); + } + + private static int getRandomPort(){ + try (ServerSocket socket = new ServerSocket(0)){ + return port = socket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port"); + } + } + + private void makeRequest(String method, String csecHeaders) throws IOException { + String ENDPOINT = String.format("http://localhost:%d/test", port); + HttpURLConnection conn = (HttpURLConnection) new URL(ENDPOINT).openConnection(); + conn.setRequestMethod(method); + conn.setDoOutput(true); + conn.setRequestProperty("Content-Type", "text/plain"); + if (method.equalsIgnoreCase("POST")) { + conn.getOutputStream().write("data".getBytes()); + } + if (!csecHeaders.isEmpty()) { + conn.setRequestProperty(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID, csecHeaders); + conn.setRequestProperty(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER, csecHeaders); + conn.setRequestProperty(GenericHelper.CSEC_PARENT_ID, csecHeaders); + } + conn.connect(); + System.out.println("response status: " + conn.getResponseCode()); + } +} diff --git a/instrumentation-security/spray-http-1.3.1/src/test/scala/com/nr/agent/security/instrumentation/spray/http/HttpServer.scala b/instrumentation-security/spray-http-1.3.1/src/test/scala/com/nr/agent/security/instrumentation/spray/http/HttpServer.scala new file mode 100644 index 000000000..972012aea --- /dev/null +++ b/instrumentation-security/spray-http-1.3.1/src/test/scala/com/nr/agent/security/instrumentation/spray/http/HttpServer.scala @@ -0,0 +1,43 @@ +package com.nr.agent.security.instrumentation.spray.http + +import akka.actor.{Actor, ActorContext, ActorRef, ActorSystem, Props} +import akka.io.IO +import akka.pattern._ +import akka.util.Timeout +import org.junit.rules.ExternalResource +import spray.can.Http +import spray.routing.{HttpService, RequestContext, Route} + +import scala.concurrent.duration._ +import scala.concurrent.{Await, Future} +import scala.language.postfixOps + +class HttpServer(port: Int) extends ExternalResource { + private implicit val system: ActorSystem = ActorSystem() + private implicit val timeout: Timeout = 3 seconds + + private val handler: ActorRef = system.actorOf(Props[MainActor], name = "handler") + + private def start(port: Int): Future[Any] = Await.ready( + IO(Http) ? Http.Bind(handler, "localhost", port), + timeout.duration + ) + + private def stop(): Unit = { + IO(Http) ? Http.CloseAll + system.stop(handler) + system.shutdown() + } + override def before(): Unit = start(port) + override def after(): Unit = stop() +} + +object HttpServer { + def apply(port: Int) = new HttpServer(port) +} + +class MainActor extends Actor with HttpService { + def route: Route = path("test") { (ctx: RequestContext) => ctx.complete("testing API")} + def actorRefFactory: ActorContext = context + def receive: Receive = runRoute(route) +} \ No newline at end of file diff --git a/instrumentation-security/spring-web/src/main/java/com/newrelic/agent/security/instrumentation/springweb/SpringController_Instrumentation.java b/instrumentation-security/spring-web/src/main/java/com/newrelic/agent/security/instrumentation/springweb/SpringController_Instrumentation.java index 9776c311c..696ff25b9 100644 --- a/instrumentation-security/spring-web/src/main/java/com/newrelic/agent/security/instrumentation/springweb/SpringController_Instrumentation.java +++ b/instrumentation-security/spring-web/src/main/java/com/newrelic/agent/security/instrumentation/springweb/SpringController_Instrumentation.java @@ -28,6 +28,7 @@ public class SpringController_Instrumentation { @WeaveIntoAllMethods private static void requestMapping() { ServletHelper.registerUserLevelCode("spring-annotation"); + ServletHelper.setFoundAnnotedUserLevelServiceMethod(); } } diff --git a/instrumentation-security/spring-web/src/test/java/com/nr/agent/security/instrumentation/springweb/springweb/test/SpringControllerTest.java b/instrumentation-security/spring-web/src/test/java/com/nr/agent/security/instrumentation/springweb/springweb/test/SpringControllerTest.java index 9a8038943..42c7fea30 100644 --- a/instrumentation-security/spring-web/src/test/java/com/nr/agent/security/instrumentation/springweb/springweb/test/SpringControllerTest.java +++ b/instrumentation-security/spring-web/src/test/java/com/nr/agent/security/instrumentation/springweb/springweb/test/SpringControllerTest.java @@ -31,6 +31,7 @@ public void testRequestMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -41,6 +42,7 @@ public void testGetMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -51,6 +53,7 @@ public void testPostMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -61,6 +64,7 @@ public void testPatchMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -71,6 +75,7 @@ public void testPutMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -81,5 +86,6 @@ public void testDeleteMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } } diff --git a/instrumentation-security/spring-web/src/test/java/com/nr/agent/security/instrumentation/springweb/springweb/test/SpringRestControllerTest.java b/instrumentation-security/spring-web/src/test/java/com/nr/agent/security/instrumentation/springweb/springweb/test/SpringRestControllerTest.java index ae4bfa3be..53a0686f8 100644 --- a/instrumentation-security/spring-web/src/test/java/com/nr/agent/security/instrumentation/springweb/springweb/test/SpringRestControllerTest.java +++ b/instrumentation-security/spring-web/src/test/java/com/nr/agent/security/instrumentation/springweb/springweb/test/SpringRestControllerTest.java @@ -30,6 +30,7 @@ public void testRequestMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -40,6 +41,7 @@ public void testGetMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -50,6 +52,7 @@ public void testPostMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -60,6 +63,7 @@ public void testPatchMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -70,6 +74,7 @@ public void testPutMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } @Test @@ -80,5 +85,6 @@ public void testDeleteMapping() { AgentMetaData meta = introspector.getSecurityMetaData().getMetaData(); Assert.assertNotNull("Service trace can not be empty/null.", meta.getServiceTrace()); Assert.assertTrue("user level service method was not encountered.", meta.isUserLevelServiceMethodEncountered()); + Assert.assertTrue("Annotated userLevelService Method was not encountered.", meta.isFoundAnnotedUserLevelServiceMethod()); } } diff --git a/instrumentation-security/spring-webclient-5.0/build.gradle b/instrumentation-security/spring-webclient-5.0/build.gradle new file mode 100644 index 000000000..c5fb21ae9 --- /dev/null +++ b/instrumentation-security/spring-webclient-5.0/build.gradle @@ -0,0 +1,23 @@ +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("org.springframework:spring-webflux:5.0.0.RELEASE") + +} + + + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.spring-webclient-5.0' } +} + +verifyInstrumentation { + passesOnly 'org.springframework:spring-webflux:[5.0.0.RELEASE,)' + excludeRegex 'org.springframework:spring-webflux:.*(RC|SEC|M)[0-9]*$' +} + +site { + title 'Spring webclient' + type 'Messaging' +} diff --git a/instrumentation-security/spring-webclient-5.0/src/main/java/com/newrelic/agent/security/instrumentation/spring/client5/SpringWebClientHelper.java b/instrumentation-security/spring-webclient-5.0/src/main/java/com/newrelic/agent/security/instrumentation/spring/client5/SpringWebClientHelper.java new file mode 100644 index 000000000..9c8a58a4a --- /dev/null +++ b/instrumentation-security/spring-webclient-5.0/src/main/java/com/newrelic/agent/security/instrumentation/spring/client5/SpringWebClientHelper.java @@ -0,0 +1,110 @@ +package com.newrelic.agent.security.instrumentation.spring.client5; + +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.schema.AbstractOperation; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; +import com.newrelic.api.agent.security.schema.operation.SSRFOperation; +import com.newrelic.api.agent.security.utils.SSRFUtils; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import org.springframework.http.HttpMethod; +import org.springframework.web.reactive.function.client.ClientRequest; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +public class SpringWebClientHelper { + + public static final String NR_SEC_CUSTOM_ATTRIB_NAME = "SPRING_CLIENT_OPERATION_LOCK-"; + public static final String METHOD_EXECHANGE = "exchange"; + + public static final String SPRING_WEBCLIENT_5_0 = "spring-webclient-5.0"; + public static final String SPRING_WEB_CLIENT_REQUEST_LIST_CUSTOM_ATTRIB = "SPRING-WEB-CLIENT-REQUEST-LIST"; + + public static String getNrSecCustomAttribName() { + return NR_SEC_CUSTOM_ATTRIB_NAME + Thread.currentThread().getId(); + } + + public static AbstractOperation preprocessSecurityHook(URI url, HttpMethod method, String className, String methodName) { + try { + if (!NewRelicSecurity.isHookProcessingActive() || + NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty() || + url == null || url.getPath().isEmpty()) { + return null; + } + ArrayList springClientRequestURIs = NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(SPRING_WEB_CLIENT_REQUEST_LIST_CUSTOM_ATTRIB, ArrayList.class); + if (springClientRequestURIs == null){ + springClientRequestURIs = new ArrayList<>(); + } + if (!springClientRequestURIs.contains(url.toString())) { + SSRFOperation ssrfOperation = new SSRFOperation(url.toString(), className, methodName); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); + NewRelicSecurity.getAgent().registerOperation(ssrfOperation); + springClientRequestURIs.add(url.toString()); + NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(SPRING_WEB_CLIENT_REQUEST_LIST_CUSTOM_ATTRIB, springClientRequestURIs); + return ssrfOperation; + } + } catch (Throwable e) { + if (e instanceof NewRelicSecurityException) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, SPRING_WEBCLIENT_5_0, e.getMessage()), e, SpringWebClientHelper.class.getName()); + throw e; + } + NewRelicSecurity.getAgent().log(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, SPRING_WEBCLIENT_5_0, e.getMessage()), e, SpringWebClientHelper.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.SEVERE, String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, SPRING_WEBCLIENT_5_0, e.getMessage()), e, SpringWebClientHelper.class.getName()); + } + return null; + } + + public static void registerExitOperation(boolean isProcessingAllowed, AbstractOperation operation) { + try { + if (operation == null || !isProcessingAllowed || !NewRelicSecurity.isHookProcessingActive() || + NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty() || skipExistsEvent() + ) { + return; + } + NewRelicSecurity.getAgent().registerExitEvent(operation); + } catch (Throwable e) { + NewRelicSecurity.getAgent().log(LogLevel.FINEST, String.format(GenericHelper.EXIT_OPERATION_EXCEPTION_MESSAGE, SPRING_WEBCLIENT_5_0, e.getMessage()), e, SpringWebClientHelper.class.getName()); + } + } + + public static boolean skipExistsEvent() { + if (!(NewRelicSecurity.getAgent().getCurrentPolicy().getVulnerabilityScan().getEnabled() && + NewRelicSecurity.getAgent().getCurrentPolicy().getVulnerabilityScan().getIastScan().getEnabled())) { + return true; + } + + return false; + } + + public static ClientRequest addSecurityHeaders(ClientRequest request, AbstractOperation operation) { + if (operation == null || request == null) { + return null; + } + ClientRequest.Builder requestBuilder = ClientRequest.from(request); + + String iastHeader = NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getRaw(); + if (iastHeader != null && !iastHeader.trim().isEmpty()) { + requestBuilder.header(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID, iastHeader); + } + String csecParaentId = NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(GenericHelper.CSEC_PARENT_ID, String.class); + if(StringUtils.isNotBlank(csecParaentId)){ + requestBuilder.header(GenericHelper.CSEC_PARENT_ID, csecParaentId); + } + + if (operation.getApiID() != null && !operation.getApiID().trim().isEmpty() && + operation.getExecutionId() != null && !operation.getExecutionId().trim().isEmpty()) { + // Add Security distributed tracing header + requestBuilder.header(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER, + SSRFUtils.generateTracingHeaderValue(NewRelicSecurity.getAgent().getSecurityMetaData() + .getTracingHeaderValue(), + operation.getApiID(), operation.getExecutionId(), + NewRelicSecurity.getAgent().getAgentUUID())); + } + return requestBuilder.build(); + + } +} diff --git a/instrumentation-security/spring-webclient-5.0/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction_Instrumentation.java b/instrumentation-security/spring-webclient-5.0/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction_Instrumentation.java new file mode 100644 index 000000000..e773eeb16 --- /dev/null +++ b/instrumentation-security/spring-webclient-5.0/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunction_Instrumentation.java @@ -0,0 +1,52 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.springframework.web.reactive.function.client; + +import com.newrelic.agent.security.instrumentation.spring.client5.SpringWebClientHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.schema.AbstractOperation; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.publisher.Mono; + +@Weave(type = MatchType.Interface, originalName = "org.springframework.web.reactive.function.client.ExchangeFunction") +public class ExchangeFunction_Instrumentation { + + public Mono exchange(ClientRequest request) { + boolean isLockAcquired = acquireLockIfPossible(); + AbstractOperation operation = null; + if(isLockAcquired) { + operation = SpringWebClientHelper.preprocessSecurityHook(request.url(), request.method(), this.getClass().getName(), SpringWebClientHelper.METHOD_EXECHANGE); + ClientRequest updatedRequest = SpringWebClientHelper.addSecurityHeaders(request, operation); + if (updatedRequest != null) { + request = updatedRequest; + } + } + Mono response; + try { + response = Weaver.callOriginal(); + } finally { + if(isLockAcquired){ + releaseLock(); + } + } + SpringWebClientHelper.registerExitOperation(isLockAcquired, operation); + return response; + } + + private void releaseLock() { + GenericHelper.releaseLock(SpringWebClientHelper.getNrSecCustomAttribName()); + } + + + private boolean acquireLockIfPossible() { + return GenericHelper.acquireLockIfPossible(SpringWebClientHelper.getNrSecCustomAttribName()); + } + +} diff --git a/instrumentation-security/spring-webflux/build.gradle b/instrumentation-security/spring-webflux/build.gradle index 90c8f5746..492d134f9 100644 --- a/instrumentation-security/spring-webflux/build.gradle +++ b/instrumentation-security/spring-webflux/build.gradle @@ -7,6 +7,9 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") implementation("org.springframework:spring-webflux:5.0.0.RELEASE") + testImplementation("org.springframework:spring-context:5.0.0.RELEASE") + testImplementation("org.springframework:spring-aop:5.0.0.RELEASE") + testImplementation("jakarta.servlet:jakarta.servlet-api:4.0.2") } jar { diff --git a/instrumentation-security/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SpringHelper.java b/instrumentation-security/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SpringHelper.java index 58f358731..2f5b15824 100644 --- a/instrumentation-security/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SpringHelper.java +++ b/instrumentation-security/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SpringHelper.java @@ -9,13 +9,18 @@ import java.lang.reflect.Method; public class SpringHelper { + private static final String WILDCARD = "*"; public static void gatherURLMappings(T mapping, Method method){ try { RequestMappingInfo mappingInfo = (RequestMappingInfo) mapping; PatternsRequestCondition patternsCondition = mappingInfo.getPatternsCondition(); - if(patternsCondition!=null) { - for (RequestMethod requestMethod : mappingInfo.getMethodsCondition().getMethods()) { - for (PathPattern url : patternsCondition.getPatterns()) { + if (patternsCondition != null) { + for (PathPattern url : patternsCondition.getPatterns()) { + if (mappingInfo.getMethodsCondition().getMethods().isEmpty()) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, url.getPatternString(), method.getDeclaringClass().getName())); + continue; + } + for (RequestMethod requestMethod : mappingInfo.getMethodsCondition().getMethods()) { URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(requestMethod.name(), url.getPatternString(), method.getDeclaringClass().getName())); } } diff --git a/instrumentation-security/spring-webflux/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java b/instrumentation-security/spring-webflux/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java new file mode 100644 index 000000000..e9c1e1706 --- /dev/null +++ b/instrumentation-security/spring-webflux/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java @@ -0,0 +1,61 @@ +package com.nr.agent.security.instrumentation.spring.webmvc; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +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 org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; + +import java.util.HashMap; +import java.util.Set; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"org.springframework.web.reactive"}) +public class APIEndpointTest { + + TestHandlerMethodMapping handlerMapping = new TestHandlerMethodMapping(); + private final String handler = TestMappings.class.getName(); + + private final static HashMap map = new HashMap<>(); + + @BeforeClass + public static void addMappings() { + map.put("/postMapping", "POST"); + map.put("/requestMapping", "GET"); + map.put("/getMapping", "GET"); + map.put("/patchMapping", "PATCH"); + map.put("/putMapping", "PUT"); + map.put("/deleteMapping", "DELETE"); + } + + @Test + public void testAPIEndpointDetection() { + handlerMapping.addMapping(new TestMappings()); + + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertEquals(6, mappings.size()); + + for (ApplicationURLMapping mapping: mappings) { + Assert.assertNotNull(mapping); + assertMapping(mapping); + } + + } + + private void assertMapping(ApplicationURLMapping actualMapping) { + String path = actualMapping.getPath(); + String method = map.get(path); + Assert.assertEquals(method, actualMapping.getMethod()); + Assert.assertEquals(handler, actualMapping.getHandler()); + } +} + +class TestHandlerMethodMapping extends RequestMappingHandlerMapping { + public void addMapping(Object handler) { + super.detectHandlerMethods(handler); + } +} \ No newline at end of file diff --git a/instrumentation-security/spring-webflux/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/TestMappings.java b/instrumentation-security/spring-webflux/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/TestMappings.java new file mode 100644 index 000000000..c95ca87eb --- /dev/null +++ b/instrumentation-security/spring-webflux/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/TestMappings.java @@ -0,0 +1,48 @@ +package com.nr.agent.security.instrumentation.spring.webmvc;/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + + +public class TestMappings { + + @RequestMapping(value = "/requestMapping", method = RequestMethod.GET) + public String testRequest() { + return "From Request Mapping"; + } + + @GetMapping(value = "/getMapping") + public String testGet() { + return "From Get Mapping"; + } + + @PostMapping(value = "/postMapping") + public String testPost() { + return "From Post Mapping"; + } + + @PatchMapping(value = "/patchMapping") + public String testPatch() { + return "From Patch Mapping"; + } + + @PutMapping(value = "/putMapping") + public String testPut() { + return "From Put Mapping"; + } + + @DeleteMapping(value = "/deleteMapping") + public String testDelete() { + return "From Delete Mapping"; + } +} diff --git a/instrumentation-security/spring-webmvc-3.1.0/build.gradle b/instrumentation-security/spring-webmvc-3.1.0/build.gradle index ae3d6ba5f..1270f47a9 100644 --- a/instrumentation-security/spring-webmvc-3.1.0/build.gradle +++ b/instrumentation-security/spring-webmvc-3.1.0/build.gradle @@ -7,6 +7,8 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") implementation("org.springframework:spring-webmvc:3.1.0.RELEASE") + testImplementation("jakarta.servlet:jakarta.servlet-api:4.0.2") + testImplementation("org.springframework:spring-web:3.1.0.RELEASE") } jar { diff --git a/instrumentation-security/spring-webmvc-3.1.0/src/main/java/org/springframework/web/servlet/handler310/SpringHelper.java b/instrumentation-security/spring-webmvc-3.1.0/src/main/java/org/springframework/web/servlet/handler310/SpringHelper.java index bff8969bc..09ee44f0b 100644 --- a/instrumentation-security/spring-webmvc-3.1.0/src/main/java/org/springframework/web/servlet/handler310/SpringHelper.java +++ b/instrumentation-security/spring-webmvc-3.1.0/src/main/java/org/springframework/web/servlet/handler310/SpringHelper.java @@ -9,13 +9,18 @@ import java.lang.reflect.Method; public class SpringHelper { + private static final String WILDCARD = "*"; public static void gatherURLMappings(T mapping, Method method){ try { RequestMappingInfo mappingInfo = (RequestMappingInfo) mapping; PatternsRequestCondition patternsCondition = mappingInfo.getPatternsCondition(); if (patternsCondition != null) { - for (RequestMethod requestMethod : mappingInfo.getMethodsCondition().getMethods()) { - for (String url : patternsCondition.getPatterns()) { + for (String url : patternsCondition.getPatterns()) { + if (mappingInfo.getMethodsCondition().getMethods().isEmpty()) { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, url, method.getDeclaringClass().getName())); + continue; + } + for (RequestMethod requestMethod : mappingInfo.getMethodsCondition().getMethods()) { URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(requestMethod.name(), url, method.getDeclaringClass().getName())); } } 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 new file mode 100644 index 000000000..8c10e6923 --- /dev/null +++ b/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java @@ -0,0 +1,63 @@ +package com.nr.agent.security.instrumentation.spring.webmvc; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +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 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 java.util.HashMap; +import java.util.Set; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"org.springframework.web.servlet"}) +public class APIEndpointTest { + + TestHandlerMethodMapping methodMapping = new TestHandlerMethodMapping(); + + + private final String handler = TestMappings.class.getName(); + + private final static HashMap map = new HashMap<>(); + + + @BeforeClass + public static void addMappings() { + map.put("/postMapping", "POST"); + map.put("/requestMapping", "GET"); + map.put("/putMapping", "PUT"); + map.put("/deleteMapping", "DELETE"); + } + + @Test + public void testAPIEndpointDetection() { + methodMapping.addMapping(new TestMappings()); + + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertEquals(4, mappings.size()); + + for (ApplicationURLMapping mapping: mappings) { + Assert.assertNotNull(mapping); + assertMapping(mapping); + } + + } + + private void assertMapping(ApplicationURLMapping actualMapping) { + String path = actualMapping.getPath(); + String method = map.get(path); + Assert.assertEquals(method, actualMapping.getMethod()); + Assert.assertEquals(handler, actualMapping.getHandler()); + } +} + +class TestHandlerMethodMapping extends RequestMappingHandlerMapping { + + public void addMapping(Object handler) { + super.detectHandlerMethods(handler); + } +} diff --git a/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/TestMappings.java b/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/TestMappings.java new file mode 100644 index 000000000..8a0b25b14 --- /dev/null +++ b/instrumentation-security/spring-webmvc-3.1.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/TestMappings.java @@ -0,0 +1,34 @@ +package com.nr.agent.security.instrumentation.spring.webmvc;/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class TestMappings { + + @RequestMapping(value = "/requestMapping", method = RequestMethod.GET) + public String testRequest() { + return "From Request Mapping"; + } + + @RequestMapping(value = "/postMapping", method = RequestMethod.POST) + public String testPost() { + return "From Post Mapping"; + } + + @RequestMapping(value = "/putMapping", method = RequestMethod.PUT) + public String testPut() { + return "From Put Mapping"; + } + + @RequestMapping(value = "/deleteMapping", method = RequestMethod.DELETE) + public String testDelete() { + return "From Delete Mapping"; + } +} diff --git a/instrumentation-security/spring-webmvc-5.3.0/build.gradle b/instrumentation-security/spring-webmvc-5.3.0/build.gradle index 678ca7ac0..0b657eaf3 100644 --- a/instrumentation-security/spring-webmvc-5.3.0/build.gradle +++ b/instrumentation-security/spring-webmvc-5.3.0/build.gradle @@ -7,6 +7,8 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") implementation("org.springframework:spring-webmvc:5.3.0") + testImplementation("org.springframework:spring-web:5.3.0") + testImplementation("jakarta.servlet:jakarta.servlet-api:4.0.2") } jar { diff --git a/instrumentation-security/spring-webmvc-5.3.0/src/main/java/org/springframework/web/servlet/handler530/SpringHelper.java b/instrumentation-security/spring-webmvc-5.3.0/src/main/java/org/springframework/web/servlet/handler530/SpringHelper.java index f40be47a1..b75150c15 100644 --- a/instrumentation-security/spring-webmvc-5.3.0/src/main/java/org/springframework/web/servlet/handler530/SpringHelper.java +++ b/instrumentation-security/spring-webmvc-5.3.0/src/main/java/org/springframework/web/servlet/handler530/SpringHelper.java @@ -11,20 +11,31 @@ import java.lang.reflect.Method; public class SpringHelper { + private static final String WILDCARD = "*"; public static void gatherURLMappings(T mapping, Method method){ try { RequestMappingInfo mappingInfo = (RequestMappingInfo) mapping; PatternsRequestCondition patternsCondition = mappingInfo.getPatternsCondition(); PathPatternsRequestCondition pathPatternsCondition = mappingInfo.getPathPatternsCondition(); - for (RequestMethod requestMethod : mappingInfo.getMethodsCondition().getMethods()) { - if (patternsCondition != null) { - for (String url : patternsCondition.getPatterns()) { + if (patternsCondition != null) { + for (String url : patternsCondition.getPatterns()) { + if(mappingInfo.getMethodsCondition().getMethods().isEmpty()){ + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, url, method.getDeclaringClass().getName())); + continue; + } + for (RequestMethod requestMethod : mappingInfo.getMethodsCondition().getMethods()){ URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(requestMethod.name(), url, method.getDeclaringClass().getName())); } } - else if (pathPatternsCondition != null) { - for (PathPattern url : pathPatternsCondition.getPatterns()) { - if (url != null) { + } + else if (pathPatternsCondition != null) { + for (PathPattern url : pathPatternsCondition.getPatterns()) { + if (url != null) { + if(mappingInfo.getMethodsCondition().getMethods().isEmpty()){ + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, url.getPatternString(), method.getDeclaringClass().getName())); + continue; + } + for (RequestMethod requestMethod : mappingInfo.getMethodsCondition().getMethods()){ URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(requestMethod.name(), url.getPatternString(), method.getDeclaringClass().getName())); } } 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 new file mode 100644 index 000000000..d1b94dc24 --- /dev/null +++ b/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/APIEndpointTest.java @@ -0,0 +1,63 @@ +package com.nr.agent.security.instrumentation.spring.webmvc; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +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 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 java.util.HashMap; +import java.util.Set; + +@RunWith(SecurityInstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"org.springframework.web.servlet"}) +public class APIEndpointTest { + + TestHandlerMethodMapping methodMapping = new TestHandlerMethodMapping(); + private final String handler = TestMappings.class.getName(); + + private final static HashMap map = new HashMap<>(); + + + @BeforeClass + public static void addMappings() { + map.put("/postMapping", "POST"); + map.put("/requestMapping", "GET"); + map.put("/getMapping", "GET"); + map.put("/patchMapping", "PATCH"); + map.put("/putMapping", "PUT"); + map.put("/deleteMapping", "DELETE"); + } + + @Test + public void testAPIEndpointDetection() { + methodMapping.addMapping(new TestMappings()); + + Set mappings = URLMappingsHelper.getApplicationURLMappings(); + Assert.assertEquals(6, mappings.size()); + + for (ApplicationURLMapping mapping: mappings) { + Assert.assertNotNull(mapping); + assertMapping(mapping); + } + + } + + private void assertMapping(ApplicationURLMapping actualMapping) { + String path = actualMapping.getPath(); + String method = map.get(path); + Assert.assertEquals(method, actualMapping.getMethod()); + Assert.assertEquals(handler, actualMapping.getHandler()); + } +} + +class TestHandlerMethodMapping extends RequestMappingHandlerMapping { + + public void addMapping(Object handler) { + super.detectHandlerMethods(handler); + } +} \ 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/TestMappings.java b/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/TestMappings.java new file mode 100644 index 000000000..fbba258fd --- /dev/null +++ b/instrumentation-security/spring-webmvc-5.3.0/src/test/java/com/nr/agent/security/instrumentation/spring/webmvc/TestMappings.java @@ -0,0 +1,49 @@ +package com.nr.agent.security.instrumentation.spring.webmvc;/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class TestMappings { + + @RequestMapping(value = "/requestMapping", method = RequestMethod.GET) + public String testRequest() { + return "From Request Mapping"; + } + + @GetMapping(value = "/getMapping") + public String testGet() { + return "From Get Mapping"; + } + + @PostMapping(value = "/postMapping") + public String testPost() { + return "From Post Mapping"; + } + + @PatchMapping(value = "/patchMapping") + public String testPatch() { + return "From Patch Mapping"; + } + + @PutMapping(value = "/putMapping") + public String testPut() { + return "From Put Mapping"; + } + + @DeleteMapping(value = "/deleteMapping") + public String testDelete() { + return "From Delete Mapping"; + } +} diff --git a/instrumentation-security/spymemcached-2.12.0/src/main/java/com/newrelic/agent/security/instrumentation/spy/memcached/MemcachedHelper.java b/instrumentation-security/spymemcached-2.12.0/src/main/java/com/newrelic/agent/security/instrumentation/spy/memcached/MemcachedHelper.java index 2d0220954..cef4b3e35 100644 --- a/instrumentation-security/spymemcached-2.12.0/src/main/java/com/newrelic/agent/security/instrumentation/spy/memcached/MemcachedHelper.java +++ b/instrumentation-security/spymemcached-2.12.0/src/main/java/com/newrelic/agent/security/instrumentation/spy/memcached/MemcachedHelper.java @@ -18,7 +18,7 @@ public class MemcachedHelper { public static final String METHOD_ASYNC_CAS = "asyncCAS"; private static final String SPYMEMCACHED_2_12_0 = "SPYMEMCACHED-2.12.0"; - public static AbstractOperation preprocessSecurityHook(String command, String type, String key, Object val, String klass, String method) { + public static AbstractOperation preprocessSecurityHook(String type, String command, String key, Object val, String klass, String method) { try { if (!NewRelicSecurity.isHookProcessingActive() || NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty()){ return null; diff --git a/instrumentation-security/spymemcached-2.12.0/src/test/java/com/nr/agent/security/instrumentation/memcached/test/MemcachedTest.java b/instrumentation-security/spymemcached-2.12.0/src/test/java/com/nr/agent/security/instrumentation/memcached/test/MemcachedTest.java index 22f798b3e..2bfb72ddc 100644 --- a/instrumentation-security/spymemcached-2.12.0/src/test/java/com/nr/agent/security/instrumentation/memcached/test/MemcachedTest.java +++ b/instrumentation-security/spymemcached-2.12.0/src/test/java/com/nr/agent/security/instrumentation/memcached/test/MemcachedTest.java @@ -1,6 +1,7 @@ package com.nr.agent.security.instrumentation.memcached.test; import com.github.mwarc.embeddedmemcached.JMemcachedServer; +import com.newrelic.agent.security.instrumentation.spy.memcached.MemcachedHelper; import com.newrelic.agent.security.introspec.InstrumentationTestConfig; import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; import com.newrelic.agent.security.introspec.SecurityIntrospector; @@ -8,6 +9,7 @@ import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.MemcachedOperation; import net.spy.memcached.MemcachedClient; +import net.spy.memcached.ops.StoreType; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -68,7 +70,7 @@ public void testSet() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), WRITE, "asyncStore"); + verifier(operation, Arrays.asList(key, value), WRITE, "asyncStore", "set"); } @Test @@ -80,7 +82,7 @@ public void testAdd() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), WRITE, "asyncStore"); + verifier(operation, Arrays.asList(key, value), WRITE, "asyncStore", "add"); } @Test public void testReplace() { @@ -91,7 +93,7 @@ public void testReplace() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), WRITE, "asyncStore"); + verifier(operation, Arrays.asList(key, value), WRITE, "asyncStore", "replace"); } @Test public void testAppend() { @@ -102,7 +104,7 @@ public void testAppend() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), UPDATE, "asyncCat"); + verifier(operation, Arrays.asList(key, value), UPDATE, "asyncCat", "append"); } @Test public void testPrepend() { @@ -113,7 +115,7 @@ public void testPrepend() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), UPDATE, "asyncCat"); + verifier(operation, Arrays.asList(key, value), UPDATE, "asyncCat", "prepend"); } @Test public void testPrepend1() { @@ -124,7 +126,7 @@ public void testPrepend1() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), UPDATE, "asyncCat"); + verifier(operation, Arrays.asList(key, value), UPDATE, "asyncCat", "prepend"); } @Test public void testCas() { @@ -135,7 +137,7 @@ public void testCas() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), WRITE, "asyncCAS"); + verifier(operation, Arrays.asList(key, value), WRITE, "asyncCAS", "set"); } @Test public void testCas1() { @@ -146,7 +148,7 @@ public void testCas1() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), WRITE, "asyncCAS"); + verifier(operation, Arrays.asList(key, value), WRITE, "asyncCAS", "set"); } @Test @@ -158,7 +160,7 @@ public void testAsyncCAS() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), WRITE, "asyncCAS"); + verifier(operation, Arrays.asList(key, value), WRITE, "asyncCAS", "set"); } @Test public void testAsyncCAS1() { @@ -169,14 +171,15 @@ public void testAsyncCAS1() { Assert.assertEquals("No operations detected.", 1, operations.size()); MemcachedOperation operation = (MemcachedOperation) operations.get(0); - verifier(operation, Arrays.asList(key, value), WRITE, "asyncCAS"); + verifier(operation, Arrays.asList(key, value), WRITE, "asyncCAS", "set"); } - private void verifier(MemcachedOperation operation, List args, String type, String method) { + private void verifier(MemcachedOperation operation, List args, String type, String method, String command) { Assert.assertEquals("Incorrect executed parameters.", args, operation.getArguments()); Assert.assertEquals("Incorrect event case type.", VulnerabilityCaseType.CACHING_DATA_STORE, operation.getCaseType()); Assert.assertEquals("Incorrect event category.", MemcachedOperation.MEMCACHED, operation.getCategory()); - Assert.assertEquals("Incorrect event category.", type, operation.getType()); + Assert.assertEquals("Incorrect event category.", type, operation.getCommand()); + Assert.assertEquals("Incorrect event category.", command, operation.getType()); Assert.assertEquals("Incorrect executed class-name.", memcachedClient.getClass().getName(), operation.getClassName()); Assert.assertEquals("Incorrect executed method-name.", method, operation.getMethodName()); } diff --git a/instrumentation-security/sun-net-httpserver/build.gradle b/instrumentation-security/sun-net-httpserver/build.gradle index 89575877f..28649f7f9 100644 --- a/instrumentation-security/sun-net-httpserver/build.gradle +++ b/instrumentation-security/sun-net-httpserver/build.gradle @@ -6,4 +6,8 @@ dependencies { jar { manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.sun-net-httpserver' } +} + +verifyInstrumentation { + passes("com.sun.net.httpserver:http:20070405") } \ No newline at end of file diff --git a/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/BasicAuthenticator_Instrumentation.java b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/BasicAuthenticator_Instrumentation.java new file mode 100644 index 000000000..7d56b0a53 --- /dev/null +++ b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/BasicAuthenticator_Instrumentation.java @@ -0,0 +1,20 @@ +package com.sun.net.httpserver; + +import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(type = MatchType.BaseClass, originalName = "com.sun.net.httpserver.BasicAuthenticator") +public class BasicAuthenticator_Instrumentation { + + public boolean checkCredentials (String username, String password) { + ServletHelper.registerUserLevelCode(HttpServerHelper.SUN_NET_HTTP_SERVER); + return Weaver.callOriginal(); + } + + public Authenticator.Result authenticate (HttpExchange t){ + ServletHelper.registerUserLevelCode(HttpServerHelper.SUN_NET_HTTP_SERVER); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/Filter_Instrumentation.java b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/Filter_Instrumentation.java index 463296ad0..54518e111 100644 --- a/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/Filter_Instrumentation.java +++ b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/Filter_Instrumentation.java @@ -24,6 +24,7 @@ public void doFilter (HttpExchange exchange, Filter.Chain chain) throws IOExcept if (isServletLockAcquired){ preprocessSecurityHook(exchange); } + ServletHelper.registerUserLevelCode(HttpServerHelper.SUN_NET_HTTP_SERVER); try{ Weaver.callOriginal(); } finally { @@ -69,7 +70,6 @@ private void preprocessSecurityHook(HttpExchange exchange) { } securityRequest.setContentType(HttpServerHelper.getContentType(exchange.getRequestHeaders())); - ServletHelper.registerUserLevelCode("sun-net-http-server"); securityRequest.setRequestParsed(true); } catch (Throwable e){ NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, HttpServerHelper.SUN_NET_HTTPSERVER, e.getMessage()), e, this.getClass().getName()); diff --git a/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpContext_Instrumentation.java b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpContext_Instrumentation.java new file mode 100644 index 000000000..4de7f71ea --- /dev/null +++ b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpContext_Instrumentation.java @@ -0,0 +1,23 @@ +package com.sun.net.httpserver; + +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +import static com.sun.net.httpserver.HttpServerHelper.HTTP_METHOD; + +@Weave(originalName = "com.sun.net.httpserver.HttpContext", type = MatchType.BaseClass) +public abstract class HttpContext_Instrumentation { + public abstract String getPath(); + + public void setHandler (HttpHandler h) { + try { + Weaver.callOriginal(); + } finally { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(HTTP_METHOD, getPath(), h.getClass().getName())); + } + } + +} diff --git a/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpHandler_Instrumentation.java b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpHandler_Instrumentation.java index b49501523..04e5f0727 100644 --- a/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpHandler_Instrumentation.java +++ b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpHandler_Instrumentation.java @@ -24,6 +24,7 @@ public void handle (HttpExchange exchange) throws IOException { if (isServletLockAcquired){ preprocessSecurityHook(exchange); } + ServletHelper.registerUserLevelCode(HttpServerHelper.SUN_NET_HTTP_SERVER); try{ Weaver.callOriginal(); } finally { @@ -69,8 +70,6 @@ private void preprocessSecurityHook(HttpExchange exchange) { } securityRequest.setContentType(HttpServerHelper.getContentType(exchange.getRequestHeaders())); - - ServletHelper.registerUserLevelCode("sun-net-http-server"); securityRequest.setRequestParsed(true); } catch (Throwable e){ NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_GENERATING_HTTP_REQUEST, HttpServerHelper.SUN_NET_HTTPSERVER, e.getMessage()), e, this.getClass().getName()); diff --git a/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpServerHelper.java b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpServerHelper.java index 41b5c5962..0ee68c2ee 100644 --- a/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpServerHelper.java +++ b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpServerHelper.java @@ -2,6 +2,7 @@ import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.schema.AgentMetaData; import com.newrelic.api.agent.security.schema.HttpRequest; @@ -23,6 +24,8 @@ public class HttpServerHelper { public static final String HTTPS_PROTOCOL = "https"; private static final String REQUEST_INPUTSTREAM_HASH = "REQUEST_INPUTSTREAM_HASH"; public static final String SUN_NET_READER_OPERATION_LOCK = "SUN_NET_READER_OPERATION_LOCK-"; + public static final String HTTP_METHOD = "*"; + public static final String SUN_NET_HTTP_SERVER = "sun-net-http-server"; public static void processHttpRequestHeaders(Headers headers, HttpRequest securityRequest){ for (String headerKey : headers.keySet()) { @@ -44,6 +47,9 @@ public static void processHttpRequestHeaders(Headers headers, HttpRequest securi } else if(GenericHelper.CSEC_PARENT_ID.equals(headerKey)) { NewRelicSecurity.getAgent().getSecurityMetaData() .addCustomAttribute(GenericHelper.CSEC_PARENT_ID, headers.getFirst(headerKey)); + } else if (ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST.equals(headerKey)) { + NewRelicSecurity.getAgent().getSecurityMetaData() + .addCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, true); } String headerFullValue = EMPTY; for (String headerValue : headers.get(headerKey)) { diff --git a/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpServer_Instrumentation.java b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpServer_Instrumentation.java new file mode 100644 index 000000000..42dcf740c --- /dev/null +++ b/instrumentation-security/sun-net-httpserver/src/main/java/com/sun/net/httpserver/HttpServer_Instrumentation.java @@ -0,0 +1,22 @@ +package com.sun.net.httpserver; + +import com.newrelic.api.agent.security.instrumentation.helpers.URLMappingsHelper; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +import static com.sun.net.httpserver.HttpServerHelper.HTTP_METHOD; + +@Weave(originalName = "com.sun.net.httpserver.HttpServer", type = MatchType.BaseClass) +public class HttpServer_Instrumentation { + public HttpContext createContext (String path, HttpHandler handler){ + HttpContext context; + try { + context = Weaver.callOriginal(); + } finally { + URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(HTTP_METHOD, path, handler.getClass().getName())); + } + return context; + } +} diff --git a/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/HttpServerTest.java b/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/HttpServerTest.java index ad42038af..6f56e44cd 100644 --- a/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/HttpServerTest.java +++ b/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/HttpServerTest.java @@ -6,7 +6,9 @@ import com.newrelic.api.agent.Trace; 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.URLMappingsHelper; import com.newrelic.api.agent.security.schema.AbstractOperation; +import com.newrelic.api.agent.security.schema.ApplicationURLMapping; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; import com.newrelic.security.test.marker.Java11IncompatibleTest; @@ -25,8 +27,10 @@ import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; @RunWith(SecurityInstrumentationTestRunner.class) @@ -126,6 +130,24 @@ public void testHandle2() throws URISyntaxException, IOException, InterruptedExc Assert.assertEquals("Wrong hashcode detected", Collections.singleton(expectedHash), introspector.getRequestInStreamHash()); } + @Test + public void testURLMapping() { + Iterator urlMappings = URLMappingsHelper.getApplicationURLMappings().iterator(); + Assert.assertTrue("should have elements", urlMappings.hasNext()); + + ApplicationURLMapping urlMapping = urlMappings.next(); + Assert.assertEquals("invalid handler", Httpserver.Handler.class.getName(), urlMapping.getHandler()); + Assert.assertEquals("invalid http-method", "*", urlMapping.getMethod()); + Assert.assertEquals("invalid path", "/", urlMapping.getPath()); + + Assert.assertTrue("should have elements", urlMappings.hasNext()); + + urlMapping = urlMappings.next(); + Assert.assertEquals("invalid handler", Httpserver.Handler.class.getName(), urlMapping.getHandler()); + Assert.assertEquals("invalid http-method", "*", urlMapping.getMethod()); + Assert.assertEquals("invalid path", "/new", urlMapping.getPath()); + } + @Trace(dispatcher = true) private void handle() throws URISyntaxException, IOException { URL url = server.getEndPoint().toURL(); diff --git a/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/Httpserver.java b/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/Httpserver.java index 9af1c767c..12a32c704 100644 --- a/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/Httpserver.java +++ b/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/Httpserver.java @@ -1,5 +1,6 @@ package com.nr.agent.security.instrumentation.httpServer.test; +import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; @@ -29,7 +30,12 @@ protected void after() { public void startServer() throws IOException { server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), PORT), 0); server.createContext("/", new Handler()); + + // creating HttpContext and will specify the handler later + HttpContext context = server.createContext("/new"); server.setExecutor(null); + + context.setHandler(new Handler()); server.start(); } diff --git a/instrumentation-security/websphere-8/build.gradle b/instrumentation-security/websphere-8/build.gradle new file mode 100644 index 000000000..0250668d8 --- /dev/null +++ b/instrumentation-security/websphere-8/build.gradle @@ -0,0 +1,30 @@ +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("javax.servlet:javax.servlet-api:3.0.1") + implementation(fileTree(include: ["*.jar"], dir: "lib")) +} + +def shouldBuild = fileTree(include: ["*.jar"], dir: "lib").size() > 0 + +compileJava { + enabled(shouldBuild) +} + +compileTestJava { + enabled(shouldBuild) +} + +tasks.getByName("writeCachedWeaveAttributes").enabled(shouldBuild) + +jar { + enabled(shouldBuild) + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.websphere-8' } +} + +site { + title 'WebSphere' + type 'Appserver' + versionOverride '[8,9)' +} \ No newline at end of file diff --git a/instrumentation-security/websphere-8/lib/.gitignore b/instrumentation-security/websphere-8/lib/.gitignore new file mode 100644 index 000000000..c96a04f00 --- /dev/null +++ b/instrumentation-security/websphere-8/lib/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/instrumentation-security/websphere-8/src/main/java/com/ibm/ws/webcontainer/component/WebContainerImpl.java b/instrumentation-security/websphere-8/src/main/java/com/ibm/ws/webcontainer/component/WebContainerImpl.java new file mode 100644 index 000000000..2b28914d4 --- /dev/null +++ b/instrumentation-security/websphere-8/src/main/java/com/ibm/ws/webcontainer/component/WebContainerImpl.java @@ -0,0 +1,66 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.ibm.ws.webcontainer.component; + +import java.text.MessageFormat; +import java.util.logging.Level; + +import javax.management.ObjectName; + +import com.ibm.websphere.management.Session; +import com.ibm.websphere.management.configservice.ConfigService; +import com.ibm.websphere.management.configservice.ConfigServiceFactory; +import com.ibm.websphere.management.configservice.ConfigServiceHelper; +import com.ibm.websphere.product.WASDirectory; +import com.ibm.websphere.product.WASProductInfo; +import com.ibm.websphere.runtime.ServerName; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave +public class WebContainerImpl { + + public void start() { +// String instanceName = ServerName.getFullName(); +// if (instanceName != null) { +// AgentBridge.publicApi.setInstanceName(instanceName); +// } + Integer port = getServerPort(); + if (port != null) { + //TODO find protocol + NewRelicSecurity.getAgent().setApplicationConnectionConfig(port, "http"); +// AgentBridge.publicApi.setAppServerPort(port); + } + Weaver.callOriginal(); + } + + private Integer getServerPort() { + try { + ConfigService cs = ConfigServiceFactory.getConfigService(); + Session session = new Session(); + ObjectName[] serverIndexONs = cs.resolve(session, "ServerIndex="); + ObjectName[] namedEndPointsONs = cs.queryConfigObjects(session, serverIndexONs[0], + ConfigServiceHelper.createObjectName(null, "NamedEndPoint"), null); + for (ObjectName namedEndPointsON : namedEndPointsONs) { + String endPointName = (String) cs.getAttribute(session, namedEndPointsON, "endPointName"); + if (endPointName.equals("WC_defaulthost")) { + Integer port = (Integer) cs.getAttribute(session, (cs.queryConfigObjects(session, namedEndPointsON, + ConfigServiceHelper.createObjectName(null, "EndPoint"), null)[0]), "port"); + return port; + } + } + } catch (Exception ex) { + NewRelicSecurity.getAgent().log(LogLevel.FINER, "Exception getting port", ex, this.getClass().getName()); +// AgentBridge.getAgent().getLogger().log(Level.FINER, ex, "Exception getting port"); + } + return null; + } + +} \ No newline at end of file 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 244cb4fd3..af043606f 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 @@ -17,6 +17,7 @@ import org.apache.commons.lang3.StringUtils; import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -29,7 +30,8 @@ public class AgentConfig { public static final String CLEANING_STATUS_SNAPSHOTS_FROM_LOG_DIRECTORY_MAX_S_FILE_COUNT_REACHED_REMOVED_S = "Cleaning status-snapshots from snapshots directory, max %s file count reached removed : %s"; - private static final Object lock = new Object(); + public static final String AGENT_JAR_LOCATION = "agent_jar_location"; + public static final String AGENT_HOME = "agent_home"; private String NR_CSEC_HOME; private String logLevel; @@ -49,7 +51,14 @@ private AgentConfig(){ public void instantiate(){ //Set k2 home path - boolean validHomePath = setK2HomePath(); + try { + boolean validHomePath = setK2HomePath(); + System.out.println("New Relic Security Agent: Setting csec home path to directory:"+NR_CSEC_HOME); + } catch (IOException e) { + String tmpDir = System.getProperty("java.io.tmpdir"); + System.err.println("[NR-CSEC-JA] "+e.getMessage()+" Please find the error in " + tmpDir + File.separator + "NR-CSEC-Logger.err"); + throw new RuntimeException("CSEC Agent Exiting!!! Unable to create csec home directory", e); + } isNRSecurityEnabled = NewRelic.getAgent().getConfig().getValue(IUtilConstants.NR_SECURITY_ENABLED, false); // Set required Group groupName = applyRequiredGroup(); @@ -94,21 +103,28 @@ private String applyRequiredLogLevel() { return logLevel; } - public boolean setK2HomePath() { - if (NewRelic.getAgent().getConfig().getValue("agent_home") != null) { - NR_CSEC_HOME = NewRelic.getAgent().getConfig().getValue("agent_home"); + public boolean setK2HomePath() throws IOException { + String agentJarLocation = NewRelic.getAgent().getConfig().getValue(AGENT_JAR_LOCATION); + if (NewRelic.getAgent().getConfig().getValue(AGENT_HOME) != null) { + NR_CSEC_HOME = NewRelic.getAgent().getConfig().getValue(AGENT_HOME); + } else if (StringUtils.isNotBlank(agentJarLocation)){ + //fallback to agent_jar_location as home + NR_CSEC_HOME = agentJarLocation; } else { - NR_CSEC_HOME = "."; + System.err.println("[NR-CSEC-JA] Missing or Incorrect system property `newrelic.home` or environment variable `NEWRELIC_HOME`. Collector exiting."); + return false; } Path k2homePath = Paths.get(NR_CSEC_HOME, IUtilConstants.NR_SECURITY_HOME); - CommonUtils.forceMkdirs(k2homePath, DIRECTORY_PERMISSION); + if(!CommonUtils.forceMkdirs(k2homePath, DIRECTORY_PERMISSION)){ + System.err.println(String.format("[NR-CSEC-JA] CSEC home directory creation failed at %s", NR_CSEC_HOME)); + return false; + } NR_CSEC_HOME = k2homePath.toString(); AgentUtils.getInstance().getStatusLogValues().put("csec-home", NR_CSEC_HOME); AgentUtils.getInstance().getStatusLogValues().put("csec-home-permissions", String.valueOf(k2homePath.toFile().canWrite() && k2homePath.toFile().canRead())); - AgentUtils.getInstance().getStatusLogValues().put("agent-location", - NewRelic.getAgent().getConfig().getValue("agent_jar_location")); + AgentUtils.getInstance().getStatusLogValues().put("agent-location", agentJarLocation); if (!isValidK2HomePath(NR_CSEC_HOME)) { - System.err.println("[NR-CSEC-JA] Incomplete startup env parameters provided : Missing or Incorrect NR_CSEC_HOME. Collector exiting."); + System.err.println("[NR-CSEC-JA] Incomplete startup env parameters provided : Missing or Incorrect 'newrelic.home'. Collector exiting."); return false; } return true; @@ -148,7 +164,7 @@ public void setConfig(CollectorConfig config) { this.config = config; } - public void createSnapshotDirectory() { + public void createSnapshotDirectory() throws IOException { Path snapshotDir = Paths.get(osVariables.getSnapshotDir()); // Remove any file with this name from target. if (!snapshotDir.toFile().isDirectory()) { @@ -163,13 +179,17 @@ private void keepMaxStatusLogFiles(int max) { File[] sortedStatusFiles = statusFiles.toArray(new File[0]); Arrays.sort(sortedStatusFiles, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR); FileUtils.deleteQuietly(sortedStatusFiles[0]); - logger.log(LogLevel.INFO, String.format(CLEANING_STATUS_SNAPSHOTS_FROM_LOG_DIRECTORY_MAX_S_FILE_COUNT_REACHED_REMOVED_S, max, sortedStatusFiles[0].getAbsolutePath()), FileLoggerThreadPool.class.getName()); + logger.log(LogLevel.INFO, String.format(CLEANING_STATUS_SNAPSHOTS_FROM_LOG_DIRECTORY_MAX_S_FILE_COUNT_REACHED_REMOVED_S, max, sortedStatusFiles[0].getAbsolutePath()), AgentConfig.class.getName()); } } public void setupSnapshotDir() { - createSnapshotDirectory(); - keepMaxStatusLogFiles(100); + try { + createSnapshotDirectory(); + keepMaxStatusLogFiles(100); + } catch (Exception e) { + logger.log(LogLevel.WARNING, String.format("Snapshot directory creation failed !!! Please check file permissions. error:%s ", e.getMessage()), e, AgentConfig.class.getName()); + } } public String getGroupName() { diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/dispatcher/Dispatcher.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/dispatcher/Dispatcher.java index a2a04577a..f9755809a 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/dispatcher/Dispatcher.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/dispatcher/Dispatcher.java @@ -510,6 +510,15 @@ private JavaAgentEventBean prepareSQLDbCommandEvent(SQLOperation operation, if(operation.getParams() != null) { query.put(PARAMETERS, new JSONObject(operation.getParams())); } + if(operation.getObjectParams() != null && !operation.getObjectParams().isEmpty()){ + JSONObject jsonObject = (JSONObject) query.get(PARAMETERS); + if(jsonObject == null){ + query.put(PARAMETERS, jsonObject); + } + for (Map.Entry objParameter : operation.getObjectParams().entrySet()) { + jsonObject.put(objParameter.getKey(), JsonConverter.toJSON(objParameter.getValue())); + } + } params.add(query); eventBean.setParameters(params); if (operation.isStoredProcedureCall()) { diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RequestUtils.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RequestUtils.java index 5a3459062..b518f2d63 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RequestUtils.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RequestUtils.java @@ -2,6 +2,7 @@ import com.newrelic.agent.security.intcodeagent.filelogging.FileLoggerThreadPool; import com.newrelic.agent.security.intcodeagent.websocket.JsonConverter; +import com.newrelic.api.agent.security.instrumentation.helpers.ICsecApiConstants; import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.agent.security.intcodeagent.models.FuzzRequestBean; @@ -10,6 +11,9 @@ import okhttp3.internal.http.HttpMethod; import org.apache.commons.lang3.StringUtils; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -18,6 +22,31 @@ public class RequestUtils { private static final FileLoggerThreadPool logger = FileLoggerThreadPool.getInstance(); public static final String ERROR_IN_FUZZ_REQUEST_GENERATION = "Error in fuzz request generation {}"; + public static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded"; + + public static Request generateHeadRequest(FuzzRequestBean httpRequest, String endpoint) { + try { + logger.log(LogLevel.FINEST, String.format("Generate HEAD request : %s%s", endpoint, httpRequest.getUrl()), RequestUtils.class.getName()); + StringBuilder url = new StringBuilder(endpoint); + url.append(httpRequest.getUrl()); + Request request = new Request.Builder() + .url(url.toString()) + .head() // Use HEAD to fetch only headers without the body + .headers(Headers.of((Map) httpRequest.getHeaders())) + .addHeader(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, "true") + .build(); + return request; + } catch (Exception e) { + logger.log(LogLevel.FINEST, String.format("Error while Generating HEAD request : %s", JsonConverter.toJSON(httpRequest.getUrl())), RequestUtils.class.getName()); + } + return null; + } + + + public static boolean refineEndpoints(FuzzRequestBean httpRequest, String endpoint) { + Request request = generateHeadRequest(httpRequest, endpoint); + return RestClient.getInstance().isListening(request); + } public static Request generateK2Request(FuzzRequestBean httpRequest, String endpoint) { try { @@ -27,7 +56,7 @@ public static Request generateK2Request(FuzzRequestBean httpRequest, String endp RequestBody requestBody = null; if (StringUtils.isNotBlank(httpRequest.getContentType())) { - if (httpRequest.getParameterMap() != null && !httpRequest.getParameterMap().isEmpty()) { + if (httpRequest.getParameterMap() != null && !httpRequest.getParameterMap().isEmpty() && StringUtils.startsWith(httpRequest.getContentType(), APPLICATION_X_WWW_FORM_URLENCODED)) { FormBody.Builder builder = new FormBody.Builder(); for (Entry param : httpRequest.getParameterMap().entrySet()) { for (int i = 0; i < param.getValue().length; i++) { @@ -35,11 +64,12 @@ public static Request generateK2Request(FuzzRequestBean httpRequest, String endp } } requestBody = builder.build(); - } else { + } else if( StringUtils.isNotBlank(httpRequest.getBody().toString())) { requestBody = RequestBody.create(httpRequest.getBody().toString(), MediaType.parse(httpRequest.getContentType())); } - } else if (StringUtils.equalsIgnoreCase(httpRequest.getMethod(), "POST")) { + } + if (requestBody == null && HttpMethod.permitsRequestBody(httpRequest.getMethod())) { requestBody = RequestBody.create(httpRequest.getBody().toString(), null); } @@ -91,4 +121,6 @@ public static String extractNRCsecFuzzReqHeader(Headers headers) { } return null; } + + } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RestClient.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RestClient.java index 49b4e4f6b..ec44bec82 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RestClient.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RestClient.java @@ -26,7 +26,7 @@ public class RestClient { public static final String REQUEST_FIRED_SUCCESS = "Request Fired successfuly : %s "; public static final String REQUEST_SUCCESS_S_RESPONSE_S_S = "Request Fired successfuly : %s :: response : %s : %s"; - public static final String CALL_FAILED_REQUEST_S_REASON = "Call failed : request %s reason : "; + public static final String CALL_FAILED_REQUEST_S_REASON = "Call failed : request %s reason : %s "; public static final String CALL_FAILED_REQUEST_S_REASON_S = "Call failed : request %s reason : %s : body : %s"; public static final String FIRING_REQUEST_METHOD_S = "Firing request :: Method : %s"; @@ -71,7 +71,7 @@ protected OkHttpClient initialValue() { try { ConnectionPool connectionPool = new ConnectionPool(1, 5, TimeUnit.MINUTES); builder = builder.connectionPool(connectionPool); - builder = builder.callTimeout(10, TimeUnit.SECONDS); + builder = builder.callTimeout(5, TimeUnit.SECONDS); // Install the all-trusting trust manager final SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); @@ -134,9 +134,9 @@ public void fireRequest(FuzzRequestBean httpRequest, List endpoints, int NewRelicSecurity.getAgent().reportIASTScanFailure(null, null, e, RequestUtils.extractNRCsecFuzzReqHeader(httpRequest), fuzzRequestId, String.format(IAgentConstants.SSL_EXCEPTION_FAILURE_MESSAGE, request.url())); - logger.log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, request), e, RestClient.class.getName()); + logger.log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, request, e.getMessage()), e, RestClient.class.getName()); logger.postLogMessageIfNecessary(LogLevel.WARNING, - String.format(CALL_FAILED_REQUEST_S_REASON, fuzzRequestId), + String.format(CALL_FAILED_REQUEST_S_REASON, fuzzRequestId, e.getMessage()), e, RestRequestProcessor.class.getName()); RestRequestThreadPool.getInstance().getProcessedIds().putIfAbsent(fuzzRequestId, new HashSet<>()); // TODO: Add to fuzz fail count in HC and remove FuzzFailEvent if not needed. @@ -156,13 +156,35 @@ public void fireRequest(FuzzRequestBean httpRequest, List endpoints, int if(responseCode == 301){continue;} break; } catch (SSLException e){ - logger.log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, request), e, RestClient.class.getName()); + logger.log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, e.getMessage(), request), e, RestClient.class.getName()); } } } + public boolean isListening(Request request) { + if(request == null){ + return false; + } + + OkHttpClient client = clientThreadLocal.get(); + Call call = client.newCall(request); + try(Response response = call.execute()) { + if(response.isSuccessful()){ + logger.log(LogLevel.FINER, String.format("Server is reachable url: %s", request.url()), RestClient.class.getName()); + return true; + } + if (client.connectionPool() != null) { + client.connectionPool().evictAll(); + } + } catch (IOException e) { + logger.log(LogLevel.FINER, String.format("Server is not reachable url: %s", request.url()), RestClient.class.getName()); + return false; + } + return false; + } + public int fireRequest(Request request, int repeatCount, String fuzzRequestId) throws SSLException { OkHttpClient client = clientThreadLocal.get(); @@ -171,8 +193,7 @@ public int fireRequest(Request request, int repeatCount, String fuzzRequestId) t logger.log(LogLevel.FINER, String.format(FIRING_REQUEST_HEADERS_S, request.headers()), RestClient.class.getName()); Call call = client.newCall(request); - try { - Response response = call.execute(); + try (Response response = call.execute()){ logger.log(LogLevel.FINER, String.format(REQUEST_FIRED_SUCCESS, request), RestClient.class.getName()); if (response.code() >= 500) { logger.postLogMessageIfNecessary(LogLevel.WARNING, @@ -199,7 +220,7 @@ else if(response.code() >= 400){ } return response.code(); } catch (SSLException e){ - logger.log(LogLevel.FINE, String.format("Request failed due to SSL Exception %s ", request, e), RestClient.class.getName()); + logger.log(LogLevel.FINE, String.format("Request failed due to SSL Exception %s : reason %s", request, e.getMessage()), e, RestClient.class.getName()); throw e; } catch (InterruptedIOException e){ if(repeatCount >= 0){ @@ -210,15 +231,17 @@ else if(response.code() >= 400){ e, RequestUtils.extractNRCsecFuzzReqHeader(request.headers()), fuzzRequestId, IAgentConstants.REQUEST_FAILURE_DUE_TO_IOEXCEPTION); - logger.log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, request), e, RestClient.class.getName()); + logger.log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, e.getMessage(), request), e, RestClient.class.getName()); logger.postLogMessageIfNecessary(LogLevel.WARNING, - String.format(CALL_FAILED_REQUEST_S_REASON, fuzzRequestId), + String.format(CALL_FAILED_REQUEST_S_REASON, e.getMessage(), fuzzRequestId), e, RestRequestProcessor.class.getName()); RestRequestThreadPool.getInstance().getProcessedIds().putIfAbsent(fuzzRequestId, new HashSet<>()); // TODO: Add to fuzz fail count in HC and remove FuzzFailEvent if not needed. FuzzFailEvent fuzzFailEvent = new FuzzFailEvent(AgentInfo.getInstance().getApplicationUUID()); fuzzFailEvent.setFuzzHeader(request.header(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID)); EventSendPool.getInstance().sendEvent(fuzzFailEvent); + } catch (Exception e){ + logger.log(LogLevel.FINER, String.format(CALL_FAILED_REQUEST_S_REASON, e.getMessage(), request), e, RestClient.class.getName()); } return 999; diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RestRequestProcessor.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RestRequestProcessor.java index 2adf2bc41..021694b94 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RestRequestProcessor.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/httpclient/RestRequestProcessor.java @@ -8,6 +8,7 @@ import com.newrelic.agent.security.intcodeagent.filelogging.FileLoggerThreadPool; import com.newrelic.agent.security.intcodeagent.logging.IAgentConstants; import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.schema.ServerConnectionConfiguration; import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.agent.security.intcodeagent.models.FuzzRequestBean; import com.newrelic.agent.security.intcodeagent.models.javaagent.IntCodeControlCommand; @@ -37,6 +38,7 @@ public class RestRequestProcessor implements Callable { public static final String JSON_PARSING_ERROR_WHILE_PROCESSING_FUZZING_REQUEST_S = "JSON parsing error while processing fuzzing request : %s"; private static final int MAX_REPETITION = 3; + public static final String ENDPOINT_LOCALHOST_S = "%s://localhost:%s"; private IntCodeControlCommand controlCommand; private int repeatCount; @@ -104,7 +106,14 @@ public Boolean call() throws InterruptedException { MonitorGrpcFuzzFailRequestQueueThread.submitNewTask(); GrpcClientRequestReplayHelper.getInstance().addToRequestQueue(new ControlCommandDto(controlCommand.getId(), httpRequest, payloadList)); } else { - List endpoints = prepareAllEndpoints(NewRelicSecurity.getAgent().getApplicationConnectionConfig()); + boolean postSSL = false; + List endpoints = prepareAllEndpoints(NewRelicSecurity.getAgent().getApplicationConnectionConfig(), httpRequest); + logger.log(LogLevel.FINER, String.format("Endpoints to fire : %s", endpoints), RestRequestProcessor.class.getSimpleName()); + if (endpoints.isEmpty()){ + endpoints = prepareAllEndpoints(httpRequest); + logger.log(LogLevel.FINER, String.format("Endpoints to fire in empty: %s", endpoints), RestRequestProcessor.class.getSimpleName()); + postSSL = true; + } RestClient.getInstance().fireRequest(httpRequest, endpoints, repeatCount + endpoints.size() -1, controlCommand.getId()); } return true; @@ -136,15 +145,38 @@ public Boolean call() throws InterruptedException { return true; } - private List prepareAllEndpoints(Map applicationConnectionConfig) { + private List prepareAllEndpoints(FuzzRequestBean httpRequest) { List endpoitns = new ArrayList<>(); - for (Map.Entry connectionConfig : applicationConnectionConfig.entrySet()) { - endpoitns.add(String.format("%s://localhost:%s", connectionConfig.getValue(), connectionConfig.getKey())); - endpoitns.add(String.format("%s://localhost:%s", toggleProtocol(connectionConfig.getValue()), connectionConfig.getKey())); - } + endpoitns.add(String.format(ENDPOINT_LOCALHOST_S, httpRequest.getProtocol(), httpRequest.getServerPort())); + endpoitns.add(String.format(ENDPOINT_LOCALHOST_S, toggleProtocol(httpRequest.getProtocol()), httpRequest.getServerPort())); return endpoitns; } + private List prepareAllEndpoints(Map applicationConnectionConfig, FuzzRequestBean httpRequest) { + List endpoints = new ArrayList<>(); + for (Map.Entry connectionConfig : applicationConnectionConfig.entrySet()) { + ServerConnectionConfiguration connectionConfiguration = connectionConfig.getValue(); + if(!connectionConfig.getValue().isConfirmed()){ + if (RequestUtils.refineEndpoints(httpRequest, String.format(ENDPOINT_LOCALHOST_S, connectionConfiguration.getProtocol(), connectionConfiguration.getPort()))) { + updateServerConnectionConfiguration(connectionConfiguration, connectionConfiguration.getProtocol()); + endpoints.add(connectionConfiguration.getEndpoint()); + } else if (RequestUtils.refineEndpoints(httpRequest, String.format(ENDPOINT_LOCALHOST_S, toggleProtocol(connectionConfiguration.getProtocol()), connectionConfiguration.getPort()))) { + updateServerConnectionConfiguration(connectionConfiguration, toggleProtocol(connectionConfiguration.getProtocol())); + endpoints.add(connectionConfiguration.getEndpoint()); + } + } else { + endpoints.add(connectionConfiguration.getEndpoint()); + } + } + return endpoints; + } + + private void updateServerConnectionConfiguration(ServerConnectionConfiguration connectionConfiguration, String protocol) { + connectionConfiguration.setEndpoint(String.format(ENDPOINT_LOCALHOST_S, protocol, connectionConfiguration.getPort())); + connectionConfiguration.setProtocol(protocol); + connectionConfiguration.setConfirmed(true); + } + private String toggleProtocol(String value) { return StringUtils.equalsAnyIgnoreCase(value, "https")? "http": "https"; } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/utils/AgentUtils.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/utils/AgentUtils.java index 0aabb9ac1..da5c72381 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/utils/AgentUtils.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/utils/AgentUtils.java @@ -669,6 +669,7 @@ private void applyNRPolicyOverride() { public static void sendApplicationURLMappings() { //TODO mappings to be send once new mappings are discovered, after startup. ApplicationURLMappings applicationURLMappings = new ApplicationURLMappings(URLMappingsHelper.getApplicationURLMappings()); + applicationURLMappings.setApplicationUUID(AgentInfo.getInstance().getApplicationUUID()); logger.logInit(LogLevel.INFO, String.format("Collected application url mappings %s", applicationURLMappings), Agent.class.getName()); EventSendPool.getInstance().sendEvent(applicationURLMappings); } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/filelogging/InitLogWriter.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/filelogging/InitLogWriter.java index 83f8aff4a..ba49ac376 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/filelogging/InitLogWriter.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/filelogging/InitLogWriter.java @@ -1,5 +1,6 @@ package com.newrelic.agent.security.intcodeagent.filelogging; +import com.newrelic.agent.security.AgentConfig; import com.newrelic.agent.security.AgentInfo; import com.newrelic.agent.security.instrumentator.os.OSVariables; import com.newrelic.agent.security.instrumentator.os.OsVariablesInstance; @@ -13,6 +14,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.file.Files; +import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.PosixFilePermissions; import java.text.SimpleDateFormat; @@ -30,6 +32,8 @@ public class InitLogWriter implements Runnable { private static final String STR_COLON = " : "; + public static final String LOGS = "logs"; + public static final String THREAD_NAME_TEMPLATE = " [%s] [%s] "; private static final String LOG_FILE_INITIATED_MSG = "Init Log File initiated.\n"; @@ -74,14 +78,22 @@ public class InitLogWriter implements Runnable { } else { fileName = new File(osVariables.getLogDirectory(), "java-security-collector-init.log").getAbsolutePath(); currentLogFile = new File(fileName); - CommonUtils.forceMkdirs(currentLogFile.getParentFile().toPath(), DIRECTORY_PERMISSION); currentLogFileName = fileName; - createLogFile(); + if(!createLogFile()) { + osVariables.setLogDirectory(Paths.get(AgentConfig.getInstance().getK2Home(), LOGS).toString()); + fileName = new File(osVariables.getLogDirectory(), "java-security-collector-init.log").getAbsolutePath(); + currentLogFile = new File(fileName); + currentLogFileName = fileName; + createLogFile(); + } + } } - private static void createLogFile() { + private static Boolean createLogFile() { try { + CommonUtils.forceMkdirs(currentLogFile.getParentFile().toPath(), DIRECTORY_PERMISSION); + System.out.println("New Relic Security Agent: Writing InitLogs to log file:"+currentLogFile); currentLogFile.setReadable(true, false); writer = new BufferedWriter(new FileWriter(currentLogFileName, true)); writer.write(LOG_FILE_INITIATED_MSG); @@ -95,14 +107,16 @@ private static void createLogFile() { } writer.write(String.format(LOG_CONFIGURED_SUCCESSFULLY_MSG, LogLevel.getLevelName(defaultLogLevel), maxFileSize)); writer.flush(); + return true; } catch (Throwable e) { FileLoggerThreadPool.getInstance().setInitLoggingActive(false); String tmpDir = System.getProperty("java.io.tmpdir"); - System.err.println("[NR-CSEC-JA] Unable to create status log file!!! Please find the error in " + tmpDir + File.separator + "NR-CSEC-Logger.err"); + System.err.println("[NR-CSEC-JA] Init Log : "+e.getMessage()+" Please find the error in " + tmpDir + File.separator + "NR-CSEC-Logger.err"); try { e.printStackTrace(new PrintStream(tmpDir + File.separator + "NR-CSEC-Logger.err")); } catch (FileNotFoundException ex) { } + return false; } } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/filelogging/LogWriter.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/filelogging/LogWriter.java index 5675f8e0f..1fac40082 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/filelogging/LogWriter.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/filelogging/LogWriter.java @@ -1,5 +1,6 @@ package com.newrelic.agent.security.intcodeagent.filelogging; +import com.newrelic.agent.security.AgentConfig; import com.newrelic.agent.security.AgentInfo; import com.newrelic.agent.security.instrumentator.os.OSVariables; import com.newrelic.agent.security.instrumentator.os.OsVariablesInstance; @@ -13,6 +14,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.file.Files; +import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.PosixFilePermissions; import java.text.SimpleDateFormat; @@ -28,6 +30,8 @@ public class LogWriter implements Runnable { private static final String STR_COLON = " : "; + public static final String LOGS = "logs"; + public static final String THREAD_NAME_TEMPLATE = " [%s] [%s] "; public static final String CAUSED_BY = "Caused by: "; @@ -64,9 +68,10 @@ public class LogWriter implements Runnable { private String logTime; private static boolean createLogFile() { - CommonUtils.forceMkdirs(currentLogFile.getParentFile().toPath(), IUtilConstants.DIRECTORY_PERMISSION); try { + CommonUtils.forceMkdirs(currentLogFile.getParentFile().toPath(), IUtilConstants.DIRECTORY_PERMISSION); + System.out.println("New Relic Security Agent: Writing to log file:"+currentLogFile); currentLogFile.setReadable(true, false); writer = new BufferedWriter(new FileWriter(currentLogFileName, true)); @@ -81,7 +86,7 @@ private static boolean createLogFile() { FileLoggerThreadPool.getInstance().setLoggingActive(false); } String tmpDir = System.getProperty("java.io.tmpdir"); - System.err.println("[NR-CSEC-JA] Unable to create log file!!! Please find the error in " + tmpDir + File.separator + "NR-CSEC-Logger.err"); + System.err.println("[NR-CSEC-JA] CSEC Log : "+e.getMessage()+" Please find the error in " + tmpDir + File.separator + "NR-CSEC-Logger.err"); try { e.printStackTrace(new PrintStream(tmpDir + File.separator + "NR-CSEC-Logger.err")); } catch (FileNotFoundException ex) { @@ -98,7 +103,14 @@ private static boolean createLogFile() { fileName = new File(osVariables.getLogDirectory(), "java-security-collector.log").getAbsolutePath(); currentLogFile = new File(fileName); currentLogFileName = fileName; - createLogFile(); + if(!createLogFile()) { + osVariables.setLogDirectory(Paths.get(AgentConfig.getInstance().getK2Home(), LOGS).toString()); + fileName = new File(osVariables.getLogDirectory(), "java-security-collector.log").getAbsolutePath(); + currentLogFile = new File(fileName); + currentLogFileName = fileName; + createLogFile(); + } + } } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/models/javaagent/ApplicationURLMappings.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/models/javaagent/ApplicationURLMappings.java index 6dec9b791..a65c8177c 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/models/javaagent/ApplicationURLMappings.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/models/javaagent/ApplicationURLMappings.java @@ -7,6 +7,7 @@ public class ApplicationURLMappings extends AgentBasicInfo{ + private String applicationUUID; private Set mappings; public ApplicationURLMappings(Set mappings) { @@ -25,4 +26,12 @@ public void setMappings(Set mappings) { public String toString() { return JsonConverter.toJSON(this); } + + public String getApplicationUUID() { + return applicationUUID; + } + + public void setApplicationUUID(String applicationUUID) { + this.applicationUUID = applicationUUID; + } } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/CommonUtils.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/CommonUtils.java index a3b69b60a..4bc17ee4b 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/CommonUtils.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/CommonUtils.java @@ -24,7 +24,7 @@ public class CommonUtils { public static SecureRandom secureRandom = new SecureRandom(); - public static Boolean forceMkdirs(Path directory, String permissions) { + public static Boolean forceMkdirs(Path directory, String permissions) throws IOException { File existingDirectory = directory.toFile(); Stack pathStack = new Stack<>(); while (!existingDirectory.isDirectory()) { @@ -36,11 +36,7 @@ public static Boolean forceMkdirs(Path directory, String permissions) { existingDirectory = next; } - try { - FileUtils.forceMkdir(directory.toFile()); - } catch (IOException e) { - return false; - } + FileUtils.forceMkdir(directory.toFile()); while (!pathStack.isEmpty()) { try { 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 feef8d793..d2f07257d 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 @@ -4,6 +4,7 @@ import com.newrelic.agent.security.AgentInfo; import com.newrelic.agent.security.instrumentator.dispatcher.DispatcherPool; import com.newrelic.agent.security.instrumentator.httpclient.RestRequestThreadPool; +import com.newrelic.agent.security.instrumentator.utils.AgentUtils; import com.newrelic.agent.security.instrumentator.utils.INRSettingsKey; import com.newrelic.agent.security.intcodeagent.controlcommand.ControlCommandProcessor; import com.newrelic.agent.security.intcodeagent.filelogging.FileLoggerThreadPool; @@ -191,7 +192,7 @@ public void onOpen(ServerHandshake handshakedata) { WSUtils.getInstance().notifyAll(); } WSUtils.getInstance().setConnected(true); -// AgentUtils.sendApplicationURLMappings(); + AgentUtils.sendApplicationURLMappings(); logger.logInit(LogLevel.INFO, String.format(IAgentConstants.APPLICATION_INFO_SENT_ON_WS_CONNECT, AgentInfo.getInstance().getApplicationInfo()), WSClient.class.getName()); } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/util/IUtilConstants.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/util/IUtilConstants.java index 79946c855..8764c0b62 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/util/IUtilConstants.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/util/IUtilConstants.java @@ -22,6 +22,8 @@ public interface IUtilConstants { String NR_SECURITY_ENABLED = "security.enabled"; + String NR_SECURITY_HOME_APP = "security.is_home_app"; + String NR_SECURITY_CA_BUNDLE_PATH = "security.ca_bundle_path"; String NR_CSEC_DEBUG_LOGFILE_SIZE = "NR_CSEC_DEBUG_LOGFILE_SIZE"; String NR_CSEC_DEBUG_LOGFILE_MAX_COUNT = "NR_CSEC_DEBUG_LOGFILE_MAX_COUNT"; 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 f91a75f86..20251b010 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 @@ -33,7 +33,6 @@ import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; import java.net.HttpURLConnection; -import java.net.Socket; import java.net.URL; import java.time.Instant; import java.util.ArrayList; @@ -125,6 +124,9 @@ private void initialise() { this.getClass().getName()); logger.logInit(LogLevel.INFO, NewRelic.getAgent().getConfig().getValue(LowSeverityHelper.LOW_SEVERITY_HOOKS_ENABLED, LowSeverityHelper.DEFAULT)? "Low priority instrumentations are enabled.":"Low priority instrumentations are disabled!", this.getClass().getName()); + if( NewRelic.getAgent().getConfig().getValue(IUtilConstants.NR_SECURITY_HOME_APP, false) ) { + logger.logInit(LogLevel.INFO, "App being scanned is a Newrelic's Home Grown application", this.getClass().getName()); + } info.setIdentifier(ApplicationInfoUtils.envDetection()); ApplicationInfoUtils.continueIdentifierProcessing(info.getIdentifier(), config.getConfig()); info.generateAppInfo(config.getConfig()); @@ -256,11 +258,15 @@ public void registerOperation(AbstractOperation operation) { securityMetaData.getResponse().setResponseBody( new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_RESPONSE_DATA, List.class)))); } - // end if (operation == null || operation.isEmpty()) { return; } + Boolean csecJavaHeadRequest = securityMetaData.getCustomAttribute(ICsecApiConstants.NR_CSEC_JAVA_HEAD_REQUEST, Boolean.class); + if(csecJavaHeadRequest != null && csecJavaHeadRequest){ + return; + } + String executionId = ExecutionIDGenerator.getExecutionId(); operation.setExecutionId(executionId); operation.setStartTime(Instant.now().toEpochMilli()); @@ -269,9 +275,10 @@ public void registerOperation(AbstractOperation operation) { } if (operation instanceof RXSSOperation) { operation.setStackTrace(securityMetaData.getMetaData().getServiceTrace()); + securityMetaData.addCustomAttribute("RXSS_PROCESSED", true); } else { StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - operation.setStackTrace(Arrays.copyOfRange(trace, 2, trace.length)); + operation.setStackTrace(Arrays.copyOfRange(trace, securityMetaData.getMetaData().getFromJumpRequiredInStackTrace(), trace.length)); } // added to fetch request/response in case of grpc requests @@ -282,7 +289,14 @@ public void registerOperation(AbstractOperation operation) { new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_RESPONSE_DATA, List.class)))); } - if (checkIfNRGeneratedEvent(operation)) { + if(NewRelic.getAgent().getConfig().getValue(IUtilConstants.NR_SECURITY_HOME_APP, false) && checkIfCSECGeneratedEvent(operation)) { + logger.log(LogLevel.FINEST, DROPPING_EVENT_AS_IT_WAS_GENERATED_BY_K_2_INTERNAL_API_CALL + + JsonConverter.toJSON(operation), + Agent.class.getName()); + return; + } + + if(!NewRelic.getAgent().getConfig().getValue(IUtilConstants.NR_SECURITY_HOME_APP, false) && checkIfNRGeneratedEvent(operation)) { logger.log(LogLevel.FINEST, DROPPING_EVENT_AS_IT_WAS_GENERATED_BY_K_2_INTERNAL_API_CALL + JsonConverter.toJSON(operation), Agent.class.getName()); @@ -313,6 +327,18 @@ public void registerOperation(AbstractOperation operation) { } } + private boolean checkIfCSECGeneratedEvent(AbstractOperation operation) { + for (int i = 1, j = 0; i < operation.getStackTrace().length; i++) { + // Only remove consecutive top com.newrelic and com.nr. elements from stack. + if (i - 1 == j && StringUtils.startsWithAny(operation.getStackTrace()[i].getClassName(), "com.newrelic.agent.security.", "com.newrelic.api.agent.")) { + j++; + } else if (StringUtils.startsWithAny(operation.getStackTrace()[i].getClassName(), "com.newrelic.agent.security.", "com.newrelic.api.agent.")) { + return true; + } + } + return false; + } + private void logIfIastScanForFirstTime(K2RequestIdentifier fuzzRequestIdentifier, HttpRequest request) { String url = StringUtils.EMPTY; @@ -327,15 +353,30 @@ private void logIfIastScanForFirstTime(K2RequestIdentifier fuzzRequestIdentifier } private static boolean checkIfNRGeneratedEvent(AbstractOperation operation) { + boolean isNettyReactor = false, isNRGeneratedEvent = false; for (int i = 1, j = 0; i < operation.getStackTrace().length; i++) { + if(StringUtils.equalsAny(operation.getStackTrace()[i].getClassName(), "com.nr.instrumentation.TokenLinkingSubscriber", "com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber")){ + isNettyReactor = true; + continue; + } + // Only remove consecutive top com.newrelic and com.nr. elements from stack. if (i - 1 == j && StringUtils.startsWithAny(operation.getStackTrace()[i].getClassName(), "com.newrelic.", "com.nr.")) { j++; } else if (StringUtils.startsWithAny(operation.getStackTrace()[i].getClassName(), "com.newrelic.", "com.nr.")) { - return true; + isNRGeneratedEvent = true; } } - return false; + if (isNettyReactor) { + operation.setStackTrace(removeNettyReactorLinkingTraces(operation.getStackTrace())); + } + return isNRGeneratedEvent; + } + + private static StackTraceElement[] removeNettyReactorLinkingTraces(StackTraceElement[] stackTrace) { + return Arrays.stream(stackTrace).filter(stackTraceElement -> + !StringUtils.equalsAny(stackTraceElement.getClassName(), "com.nr.instrumentation.TokenLinkingSubscriber", "com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber") + ).toArray(StackTraceElement[]::new); } private static boolean needToGenerateEvent(String apiID) { @@ -354,12 +395,23 @@ private UserClassEntity setUserClassEntity(AbstractOperation operation, Security for (int i = 0; i < operation.getStackTrace().length; i++) { StackTraceElement stackTraceElement = operation.getStackTrace()[i]; + + // Section for user class identification using API handlers + if( !securityMetaData.getMetaData().isFoundAnnotedUserLevelServiceMethod() && URLMappingsHelper.getHandlersHash().contains(stackTraceElement.getClassName().hashCode())){ + //Found -> assign user class and return + userClassEntity.setUserClassElement(stackTraceElement); + securityMetaData.getMetaData().setUserLevelServiceMethodEncountered(true); + userClassEntity.setCalledByUserCode(true); + return userClassEntity; + } + + //Fallback to old mechanism if(userStackTraceElement != null){ if(StringUtils.equals(stackTraceElement.getClassName(), userStackTraceElement.getClassName()) && StringUtils.equals(stackTraceElement.getMethodName(), userStackTraceElement.getMethodName())){ userClassEntity.setUserClassElement(stackTraceElement); userClassEntity.setCalledByUserCode(securityMetaData.getMetaData().isUserLevelServiceMethodEncountered()); - return userClassEntity; + userStackTraceElement = stackTraceElement; } } // TODO: the `if` should be `else if` please check crypto case BenchmarkTest01978. service trace is being registered from doSomething() @@ -396,7 +448,7 @@ private static void processStackTrace(AbstractOperation operation) { markedForRemoval = false; // Only remove consecutive top com.newrelic and com.nr. elements from stack. - if (i - 1 == j && StringUtils.startsWithAny(stackTrace[i].getClassName(), "com.newrelic.", "com.nr.")) { + if (i - 1 == j && StringUtils.startsWithAny(stackTrace[i].getClassName(), "com.newrelic.agent.security.", "com.newrelic.api.agent.")) { resetFactor++; j++; markedForRemoval = true; @@ -413,7 +465,8 @@ private static void processStackTrace(AbstractOperation operation) { AgentMetaData metaData = NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData(); if (stackTrace[i - 1].getLineNumber() > 0 && StringUtils.isNotBlank(stackTrace[i - 1].getFileName()) && - !StringUtils.startsWithAny(stackTrace[i - 1].getClassName(), "com.newrelic.", "com.nr.") + !StringUtils.startsWithAny(stackTrace[i - 1].getClassName(), "com.newrelic.agent.security.", "com.newrelic.api.agent.", + "com.newrelic.agent.deps.", "com.nr.instrumentation.") ) { metaData.setTriggerViaRCI(true); metaData.getRciMethodsCalls() @@ -437,7 +490,11 @@ private static void processStackTrace(AbstractOperation operation) { private static void setAPIId(AbstractOperation operation, List traceForIdCalc, VulnerabilityCaseType vulnerabilityCaseType) { try { traceForIdCalc.add(operation.getSourceMethod().hashCode()); - operation.setApiID(vulnerabilityCaseType.getCaseType() + "-" + HashGenerator.getXxHash64Digest(traceForIdCalc.stream().mapToInt(Integer::intValue).toArray())); + int[] traceArray = new int[traceForIdCalc.size()]; + for (int i = 0; i < traceForIdCalc.size(); i++) { + traceArray[i] = traceForIdCalc.get(i); + } + operation.setApiID(vulnerabilityCaseType.getCaseType() + "-" + HashGenerator.getXxHash64Digest(traceArray)); } catch (IOException e) { operation.setApiID("UNDEFINED"); } @@ -551,18 +608,11 @@ public boolean isLowPriorityInstrumentationEnabled() { public void setApplicationConnectionConfig(int port, String scheme) { AppServerInfo appServerInfo = AppServerInfoHelper.getAppServerInfo(); - appServerInfo.getConnectionConfiguration().put(port, scheme); + ServerConnectionConfiguration serverConnectionConfiguration = new ServerConnectionConfiguration(port, scheme); + appServerInfo.getConnectionConfiguration().put(port, serverConnectionConfiguration); // verifyConnectionAndPut(port, scheme, appServerInfo); } - private void verifyConnectionAndPut(int port, String scheme, AppServerInfo appServerInfo) { - if(isConnectionSuccessful(port, scheme)){ - appServerInfo.getConnectionConfiguration().put(port, scheme); - } else if (isConnectionSuccessful(port,StringUtils.equalsAnyIgnoreCase(scheme, HTTPS_STR)? HTTP_STR : HTTPS_STR)) { - appServerInfo.getConnectionConfiguration().put(port, StringUtils.equalsAnyIgnoreCase(scheme, HTTPS_STR)? HTTP_STR : HTTPS_STR); - } - } - private boolean isConnectionSuccessful(int port, String scheme) { try { java.net.URL endpoint = new URL(String.format("%s://localhost:%s", scheme, port)); @@ -585,13 +635,13 @@ private boolean isConnectionSuccessful(int port, String scheme) { } } - public String getApplicationConnectionConfig(int port) { + public ServerConnectionConfiguration getApplicationConnectionConfig(int port) { AppServerInfo appServerInfo = AppServerInfoHelper.getAppServerInfo(); return appServerInfo.getConnectionConfiguration().get(port); } @Override - public Map getApplicationConnectionConfig() { + public Map getApplicationConnectionConfig() { return AppServerInfoHelper.getAppServerInfo().getConnectionConfiguration(); } @@ -701,4 +751,5 @@ public String decryptAndVerify(String encryptedData, String hashVerifier) { return null; } } + } \ No newline at end of file diff --git a/newrelic-security-api-test-impl/src/main/java/com/newrelic/api/agent/security/Agent.java b/newrelic-security-api-test-impl/src/main/java/com/newrelic/api/agent/security/Agent.java index 1644b501d..c906210fa 100644 --- a/newrelic-security-api-test-impl/src/main/java/com/newrelic/api/agent/security/Agent.java +++ b/newrelic-security-api-test-impl/src/main/java/com/newrelic/api/agent/security/Agent.java @@ -5,6 +5,7 @@ import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.api.agent.security.schema.ServerConnectionConfiguration; import com.newrelic.api.agent.security.schema.operation.FileIntegrityOperation; import com.newrelic.api.agent.security.schema.policy.AgentPolicy; import com.newrelic.api.agent.security.utils.logging.LogLevel; @@ -161,12 +162,12 @@ public void setApplicationConnectionConfig(int port, String scheme) { } @Override - public String getApplicationConnectionConfig(int port) { + public ServerConnectionConfiguration getApplicationConnectionConfig(int port) { return null; } @Override - public Map getApplicationConnectionConfig() { + public Map getApplicationConnectionConfig() { //TODO Ishika please fill this as per your needs return null; } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/NoOpAgent.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/NoOpAgent.java index 055d8f66a..d7ae1ec51 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/NoOpAgent.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/NoOpAgent.java @@ -10,6 +10,7 @@ import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper; import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.api.agent.security.schema.ServerConnectionConfiguration; import com.newrelic.api.agent.security.schema.policy.AgentPolicy; import com.newrelic.api.agent.security.utils.logging.LogLevel; @@ -94,12 +95,12 @@ public void setApplicationConnectionConfig(int port, String scheme) { } @Override - public String getApplicationConnectionConfig(int port) { + public ServerConnectionConfiguration getApplicationConnectionConfig(int port) { return null; } @Override - public Map getApplicationConnectionConfig() { + public Map getApplicationConnectionConfig() { return Collections.emptyMap(); } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/SecurityAgent.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/SecurityAgent.java index 1b5be23c0..2ae86ea9e 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/SecurityAgent.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/SecurityAgent.java @@ -9,6 +9,7 @@ import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.api.agent.security.schema.ServerConnectionConfiguration; import com.newrelic.api.agent.security.schema.policy.AgentPolicy; import com.newrelic.api.agent.security.utils.logging.LogLevel; @@ -54,9 +55,9 @@ public interface SecurityAgent { void setApplicationConnectionConfig(int port, String scheme); - String getApplicationConnectionConfig(int port); + ServerConnectionConfiguration getApplicationConnectionConfig(int port); - Map getApplicationConnectionConfig(); + Map getApplicationConnectionConfig(); void log(LogLevel logLevel, String event, Throwable throwableEvent, String logSourceClassName); diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/FileHelper.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/FileHelper.java index 2349d6a34..223a44041 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/FileHelper.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/FileHelper.java @@ -141,6 +141,8 @@ public static void checkEntryOfFileIntegrity(List fileNames) { if(NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().containsKey(fileName)){ FileIntegrityOperation fbean = NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().get(fileName); if(fbean.isIntegrityBreached(file)){ + //Lock release is required here, as this register operation inside lock is intentional + ThreadLocalLockHelper.releaseLock(); NewRelicSecurity.getAgent().registerOperation(fbean); } } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/GenericHelper.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/GenericHelper.java index 4a5e28067..f4c44c535 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/GenericHelper.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/GenericHelper.java @@ -18,6 +18,8 @@ public class GenericHelper { public static final String URI_EXCEPTION_MESSAGE = "Instrumentation library: %s , error while extracting URI : %s"; public static final String ERROR_GENERATING_HTTP_REQUEST = "Instrumentation library: %s , error while generating HTTP request : %s"; public static final String ERROR_PARSING_HTTP_REQUEST_DATA = "Instrumentation library: %s , error while parsing HTTP request data : %s"; + public static final String ERROR_WHILE_GETTING_APP_ENDPOINTS = "Instrumentation library: %s , error while getting application API endpoints : %s"; + public static final String ERROR_PARSING_HTTP_RESPONSE = "Instrumentation library: %s , error while parsing HTTP Response data : %s"; public static boolean skipExistsEvent() { if (!(NewRelicSecurity.getAgent().getCurrentPolicy().getVulnerabilityScan().getEnabled() && diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ICsecApiConstants.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ICsecApiConstants.java new file mode 100644 index 000000000..4281ffaec --- /dev/null +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ICsecApiConstants.java @@ -0,0 +1,5 @@ +package com.newrelic.api.agent.security.instrumentation.helpers; + +public class ICsecApiConstants { + public static final String NR_CSEC_JAVA_HEAD_REQUEST = "nr-csec-java-head-request"; +} diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/R2dbcHelper.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/R2dbcHelper.java index b1843ef6f..c23e60285 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/R2dbcHelper.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/R2dbcHelper.java @@ -41,6 +41,7 @@ public static AbstractOperation preprocessSecurityHook(String sql, String method sqlOperation.setPreparedCall(isPrepared); sqlOperation.setParams(params); + NewRelicSecurity.getAgent().getSecurityMetaData().getMetaData().setFromJumpRequiredInStackTrace(3); NewRelicSecurity.getAgent().registerOperation(sqlOperation); return sqlOperation; } catch (Throwable e) { diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java index 8217daf9a..0a8e7d99d 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java @@ -111,22 +111,28 @@ public static K2RequestIdentifier parseFuzzRequestIdentifierHeader(String reques tmpFile = StringUtils.replace(tmpFile, NR_CSEC_VALIDATOR_HOME_TMP, NewRelicSecurity.getAgent().getAgentTempDir()); k2RequestIdentifierInstance.getTempFiles().add(tmpFile); + boolean lockAcquired = ThreadLocalLockHelper.acquireLock(); try { - - File fileToCreate = new File(tmpFile); - if (fileToCreate.getParentFile() != null) { - - File parentFile = fileToCreate; - while(parentFile != null && parentFile.getParentFile() != null && !parentFile.getParentFile().exists()){ - parentFile = parentFile.getParentFile(); + if (lockAcquired) { + File fileToCreate = new File(tmpFile); + if (fileToCreate.getParentFile() != null) { + + File parentFile = fileToCreate; + while (parentFile != null && parentFile.getParentFile() != null && !parentFile.getParentFile().exists()) { + parentFile = parentFile.getParentFile(); + } + filesToRemove.add(parentFile.getAbsolutePath()); + fileToCreate.getParentFile().mkdirs(); + } + if (!fileToCreate.exists()) { + Files.createFile(fileToCreate.toPath()); } - filesToRemove.add(parentFile.getAbsolutePath()); - fileToCreate.getParentFile().mkdirs(); } - Files.createFile(fileToCreate.toPath()); } catch (Throwable e) { String message = "Error while parsing fuzz request : %s"; NewRelicSecurity.getAgent().log(LogLevel.INFO, String.format(message, e.getMessage()), e, ServletHelper.class.getName()); + } finally { + ThreadLocalLockHelper.releaseLock(); } } } @@ -163,8 +169,9 @@ public static boolean registerUserLevelCode(String frameworkName, boolean asyncC return false; } SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); - if (!securityMetaData.getMetaData().isUserLevelServiceMethodEncountered(frameworkName)) { + if (!securityMetaData.getMetaData().isFoundAnnotedUserLevelServiceMethod()) { securityMetaData.getMetaData().setUserLevelServiceMethodEncountered(true); + securityMetaData.getMetaData().setUserLevelServiceMethodEncounteredFramework(frameworkName); StackTraceElement[] trace = Thread.currentThread().getStackTrace(); securityMetaData.getMetaData().setServiceTrace(Arrays.copyOfRange(trace, asyncContext?2:3, trace.length)); return true; @@ -174,18 +181,40 @@ public static boolean registerUserLevelCode(String frameworkName, boolean asyncC return false; } + public static boolean setFoundAnnotedUserLevelServiceMethod() { + try { + if (!NewRelicSecurity.isHookProcessingActive() || (NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty()) + ) { + return false; + } + SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData(); + securityMetaData.getMetaData().setFoundAnnotedUserLevelServiceMethod(true); + return true; + } catch (Throwable ignored){ + } + return false; + } + public static Set getFilesToRemove() { return filesToRemove; } public static void tmpFileCleanUp(List files){ - for (String file : files) { - try { - Files.deleteIfExists(Paths.get(file)); - } catch (IOException e) { + boolean lockAcquired = ThreadLocalLockHelper.acquireLock(); + try { + if (lockAcquired) { + for (String file : files) { + try { + Files.deleteIfExists(Paths.get(file)); + } catch (IOException e) { + } + } } + } finally { + ThreadLocalLockHelper.releaseLock(); } + } public static boolean isResponseContentTypeExcluded( String responseContentType) { 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 4e5f5df4c..4ee5eb972 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 @@ -2,17 +2,38 @@ import com.newrelic.api.agent.security.schema.ApplicationURLMapping; +import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class URLMappingsHelper { private static Set mappings = ConcurrentHashMap.newKeySet(); + private static final Set defaultHandlers = new HashSet() {{ + add("org.eclipse.jetty.jsp.JettyJspServlet"); + add("org.eclipse.jetty.servlet.ServletHandler$Default404Servlet"); + add("org.glassfish.jersey.servlet.ServletContainer"); + add("org.apache.jasper.servlet.JspServlet"); + add("org.apache.catalina.servlets.DefaultServlet"); + add("org.eclipse.jetty.servlet.DefaultServlet"); + add("grails.plugin.databasemigration.DbdocController"); + }}; public static Set getApplicationURLMappings() { return mappings; } + private static Set handlers = ConcurrentHashMap.newKeySet(); + + public static Set getHandlersHash() { + return handlers; + } + public static void addApplicationURLMapping(ApplicationURLMapping mapping) { - mappings.add(mapping); + if (mapping.getHandler() == null || (mapping.getHandler() != null && !defaultHandlers.contains(mapping.getHandler()))) { + mappings.add(mapping); + } + if (mapping.getHandler() != null){ + handlers.add(mapping.getHandler().hashCode()); + } } } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/AgentMetaData.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/AgentMetaData.java index 751f4d24f..307f8e053 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/AgentMetaData.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/AgentMetaData.java @@ -35,6 +35,10 @@ public class AgentMetaData { @JsonIgnore private String userLevelServiceMethodEncounteredFramework; + private int fromJumpRequiredInStackTrace = 2; + + private boolean foundAnnotedUserLevelServiceMethod = false; + @JsonIgnore private Set ips; @@ -50,6 +54,7 @@ public AgentMetaData() { public AgentMetaData(AgentMetaData agentMetaData) { this.rciMethodsCalls = new HashSet<>(); + agentMetaData.rciMethodsCalls.remove(null); this.rciMethodsCalls.addAll(agentMetaData.rciMethodsCalls); this.triggerViaDeserialisation = agentMetaData.triggerViaDeserialisation; this.triggerViaRCI = agentMetaData.triggerViaRCI; @@ -61,6 +66,9 @@ public AgentMetaData(AgentMetaData agentMetaData) { this.userLevelServiceMethodEncountered = agentMetaData.userLevelServiceMethodEncountered; this.reflectedMetaData = agentMetaData.reflectedMetaData; this.appServerInfo = agentMetaData.appServerInfo; + this.triggerViaXXE = agentMetaData.triggerViaXXE; + this.userLevelServiceMethodEncounteredFramework = agentMetaData.userLevelServiceMethodEncounteredFramework; + this.foundAnnotedUserLevelServiceMethod = agentMetaData.foundAnnotedUserLevelServiceMethod; } public boolean isTriggerViaRCI() { @@ -163,6 +171,10 @@ public void setUserLevelServiceMethodEncountered(boolean userLevelServiceMethodE this.userLevelServiceMethodEncountered = userLevelServiceMethodEncountered; } + public void setUserLevelServiceMethodEncounteredFramework(String userLevelServiceMethodEncounteredFramework) { + this.userLevelServiceMethodEncounteredFramework = userLevelServiceMethodEncounteredFramework; + } + public AppServerInfo getAppServerInfo() { return appServerInfo; } @@ -170,4 +182,19 @@ public AppServerInfo getAppServerInfo() { public void setAppServerInfo(AppServerInfo appServerInfo) { this.appServerInfo = appServerInfo; } + + public int getFromJumpRequiredInStackTrace() { + return fromJumpRequiredInStackTrace; + } + + public void setFromJumpRequiredInStackTrace(int fromJumpRequiredInStackTrace) { + this.fromJumpRequiredInStackTrace = fromJumpRequiredInStackTrace; + } + public boolean isFoundAnnotedUserLevelServiceMethod() { + return foundAnnotedUserLevelServiceMethod; + } + + public void setFoundAnnotedUserLevelServiceMethod(boolean foundAnnotedUserLevelServiceMethod) { + this.foundAnnotedUserLevelServiceMethod = foundAnnotedUserLevelServiceMethod; + } } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/AppServerInfo.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/AppServerInfo.java index 53a7b9922..f16dc3588 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/AppServerInfo.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/AppServerInfo.java @@ -13,7 +13,7 @@ public class AppServerInfo { String applicationTmpDirectory; - Map connectionConfiguration; + Map connectionConfiguration; public AppServerInfo() { connectionConfiguration = new ConcurrentHashMap<>(); @@ -51,11 +51,7 @@ public void setApplicationTmpDirectory(String applicationTmpDirectory) { this.applicationTmpDirectory = applicationTmpDirectory; } - public Map getConnectionConfiguration() { + public Map getConnectionConfiguration() { return connectionConfiguration; } - - public void setConnectionConfiguration(Map connectionConfiguration) { - this.connectionConfiguration = connectionConfiguration; - } } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/ApplicationURLMapping.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/ApplicationURLMapping.java index 8b48ab5da..74912886b 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/ApplicationURLMapping.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/ApplicationURLMapping.java @@ -47,10 +47,11 @@ public boolean equals(Object obj) { if (obj == this) { return true; } - if (obj instanceof ApplicationURLMapping) { ApplicationURLMapping mapping = (ApplicationURLMapping) obj; - return path.equals(mapping.path) && method.equals(mapping.method) && handler.equals(mapping.handler); + return Objects.equals(this.path, mapping.path) && + Objects.equals(this.method, mapping.method) && + Objects.equals(this.handler, mapping.handler); } return false; } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/ServerConnectionConfiguration.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/ServerConnectionConfiguration.java new file mode 100644 index 000000000..0db051003 --- /dev/null +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/ServerConnectionConfiguration.java @@ -0,0 +1,54 @@ +package com.newrelic.api.agent.security.schema; + +public class ServerConnectionConfiguration { + + private Integer port; + + private String protocol; + + private boolean confirmed; + + private String endpoint; + + public ServerConnectionConfiguration() { + this.confirmed = false; + } + + public ServerConnectionConfiguration(int port, String scheme) { + this.port = port; + this.protocol = scheme; + this.confirmed = false; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public boolean isConfirmed() { + return confirmed; + } + + public void setConfirmed(boolean confirmed) { + this.confirmed = confirmed; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } +} 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 ade0ee424..433f75dc0 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 @@ -182,6 +182,49 @@ public static String substringBeforeLast(final String str, final String separato return str.substring(0, pos); } + /** + *

Gets the substring after the first occurrence of a separator. + * The separator is not returned.

+ * + *

A {@code null} string input will return {@code null}. + * An empty ("") string input will return the empty string. + * A {@code null} separator will return the empty string if the + * input string is not {@code null}.

+ * + *

If nothing is found, the empty string is returned.

+ * + *
+     * StringUtils.substringAfter(null, *)      = null
+     * StringUtils.substringAfter("", *)        = ""
+     * StringUtils.substringAfter(*, null)      = ""
+     * StringUtils.substringAfter("abc", "a")   = "bc"
+     * StringUtils.substringAfter("abcba", "b") = "cba"
+     * StringUtils.substringAfter("abc", "c")   = ""
+     * StringUtils.substringAfter("abc", "d")   = ""
+     * StringUtils.substringAfter("abc", "")    = "abc"
+     * 
+ * + * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring after the first occurrence of the separator, + * {@code null} if null String input + * @since 2.0 + */ + public static String substringAfter(final String str, final String separator) { + if (isEmpty(str)) { + return str; + } + if (separator == null) { + return EMPTY; + } + final int pos = str.indexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return EMPTY; + } + return str.substring(pos + separator.length()); + } + + /** *

Gets the substring after the last occurrence of a separator. * The separator is not returned.

diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/helper/RedisCommands.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/helper/RedisCommands.java index d9f17958e..0109c351a 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/helper/RedisCommands.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/helper/RedisCommands.java @@ -55,6 +55,7 @@ public class RedisCommands { "OBJECT", "PFCOUNT", "PTTL", + "PEXPIRETIME", "RANDOMKEY", "SCAN", "SCARD", @@ -151,7 +152,6 @@ public class RedisCommands { "PERSIST", "PEXPIRE", "PEXPIREAT", - "PEXPIRETIME", "PFADD", "PFMERGE", "PSETEX", @@ -191,6 +191,7 @@ public class RedisCommands { "BLPOP", "BRPOP", "BRPOPLPUSH", + "BZMPOP", "BZPOPMAX", "BZPOPMIN", "DEL", @@ -216,6 +217,7 @@ public class RedisCommands { "XDEL", "XGROUP ", "XTRIM", + "ZMPOP", "ZPOPMAX", "ZPOPMIN", "ZREM", diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/FileIntegrityOperation.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/FileIntegrityOperation.java index d334a4158..cca591220 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/FileIntegrityOperation.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/FileIntegrityOperation.java @@ -144,25 +144,18 @@ public void setPermissionString(String permissionString) { } public boolean isIntegrityBreached(File file){ - boolean lockAcquired = ThreadLocalLockHelper.acquireLock(); try { - if(lockAcquired) { - Boolean exists = file.exists(); - long lastModified = exists ? file.lastModified() : -1; - String permissions = StringUtils.EMPTY; - long length = file.length(); - if (exists) { - PosixFileAttributes fileAttributes = Files.readAttributes(Paths.get(file.getPath()), PosixFileAttributes.class); - Set permissionSet = fileAttributes.permissions(); - permissions = permissionSet.toString(); - } - return (exists != this.exists || lastModified != this.lastModified || !StringUtils.equals(permissions, this.permissionString) || length != this.length); + Boolean exists = file.exists(); + long lastModified = exists ? file.lastModified() : -1; + String permissions = StringUtils.EMPTY; + long length = file.length(); + if (exists) { + PosixFileAttributes fileAttributes = Files.readAttributes(Paths.get(file.getPath()), PosixFileAttributes.class); + Set permissionSet = fileAttributes.permissions(); + permissions = permissionSet.toString(); } + return (exists != this.exists || lastModified != this.lastModified || !StringUtils.equals(permissions, this.permissionString) || length != this.length); } catch (IOException e) { - } finally { - if(lockAcquired) { - ThreadLocalLockHelper.releaseLock(); - } } return false; } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/SQLOperation.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/SQLOperation.java index aab96090d..54d7e807b 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/SQLOperation.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/SQLOperation.java @@ -14,6 +14,8 @@ public class SQLOperation extends AbstractOperation { private Map params; + private Map objectParams; + private String dbName = "UNKNOWN"; private boolean isPreparedCall; @@ -24,6 +26,7 @@ public SQLOperation(String className, String methodName) { this.setCaseType(VulnerabilityCaseType.SQL_DB_COMMAND); this.query = EMPTY; this.params = new HashMap<>(); + this.objectParams = new HashMap<>(); } public String getQuery() { @@ -54,7 +57,7 @@ public void setPreparedCall(boolean preparedCall) { public boolean isEmpty() { if (query == null || query.trim().isEmpty()) { return true; - } else if (isPreparedCall) { + } else if (isPreparedCall && params != null) { return query.contains("?") && params.isEmpty(); } return false; @@ -82,6 +85,14 @@ public String getDbName() { return dbName; } + public Map getObjectParams() { + return objectParams; + } + + public void setObjectParams(Map objectParams) { + this.objectParams = objectParams; + } + /** * @param dbName the dbName to set */ diff --git a/settings.gradle b/settings.gradle index df0e06951..8c3bd415a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -118,7 +118,8 @@ include 'instrumentation:akka-http-core-10.0' include 'instrumentation:akka-http-2.11_10.0.0' include 'instrumentation:jetty-9' include 'instrumentation:jetty-11' -//include 'instrumentation:netty-4.0.0' +include 'instrumentation:netty-4.0.0' +include 'instrumentation:netty-4.0.8' //include 'instrumentation:grpc-1.40.0' include 'instrumentation:dynamodb-1.11.80' include 'instrumentation:dynamodb-1.11.390' @@ -142,20 +143,6 @@ include 'instrumentation:file-low-priority-instrumentation' include 'instrumentation:cassandra-datastax-3' include 'instrumentation:cassandra-datastax-4' include 'instrumentation:commons-jxpath' -//include 'instrumentation:grails-1.3' -//include 'instrumentation:grails-2.0' -//include 'instrumentation:grails-3.0' -//include 'instrumentation:resteasy-2.2' -//include 'instrumentation:resteasy-3' -//include 'instrumentation:resteasy-4' -//include 'instrumentation:jersey' -//include 'instrumentation:apache-struts2' -//include 'instrumentation:spring-webflux' -//include 'instrumentation:spring-webmvc-3.1.0' -//include 'instrumentation:spring-webmvc-5.3.0' -//include 'instrumentation:apache-wicket-6.4' -//include 'instrumentation:apache-wicket-7.0' -//include 'instrumentation:apache-wicket-8.0' include 'instrumentation:async-http-client-2.0.0' include 'instrumentation:sun-net-httpserver' include 'instrumentation:tomcat-7' @@ -164,7 +151,6 @@ include 'instrumentation:wildfly-8' include 'instrumentation:grpc-1.4.0' include 'instrumentation:grpc-1.22.0' include 'instrumentation:grpc-1.40.0' - include 'instrumentation:ning-async-http-client-1.1.0' include 'instrumentation:ning-async-http-client-1.6.1' include 'instrumentation:ning-async-http-client-1.0.0' @@ -172,10 +158,36 @@ include 'instrumentation:jersey-2' include 'instrumentation:jersey-2.16' include 'instrumentation:jersey-3' include 'instrumentation:spring-data-redis' -//include 'instrumentation:jcache-1.0.0' +include 'instrumentation:jcache-1.0.0' include 'instrumentation:lettuce-4.3' include 'instrumentation:lettuce-5.0' //include 'instrumentation:spymemcached-2.12.0' include 'instrumentation:jetty-12' include 'instrumentation:mule-3.7' include 'instrumentation:mule-3.6' +include 'instrumentation:websphere-8' +include 'instrumentation:grails-1.3' +include 'instrumentation:grails-2.0' +include 'instrumentation:grails-3.0' +include 'instrumentation:resteasy-2.2' +include 'instrumentation:resteasy-3' +include 'instrumentation:resteasy-4' +include 'instrumentation:jersey' +include 'instrumentation:apache-struts2' +include 'instrumentation:spring-webflux' +include 'instrumentation:spring-webmvc-3.1.0' +include 'instrumentation:spring-webmvc-5.3.0' +include 'instrumentation:apache-wicket-6.4' +include 'instrumentation:apache-wicket-7.0' +include 'instrumentation:apache-wicket-8.0' +include 'instrumentation:cxf-jaxrs' +include 'instrumentation:play-2.4' +include 'instrumentation:play-2.6' +include 'instrumentation:play-2.13_2.7' +include 'instrumentation:apache-tomcat-7' +include 'instrumentation:apache-tomcat-10' +include 'instrumentation:servlet-3.0' +include 'instrumentation:spray-http-1.3.1' +include 'instrumentation:spray-client' +include 'instrumentation:spray-can-1.3.1' +include 'instrumentation:spring-webclient-5.0'