High severity7.3NVD Advisory· Published Feb 9, 2017· Updated May 13, 2026
CVE-2016-3102
CVE-2016-3102
Description
The Script Security plugin before 1.18.1 in Jenkins might allow remote attackers to bypass a Groovy sandbox protection mechanism via a plugin that performs (1) direct field access or (2) get/set array operations.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.jenkins-ci.plugins:script-securityMaven | < 1.18.1 | 1.18.1 |
Affected products
19cpe:2.3:a:jenkins:script_security:1.1:*:*:*:*:jenkins:*:*+ 18 more
- cpe:2.3:a:jenkins:script_security:1.1:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.2:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.3:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.4:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.5:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.6:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.7:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.8:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.9:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.0:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.11:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.12:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.13:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.14:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.15:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.10:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.16:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.17:*:*:*:*:jenkins:*:*
- cpe:2.3:a:jenkins:script_security:1.18:*:*:*:*:jenkins:*:*
Patches
1e7d3bc9c1e25[SECURITY-258] Trap field access and array index calls.
4 files changed · +92 −3
src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SandboxInterceptor.java+76 −2 modified@@ -348,13 +348,87 @@ private interface Rejector { @Nonnull RejectedAccessException reject(); } - // TODO consider whether it is useful to override onGet/SetArray/Attribute + @Override public Object onGetAttribute(Invoker invoker, Object receiver, String attribute) throws Throwable { + Field field = GroovyCallSiteSelector.field(receiver, attribute); + if (field == null) { + throw unclassifiedField(receiver, attribute); + } else if (whitelist.permitsFieldGet(field, receiver)) { + return super.onGetAttribute(invoker, receiver, attribute); + } else { + throw StaticWhitelist.rejectField(field); + } + } + + @Override public Object onSetAttribute(Invoker invoker, Object receiver, String attribute, Object value) throws Throwable { + Field field = GroovyCallSiteSelector.field(receiver, attribute); + if (field == null) { + throw unclassifiedField(receiver, attribute); + } else if (whitelist.permitsFieldSet(field, receiver, value)) { + return super.onSetAttribute(invoker, receiver, attribute, value); + } else { + throw StaticWhitelist.rejectField(field); + } + } + + @Override public Object onGetArray(Invoker invoker, Object receiver, Object index) throws Throwable { + if (receiver.getClass().isArray() && index instanceof Integer) { + return super.onGetArray(invoker, receiver, index); + } + Object[] args = new Object[] {index}; + Method method = GroovyCallSiteSelector.method(receiver, "getAt", args); + if (method != null) { + if (whitelist.permitsMethod(method, receiver, args)) { + return super.onGetArray(invoker, receiver, index); + } else { + throw StaticWhitelist.rejectMethod(method); + } + } + args = new Object[] {receiver, index}; + for (Class<?> dgm : DGM_CLASSES) { + method = GroovyCallSiteSelector.staticMethod(dgm, "getAt", args); + if (method != null) { + if (whitelist.permitsStaticMethod(method, args)) { + return super.onGetArray(invoker, receiver, index); + } else { + throw StaticWhitelist.rejectStaticMethod(method); + } + } + } + throw new RejectedAccessException("unclassified getAt method " + EnumeratingWhitelist.getName(receiver) + "[" + EnumeratingWhitelist.getName(index) + "]"); + } + + @Override public Object onSetArray(Invoker invoker, Object receiver, Object index, Object value) throws Throwable { + if (receiver.getClass().isArray() && index instanceof Integer) { + return super.onSetArray(invoker, receiver, index, value); + } + Object[] args = new Object[] {index, value}; + Method method = GroovyCallSiteSelector.method(receiver, "putAt", args); + if (method != null) { + if (whitelist.permitsMethod(method, receiver, args)) { + return super.onSetArray(invoker, receiver, index, value); + } else { + throw StaticWhitelist.rejectMethod(method); + } + } + args = new Object[] {receiver, index, value}; + for (Class<?> dgm : DGM_CLASSES) { + method = GroovyCallSiteSelector.staticMethod(dgm, "putAt", args); + if (method != null) { + if (whitelist.permitsStaticMethod(method, args)) { + return super.onSetArray(invoker, receiver, index, value); + } else { + throw StaticWhitelist.rejectStaticMethod(method); + } + } + } + throw new RejectedAccessException("unclassified putAt method " + EnumeratingWhitelist.getName(receiver) + "[" + EnumeratingWhitelist.getName(index) + "]=" + EnumeratingWhitelist.getName(value)); + } private static String printArgumentTypes(Object[] args) { StringBuilder b = new StringBuilder(); for (Object arg : args) { b.append(' '); - b.append(arg == null ? "null" : EnumeratingWhitelist.getName(arg.getClass())); + b.append(EnumeratingWhitelist.getName(arg)); } return b.toString(); }
src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/whitelists/EnumeratingWhitelist.java+7 −1 modified@@ -29,6 +29,8 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; import org.apache.commons.lang.ClassUtils; import org.jenkinsci.plugins.scriptsecurity.sandbox.Whitelist; @@ -112,7 +114,7 @@ public abstract class EnumeratingWhitelist extends Whitelist { return false; } - public static String getName(Class<?> c) { + public static @Nonnull String getName(@Nonnull Class<?> c) { Class<?> e = c.getComponentType(); if (e == null) { return c.getName(); @@ -121,6 +123,10 @@ public static String getName(Class<?> c) { } } + public static @Nonnull String getName(@CheckForNull Object o) { + return o == null ? "null" : getName(o.getClass()); + } + private static String[] argumentTypes(Class<?>[] argumentTypes) { String[] s = new String[argumentTypes.length]; for (int i = 0; i < argumentTypes.length; i++) {
src/main/resources/org/jenkinsci/plugins/scriptsecurity/sandbox/whitelists/generic-whitelist+5 −0 modified@@ -122,6 +122,9 @@ staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods eachLine java.lang staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods eachLine java.lang.CharSequence int groovy.lang.Closure staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods eachLine java.lang.String groovy.lang.Closure staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods eachLine java.lang.String int groovy.lang.Closure +staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods getAt java.util.List int +staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods getAt java.util.Map java.lang.Object +staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods getAt java.util.regex.Matcher int staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods getChars java.lang.String staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods isCase java.util.Collection java.lang.Object staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods isNumber java.lang.String @@ -134,6 +137,8 @@ staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods plus java.lang.Cha staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods plus java.lang.Number java.lang.String staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods plus java.lang.String java.lang.Object staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods plus java.lang.StringBuffer java.lang.String +staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods putAt java.util.List int java.lang.Object +staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods putAt java.util.Map java.lang.Object java.lang.Object staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods size boolean[] staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods size byte[] staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods size char[]
src/test/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SandboxInterceptorTest.java+4 −0 modified@@ -187,6 +187,10 @@ public boolean permitsMethod(Method method, Object receiver, Object[] args) { assertEvaluate(new StaticWhitelist("new " + clazz, "method " + clazz + " getProp5"), "DEFAULT", "new " + clazz + "().prop5"); assertRejected(new StaticWhitelist("new " + clazz, "method " + clazz + " getProp5"), "method " + clazz + " setProp5 java.lang.String", "def c = new " + clazz + "(); c.prop5 = 'EDITED'; c.prop5"); assertEvaluate(new StaticWhitelist("new " + clazz, "method " + clazz + " getProp5", "method " + clazz + " setProp5 java.lang.String", "method " + clazz + " rawProp5"), "EDITEDedited", "def c = new " + clazz + "(); c.prop5 = 'EDITED'; c.prop5 + c.rawProp5()"); + assertRejected(new StaticWhitelist("new " + clazz), "field " + clazz + " prop5", "new " + clazz + "().@prop5"); + assertEvaluate(new StaticWhitelist("new " + clazz, "field " + clazz + " prop5"), "default", "new " + clazz + "().@prop5"); + assertRejected(new StaticWhitelist("new " + clazz, "method " + clazz + " getProp5"), "field " + clazz + " prop5", "def c = new " + clazz + "(); c.@prop5 = 'edited'; c.prop5"); + assertEvaluate(new StaticWhitelist("new " + clazz, "method " + clazz + " getProp5", "field " + clazz + " prop5"), "EDITED", "def c = new " + clazz + "(); c.@prop5 = 'edited'; c.prop5"); } @Test public void syntheticMethods() throws Exception {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.