VYPR
Critical severityNVD Advisory· Published Feb 15, 2023· Updated Mar 19, 2025

CVE-2023-25765

CVE-2023-25765

Description

In Jenkins Email Extension Plugin 2.93 and earlier, templates defined inside a folder were not subject to Script Security protection, allowing attackers able to define email templates in folders to bypass the sandbox protection and execute arbitrary code in the context of the Jenkins controller JVM.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Jenkins Email Extension Plugin 2.93 and earlier allowed folder-level email templates to bypass Script Security sandbox, enabling remote code execution.

Vulnerability

CVE-2023-25765 affects Jenkins Email Extension Plugin versions 2.93 and earlier. The vulnerability occurs because email templates defined inside folders were not subject to Script Security protection, allowing them to bypass the sandbox and execute arbitrary code [3].

Exploitation

An attacker must have the ability to define email templates in folders, typically requiring Job/Configure permissions. By crafting a malicious template, the attacker can bypass the Script Security sandbox and execute arbitrary code on the Jenkins controller [4].

Impact

Successful exploitation results in arbitrary code execution in the context of the Jenkins controller JVM, which can lead to full compromise of the Jenkins instance and its data [3].

Mitigation

The vulnerability has been fixed in Email Extension Plugin version 2.93.1. The fix ensures that templates defined in folders are properly sandboxed using Script Security [2][3]. Users are strongly advised to upgrade to the latest version.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.jenkins-ci.plugins:email-extMaven
< 2.942.94

Affected products

2

Patches

1
ffe44a4c1c18

SECURITY-2939

