Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
34 changes: 33 additions & 1 deletion .github/actions/unit-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ inputs:
description: 'The JVM Version to use'
required: true
default: '8'
codecov-token:
description: 'The CODECOV TOKEN'

runs:
using: composite
Expand Down Expand Up @@ -42,4 +44,34 @@ runs:
if: steps.run_tests_3.outcome == 'failure'
run: |
echo "Running attempt 4"
./gradlew ${GRADLE_OPTIONS} --info test -Ptest${{ inputs.java-version }} --continue
./gradlew ${GRADLE_OPTIONS} --info test -Ptest${{ inputs.java-version }} --continue

- name: Upload coverage to Codecov
if: matrix.java-version == '8'
uses: codecov/codecov-action@v3
with:
token: ${{ inputs.codecov-token }}
files: '**/build/reports/jacoco/test/jacocoTestReport.xml'
fail_ci_if_error: false #default is false, but being explicit about what to expect.
verbose: true

- name: Capture Jacoco reports
if: success()
uses: actions/upload-artifact@v4
with:
name: jacoco-reports-java-${{ matrix.java-version }}
path: |
**/build/reports/jacoco/**

- name: Capture build reports
# If previous step fails, run this step regardless
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: unit-tests-results-java-${{ matrix.java-version }}
# The regex for the path below will capture unit test HTML reports generated by gradle for all
# related modules: (newrelic-security-agent, newrelic-security-api, etc).
# However, it's critical that the previous build step does a ./gradlew clean or the regex will capture test reports
# that were leftover in unrelated modules for the instrumentation tests.
path: |
**/build/reports/tests/*
2 changes: 2 additions & 0 deletions .github/codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
codecov:
max_report_age: off
3 changes: 2 additions & 1 deletion .github/workflows/X-Reusable-Build-Security-Agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,5 @@ jobs:
if: ${{ inputs.run-unit-test == 'true' }}
uses: ./.github/actions/unit-test
with:
java-version: ${{ matrix.java-version }}
java-version: ${{ matrix.java-version }}
codecov-token: ${{ secrets.CODECOV_TOKEN }}
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,22 @@ To publish csec agent on maven local use below command :

```./gradlew clean publishToMavenLocal```


## Code Quality

The agent utilizes [Jacoco](https://www.jacoco.org/jacoco/) to measure and improve code quality. We also use [Codecov](https://docs.codecov.com/docs/quick-start) to report
code coverage. Jacoco tool is configured through the gradle build system. Codecov is configured and uploaded through a GitHub Actions workflow.

#### Jacoco

The `jacocoTestReport` task depends on the unit `test` task. It uses data generated by the test task to create the coverage reports. `jacocoTestReport` task creates an html and xml file of the output. We store these files locally in the respective module's `build/reports/jacoco` folder. The html file can be opened normally in a browser.
Some subprojects have not been configured to run because the code is either too trivial to test or it is code that is only compile time dependency and not necessary to test due to the nature
of how the agent instruments customer code.

#### Codecov

We use a codecov github action to upload the jacoco coverage reports to [codecov.io](https://app.codecov.io/gh/newrelic/csec-java-agent). Additionally, codecov will comment on pull requests (PRs) with a quick overview of how PRs (and relevant commits) will affect the code coverage as compared to the `main` branch.

## **Contributing Feedback**

Any feedback provided to New Relic about the New Relic csec-java-agent, including feedback provided as source code, comments, or other copyrightable or patentable material, is provided to New Relic under the terms of the New Relic Software License version, 1.0. If you do not provide attribution information or a copy of the license with your feedback, you waive the performance of those requirements of the New Relic Software License with respect to New Relic. The license grant regarding any feedback is irrevocable.
Expand Down
73 changes: 73 additions & 0 deletions gradle/script/jacoco.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
apply plugin: 'java'
apply plugin: 'jacoco'

jacoco {
toolVersion = "0.8.11"
}

test {
jacoco {
enabled = true
destinationFile = layout.buildDirectory.file('jacoco/test.exec').get().asFile
classDumpDir = layout.buildDirectory.dir('jacoco/classpathdumps').get().asFile
destinationFile = layout.buildDirectory.file("jacoco/${name}.exec").get().asFile

}
finalizedBy jacocoTestReport // report is always generated after tests run
}

jacocoTestReport {

dependsOn test
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it,
exclude: ['**/*NoOp*.class',
'**/SecurityAgent.class',
'**/*Exception.class',
'**/*Error.class',
'**/*Constant*.class',
'**/*Pool*.class',
'com/newrelic/agent/security/intcodeagent/utils/TTLMap.class',
'com/newrelic/agent/security/intcodeagent/utils/ResourceUtils.class',
'com/newrelic/agent/security/intcodeagent/models/**',
'com/newrelic/agent/security/util/*.class',
'com/newrelic/agent/security/intcodeagent/properties/*.class',
'com/newrelic/agent/security/intcodeagent/*logging/*',
'com/newrelic/agent/security/intcodeagent/serializers/*',
'com/newrelic/agent/security/intcodeagent/schedulers/*',
'com/newrelic/agent/security/intcodeagent/controlcommand/*',
'com/newrelic/agent/security/intcodeagent/websocket/*',
'com/newrelic/agent/security/intcodeagent/executor/*',
'com/newrelic/agent/security/intcodeagent/constants/*.class',
'com/newrelic/agent/security/instrumentator/utils/AgentUtils.class',
'com/newrelic/agent/security/instrumentator/utils/ApplicationInfoUtils.class',
'com/newrelic/agent/security/instrumentator/utils/EnvLogUtils*.class',
'com/newrelic/agent/security/instrumentator/utils/HashGenerator.class',
'com/newrelic/agent/security/instrumentator/utils/InstrumentationUtils.class',
'com/newrelic/agent/security/instrumentator/utils/INRSettingsKey.class',
'com/newrelic/agent/security/instrumentator/utils/NameFileFilter.class',
'com/newrelic/agent/security/instrumentator/os/*',
'com/newrelic/agent/security/instrumentator/httpclient/*.class',
'com/newrelic/agent/security/instrumentator/*Hooks.class',
'com/newrelic/agent/security/intcodeagent/websocket/WSUtils.class',
'com/newrelic/agent/security/intcodeagent/websocket/CustomTrustStoreManagerUtils.class',
'com/newrelic/agent/security/intcodeagent/apache/httpclient/*',
'com/newrelic/api/agent/security/*.class',
'com/newrelic/agent/security/*.class',
'com/newrelic/api/agent/security/schema/*',
'com/newrelic/api/agent/security/NewRelicSecurity.class',
'com/newrelic/api/agent/security/instrumentation/helpers/VertxApiEndpointUtils*',
'com/newrelic/api/agent/security/utils/UserDataTranslationHelper.class',
'com/newrelic/api/agent/security/utils/logging/LogLevel.class',
'com/newrelic/api/agent/security/instrumentation/helpers/GrpcClientRequestReplayHelper*',
'com/newrelic/api/agent/security/instrumentation/helpers/AppServerInfoHelper.class',
])
}))
}
reports {
xml.required = true
html.outputLocation = layout.buildDirectory.dir('reports/jacoco')
}

}
16 changes: 16 additions & 0 deletions newrelic-security-agent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ plugins {
id 'org.cyclonedx.bom' version '1.7.3'
}

