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

Instrumentation support for mule-3.7 #143

Merged
merged 2 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions instrumentation-security/mule-3.7/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
repositories {
maven {
url 'https://repository.mulesoft.org/releases/'
}
maven {
url 'https://repository.mulesoft.org/snapshots/'
}
maven {
url 'https://repository.mulesoft.org/nexus/content/repositories/public/'
}
}

dependencies {
implementation(project(":newrelic-security-api"))
implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}")

implementation("org.mule:mule-core:3.7.0")
implementation("org.mule.modules:mule-module-http:3.7.0")
}

jar {
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.mule-3.7',
'Implementation-Title-Alias': 'mule_instrumentation' }
}

// This will still match [3.7.1,3.7.5], and [3.8.2,3.8.7]
// but we can't verify that because the artifacts are enterprise only (behind auth)
verifyInstrumentation {
passes('org.mule:mule-core:3.7.0') {
implementation("org.mule.modules:mule-module-http:3.7.0")
}
passes('org.mule:mule-core:[3.8.0,3.8.2)') {
implementation("org.mule.modules:mule-module-http:3.7.0")
}

// these versions cause problems getting artifacts
exclude 'org.mule:mule-core:[0,3.4.0)'
exclude 'org.mule:mule-core:3.5.4'
exclude 'org.mule:mule-core:[3.6.2,3.7.0)'
exclude 'org.mule:mule-core:[3.7.1,3.8.0)'
exclude 'org.mule:mule-core:[3.8.2,)'

excludeRegex 'org.mule:mule-core:.*-(EA|HF|RC|M|rc|bighorn|cascade).*[0-9]*.*'
}

