diff --git a/core/pom.xml b/core/pom.xml index b6d8645735..4f59983192 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -39,7 +39,7 @@ THE SOFTWARE. true - 1.256 + 1.256.1 2.5.6.SEC03 2.4.12 @@ -180,7 +180,7 @@ THE SOFTWARE. io.jenkins.stapler jenkins-stapler-support - 1.0 + 1.1 org.hamcrest diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 5e2ab71644..37df5372d9 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -240,6 +240,7 @@ public class Functions { public static void initPageVariables(JellyContext context) { StaplerRequest currentRequest = Stapler.getCurrentRequest(); + currentRequest.getWebApp().getDispatchValidator().allowDispatch(currentRequest, Stapler.getCurrentResponse()); String rootURL = currentRequest.getContextPath(); Functions h = new Functions(); diff --git a/core/src/main/java/hudson/widgets/RenderOnDemandClosure.java b/core/src/main/java/hudson/widgets/RenderOnDemandClosure.java index 05c3611a86..399d19419a 100644 --- a/core/src/main/java/hudson/widgets/RenderOnDemandClosure.java +++ b/core/src/main/java/hudson/widgets/RenderOnDemandClosure.java @@ -95,6 +95,7 @@ public class RenderOnDemandClosure { public HttpResponse render() { return new HttpResponse() { public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException { + req.getWebApp().getDispatchValidator().allowDispatch(req, rsp); try { new DefaultScriptInvoker() { @Override diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 411d9bde48..fac0ee6a49 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -39,6 +39,7 @@ import hudson.Launcher.LocalLauncher; import jenkins.AgentProtocol; import jenkins.diagnostics.URICheckEncodingMonitor; import jenkins.security.stapler.DoActionFilter; +import jenkins.security.stapler.StaplerDispatchValidator; import jenkins.security.stapler.StaplerFilteredActionListener; import jenkins.security.stapler.StaplerDispatchable; import jenkins.security.RedactSecretJsonInErrorMessageSanitizer; @@ -913,6 +914,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve webApp.setFilteredDoActionTriggerListener(actionListener); webApp.setFilteredFieldTriggerListener(actionListener); + webApp.setDispatchValidator(new StaplerDispatchValidator()); + webApp.setFilteredDispatchTriggerListener(actionListener); + adjuncts = new AdjunctManager(servletContext, pluginManager.uberClassLoader,"adjuncts/"+SESSION_HASH, TimeUnit.DAYS.toMillis(365)); ClassFilterImpl.register(); @@ -4426,9 +4430,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve @RequirePOST public void doEval(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { checkPermission(RUN_SCRIPTS); - + req.getWebApp().getDispatchValidator().allowDispatch(req, rsp); try { - MetaClass mc = WebApp.getCurrent().getMetaClass(getClass()); + MetaClass mc = req.getWebApp().getMetaClass(getClass()); Script script = mc.classLoader.loadTearOff(JellyClassLoaderTearOff.class).createContext().compileScript(new InputSource(req.getReader())); new JellyRequestDispatcher(this,script).forward(req,rsp); } catch (JellyException e) { diff --git a/core/src/main/java/jenkins/security/stapler/StaplerDispatchValidator.java b/core/src/main/java/jenkins/security/stapler/StaplerDispatchValidator.java new file mode 100644 index 0000000000..0d601e1ee6 --- /dev/null +++ b/core/src/main/java/jenkins/security/stapler/StaplerDispatchValidator.java @@ -0,0 +1,354 @@ +/* + * The MIT License + * + * Copyright (c) 2019 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.security.stapler; + +import com.google.common.annotations.VisibleForTesting; +import jenkins.model.Jenkins; +import jenkins.util.SystemProperties; +import org.apache.commons.io.IOUtils; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.CancelRequestHandlingException; +import org.kohsuke.stapler.DispatchValidator; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.WebApp; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.servlet.ServletContext; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Validates views dispatched by Stapler. This validation consists of two phases: + * + *

Validation can be disabled by setting the system property + * {@code jenkins.security.stapler.StaplerDispatchValidator.disabled=true} or setting {@link #DISABLED} to + * {@code true} in the script console.

+ * + * @since TODO + */ +@Restricted(NoExternalUse.class) +public class StaplerDispatchValidator implements DispatchValidator { + + private static final Logger LOGGER = Logger.getLogger(StaplerDispatchValidator.class.getName()); + private static final String ATTRIBUTE_NAME = StaplerDispatchValidator.class.getName() + ".status"; + private static final String ESCAPE_HATCH = StaplerDispatchValidator.class.getName() + ".disabled"; + /** + * Escape hatch to disable dispatch validation. + */ + public static /* script-console editable */ boolean DISABLED = SystemProperties.getBoolean(ESCAPE_HATCH); + + private static @CheckForNull Boolean setStatus(@Nonnull StaplerRequest req, @CheckForNull Boolean status) { + if (status == null) { + return null; + } + LOGGER.fine(() -> "Request dispatch set status to " + status + " for URL " + req.getPathInfo()); + req.setAttribute(ATTRIBUTE_NAME, status); + return status; + } + + private static @CheckForNull Boolean computeStatusIfNull(@Nonnull StaplerRequest req, @Nonnull Supplier statusIfNull) { + Object requestStatus = req.getAttribute(ATTRIBUTE_NAME); + return requestStatus instanceof Boolean ? (Boolean) requestStatus : setStatus(req, statusIfNull.get()); + } + + private final ValidatorCache cache; + + public StaplerDispatchValidator() { + cache = new ValidatorCache(); + cache.load(); + } + + @Override + public @CheckForNull Boolean isDispatchAllowed(@Nonnull StaplerRequest req, @Nonnull StaplerResponse rsp) { + if (DISABLED) { + return true; + } + Boolean status = computeStatusIfNull(req, () -> { + if (rsp.getContentType() != null) { + return true; + } + if (rsp.getStatus() >= 300) { + return true; + } + return null; + }); + LOGGER.finer(() -> req.getRequestURI() + " -> " + status); + return status; + } + + @Override + public @CheckForNull Boolean isDispatchAllowed(@Nonnull StaplerRequest req, @Nonnull StaplerResponse rsp, @Nonnull String viewName, @CheckForNull Object node) { + if (DISABLED) { + return true; + } + Boolean status = computeStatusIfNull(req, () -> { + if (viewName.equals("index")) { + return true; + } + if (node == null) { + return null; + } + return cache.find(node.getClass()).isViewValid(viewName); + }); + LOGGER.finer(() -> "<" + req.getRequestURI() + ", " + viewName + ", " + node + "> -> " + status); + return status; + } + + @Override + public void allowDispatch(@Nonnull StaplerRequest req, @Nonnull StaplerResponse rsp) { + if (DISABLED) { + return; + } + setStatus(req, true); + } + + @Override + public void requireDispatchAllowed(@Nonnull StaplerRequest req, @Nonnull StaplerResponse rsp) throws CancelRequestHandlingException { + if (DISABLED) { + return; + } + Boolean status = isDispatchAllowed(req, rsp); + if (status == null || !status) { + LOGGER.fine(() -> "Cancelling dispatch for " + req.getRequestURI()); + throw new CancelRequestHandlingException(); + } + } + + @VisibleForTesting + static StaplerDispatchValidator getInstance(@Nonnull ServletContext context) { + return (StaplerDispatchValidator) WebApp.get(context).getDispatchValidator(); + } + + @VisibleForTesting + void loadWhitelist(@Nonnull InputStream in) throws IOException { + cache.loadWhitelist(IOUtils.readLines(in)); + } + + private static class ValidatorCache { + private final Map validators = new HashMap<>(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + // provided as alternative to ConcurrentHashMap.computeIfAbsent which doesn't allow for recursion in the supplier + // see https://stackoverflow.com/q/28840047 + private @Nonnull Validator computeIfAbsent(@Nonnull String className, @Nonnull Function constructor) { + lock.readLock().lock(); + try { + if (validators.containsKey(className)) { + // cached value + return validators.get(className); + } + } finally { + lock.readLock().unlock(); + } + lock.writeLock().lock(); + try { + if (validators.containsKey(className)) { + // cached between readLock.unlock and writeLock.lock + return validators.get(className); + } + Validator value = constructor.apply(className); + validators.put(className, value); + return value; + } finally { + lock.writeLock().unlock(); + } + } + + private @Nonnull Validator find(@Nonnull Class clazz) { + return computeIfAbsent(clazz.getName(), name -> create(clazz)); + } + + private @Nonnull Validator find(@Nonnull String className) { + return computeIfAbsent(className, this::create); + } + + private @Nonnull Collection findParents(@Nonnull Class clazz) { + List parents = new ArrayList<>(); + Class superclass = clazz.getSuperclass(); + if (superclass != null) { + parents.add(find(superclass)); + } + for (Class iface : clazz.getInterfaces()) { + parents.add(find(iface)); + } + return parents; + } + + private @Nonnull Validator create(@Nonnull Class clazz) { + Set allowed = new HashSet<>(); + StaplerViews views = clazz.getDeclaredAnnotation(StaplerViews.class); + if (views != null) { + allowed.addAll(Arrays.asList(views.value())); + } + Set denied = new HashSet<>(); + StaplerFragments fragments = clazz.getDeclaredAnnotation(StaplerFragments.class); + if (fragments != null) { + denied.addAll(Arrays.asList(fragments.value())); + } + return new Validator(() -> findParents(clazz), allowed, denied); + } + + private @Nonnull Validator create(@Nonnull String className) { + ClassLoader loader = Jenkins.get().pluginManager.uberClassLoader; + return new Validator(() -> { + try { + return findParents(loader.loadClass(className)); + } catch (ClassNotFoundException e) { + LOGGER.log(Level.WARNING, e, () -> "Could not load class " + className + " to validate views"); + return Collections.emptySet(); + } + }); + } + + private void load() { + try { + try (InputStream is = Validator.class.getResourceAsStream("default-views-whitelist.txt")) { + loadWhitelist(IOUtils.readLines(is, StandardCharsets.UTF_8)); + } + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Could not load default views whitelist", e); + } + String whitelist = SystemProperties.getString(StaplerDispatchValidator.class.getName() + ".whitelist"); + Path configFile = whitelist != null ? Paths.get(whitelist) : Jenkins.get().getRootDir().toPath().resolve("stapler-views-whitelist.txt"); + if (Files.exists(configFile)) { + try { + loadWhitelist(Files.readAllLines(configFile)); + } catch (IOException e) { + LOGGER.log(Level.WARNING, e, () -> "Could not load user defined whitelist from " + configFile); + } + } + } + + private void loadWhitelist(@Nonnull List whitelistLines) { + for (String line : whitelistLines) { + if (line.matches("#.*|\\s*")) { + // commented line + continue; + } + String[] parts = line.split("\\s+"); + if (parts.length < 2) { + // invalid input format + LOGGER.warning(() -> "Cannot update validator with malformed line: " + line); + continue; + } + Validator validator = find(parts[0]); + for (int i = 1; i < parts.length; i++) { + String view = parts[i]; + if (view.startsWith("!")) { + validator.denyView(view.substring(1)); + } else { + validator.allowView(view); + } + } + } + } + + private class Validator { + // lazy load parents to avoid trying to load potentially unavailable classes + private final Supplier> parentsSupplier; + private volatile Collection parents; + private final Set allowed = ConcurrentHashMap.newKeySet(); + private final Set denied = ConcurrentHashMap.newKeySet(); + + private Validator(@Nonnull Supplier> parentsSupplier) { + this.parentsSupplier = parentsSupplier; + } + + private Validator(@Nonnull Supplier> parentsSupplier, @Nonnull Collection allowed, @Nonnull Collection denied) { + this(parentsSupplier); + this.allowed.addAll(allowed); + this.denied.addAll(denied); + } + + private @Nonnull Collection getParents() { + if (parents == null) { + synchronized (this) { + if (parents == null) { + parents = parentsSupplier.get(); + } + } + } + return parents; + } + + private @CheckForNull Boolean isViewValid(@Nonnull String viewName) { + if (allowed.contains(viewName)) { + return Boolean.TRUE; + } + if (denied.contains(viewName)) { + return Boolean.FALSE; + } + for (Validator parent : getParents()) { + Boolean result = parent.isViewValid(viewName); + if (result != null) { + return result; + } + } + return null; + } + + private void allowView(@Nonnull String viewName) { + allowed.add(viewName); + } + + private void denyView(@Nonnull String viewName) { + denied.add(viewName); + } + } + } +} diff --git a/core/src/main/java/jenkins/security/stapler/StaplerFilteredActionListener.java b/core/src/main/java/jenkins/security/stapler/StaplerFilteredActionListener.java index bdc329775f..2a16d90561 100644 --- a/core/src/main/java/jenkins/security/stapler/StaplerFilteredActionListener.java +++ b/core/src/main/java/jenkins/security/stapler/StaplerFilteredActionListener.java @@ -28,6 +28,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.Function; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.event.FilteredDispatchTriggerListener; import org.kohsuke.stapler.event.FilteredDoActionTriggerListener; import org.kohsuke.stapler.event.FilteredFieldTriggerListener; import org.kohsuke.stapler.event.FilteredGetterTriggerListener; @@ -37,10 +38,10 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Log a warning message when a "getter" or "doAction" function that was filtered out by SECURITY-400 new rules + * Log a warning message when a "getter" or "doAction" function or fragment view that was filtered out by SECURITY-400 new rules */ @Restricted(NoExternalUse.class) -public class StaplerFilteredActionListener implements FilteredDoActionTriggerListener, FilteredGetterTriggerListener, FilteredFieldTriggerListener { +public class StaplerFilteredActionListener implements FilteredDoActionTriggerListener, FilteredGetterTriggerListener, FilteredFieldTriggerListener, FilteredDispatchTriggerListener { private static final Logger LOGGER = Logger.getLogger(StaplerFilteredActionListener.class.getName()); private static final String LOG_MESSAGE = "New Stapler routing rules result in the URL \"{0}\" no longer being allowed. " + @@ -73,4 +74,12 @@ public class StaplerFilteredActionListener implements FilteredDoActionTriggerLis }); return false; } + + @Override + public boolean onDispatchTrigger(StaplerRequest req, StaplerResponse rsp, Object node, String viewName) { + LOGGER.warning(() -> "New Stapler dispatch rules result in the URL \"" + req.getPathInfo() + "\" no longer being allowed. " + + "If you consider it safe to use, add the following to the whitelist: \"" + node.getClass().getName() + " " + viewName + "\". "+ + "Learn more: https://jenkins.io/redirect/stapler-facet-restrictions"); + return false; + } } diff --git a/core/src/main/resources/hudson/atom.jelly b/core/src/main/resources/hudson/atom.jelly index de99ef25bc..6a7200641f 100644 --- a/core/src/main/resources/hudson/atom.jelly +++ b/core/src/main/resources/hudson/atom.jelly @@ -23,9 +23,7 @@ THE SOFTWARE. --> -<?xml version="1.0" encoding="UTF-8"?> - - +<?xml version="1.0" encoding="UTF-8"?> diff --git a/core/src/main/resources/hudson/model/Computer/_scriptText.jelly b/core/src/main/resources/hudson/model/Computer/_scriptText.jelly index 0867147550..f87b23b69d 100644 --- a/core/src/main/resources/hudson/model/Computer/_scriptText.jelly +++ b/core/src/main/resources/hudson/model/Computer/_scriptText.jelly @@ -26,5 +26,8 @@ THE SOFTWARE. Called from doScriptText() to display the execution result. --> - - + + + + + diff --git a/core/src/main/resources/hudson/model/View/cc.xml.jelly b/core/src/main/resources/hudson/model/View/cc.xml.jelly index b83ec111d1..1b48f2af19 100644 --- a/core/src/main/resources/hudson/model/View/cc.xml.jelly +++ b/core/src/main/resources/hudson/model/View/cc.xml.jelly @@ -27,10 +27,8 @@ THE SOFTWARE. See http://confluence.public.thoughtworks.org/display/CI/Multiple+Project+Summary+Reporting+Standard --> - - - - + + @@ -45,4 +43,5 @@ THE SOFTWARE. + \ No newline at end of file diff --git a/core/src/main/resources/hudson/rss20.jelly b/core/src/main/resources/hudson/rss20.jelly index 64a7c9f387..9919b177fc 100644 --- a/core/src/main/resources/hudson/rss20.jelly +++ b/core/src/main/resources/hudson/rss20.jelly @@ -23,11 +23,9 @@ THE SOFTWARE. --> -<?xml version="1.0" encoding="UTF-8"?> - - +<?xml version="1.0" encoding="UTF-8"?> - + ${title} diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly index f588306700..5adbb0a145 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly @@ -27,19 +27,14 @@ THE SOFTWARE. --> - + - - - - - ${h.initPageVariables(context)} @@ -297,4 +292,5 @@ THE SOFTWARE. + diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly b/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly index f515203c0e..e07b789741 100644 --- a/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly +++ b/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly @@ -24,8 +24,7 @@ THE SOFTWARE. - - + - - + + + + + diff --git a/core/src/main/resources/jenkins/model/Jenkins/login.jelly b/core/src/main/resources/jenkins/model/Jenkins/login.jelly index 3276493af8..3289c2c44c 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/login.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/login.jelly @@ -24,10 +24,10 @@ THE SOFTWARE. + - ${h.advertiseHeaders(response)} @@ -35,8 +35,6 @@ THE SOFTWARE. - - - - + + + Jenkins Jenkins at ${rootURL} @@ -38,4 +37,5 @@ THE SOFTWARE. + \ No newline at end of file diff --git a/core/src/main/resources/jenkins/security/stapler/default-views-whitelist.txt b/core/src/main/resources/jenkins/security/stapler/default-views-whitelist.txt new file mode 100644 index 0000000000..2f46c52312 --- /dev/null +++ b/core/src/main/resources/jenkins/security/stapler/default-views-whitelist.txt @@ -0,0 +1,34 @@ +# This file contains the built-in whitelist of allows views for Stapler dispatch filtering. +# This is provided as part of SECURITY-534 for backward compatibility. + +# Format: fully qualified class name of model class, a space, and then a space-separated list of view names to allow or deny (prefix with !) +# * class names can be repeated +# * subclasses of classes listed here are also whitelisted with the same view names +# Example: +# io.jenkins.plugins.example.ModelClass viewA viewB !viewC + +# https://plugins.jenkins.io/gerrit-trigger +com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.actions.manual.ManualTriggerAction help-Search + +# https://plugins.jenkins.io/depgraph-view +# This works experimentally even without this entry, but probably only because of buffer size, so be safe +hudson.plugins.depgraph_view.AbstractDependencyGraphAction jsplumb + +# https://plugins.jenkins.io/multi-slave-config-plugin +com.sonyericsson.hudson.plugins.multislaveconfigplugin.NodeManageLink slavefilter + +# https://plugins.jenkins.io/coverity +jenkins.plugins.coverity.CheckConfig$DescriptorImpl checkConfig +jenkins.plugins.coverity.CIMStream$DescriptorImpl defectFilters + +# https://plugins.jenkins.io/sounds +net.hurstfrost.hudson.sounds.SoundsAgentAction jsonpdemo + +# https://plugins.jenkins.io/scm2job +com.unitedinternet.jenkins.plugins.scm2job.SCM2Job showResultsPlain + +# https://plugins.jenkins.io/serenity +com.ikokoon.serenity.hudson.SerenityProjectAction nocoverage + +# https://plugins.jenkins.io/pipeline-config-history +org.jenkinsci.plugins.pipelineConfigHistory.view.PipelineConfigHistoryProjectAction configSingleFile diff --git a/core/src/main/resources/lib/layout/ajax.jelly b/core/src/main/resources/lib/layout/ajax.jelly index 9d9ef3b2d5..49b25db7db 100644 --- a/core/src/main/resources/lib/layout/ajax.jelly +++ b/core/src/main/resources/lib/layout/ajax.jelly @@ -50,13 +50,10 @@ THE SOFTWARE. - - - - ${h.initPageVariables(context)} - - + + + \ No newline at end of file diff --git a/core/src/main/resources/lib/layout/html.jelly b/core/src/main/resources/lib/layout/html.jelly index f364ff17f9..9a957defe8 100644 --- a/core/src/main/resources/lib/layout/html.jelly +++ b/core/src/main/resources/lib/layout/html.jelly @@ -56,13 +56,11 @@ THE SOFTWARE. (The permission will be checked against the "it" object.) + - - -${h.initPageVariables(context)} -${h.initPageVariables(context)} + + + + Declares a view as dispatchable. Views that do not use this tag or another tag that invokes this are + considered fragment views and are not dispatchable. + + HTTP Content-Type header value to use for this view. + + + + + + + ${h.initPageVariables(context)} + + diff --git a/test/src/test/java/jenkins/security/stapler/Security867Test.java b/test/src/test/java/jenkins/security/stapler/Security867Test.java index 8527cac809..bae57b906a 100644 --- a/test/src/test/java/jenkins/security/stapler/Security867Test.java +++ b/test/src/test/java/jenkins/security/stapler/Security867Test.java @@ -120,6 +120,7 @@ public class Security867Test { } @TestExtension + @StaplerViews("public") public static class RootAction1 implements RootAction { // not displayed in its own public.jelly public String getMyConfig() { @@ -143,6 +144,7 @@ public class Security867Test { } @TestExtension + @StaplerViews("showConfig") public static class RootAction3 implements RootAction { // displayed in its showConfig.jelly public String getMyConfig() { diff --git a/test/src/test/java/jenkins/security/stapler/StaplerDispatchValidatorTest.java b/test/src/test/java/jenkins/security/stapler/StaplerDispatchValidatorTest.java new file mode 100644 index 0000000000..0a8a5b98d8 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/StaplerDispatchValidatorTest.java @@ -0,0 +1,179 @@ +/* + * The MIT License + * + * Copyright (c) 2019 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.security.stapler; + +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import hudson.model.UnprotectedRootAction; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.For; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.TestExtension; + +import javax.annotation.CheckForNull; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; + +@Issue("SECURITY-534") +public class StaplerDispatchValidatorTest { + + @Rule public JenkinsRule j = new JenkinsRule(); + + @Before + public void setUp() throws Exception { + StaplerDispatchValidator validator = StaplerDispatchValidator.getInstance(j.jenkins.servletContext); + try (InputStream whitelist = getClass().getResourceAsStream("StaplerDispatchValidatorTest/whitelist.txt")) { + validator.loadWhitelist(whitelist); + } + } + + @Test + @For(StaplerViews.class) + public void canViewStaplerViews() throws Exception { + String[] urls = {"annotated/explicitRoot", "extended/explicitRoot", "extended/whitelistedRoot"}; + for (String url : urls) { + HtmlPage root = j.createWebClient().goTo(url); + assertEquals("Fragment", root.getElementById("frag").asText()); + assertEquals("Explicit Fragment", root.getElementById("explicit-frag").asText()); + } + } + + @Test + @For(StaplerFragments.class) + public void cannotViewStaplerFragments() throws Exception { + String[] urls = {"annotated/explicitFrag", "extended/explicitFrag"}; + for (String url : urls) { + j.createWebClient().assertFails(url, HttpURLConnection.HTTP_NOT_FOUND); + } + } + + @Test + public void canViewRoot() throws Exception { + String[] urls = {"annotated/root", "groovy/root", "jelly/root", "whitelist/root"}; + for (String url : urls) { + HtmlPage root = j.createWebClient().goTo(url); + assertEquals("Fragment", root.getElementById("frag").asText()); + } + } + + @Test + public void canViewIndex() throws Exception { + String[] urls = {"annotated", "groovy", "jelly"}; + for (String url : urls) { + HtmlPage root = j.createWebClient().goTo(url); + assertEquals("Fragment", root.getElementById("frag").asText()); + } + } + + @Test + public void canViewPagesThatIncludeViews() throws Exception { + String[] urls = {"groovy/include", "jelly/include"}; + for (String url : urls) { + HtmlPage root = j.createWebClient().goTo(url); + assertEquals("Fragment", root.getElementById("frag").asText()); + } + } + + @Test + public void canViewPagesThatRedirectToViews() throws Exception { + String[] urls = {"groovy/redirect", "jelly/redirect"}; + for (String url : urls) { + HtmlPage root = j.createWebClient().goTo(url); + assertEquals("Fragment", root.getElementById("frag").asText()); + } + } + + @Test + public void canViewCompressedViews() throws Exception { + String[] urls = {"groovy/compress", "jelly/compress"}; + for (String url : urls) { + HtmlPage root = j.createWebClient().goTo(url); + assertEquals("Fragment", root.getElementById("frag").asText()); + } + } + + @Test + public void cannotViewFragment() throws Exception { + String[] urls = {"annotated/frag", "groovy/frag", "jelly/frag", "whitelist/frag"}; + for (String url : urls) { + j.createWebClient().assertFails(url, HttpURLConnection.HTTP_NOT_FOUND); + } + } + + @Test + public void canSetStatusCodeBeforeValidation() throws Exception { + String[] urls = {"groovy/error", "jelly/error"}; + for (String url : urls) { + j.createWebClient().assertFails(url, 400); + } + } + + private static class Base implements UnprotectedRootAction { + @CheckForNull + @Override + public String getIconFileName() { + return null; + } + + @CheckForNull + @Override + public String getDisplayName() { + return getClass().getSimpleName() + " Test Data"; + } + + @CheckForNull + @Override + public String getUrlName() { + return getClass().getSimpleName().toLowerCase(Locale.ENGLISH); + } + } + + @TestExtension + public static class Jelly extends Base { + } + + @TestExtension + public static class Groovy extends Base { + } + + @TestExtension + @StaplerViews("explicitRoot") + @StaplerFragments("explicitFrag") + public static class Annotated extends Base { + } + + @TestExtension + public static class Whitelist extends Base { + } + + @TestExtension + public static class Extended extends Annotated { + } +} diff --git a/test/src/test/resources/hudson/HtmlEscapeTest/foo.jelly b/test/src/test/resources/hudson/HtmlEscapeTest/foo.jelly index 5e486ce521..d30ba6266e 100644 --- a/test/src/test/resources/hudson/HtmlEscapeTest/foo.jelly +++ b/test/src/test/resources/hudson/HtmlEscapeTest/foo.jelly @@ -1,17 +1,18 @@ - - - - -
- ${%msg} -
-
- ${%msg2(it.str)} -
-
- ${it.str} -
- - + + + + +
+ ${%msg} +
+
+ ${%msg2(it.str)} +
+
+ ${it.str} +
+ + +
\ No newline at end of file diff --git a/test/src/test/resources/hudson/core/PluginManagerOverrideTest/BasicPluginManagerOverride/newview.jelly b/test/src/test/resources/hudson/core/PluginManagerOverrideTest/BasicPluginManagerOverride/newview.jelly index e8c7438fe7..63ee6462fe 100644 --- a/test/src/test/resources/hudson/core/PluginManagerOverrideTest/BasicPluginManagerOverride/newview.jelly +++ b/test/src/test/resources/hudson/core/PluginManagerOverrideTest/BasicPluginManagerOverride/newview.jelly @@ -1,6 +1,7 @@ - - -
LoremIpsum
+ + +
LoremIpsum
+
\ No newline at end of file diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/explicitFrag.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/explicitFrag.jelly new file mode 100644 index 0000000000..9487ba2ce1 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/explicitFrag.jelly @@ -0,0 +1,27 @@ + + + +

Explicit Fragment

+
diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/explicitRoot.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/explicitRoot.jelly new file mode 100644 index 0000000000..e44b4ec011 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/explicitRoot.jelly @@ -0,0 +1,29 @@ + + + + + + + diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/frag.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/frag.jelly new file mode 100644 index 0000000000..9f11a15424 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/frag.jelly @@ -0,0 +1,27 @@ + + + +

Fragment

+
diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/index.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/index.jelly new file mode 100644 index 0000000000..da8815b2ac --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/index.jelly @@ -0,0 +1,28 @@ + + + + +

Fragment

+
diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/root.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/root.jelly new file mode 100644 index 0000000000..d49c04f4de --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Annotated/root.jelly @@ -0,0 +1,29 @@ + + + + + + + diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Extended/whitelistedRoot.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Extended/whitelistedRoot.jelly new file mode 100644 index 0000000000..e44b4ec011 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Extended/whitelistedRoot.jelly @@ -0,0 +1,29 @@ + + + + + + + diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/compress.groovy b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/compress.groovy new file mode 100644 index 0000000000..2c342e42bf --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/compress.groovy @@ -0,0 +1,34 @@ +/* + * The MIT License + * + * Copyright (c) 2019 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.security.stapler.StaplerDispatchValidatorTest.Groovy + +def st = namespace('jelly:stapler') +def l = namespace('/lib/layout') +st.compress { + l.view { + st.contentType(value: 'text/html') + st.include(page: 'frag') + } +} diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/error.groovy b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/error.groovy new file mode 100644 index 0000000000..5e058918ea --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/error.groovy @@ -0,0 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2019 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.security.stapler.StaplerDispatchValidatorTest.Groovy + +namespace('jelly:stapler').statusCode(value: 400) diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/frag.groovy b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/frag.groovy new file mode 100644 index 0000000000..53d5c1e079 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/frag.groovy @@ -0,0 +1,28 @@ +/* + * The MIT License + * + * Copyright (c) 2019 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.security.stapler.StaplerDispatchValidatorTest.Groovy +p(id: 'frag') { + text('Fragment') +} diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/include.groovy b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/include.groovy new file mode 100644 index 0000000000..fb26c7575c --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/include.groovy @@ -0,0 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2019 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.security.stapler.StaplerDispatchValidatorTest.Groovy + +namespace('jelly:stapler').include(page: 'root') diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/index.groovy b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/index.groovy new file mode 100644 index 0000000000..bce2c0119b --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/index.groovy @@ -0,0 +1,29 @@ +/* + * The MIT License + * + * Copyright (c) 2019 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.security.stapler.StaplerDispatchValidatorTest.Groovy +namespace('jelly:stapler').contentType(value: 'text/html') +p(id: 'frag') { + text('Fragment') +} diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/redirect.groovy b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/redirect.groovy new file mode 100644 index 0000000000..6b2d10deb8 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/redirect.groovy @@ -0,0 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2019 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.security.stapler.StaplerDispatchValidatorTest.Groovy + +namespace('jelly:stapler').redirect(url: 'root') diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/root.groovy b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/root.groovy new file mode 100644 index 0000000000..45e0624b7c --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Groovy/root.groovy @@ -0,0 +1,32 @@ +/* + * The MIT License + * + * Copyright (c) 2019 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.security.stapler.StaplerDispatchValidatorTest.Groovy + +def st = namespace('jelly:stapler') +def l = namespace('/lib/layout') +l.view { + st.contentType(value: 'text/html') + st.include(page: 'frag') +} diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/compress.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/compress.jelly new file mode 100644 index 0000000000..8ef1c236f5 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/compress.jelly @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/error.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/error.jelly new file mode 100644 index 0000000000..556ff95ec3 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/error.jelly @@ -0,0 +1,27 @@ + + + + + diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/frag.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/frag.jelly new file mode 100644 index 0000000000..9f11a15424 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/frag.jelly @@ -0,0 +1,27 @@ + + + +

Fragment

+
diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/include.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/include.jelly new file mode 100644 index 0000000000..9f1a5b1b34 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/include.jelly @@ -0,0 +1,27 @@ + + + + + diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/index.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/index.jelly new file mode 100644 index 0000000000..da8815b2ac --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/index.jelly @@ -0,0 +1,28 @@ + + + + +

Fragment

+
diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/redirect.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/redirect.jelly new file mode 100644 index 0000000000..a248e92891 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/redirect.jelly @@ -0,0 +1,27 @@ + + + + + diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/root.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/root.jelly new file mode 100644 index 0000000000..7a8982e417 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Jelly/root.jelly @@ -0,0 +1,29 @@ + + + + + + + diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Whitelist/frag.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Whitelist/frag.jelly new file mode 100644 index 0000000000..9f11a15424 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Whitelist/frag.jelly @@ -0,0 +1,27 @@ + + + +

Fragment

+
diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Whitelist/root.jelly b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Whitelist/root.jelly new file mode 100644 index 0000000000..e8d9ea4106 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/Whitelist/root.jelly @@ -0,0 +1,29 @@ + + + + + + + \ No newline at end of file diff --git a/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/whitelist.txt b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/whitelist.txt new file mode 100644 index 0000000000..3a3853e603 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaplerDispatchValidatorTest/whitelist.txt @@ -0,0 +1,2 @@ +jenkins.security.stapler.StaplerDispatchValidatorTest$Whitelist root !frag +jenkins.security.stapler.StaplerDispatchValidatorTest$Extended whitelistedRoot diff --git a/test/src/test/resources/lib/layout/RenderOnDemandTest/externalScript.jelly b/test/src/test/resources/lib/layout/RenderOnDemandTest/externalScript.jelly index 4d0df4b8b0..fc8fa5b493 100644 --- a/test/src/test/resources/lib/layout/RenderOnDemandTest/externalScript.jelly +++ b/test/src/test/resources/lib/layout/RenderOnDemandTest/externalScript.jelly @@ -1,4 +1,4 @@ - + y = "yyy"; - +