VYPR
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.

PackageAffected versionsPatched versions
org.jenkins-ci.plugins:script-securityMaven
< 1.18.11.18.1

Affected products

19
  • cpe: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

1
e7d3bc9c1e25

[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

4

News mentions

0

No linked articles in our index yet.