site {
title 'Mule'
type 'Appserver'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.newrelic.agent.security.instrumentation.mule37;

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.AgentMetaData;
import com.newrelic.api.agent.security.schema.policy.AgentPolicy;
import org.mule.module.http.api.HttpHeaders;
import org.mule.module.http.internal.domain.request.HttpRequest;

import java.util.Map;

import static org.mule.module.http.api.HttpHeaders.Names.X_FORWARDED_FOR;

public class MuleHelper {
private static final String MULE_LOCK_CUSTOM_ATTRIB_NAME = "MULE_LOCK-";
public static final String MULE_SERVER_PORT_ATTRIB_NAME = "MULE_SERVER_PORT";
public static final String TRANSFORM_METHOD = "transform";
public static final String HANDLE_REQUEST_METHOD = "handleRequest";
private static final String EMPTY = "";
public static final String LIBRARY_NAME = "MULE-SERVER";

public static void processHttpRequestHeader(HttpRequest httpRequest, com.newrelic.api.agent.security.schema.HttpRequest securityRequest) {
for (String headerName : httpRequest.getHeaderNames()) {
boolean takeNextValue = false;
String headerKey = headerName;
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(httpRequest.getHeaderValue(headerKey)));
} else if (GenericHelper.CSEC_PARENT_ID.equals(headerKey)) {
NewRelicSecurity.getAgent().getSecurityMetaData()
.addCustomAttribute(GenericHelper.CSEC_PARENT_ID, httpRequest.getHeaderValue(headerKey));
}
String headerFullValue = EMPTY;
for (String headerValue : httpRequest.getHeaderValues(headerKey)) {
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<String, String> 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 String getContentType(HttpRequest httpRequest) {
return httpRequest.getHeaderValue(HttpHeaders.Names.CONTENT_TYPE);
}
public static String getNrSecCustomAttribName(int hashcode) {
return MULE_LOCK_CUSTOM_ATTRIB_NAME + Thread.currentThread().getId() + hashcode;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.mule.module.http.internal.listener;

import com.newrelic.agent.security.instrumentation.mule37.MuleHelper;
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.schema.AgentMetaData;
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.weaver.MatchType;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;
import org.mule.api.MuleContext;
import org.mule.api.MuleEvent;
import org.mule.api.construct.FlowConstruct;
import org.mule.module.http.internal.domain.request.HttpRequest;
import org.mule.module.http.internal.domain.request.HttpRequestContext;

@Weave(type = MatchType.ExactClass, originalName = "org.mule.module.http.internal.listener.HttpRequestToMuleEvent")
public class HttpRequestToMuleEvent_Instrumentation {
public static MuleEvent transform(final HttpRequestContext requestContext, final MuleContext muleContext, final FlowConstruct flowConstruct, Boolean parseRequest, ListenerPath listenerPath) throws HttpRequestParsingException
{
boolean isLockAcquired = acquireLockIfPossible(requestContext.hashCode());
MuleEvent event;
if (isLockAcquired) {
preprocessSecurityHook(requestContext);
}
try {
event = Weaver.callOriginal();
} finally {
if (isLockAcquired) {
releaseLock(requestContext.hashCode());
}
}
if (isLockAcquired) {
postProcessSecurityHook();
}
return event;
}

private static void preprocessSecurityHook(HttpRequestContext requestContext) {
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();

HttpRequest httpRequest = requestContext.getRequest();
securityRequest.setMethod(httpRequest.getMethod());
securityRequest.setClientIP(requestContext.getClientConnection().getRemoteHostAddress().toString());
securityRequest.setServerPort(
NewRelicSecurity
.getAgent()
.getSecurityMetaData()
.getCustomAttribute(MuleHelper.MULE_SERVER_PORT_ATTRIB_NAME, Integer.class)
);

if (securityRequest.getClientIP() != null && !securityRequest.getClientIP().trim().isEmpty()) {
securityAgentMetaData.getIps().add(securityRequest.getClientIP());
securityRequest.setClientPort(String.valueOf(requestContext.getClientConnection().getRemoteHostAddress().getPort()));
}

MuleHelper.processHttpRequestHeader(httpRequest, securityRequest);
securityMetaData.setTracingHeaderValue(MuleHelper.getTraceHeader(securityRequest.getHeaders()));

securityRequest.setProtocol(requestContext.getScheme());
securityRequest.setUrl(httpRequest.getUri());

// TODO: Create OutBoundHttp data here : Skipping for now.

securityRequest.setContentType(MuleHelper.getContentType(httpRequest));

// TODO: need to update UserClassEntity
ServletHelper.registerUserLevelCode(MuleHelper.LIBRARY_NAME);
securityRequest.setRequestParsed(true);
} catch (Throwable ignored){}
}

private static void postProcessSecurityHook() {
try {
if (!NewRelicSecurity.isHookProcessingActive()
) {
return;
}
//Add request URI hash to low severity event filter
LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest());

RXSSOperation rxssOperation = new RXSSOperation(
NewRelicSecurity.getAgent().getSecurityMetaData().getRequest(),
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse(),
HttpRequestToMuleEvent_Instrumentation.class.getName(),
MuleHelper.TRANSFORM_METHOD
);
NewRelicSecurity.getAgent().registerOperation(rxssOperation);
ServletHelper.tmpFileCleanUp(NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getTempFiles());
} catch (Throwable e) {
if(e instanceof NewRelicSecurityException){
e.printStackTrace();
throw e;
}
}
}

private static boolean acquireLockIfPossible(int hashcode) {
try {
return GenericHelper.acquireLockIfPossible(MuleHelper.getNrSecCustomAttribName(hashcode));
} catch (Throwable ignored) {}
return false;
}

private static void releaseLock(int hashcode) {
try {
GenericHelper.releaseLock(MuleHelper.getNrSecCustomAttribName(hashcode));
} catch (Throwable e) {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package org.mule.module.http.internal.listener.async;

import com.newrelic.agent.security.instrumentation.mule37.MuleHelper;
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.schema.AgentMetaData;
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.weaver.MatchType;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;
import org.mule.module.http.internal.domain.request.HttpRequest;
import org.mule.module.http.internal.domain.request.HttpRequestContext;

@Weave(type = MatchType.Interface, originalName = "org.mule.module.http.internal.listener.async.RequestHandler")
public class RequestHandler_Instrumentation {
public void handleRequest(HttpRequestContext requestContext, HttpResponseReadyCallback responseCallback) {
boolean isLockAcquired = acquireLockIfPossible(requestContext.hashCode());
if (isLockAcquired) {
preprocessSecurityHook(requestContext);
}
try {
Weaver.callOriginal();
} finally {
if (isLockAcquired) {
releaseLock(requestContext.hashCode());
}
}
if (isLockAcquired) {
postProcessSecurityHook();
}
}

private void preprocessSecurityHook(HttpRequestContext requestContext) {
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();

HttpRequest httpRequest = requestContext.getRequest();
securityRequest.setMethod(httpRequest.getMethod());
securityRequest.setClientIP(requestContext.getClientConnection().getRemoteHostAddress().toString());
securityRequest.setServerPort(
NewRelicSecurity
.getAgent()
.getSecurityMetaData()
.getCustomAttribute(MuleHelper.MULE_SERVER_PORT_ATTRIB_NAME, Integer.class)
);

if (securityRequest.getClientIP() != null && !securityRequest.getClientIP().trim().isEmpty()) {
securityAgentMetaData.getIps().add(securityRequest.getClientIP());
securityRequest.setClientPort(String.valueOf(requestContext.getClientConnection().getRemoteHostAddress().getPort()));
}

MuleHelper.processHttpRequestHeader(httpRequest, securityRequest);
securityMetaData.setTracingHeaderValue(MuleHelper.getTraceHeader(securityRequest.getHeaders()));

securityRequest.setProtocol(requestContext.getScheme());
securityRequest.setUrl(httpRequest.getUri());

// TODO: Create OutBoundHttp data here : Skipping for now.

securityRequest.setContentType(MuleHelper.getContentType(httpRequest));

// TODO: need to update UserClassEntity
ServletHelper.registerUserLevelCode(MuleHelper.LIBRARY_NAME);
securityRequest.setRequestParsed(true);
} catch (Throwable ignored){}
}

private void postProcessSecurityHook() {
try {
if (!NewRelicSecurity.isHookProcessingActive()
) {
return;
}
//Add request URI hash to low severity event filter
LowSeverityHelper.addRrequestUriToEventFilter(NewRelicSecurity.getAgent().getSecurityMetaData().getRequest());

RXSSOperation rxssOperation = new RXSSOperation(
NewRelicSecurity.getAgent().getSecurityMetaData().getRequest(),
NewRelicSecurity.getAgent().getSecurityMetaData().getResponse(),
this.getClass().getName(),
MuleHelper.HANDLE_REQUEST_METHOD
);
NewRelicSecurity.getAgent().registerOperation(rxssOperation);
ServletHelper.tmpFileCleanUp(NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getTempFiles());
} catch (Throwable e) {
if(e instanceof NewRelicSecurityException){
e.printStackTrace();
throw e;
}
}
}

private boolean acquireLockIfPossible(int hashcode) {
try {
return GenericHelper.acquireLockIfPossible(MuleHelper.getNrSecCustomAttribName(hashcode));
} catch (Throwable ignored) {}
return false;
}

private void releaseLock(int hashcode) {
try {
GenericHelper.releaseLock(MuleHelper.getNrSecCustomAttribName(hashcode));
} catch (Throwable e) {}
}
}
Loading
Loading