Skip to content

Commit

Permalink
Merge pull request #116 from newrelic/feature/ning-http-client-1.0
Browse files Browse the repository at this point in the history
Support for Ning async http client 1.0.x
  • Loading branch information
lovesh-ap authored Dec 26, 2023
2 parents bf3283b + e9213d8 commit 6114931
Show file tree
Hide file tree
Showing 7 changed files with 588 additions and 0 deletions.
19 changes: 19 additions & 0 deletions instrumentation-security/ning-async-http-client-1.0.0/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
dependencies {
implementation(project(":newrelic-security-api"))
implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}")
implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}")
implementation("com.ning:async-http-client:1.0.0")
}

jar {
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.ning-async-http-client-1.0.0' }
}

verifyInstrumentation {
passesOnly 'com.ning:async-http-client:[1.0,1.1)'
}

site {
title 'Ning AsyncHttpClient'
type 'Messaging'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.newrelic.agent.security.instrumentation.ning.http_1_0;

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.ning.http.client.Request;

import java.net.URI;
import java.net.URISyntaxException;

public class NingHelper {
public static final String METHOD_NAME_EXECUTE = "execute";
public static final String NR_SEC_CUSTOM_ATTRIB_NAME = "SSRF_OPERATION_LOCK_NING-";

public static 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 ignored) {
}
}

public static AbstractOperation preprocessSecurityHook(Request request, String uri, String methodName, String className) {
try {
SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData();
if (!NewRelicSecurity.isHookProcessingActive() || securityMetaData.getRequest().isEmpty()
) {
return null;
}

// Add Security IAST header
String iastHeader = NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier().getRaw();
if (iastHeader != null && !iastHeader.trim().isEmpty()) {
request.getHeaders().add(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID, iastHeader);
}

String csecParaentId = securityMetaData.getCustomAttribute(GenericHelper.CSEC_PARENT_ID, String.class);
if(StringUtils.isNotBlank(csecParaentId)){
request.getHeaders().add(GenericHelper.CSEC_PARENT_ID, csecParaentId);
}

SSRFOperation operation = new SSRFOperation(uri, className, methodName);
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
request.getHeaders().add(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER, SSRFUtils.generateTracingHeaderValue(securityMetaData.getTracingHeaderValue(), operation.getApiID(), operation.getExecutionId(), NewRelicSecurity.getAgent().getAgentUUID()));
}
}
return operation;
} catch (Throwable e) {
if (e instanceof NewRelicSecurityException) {
e.printStackTrace();
throw e;
}
}
return null;
}

public static void releaseLock(int hashCode) {
try {
GenericHelper.releaseLock(NR_SEC_CUSTOM_ATTRIB_NAME, hashCode);
} catch (Throwable ignored) {
}
}

public static boolean acquireLockIfPossible(int hashCode) {
try {
return GenericHelper.acquireLockIfPossible(NR_SEC_CUSTOM_ATTRIB_NAME, hashCode);
} catch (Throwable ignored) {
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.ning.http.client;

import com.newrelic.agent.security.instrumentation.ning.http_1_0.NingHelper;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Segment;
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 java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.Future;

@Weave(type = MatchType.Interface, originalName = "com.ning.http.client.AsyncHttpProvider")
public class AsyncHttpProvider_Instrumentation {

public <T> Future<T> execute(Request request, AsyncHandler<T> handler) throws IOException {
boolean isLockAcquired = NingHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
URI uri = null;
Future<T> returnObj = null;

try {
uri = new URI(request.getUrl());
String scheme = uri.getScheme();

if (isLockAcquired && (scheme == null || scheme.equals("http") || scheme.equals("https"))) {
operation = NingHelper.preprocessSecurityHook(request, uri.toString(), NingHelper.METHOD_NAME_EXECUTE, this.getClass().getName());
}
} catch (URISyntaxException uriSyntaxException) {
// Instrumentation won't work and normal execution will continue
}

try {
returnObj = Weaver.callOriginal();
} finally {
if (isLockAcquired) {
NingHelper.releaseLock(this.hashCode());
}
}
NingHelper.registerExitOperation(isLockAcquired, operation);

return returnObj;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.ning.http.client;

import com.newrelic.api.agent.weaver.Weave;

@Weave(originalName = "com.ning.http.client.RequestBuilderBase")
abstract class RequestBuilderBase_Instrumentation {

@Weave(originalName = "com.ning.http.client.RequestBuilderBase$RequestImpl")
private abstract static class RequestImpl_Instrumentation {
private Headers headers;

// Added this instrumentation to return modifiable headers instead
public Headers getHeaders() {
return headers;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package play;

import com.newrelic.api.agent.weaver.SkipIfPresent;

/**
* Play v1 instrumentation is implemented using its own set of pointcuts that don't work well with our async APIs. This
* class is present in Play v1 but not v2, and will cause this module NOT to load if the customer is using Play v1.
*/
@SkipIfPresent
public class CorePlugin {
}
Loading

0 comments on commit 6114931

Please sign in to comment.