Skip to content

Commit

Permalink
Support for org.asynchttpclient:async-http-client (#102)
Browse files Browse the repository at this point in the history
* Support for org.asynchttpclient:async-http-client

* Unit test cases of async http client (#103)

Co-authored-by: Monu Lakshkar <[email protected]>
  • Loading branch information
lovesh-ap and monu-k2io authored Nov 23, 2023
1 parent f0ba272 commit 0d97346
Show file tree
Hide file tree
Showing 6 changed files with 786 additions and 1 deletion.
27 changes: 27 additions & 0 deletions instrumentation-security/async-http-client-2.0.0/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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.asynchttpclient:async-http-client:2.0.0")
}

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

verifyInstrumentation {
passesOnly 'org.asynchttpclient:async-http-client:[2.0.0-RC1,)'
excludeRegex ".*(alpha|RC).*"
}

test {
// These instrumentation tests only run on Java 8 regardless of the -PtestN gradle property that is set.
onlyIf {
project.hasProperty('test8')
}
}

site {
title 'Async Http Client'
type 'Messaging'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.newrelic.agent.security.instrumentation.org.asynchttpclient;

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 org.asynchttpclient.Request;

public class AsynchttpHelper {


public static final String NR_SEC_CUSTOM_ATTRIB_NAME = "ASYNCHTTP_OPERATION_LOCK-";

public static final String METHOD_EXECUTE = "executeRequest";

public static boolean skipExistsEvent() {
if (!(NewRelicSecurity.getAgent().getCurrentPolicy().getVulnerabilityScan().getEnabled() &&
NewRelicSecurity.getAgent().getCurrentPolicy().getVulnerabilityScan().getIastScan().getEnabled())) {
return true;
}

return false;
}

public static boolean isLockAcquired() {
try {
return NewRelicSecurity.isHookProcessingActive() &&
Boolean.TRUE.equals(NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(getNrSecCustomAttribName(), Boolean.class));
} catch (Throwable ignored) {}
return false;
}

public static boolean acquireLockIfPossible() {
try {
if (NewRelicSecurity.isHookProcessingActive() &&
!isLockAcquired()) {
NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(getNrSecCustomAttribName(), true);
return true;
}
} catch (Throwable ignored){}
return false;
}

public static void releaseLock() {
try {
if (NewRelicSecurity.isHookProcessingActive()) {
NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(getNrSecCustomAttribName(), null);
}
} catch (Throwable ignored) {
}
}

private static String getNrSecCustomAttribName() {
return NR_SEC_CUSTOM_ATTRIB_NAME + Thread.currentThread().getId();
}

public static AbstractOperation preprocessSecurityHook(String url, String className, String methodName) {
try {
if (!NewRelicSecurity.isHookProcessingActive() ||
NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty() ||
url == null || url.trim().isEmpty()) {
return null;
}

SSRFOperation operation = new SSRFOperation(url,
className, methodName);
NewRelicSecurity.getAgent().registerOperation(operation);
return operation;
} catch (Throwable e) {
if (e instanceof NewRelicSecurityException) {
e.printStackTrace();
throw e;
}
}
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 ignored) {
}
}

public static Request addSecurityHeaders(Request request, AbstractOperation operation) {
if (operation == null || request == null) {
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 = NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(GenericHelper.CSEC_PARENT_ID, String.class);
if(StringUtils.isNotBlank(csecParaentId)){
request.getHeaders().add(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
request.getHeaders().remove(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER);
request.getHeaders().add(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER,
SSRFUtils.generateTracingHeaderValue(NewRelicSecurity.getAgent().getSecurityMetaData()
.getTracingHeaderValue(),
operation.getApiID(), operation.getExecutionId(),
NewRelicSecurity.getAgent().getAgentUUID()));
}
return request;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package org.asynchttpclient;
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 com.newrelic.agent.security.instrumentation.org.asynchttpclient.AsynchttpHelper;

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

/**
* Instrumentation for the provider interface.
*/
@Weave(type = MatchType.Interface, originalName = "org.asynchttpclient.AsyncHttpClient")
public abstract class AsyncHttpClient_Instrumentation {

public <T> ListenableFuture<T> executeRequest(Request request, AsyncHandler<T> handler) {
URI uri = null;
boolean isLockAcquired = AsynchttpHelper.acquireLockIfPossible();
AbstractOperation operation = null;
if(isLockAcquired) {
try {
uri = new URI(request.getUrl());
String scheme = uri.getScheme().toLowerCase();

// only instrument HTTP or HTTPS calls
if (("http".equals(scheme) || "https".equals(scheme))) {
operation = AsynchttpHelper.preprocessSecurityHook(uri.toURL().toString(), this.getClass().getName(),
AsynchttpHelper.METHOD_EXECUTE);
Request updatedRequest = AsynchttpHelper.addSecurityHeaders(request, operation);
if (updatedRequest != null) {
request = updatedRequest;
}
}

} catch (URISyntaxException | MalformedURLException uriSyntaxException) {
// if Java can't parse the URI, asynchttpclient won't be able to either
// let's just proceed without instrumentation
}
}
ListenableFuture<T> returnVal = null;
try {
returnVal = Weaver.callOriginal();
} finally {
if(isLockAcquired){
AsynchttpHelper.releaseLock();
}
}
AsynchttpHelper.registerExitOperation(isLockAcquired, operation);
return returnVal;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

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 0d97346

Please sign in to comment.