4 files changed · +461 16
  • src/main/java/hudson/plugins/emailext/groovy/sandbox/SimpleTemplateEngine.java+368 0 added
    @@ -0,0 +1,368 @@
    +/*
    + * Copyright 2003-2009 the original author or authors.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + *     http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package hudson.plugins.emailext.groovy.sandbox;
    +
    +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    +import groovy.lang.Binding;
    +import groovy.lang.GroovyRuntimeException;
    +import groovy.lang.GroovyShell;
    +import groovy.lang.MissingPropertyException;
    +import groovy.lang.Script;
    +import groovy.lang.Writable;
    +import groovy.text.Template;
    +import groovy.text.TemplateEngine;
    +import hudson.plugins.emailext.plugins.content.ScriptContent;
    +import jenkins.util.SystemProperties;
    +import org.codehaus.groovy.control.CompilationFailedException;
    +import org.codehaus.groovy.runtime.InvokerHelper;
    +import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox;
    +
    +import java.io.BufferedReader;
    +import java.io.IOException;
    +import java.io.PrintWriter;
    +import java.io.Reader;
    +import java.io.StringWriter;
    +import java.io.Writer;
    +import java.util.Map;
    +import java.util.concurrent.atomic.AtomicInteger;
    +import java.util.logging.Level;
    +import java.util.logging.Logger;
    +
    +/**
    + * Processes template source files substituting variables and expressions into
    + * placeholders in a template source text to produce the desired output.
    + * <p>
    + * The template engine uses JSP style &lt;% %&gt; script and &lt;%= %&gt; expression syntax
    + * or GString style expressions. The variable '<code>out</code>' is bound to the writer that the template
    + * is being written to.
    + * <p>
    + * Frequently, the template source will be in a file but here is a simple
    + * example providing the template as a string:
    + * <pre>
    + * def binding = [
    + *     firstname : "Grace",
    + *     lastname  : "Hopper",
    + *     accepted  : true,
    + *     title     : 'Groovy for COBOL programmers'
    + * ]
    + * def engine = new groovy.text.SimpleTemplateEngine()
    + * def text = '''\
    + * Dear &lt;%= firstname %&gt; $lastname,
    + *
    + * We &lt;% if (accepted) print 'are pleased' else print 'regret' %&gt; \
    + * to inform you that your paper entitled
    + * '$title' was ${ accepted ? 'accepted' : 'rejected' }.
    + *
    + * The conference committee.
    + * '''
    + * def template = engine.createTemplate(text).make(binding)
    + * println template.toString()
    + * </pre>
    + * This example uses a mix of the JSP style and GString style placeholders
    + * but you can typically use just one style if you wish. Running this
    + * example will produce this output:
    + * <pre>
    + * Dear Grace Hopper,
    + *
    + * We are pleased to inform you that your paper entitled
    + * 'Groovy for COBOL programmers' was accepted.
    + *
    + * The conference committee.
    + * </pre>
    + * The template engine can also be used as the engine for {@link groovy.servlet.TemplateServlet} by placing the
    + * following in your <code>web.xml</code> file (plus a corresponding servlet-mapping element):
    + * <pre>
    + * &lt;servlet&gt;
    + *   &lt;servlet-name&gt;SimpleTemplate&lt;/servlet-name&gt;
    + *   &lt;servlet-class&gt;groovy.servlet.TemplateServlet&lt;/servlet-class&gt;
    + *   &lt;init-param&gt;
    + *     &lt;param-name&gt;template.engine&lt;/param-name&gt;
    + *     &lt;param-value&gt;groovy.text.SimpleTemplateEngine&lt;/param-value&gt;
    + *   &lt;/init-param&gt;
    + * &lt;/servlet&gt;
    + * </pre>
    + * In this case, your template source file should be HTML with the appropriate embedded placeholders.
    + *
    + * @author sam
    + * @author Christian Stein
    + * @author Paul King
    + * @author Alex Tkachman
    + */
    +public class SimpleTemplateEngine extends TemplateEngine {
    +
    +    private static final Logger LOGGER = Logger.getLogger(SimpleTemplateEngine.class.getName());
    +
    +    /**
    +     * Maximum size of template expansion we expect to produce.
    +     * Unit: bytes
    +     * Default: 1MB
    +     */
    +    // public for testing
    +    @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "non final to make it editable from the script console (convenient to temporarily change the value without restarting)")
    +    public static int MAX_EXPANDED_SIZE_BYTES = SystemProperties.getInteger(SimpleTemplateEngine.class.getName() + ".MAX_EXPANDED_SIZE_BYTES", 1024 * 1024);
    +
    +    private static AtomicInteger counter = new AtomicInteger(1);
    +
    +    protected final GroovyShell groovyShell;
    +    private final boolean sandbox;
    +
    +    public SimpleTemplateEngine(GroovyShell groovyShell, boolean sandbox) {
    +        this.groovyShell = groovyShell;
    +        this.sandbox = sandbox;
    +    }
    +
    +    public Template createTemplate(Reader reader) throws CompilationFailedException, IOException {
    +        return createTemplate(reader,"SimpleTemplateScript" + counter.getAndIncrement() + ".groovy");
    +    }
    +
    +    public Template createTemplate(Reader reader, String fileName) throws CompilationFailedException, IOException {
    +        SimpleTemplate template = new SimpleTemplate(sandbox);
    +        template.script = parseScript(reader,fileName);
    +        return template;
    +    }
    +
    +    protected Script parseScript(Reader reader, String fileName) throws CompilationFailedException, IOException {
    +        String script = parse(reader);
    +        if (LOGGER.isLoggable(Level.FINE)) {
    +            LOGGER.fine("\n-- script source --");
    +            LOGGER.fine(script);
    +            LOGGER.fine("\n-- script end --\n");
    +        }
    +        try (GroovySandbox.Scope scope = new GroovySandbox().enter()) {
    +            return groovyShell.parse(script, fileName);
    +        } catch (Exception e) {
    +            throw new GroovyRuntimeException("Failed to parse template script (your template may contain an error or be trying to use expressions not currently supported): " + e, e);
    +        }
    +    }
    +
    +    private static class SimpleTemplate implements Template {
    +
    +        private final boolean sandbox;
    +
    +        SimpleTemplate(boolean sandbox) {
    +            this.sandbox = sandbox;
    +        }
    +
    +        protected Script script;
    +
    +        public Writable make() {
    +            return make(null);
    +        }
    +
    +        public Writable make(final Map map) {
    +            return new Writable() {
    +                /**
    +                 * Write the template document with the set binding applied to the writer.
    +                 *
    +                 * @see groovy.lang.Writable#writeTo(java.io.Writer)
    +                 */
    +                @Override public Writer writeTo(Writer writer) throws IOException {
    +                    Binding binding;
    +                    if (map == null)
    +                        binding = new Binding();
    +                    else
    +                        binding = new Binding(map);
    +                    PrintWriter pw = new PrintWriter(writer);
    +                    try {
    +                        if (sandbox) {
    +                            // Cannot use normal GroovySandbox.runScript here because template preparation was separated.
    +                            try (GroovySandbox.Scope scope = new GroovySandbox().enter()) {
    +                                final Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
    +                                scriptObject.setProperty("out", pw);
    +                                scriptObject.run();
    +                            }
    +                        } else {
    +                            final Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
    +                            scriptObject.setProperty("out", pw);
    +                            scriptObject.run();
    +                        }
    +                    } catch (MissingPropertyException x) {
    +                        throw (IOException) new IOException("did you forget to escape \\$" + x.getProperty() + " for non-Groovy variables?").initCause(x);
    +                    }
    +                    pw.flush();
    +                    return writer;
    +                }
    +
    +                /**
    +                 * Convert the template and binding into a result String.
    +                 *
    +                 * @see java.lang.Object#toString()
    +                 */
    +                public String toString() {
    +                    StringWriter sw = new StringWriter();
    +                    try {
    +                        writeTo(sw);
    +                    } catch (IOException x) {
    +                        PrintWriter pw = new PrintWriter(sw);
    +                        x.printStackTrace(pw);
    +                        pw.flush();
    +                    }
    +                    return sw.toString();
    +                }
    +            };
    +        }
    +    }
    +
    +    /**
    +     * Parse the text document looking for {@code <%} or {@code <%=} and then call out to the appropriate handler, otherwise copy the text directly
    +     * into the script while escaping quotes.
    +     *
    +     * @param reader a reader for the template text
    +     * @return the parsed text
    +     * @throws IOException if something goes wrong
    +     */
    +    protected String parse(Reader reader) throws IOException {
    +        if (!reader.markSupported()) {
    +            reader = new BufferedReader(reader);
    +        }
    +        StringWriter sw = new StringWriter();
    +        startScript(sw);
    +        int c;
    +        while ((c = reader.read()) != -1) {
    +            if (c == '<') {
    +                reader.mark(1);
    +                c = reader.read();
    +                if (c != '%') {
    +                    sw.write('<');
    +                    reader.reset();
    +                } else {
    +                    reader.mark(1);
    +                    c = reader.read();
    +                    if (c == '=') {
    +                        groovyExpression(reader, sw);
    +                    } else {
    +                        reader.reset();
    +                        groovySection(reader, sw);
    +                    }
    +                }
    +                continue; // at least '<' is consumed ... read next chars.
    +            }
    +            if (c == '$') {
    +                reader.mark(1);
    +                c = reader.read();
    +                if (c != '{') {
    +                    sw.write('$');
    +                    reader.reset();
    +                } else {
    +                    reader.mark(1);
    +                    sw.write("${");
    +                    processGSstring(reader, sw);
    +                }
    +                continue; // at least '$' is consumed ... read next chars.
    +            }
    +            if (c == '\"') {
    +                sw.write('\\');
    +            }
    +            /*
    +             * Handle raw new line characters.
    +             */
    +            if (c == '\n' || c == '\r') {
    +                if (c == '\r') { // on Windows, "\r\n" is a new line.
    +                    reader.mark(1);
    +                    c = reader.read();
    +                    if (c != '\n') {
    +                        reader.reset();
    +                    }
    +                }
    +                sw.write("\n");
    +                continue;
    +            }
    +            sw.write(c);
    +        }
    +        endScript(sw);
    +        return sw.toString();
    +    }
    +
    +    private void startScript(StringWriter sw) {
    +        sw.write("/* Generated by SimpleTemplateEngine */\n");
    +        sw.write(printMethod()+"\"\"\"");
    +    }
    +
    +    private void endScript(StringWriter sw) {
    +        sw.write("\"\"\");\n");
    +    }
    +
    +    private void processGSstring(Reader reader, StringWriter sw) throws IOException {
    +        int c;
    +        while ((c = reader.read()) != -1) {
    +            if (c != '\n' && c != '\r') {
    +                sw.write(c);
    +            }
    +            if (c == '}') {
    +                break;
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>.
    +     *
    +     * @param reader a reader for the template text
    +     * @param sw     a StringWriter to write expression content
    +     * @throws IOException if something goes wrong
    +     */
    +    private void groovyExpression(Reader reader, StringWriter sw) throws IOException {
    +        sw.write("${");
    +        int c;
    +        while ((c = reader.read()) != -1) {
    +            if (c == '%') {
    +                c = reader.read();
    +                if (c != '>') {
    +                    sw.write('%');
    +                } else {
    +                    break;
    +                }
    +            }
    +            if (c != '\n' && c != '\r') {
    +                sw.write(c);
    +            }
    +        }
    +        sw.write("}");
    +    }
    +
    +    /**
    +     * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>.
    +     *
    +     * @param reader a reader for the template text
    +     * @param sw     a StringWriter to write expression content
    +     * @throws IOException if something goes wrong
    +     */
    +    private void groovySection(Reader reader, StringWriter sw) throws IOException {
    +        sw.write("\"\"\");");
    +        int c;
    +        while ((c = reader.read()) != -1) {
    +            if (c == '%') {
    +                c = reader.read();
    +                if (c != '>') {
    +                    sw.write('%');
    +                } else {
    +                    break;
    +                }
    +            }
    +            /* Don't eat EOL chars in sections - as they are valid instruction separators.
    +             * See http://jira.codehaus.org/browse/GROOVY-980
    +             */
    +            // if (c != '\n' && c != '\r') {
    +            sw.write(c);
    +            //}
    +        }
    +        sw.write(";\n"+printMethod()+"\"\"\"");
    +    }
    +
    +    protected String printMethod() {
    +        return "out.print(";
    +    }
    +}
    
  • src/main/java/hudson/plugins/emailext/plugins/content/AbstractEvalContent.java+13 11 modified
    @@ -23,6 +23,7 @@
      */
     package hudson.plugins.emailext.plugins.content;
     
    +import hudson.ExtensionList;
     import hudson.FilePath;
     import hudson.Plugin;
     import hudson.model.AbstractBuild;
    @@ -32,6 +33,7 @@
     import hudson.remoting.VirtualChannel;
     import hudson.security.ACL;
     import hudson.security.ACLContext;
    +import hudson.security.Permission;
     import hudson.util.FormValidation;
     import java.io.ByteArrayInputStream;
     import java.io.File;
    @@ -49,6 +51,7 @@
     import org.jenkinsci.lib.configprovider.ConfigProvider;
     import org.jenkinsci.lib.configprovider.model.Config;
     import org.jenkinsci.plugins.configfiles.ConfigFiles;
    +import org.jenkinsci.plugins.configfiles.GlobalConfigFiles;
     import org.jenkinsci.plugins.scriptsecurity.scripts.Language;
     import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval;
     import org.jenkinsci.plugins.tokenmacro.DataBoundTokenMacro;
    @@ -159,7 +162,7 @@ public static boolean isChildOf(final FilePath potentialChild, final FilePath pa
         }
     
         private InputStream getManagedFile(Run<?, ?> run, String fileName) {
    -        InputStream stream = null;
    +        InputStream stream;
             Plugin plugin = Jenkins.get().getPlugin("config-file-provider");
             if (plugin != null) {
                 Config config = null;
    @@ -173,9 +176,16 @@ private InputStream getManagedFile(Run<?, ?> run, String fileName) {
     
                 if (config != null) {
                    stream = new ByteArrayInputStream(config.content.getBytes(StandardCharsets.UTF_8));
    +                if (ExtensionList.lookupSingleton(GlobalConfigFiles.class).getById(config.id) == config) {
    +                    // the config is in the Global configuration not a folder - so it is approved by virtue of only being modified by an admin
    +                    return stream;
    +                } else {
    +                    // the script may have been written by anyone so it needs to be sandboxed
    +                    return new UserProvidedContentInputStream(stream);
    +                }
                 }
             }
    -        return stream;
    +        return null;
         }
         
         protected String generateMissingFile(String type, String fileName) {
    @@ -189,15 +199,7 @@ protected String getCharset(Run<?, ?> build) {
         @Restricted(NoExternalUse.class)
         public static boolean isApprovedScript(final String script, final Language language) {
             final ScriptApproval approval = ScriptApproval.get();
    -        try {
    -            //checking doesn't check if we are system or not since it assumed being called from doCheckField
    -            try (ACLContext context = ACL.as2(Jenkins.ANONYMOUS2)) {
    -                return approval.checking(script, language).kind == FormValidation.Kind.OK;
    -            }
    -        } catch (Exception e) {
    -            Logger.getLogger(AbstractEvalContent.class.getName()).log(Level.WARNING, "Could not determine approval state of script.", e);
    -            return false;
    -        }
    +        return approval.isScriptApproved(script, language);
         }
     
         private static class IsChildFileCallable extends MasterToSlaveFileCallable<Boolean> {
    
  • src/main/java/hudson/plugins/emailext/plugins/content/ScriptContent.java+11 5 modified
    @@ -6,6 +6,7 @@
     import groovy.lang.Script;
     import groovy.text.SimpleTemplateEngine;
     import groovy.text.Template;
    +import groovy.text.TemplateEngine;
     import hudson.FilePath;
     import hudson.Functions;
     import hudson.model.Item;
    @@ -133,20 +134,26 @@ private String renderTemplate(Run<?, ?> build, FilePath workspace, TaskListener
                 boolean approvedScript = false;
                 if (templateStream instanceof UserProvidedContentInputStream && !AbstractEvalContent.isApprovedScript(text, GroovyLanguage.get())) {
                     approvedScript = false;
    -                ScriptApproval.get().configuring(text, GroovyLanguage.get(), ApprovalContext.create().withItem(build.getParent()));
    +                ScriptApproval.get().configuring(text, GroovyLanguage.get(), ApprovalContext.create().withItem(build.getParent()), false);
                 } else {
                     approvedScript = true;
                 }
                 // we add the binding to the SimpleTemplateEngine instead of the shell
                 GroovyShell shell = createEngine(descriptor, Collections.emptyMap(), !approvedScript);
    -            SimpleTemplateEngine engine = new SimpleTemplateEngine(shell);
    +            TemplateEngine engine;
    +            if (!approvedScript) {
    +                engine = new hudson.plugins.emailext.groovy.sandbox.SimpleTemplateEngine(shell, true);
    +            } else {
    +                engine = new SimpleTemplateEngine(shell);
    +            }
                 Template tmpl;
                 synchronized (templateCache) {
    -                Reference<Template> templateR = templateCache.get(text);
    +                final String key = text + ":" + approvedScript;
    +                Reference<Template> templateR = templateCache.get(key);
                     tmpl = templateR == null ? null : templateR.get();
                     if (tmpl == null) {
                         tmpl = engine.createTemplate(text);
    -                    templateCache.put(text, new SoftReference<>(tmpl));
    +                    templateCache.put(key, new SoftReference<>(tmpl));
                     }
                 }
                 final Template tmplR = tmpl;
    @@ -259,7 +266,6 @@ private GroovyShell createEngine(ExtendedEmailPublisherDescriptor descriptor, Ma
             for (Map.Entry<String, Object> e : variables.entrySet()) {
                 binding.setVariable(e.getKey(), e.getValue());
             }
    -
             return new GroovyShell(cl, binding, cc);
         }
     
    
  • src/test/java/hudson/plugins/emailext/plugins/content/ScriptContentSecureTest.java+69 0 modified
    @@ -24,18 +24,37 @@
     
     package hudson.plugins.emailext.plugins.content;
     
    +import static org.hamcrest.CoreMatchers.not;
     import static org.hamcrest.MatcherAssert.assertThat;
     import static org.hamcrest.Matchers.containsString;
    +import static org.hamcrest.Matchers.empty;
    +import static org.hamcrest.Matchers.hasSize;
    +import static org.junit.Assert.assertEquals;
    +import static org.junit.Assert.assertNotEquals;
     import static org.junit.Assert.assertNull;
     import static org.junit.Assert.assertThrows;
     
    +import com.cloudbees.hudson.plugins.folder.Folder;
    +import hudson.model.FreeStyleBuild;
    +import hudson.model.FreeStyleProject;
     import hudson.model.Item;
    +import hudson.plugins.emailext.ExtendedEmailPublisher;
    +import hudson.plugins.emailext.GroovyTemplateConfig;
    +import hudson.util.LogTaskListener;
     import jenkins.model.Jenkins;
    +import org.jenkinsci.plugins.configfiles.folder.FolderConfigFileAction;
    +import org.jenkinsci.plugins.configfiles.folder.FolderConfigFileProperty;
     import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval;
     import org.junit.Before;
     import org.junit.Test;
    +import org.jvnet.hudson.test.CaptureEnvironmentBuilder;
     import org.jvnet.hudson.test.Issue;
     import org.jvnet.hudson.test.MockAuthorizationStrategy;
    +import org.jvnet.hudson.test.MockFolder;
    +
    +import java.io.IOException;
    +import java.util.logging.Level;
    +import java.util.logging.Logger;
     
     /**
      * Runs some {@link ScriptContentTest} in a secured Jenkins.
    @@ -98,4 +117,54 @@ public void templateInWorkspaceWithConstructor() throws Exception {
         public void testGroovyTemplateWithContentToken() throws Exception {
             super.testGroovyTemplateWithContentToken();
         }
    +
    +    @Test @Issue("SECURITY-2939")
    +    public void managedBadTemplateInFolder() throws Exception {
    +        final Folder folder = j.createProject(Folder.class, "sub");
    +
    +        final FreeStyleProject project = folder.createProject(FreeStyleProject.class, "Free");
    +        project.getBuildersList().add(new CaptureEnvironmentBuilder());
    +        project.getPublishersList().add(new ExtendedEmailPublisher());
    +        final FreeStyleBuild build = j.buildAndAssertSuccess(project);
    +        FolderConfigFileAction folderConfigFileAction = folder.getAction(FolderConfigFileAction.class);
    +        folderConfigFileAction.getGroupedConfigs(); //Should create the config store accessed below
    +        final FolderConfigFileProperty folderConfigFileProperty = folder.getProperties().get(FolderConfigFileProperty.class);
    +        folderConfigFileProperty.getConfigs().add(
    +                new GroovyTemplateConfig(
    +                "long-long-id", "test.groovy", "Bad groovy template script",
    +                        "<%\n" +
    +                                "  // Whatever Groovy code you want\n" +
    +                                "  Jenkins.get().setSystemMessage(\"You got hax0red\");\n" +
    +                                "  // You can easily exfiltrate data using out.print or throwing an exception with the data in the message\n" +
    +                                "  out.println(Jenkins.get().getSystemMessage());\n" +
    +                                "%>")
    +        );
    +        ScriptContent scriptContent = new ScriptContent();
    +        scriptContent.template = "managed:test.groovy";
    +        final LogTaskListener listener = new LogTaskListener(Logger.getLogger(getClass().getName()), Level.INFO);
    +        String result = scriptContent.evaluate(build, listener, "SCRIPT");
    +        assertThat(result, not(containsString("You got hax0red")));
    +        assertNotEquals("You got hax0red", Jenkins.get().getSystemMessage());
    +        assertThat(result, containsString("Scripts not permitted to use"));
    +
    +        final ScriptApproval scriptApproval = ScriptApproval.get();
    +        assertThat(scriptApproval.getPendingScripts(), hasSize(1));
    +        scriptApproval.preapproveAll();
    +
    +        scriptContent = new ScriptContent();
    +        scriptContent.template = "managed:test.groovy";
    +        result = scriptContent.evaluate(build, listener, "SCRIPT");
    +        assertThat(result, containsString("You got hax0red"));
    +        assertEquals("You got hax0red", Jenkins.get().getSystemMessage());
    +        Jenkins.get().setSystemMessage("");
    +
    +        scriptApproval.clearApprovedScripts();
    +
    +        scriptContent = new ScriptContent();
    +        scriptContent.template = "managed:test.groovy";
    +        result = scriptContent.evaluate(build, listener, "SCRIPT");
    +        assertThat(result, not(containsString("You got hax0red")));
    +        assertNotEquals("You got hax0red", Jenkins.get().getSystemMessage());
    +        assertThat(result, containsString("Scripts not permitted to use"));
    +    }
     }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

1