diff --git a/instrumentation-security/graalvm-jsinjection-24.1.0/build.gradle b/instrumentation-security/graalvm-jsinjection-24.1.0/build.gradle new file mode 100644 index 000000000..33c3f4a6d --- /dev/null +++ b/instrumentation-security/graalvm-jsinjection-24.1.0/build.gradle @@ -0,0 +1,38 @@ +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.graalvm.truffle:truffle-api:24.1.0") + implementation("org.graalvm.js:js:24.1.0") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.graalvm-jsinjection-24.1.0' } +} + +verifyInstrumentation { + passes 'org.graalvm.truffle:truffle-api:[24.1.0,)' +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + +compileJava { + options.fork = true + options.bootstrapClasspath = null +} + +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 'JSInjection' + type 'Messaging' +} \ No newline at end of file diff --git a/instrumentation-security/graalvm-jsinjection-24.1.0/src/main/java/com/newrelic/agent/security/instrumentation/graalvm24/JSEngineUtils.java b/instrumentation-security/graalvm-jsinjection-24.1.0/src/main/java/com/newrelic/agent/security/instrumentation/graalvm24/JSEngineUtils.java new file mode 100644 index 000000000..457cbc28e --- /dev/null +++ b/instrumentation-security/graalvm-jsinjection-24.1.0/src/main/java/com/newrelic/agent/security/instrumentation/graalvm24/JSEngineUtils.java @@ -0,0 +1,9 @@ +package com.newrelic.agent.security.instrumentation.graalvm24; + +public class JSEngineUtils { + + public static final String NR_SEC_CUSTOM_ATTRIB_NAME = "JSENGINE_OPERATION_LOCK_NASHORN-"; + public static final String GRAALVM_JS_INJECTION_24_1_0 = "GRAALVM_JS_INJECTION_24.1.0"; + public static final String METHOD_EVAL = "eval"; + public static final CharSequence LANGUAGE_ID_JS = "js"; +} diff --git a/instrumentation-security/graalvm-jsinjection-24.1.0/src/main/java/com/oracle/truffle/polyglot/PolyglotContextImpl_Instrumentation.java b/instrumentation-security/graalvm-jsinjection-24.1.0/src/main/java/com/oracle/truffle/polyglot/PolyglotContextImpl_Instrumentation.java new file mode 100644 index 000000000..3a36f5442 --- /dev/null +++ b/instrumentation-security/graalvm-jsinjection-24.1.0/src/main/java/com/oracle/truffle/polyglot/PolyglotContextImpl_Instrumentation.java @@ -0,0 +1,100 @@ +package com.oracle.truffle.polyglot; + +import com.newrelic.agent.security.instrumentation.graalvm24.JSEngineUtils; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper; +import com.newrelic.api.agent.security.schema.AbstractOperation; +import com.newrelic.api.agent.security.schema.StringUtils; +import com.newrelic.api.agent.security.schema.VulnerabilityCaseType; +import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException; +import com.newrelic.api.agent.security.schema.operation.JSInjectionOperation; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.oracle.truffle.api.source.Source; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl; + +@Weave(originalName = "com.oracle.truffle.polyglot.PolyglotContextImpl") +final class PolyglotContextImpl_Instrumentation { + + public AbstractPolyglotImpl.APIAccess getAPIAccess() { + return Weaver.callOriginal(); + } + + public Object eval(String languageId, Object source) { + boolean isLockAcquired = acquireLockIfPossible(); + AbstractOperation operation = null; + if (isLockAcquired) { + operation = preprocessSecurityHook(languageId, source, JSEngineUtils.METHOD_EVAL); + } + Object result; + try { + result = Weaver.callOriginal(); + } finally { + if (isLockAcquired) { + releaseLock(); + } + } + registerExitOperation(isLockAcquired, operation); + return result; + } + + private void registerExitOperation(boolean isProcessingAllowed, AbstractOperation operation) { + try { + if (operation == null || !isProcessingAllowed || !NewRelicSecurity.isHookProcessingActive() || + NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty() || GenericHelper.skipExistsEvent() + ) { + return; + } + NewRelicSecurity.getAgent().registerExitEvent(operation); + } catch (Throwable ignored) { + NewRelicSecurity.getAgent() + .log(LogLevel.FINEST, + String.format(GenericHelper.EXIT_OPERATION_EXCEPTION_MESSAGE, JSEngineUtils.GRAALVM_JS_INJECTION_24_1_0, ignored.getMessage()), + ignored, this.getClass().getName()); + } + } + + private AbstractOperation preprocessSecurityHook(String languageId, Object source, String methodName) { + try { + if (!NewRelicSecurity.isHookProcessingActive() || NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty() || + !StringUtils.equalsIgnoreCase(languageId, JSEngineUtils.LANGUAGE_ID_JS)) { + return null; + } + + // Getting the source to get the Characters + Source sourceReceiver = (com.oracle.truffle.api.source.Source) getAPIAccess().getSourceReceiver(source); + JSInjectionOperation jsInjectionOperation = new JSInjectionOperation(String.valueOf(sourceReceiver.getCharacters()), this.getClass().getName(), + methodName); + NewRelicSecurity.getAgent().registerOperation(jsInjectionOperation); + return jsInjectionOperation; + } catch (Throwable e) { + if (e instanceof NewRelicSecurityException) { + NewRelicSecurity.getAgent() + .log(LogLevel.WARNING, + String.format(GenericHelper.SECURITY_EXCEPTION_MESSAGE, JSEngineUtils.GRAALVM_JS_INJECTION_24_1_0, e.getMessage()), e, + this.getClass().getName()); + throw e; + } + NewRelicSecurity.getAgent() + .log(LogLevel.SEVERE, + String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, JSEngineUtils.GRAALVM_JS_INJECTION_24_1_0, e.getMessage()), e, + this.getClass().getName()); + NewRelicSecurity.getAgent() + .reportIncident(LogLevel.SEVERE, + String.format(GenericHelper.REGISTER_OPERATION_EXCEPTION_MESSAGE, JSEngineUtils.GRAALVM_JS_INJECTION_24_1_0, e.getMessage()), e, + this.getClass().getName()); + } + return null; + } + + private void releaseLock() { + GenericHelper.releaseLock(JSEngineUtils.NR_SEC_CUSTOM_ATTRIB_NAME); + } + + private boolean acquireLockIfPossible() { + return GenericHelper.acquireLockIfPossible(VulnerabilityCaseType.JAVASCRIPT_INJECTION, JSEngineUtils.NR_SEC_CUSTOM_ATTRIB_NAME); + } + +} + diff --git a/settings.gradle b/settings.gradle index 64dd79da8..28ae0a5e2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -107,6 +107,7 @@ include 'instrumentation:nashorn-jsinjection' include 'instrumentation:rhino-jsinjection' include 'instrumentation:graalvm-jsinjection-19.0.0' include 'instrumentation:graalvm-jsinjection-22.0.0' +include 'instrumentation:graalvm-jsinjection-24.1.0' include 'instrumentation:apache-log4j-2.0' include 'instrumentation:apache-log4j-2.17.2' include 'instrumentation:apache-log4j-3.0.0'