Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NR-259467 : Fix issue of nested event generation from CSEC's agent itself #230

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading