Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[NR-265913] API Endpoint Phase 2 : Detect route of an endpoint for Jax-RS Framework #256

Merged
merged 36 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
064ad10
API Endpoint Phase 2: Add Endpoint route in eventBean
IshikaDawda May 24, 2024
11bce32
API Endpoint Phase 2: Add Endpoint route in Jersey Framework
IshikaDawda May 24, 2024
0b74235
API Endpoint Phase 2: Add Endpoint route in CXF Jax-RS Framework
IshikaDawda May 24, 2024
dd8379f
API Endpoint Phase 2: Add Endpoint route in RestEasy-2.2 Framework
IshikaDawda May 24, 2024
bd620dd
API Endpoint Phase 2: Add Endpoint route in RestEasy-3 Framework
IshikaDawda May 24, 2024
ad6ef85
API Endpoint Phase 2: Add Endpoint route in RestEasy-4 Framework
IshikaDawda May 24, 2024
c30ebc6
API Endpoint Phase 2: Add Endpoint route to httpRequestBean
IshikaDawda May 26, 2024
48ceec6
Add framework to AgentMetaData & reset route if previous framework is…
IshikaDawda May 28, 2024
b37f0dd
Add critical message to cxf-jaxrs
IshikaDawda May 28, 2024
6b04bed
Add critical message to jersey
IshikaDawda May 28, 2024
1dbe4e2
Add endsWith functions to StringUtils
IshikaDawda Jun 3, 2024
80d16c0
NR-273607 : Fallback mechanism for route calculation
IshikaDawda Jun 3, 2024
8e3dd1d
NPE fix for FRAMEWORK
lovesh-ap Jun 3, 2024
d0952fa
Add setter for detected route
IshikaDawda Jun 5, 2024
63fe995
Bypass Route Calculation if the incoming request is an IAST Request
IshikaDawda Jun 5, 2024
d0b95df
Merge branch 'refs/heads/feature/api-endpoint/JaxRS-NR-265913' into f…
IshikaDawda Jun 5, 2024
c754664
Route Calculation if the route is blank for a request
IshikaDawda Jun 5, 2024
7b12f8e
Optimise the normalization of URI Path in API Endpoint detection
IshikaDawda Jun 5, 2024
e38199b
Merge branch 'refs/heads/feature/api-endpoint/JaxRS-NR-265913' into f…
IshikaDawda Jun 5, 2024
53847b0
update set route remove checks
IshikaDawda Jun 5, 2024
2df904f
Merge branch 'refs/heads/feature/api-endpoint/JaxRS-NR-265913' into f…
IshikaDawda Jun 5, 2024
b2d2724
ApplicationURLMapping path normalization
IshikaDawda Jun 5, 2024
dccad86
Merge branch 'refs/heads/feature/api-endpoint/JaxRS-NR-265913' into f…
IshikaDawda Jun 5, 2024
7e3ed90
[NR-261653] API Endpoint Phase 2 : Detect route of an endpoint for Sp…
IshikaDawda Jun 11, 2024
05e6f34
Bump json version to 1.2.2 due to route reporting in http request bean
IshikaDawda Jun 12, 2024
321b03e
[NR-273605] API Endpoint Phase 2 : Detect route of an endpoint for Sp…
IshikaDawda Jun 14, 2024
708e7af
Updated check for framework in route detection
IshikaDawda Jun 14, 2024
0e20f94
Updated check for framework in route detection
IshikaDawda Jun 14, 2024
5298fd2
Add logging for route detection using Apps Endpoint
IshikaDawda Jun 14, 2024
0bd5233
[NR-273281] Sub-resource case handling in JAX-RS Framework for Route …
IshikaDawda Jun 14, 2024
355db2e
Merge branch 'refs/heads/main' into feature/api-endpoint/JaxRS-NR-265913
IshikaDawda Jun 20, 2024
66463f5
Merge branch 'feature/api-endpoint/JaxRS-NR-265913' into feature/api-…
IshikaDawda Jun 22, 2024
ecf40a2
Merge pull request #267 from newrelic/feature/api-endpoint/fallback-m…
IshikaDawda Jun 22, 2024
cf03769
code fix
IshikaDawda Jun 22, 2024
a0196bc
Merge branch 'refs/heads/main' into feature/api-endpoint/JaxRS-NR-265913
IshikaDawda Jun 22, 2024
a74b739
Merge branch 'refs/heads/main' into feature/api-endpoint/JaxRS-NR-265913
IshikaDawda Jun 22, 2024
File filter

Filter by extension

Filter by extension

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.newrelic.agent.security.instrumentation.apache.struts2;

