Skip to content

Commit

Permalink
Merge pull request #230 from newrelic/fix/ThreadLocalLockHelper/NR-25…
Browse files Browse the repository at this point in the history
…9467

NR-259467 : Fix issue of nested event generation from CSEC's agent itself
  • Loading branch information
IshikaDawda authored Apr 17, 2024
2 parents e9cb399 + 77433a2 commit 0c51482
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.newrelic.agent.security.intcodeagent.filelogging.FileLoggerThreadPool;
import com.newrelic.agent.security.intcodeagent.filelogging.LogFileHelper;
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;
Expand All @@ -22,10 +23,6 @@
import com.newrelic.agent.security.util.IUtilConstants;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Transaction;
import com.newrelic.api.agent.security.instrumentation.helpers.GrpcHelper;
import com.newrelic.api.agent.security.instrumentation.helpers.AppServerInfoHelper;
import com.newrelic.api.agent.security.instrumentation.helpers.InstrumentedClass;
import com.newrelic.api.agent.security.instrumentation.helpers.LowSeverityHelper;
import com.newrelic.api.agent.security.schema.*;
import com.newrelic.api.agent.security.schema.operation.RXSSOperation;
import com.newrelic.api.agent.security.schema.policy.AgentPolicy;
Expand Down Expand Up @@ -249,60 +246,69 @@ private void deactivateSecurityServices(){
@Override
public void registerOperation(AbstractOperation operation) {
// added to fetch request/response in case of grpc requests
SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData();
if (securityMetaData!=null && securityMetaData.getRequest().getIsGrpc()){
securityMetaData.getRequest().setBody(
new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_REQUEST_DATA, List.class))));
securityMetaData.getResponse().setResponseBody(
new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_RESPONSE_DATA, List.class))));
}
// end
boolean lockAcquired = ThreadLocalLockHelper.acquireLock();
try {
if(lockAcquired) {
SecurityMetaData securityMetaData = NewRelicSecurity.getAgent().getSecurityMetaData();
if (securityMetaData != null && securityMetaData.getRequest().getIsGrpc()) {
securityMetaData.getRequest().setBody(
new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_REQUEST_DATA, List.class))));
securityMetaData.getResponse().setResponseBody(
new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_RESPONSE_DATA, List.class))));
}
// end

if (operation == null || operation.isEmpty()) {
return;
}
String executionId = ExecutionIDGenerator.getExecutionId();
operation.setExecutionId(executionId);
operation.setStartTime(Instant.now().toEpochMilli());
if(securityMetaData!=null && securityMetaData.getFuzzRequestIdentifier().getK2Request()){
logger.log(LogLevel.FINEST, String.format("New Event generation with id %s of type %s", operation.getExecutionId(), operation.getClass().getSimpleName()), Agent.class.getName());
}
if (operation instanceof RXSSOperation) {
operation.setStackTrace(securityMetaData.getMetaData().getServiceTrace());
} else {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
operation.setStackTrace(Arrays.copyOfRange(trace, 2, trace.length));
}
if (operation == null || operation.isEmpty()) {
return;
}
String executionId = ExecutionIDGenerator.getExecutionId();
operation.setExecutionId(executionId);
operation.setStartTime(Instant.now().toEpochMilli());
if (securityMetaData != null && securityMetaData.getFuzzRequestIdentifier().getK2Request()) {
logger.log(LogLevel.FINEST, String.format("New Event generation with id %s of type %s", operation.getExecutionId(), operation.getClass().getSimpleName()), Agent.class.getName());
}
if (operation instanceof RXSSOperation) {
operation.setStackTrace(securityMetaData.getMetaData().getServiceTrace());
} else {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
operation.setStackTrace(Arrays.copyOfRange(trace, 2, trace.length));
}