apply from: '../gradle/script/jacoco.gradle'

java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
Expand Down Expand Up @@ -79,6 +81,9 @@ dependencies {
shadowIntoJar 'com.google.code.gson:gson:2.10.1'
shadowIntoJar 'org.apache.httpcomponents:httpclient:4.5.14'
implementation "com.newrelic.agent.java:newrelic-api:${nrAPIVersion}"
testImplementation('junit:junit:4.13.2')
testImplementation('org.mockito:mockito-inline:4.11.0')
testImplementation('software.amazon.awssdk:dynamodb:2.16.46')
}

/**
Expand Down Expand Up @@ -253,6 +258,17 @@ jar {
enabled = false
}

test {
forkEvery = 1
maxParallelForks = Runtime.runtime.availableProcessors()
useJUnit {

}

minHeapSize = "256m"
maxHeapSize = "768m"
}

artifacts {
finalArtifact newrelicVersionedAgentJar
archives newrelicVersionedAgentJar
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.newrelic.agent.security;

import com.newrelic.agent.security.intcodeagent.exceptions.RestrictionModeException;
import com.newrelic.agent.security.util.IUtilConstants;
import com.newrelic.api.agent.NewRelic;
import org.apache.commons.io.FileUtils;
import org.junit.Assert;

import org.junit.Test;
import org.mockito.Answers;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;

public class AgentConfigTest {

@Test
public void applyRequiredGroup() throws RestrictionModeException {
AgentConfig.getInstance().instantiate();
Assert.assertEquals("IAST", AgentConfig.getInstance().getGroupName());
}
@Test

public void applyRequiredGroup1() throws RestrictionModeException{
// try (MockedStatic<NewRelic> nrMock = Mockito.mockStatic(NewRelic.class, Answers.RETURNS_DEEP_STUBS)){
// nrMock.when(NewRelic.getAgent().getConfig().getValue(IUtilConstants.SECURITY_MODE)).thenReturn("RASP");
// nrMock.when(NewRelic.getAgent().getConfig().getValue(eq(IUtilConstants.NR_SECURITY_ENABLED), any())).thenReturn(false);
// AgentConfig.getInstance().instantiate();
// Assert.assertEquals("RASP", AgentConfig.getInstance().getGroupName());
// nrMock.reset();
// nrMock.clearInvocations();
// }
}
@Test
public void isNRSecurityEnabled() throws RestrictionModeException{
AgentConfig.getInstance().instantiate();
Assert.assertFalse(AgentConfig.getInstance().isNRSecurityEnabled());
}
@Test
public void setK2HomePath() {
Assert.assertFalse(AgentConfig.getInstance().setSecurityHomePath());
}
@Test
public void setK2HomePath1() throws IOException {
String AGENT_HOME = "/tmp/file_"+ UUID.randomUUID();
try (MockedStatic<NewRelic> nrMock = Mockito.mockStatic(NewRelic.class, Answers.RETURNS_DEEP_STUBS)){
nrMock.when(NewRelic.getAgent().getConfig().getValue("agent_home")).thenReturn(AGENT_HOME);

Assert.assertTrue(AgentConfig.getInstance().setSecurityHomePath());
Assert.assertEquals(AGENT_HOME+"/nr-security-home", AgentConfig.getInstance().getSecurityHome());
nrMock.reset();
nrMock.clearInvocations();
} finally {
FileUtils.forceDeleteOnExit(new File(AGENT_HOME));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.newrelic.agent.security;

import com.newrelic.agent.security.instrumentator.utils.INRSettingsKey;
import com.newrelic.agent.security.intcodeagent.models.collectorconfig.CollectorConfig;
import com.newrelic.agent.security.intcodeagent.models.javaagent.ApplicationInfoBean;
import com.newrelic.agent.security.intcodeagent.models.javaagent.EventStats;
import com.newrelic.agent.security.intcodeagent.models.javaagent.Identifier;
import com.newrelic.agent.security.intcodeagent.models.javaagent.IdentifierEnvs;
import com.newrelic.agent.security.intcodeagent.models.javaagent.JAHealthCheck;
import com.newrelic.agent.security.util.IUtilConstants;
import com.newrelic.api.agent.NewRelic;
import org.junit.Assert;
import org.junit.BeforeClass;

import org.junit.Test;
import org.mockito.Mockito;

import java.util.Collections;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;

public class AgentInfoTest {
@BeforeClass
public static void setUp() {
Identifier identifier = new Identifier();
identifier.setKind(IdentifierEnvs.HOST);
AgentInfo.getInstance().setIdentifier(identifier);
}

@Test

public void generateAppInfo() {
// ApplicationInfoBean appInfoBean = AgentInfo.getInstance().generateAppInfo(Mockito.mock(CollectorConfig.class));
// Assert.assertEquals(AgentInfo.getInstance().getApplicationUUID(), appInfoBean.getApplicationUUID());
// Assert.assertEquals("STATIC", appInfoBean.getAgentAttachmentType());
// Assert.assertEquals(AgentInfo.getInstance().getVMPID(), appInfoBean.getPid());
// Assert.assertEquals("JAVA", appInfoBean.getCollectorType());
// Assert.assertEquals("Java", appInfoBean.getLanguage());
}

@Test
public void initializeHC() {
// AgentInfo.getInstance().generateAppInfo(Mockito.mock(CollectorConfig.class));
// AgentInfo.getInstance().initialiseHC();
// JAHealthCheck jaHealthCheck = AgentInfo.getInstance().getJaHealthCheck();
Properties properties = new Properties();
//default application_logging is true
properties.put("newrelic.config.application_logging.enabled", "false");
System.setProperty("newrelic.config.security.scan_controllers.scan_instance_count", "1");
System.out.println("config found ---" + NewRelic.getAgent().getConfig().getValue(IUtilConstants.IAST_SCAN_INSTANCE_COUNT));
// assertion(jaHealthCheck, new AtomicInteger(0), new EventStats());
}

@Test
public void isAgentActive() {
Assert.assertFalse(AgentInfo.getInstance().isAgentActive());
}
// @Test
// public void initializeHC1() {
// AgentInfo.getInstance().generateAppInfo(Mockito.mock(CollectorConfig.class));
// AgentInfo.getInstance().initialiseHC();
// JAHealthCheck jaHealthCheck = AgentInfo.getInstance().getJaHealthCheck();
// jaHealthCheck.incrementInvokedHookCount();
// jaHealthCheck.incrementDropCount();
// jaHealthCheck.incrementProcessedCount();
// jaHealthCheck.incrementEventSentCount();
// jaHealthCheck.incrementHttpRequestCount();
// jaHealthCheck.incrementExitEventSentCount();
// jaHealthCheck.incrementEventRejectionCount();
// jaHealthCheck.incrementEventProcessingErrorCount();
// jaHealthCheck.incrementEventSendRejectionCount();
// jaHealthCheck.incrementEventSendErrorCount();
// assertion(jaHealthCheck, new AtomicInteger(1), new EventStats());
//
// // resetting the JAHealthCheck
// jaHealthCheck.reset();
// assertion(jaHealthCheck, new AtomicInteger(0), new EventStats());
// }

// private void assertion(JAHealthCheck jaHealthCheck, AtomicInteger atomicInteger, EventStats expectedEventStats) {
// Assert.assertEquals(AgentInfo.getInstance().getApplicationUUID(), jaHealthCheck.getApplicationUUID());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getInvokedHookCount());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getEventDropCount().intValue());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getEventProcessed().intValue());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getEventSentCount().get());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getHttpRequestCount().get());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getExitEventSentCount().get());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getEventRejectionCount().get());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getEventProcessingErrorCount().get());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getEventSendRejectionCount().get());
// Assert.assertEquals(atomicInteger.get(), jaHealthCheck.getEventSendErrorCount().get());
//
// assertEventStats(expectedEventStats, jaHealthCheck.getExitEventStats());
// assertEventStats(expectedEventStats, jaHealthCheck.getIastEventStats());
// assertEventStats(expectedEventStats, jaHealthCheck.getRaspEventStats());
// Assert.assertEquals("HOST", jaHealthCheck.getKind().name());
// }

// private void assertEventStats(EventStats expectedEventStats, EventStats actualEventStats) {
// Assert.assertSame(expectedEventStats.getErrorCount().get(), actualEventStats.getErrorCount().get());
// Assert.assertSame(expectedEventStats.getProcessed().get(), actualEventStats.getProcessed().get());
// Assert.assertSame(expectedEventStats.getSent().get(), actualEventStats.getSent().get());
// Assert.assertSame(expectedEventStats.getRejected().get(), actualEventStats.getRejected().get());
// }
}
Loading
Loading