import com.newrelic.api.agent.security.NewRelicSecurity;
import com.newrelic.api.agent.security.schema.ApplicationURLMapping;
import com.newrelic.api.agent.security.instrumentation.helpers.*;
import com.newrelic.api.agent.security.schema.StringUtils;
import com.newrelic.api.agent.security.utils.logging.LogLevel;
import com.opensymphony.xwork2.config.RuntimeConfiguration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import java.util.Map;
Expand All @@ -10,22 +13,19 @@ public class StrutsHelper {

private static final String SEPARATOR = "/";
private static final String WILDCARD = "*";
private static final String APACHE_STRUTS2 = "APACHE-STRUTS2";
public static void gatherURLMappings(RuntimeConfiguration runtimeConfig) {
try {
Map<String, Map<String, ActionConfig>> namespaces = runtimeConfig.getActionConfigs();
for (Map.Entry<String, Map<String, ActionConfig>> namespace : namespaces.entrySet()) {
String url = namespace.getKey();
for (ActionConfig actionConfig : namespace.getValue().values()) {
String mapping;
if(url.endsWith(SEPARATOR)){
mapping = url + actionConfig.getName();
} else {
mapping = url + SEPARATOR + actionConfig.getName();
}
String mapping = StringUtils.appendIfMissing(url, SEPARATOR) + actionConfig.getName();
URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, mapping, actionConfig.getClassName()));
}
}
} catch (Exception ignored){
NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, APACHE_STRUTS2, ignored.getMessage()), ignored, StrutsHelper.class.getName());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ public static void gatherURLMappings(ServletContext servletContext) {

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()));
URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, mapping, servletRegistration.getClassName()));
}
}
} catch (Exception e){
Expand All @@ -42,7 +41,7 @@ private static void getJSPMappings(ServletContext servletContext, String dir) {
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));
URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, path));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
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";
Expand All @@ -24,8 +23,7 @@ public static void gatherURLMappings(ServletContext servletContext) {

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()));
URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, mapping, servletRegistration.getClassName()));
}
}
} catch (Exception e){
Expand All @@ -42,7 +40,7 @@ private static void getJSPMappings(ServletContext servletContext, String dir) {
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));
URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, path));
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions instrumentation-security/cxf-jaxrs/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ 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-frontend-jaxrs:2.1.3')

implementation('org.apache.cxf:cxf-rt-transports-http-jetty:2.1.4')
testImplementation('org.apache.cxf:cxf-rt-transports-http-jetty:2.1.4')
}


Expand All @@ -14,7 +14,8 @@ jar {


verifyInstrumentation {
passesOnly 'org.apache.cxf:cxf-rt-frontend-jaxrs:[2.1.4,)'
passesOnly 'org.apache.cxf:cxf-rt-frontend-jaxrs:[2.1.3,)'
exclude('org.apache.cxf:cxf-rt-frontend-jaxrs:2.1.2')
}

site {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,51 +1,73 @@
package com.newrelic.agent.java.security.cxf.jaxrs;

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.schema.StringUtils;
import com.newrelic.api.agent.security.utils.logging.LogLevel;
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 final String CXF_JAX_RS = "CXF-JAX-RS";

public static void gatherURLMapping(List<ClassResourceInfo> classResourceInfo) {
for (ClassResourceInfo classResource: classResourceInfo){
resources(classResource.getURITemplate().getValue(), classResource);
try {
for (ClassResourceInfo classResource: classResourceInfo){
resources(classResource.getURITemplate().getValue(), classResource);
}
} catch (Exception e) {
NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, CXF_JAX_RS, e.getMessage()), e, CXFHelper.class.getName());
}
}

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()
));
try {
MethodDispatcher methodDispatcher = classResourceInfo.getMethodDispatcher();
for (OperationResourceInfo method : methodDispatcher.getOperationResourceInfos()) {
String segment1 = method.getURITemplate().getValue();
String path = StringUtils.removeEnd(segment, StringUtils.SEPARATOR) + StringUtils.prependIfMissing(segment1, StringUtils.SEPARATOR);

// http-method is null, then it can be a sub-resource
if (method.getHttpMethod() == null) {
URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(
WILDCARD,
StringUtils.appendIfMissing(path, StringUtils.SEPARATOR) + WILDCARD,
classResourceInfo.getResourceClass().getName()
));
} else {
URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(
method.getHttpMethod(),
path,
classResourceInfo.getResourceClass().getName()
));
}

}
} catch (Exception e) {
NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, CXF_JAX_RS, e.getMessage()), e, CXFHelper.class.getName());
}
}

