diff --git a/.github/actions/setup-environment-inst-verifier/action.yml b/.github/actions/setup-environment-inst-verifier/action.yml index 0e307833c..3ec3aadd7 100644 --- a/.github/actions/setup-environment-inst-verifier/action.yml +++ b/.github/actions/setup-environment-inst-verifier/action.yml @@ -17,6 +17,7 @@ runs: with: distribution: 'temurin' java-version: | + 21 20 17 11 @@ -29,7 +30,8 @@ runs: sed -i -e "s|jdk8=8|jdk8=${JAVA_HOME_8_X64}| s|jdk11=11|jdk11=${JAVA_HOME_11_X64}| s|jdk17=17|jdk17=${JAVA_HOME_17_X64}| - s|jdk20=20|jdk20=${JAVA_HOME_20_X64}|" gradle.properties.gha + s|jdk20=20|jdk20=${JAVA_HOME_20_X64}| + s|jdk21=21|jdk20=${JAVA_HOME_21_X64}|" gradle.properties.gha cat gradle.properties.gha >> gradle.properties - name: Setup Gradle diff --git a/.github/actions/setup-environment/action.yml b/.github/actions/setup-environment/action.yml index e8734b4ae..c9971074c 100644 --- a/.github/actions/setup-environment/action.yml +++ b/.github/actions/setup-environment/action.yml @@ -36,6 +36,7 @@ runs: with: distribution: 'temurin' java-version: | + 21 20 17 11 @@ -48,7 +49,8 @@ runs: sed -i -e "s|jdk8=8|jdk8=${JAVA_HOME_8_X64}| s|jdk11=11|jdk11=${JAVA_HOME_11_X64}| s|jdk17=17|jdk17=${JAVA_HOME_17_X64}| - s|jdk20=20|jdk20=${JAVA_HOME_20_X64}|" gradle.properties.gha + s|jdk20=20|jdk20=${JAVA_HOME_20_X64}| + s|jdk21=21|jdk21=${JAVA_HOME_21_X64}|" gradle.properties.gha cat gradle.properties.gha >> gradle.properties - name: Setup Gradle diff --git a/.github/actions/unit-test/action.yml b/.github/actions/unit-test/action.yml index 42e16e7e5..06ab3371d 100644 --- a/.github/actions/unit-test/action.yml +++ b/.github/actions/unit-test/action.yml @@ -1,10 +1,45 @@ name: Instrumentation unit tests description: Run all the instrumentation unit test. +inputs: + java-version: + description: 'The JVM Version to use' + required: true + default: '8' runs: using: composite steps: - - name: Run instrumentation unit tests + - name: Run instrumentation unit tests on Java ${{ inputs.java-version }} attempt 1 + id: run_tests_1 shell: bash - run: ./gradlew ${GRADLE_OPTIONS} --info test \ No newline at end of file + continue-on-error: true + run: | + echo "Running attempt 1" + ./gradlew ${GRADLE_OPTIONS} --info test -Ptest${{ inputs.java-version }} --continue + + - name: Run instrumentation unit tests on Java ${{ inputs.java-version }} attempt 2 + id: run_tests_2 + shell: bash + continue-on-error: true + if: steps.run_tests_1.outcome == 'failure' + run: | + echo "Running attempt 2" + ./gradlew ${GRADLE_OPTIONS} --info test -Ptest${{ inputs.java-version }} --continue + + - name: Run instrumentation unit tests on Java ${{ inputs.java-version }} attempt 3 + id: run_tests_3 + shell: bash + continue-on-error: true + if: steps.run_tests_2.outcome == 'failure' + run: | + echo "Running attempt 3" + ./gradlew ${GRADLE_OPTIONS} --info test -Ptest${{ inputs.java-version }} --continue + + - name: Run instrumentation unit tests on Java ${{ inputs.java-version }} attempt 4 + id: run_tests_4 + shell: bash + if: steps.run_tests_3.outcome == 'failure' + run: | + echo "Running attempt 4" + ./gradlew ${GRADLE_OPTIONS} --info test -Ptest${{ inputs.java-version }} --continue \ No newline at end of file diff --git a/.github/workflows/X-Reusable-Build-Security-Agent.yml b/.github/workflows/X-Reusable-Build-Security-Agent.yml index a084d358d..0d3779123 100644 --- a/.github/workflows/X-Reusable-Build-Security-Agent.yml +++ b/.github/workflows/X-Reusable-Build-Security-Agent.yml @@ -59,6 +59,10 @@ jobs: # this list is paginated and will be used in the verify-module job. build-agent: runs-on: ubuntu-20.04 + strategy: + matrix: + java-version: [ 8, 11, 17 ] + steps: - name: Checkout CSEC Repo uses: actions/checkout@v3 @@ -79,4 +83,6 @@ jobs: - name: Run CSEC unit tests if: ${{ inputs.run-unit-test == 'true' }} - uses: ./.github/actions/unit-test \ No newline at end of file + uses: ./.github/actions/unit-test + with: + java-version: ${{ matrix.java-version }} \ No newline at end of file diff --git a/Changelog.md b/Changelog.md index 23ab4cec9..367a24675 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,18 @@ Noteworthy changes to the agent are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.2] - 2024-3-11 +### Changes +- [NR-174177](https://new-relic.atlassian.net/browse/NR-174177) Ning Async HTTP client Support: The security agent now also supports com.ning:async-http-client 1.0.0 and above [PR-152](https://github.com/newrelic/csec-java-agent/pull/152), [PR-118](https://github.com/newrelic/csec-java-agent/pull/118), [PR-116](https://github.com/newrelic/csec-java-agent/pull/116) +- [NR-181375](https://new-relic.atlassian.net/browse/NR-181375) Jersey Support: The security agent now also supports Jersey 2.0 and above [PR-150](https://github.com/newrelic/csec-java-agent/pull/150), [PR-149](https://github.com/newrelic/csec-java-agent/pull/149) +- [NR-187224](https://new-relic.atlassian.net/browse/NR-187224) Mule Support: The security agent now also supports Mule server version 3.6 to 3.9.x [PR-144](https://github.com/newrelic/csec-java-agent/pull/144), [PR-143](https://github.com/newrelic/csec-java-agent/pull/143) +- Jetty v12 Support: The security agent now also support Jetty version 12 and above [PR-106](https://github.com/newrelic/csec-java-agent/pull/106) +- [NR-174175](https://new-relic.atlassian.net/browse/NR-174175) Lettuce Support: The security agent now also supports Lettuce 4.4.0.Final and above [PR-125](https://github.com/newrelic/csec-java-agent/pull/125) +- [NR-234869](https://new-relic.atlassian.net/browse/NR-234869) GHA Update Unit Test Action for Testing Unit tests with different java-version with re-tries on failure [PR-204](https://github.com/newrelic/csec-java-agent/pull/204) + +### Fixes +- [NR-223811](https://new-relic.atlassian.net/browse/NR-223811) Extract Server Configuration to resolve IAST localhost connection with application for wildfly server [PR-192](https://github.com/newrelic/csec-java-agent/pull/192) +- [NR-234903](https://new-relic.atlassian.net/browse/NR-234903) Trustboundary events now will have list of string as parameter schema ## [1.1.1] - 2024-2-16 ### Changes diff --git a/README.md b/README.md index 3a67dae46..51e0ea935 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ The agent automatically instruments the following frameworks. - Jetty 9.3.0.M1 to latest - Mule ESB 3.6 to 3.9.x - gRPC 1.4.0 to latest** +- Jersey 2.0 to latest ** IAST for **gRPC** requires the dependency [protobuf-java-util](https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util) for IAST request replay. @@ -64,6 +65,7 @@ The agent automatically instruments the following HTTP clients and messaging ser - Saxpath 1.0 - Xalan XPATH 2.1.0 to latest - Async Http Client from 2.0 to latest +- Ning Async HTTP Client 1.0.0 to latest ### Datastores diff --git a/gradle.properties b/gradle.properties index 91571c63b..765ec9338 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # The agent version. -agentVersion=1.1.1 +agentVersion=1.1.2 jsonVersion=1.1.1 # Updated exposed NR APM API version. nrAPIVersion=8.4.0 diff --git a/instrumentation-security-test/src/main/java/com/newrelic/agent/security/introspec/internal/SecurityIntrospectorImpl.java b/instrumentation-security-test/src/main/java/com/newrelic/agent/security/introspec/internal/SecurityIntrospectorImpl.java index 91aa1353d..57565f279 100644 --- a/instrumentation-security-test/src/main/java/com/newrelic/agent/security/introspec/internal/SecurityIntrospectorImpl.java +++ b/instrumentation-security-test/src/main/java/com/newrelic/agent/security/introspec/internal/SecurityIntrospectorImpl.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.net.ServerSocket; import java.sql.Statement; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -134,22 +135,9 @@ public void setRequestInputStreamHash(int hashCode) { @Override public void clear() { - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(JDBCVendor.META_CONST_JDBC_VENDOR, null); - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(R2DBCVendor.META_CONST_R2DBC_VENDOR, null); - NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(Agent.OPERATIONS, List.class).clear(); - NewRelicSecurity.getAgent().getSecurityMetaData().getCustomAttribute(Agent.EXIT_OPERATIONS, List.class).clear(); - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(REQUEST_READER_HASH, null); - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(REQUEST_INPUTSTREAM_HASH, null); - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(RESPONSE_WRITER_HASH, null); - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(RESPONSE_OUTPUTSTREAM_HASH, null); - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(UserDataTranslationHelper.getAttributeName(Log4JStrSubstitutor.class.getName()), null); - - // used internally by some methods before saving hash code hence cleanup required - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(REQUEST_STREAM_OR_READER_CALLED, null); - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(RESPONSE_STREAM_OR_WRITER_CALLED, null); - - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(GrpcHelper.NR_SEC_GRPC_REQUEST_DATA, null); - NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(GrpcHelper.NR_SEC_GRPC_RESPONSE_DATA, null); + NewRelicSecurity.getAgent().getSecurityMetaData().clearCustomAttr(); + NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(Agent.OPERATIONS, new ArrayList<>()); + NewRelicSecurity.getAgent().getSecurityMetaData().addCustomAttribute(Agent.EXIT_OPERATIONS, new ArrayList<>()); SecurityMetaData meta = NewRelicSecurity.getAgent().getSecurityMetaData(); meta.setRequest(new HttpRequest()); diff --git a/instrumentation-security/apache-log4j-3.0.0/build.gradle b/instrumentation-security/apache-log4j-3.0.0/build.gradle index bc2e57321..816ac607a 100644 --- a/instrumentation-security/apache-log4j-3.0.0/build.gradle +++ b/instrumentation-security/apache-log4j-3.0.0/build.gradle @@ -23,3 +23,10 @@ java { languageVersion.set(JavaLanguageVersion.of(11)) } } + +test { + // These instrumentation tests only run on Java 11+ regardless of the -PtestN gradle property that is set. + onlyIf { + !project.hasProperty('test8') + } +} diff --git a/instrumentation-security/dynamodb-2.1.2/src/test/java/com/nr/agent/security/instrumentation/dynamodb_212/DynamodbTest.java b/instrumentation-security/dynamodb-2.1.2/src/test/java/com/nr/agent/security/instrumentation/dynamodb_212/DynamodbTest.java index 67f880d0e..4ee0c310c 100644 --- a/instrumentation-security/dynamodb-2.1.2/src/test/java/com/nr/agent/security/instrumentation/dynamodb_212/DynamodbTest.java +++ b/instrumentation-security/dynamodb-2.1.2/src/test/java/com/nr/agent/security/instrumentation/dynamodb_212/DynamodbTest.java @@ -193,7 +193,7 @@ public void testTransactGetItems() { else if (i==1) { Assert.assertEquals("Invalid payload value.", "Red",query.get("artist").s()); } - Assert.assertEquals("Invalid payload value.", "artist",request.getQuery().getProjectionExpression()); + Assert.assertEquals("Invalid payload value.", "artist,Genre",request.getQuery().getProjectionExpression()); Assert.assertEquals("Invalid query-type.", "read", request.getQueryType()); i++; } @@ -855,8 +855,8 @@ public void transactGetItems() { key2.put("artist", AttributeValue.builder().s("Red").build()); TransactGetItemsRequest queryRequest = TransactGetItemsRequest.builder().transactItems( - TransactGetItem.builder().get(Get.builder().tableName(DynamoUtil.TABLE).key(key).projectionExpression("artist").build()).build(), - TransactGetItem.builder().get(Get.builder().tableName(DynamoUtil.TABLE).key(key2).projectionExpression("artist").build()).build()).build(); + TransactGetItem.builder().get(Get.builder().tableName(DynamoUtil.TABLE).key(key).projectionExpression("artist,Genre").build()).build(), + TransactGetItem.builder().get(Get.builder().tableName(DynamoUtil.TABLE).key(key2).projectionExpression("artist,Genre").build()).build()).build(); client.transactGetItems(queryRequest); } diff --git a/instrumentation-security/httpclient-jdk11/build.gradle b/instrumentation-security/httpclient-jdk11/build.gradle index 2b8ffcafa..b04e2464a 100644 --- a/instrumentation-security/httpclient-jdk11/build.gradle +++ b/instrumentation-security/httpclient-jdk11/build.gradle @@ -21,11 +21,13 @@ java { } test { + // These instrumentation tests only run on Java 11+ regardless of the -PtestN gradle property that is set. onlyIf { - inputs.getProperties()["test.jdk"]!="jdk8" + !project.hasProperty('test8') } } + compileJava { options.fork = true options.bootstrapClasspath = null diff --git a/instrumentation-security/java-io-inputstream-jdk8/src/test/java/com/nr/agent/security/instrumentation/javaio/InputStreamJdk8Test.java b/instrumentation-security/java-io-inputstream-jdk8/src/test/java/com/nr/agent/security/instrumentation/javaio/InputStreamJdk8Test.java index 3dacd2124..eaf99a847 100644 --- a/instrumentation-security/java-io-inputstream-jdk8/src/test/java/com/nr/agent/security/instrumentation/javaio/InputStreamJdk8Test.java +++ b/instrumentation-security/java-io-inputstream-jdk8/src/test/java/com/nr/agent/security/instrumentation/javaio/InputStreamJdk8Test.java @@ -5,6 +5,7 @@ import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; import com.newrelic.agent.security.introspec.SecurityIntrospector; import com.newrelic.api.agent.security.schema.SecurityMetaData; +import com.newrelic.security.test.marker.Java17IncompatibleTest; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; @@ -12,6 +13,7 @@ import org.junit.FixMethodOrder; import org.junit.Ignore; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; @@ -31,6 +33,7 @@ @RunWith(SecurityInstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = {"java.io","com.newrelic.agent.security.instrumentation.javaio"}) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({Java17IncompatibleTest.class}) public class InputStreamJdk8Test { private static String FILE; private static String FILE_TEMP; diff --git a/instrumentation-security/java-io-inputstream-jdk9/build.gradle b/instrumentation-security/java-io-inputstream-jdk9/build.gradle index c3772d271..738cb3c39 100644 --- a/instrumentation-security/java-io-inputstream-jdk9/build.gradle +++ b/instrumentation-security/java-io-inputstream-jdk9/build.gradle @@ -21,8 +21,8 @@ java { } test { -// These instrumentation tests only run on Java 9+ regardless of the -PtestN gradle property that is set. + // These instrumentation tests only run on Java 11+ regardless of the -PtestN gradle property that is set. onlyIf { - java.toolchain.getLanguageVersion().get().asInt() > 10 + !project.hasProperty('test8') } -} \ No newline at end of file +} diff --git a/instrumentation-security/java-io-inputstream-jdk9/src/test/java/com/nr/instrumentation/security/inputstream/jdk9/InputStreamJdk9Test.java b/instrumentation-security/java-io-inputstream-jdk9/src/test/java/com/nr/instrumentation/security/inputstream/jdk9/InputStreamJdk9Test.java index 8b45fda3e..07f481cf4 100644 --- a/instrumentation-security/java-io-inputstream-jdk9/src/test/java/com/nr/instrumentation/security/inputstream/jdk9/InputStreamJdk9Test.java +++ b/instrumentation-security/java-io-inputstream-jdk9/src/test/java/com/nr/instrumentation/security/inputstream/jdk9/InputStreamJdk9Test.java @@ -5,6 +5,7 @@ import com.newrelic.agent.security.introspec.SecurityIntrospector; import com.newrelic.api.agent.security.schema.SecurityMetaData; import com.newrelic.security.test.marker.Java11IncompatibleTest; +import com.newrelic.security.test.marker.Java17IncompatibleTest; import com.newrelic.security.test.marker.Java8IncompatibleTest; import org.junit.AfterClass; import org.junit.Assert; @@ -30,7 +31,7 @@ import java.util.List; import java.util.UUID; -@Category({ Java8IncompatibleTest.class, Java11IncompatibleTest.class }) +@Category({ Java8IncompatibleTest.class, Java11IncompatibleTest.class, Java17IncompatibleTest.class }) @RunWith(SecurityInstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = {"com.newrelic.agent.security.instrumentation.javaio.io","com.newrelic.agent.security.instrumentation.javaio"}) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/XPathTest.java b/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/XPathTest.java index a897dfdee..37701dd21 100644 --- a/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/XPathTest.java +++ b/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/XPathTest.java @@ -7,10 +7,12 @@ import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.XPathOperation; import com.newrelic.agent.security.instrumentation.xpath.javax.XPATHUtils; +import com.newrelic.security.test.marker.Java17IncompatibleTest; import org.junit.Assert; import org.junit.FixMethodOrder; import org.junit.Ignore; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.w3c.dom.Document; @@ -25,6 +27,7 @@ @FixMethodOrder(MethodSorters.NAME_ASCENDING) @RunWith(SecurityInstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "javax.xml.xpath", "com.sun.org.apache.xpath.internal" }) +@Category({ Java17IncompatibleTest.class}) public class XPathTest { private final String XML_DOC = "src/test/resources/Customer.xml"; diff --git a/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/internal/XPathInternalTest.java b/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/internal/XPathInternalTest.java index b0510aa95..b6cb2dd49 100644 --- a/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/internal/XPathInternalTest.java +++ b/instrumentation-security/javax-xpath/src/test/java/com/nr/agent/security/instrumentation/xpath/javax/internal/XPathInternalTest.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.XPathOperation; +import com.newrelic.security.test.marker.Java17IncompatibleTest; import com.sun.org.apache.xml.internal.utils.DefaultErrorHandler; import com.sun.org.apache.xml.internal.utils.PrefixResolver; import com.sun.org.apache.xml.internal.utils.PrefixResolverDefault; @@ -16,6 +17,7 @@ import org.junit.Assert; import org.junit.FixMethodOrder; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.w3c.dom.Document; @@ -32,6 +34,7 @@ @RunWith(SecurityInstrumentationTestRunner.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @InstrumentationTestConfig(includePrefixes = { "javax.xml.xpath", "com.sun.org.apache.xpath.internal" }) +@Category({ Java17IncompatibleTest.class}) public class XPathInternalTest { private final String XML_DOC = "src/test/resources/Customer.xml"; private final String EXPRESSION = "/Customers/Customer"; diff --git a/instrumentation-security/jersey-2.16/build.gradle b/instrumentation-security/jersey-2.16/build.gradle index d10b64445..56b302d44 100644 --- a/instrumentation-security/jersey-2.16/build.gradle +++ b/instrumentation-security/jersey-2.16/build.gradle @@ -4,6 +4,9 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("org.glassfish.jersey.core:jersey-server:2.16") + testImplementation("org.glassfish.jersey.containers:jersey-container-grizzly2-http:2.28") + testImplementation("org.glassfish.jersey.containers:jersey-container-servlet:2.28") + testImplementation('org.glassfish.jersey.inject:jersey-hk2:2.28') } jar { diff --git a/instrumentation-security/jersey-2.16/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java b/instrumentation-security/jersey-2.16/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java index 0a3e317b1..d3ccc680e 100644 --- a/instrumentation-security/jersey-2.16/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java +++ b/instrumentation-security/jersey-2.16/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java @@ -18,6 +18,9 @@ import com.newrelic.api.agent.security.schema.HttpResponse; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.security.test.marker.Java11IncompatibleTest; +import com.newrelic.security.test.marker.Java17IncompatibleTest; +import com.newrelic.security.test.marker.Java9IncompatibleTest; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.grizzly.http.util.Header; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; @@ -26,6 +29,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import javax.ws.rs.client.Client; @@ -53,6 +57,7 @@ @RunWith(SecurityInstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = {"com.newrelic.agent.security.instrumentation.jersey2", "org.glassfish.jersey"}) +@Category({ Java9IncompatibleTest.class, Java11IncompatibleTest.class, Java17IncompatibleTest.class }) public class JerseyTests { private static HttpServer server; diff --git a/instrumentation-security/jersey-2/build.gradle b/instrumentation-security/jersey-2/build.gradle index 6b8971009..7c18e6d6b 100644 --- a/instrumentation-security/jersey-2/build.gradle +++ b/instrumentation-security/jersey-2/build.gradle @@ -4,6 +4,9 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("org.glassfish.jersey.core:jersey-server:2.0") + testImplementation("org.glassfish.jersey.containers:jersey-container-grizzly2-http:2.0") + testImplementation("org.glassfish.jersey.containers:jersey-container-servlet:2.0") + testImplementation('org.glassfish.hk2:hk2-api:2.1.88') } jar { diff --git a/instrumentation-security/jersey-2/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java b/instrumentation-security/jersey-2/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java index 966b910dd..f418c45e8 100644 --- a/instrumentation-security/jersey-2/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java +++ b/instrumentation-security/jersey-2/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java @@ -18,6 +18,9 @@ import com.newrelic.api.agent.security.schema.HttpResponse; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.security.test.marker.Java11IncompatibleTest; +import com.newrelic.security.test.marker.Java17IncompatibleTest; +import com.newrelic.security.test.marker.Java9IncompatibleTest; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.grizzly.http.util.Header; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; @@ -26,6 +29,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import javax.ws.rs.client.Client; @@ -53,6 +57,7 @@ @RunWith(SecurityInstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = {"com.newrelic.agent.security.instrumentation.jersey2", "org.glassfish.jersey"}) +@Category({ Java9IncompatibleTest.class, Java11IncompatibleTest.class, Java17IncompatibleTest.class }) public class JerseyTests { private static HttpServer server; diff --git a/instrumentation-security/jersey-3/build.gradle b/instrumentation-security/jersey-3/build.gradle index 4f65c91ea..38f1f1aaa 100644 --- a/instrumentation-security/jersey-3/build.gradle +++ b/instrumentation-security/jersey-3/build.gradle @@ -4,6 +4,9 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("org.glassfish.jersey.core:jersey-server:3.0.0") + testImplementation("org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.0.0") + testImplementation("org.glassfish.jersey.containers:jersey-container-servlet:3.0.0") + testImplementation('org.glassfish.jersey.inject:jersey-hk2:3.0.0') } jar { diff --git a/instrumentation-security/jersey-3/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java b/instrumentation-security/jersey-3/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java index 4bba34fb0..85ec2d538 100644 --- a/instrumentation-security/jersey-3/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java +++ b/instrumentation-security/jersey-3/src/test/java/com/nr/agent/security/instrumentation/jersey2/JerseyTests.java @@ -18,6 +18,8 @@ import com.newrelic.api.agent.security.schema.HttpResponse; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.security.test.marker.Java11IncompatibleTest; +import com.newrelic.security.test.marker.Java8IncompatibleTest; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.grizzly.http.util.Header; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; @@ -26,6 +28,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import jakarta.ws.rs.client.Client; @@ -53,6 +56,7 @@ @RunWith(SecurityInstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = {"com.newrelic.agent.security.instrumentation.jersey2", "org.glassfish.jersey"}) +@Category({Java8IncompatibleTest.class, Java11IncompatibleTest.class}) public class JerseyTests { private static HttpServer server; diff --git a/instrumentation-security/jetty-11/build.gradle b/instrumentation-security/jetty-11/build.gradle index 205e4f419..9752e24f4 100644 --- a/instrumentation-security/jetty-11/build.gradle +++ b/instrumentation-security/jetty-11/build.gradle @@ -21,6 +21,14 @@ java { } } +test { + // These instrumentation tests only run on Java 11+ regardless of the -PtestN gradle property that is set. + onlyIf { + !project.hasProperty('test8') + } +} + + site { title 'Jetty' type 'Appserver' diff --git a/instrumentation-security/jetty-12/build.gradle b/instrumentation-security/jetty-12/build.gradle index 8501cd8aa..d5a7f7742 100644 --- a/instrumentation-security/jetty-12/build.gradle +++ b/instrumentation-security/jetty-12/build.gradle @@ -22,6 +22,13 @@ java { } } +test { + // These instrumentation tests only run on Java 17+ regardless of the -PtestN gradle property that is set. + onlyIf { + !project.hasProperty('test8') && !project.hasProperty('test11') + } +} + site { title 'Jetty' type 'Appserver' diff --git a/instrumentation-security/jsp-3/build.gradle b/instrumentation-security/jsp-3/build.gradle index 9aa2ebb11..8a85b3374 100644 --- a/instrumentation-security/jsp-3/build.gradle +++ b/instrumentation-security/jsp-3/build.gradle @@ -24,6 +24,13 @@ java { } } +test { + // These instrumentation tests only run on Java 11+ regardless of the -PtestN gradle property that is set. + onlyIf { + !project.hasProperty('test8') + } +} + site { title 'JSP' type 'Other' diff --git a/instrumentation-security/ldaptive-2.0/build.gradle b/instrumentation-security/ldaptive-2.0/build.gradle index 1e40179e9..7f6ab5a78 100644 --- a/instrumentation-security/ldaptive-2.0/build.gradle +++ b/instrumentation-security/ldaptive-2.0/build.gradle @@ -25,8 +25,9 @@ java { } test { + // These instrumentation tests only run on Java 11+ regardless of the -PtestN gradle property that is set. onlyIf { - inputs.getProperties()["test.jdk"]!="jdk8" && inputs.getProperties()["test.jdk"]!="jdk9" && inputs.getProperties()["test.jdk"]!="jdk10" + !project.hasProperty('test8') } } diff --git a/instrumentation-security/lettuce-4.3/build.gradle b/instrumentation-security/lettuce-4.3/build.gradle index 8564b18fc..dc25f4434 100644 --- a/instrumentation-security/lettuce-4.3/build.gradle +++ b/instrumentation-security/lettuce-4.3/build.gradle @@ -3,7 +3,7 @@ dependencies { implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") implementation("biz.paluch.redis:lettuce:4.4.0.Final") - testImplementation("com.github.codemonstur:embedded-redis:1.0.0") + testImplementation('org.testcontainers:testcontainers:1.17.1') } jar { diff --git a/instrumentation-security/lettuce-4.3/src/test/java/com/nr/agent/instrumentation/lettuce_4_3/LettuceTest.java b/instrumentation-security/lettuce-4.3/src/test/java/com/nr/agent/instrumentation/lettuce_4_3/LettuceTest.java index 444200f2a..6c036b8e5 100644 --- a/instrumentation-security/lettuce-4.3/src/test/java/com/nr/agent/instrumentation/lettuce_4_3/LettuceTest.java +++ b/instrumentation-security/lettuce-4.3/src/test/java/com/nr/agent/instrumentation/lettuce_4_3/LettuceTest.java @@ -1,6 +1,5 @@ package com.nr.agent.instrumentation.lettuce_4_3; -import com.lambdaworks.redis.AbstractRedisAsyncCommands; import com.lambdaworks.redis.RedisAsyncCommandsImpl; import com.lambdaworks.redis.RedisClient; import com.lambdaworks.redis.api.StatefulRedisConnection; @@ -19,31 +18,35 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; -import redis.embedded.RedisServer; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; import java.io.IOException; import java.net.ServerSocket; -import java.util.ArrayList; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; -import static com.lambdaworks.redis.protocol.CommandType.SET; - @RunWith(SecurityInstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = "com.lambdaworks.redis") @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class LettuceTest { - private static RedisServer redisServer; private static int PORT = 0; + public static GenericContainer redis; + @BeforeClass - public static void setup() throws Exception { - PORT = getRandomPort(); - redisServer = new RedisServer(PORT); - redisServer.start(); - System.out.println(redisServer); + public static void setup() { + PORT = SecurityInstrumentationTestRunner.getIntrospector().getRandomPort(); + redis = new GenericContainer<>(DockerImageName.parse("redis:5.0.3-alpine")); + redis.setPortBindings(Collections.singletonList(PORT + ":6379")); + redis.start(); + } + @AfterClass + public static void tearDown() { + redis.stop(); } @Test @@ -446,11 +449,6 @@ public void testSadd_Sdiff_Scard_Smove_Srem() { verifier(CommandType.SREM, operation, Arrays.asList(keyValuePair1.getKey(), "test")); } - @AfterClass - public static void tearDown() throws Exception { - redisServer.stop(); - } - private static void opVerifier(List operations, int expected) { Assert.assertTrue("No operations detected.", operations.size() > 0); Assert.assertEquals("Unexpected number of operations detected.", expected, operations.size()); @@ -460,26 +458,19 @@ private static void verifier(CommandType cmd, RedisOperation operation, List redis; + @BeforeClass - public static void setup() throws Exception { - PORT = getRandomPort(); - redisServer = new RedisServer(PORT); - redisServer.start(); - System.out.println(redisServer); + public static void setup() { + PORT = SecurityInstrumentationTestRunner.getIntrospector().getRandomPort(); + redis = new GenericContainer<>(DockerImageName.parse("redis:5.0.3-alpine")); + redis.setPortBindings(Collections.singletonList(PORT + ":6379")); + redis.start(); + } + @AfterClass + public static void tearDown() { + redis.stop(); } @Test @@ -443,11 +450,6 @@ public void testSadd_Sdiff_Scard_Smove_Srem() { verifier(CommandType.SREM, operation, Arrays.asList(keyValuePair1.getKey(), "test")); } - @AfterClass - public static void tearDown() throws Exception { - redisServer.stop(); - } - private static void opVerifier(List operations, int expected) { Assert.assertTrue("No operations detected.", operations.size() > 0); Assert.assertEquals("Unexpected number of operations detected.", expected, operations.size()); @@ -457,27 +459,18 @@ private static void verifier(CommandType cmd, RedisOperation operation, List Future execute(Request request, AsyncHandler handler) throws IOException { + boolean isLockAcquired = NingHelper.acquireLockIfPossible(this.hashCode()); + AbstractOperation operation = null; + URI uri = null; + Future returnObj = null; + + try { + uri = new URI(request.getUrl()); + String scheme = uri.getScheme(); + + if (isLockAcquired && (scheme == null || scheme.equals("http") || scheme.equals("https"))) { + operation = NingHelper.preprocessSecurityHook(request, uri.toString(), NingHelper.METHOD_NAME_EXECUTE, this.getClass().getName()); + } + } catch (URISyntaxException uriSyntaxException) { + // Instrumentation won't work and normal execution will continue + } + + try { + returnObj = Weaver.callOriginal(); + } finally { + if (isLockAcquired) { + NingHelper.releaseLock(this.hashCode()); + } + } + NingHelper.registerExitOperation(isLockAcquired, operation); + + return returnObj; + } +} diff --git a/instrumentation-security/ning-async-http-client-1.1.0/src/main/java/play/CorePlugin.java b/instrumentation-security/ning-async-http-client-1.1.0/src/main/java/play/CorePlugin.java new file mode 100644 index 000000000..1d68ebdd2 --- /dev/null +++ b/instrumentation-security/ning-async-http-client-1.1.0/src/main/java/play/CorePlugin.java @@ -0,0 +1,11 @@ +package play; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +/** + * Play v1 instrumentation is implemented using its own set of pointcuts that don't work well with our async APIs. This + * class is present in Play v1 but not v2, and will cause this module NOT to load if the customer is using Play v1. + */ +@SkipIfPresent +public class CorePlugin { +} diff --git a/instrumentation-security/ning-async-http-client-1.1.0/src/test/java/com/nr/agent/security/instrumentation/ning/http_1_1/NingAsyncHttpClient11Test.java b/instrumentation-security/ning-async-http-client-1.1.0/src/test/java/com/nr/agent/security/instrumentation/ning/http_1_1/NingAsyncHttpClient11Test.java new file mode 100644 index 000000000..c90486c40 --- /dev/null +++ b/instrumentation-security/ning-async-http-client-1.1.0/src/test/java/com/nr/agent/security/instrumentation/ning/http_1_1/NingAsyncHttpClient11Test.java @@ -0,0 +1,398 @@ +package com.nr.agent.security.instrumentation.ning.http_1_1; + +import com.newrelic.agent.security.introspec.InstrumentationTestConfig; +import com.newrelic.agent.security.introspec.SecurityInstrumentationTestRunner; +import com.newrelic.agent.security.introspec.SecurityIntrospector; +import com.newrelic.agent.security.introspec.internal.HttpServerRule; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.instrumentation.helpers.ServletHelper; +import com.newrelic.api.agent.security.schema.AbstractOperation; +import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; +import com.newrelic.api.agent.security.schema.operation.SSRFOperation; +import com.ning.http.client.AsyncCompletionHandler; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.Request; +import com.ning.http.client.RequestBuilder; +import com.ning.http.client.RequestType; +import com.ning.http.client.Response; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +@RunWith(SecurityInstrumentationTestRunner.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@InstrumentationTestConfig(includePrefixes = { "com.newrelic.agent.security.instrumentation.ning.http_1_1", "com.ning" }) +public class NingAsyncHttpClient11Test { + + private static final int TIMEOUT = 30000; + + @ClassRule + public static HttpServerRule server = new HttpServerRule(); + + @Test + public void testPrepare() throws Exception { + URI endpoint = server.getEndPoint(); + String host = endpoint.getHost(); + + String headerValue = String.valueOf(UUID.randomUUID()); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + setCSECHeaders(headerValue, introspector); + + makeAsyncRequest(endpoint.toURL().toString()); + + List operations = introspector.getOperations(); + Assert.assertTrue("No operations detected", operations.size() > 0); + SSRFOperation operation = (SSRFOperation) operations.get(0); + Map headers = server.getHeaders(); + Assert.assertEquals("Invalid executed parameters.", server.getEndPoint().toString(), operation.getArg()); + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operation.getCaseType()); + Assert.assertEquals("Invalid executed method name.", "execute", operation.getMethodName()); + verifyHeaders(headerValue, headers); + } + + @Test + public void testPrepareGet() throws Exception { + URI endpoint = server.getEndPoint(); + String host = endpoint.getHost(); + + String headerValue = String.valueOf(UUID.randomUUID()); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + setCSECHeaders(headerValue, introspector); + + makeAsyncRequestGet(endpoint.toURL().toString()); + + List operations = introspector.getOperations(); + Assert.assertTrue("No operations detected", operations.size() > 0); + SSRFOperation operation = (SSRFOperation) operations.get(0); + Map headers = server.getHeaders(); + Assert.assertEquals("Invalid executed parameters.", server.getEndPoint().toString(), operation.getArg()); + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operation.getCaseType()); + Assert.assertEquals("Invalid executed method name.", "execute", operation.getMethodName()); + verifyHeaders(headerValue, headers); + } + + @Test + public void testPreparePost() throws Exception { + URI endpoint = server.getEndPoint(); + String host = endpoint.getHost(); + + String headerValue = String.valueOf(UUID.randomUUID()); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + setCSECHeaders(headerValue, introspector); + + makeAsyncRequestPost(endpoint.toURL().toString()); + + List operations = introspector.getOperations(); + Assert.assertTrue("No operations detected", operations.size() > 0); + SSRFOperation operation = (SSRFOperation) operations.get(0); + Map headers = server.getHeaders(); + Assert.assertEquals("Invalid executed parameters.", server.getEndPoint().toString(), operation.getArg()); + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operation.getCaseType()); + Assert.assertEquals("Invalid executed method name.", "execute", operation.getMethodName()); + verifyHeaders(headerValue, headers); + } + + @Test + public void testPreparePut() throws Exception { + URI endpoint = server.getEndPoint(); + String host = endpoint.getHost(); + + String headerValue = String.valueOf(UUID.randomUUID()); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + setCSECHeaders(headerValue, introspector); + + makeAsyncRequestPut(endpoint.toURL().toString()); + + List operations = introspector.getOperations(); + Assert.assertTrue("No operations detected", operations.size() > 0); + SSRFOperation operation = (SSRFOperation) operations.get(0); + Map headers = server.getHeaders(); + Assert.assertEquals("Invalid executed parameters.", server.getEndPoint().toString(), operation.getArg()); + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operation.getCaseType()); + Assert.assertEquals("Invalid executed method name.", "execute", operation.getMethodName()); + verifyHeaders(headerValue, headers); + } + + @Test + public void testPrepareDelete() throws Exception { + URI endpoint = server.getEndPoint(); + String host = endpoint.getHost(); + + String headerValue = String.valueOf(UUID.randomUUID()); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + setCSECHeaders(headerValue, introspector); + + makeAsyncRequestDelete(endpoint.toURL().toString()); + + List operations = introspector.getOperations(); + Assert.assertTrue("No operations detected", operations.size() > 0); + SSRFOperation operation = (SSRFOperation) operations.get(0); + Map headers = server.getHeaders(); + Assert.assertEquals("Invalid executed parameters.", server.getEndPoint().toString(), operation.getArg()); + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operation.getCaseType()); + Assert.assertEquals("Invalid executed method name.", "execute", operation.getMethodName()); + verifyHeaders(headerValue, headers); + } + + @Test + public void testPrepareHead() throws Exception { + URI endpoint = server.getEndPoint(); + String host = endpoint.getHost(); + + String headerValue = String.valueOf(UUID.randomUUID()); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + setCSECHeaders(headerValue, introspector); + + makeAsyncRequestHead(endpoint.toURL().toString()); + + List operations = introspector.getOperations(); + Assert.assertTrue("No operations detected", operations.size() > 0); + SSRFOperation operation = (SSRFOperation) operations.get(0); + Map headers = server.getHeaders(); + Assert.assertEquals("Invalid executed parameters.", server.getEndPoint().toString(), operation.getArg()); + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operation.getCaseType()); + Assert.assertEquals("Invalid executed method name.", "execute", operation.getMethodName()); + verifyHeaders(headerValue, headers); + } + + @Test + public void testPrepareOptions() throws Exception { + URI endpoint = server.getEndPoint(); + String host = endpoint.getHost(); + + String headerValue = String.valueOf(UUID.randomUUID()); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + setCSECHeaders(headerValue, introspector); + + makeAsyncRequestOptions(endpoint.toURL().toString()); + + List operations = introspector.getOperations(); + Assert.assertTrue("No operations detected", operations.size() > 0); + SSRFOperation operation = (SSRFOperation) operations.get(0); + Map headers = server.getHeaders(); + Assert.assertEquals("Invalid executed parameters.", server.getEndPoint().toString(), operation.getArg()); + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operation.getCaseType()); + Assert.assertEquals("Invalid executed method name.", "execute", operation.getMethodName()); + verifyHeaders(headerValue, headers); + } + + @Test + public void testExecuteRequest1() throws Exception { + URI endpoint = server.getEndPoint(); + String host = endpoint.getHost(); + + String headerValue = String.valueOf(UUID.randomUUID()); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + setCSECHeaders(headerValue, introspector); + + makeAsyncExecuteRequest1(endpoint.toURL().toString()); + + List operations = introspector.getOperations(); + Assert.assertTrue("No operations detected", operations.size() > 0); + SSRFOperation operation = (SSRFOperation) operations.get(0); + Map headers = server.getHeaders(); + Assert.assertEquals("Invalid executed parameters.", server.getEndPoint().toString(), operation.getArg()); + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operation.getCaseType()); + Assert.assertEquals("Invalid executed method name.", "execute", operation.getMethodName()); + verifyHeaders(headerValue, headers); + } + + @Test + public void testExecuteRequest2() throws Exception { + URI endpoint = server.getEndPoint(); + String host = endpoint.getHost(); + + String headerValue = String.valueOf(UUID.randomUUID()); + + SecurityIntrospector introspector = SecurityInstrumentationTestRunner.getIntrospector(); + setCSECHeaders(headerValue, introspector); + + makeAsyncExecuteRequest2(endpoint.toURL().toString()); + + List operations = introspector.getOperations(); + Assert.assertTrue("No operations detected", operations.size() > 0); + SSRFOperation operation = (SSRFOperation) operations.get(0); + Map headers = server.getHeaders(); + Assert.assertEquals("Invalid executed parameters.", server.getEndPoint().toString(), operation.getArg()); + Assert.assertEquals("Invalid event category.", VulnerabilityCaseType.HTTP_REQUEST, operation.getCaseType()); + Assert.assertEquals("Invalid executed method name.", "execute", operation.getMethodName()); + verifyHeaders(headerValue, headers); + } + + @Trace(dispatcher = true) + public static int makeAsyncRequestGet(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + try { + AsyncHttpClient.BoundRequestBuilder builder = client.prepareGet(url); + Future future = builder.execute(); + Response response = future.get(); + return response.getStatusCode(); + } catch (Exception e) { + return -1; + } finally { + client.close(); + } + } + + @Trace(dispatcher = true) + public static int makeAsyncRequestPost(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + try { + AsyncHttpClient.BoundRequestBuilder builder = client.preparePost(url); + Future future = builder.execute(); + Response response = future.get(); + return response.getStatusCode(); + } catch (Exception e) { + return -1; + } finally { + client.close(); + } + } + + @Trace(dispatcher = true) + public static int makeAsyncRequestPut(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + try { + AsyncHttpClient.BoundRequestBuilder builder = client.preparePut(url); + Future future = builder.execute(); + Response response = future.get(); + return response.getStatusCode(); + } catch (Exception e) { + return -1; + } finally { + client.close(); + } + } + + @Trace(dispatcher = true) + public static int makeAsyncRequestDelete(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + try { + AsyncHttpClient.BoundRequestBuilder builder = client.prepareDelete(url); + Future future = builder.execute(); + Response response = future.get(); + return response.getStatusCode(); + } catch (Exception e) { + return -1; + } finally { + client.close(); + } + } + + @Trace(dispatcher = true) + public static int makeAsyncRequestHead(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + try { + AsyncHttpClient.BoundRequestBuilder builder = client.prepareHead(url); + Future future = builder.execute(); + Response response = future.get(); + return response.getStatusCode(); + } catch (Exception e) { + return -1; + } finally { + client.close(); + } + } + + @Trace(dispatcher = true) + public static int makeAsyncRequestOptions(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + try { + AsyncHttpClient.BoundRequestBuilder builder = client.prepareOptions(url); + Future future = builder.execute(); + Response response = future.get(); + return response.getStatusCode(); + } catch (Exception e) { + return -1; + } finally { + client.close(); + } + } + + @Trace(dispatcher = true) + public static int makeAsyncRequest(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + try { + Request request = new RequestBuilder(RequestType.GET).setUrl(url).build(); + AsyncHttpClient.BoundRequestBuilder builder = client.prepareRequest(request); + Future future = builder.execute(); + Response response = future.get(); + return response.getStatusCode(); + } catch (Exception e) { + return -1; + } finally { + client.close(); + } + } + + @Trace(dispatcher = true) + private static void makeAsyncExecuteRequest1(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + try{ + Request request = new RequestBuilder(RequestType.GET).setUrl(url).build(); + Future f = client.executeRequest(request); + Response response = f.get(); + response.getStatusCode(); + } catch (InterruptedException | IOException | ExecutionException e) { + } + } + + @Trace(dispatcher = true) + private static void makeAsyncExecuteRequest2(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + try{ + Request request = new RequestBuilder(RequestType.GET).setUrl(url).build(); + Future f = client.executeRequest(request, new AsyncCompletionHandler() { + + @Override + public Response onCompleted(Response response) { + return response; + } + + @Override + public void onThrowable(Throwable t) { + } + }); + Response response = f.get(); + response.getStatusCode(); + } catch (InterruptedException | IOException | ExecutionException e) { + } + } + + private void setCSECHeaders(String headerValue, SecurityIntrospector introspector) { + introspector.setK2FuzzRequestId(headerValue+"a"); + introspector.setK2ParentId(headerValue+"b"); + introspector.setK2TracingData(headerValue); + } + + private void verifyHeaders(String headerValue, Map headers) { + Assert.assertTrue(String.format("Missing K2 header: %s", ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID), headers.containsKey(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID)); + Assert.assertEquals(String.format("Invalid K2 header value for: %s", ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID), headerValue+"a", headers.get(ServletHelper.CSEC_IAST_FUZZ_REQUEST_ID)); + Assert.assertTrue(String.format("Missing K2 header: %s", GenericHelper.CSEC_PARENT_ID), headers.containsKey(GenericHelper.CSEC_PARENT_ID)); + Assert.assertEquals(String.format("Invalid K2 header value for: %s", GenericHelper.CSEC_PARENT_ID), headerValue+"b", headers.get(GenericHelper.CSEC_PARENT_ID)); + Assert.assertTrue(String.format("Missing K2 header: %s", ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER), headers.containsKey(ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase())); + Assert.assertEquals(String.format("Invalid K2 header value for: %s", ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER), String.format("%s;DUMMY_UUID/dummy-api-id/dummy-exec-id;", + headerValue), headers.get( + ServletHelper.CSEC_DISTRIBUTED_TRACING_HEADER.toLowerCase())); + } +} diff --git a/instrumentation-security/servlet-6.0/build.gradle b/instrumentation-security/servlet-6.0/build.gradle index f4ccc42d1..f669df706 100644 --- a/instrumentation-security/servlet-6.0/build.gradle +++ b/instrumentation-security/servlet-6.0/build.gradle @@ -21,6 +21,14 @@ java { } } +test { + // These instrumentation tests only run on Java 11+ regardless of the -PtestN gradle property that is set. + onlyIf { + !project.hasProperty('test8') + } +} + + compileJava { options.fork = true options.bootstrapClasspath = null diff --git a/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/HttpServerTest.java b/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/HttpServerTest.java index 74b263c61..ad42038af 100644 --- a/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/HttpServerTest.java +++ b/instrumentation-security/sun-net-httpserver/src/test/java/com/nr/agent/security/instrumentation/httpServer/test/HttpServerTest.java @@ -9,9 +9,13 @@ import com.newrelic.api.agent.security.schema.AbstractOperation; import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; import com.newrelic.api.agent.security.schema.operation.RXSSOperation; +import com.newrelic.security.test.marker.Java11IncompatibleTest; +import com.newrelic.security.test.marker.Java17IncompatibleTest; +import com.newrelic.security.test.marker.Java9IncompatibleTest; import org.junit.Assert; import org.junit.ClassRule; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.io.BufferedReader; @@ -27,6 +31,7 @@ @RunWith(SecurityInstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "com.sun.net.httpserver"}) +@Category({ Java11IncompatibleTest.class, Java17IncompatibleTest.class }) public class HttpServerTest { @ClassRule public static Httpserver server = new Httpserver(); diff --git a/instrumentation-security/wildfly-8/build.gradle b/instrumentation-security/wildfly-8/build.gradle new file mode 100644 index 000000000..95fae598e --- /dev/null +++ b/instrumentation-security/wildfly-8/build.gradle @@ -0,0 +1,26 @@ +dependencies { + implementation(project(":newrelic-security-api")) + implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}") + implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}") + implementation("org.wildfly:wildfly-undertow:26.1.1.Final") { + exclude(group: "org.jacorb", module: "jacorb") + } + implementation("org.jboss.xnio:xnio-api:3.8.7.Final") + implementation("org.jboss.logging:jboss-logging:3.5.0.Final") + implementation("org.jboss.msc:jboss-msc:1.4.13.Final") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.wildfly-8' } +} + +verifyInstrumentation { + passesOnly 'org.wildfly:wildfly-undertow:[8.0.0.Final,)' + excludeRegex '.*(Alpha|Beta|CR).*' +} + +site { + title 'Wildfly' + type 'Appserver' + versionOverride '[8.0.0.Final,)' +} diff --git a/instrumentation-security/wildfly-8/src/main/java/org/wildfly/extension/undertow/HttpListenerService.java b/instrumentation-security/wildfly-8/src/main/java/org/wildfly/extension/undertow/HttpListenerService.java new file mode 100644 index 000000000..af34c889c --- /dev/null +++ b/instrumentation-security/wildfly-8/src/main/java/org/wildfly/extension/undertow/HttpListenerService.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.wildfly.extension.undertow; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import com.newrelic.api.agent.security.NewRelicSecurity; +import org.xnio.ChannelListener; +import org.xnio.StreamConnection; +import org.xnio.XnioWorker; +import org.xnio.channels.AcceptingChannel; + +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +/* + * The CAT point cut for wildfly had to be pulled out because + * the jar was not getting loaded correctly. + */ +@Weave +public abstract class HttpListenerService { + + protected void startListening(XnioWorker worker, InetSocketAddress socketAddress, + ChannelListener> acceptListener) throws IOException { + + NewRelicSecurity.getAgent().setApplicationConnectionConfig(socketAddress.getPort(), "http"); + Weaver.callOriginal(); + } + +} diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/dispatcher/Dispatcher.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/dispatcher/Dispatcher.java index b4f7cc258..a2a04577a 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/dispatcher/Dispatcher.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/dispatcher/Dispatcher.java @@ -438,7 +438,7 @@ private JavaAgentEventBean prepareTrustBoundaryEvent(JavaAgentEventBean eventBea TrustBoundaryOperation trustBoundaryOperationalBean) { JSONArray params = new JSONArray(); params.add(trustBoundaryOperationalBean.getKey()); - params.add(trustBoundaryOperationalBean.getValue()); + params.add(JsonConverter.toJSON(trustBoundaryOperationalBean.getValue())); eventBean.setParameters(params); return eventBean; } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/SecurityMetaData.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/SecurityMetaData.java index 93950bb7e..aa0a7fab6 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/SecurityMetaData.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/SecurityMetaData.java @@ -110,5 +110,8 @@ public T getCustomAttribute(String key, Class klass) { public void removeCustomAttribute(String key) { this.customData.remove(key); } + public void clearCustomAttr(){ + customData.clear(); + } } diff --git a/settings.gradle b/settings.gradle index 932f59700..df0e06951 100644 --- a/settings.gradle +++ b/settings.gradle @@ -160,21 +160,22 @@ include 'instrumentation:async-http-client-2.0.0' include 'instrumentation:sun-net-httpserver' include 'instrumentation:tomcat-7' include 'instrumentation:tomcat-8' +include 'instrumentation:wildfly-8' include 'instrumentation:grpc-1.4.0' include 'instrumentation:grpc-1.22.0' include 'instrumentation:grpc-1.40.0' -//include 'instrumentation:ning-async-http-client-1.1.0' -//include 'instrumentation:ning-async-http-client-1.6.1' -//include 'instrumentation:ning-async-http-client-1.0.0' -//include 'instrumentation:jersey-2' -//include 'instrumentation:jersey-2.16' -//include 'instrumentation:jersey-3' -//include 'instrumentation:spring-data-redis' +include 'instrumentation:ning-async-http-client-1.1.0' +include 'instrumentation:ning-async-http-client-1.6.1' +include 'instrumentation:ning-async-http-client-1.0.0' +include 'instrumentation:jersey-2' +include 'instrumentation:jersey-2.16' +include 'instrumentation:jersey-3' +include 'instrumentation:spring-data-redis' //include 'instrumentation:jcache-1.0.0' -//include 'instrumentation:lettuce-4.3' -//include 'instrumentation:lettuce-5.0' +include 'instrumentation:lettuce-4.3' +include 'instrumentation:lettuce-5.0' //include 'instrumentation:spymemcached-2.12.0' -//include 'instrumentation:jetty-12' -//include 'instrumentation:mule-3.7' -//include 'instrumentation:mule-3.6' +include 'instrumentation:jetty-12' +include 'instrumentation:mule-3.7' +include 'instrumentation:mule-3.6'