diff --git a/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java b/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java index 3b298e080..fb508054c 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java @@ -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; @@ -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; @@ -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(); } } } @@ -439,21 +445,30 @@ private static void setAPIId(AbstractOperation operation, List 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(); } } } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/NewRelicSecurity.java b/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/NewRelicSecurity.java index c99aed0a2..cfe7f0b2e 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/NewRelicSecurity.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/NewRelicSecurity.java @@ -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; @@ -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); diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/FileHelper.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/FileHelper.java index a6d21bbf4..2349d6a34 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/FileHelper.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/FileHelper.java @@ -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; @@ -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 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 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 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(); + } } } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ThreadLocalLockHelper.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ThreadLocalLockHelper.java new file mode 100644 index 000000000..5e758f1a0 --- /dev/null +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ThreadLocalLockHelper.java @@ -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 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(); + } + } + } + +} diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/FileIntegrityOperation.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/FileIntegrityOperation.java index b73e0c740..d334a4158 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/FileIntegrityOperation.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/operation/FileIntegrityOperation.java @@ -1,5 +1,6 @@ package com.newrelic.api.agent.security.schema.operation; +import com.newrelic.api.agent.security.instrumentation.helpers.ThreadLocalLockHelper; import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.StringUtils; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; @@ -143,18 +144,26 @@ public void setPermissionString(String permissionString) { } public boolean isIntegrityBreached(File file){ - Boolean exists = file.exists(); - long lastModified = exists? file.lastModified() : -1; - String permissions = StringUtils.EMPTY; - long length = file.length(); + boolean lockAcquired = ThreadLocalLockHelper.acquireLock(); try { - if(exists) { - PosixFileAttributes fileAttributes = Files.readAttributes(Paths.get(file.getPath()), PosixFileAttributes.class); - Set permissionSet = fileAttributes.permissions(); - permissions = permissionSet.toString(); + if(lockAcquired) { + Boolean exists = file.exists(); + long lastModified = exists ? file.lastModified() : -1; + String permissions = StringUtils.EMPTY; + long length = file.length(); + if (exists) { + PosixFileAttributes fileAttributes = Files.readAttributes(Paths.get(file.getPath()), PosixFileAttributes.class); + Set permissionSet = fileAttributes.permissions(); + permissions = permissionSet.toString(); + } + return (exists != this.exists || lastModified != this.lastModified || !StringUtils.equals(permissions, this.permissionString) || length != this.length); } } catch (IOException e) { + } finally { + if(lockAcquired) { + ThreadLocalLockHelper.releaseLock(); + } } - return (exists != this.exists || lastModified != this.lastModified || !StringUtils.equals(permissions, this.permissionString) || length != this.length); + return false; } }