public static String getRequestRoute(OperationResourceInfo ori) {
ClassResourceInfo cri = ori.getClassResourceInfo();
String route = StringUtils.EMPTY;
if(cri != null && cri.getURITemplate() != null){
route = cri.getURITemplate().getValue();
}
if (ori.getURITemplate() != null) {
// in case of subresource cri.getURITemplate() will be null
route += ori.getURITemplate().getValue();
}
// 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);
if (ori.isSubResourceLocator()){
route += URLMappingsHelper.subResourceSegment;
}
return route;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.newrelic.agent.java.security.cxf.jaxrs;

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.Framework;
import com.newrelic.api.agent.security.schema.SecurityMetaData;
import com.newrelic.api.agent.security.schema.StringUtils;
import com.newrelic.api.agent.security.utils.logging.LogLevel;
import com.newrelic.api.agent.weaver.MatchType;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;
import org.apache.cxf.jaxrs.model.ClassResourceInfo;
import org.apache.cxf.jaxrs.model.OperationResourceInfo;
import org.apache.cxf.message.Exchange;

@Weave(type = MatchType.ExactClass, originalName = "org.apache.cxf.jaxrs.JAXRSInvoker")
public class JAXRSInvoker {
public Object invoke(Exchange exchange, Object request) {
try {
OperationResourceInfo ori = exchange.get(OperationResourceInfo.class);
if(NewRelicSecurity.isHookProcessingActive() && ori != null) {
SecurityMetaData metaData = NewRelicSecurity.getAgent().getSecurityMetaData();
metaData.getRequest().setRoute(CXFHelper.getRequestRoute(ori));
metaData.getMetaData().setFramework(Framework.CXF_JAXRS);
}
} catch (Exception e) {
NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_ROUTE_FOR_INCOMING_REQUEST, CXFHelper.CXF_JAX_RS, e.getMessage()), e, this.getClass().getName());
}
return Weaver.callOriginal();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,48 @@
import org.apache.cxf.jaxrs.JAXRSBindingFactory;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
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.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 static Server myServer;

@BeforeClass
public static void startServer() {
int port = SecurityInstrumentationTestRunner.getIntrospector().getRandomPort();
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();

sf.setResourceClasses(CustomerLocatorResource.class, TestMapping.class);
sf.setResourceProvider(CustomerLocatorResource.class, new SingletonResourceProvider(new CustomerLocatorResource()));
sf.setBindingFactory(new JAXRSBindingFactory());

sf.setAddress("http://localhost:" + port);
myServer = sf.create();
myServer.start();
}

@AfterClass
public static void stopServer() {
myServer.stop();
}

private final String handler = TestMapping.class.getName();

@Test
public void testAPIEndpoint() {
startServer();

Set<ApplicationURLMapping> mappings = URLMappingsHelper.getApplicationURLMappings();
Assert.assertEquals(5, mappings.size());

Iterator<ApplicationURLMapping> mapping = mappings.iterator();
String path = "/users/";
String path = "/users";

Assert.assertTrue(mapping.hasNext());
assertMapping("*", "/customers/orders/*", CustomerLocatorResource.class.getName(), mapping.next());
Expand All @@ -47,7 +66,7 @@ public void testAPIEndpoint() {
assertMapping("PUT", path, handler, mapping.next());

Assert.assertTrue(mapping.hasNext());
assertMapping("GET", path +"count", handler, mapping.next());
assertMapping("GET", path +"/count", handler, mapping.next());

}

Expand All @@ -56,24 +75,4 @@ private void assertMapping(String method, String path, String handler, Applicati
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");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ public class CustomerLocatorResource {
protected OrdersSubResource ordersSubResource = new OrdersSubResource();

@Path("orders")
public Object getOrders() {
public OrdersSubResource getOrders() {
return ordersSubResource;
}
}
class OrdersSubResource {
protected TestMapping idType = new TestMapping();

@Path("getStuff")
public Object getById() {
public TestMapping getById() {
return idType;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package com.newrelic.agent.security.instrumentation.grails13;

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 java.util.Map;

public class GrailsHelper {
private static final String WILDCARD = "*";
private static final String GRAILS_13 = "GRAILS-1.3";

public static void gatherUrlMappings(Map<String, String> uri2viewMap, String handler) {
try {
for (String path : uri2viewMap.keySet()) {
URLMappingsHelper.addApplicationURLMapping(new ApplicationURLMapping(WILDCARD, path, handler));
}
} catch (Exception ignored){
NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(GenericHelper.ERROR_WHILE_GETTING_APP_ENDPOINTS, GRAILS_13, ignored.getMessage()), ignored, GrailsHelper.class.getName());
}
}
}
Loading
Loading