Skip to content

Commit

Permalink
Merge pull request #240 from newrelic/enhancments/report-iast-failures
Browse files Browse the repository at this point in the history
NR-254173 : Enhancements in reporting of IAST Scan failure
  • Loading branch information
lovesh-ap authored May 14, 2024
2 parents ee1a267 + 3dcc960 commit 11ac210
Show file tree
Hide file tree
Showing 12 changed files with 224 additions and 10 deletions.
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.2.1
jsonVersion=1.2.0
jsonVersion=1.2.1
# Updated exposed NR APM API version.
nrAPIVersion=8.4.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.newrelic.agent.security.intcodeagent.filelogging.FileLoggerThreadPool;
import com.newrelic.agent.security.intcodeagent.websocket.JsonConverter;
import com.newrelic.api.agent.security.NewRelicSecurity;
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;
import okhttp3.*;
Expand Down Expand Up @@ -59,5 +59,36 @@ public static Request generateK2Request(FuzzRequestBean httpRequest, String endp
}
return null;
}

public static String extractNRCsecFuzzReqHeader(FuzzRequestBean httpRequest) {
if( httpRequest == null) {
return null;
}
try {
for (Map.Entry<String, String> header : httpRequest.getHeaders().entrySet()) {
String key = header.getKey().toLowerCase();
if (ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID.equals(key)){
return header.getValue();
}
}
} catch (Exception e){
return null;
}
return null;
}
public static String extractNRCsecFuzzReqHeader(Headers headers) {
if (headers == null){
return null;
}
try{
for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) {
String key = header.getKey().toLowerCase();
if (ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID.equals(key)){
return com.newrelic.api.agent.security.schema.StringUtils.join(header.getValue().toArray(new String[0]));
}
}
} catch (Exception e){
return null;
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.newrelic.agent.security.intcodeagent.filelogging.FileLoggerThreadPool;
import com.newrelic.agent.security.intcodeagent.logging.IAgentConstants;
import com.newrelic.agent.security.intcodeagent.models.FuzzRequestBean;
import com.newrelic.api.agent.security.NewRelicSecurity;
import com.newrelic.api.agent.security.utils.logging.LogLevel;
import com.newrelic.agent.security.intcodeagent.models.javaagent.FuzzFailEvent;
import com.newrelic.agent.security.intcodeagent.websocket.EventSendPool;
Expand Down Expand Up @@ -130,6 +131,9 @@ public void fireRequest(FuzzRequestBean httpRequest, List<String> endpoints, int
try {
responseCode = RestClient.getInstance().fireRequest(request, repeatCount + endpoints.size() -1, fuzzRequestId);
} catch (SSLException e) {
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.postLogMessageIfNecessary(LogLevel.WARNING,
String.format(CALL_FAILED_REQUEST_S_REASON, fuzzRequestId),
Expand Down Expand Up @@ -170,11 +174,20 @@ public int fireRequest(Request request, int repeatCount, String fuzzRequestId) t
try {
Response response = call.execute();
logger.log(LogLevel.FINER, String.format(REQUEST_FIRED_SUCCESS, request), RestClient.class.getName());
if(response.code() >= 400 && response.code() < 500){
RestRequestThreadPool.getInstance().getProcessedIds().putIfAbsent(fuzzRequestId, new HashSet<>());
if (response.code() >= 500) {
logger.postLogMessageIfNecessary(LogLevel.WARNING,
String.format(RestClient.CALL_FAILED_REQUEST_S_REASON_S, fuzzRequestId, response, response.body().string()), null,
RestRequestProcessor.class.getName());
}
else if(response.code() >= 400){
String responseBody = response.body().string();
NewRelicSecurity.getAgent().reportIASTScanFailure(null, null, null,
RequestUtils.extractNRCsecFuzzReqHeader(request.headers()), fuzzRequestId,
String.format(IAgentConstants.REQUEST_FAILURE_FOR_S_WITH_RESPONSE_CODE, request.url(), response, responseBody));
RestRequestThreadPool.getInstance().getProcessedIds().putIfAbsent(fuzzRequestId, new HashSet<>());
logger.postLogMessageIfNecessary(LogLevel.WARNING,
String.format(RestClient.CALL_FAILED_REQUEST_S_REASON_S, fuzzRequestId, response, responseBody), null,
RestRequestProcessor.class.getName());
} else if(response.isSuccessful()){
RestRequestThreadPool.getInstance().getProcessedIds().putIfAbsent(fuzzRequestId, new HashSet<>());
}else {
Expand All @@ -193,6 +206,10 @@ public int fireRequest(Request request, int repeatCount, String fuzzRequestId) t
return fireRequest(request, --repeatCount, fuzzRequestId);
}
} catch (IOException e) {
NewRelicSecurity.getAgent().reportIASTScanFailure(null, null,
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.postLogMessageIfNecessary(LogLevel.WARNING,
String.format(CALL_FAILED_REQUEST_S_REASON, fuzzRequestId),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.newrelic.agent.security.instrumentator.os.OsVariablesInstance;
import com.newrelic.agent.security.instrumentator.utils.CallbackUtils;
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.utils.logging.LogLevel;
import com.newrelic.agent.security.intcodeagent.models.FuzzRequestBean;
Expand All @@ -14,10 +15,8 @@
import com.newrelic.api.agent.security.instrumentation.helpers.GrpcClientRequestReplayHelper;
import com.newrelic.api.agent.security.schema.ControlCommandDto;
import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper;
import okhttp3.Request;
import org.apache.commons.lang3.StringUtils;

import javax.net.ssl.SSLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -64,7 +63,7 @@ public Boolean call() throws InterruptedException {
return false;
}

FuzzRequestBean httpRequest;
FuzzRequestBean httpRequest = null;
try {
if (WSUtils.getInstance().isReconnecting()) {
synchronized (WSUtils.getInstance()) {
Expand Down Expand Up @@ -92,12 +91,14 @@ public Boolean call() throws InterruptedException {
if (httpRequest.getIsGrpc()){
List<String> payloadList = new ArrayList<>();
try{
logger.log(LogLevel.FINER, String.format("Firing request : %s", objectMapper.writeValueAsString(httpRequest)), RestRequestProcessor.class.getName());
List<?> list = objectMapper.readValue(String.valueOf(httpRequest.getBody()), List.class);
for (Object o : list) {
payloadList.add(objectMapper.writeValueAsString(o));
}
} catch (Throwable e) {
NewRelicSecurity.getAgent().reportIASTScanFailure(null, null,
e, RequestUtils.extractNRCsecFuzzReqHeader(httpRequest), controlCommand.getId(),
String.format(IAgentConstants.FAILURE_WHILE_GRPC_REQUEST_BODY_CONVERSION, httpRequest.getBody()));
logger.log(LogLevel.FINEST, String.format(ERROR_IN_FUZZ_REQUEST_GENERATION, e.getMessage()), RestRequestProcessor.class.getSimpleName());
}
MonitorGrpcFuzzFailRequestQueueThread.submitNewTask();
Expand All @@ -108,13 +109,21 @@ public Boolean call() throws InterruptedException {
}
return true;
} catch (JsonProcessingException e){
NewRelicSecurity.getAgent().reportIASTScanFailure(null, null,
e, null, controlCommand.getId(),
String.format(JSON_PARSING_ERROR_WHILE_PROCESSING_FUZZING_REQUEST_S, controlCommand.getArguments().get(0)));

logger.log(LogLevel.SEVERE,
String.format(JSON_PARSING_ERROR_WHILE_PROCESSING_FUZZING_REQUEST_S, controlCommand.getArguments().get(0)),
e, RestRequestProcessor.class.getName());
logger.postLogMessageIfNecessary(LogLevel.SEVERE,
String.format(JSON_PARSING_ERROR_WHILE_PROCESSING_FUZZING_REQUEST_S, controlCommand.getId()), e, RestRequestProcessor.class.getName());
RestRequestThreadPool.getInstance().getProcessedIds().putIfAbsent(controlCommand.getId(), new HashSet<>());
} catch (Throwable e) {
NewRelicSecurity.getAgent().reportIASTScanFailure(null, null,
e, RequestUtils.extractNRCsecFuzzReqHeader(httpRequest), controlCommand.getId(),
String.format(JSON_PARSING_ERROR_WHILE_PROCESSING_FUZZING_REQUEST_S, controlCommand.getArguments().get(0)));

logger.log(LogLevel.SEVERE,
String.format(ERROR_WHILE_PROCESSING_FUZZING_REQUEST_S, controlCommand.getArguments().get(0)),
e, RestRequestProcessor.class.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -574,4 +574,9 @@ public interface IAgentConstants {
String HTTP_STR = "http";
String HTTPS_STR = "https";
String ENDPOINT_LOCALHOST_S = "%s://localhost:%s";

String SSL_EXCEPTION_FAILURE_MESSAGE = "SSL Exception raised for url : %s";
String REQUEST_FAILURE_DUE_TO_IOEXCEPTION = "Request failure could be due to cancellation, a connectivity problem or timeout.";
String FAILURE_WHILE_GRPC_REQUEST_BODY_CONVERSION = "Failure while processing gRPC Request body, body : %s ";
String REQUEST_FAILURE_FOR_S_WITH_RESPONSE_CODE = "Request failure for : %s, with response : %s and response body : %s";
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class AgentBasicInfo {
public static final String NR_ENTITY_GUID = "entityGuid";
public static final String EXCEPTION_INCIDENT = "exception-incident";

public static final String IAST_SCAN_FAILURE = "iast-scan-failure";

/**
* Tool id for Language Agent.
*/
Expand Down Expand Up @@ -99,6 +101,9 @@ public AgentBasicInfo() {
} else if (this instanceof ErrorIncident) {
setJsonName(EXCEPTION_INCIDENT);
setEventType(EXCEPTION_INCIDENT);
} else if (this instanceof IASTScanFailure) {
setJsonName(IAST_SCAN_FAILURE);
setEventType(IAST_SCAN_FAILURE);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.newrelic.agent.security.intcodeagent.models.javaagent;

import com.newrelic.agent.security.intcodeagent.websocket.JsonConverter;

public class IASTReplayFailure {

private String apiId;
private String nrCsecFuzzRequestId;
private String controlCommandId;
private String failureMessage;
private LogMessageException error;

public IASTReplayFailure() {
}

public IASTReplayFailure(String apiId, String nrCsecFuzzRequestId, String controlCommandId, String failureMessage, LogMessageException error) {
this.apiId = apiId;
this.nrCsecFuzzRequestId = nrCsecFuzzRequestId;
this.controlCommandId = controlCommandId;
this.failureMessage = failureMessage;
this.error = error;
}

public String getApiId() {
return apiId;
}

public void setApiId(String apiId) {
this.apiId = apiId;
}

public String getNrCsecFuzzRequestId() {
return nrCsecFuzzRequestId;
}

public void setNrCsecFuzzRequestId(String nrCsecFuzzRequestId) {
this.nrCsecFuzzRequestId = nrCsecFuzzRequestId;
}

public String getControlCommandId() {
return controlCommandId;
}

public void setControlCommandId(String controlCommandId) {
this.controlCommandId = controlCommandId;
}

public String getFailureMessage() {
return failureMessage;
}

public void setFailureMessage(String failureMessage) {
this.failureMessage = failureMessage;
}

public LogMessageException getError() {
return error;
}

public void setError(LogMessageException error) {
this.error = error;
}

public String toString() {
return JsonConverter.toJSON(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.newrelic.agent.security.intcodeagent.models.javaagent;

import com.newrelic.agent.security.intcodeagent.websocket.JsonConverter;
import com.newrelic.api.agent.security.schema.SecurityMetaData;

import java.time.Instant;

public class IASTScanFailure extends AgentBasicInfo{

private Long timestamp;

private IASTReplayFailure replayFailure;

private SecurityMetaData securityAgentMetaData;

public IASTScanFailure(IASTReplayFailure replayFailure, SecurityMetaData securityAgentMetaData) {
super();
this.timestamp = Instant.now().toEpochMilli();
this.replayFailure = replayFailure;
this.securityAgentMetaData = securityAgentMetaData;
}

public IASTReplayFailure getReplayFailure() {
return replayFailure;
}

public void setReplayFailure(IASTReplayFailure replayFailure) {
this.replayFailure = replayFailure;
}

public SecurityMetaData getSecurityAgentMetaData() {
return securityAgentMetaData;
}

public void setSecurityAgentMetaData(SecurityMetaData securityAgentMetaData) {
this.securityAgentMetaData = securityAgentMetaData;
}

public Long getTimestamp() {
return timestamp;
}

public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}

public String toString() {
return JsonConverter.toJSON(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
import com.newrelic.agent.security.intcodeagent.constants.AgentServices;
import com.newrelic.agent.security.intcodeagent.filelogging.FileLoggerThreadPool;
import com.newrelic.agent.security.intcodeagent.filelogging.LogFileHelper;
import com.newrelic.agent.security.intcodeagent.models.javaagent.*;
import com.newrelic.agent.security.intcodeagent.utils.EncryptorUtils;
import com.newrelic.api.agent.security.instrumentation.helpers.*;
import com.newrelic.api.agent.security.utils.logging.LogLevel;
import com.newrelic.agent.security.intcodeagent.logging.HealthCheckScheduleThread;
import com.newrelic.agent.security.intcodeagent.logging.IAgentConstants;
import com.newrelic.agent.security.intcodeagent.models.javaagent.ExitEventBean;
import com.newrelic.agent.security.intcodeagent.properties.BuildInfo;
import com.newrelic.agent.security.intcodeagent.schedulers.FileCleaner;
import com.newrelic.agent.security.intcodeagent.schedulers.SchedulerHelper;
Expand Down Expand Up @@ -661,6 +661,23 @@ public void reportIncident(LogLevel logLevel, String event, Throwable exception,
}
}

@Override
public void reportIASTScanFailure(SecurityMetaData securityMetaData, String apiId, Throwable exception,
String nrCsecFuzzRequestId, String controlCommandId, String failureMessage) {
if(WSUtils.isConnected()){
LogMessageException message = null; SecurityMetaData metaData = null;
if(exception != null) {
message = new LogMessageException(exception, 0, 1);
}
if(securityMetaData != null){
metaData = new SecurityMetaData(securityMetaData);
}
IASTReplayFailure replayFailure = new IASTReplayFailure(apiId, nrCsecFuzzRequestId, controlCommandId, failureMessage, message);
IASTScanFailure scanFailure = new IASTScanFailure(replayFailure, metaData);
EventSendPool.getInstance().sendEvent(scanFailure);
}
}

@Override
public void retransformUninstrumentedClass(Class<?> classToRetransform) {
if (!classToRetransform.isAnnotationPresent(InstrumentedClass.class)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ public void reportIncident(LogLevel logLevel, String event, Throwable exception,

}

@Override
public void reportIASTScanFailure(SecurityMetaData securityMetaData, String apiId, Throwable exception, String nrCsecFuzzRequestId, String controlCommandId, String failureMessage) {

}

@Override
public void retransformUninstrumentedClass(Class<?> classToRetransform) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ public void reportIncident(LogLevel logLevel, String event, Throwable exception,

}

@Override
public void reportIASTScanFailure(SecurityMetaData securityMetaData, String apiId, Throwable exception, String nrCsecFuzzRequestId, String controlCommandId, String failureMessage) {

}

@Override
public void retransformUninstrumentedClass(Class<?> classToRetransform) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ public interface SecurityAgent {

void reportIncident(LogLevel logLevel, String event, Throwable exception, String caller);

void reportIASTScanFailure(SecurityMetaData securityMetaData, String apiId, Throwable exception,
String nrCsecFuzzRequestId, String controlCommandId, String failureMessage);

void retransformUninstrumentedClass(Class<?> classToRetransform);

String decryptAndVerify(String encryptedData, String hashVerifier);
Expand Down

0 comments on commit 11ac210

Please sign in to comment.