// added to fetch request/response in case of grpc requests
if (securityMetaData.getRequest().getIsGrpc()){
securityMetaData.getRequest().setBody(
new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_REQUEST_DATA, List.class))));
securityMetaData.getResponse().setResponseBody(
new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_RESPONSE_DATA, List.class))));
}
// added to fetch request/response in case of grpc requests
if (securityMetaData.getRequest().getIsGrpc()) {
securityMetaData.getRequest().setBody(
new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_REQUEST_DATA, List.class))));
securityMetaData.getResponse().setResponseBody(
new StringBuilder(JsonConverter.toJSON(securityMetaData.getCustomAttribute(GrpcHelper.NR_SEC_GRPC_RESPONSE_DATA, List.class))));
}

if(checkIfNRGeneratedEvent(operation)) {
logger.log(LogLevel.FINEST, DROPPING_EVENT_AS_IT_WAS_GENERATED_BY_K_2_INTERNAL_API_CALL +
JsonConverter.toJSON(operation),
Agent.class.getName());
return;
}
if (checkIfNRGeneratedEvent(operation)) {
logger.log(LogLevel.FINEST, DROPPING_EVENT_AS_IT_WAS_GENERATED_BY_K_2_INTERNAL_API_CALL +
JsonConverter.toJSON(operation),
Agent.class.getName());
return;
}

logIfIastScanForFirstTime(securityMetaData.getFuzzRequestIdentifier(), securityMetaData.getRequest());
logIfIastScanForFirstTime(securityMetaData.getFuzzRequestIdentifier(), securityMetaData.getRequest());

setRequiredStackTrace(operation, securityMetaData);
operation.setUserClassEntity(setUserClassEntity(operation, securityMetaData));
processStackTrace(operation);
setRequiredStackTrace(operation, securityMetaData);
operation.setUserClassEntity(setUserClassEntity(operation, securityMetaData));
processStackTrace(operation);
// boolean blockNeeded = checkIfBlockingNeeded(operation.getApiID());
// securityMetaData.getMetaData().setApiBlocked(blockNeeded);
if (needToGenerateEvent(operation.getApiID())) {
DispatcherPool.getInstance().dispatchEvent(operation, securityMetaData);
if (!firstEventProcessed.get()) {
logger.logInit(LogLevel.INFO,
String.format(EVENT_ZERO_PROCESSED, securityMetaData.getRequest()),
this.getClass().getName());
firstEventProcessed.set(true);
if (needToGenerateEvent(operation.getApiID())) {
DispatcherPool.getInstance().dispatchEvent(operation, securityMetaData);
if (!firstEventProcessed.get()) {
logger.logInit(LogLevel.INFO,
String.format(EVENT_ZERO_PROCESSED, securityMetaData.getRequest()),
this.getClass().getName());
firstEventProcessed.set(true);
}
}
}
} finally {
if(lockAcquired){
ThreadLocalLockHelper.releaseLock();
}
}
}
Expand Down Expand Up @@ -439,21 +445,30 @@ private static void setAPIId(AbstractOperation operation, List<Integer> traceFor

@Override
public void registerExitEvent(AbstractOperation operation) {
if (operation == null) {
return;
}
K2RequestIdentifier k2RequestIdentifier = NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier();
HttpRequest request = NewRelicSecurity.getAgent().getSecurityMetaData().getRequest();

// TODO: Generate for only native payloads
if (!request.isEmpty() && !operation.isEmpty() && k2RequestIdentifier.getK2Request()) {
if (StringUtils.equals(k2RequestIdentifier.getApiRecordId(), operation.getApiID())
&& StringUtils.equals(k2RequestIdentifier.getNextStage().getStatus(), IAgentConstants.VULNERABLE)) {
ExitEventBean exitEventBean = new ExitEventBean(operation.getExecutionId(), operation.getCaseType().getCaseType());
exitEventBean.setK2RequestIdentifier(k2RequestIdentifier.getRaw());
logger.log(LogLevel.FINER, "Exit event : " + exitEventBean, this.getClass().getName());
DispatcherPool.getInstance().dispatchExitEvent(exitEventBean);
AgentInfo.getInstance().getJaHealthCheck().incrementExitEventSentCount();
boolean lockAcquired = ThreadLocalLockHelper.acquireLock();
try {
if(lockAcquired) {
if (operation == null) {
return;
}
K2RequestIdentifier k2RequestIdentifier = NewRelicSecurity.getAgent().getSecurityMetaData().getFuzzRequestIdentifier();
HttpRequest request = NewRelicSecurity.getAgent().getSecurityMetaData().getRequest();

// TODO: Generate for only native payloads
if (!request.isEmpty() && !operation.isEmpty() && k2RequestIdentifier.getK2Request()) {
if (StringUtils.equals(k2RequestIdentifier.getApiRecordId(), operation.getApiID())
&& StringUtils.equals(k2RequestIdentifier.getNextStage().getStatus(), IAgentConstants.VULNERABLE)) {
ExitEventBean exitEventBean = new ExitEventBean(operation.getExecutionId(), operation.getCaseType().getCaseType());
exitEventBean.setK2RequestIdentifier(k2RequestIdentifier.getRaw());
logger.log(LogLevel.FINER, "Exit event : " + exitEventBean, this.getClass().getName());
DispatcherPool.getInstance().dispatchExitEvent(exitEventBean);
AgentInfo.getInstance().getJaHealthCheck().incrementExitEventSentCount();
}
}
}
} finally {
if(lockAcquired){
ThreadLocalLockHelper.releaseLock();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.newrelic.api.agent.security;

import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.security.instrumentation.helpers.ThreadLocalLockHelper;
import com.newrelic.api.agent.security.schema.SecurityMetaData;
import org.apache.commons.lang3.StringUtils;

Expand All @@ -34,7 +35,7 @@ public static SecurityAgent getAgent(){
* {@code false} otherwise.
*/
public static boolean isHookProcessingActive(){
return isAgentInitComplete && Agent.getInstance().isSecurityActive() && !isInternalThread()
return !ThreadLocalLockHelper.isLockHeldByCurrentThread() && isAgentInitComplete && Agent.getInstance().isSecurityActive() && !isInternalThread()
&& NewRelic.getAgent().getTransaction() != null
&& NewRelic.getAgent().getTransaction().getSecurityMetaData() instanceof SecurityMetaData;
// (Agent.getInstance().getSecurityMetaData() != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,21 @@ public class FileHelper {
public static final String FILE_OPERATION = "FILE_OPERATION";

public static boolean skipExistsEvent(String filename) {
String extension = getFileExtension(filename);
if (!(NewRelicSecurity.getAgent().getCurrentPolicy().getVulnerabilityScan().getEnabled() &&
NewRelicSecurity.getAgent().getCurrentPolicy().getVulnerabilityScan().getIastScan().getEnabled()) &&
extension != null && !extension.trim().isEmpty() &&
(SOURCE_EXENSIONS.contains(extension) || ALLOWED_EXTENSIONS.contains(extension))) {
return true;
boolean lockAcquired = ThreadLocalLockHelper.acquireLock();
try {
if(lockAcquired) {
String extension = getFileExtension(filename);
if (!(NewRelicSecurity.getAgent().getCurrentPolicy().getVulnerabilityScan().getEnabled() &&
NewRelicSecurity.getAgent().getCurrentPolicy().getVulnerabilityScan().getIastScan().getEnabled()) &&
extension != null && !extension.trim().isEmpty() &&
(SOURCE_EXENSIONS.contains(extension) || ALLOWED_EXTENSIONS.contains(extension))) {
return true;
}
}
} finally {
if(lockAcquired){
ThreadLocalLockHelper.releaseLock();
}
}

return false;
Expand All @@ -89,39 +98,58 @@ public static String getFileExtension(String fileName) {
}

public static FileIntegrityOperation createEntryOfFileIntegrity(String fileName, String className, String methodName) {
File file = Paths.get(fileName).toFile();
String extension = getFileExtension(file);
if (SOURCE_EXENSIONS.contains(extension) &&
!NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().containsKey(fileName)) {
long lastModified = file.exists()? file.lastModified() : -1;
String permissions = StringUtils.EMPTY;
try {
if(file.exists()) {
PosixFileAttributes fileAttributes = Files.readAttributes(Paths.get(file.getPath()), PosixFileAttributes.class);
Set<PosixFilePermission> permissionSet = fileAttributes.permissions();
permissions = permissionSet.toString();
boolean lockAcquired = ThreadLocalLockHelper.acquireLock();
try {
if(lockAcquired) {
File file = Paths.get(fileName).toFile();
String extension = getFileExtension(file);
if (SOURCE_EXENSIONS.contains(extension) &&
!NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().containsKey(fileName)) {
long lastModified = file.exists() ? file.lastModified() : -1;
String permissions = StringUtils.EMPTY;
try {
if (file.exists()) {
PosixFileAttributes fileAttributes = Files.readAttributes(Paths.get(file.getPath()), PosixFileAttributes.class);
Set<PosixFilePermission> permissionSet = fileAttributes.permissions();
permissions = permissionSet.toString();
}
} catch (IOException e) {
}
long fileLength = file.length();
FileIntegrityOperation fbean = new FileIntegrityOperation(file.exists(), fileName, className,
methodName, lastModified, permissions, fileLength);
NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().put(fileName,
fbean);
return fbean;
}
} catch (IOException e) {
}
long fileLength = file.length();
FileIntegrityOperation fbean = new FileIntegrityOperation(file.exists(), fileName, className,
methodName, lastModified, permissions, fileLength);
NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().put(fileName,
fbean);
return fbean;
} finally {
if(lockAcquired){
ThreadLocalLockHelper.releaseLock();
}
}
return null;

}

public static void checkEntryOfFileIntegrity(List<String> fileNames) {
for (String fileName : fileNames) {
File file = Paths.get(fileName).toFile();
if(NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().containsKey(fileName)){
FileIntegrityOperation fbean = NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().get(fileName);
if(fbean.isIntegrityBreached(file)){
NewRelicSecurity.getAgent().registerOperation(fbean);
boolean lockAcquired = ThreadLocalLockHelper.acquireLock();
try {
if(lockAcquired) {
for (String fileName : fileNames) {
File file = Paths.get(fileName).toFile();
if(NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().containsKey(fileName)){
FileIntegrityOperation fbean = NewRelicSecurity.getAgent().getSecurityMetaData().getFileLocalMap().get(fileName);
if(fbean.isIntegrityBreached(file)){
NewRelicSecurity.getAgent().registerOperation(fbean);
}
}
}
}
} finally {
if(lockAcquired) {
ThreadLocalLockHelper.releaseLock();
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.newrelic.api.agent.security.instrumentation.helpers;

import java.util.concurrent.locks.ReentrantLock;

public class ThreadLocalLockHelper {

private static final ThreadLocal<ReentrantLock> csecOperationLock = ThreadLocal.withInitial(ReentrantLock::new);

public static boolean isLockHeldByCurrentThread() {
ReentrantLock lock = csecOperationLock.get();
return lock.isHeldByCurrentThread();
}

public static boolean acquireLock() {
ReentrantLock lock = csecOperationLock.get();
synchronized (lock) {
if(!lock.isHeldByCurrentThread()){
lock.lock();
return true;
}
}
return false;
}

public static void releaseLock() {
ReentrantLock lock = csecOperationLock.get();
synchronized (lock) {
if(lock.isHeldByCurrentThread()){
lock.unlock();
}
}
}

}
Loading

0 comments on commit 0c51482

Please sign in to comment.