From 1aea33742950b7b1e1ce002ecd0393c123ac1b2b Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Tue, 26 Dec 2017 11:52:13 +0100 Subject: [PATCH 001/424] Bulk cleanup of code in hudson.cli. - [x] - Remove unused imports - [x] - Use diamond operator where possible - [x] - Use Jenkins#get() --- .../main/java/hudson/cli/BuildCommand.java | 4 +- core/src/main/java/hudson/cli/CLIAction.java | 2 +- core/src/main/java/hudson/cli/CLICommand.java | 6 +- .../java/hudson/cli/ClearQueueCommand.java | 1 - .../main/java/hudson/cli/CliManagerImpl.java | 4 +- .../src/main/java/hudson/cli/CliProtocol.java | 3 +- .../java/hudson/cli/ConnectNodeCommand.java | 2 +- .../java/hudson/cli/CreateNodeCommand.java | 1 - .../java/hudson/cli/DeleteBuildsCommand.java | 2 +- .../java/hudson/cli/DeleteJobCommand.java | 3 +- .../java/hudson/cli/DeleteNodeCommand.java | 3 +- .../java/hudson/cli/DeleteViewCommand.java | 3 +- .../hudson/cli/DisconnectNodeCommand.java | 2 +- .../main/java/hudson/cli/GroovyCommand.java | 2 +- .../main/java/hudson/cli/GroovyshCommand.java | 2 +- .../src/main/java/hudson/cli/HelpCommand.java | 2 +- .../java/hudson/cli/InstallPluginCommand.java | 5 +- .../java/hudson/cli/InstallToolCommand.java | 4 +- .../main/java/hudson/cli/ListJobsCommand.java | 1 - .../java/hudson/cli/OfflineNodeCommand.java | 5 +- .../java/hudson/cli/OnlineNodeCommand.java | 2 +- .../cli/ReloadConfigurationCommand.java | 2 +- .../java/hudson/cli/ReloadJobCommand.java | 4 +- .../hudson/cli/RemoveJobFromViewCommand.java | 1 - .../hudson/cli/declarative/CLIRegisterer.java | 85 +++++++++---------- .../handlers/GenericItemOptionHandler.java | 4 +- .../cli/handlers/NodeOptionHandler.java | 2 +- 27 files changed, 71 insertions(+), 86 deletions(-) diff --git a/core/src/main/java/hudson/cli/BuildCommand.java b/core/src/main/java/hudson/cli/BuildCommand.java index cd6a13efa1..8cd09281c5 100644 --- a/core/src/main/java/hudson/cli/BuildCommand.java +++ b/core/src/main/java/hudson/cli/BuildCommand.java @@ -89,7 +89,7 @@ public class BuildCommand extends CLICommand { public boolean checkSCM = false; @Option(name="-p",usage="Specify the build parameters in the key=value format.") - public Map parameters = new HashMap(); + public Map parameters = new HashMap<>(); @Option(name="-v",usage="Prints out the console output of the build. Use with -s") public boolean consoleOutput = false; @@ -109,7 +109,7 @@ public class BuildCommand extends CLICommand { throw new IllegalStateException(job.getFullDisplayName()+" is not parameterized but the -p option was specified."); //TODO: switch to type annotations after the migration to Java 1.8 - List values = new ArrayList(); + List values = new ArrayList<>(); for (Entry e : parameters.entrySet()) { String name = e.getKey(); diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java index 2c37db46cc..f9b0ef6d92 100644 --- a/core/src/main/java/hudson/cli/CLIAction.java +++ b/core/src/main/java/hudson/cli/CLIAction.java @@ -250,7 +250,7 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { // do not require any permission to establish a CLI connection // the actual authentication for the connecting Channel is done by CLICommand - return new FullDuplexHttpChannel(uuid, !Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER)) { + return new FullDuplexHttpChannel(uuid, !Jenkins.get().hasPermission(Jenkins.ADMINISTER)) { @SuppressWarnings("deprecation") @Override protected void main(Channel channel) throws IOException, InterruptedException { diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java index 5efbaf3a33..d4a140f2da 100644 --- a/core/src/main/java/hudson/cli/CLICommand.java +++ b/core/src/main/java/hudson/cli/CLICommand.java @@ -618,9 +618,9 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { * Key for {@link Channel#getProperty(Object)} that links to the {@link Authentication} object * which captures the identity of the client given by the transport layer. */ - public static final ChannelProperty TRANSPORT_AUTHENTICATION = new ChannelProperty(Authentication.class,"transportAuthentication"); + public static final ChannelProperty TRANSPORT_AUTHENTICATION = new ChannelProperty<>(Authentication.class, "transportAuthentication"); - private static final ThreadLocal CURRENT_COMMAND = new ThreadLocal(); + private static final ThreadLocal CURRENT_COMMAND = new ThreadLocal<>(); /*package*/ static CLICommand setCurrent(CLICommand cmd) { CLICommand old = getCurrent(); @@ -638,7 +638,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { static { // register option handlers that are defined ClassLoaders cls = new ClassLoaders(); - Jenkins j = Jenkins.getActiveInstance(); + Jenkins j = Jenkins.getInstanceOrNull(); if (j!=null) {// only when running on the master cls.put(j.getPluginManager().uberClassLoader); diff --git a/core/src/main/java/hudson/cli/ClearQueueCommand.java b/core/src/main/java/hudson/cli/ClearQueueCommand.java index 8e76a477aa..52cd7c4063 100644 --- a/core/src/main/java/hudson/cli/ClearQueueCommand.java +++ b/core/src/main/java/hudson/cli/ClearQueueCommand.java @@ -26,7 +26,6 @@ package hudson.cli; import hudson.Extension; import jenkins.model.Jenkins; -import org.acegisecurity.AccessDeniedException; import java.util.logging.Logger; diff --git a/core/src/main/java/hudson/cli/CliManagerImpl.java b/core/src/main/java/hudson/cli/CliManagerImpl.java index 750a38d210..d799e6bf99 100644 --- a/core/src/main/java/hudson/cli/CliManagerImpl.java +++ b/core/src/main/java/hudson/cli/CliManagerImpl.java @@ -90,7 +90,7 @@ public class CliManagerImpl implements CliEntryPoint, Serializable { cmd.channel = Channel.current(); final CLICommand old = CLICommand.setCurrent(cmd); try { - transportAuth = Channel.current().getProperty(CLICommand.TRANSPORT_AUTHENTICATION); + transportAuth = Channel.currentOrFail().getProperty(CLICommand.TRANSPORT_AUTHENTICATION); cmd.setTransportAuth(transportAuth); return cmd.main(args.subList(1,args.size()),locale, stdin, out, err); } finally { @@ -99,7 +99,7 @@ public class CliManagerImpl implements CliEntryPoint, Serializable { } err.println("No such command: "+subCmd); - new HelpCommand().main(Collections.emptyList(), locale, stdin, out, err); + new HelpCommand().main(Collections.emptyList(), locale, stdin, out, err); return -1; } diff --git a/core/src/main/java/hudson/cli/CliProtocol.java b/core/src/main/java/hudson/cli/CliProtocol.java index 58b5c95a64..2a0cf8d423 100644 --- a/core/src/main/java/hudson/cli/CliProtocol.java +++ b/core/src/main/java/hudson/cli/CliProtocol.java @@ -116,7 +116,8 @@ public class CliProtocol extends AgentProtocol { private static final boolean OPT_IN; static { - byte hash = Util.fromHexString(Jenkins.getInstance().getLegacyInstanceId())[0]; + //TODO: Jenkins#get() is not safe in the current context + byte hash = Util.fromHexString(Jenkins.get().getLegacyInstanceId())[0]; OPT_IN = (hash % 10) == 0; } } diff --git a/core/src/main/java/hudson/cli/ConnectNodeCommand.java b/core/src/main/java/hudson/cli/ConnectNodeCommand.java index ae20f5e608..189d2b5406 100644 --- a/core/src/main/java/hudson/cli/ConnectNodeCommand.java +++ b/core/src/main/java/hudson/cli/ConnectNodeCommand.java @@ -62,7 +62,7 @@ public class ConnectNodeCommand extends CLICommand { boolean errorOccurred = false; final Jenkins jenkins = Jenkins.getActiveInstance(); - final HashSet hs = new HashSet(); + final HashSet hs = new HashSet<>(); hs.addAll(nodes); List names = null; diff --git a/core/src/main/java/hudson/cli/CreateNodeCommand.java b/core/src/main/java/hudson/cli/CreateNodeCommand.java index 6a7f6dee76..edab3f7e71 100644 --- a/core/src/main/java/hudson/cli/CreateNodeCommand.java +++ b/core/src/main/java/hudson/cli/CreateNodeCommand.java @@ -32,7 +32,6 @@ import hudson.model.User; import jenkins.model.Jenkins; import org.kohsuke.args4j.Argument; -import org.kohsuke.args4j.CmdLineException; /** * @author ogondza diff --git a/core/src/main/java/hudson/cli/DeleteBuildsCommand.java b/core/src/main/java/hudson/cli/DeleteBuildsCommand.java index db86c433ab..37d6623f1f 100644 --- a/core/src/main/java/hudson/cli/DeleteBuildsCommand.java +++ b/core/src/main/java/hudson/cli/DeleteBuildsCommand.java @@ -57,7 +57,7 @@ public class DeleteBuildsCommand extends RunRangeCommand { protected int act(List> builds) throws IOException { job.checkPermission(Run.DELETE); - final HashSet hsBuilds = new HashSet(); + final HashSet hsBuilds = new HashSet<>(); for (Run build : builds) { if (!hsBuilds.contains(build.number)) { diff --git a/core/src/main/java/hudson/cli/DeleteJobCommand.java b/core/src/main/java/hudson/cli/DeleteJobCommand.java index 199296f87f..03e63e8579 100644 --- a/core/src/main/java/hudson/cli/DeleteJobCommand.java +++ b/core/src/main/java/hudson/cli/DeleteJobCommand.java @@ -31,7 +31,6 @@ import org.kohsuke.args4j.Argument; import java.util.List; import java.util.HashSet; -import java.util.logging.Logger; /** * CLI command, which deletes a job or multiple jobs. @@ -56,7 +55,7 @@ public class DeleteJobCommand extends CLICommand { boolean errorOccurred = false; final Jenkins jenkins = Jenkins.getActiveInstance(); - final HashSet hs = new HashSet(); + final HashSet hs = new HashSet<>(); hs.addAll(jobs); for (String job_s: hs) { diff --git a/core/src/main/java/hudson/cli/DeleteNodeCommand.java b/core/src/main/java/hudson/cli/DeleteNodeCommand.java index 03001fccc3..60a8821e0f 100644 --- a/core/src/main/java/hudson/cli/DeleteNodeCommand.java +++ b/core/src/main/java/hudson/cli/DeleteNodeCommand.java @@ -31,7 +31,6 @@ import org.kohsuke.args4j.Argument; import java.util.HashSet; import java.util.List; -import java.util.logging.Logger; /** * CLI command, which deletes Jenkins nodes. @@ -56,7 +55,7 @@ public class DeleteNodeCommand extends CLICommand { boolean errorOccurred = false; final Jenkins jenkins = Jenkins.getActiveInstance(); - final HashSet hs = new HashSet(); + final HashSet hs = new HashSet<>(); hs.addAll(nodes); for (String node_s : hs) { diff --git a/core/src/main/java/hudson/cli/DeleteViewCommand.java b/core/src/main/java/hudson/cli/DeleteViewCommand.java index 894978e683..955a476ad9 100644 --- a/core/src/main/java/hudson/cli/DeleteViewCommand.java +++ b/core/src/main/java/hudson/cli/DeleteViewCommand.java @@ -33,7 +33,6 @@ import org.kohsuke.args4j.Argument; import java.util.HashSet; import java.util.List; -import java.util.logging.Logger; /** * @author ogondza, pjanouse @@ -57,7 +56,7 @@ public class DeleteViewCommand extends CLICommand { boolean errorOccurred = false; // Remove duplicates - final HashSet hs = new HashSet(); + final HashSet hs = new HashSet<>(); hs.addAll(views); ViewOptionHandler voh = new ViewOptionHandler(null, null, null); diff --git a/core/src/main/java/hudson/cli/DisconnectNodeCommand.java b/core/src/main/java/hudson/cli/DisconnectNodeCommand.java index 65c95106eb..ad003521d7 100644 --- a/core/src/main/java/hudson/cli/DisconnectNodeCommand.java +++ b/core/src/main/java/hudson/cli/DisconnectNodeCommand.java @@ -61,7 +61,7 @@ public class DisconnectNodeCommand extends CLICommand { boolean errorOccurred = false; final Jenkins jenkins = Jenkins.getActiveInstance(); - final HashSet hs = new HashSet(); + final HashSet hs = new HashSet<>(); hs.addAll(nodes); List names = null; diff --git a/core/src/main/java/hudson/cli/GroovyCommand.java b/core/src/main/java/hudson/cli/GroovyCommand.java index e7b2479516..5e27f54781 100644 --- a/core/src/main/java/hudson/cli/GroovyCommand.java +++ b/core/src/main/java/hudson/cli/GroovyCommand.java @@ -59,7 +59,7 @@ public class GroovyCommand extends CLICommand { * Remaining arguments. */ @Argument(metaVar="ARGUMENTS", index=1, usage="Command line arguments to pass into script.") - public List remaining = new ArrayList(); + public List remaining = new ArrayList<>(); protected int run() throws Exception { // this allows the caller to manipulate the JVM state, so require the execute script privilege. diff --git a/core/src/main/java/hudson/cli/GroovyshCommand.java b/core/src/main/java/hudson/cli/GroovyshCommand.java index 31f998d0aa..6bd34dc691 100644 --- a/core/src/main/java/hudson/cli/GroovyshCommand.java +++ b/core/src/main/java/hudson/cli/GroovyshCommand.java @@ -56,7 +56,7 @@ public class GroovyshCommand extends CLICommand { return Messages.GroovyshCommand_ShortDescription(); } - @Argument(metaVar="ARGS") public List args = new ArrayList(); + @Argument(metaVar="ARGS") public List args = new ArrayList<>(); @Override protected int run() { diff --git a/core/src/main/java/hudson/cli/HelpCommand.java b/core/src/main/java/hudson/cli/HelpCommand.java index 60fcc0970b..d417244a12 100644 --- a/core/src/main/java/hudson/cli/HelpCommand.java +++ b/core/src/main/java/hudson/cli/HelpCommand.java @@ -65,7 +65,7 @@ public class HelpCommand extends CLICommand { } private int showAllCommands() { - Map commands = new TreeMap(); + Map commands = new TreeMap<>(); for (CLICommand c : CLICommand.all()) commands.put(c.getName(),c); diff --git a/core/src/main/java/hudson/cli/InstallPluginCommand.java b/core/src/main/java/hudson/cli/InstallPluginCommand.java index e040328936..79bf8a7c5e 100644 --- a/core/src/main/java/hudson/cli/InstallPluginCommand.java +++ b/core/src/main/java/hudson/cli/InstallPluginCommand.java @@ -35,7 +35,6 @@ import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; import java.io.File; -import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import java.util.HashSet; @@ -61,7 +60,7 @@ public class InstallPluginCommand extends CLICommand { "If it is the string ‘=’, the file will be read from standard input of the command, and ‘-name’ must be specified. " + "Otherwise the name is assumed to be the short name of the plugin in the existing update center (like ‘findbugs’), " + "and the plugin will be installed from the update center.") - public List sources = new ArrayList(); + public List sources = new ArrayList<>(); @Option(name="-name",usage="If specified, the plugin will be installed as this short name (whereas normally the name is inferred from the source name automatically).") public String name; // TODO better to parse out Short-Name from the manifest and deprecate this option @@ -152,7 +151,7 @@ public class InstallPluginCommand extends CLICommand { if (h.getUpdateCenter().getSites().isEmpty()) { stdout.println(Messages.InstallPluginCommand_NoUpdateCenterDefined()); } else { - Set candidates = new HashSet(); + Set candidates = new HashSet<>(); for (UpdateSite s : h.getUpdateCenter().getSites()) { Data dt = s.getData(); if (dt==null) diff --git a/core/src/main/java/hudson/cli/InstallToolCommand.java b/core/src/main/java/hudson/cli/InstallToolCommand.java index ef8db698f9..4b06b51686 100644 --- a/core/src/main/java/hudson/cli/InstallToolCommand.java +++ b/core/src/main/java/hudson/cli/InstallToolCommand.java @@ -78,11 +78,11 @@ public class InstallToolCommand extends CLICommand { throw new IllegalStateException("No such job found: "+id.job); p.checkPermission(Item.CONFIGURE); - List toolTypes = new ArrayList(); + List toolTypes = new ArrayList<>(); for (ToolDescriptor d : ToolInstallation.all()) { toolTypes.add(d.getDisplayName()); if (d.getDisplayName().equals(toolType)) { - List toolNames = new ArrayList(); + List toolNames = new ArrayList<>(); for (ToolInstallation t : d.getInstallations()) { toolNames.add(t.getName()); if (t.getName().equals(toolName)) diff --git a/core/src/main/java/hudson/cli/ListJobsCommand.java b/core/src/main/java/hudson/cli/ListJobsCommand.java index 68c30dd015..c358eced08 100644 --- a/core/src/main/java/hudson/cli/ListJobsCommand.java +++ b/core/src/main/java/hudson/cli/ListJobsCommand.java @@ -26,7 +26,6 @@ package hudson.cli; import java.util.Collection; import hudson.model.Item; -import hudson.model.Items; import hudson.model.TopLevelItem; import hudson.model.View; import hudson.Extension; diff --git a/core/src/main/java/hudson/cli/OfflineNodeCommand.java b/core/src/main/java/hudson/cli/OfflineNodeCommand.java index e003a633b2..39270918dc 100644 --- a/core/src/main/java/hudson/cli/OfflineNodeCommand.java +++ b/core/src/main/java/hudson/cli/OfflineNodeCommand.java @@ -34,7 +34,6 @@ import jenkins.model.Jenkins; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -60,8 +59,8 @@ public class OfflineNodeCommand extends CLICommand { @Override protected int run() throws Exception { boolean errorOccurred = false; - final Jenkins jenkins = Jenkins.getInstance(); - final HashSet hs = new HashSet(nodes); + final Jenkins jenkins = Jenkins.get(); + final HashSet hs = new HashSet<>(nodes); List names = null; for (String node_s : hs) { diff --git a/core/src/main/java/hudson/cli/OnlineNodeCommand.java b/core/src/main/java/hudson/cli/OnlineNodeCommand.java index 5cb190fcd8..0594d3770c 100644 --- a/core/src/main/java/hudson/cli/OnlineNodeCommand.java +++ b/core/src/main/java/hudson/cli/OnlineNodeCommand.java @@ -56,7 +56,7 @@ public class OnlineNodeCommand extends CLICommand { protected int run() throws Exception { boolean errorOccurred = false; final Jenkins jenkins = Jenkins.getActiveInstance(); - final HashSet hs = new HashSet(nodes); + final HashSet hs = new HashSet<>(nodes); List names = null; for (String node_s : hs) { diff --git a/core/src/main/java/hudson/cli/ReloadConfigurationCommand.java b/core/src/main/java/hudson/cli/ReloadConfigurationCommand.java index adb61e12c6..6b29dcf529 100644 --- a/core/src/main/java/hudson/cli/ReloadConfigurationCommand.java +++ b/core/src/main/java/hudson/cli/ReloadConfigurationCommand.java @@ -46,7 +46,7 @@ public class ReloadConfigurationCommand extends CLICommand { @Override protected int run() throws Exception { - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); // Or perhaps simpler to inline the thread body of doReload? j.doReload(); Object app; diff --git a/core/src/main/java/hudson/cli/ReloadJobCommand.java b/core/src/main/java/hudson/cli/ReloadJobCommand.java index c8849a317f..3e42d64e68 100644 --- a/core/src/main/java/hudson/cli/ReloadJobCommand.java +++ b/core/src/main/java/hudson/cli/ReloadJobCommand.java @@ -26,11 +26,9 @@ package hudson.cli; import hudson.AbortException; import hudson.Extension; import hudson.model.AbstractItem; -import hudson.model.AbstractProject; import hudson.model.Item; import hudson.model.Items; -import hudson.model.TopLevelItem; import jenkins.model.Jenkins; import org.kohsuke.args4j.Argument; @@ -64,7 +62,7 @@ public class ReloadJobCommand extends CLICommand { boolean errorOccurred = false; final Jenkins jenkins = Jenkins.getActiveInstance(); - final HashSet hs = new HashSet(); + final HashSet hs = new HashSet<>(); hs.addAll(jobs); for (String job_s: hs) { diff --git a/core/src/main/java/hudson/cli/RemoveJobFromViewCommand.java b/core/src/main/java/hudson/cli/RemoveJobFromViewCommand.java index 786f33d21e..5a543c91c5 100644 --- a/core/src/main/java/hudson/cli/RemoveJobFromViewCommand.java +++ b/core/src/main/java/hudson/cli/RemoveJobFromViewCommand.java @@ -31,7 +31,6 @@ import hudson.model.DirectlyModifiableView; import hudson.model.View; import org.kohsuke.args4j.Argument; -import org.kohsuke.args4j.CmdLineException; /** * @author ogondza diff --git a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java index 5868d62b9a..2bfcdf4650 100644 --- a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java +++ b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java @@ -47,6 +47,7 @@ import org.kohsuke.args4j.ClassParser; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.CmdLineException; +import javax.annotation.Nonnull; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; @@ -90,7 +91,7 @@ public class CLIRegisterer extends ExtensionFinder { * Finds a resolved method annotated with {@link CLIResolver}. */ private Method findResolver(Class type) throws IOException { - List resolvers = Util.filter(Index.list(CLIResolver.class, Jenkins.getInstance().getPluginManager().uberClassLoader), Method.class); + List resolvers = Util.filter(Index.list(CLIResolver.class, Jenkins.get().getPluginManager().uberClassLoader), Method.class); for ( ; type!=null; type=type.getSuperclass()) for (Method m : resolvers) if (m.getReturnType()==type) @@ -98,12 +99,12 @@ public class CLIRegisterer extends ExtensionFinder { return null; } - private List> discover(final Jenkins hudson) { + private List> discover(@Nonnull final Jenkins jenkins) { LOGGER.fine("Listing up @CLIMethod"); - List> r = new ArrayList>(); + List> r = new ArrayList<>(); try { - for ( final Method m : Util.filter(Index.list(CLIMethod.class, hudson.getPluginManager().uberClassLoader),Method.class)) { + for ( final Method m : Util.filter(Index.list(CLIMethod.class, jenkins.getPluginManager().uberClassLoader),Method.class)) { try { // command name final String name = m.getAnnotation(CLIMethod.class).name(); @@ -111,7 +112,7 @@ public class CLIRegisterer extends ExtensionFinder { final ResourceBundleHolder res = loadMessageBundle(m); res.format("CLI."+name+".shortDescription"); // make sure we have the resource, to fail early - r.add(new ExtensionComponent(new CloneableCLICommand() { + r.add(new ExtensionComponent<>(new CloneableCLICommand() { @Override public String getName() { return name; @@ -120,12 +121,12 @@ public class CLIRegisterer extends ExtensionFinder { @Override public String getShortDescription() { // format by using the right locale - return res.format("CLI."+name+".shortDescription"); + return res.format("CLI." + name + ".shortDescription"); } @Override protected CmdLineParser getCmdLineParser() { - return bindMethod(new ArrayList()); + return bindMethod(new ArrayList<>()); } private CmdLineParser bindMethod(List binders) { @@ -134,7 +135,7 @@ public class CLIRegisterer extends ExtensionFinder { CmdLineParser parser = new CmdLineParser(null); // build up the call sequence - Stack chains = new Stack(); + Stack chains = new Stack<>(); Method method = m; while (true) { chains.push(method); @@ -146,15 +147,15 @@ public class CLIRegisterer extends ExtensionFinder { try { method = findResolver(type); } catch (IOException ex) { - throw new RuntimeException("Unable to find the resolver method annotated with @CLIResolver for "+type, ex); + throw new RuntimeException("Unable to find the resolver method annotated with @CLIResolver for " + type, ex); } - if (method==null) { - throw new RuntimeException("Unable to find the resolver method annotated with @CLIResolver for "+type); + if (method == null) { + throw new RuntimeException("Unable to find the resolver method annotated with @CLIResolver for " + type); } } while (!chains.isEmpty()) - binders.add(new MethodBinder(chains.pop(),this,parser)); + binders.add(new MethodBinder(chains.pop(), this, parser)); return parser; } @@ -162,36 +163,30 @@ public class CLIRegisterer extends ExtensionFinder { /** * Envelope an annotated CLI command * - * @param args - * Arguments to the sub command. For example, if the CLI is invoked like "java -jar cli.jar foo bar zot", - * then "foo" is the sub-command and the argument list is ["bar","zot"]. - * @param locale - * Locale of the client (which can be different from that of the server.) Good behaving command implementation - * would use this locale for formatting messages. - * @param stdin - * Connected to the stdin of the CLI client. - * @param stdout - * Connected to the stdout of the CLI client. - * @param stderr - * Connected to the stderr of the CLI client. - * @return - * Exit code from the CLI command execution - * - *

- * Jenkins standard exit codes from CLI: - * 0 means everything went well. - * 1 means further unspecified exception is thrown while performing the command. - * 2 means CmdLineException is thrown while performing the command. - * 3 means IllegalArgumentException is thrown while performing the command. - * 4 mean IllegalStateException is thrown while performing the command. - * 5 means AbortException is thrown while performing the command. - * 6 means AccessDeniedException is thrown while performing the command. - * 7 means BadCredentialsException is thrown while performing the command. - * 8-15 are reserved for future usage - * 16+ mean a custom CLI exit error code (meaning defined by the CLI command itself) - * - *

- * Note: For details - see JENKINS-32273 + * @param args Arguments to the sub command. For example, if the CLI is invoked like "java -jar cli.jar foo bar zot", + * then "foo" is the sub-command and the argument list is ["bar","zot"]. + * @param locale Locale of the client (which can be different from that of the server.) Good behaving command implementation + * would use this locale for formatting messages. + * @param stdin Connected to the stdin of the CLI client. + * @param stdout Connected to the stdout of the CLI client. + * @param stderr Connected to the stderr of the CLI client. + * @return Exit code from the CLI command execution + *

+ *

+ * Jenkins standard exit codes from CLI: + * 0 means everything went well. + * 1 means further unspecified exception is thrown while performing the command. + * 2 means CmdLineException is thrown while performing the command. + * 3 means IllegalArgumentException is thrown while performing the command. + * 4 mean IllegalStateException is thrown while performing the command. + * 5 means AbortException is thrown while performing the command. + * 6 means AccessDeniedException is thrown while performing the command. + * 7 means BadCredentialsException is thrown while performing the command. + * 8-15 are reserved for future usage + * 16+ mean a custom CLI exit error code (meaning defined by the CLI command itself) + *

+ *

+ * Note: For details - see JENKINS-32273 */ @Override public int main(List args, Locale locale, InputStream stdin, PrintStream stdout, PrintStream stderr) { @@ -199,7 +194,7 @@ public class CLIRegisterer extends ExtensionFinder { this.stderr = stderr; this.locale = locale; - List binders = new ArrayList(); + List binders = new ArrayList<>(); CmdLineParser parser = bindMethod(binders); try { @@ -207,7 +202,7 @@ public class CLIRegisterer extends ExtensionFinder { Authentication old = sc.getAuthentication(); try { // authentication - CliAuthenticator authenticator = Jenkins.getInstance().getSecurityRealm().createCliAuthenticator(this); + CliAuthenticator authenticator = Jenkins.get().getSecurityRealm().createCliAuthenticator(this); new ClassParser().parse(authenticator, parser); // fill up all the binders @@ -217,7 +212,7 @@ public class CLIRegisterer extends ExtensionFinder { if (auth == Jenkins.ANONYMOUS) auth = loadStoredAuthentication(); sc.setAuthentication(auth); // run the CLI with the right credential - hudson.checkPermission(Jenkins.READ); + jenkins.checkPermission(Jenkins.READ); // resolve them Object instance = null; diff --git a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java index 6cd1d86eba..e854cdd9c7 100644 --- a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java @@ -57,12 +57,12 @@ public abstract class GenericItemOptionHandler extends OptionHan protected abstract Class type(); @Override public int parseArguments(Parameters params) throws CmdLineException { - final Jenkins j = Jenkins.getInstance(); + final Jenkins j = Jenkins.get(); final String src = params.getParameter(0); T s = j.getItemByFullName(src, type()); if (s == null) { final Authentication who = Jenkins.getAuthentication(); - try (ACLContext _ = ACL.as(ACL.SYSTEM)) { + try (ACLContext aclContext = ACL.as(ACL.SYSTEM)) { Item actual = j.getItemByFullName(src); if (actual == null) { LOGGER.log(Level.FINE, "really no item exists named {0}", src); diff --git a/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java b/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java index 90905d5348..a90199e8c1 100644 --- a/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java @@ -53,7 +53,7 @@ public class NodeOptionHandler extends OptionHandler { String nodeName = params.getParameter(0); - final Node node = Jenkins.getInstance().getNode(nodeName); + final Node node = Jenkins.get().getNode(nodeName); if (node == null) throw new IllegalArgumentException("No such node '" + nodeName + "'"); setter.addValue(node); -- GitLab From 884e95e0fa4dbfca570236ddb88399d3b0ec737c Mon Sep 17 00:00:00 2001 From: Stephen Connolly Date: Fri, 26 Jan 2018 11:32:36 +0000 Subject: [PATCH 002/424] [FIXED JENKINS-45235] When multiple update sites publish different versions, allow those versions to be seen - When automatically installing dependent plugins, the first update center with a version meeting the minimum required will win - Also enhances the CLI command to allow specification of a minimum version --- core/src/main/java/hudson/PluginManager.java | 4 +- core/src/main/java/hudson/PluginWrapper.java | 4 +- .../java/hudson/cli/InstallPluginCommand.java | 18 +++++++- .../main/java/hudson/model/UpdateCenter.java | 43 +++++++++++++++++++ .../main/java/hudson/model/UpdateSite.java | 10 ++--- .../resources/hudson/PluginManager/_table.js | 17 +++++--- .../hudson/PluginManager/table.jelly | 2 +- 7 files changed, 80 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 7ffbbf40d9..a7971ca33c 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -1700,7 +1700,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas for (Map.Entry requestedPlugin : parseRequestedPlugins(configXml).entrySet()) { PluginWrapper pw = getPlugin(requestedPlugin.getKey()); if (pw == null) { // install new - UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey()); + UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey(), requestedPlugin.getValue()); if (toInstall == null) { LOGGER.log(WARNING, "No such plugin {0} to install", requestedPlugin.getKey()); continue; @@ -1713,7 +1713,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } jobs.add(toInstall.deploy(true)); } else if (pw.isOlderThan(requestedPlugin.getValue())) { // upgrade - UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey()); + UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey(), requestedPlugin.getValue()); if (toInstall == null) { LOGGER.log(WARNING, "No such plugin {0} to upgrade", requestedPlugin.getKey()); continue; diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index 9ca85b04aa..75e358a811 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -641,7 +641,7 @@ public class PluginWrapper implements Comparable, ModelObject { */ public UpdateSite.Plugin getUpdateInfo() { UpdateCenter uc = Jenkins.getInstance().getUpdateCenter(); - UpdateSite.Plugin p = uc.getPlugin(getShortName()); + UpdateSite.Plugin p = uc.getPlugin(getShortName(), getVersionNumber()); if(p!=null && p.isNewerThan(getVersion())) return p; return null; } @@ -651,6 +651,8 @@ public class PluginWrapper implements Comparable, ModelObject { */ public UpdateSite.Plugin getInfo() { UpdateCenter uc = Jenkins.getInstance().getUpdateCenter(); + UpdateSite.Plugin p = uc.getPlugin(getShortName(), getVersionNumber()); + if (p != null) return p; return uc.getPlugin(getShortName()); } diff --git a/core/src/main/java/hudson/cli/InstallPluginCommand.java b/core/src/main/java/hudson/cli/InstallPluginCommand.java index e040328936..5268df333f 100644 --- a/core/src/main/java/hudson/cli/InstallPluginCommand.java +++ b/core/src/main/java/hudson/cli/InstallPluginCommand.java @@ -27,6 +27,7 @@ import hudson.AbortException; import hudson.Extension; import hudson.FilePath; import hudson.PluginManager; +import hudson.util.VersionNumber; import jenkins.model.Jenkins; import hudson.model.UpdateSite; import hudson.model.UpdateSite.Data; @@ -60,7 +61,9 @@ public class InstallPluginCommand extends CLICommand { "If this is an URL, Jenkins downloads the URL and installs that as a plugin. " + "If it is the string ‘=’, the file will be read from standard input of the command, and ‘-name’ must be specified. " + "Otherwise the name is assumed to be the short name of the plugin in the existing update center (like ‘findbugs’), " + - "and the plugin will be installed from the update center.") + "and the plugin will be installed from the update center. If the short name includes a minimum version number " + + "(like ‘findbugs:1.4’), and there are multiple update centers publishing different versions, the update centers " + + "will be searched in order for the first one publishing a version that is at least the specified version.") public List sources = new ArrayList(); @Option(name="-name",usage="If specified, the plugin will be installed as this short name (whereas normally the name is inferred from the source name automatically).") @@ -133,7 +136,18 @@ public class InstallPluginCommand extends CLICommand { } // is this a plugin the update center? - UpdateSite.Plugin p = h.getUpdateCenter().getPlugin(source); + int index = source.lastIndexOf(':'); + UpdateSite.Plugin p; + if (index == -1) { + p = h.getUpdateCenter().getPlugin(source); + } else { + // try to find matching min version number + VersionNumber version = new VersionNumber(source.substring(index + 1)); + p = h.getUpdateCenter().getPlugin(source.substring(0,index), version); + if (p == null) { + p = h.getUpdateCenter().getPlugin(source); + } + } if (p!=null) { stdout.println(Messages.InstallPluginCommand_InstallingFromUpdateCenter(source)); Throwable e = p.deploy(dynamicLoad).get().getError(); diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index 109efb9d37..e2686cee56 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -31,6 +31,7 @@ import hudson.PluginManager; import hudson.PluginWrapper; import hudson.ProxyConfiguration; import hudson.security.ACLContext; +import hudson.util.VersionNumber; import java.nio.file.Files; import java.nio.file.InvalidPathException; import jenkins.util.SystemProperties; @@ -615,6 +616,19 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas return null; } + /** + * Gets the plugin with the given name from the first {@link UpdateSite} to contain it. + * @return Discovered {@link Plugin}. {@code null} if it cannot be found + */ + public @CheckForNull Plugin getPlugin(String artifactId, VersionNumber minVersion) { + for (UpdateSite s : sites) { + Plugin p = s.getPlugin(artifactId); + if (minVersion.isNewerThan(new VersionNumber(p.version))) continue; + if (p!=null) return p; + } + return null; + } + /** * Schedules a Jenkins upgrade. */ @@ -2159,8 +2173,37 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas public int compareTo(PluginEntry o) { int r = category.compareTo(o.category); if (r==0) r = plugin.name.compareToIgnoreCase(o.plugin.name); + if (r==0) r = new VersionNumber(plugin.version).compareTo(new VersionNumber(o.plugin.version)); return r; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + PluginEntry that = (PluginEntry) o; + + if (!category.equals(that.category)) { + return false; + } + if (!plugin.name.equals(that.plugin.name)) { + return false; + } + return plugin.version.equals(that.plugin.version); + } + + @Override + public int hashCode() { + int result = category.hashCode(); + result = 31 * result + plugin.name.hashCode(); + result = 31 * result + plugin.version.hashCode(); + return result; + } } /** diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index 5771bf1e54..c7f7899431 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -1027,13 +1027,13 @@ public class UpdateSite { List deps = new ArrayList(); for(Map.Entry e : dependencies.entrySet()) { - Plugin depPlugin = Jenkins.getInstance().getUpdateCenter().getPlugin(e.getKey()); + VersionNumber requiredVersion = new VersionNumber(e.getValue()); + Plugin depPlugin = Jenkins.getInstance().getUpdateCenter().getPlugin(e.getKey(), requiredVersion); if (depPlugin == null) { LOGGER.log(Level.WARNING, "Could not find dependency {0} of {1}", new Object[] {e.getKey(), name}); continue; } - VersionNumber requiredVersion = new VersionNumber(e.getValue()); - + // Is the plugin installed already? If not, add it. PluginWrapper current = depPlugin.getInstalled(); @@ -1052,11 +1052,11 @@ public class UpdateSite { } for(Map.Entry e : optionalDependencies.entrySet()) { - Plugin depPlugin = Jenkins.getInstance().getUpdateCenter().getPlugin(e.getKey()); + VersionNumber requiredVersion = new VersionNumber(e.getValue()); + Plugin depPlugin = Jenkins.getInstance().getUpdateCenter().getPlugin(e.getKey(), requiredVersion); if (depPlugin == null) { continue; } - VersionNumber requiredVersion = new VersionNumber(e.getValue()); PluginWrapper current = depPlugin.getInstalled(); diff --git a/core/src/main/resources/hudson/PluginManager/_table.js b/core/src/main/resources/hudson/PluginManager/_table.js index 4176940362..b75704beb1 100644 --- a/core/src/main/resources/hudson/PluginManager/_table.js +++ b/core/src/main/resources/hudson/PluginManager/_table.js @@ -1,15 +1,15 @@ function showhideCategories(hdr,on) { var table = hdr.parentNode.parentNode.parentNode, newDisplay = on ? '' : 'none', - nameList = new Array(), name; + nameList = new Array(), id; for (var i = 1; i < table.rows.length; i++) { if (on || table.rows[i].cells.length == 1) table.rows[i].style.display = newDisplay; else { - // Hide duplicate rows for a plugin when not viewing by-category - name = table.rows[i].cells[1].getAttribute('data'); - if (nameList[name] == 1) table.rows[i].style.display = 'none'; - nameList[name] = 1; + // Hide duplicate rows for a plugin:version when not viewing by-category + id = table.rows[i].cells[1].getAttribute('data-id'); + if (nameList[id] == 1) table.rows[i].style.display = 'none'; + nameList[id] = 1; } } } @@ -28,8 +28,11 @@ Behaviour.specify("#filter-box", '_table', 0, function(e) { var items = document.getElementsBySelector(clz); for (var i=0; i=0); - var name = items[i].getAttribute("name"); + var name = items[i].cells && items[i].cells.length > 1 + ? items[i].cells[1].getAttribute('data-id') + : items[i].getAttribute("name"); if (visible && name != null) { + console.log(name); if (encountered[name]) { visible = false; } @@ -397,4 +400,4 @@ Behaviour.specify("#filter-box", '_table', 0, function(e) { setEnableWidgetStates(); }); -}()); \ No newline at end of file +}()); diff --git a/core/src/main/resources/hudson/PluginManager/table.jelly b/core/src/main/resources/hudson/PluginManager/table.jelly index 52fed76549..5e2ab64709 100644 --- a/core/src/main/resources/hudson/PluginManager/table.jelly +++ b/core/src/main/resources/hudson/PluginManager/table.jelly @@ -92,7 +92,7 @@ THE SOFTWARE. checked="${installedOk ? 'checked' : null}" disabled="${installedOk ? 'disabled' : null}" onClick="flip(this);"/> - +

-- GitLab From 027d7c682b2d5c5d947a2798a0817871169df4f2 Mon Sep 17 00:00:00 2001 From: Stephen Connolly Date: Tue, 6 Feb 2018 15:23:52 +0000 Subject: [PATCH 003/424] [JENKINS-48775] HACKY HACKY FIX - Cheat and try a HTTP url if the proxy has never been accessed with a HTTP url before attempting a HTTPS url --- .../main/java/hudson/ProxyConfiguration.java | 74 +++++++++++++------ 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/hudson/ProxyConfiguration.java b/core/src/main/java/hudson/ProxyConfiguration.java index 3baea805d4..eeb01c5077 100644 --- a/core/src/main/java/hudson/ProxyConfiguration.java +++ b/core/src/main/java/hudson/ProxyConfiguration.java @@ -110,6 +110,10 @@ public final class ProxyConfiguration extends AbstractDescribableImpl Date: Tue, 20 Feb 2018 17:33:50 -0500 Subject: [PATCH 004/424] [JENKINS-49588] add readResolve method to ensure that is always initialized properly when deserializing a fingerprint --- core/src/main/java/hudson/model/Fingerprint.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java index 2e0a7ce166..81e3383600 100644 --- a/core/src/main/java/hudson/model/Fingerprint.java +++ b/core/src/main/java/hudson/model/Fingerprint.java @@ -868,7 +868,7 @@ public class Fingerprint implements ModelObject, Saveable { /** * Range of builds that use this file keyed by a job full name. */ - private final Hashtable usages = new Hashtable(); + private Hashtable usages = new Hashtable(); PersistedList facets = new PersistedList(this); @@ -1031,6 +1031,14 @@ public class Fingerprint implements ModelObject, Saveable { save(); } + // JENKINS-49588 + protected Object readResolve() { + if (usages == null) { + usages = new Hashtable(); + } + return this; + } + void addWithoutSaving(@Nonnull String jobFullName, int n) { synchronized(usages) { // TODO why not synchronized (this) like some, though not all, other accesses? RangeSet r = usages.get(jobFullName); -- GitLab From 5c14b748923869831d8546bc2b05f045a2c6ac38 Mon Sep 17 00:00:00 2001 From: mike cirioli Date: Wed, 21 Feb 2018 13:13:10 -0500 Subject: [PATCH 005/424] [JENKINS-49588] Added a test for the new readResolve() method to ensure that a fingerprint file without a can still be deserialized --- core/src/test/java/hudson/model/FingerprintTest.java | 7 +++++++ .../hudson/model/fingerprintWithoutUsages.xml | 11 +++++++++++ 2 files changed, 18 insertions(+) create mode 100644 core/src/test/resources/hudson/model/fingerprintWithoutUsages.xml diff --git a/core/src/test/java/hudson/model/FingerprintTest.java b/core/src/test/java/hudson/model/FingerprintTest.java index 6f0d117847..f649ab788f 100644 --- a/core/src/test/java/hudson/model/FingerprintTest.java +++ b/core/src/test/java/hudson/model/FingerprintTest.java @@ -222,6 +222,13 @@ public class FingerprintTest { Fingerprint.load(new File(FingerprintTest.class.getResource("fingerprint.xml").toURI())).toString()); } + @Test public void loadFingerprintWithoutUsages() throws Exception { + Fingerprint fp = Fingerprint.load(new File(FingerprintTest.class.getResource("fingerprintWithoutUsages.xml").toURI())); + assertNotNull(fp); + assertEquals("test:jenkinsfile-example-1.0-SNAPSHOT.jar", fp.getFileName()); + assertNotNull(fp.getUsages()); + } + @Test public void roundTrip() throws Exception { Fingerprint f = new Fingerprint(new Fingerprint.BuildPtr("foo", 13), "stuff&more.jar", SOME_MD5); f.addWithoutSaving("some", 1); diff --git a/core/src/test/resources/hudson/model/fingerprintWithoutUsages.xml b/core/src/test/resources/hudson/model/fingerprintWithoutUsages.xml new file mode 100644 index 0000000000..de79804b60 --- /dev/null +++ b/core/src/test/resources/hudson/model/fingerprintWithoutUsages.xml @@ -0,0 +1,11 @@ + + + 2018-02-21 16:33:12.291 UTC + + maven1/test:jenkinsfile-example + 4 + + f003726dfd1d07868e3c1ab5aab0682d + test:jenkinsfile-example-1.0-SNAPSHOT.jar + + -- GitLab From 6e5c41c65bbb8064aacfb9f618b86082c2fdeddd Mon Sep 17 00:00:00 2001 From: talkdirty Date: Fri, 27 Oct 2017 14:33:41 +0200 Subject: [PATCH 006/424] [JENKINS-47530] Add null check to RunParameterDefinition#getProject() in RunParameterDefinition.getDefaultParameterValue() --- .../hudson/model/RunParameterDefinition.java | 18 ++++++++++++++---- core/src/main/java/jenkins/model/Jenkins.java | 2 +- .../model/RunParameterDefinitionTest.java | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/hudson/model/RunParameterDefinition.java b/core/src/main/java/hudson/model/RunParameterDefinition.java index da1e7865e6..432ee0c251 100644 --- a/core/src/main/java/hudson/model/RunParameterDefinition.java +++ b/core/src/main/java/hudson/model/RunParameterDefinition.java @@ -23,6 +23,10 @@ */ package hudson.model; +import static java.util.logging.Level.WARNING; + +import java.util.logging.Level; +import java.util.logging.Logger; import jenkins.model.Jenkins; import net.sf.json.JSONObject; @@ -164,20 +168,25 @@ public class RunParameterDefinition extends SimpleParameterDefinition { } Run lastBuild = null; + Job project = getProject(); + + if (project == null) { + return null; + } // use getFilter() so we dont have to worry about null filter value. switch (getFilter()) { case COMPLETED: - lastBuild = getProject().getLastCompletedBuild(); + lastBuild = project.getLastCompletedBuild(); break; case SUCCESSFUL: - lastBuild = getProject().getLastSuccessfulBuild(); + lastBuild = project.getLastSuccessfulBuild(); break; case STABLE : - lastBuild = getProject().getLastStableBuild(); + lastBuild = project.getLastStableBuild(); break; default: - lastBuild = getProject().getLastBuild(); + lastBuild = project.getLastBuild(); break; } @@ -199,4 +208,5 @@ public class RunParameterDefinition extends SimpleParameterDefinition { return new RunParameterValue(getName(), value, getDescription()); } + private static final Logger LOGGER = Logger.getLogger(RunParameterDefinition.class.getName()); } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index a9a5b1af2d..fedfca137e 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -2871,7 +2871,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * or it exists but it's no an instance of the given type. * @throws AccessDeniedException as per {@link ItemGroup#getItem} */ - public @CheckForNull T getItemByFullName(String fullName, Class type) throws AccessDeniedException { + public @CheckForNull T getItemByFullName(@Nonnull String fullName, Class type) throws AccessDeniedException { StringTokenizer tokens = new StringTokenizer(fullName,"/"); ItemGroup parent = this; diff --git a/test/src/test/java/hudson/model/RunParameterDefinitionTest.java b/test/src/test/java/hudson/model/RunParameterDefinitionTest.java index 82db916dfa..f085ed7d6d 100644 --- a/test/src/test/java/hudson/model/RunParameterDefinitionTest.java +++ b/test/src/test/java/hudson/model/RunParameterDefinitionTest.java @@ -26,6 +26,7 @@ package hudson.model; import hudson.EnvVars; import static org.junit.Assert.*; + import hudson.Launcher; import hudson.model.RunParameterDefinition.RunParameterFilter; import hudson.tasks.BuildStepMonitor; -- GitLab From 553ca57a63a6652a88e8ac9fd829bb8567493a21 Mon Sep 17 00:00:00 2001 From: James Nord Date: Fri, 23 Mar 2018 14:21:17 +0000 Subject: [PATCH 007/424] Adding test coverage for BuildAuthorisationToken I could not see any test coverage for BuildAuthorisationToken so added some. --- .../model/BuildAuthorizationTokenTest.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 test/src/test/java/hudson/model/BuildAuthorizationTokenTest.java diff --git a/test/src/test/java/hudson/model/BuildAuthorizationTokenTest.java b/test/src/test/java/hudson/model/BuildAuthorizationTokenTest.java new file mode 100644 index 0000000000..d5cee18557 --- /dev/null +++ b/test/src/test/java/hudson/model/BuildAuthorizationTokenTest.java @@ -0,0 +1,80 @@ +package hudson.model; + +import java.lang.reflect.Field; +import java.net.URL; +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; +import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.WebRequest; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockAuthorizationStrategy; +import jenkins.model.Jenkins; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +public class BuildAuthorizationTokenTest { + + @Rule + public JenkinsRule jr = new JenkinsRule(); + + private static final String token = "whatever"; + + @Before + public void setupSecurity() { + jr.jenkins.setSecurityRealm(jr.createDummySecurityRealm()); + jr.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy() + .grant(Jenkins.READ).everywhere().toEveryone() + .grant(Item.READ).everywhere().toEveryone()); + } + + + @Test + public void triggerJobWithTokenShouldSucceedWithPost() throws Exception { + FreeStyleProject project = createFreestyleProjectWithToken(); + JenkinsRule.WebClient wc = jr.createWebClient(); + HtmlPage page = wc.getPage(wc.addCrumb(new WebRequest(new URL(jr.getURL(), project.getUrl() + + "build?delay=0&token="+token) + ,HttpMethod.POST))); + jr.waitUntilNoActivity(); + assertThat("the project should have been built", project.getBuilds(), hasSize(1)); + } + + @Test + public void triggerJobWithTokenShouldSucceedWithGet() throws Exception { + FreeStyleProject project = createFreestyleProjectWithToken(); + JenkinsRule.WebClient wc = jr.createWebClient(); + HtmlPage page = wc.getPage(new WebRequest(new URL(jr.getURL(), project.getUrl() + "build?delay=0&token=" + token) + ,HttpMethod.GET)); + jr.waitUntilNoActivity(); + assertThat("the project should have been built", project.getBuilds(), hasSize(1)); + } + + + @Test + public void triggerJobsWithoutTokenShouldFail() throws Exception { + FreeStyleProject project = jr.createFreeStyleProject(); + JenkinsRule.WebClient wc = jr.createWebClient(); + try { + HtmlPage page = wc.getPage(wc.addCrumb( + new WebRequest(new URL(jr.getURL(), project.getUrl() + "build?delay=0"), HttpMethod.POST))); + fail("should not reach here as anonymous does not have Item.BUILD and token is not set"); + } + catch (FailingHttpStatusCodeException fex) { + assertThat("Should fail with access denined", fex.getStatusCode(), is(403)); + } + } + + private FreeStyleProject createFreestyleProjectWithToken() throws Exception { + FreeStyleProject fsp = jr.createFreeStyleProject(); + Field f = AbstractProject.class.getDeclaredField("authToken"); + f.setAccessible(true); + f.set(fsp, new BuildAuthorizationToken(token)); + return fsp; + } +} -- GitLab From e7cc06e7458303913498779af2c2fa3353f4dab1 Mon Sep 17 00:00:00 2001 From: aviadatsnyk Date: Sun, 6 May 2018 16:44:17 +0300 Subject: [PATCH 008/424] handle absolute paths correctly when unzipping When checking that an unzipped file does not break out of the target directory - this handles '..' in absolute paths correctly, where the previous implementation might not. --- core/src/main/java/hudson/FilePath.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 0bcc31c32b..089d8f8ff8 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -598,7 +598,7 @@ public final class FilePath implements Serializable { while (entries.hasMoreElements()) { ZipEntry e = entries.nextElement(); File f = new File(dir, e.getName()); - if (!f.toPath().normalize().startsWith(dir.toPath())) { + if (!f.getCanonicalPath().startsWith(dir.getCanonicalPath())) { throw new IOException( "Zip " + zipFile.getPath() + " contains illegal file name that breaks out of the target directory: " + e.getName()); } -- GitLab From 434135ef200d42de526207841afcfc2a5627b336 Mon Sep 17 00:00:00 2001 From: Jenn Briden Date: Mon, 14 May 2018 22:04:47 -0700 Subject: [PATCH 009/424] Changed references to "slave" and "slaves" to "agent" and "agents" --- core/src/main/java/hudson/Util.java | 2 +- .../main/java/hudson/slaves/ChannelPinger.java | 2 +- .../main/java/hudson/slaves/SlaveComputer.java | 4 ++-- core/src/main/java/jenkins/model/Jenkins.java | 6 +++--- .../model/AbstractItem/help-slaveAffinity.html | 2 +- .../slaves/JNLPLauncher/main_pt_BR.properties | 2 +- .../model/Jenkins/systemInfo_bg.properties | 2 +- .../jenkins/security/s2m/filepath-filter.conf | 8 ++++---- .../description.properties | 2 +- .../description_bg.properties | 2 +- core/src/test/java/hudson/LauncherTest.java | 2 +- core/src/test/java/hudson/model/JobTest.java | 4 ++-- .../bugs/JnlpAccessWithSecuredHudsonTest.java | 2 +- .../java/hudson/cli/UpdateNodeCommandTest.java | 2 +- test/src/test/java/hudson/model/ProjectTest.java | 6 +++--- test/src/test/java/hudson/model/QueueTest.java | 2 +- test/src/test/java/hudson/model/ViewTest.java | 4 ++-- .../hudson/model/labels/LabelExpressionTest.java | 4 ++-- .../EnvironmentVariableNodePropertyTest.java | 10 +++++----- .../hudson/tasks/EnvVarsInConfigTasksTest.java | 16 ++++++++-------- .../java/hudson/util/ProcessTreeKillerTest.java | 2 +- .../model/UnlabeledLoadStatisticsTest.java | 2 +- .../java/jenkins/security/Security218Test.java | 8 ++++---- .../security/s2m/AdminFilePathFilterTest.java | 2 +- test/src/test/resources/hudson/model/node.xml | 2 +- .../master-slave/availability_ja.html | 12 ++++++------ .../help/system-config/master-slave/clock.html | 2 +- .../system-config/master-slave/jnlp-tunnel.html | 6 +++--- .../system-config/master-slave/jnlpSecurity.html | 6 +++--- .../system-config/master-slave/numExecutors.html | 2 +- 30 files changed, 64 insertions(+), 64 deletions(-) diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index 4053ce915e..69344e0ea5 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -1721,7 +1721,7 @@ public class Util { * Warning: This should only ever be used if you find that your builds are * failing because Jenkins is unable to delete files, that this failure is * because Jenkins itself has those files locked "open", and even then it - * should only be used on slaves with relatively few executors (because the + * should only be used on agents with relatively few executors (because the * garbage collection can impact the performance of all job executors on * that slave).
* i.e. Setting this flag is a act of last resort - it is not diff --git a/core/src/main/java/hudson/slaves/ChannelPinger.java b/core/src/main/java/hudson/slaves/ChannelPinger.java index 85d6d78675..ef0701227a 100644 --- a/core/src/main/java/hudson/slaves/ChannelPinger.java +++ b/core/src/main/java/hudson/slaves/ChannelPinger.java @@ -42,7 +42,7 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Establish a periodic ping to keep connections between {@link Slave slaves} + * Establish a periodic ping to keep connections between {@link Agent agent} * and the main Jenkins node alive. This prevents network proxies from * terminating connections that are idle for too long. * diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index c1b5166e71..332ab0aaca 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -860,7 +860,7 @@ public class SlaveComputer extends Computer { public Void call() { SLAVE_LOG_HANDLER = new RingBufferLogHandler(ringBufferSize); - // avoid double installation of the handler. JNLP slaves can reconnect to the master multiple times + // avoid double installation of the handler. JNLP agents can reconnect to the master multiple times // and each connection gets a different RemoteClassLoader, so we need to evict them by class name, // not by their identity. for (Handler h : LOGGER.getHandlers()) { @@ -877,7 +877,7 @@ public class SlaveComputer extends Computer { } try { - getChannelOrFail().setProperty("slave",Boolean.TRUE); // indicate that this side of the channel is the slave side. + getChannelOrFail().setProperty("slave",Boolean.TRUE); // indicate that this side of the channel is the agent side. } catch (ChannelClosedException e) { throw new IllegalStateException(e); } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index a7734f503d..1a19c88bf6 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3521,17 +3521,17 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve private void _cleanUpShutdownTcpSlaveAgent(List errors) { if(tcpSlaveAgentListener!=null) { - LOGGER.log(FINE, "Shutting down TCP/IP slave agent listener"); + LOGGER.log(FINE, "Shutting down TCP/IP agent listener"); try { tcpSlaveAgentListener.shutdown(); } catch (OutOfMemoryError e) { // we should just propagate this, no point trying to log throw e; } catch (LinkageError e) { - LOGGER.log(SEVERE, "Failed to shut down TCP/IP slave agent listener", e); + LOGGER.log(SEVERE, "Failed to shut down TCP/IP agent listener", e); // safe to ignore and continue for this one } catch (Throwable e) { - LOGGER.log(SEVERE, "Failed to shut down TCP/IP slave agent listener", e); + LOGGER.log(SEVERE, "Failed to shut down TCP/IP agent listener", e); // save for later errors.add(e); } diff --git a/core/src/main/resources/hudson/model/AbstractItem/help-slaveAffinity.html b/core/src/main/resources/hudson/model/AbstractItem/help-slaveAffinity.html index 61dd733294..e120ea844b 100644 --- a/core/src/main/resources/hudson/model/AbstractItem/help-slaveAffinity.html +++ b/core/src/main/resources/hudson/model/AbstractItem/help-slaveAffinity.html @@ -1,5 +1,5 @@
- By default, builds of this project may be executed on any build agents that + By default, builds of this project may be executed on any agents that are available and configured to accept new builds.

When this option is checked, you have the possibility to ensure that builds of diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/main_pt_BR.properties b/core/src/main/resources/hudson/slaves/JNLPLauncher/main_pt_BR.properties index af9f3c2747..d5538a43a9 100644 --- a/core/src/main/resources/hudson/slaves/JNLPLauncher/main_pt_BR.properties +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/main_pt_BR.properties @@ -21,7 +21,7 @@ # THE SOFTWARE. launch\ agent=Lan\u00e7ar agente -# TCP port for JNLP slave agents is disabled. +# The TCP port for the JNLP agents is disabled. slaveAgentPort.disabled=Porta TCP para JNLP est\u00e1 desativada Connected\ via\ JNLP\ agent.=Conectado via JNLP agente. # Go to security configuration screen and change it diff --git a/core/src/main/resources/jenkins/model/Jenkins/systemInfo_bg.properties b/core/src/main/resources/jenkins/model/Jenkins/systemInfo_bg.properties index bf6faaac09..c3b52a1303 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/systemInfo_bg.properties +++ b/core/src/main/resources/jenkins/model/Jenkins/systemInfo_bg.properties @@ -26,7 +26,7 @@ Name=\ \u0418\u043c\u0435 No\ plugins\ installed.=\ \u041d\u0435 \u0441\u0430 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0438 \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0438 -# Visit this page for master and slave thread dumps. +# Visit this page for master and agent thread dumps. threadDump_blurb=\ \u041d\u0430 \u0442\u0430\u0437\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 f\u0441\u0430 \u0434\u0430\u0434\u0435\u043d\u0438 \u0440\u0430\u0437\u0442\u043e\u0432\u0430\u0440\u0432\u0430\u043d\u0438\u044f\u0442\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u0438\u0442\u0435 \u0438\ \u043f\u043e\u0434\u0447\u0438\u043d\u0435\u043d\u0438 \u043d\u0438\u0448\u043a\u0438. diff --git a/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf b/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf index 0905bd0ce8..ca58e15ecf 100644 --- a/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf +++ b/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf @@ -1,7 +1,7 @@ # GENERATED FILE. DO NOT MODIFY. # # This file is for Jenkins core developers to list what we think are the best filtering rules -# for apparently harmless accesses to files on the Jenkins master from slaves. +# for apparently harmless accesses to files on the Jenkins master from agents. # # To override these rules, place *.conf files by other names into this folder. Files are sorted # before parsed, so using a lower number allows you to override what we have here. This file @@ -10,12 +10,12 @@ # See https://jenkins.io/redirect/security-144 for more details. # This directory contains credentials, master encryption keys, and other sensitive information -# that slaves have absolutely no business with. +# that agents have absolutely no business with. # Unless there are rules in other files allowing access to other portions of $JENKINS_HOME, # this rule as it stands here has no effect, because anything left unspecified is rejected. deny all /secrets($|/.*) -# User content is publicly readable, so quite safe for slaves to read, too. +# User content is publicly readable, so quite safe for agents to read, too. # (The xunit plugin is known to read from here.) # https://jenkins.io/redirect/user-content-directory allow read,stat /userContent($|/.*) @@ -28,7 +28,7 @@ deny all /program.dat deny all /workflow($|/.*) # Various plugins read/write files under build directories, so allow them all. -# - git 1.x writes changelog.xml from the slave (2.x writes from the master so need not be listed) +# - git 1.x writes changelog.xml from the agent (2.x writes from the master so need not be listed) # - analysis-core and plugins based on it write reports to workspace-files/ # - cobertura writes coverage.xml # - violations writes violations.xml and other content under violations/ diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol/description.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol/description.properties index 3422f15553..f4a0f9073c 100644 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol/description.properties +++ b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol/description.properties @@ -1,2 +1,2 @@ -summary=Accepts connections from remote clients so that they can be used as additional build agents. \ +summary=Accepts connections from remote clients so that they can be used as additional agents. \ This protocol is unencrypted. diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol/description_bg.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol/description_bg.properties index c19dc4a111..4e35de871c 100644 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol/description_bg.properties +++ b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol/description_bg.properties @@ -23,7 +23,7 @@ Accepts\ connections\ from\ remote\ clients\ so\ that\ they\ can\ be\ used\ as\ additional\ build\ agents=\ \u041f\u0440\u0438\u0435\u043c\u0430\u043d\u0435 \u043d\u0430 \u0432\u0440\u044a\u0437\u043a\u0438 \u043e\u0442 \u043e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0438, \u0442\u0430\u043a\u0430 \u0447\u0435 \u0442\u0435 \u0434\u0430 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442 \u043a\u0430\u0442\u043e\ \u0434\u043e\u043f\u044a\u043b\u043d\u0438\u0442\u0435\u043b\u043d\u0438 \u043c\u0430\u0448\u0438\u043d\u0438 \u0437\u0430 \u0438\u0437\u0433\u0440\u0430\u0436\u0434\u0430\u043d\u0435. -# Accepts connections from remote clients so that they can be used as additional build agents. \ +# Accepts connections from remote clients so that they can be used as additional agents. \ # This protocol is unencrypted. summary=\ \u041f\u0440\u0438\u0435\u043c\u0430\u043d\u0435 \u043d\u0430 \u0432\u0440\u044a\u0437\u043a\u0438 \u043e\u0442 \u043e\u0442\u0434\u0430\u043b\u0435\u0447\u0435\u043d\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0438, \u0442\u0430\u043a\u0430 \u0447\u0435 \u0442\u0435 \u0434\u0430 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442 \u043a\u0430\u0442\u043e\ diff --git a/core/src/test/java/hudson/LauncherTest.java b/core/src/test/java/hudson/LauncherTest.java index 4df308b41d..5e2114aede 100644 --- a/core/src/test/java/hudson/LauncherTest.java +++ b/core/src/test/java/hudson/LauncherTest.java @@ -73,7 +73,7 @@ public class LauncherTest { // returns immediately and pgrep sleep => nothing. But without fix // hudson.model.Hudson.instance.nodes[0].rootPath.createLauncher(new hudson.util.StreamTaskListener(System.err)). // launch().cmds("sleep", "1d").stdout(System.out).stderr(System.err).start().kill() - // hangs and on slave machine pgrep sleep => one process; after manual kill, script returns. + // hangs and on agent machine pgrep sleep => one process; after manual kill, script returns. } private static final Callable NOOP = new MasterToSlaveCallable() { diff --git a/core/src/test/java/hudson/model/JobTest.java b/core/src/test/java/hudson/model/JobTest.java index 44a1f07e39..c950ec57c9 100644 --- a/core/src/test/java/hudson/model/JobTest.java +++ b/core/src/test/java/hudson/model/JobTest.java @@ -47,7 +47,7 @@ public class JobTest { @Issue("JENKINS-14807") @Test public void use_slave_platform_path_separator_when_contribute_path() throws Throwable { - // mock environment to simulate EnvVars of slave node with different platform than master + // mock environment to simulate EnvVars of agent node with different platform than master Platform slavePlatform = Platform.current() == Platform.UNIX ? Platform.WINDOWS : Platform.UNIX; PowerMockito.mockStatic(Platform.class); Mockito.when(Platform.current()).thenReturn(slavePlatform); @@ -78,7 +78,7 @@ public class JobTest { String path = "/test"; env.override("PATH+TEST", path); - assertThat("The contributed PATH was not joined using the path separator defined in slave node", // + assertThat("The contributed PATH was not joined using the path separator defined in agent node", // env.get("PATH"), // CoreMatchers.containsString(path + (slavePlatform == Platform.WINDOWS ? ';' : ':'))); } diff --git a/test/src/test/java/hudson/bugs/JnlpAccessWithSecuredHudsonTest.java b/test/src/test/java/hudson/bugs/JnlpAccessWithSecuredHudsonTest.java index 8e0ce19006..923f1f2a1d 100644 --- a/test/src/test/java/hudson/bugs/JnlpAccessWithSecuredHudsonTest.java +++ b/test/src/test/java/hudson/bugs/JnlpAccessWithSecuredHudsonTest.java @@ -75,7 +75,7 @@ public class JnlpAccessWithSecuredHudsonTest { public TemporaryFolder tmp = new TemporaryFolder(); /** - * Creates a new slave that needs to be launched via JNLP. + * Creates a new agent that needs to be launched via JNLP. */ protected Slave createNewJnlpSlave(String name) throws Exception { return new DumbSlave(name,"",System.getProperty("java.io.tmpdir")+'/'+name,"2", Mode.NORMAL, "", new JNLPLauncher(true), RetentionStrategy.INSTANCE, Collections.EMPTY_LIST); diff --git a/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java b/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java index ec5b4843f6..efbee5715f 100644 --- a/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java +++ b/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java @@ -78,7 +78,7 @@ public class UpdateNodeCommandTest { assertThat(result, succeededSilently()); - assertThat("A slave with old name should not exist", j.jenkins.getNode("MySlave"), nullValue()); + assertThat("An agent with old name should not exist", j.jenkins.getNode("MySlave"), nullValue()); final Node updatedSlave = j.jenkins.getNode("SlaveFromXML"); assertThat(updatedSlave.getNodeName(), equalTo("SlaveFromXML")); diff --git a/test/src/test/java/hudson/model/ProjectTest.java b/test/src/test/java/hudson/model/ProjectTest.java index f966f4e56b..5a35b101f3 100644 --- a/test/src/test/java/hudson/model/ProjectTest.java +++ b/test/src/test/java/hudson/model/ProjectTest.java @@ -728,7 +728,7 @@ public class ProjectTest { } /** - * Job is restricted, but label can not be provided by any cloud, only normal slaves. Then job will not submit, because no slave is available. + * Job is restricted, but label can not be provided by any cloud, only normal agents. Then job will not submit, because no slave is available. * @throws Exception */ @Test @@ -748,12 +748,12 @@ public class ProjectTest { j.buildAndAssertSuccess(proj); //Now create another slave. And restrict the job to that slave. The slave is offline, leaving the job with no assignable nodes. - //We tell our mock SCM to return that it has got changes. But since there are no slaves, we get the desired result. + //We tell our mock SCM to return that it has got changes. But since there are no agents, we get the desired result. Slave s2 = j.createSlave(); proj.setAssignedLabel(s2.getSelfLabel()); requiresWorkspaceScm.hasChange = true; - //Poll (We now should have NO online slaves, this should now return NO_CHANGES. + //Poll (We now should have NO online agents, this should now return NO_CHANGES. PollingResult pr = proj.poll(j.createTaskListener()); assertFalse(pr.hasChanges()); diff --git a/test/src/test/java/hudson/model/QueueTest.java b/test/src/test/java/hudson/model/QueueTest.java index bc18c08e5f..11f1b68b50 100644 --- a/test/src/test/java/hudson/model/QueueTest.java +++ b/test/src/test/java/hudson/model/QueueTest.java @@ -624,7 +624,7 @@ public class QueueTest { * and then introduce a security restriction to prohibit that. */ @Test public void permissionSensitiveSlaveAllocations() throws Exception { - r.jenkins.setNumExecutors(0); // restrict builds to those slaves + r.jenkins.setNumExecutors(0); // restrict builds to those agents DumbSlave s1 = r.createSlave(); DumbSlave s2 = r.createSlave(); diff --git a/test/src/test/java/hudson/model/ViewTest.java b/test/src/test/java/hudson/model/ViewTest.java index 4de9409993..d8216575b2 100644 --- a/test/src/test/java/hudson/model/ViewTest.java +++ b/test/src/test/java/hudson/model/ViewTest.java @@ -348,11 +348,11 @@ public class ViewTest { view2.add(foreignJob); foreignJob.setAssignedLabel(j.jenkins.getLabel("label0||label1")); - // contains all slaves having labels associated with freestyleJob or matrixJob + // contains all agents having labels associated with freestyleJob or matrixJob assertContainsNodes(view1, slave0, slave1, slave2, slave3); assertNotContainsNodes(view1, slave4); - // contains all slaves having labels associated with foreignJob + // contains all agents having labels associated with foreignJob assertContainsNodes(view2, slave0, slave1, slave3); assertNotContainsNodes(view2, slave2, slave4); diff --git a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java index 7cb41a5943..4a84caaf5e 100644 --- a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java +++ b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java @@ -75,7 +75,7 @@ public class LabelExpressionTest { FreeStyleProject p1 = j.createFreeStyleProject(); p1.getBuildersList().add(new TestBuilder() { public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { - seq.phase(0); // first, make sure the w32 slave is occupied + seq.phase(0); // first, make sure the w32 agent is occupied seq.phase(2); seq.done(); return true; @@ -91,7 +91,7 @@ public class LabelExpressionTest { Future f1 = p1.scheduleBuild2(0); - seq.phase(1); // we schedule p2 build after w32 slave is occupied + seq.phase(1); // we schedule p2 build after w32 agent is occupied Future f2 = p2.scheduleBuild2(0); Thread.sleep(1000); // time window to ensure queue has tried to assign f2 build diff --git a/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java b/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java index e2ab922e8d..070ca28457 100644 --- a/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java +++ b/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java @@ -22,7 +22,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; /** * This class tests that environment variables from node properties are applied, - * and that the priority is maintained: parameters > slave node properties > + * and that the priority is maintained: parameters > agent node properties > * master node properties */ public class EnvironmentVariableNodePropertyTest extends HudsonTestCase { @@ -31,7 +31,7 @@ public class EnvironmentVariableNodePropertyTest extends HudsonTestCase { private FreeStyleProject project; /** - * Slave properties are available + * Agent properties are available */ public void testSlavePropertyOnSlave() throws Exception { setVariables(slave, new Entry("KEY", "slaveValue")); @@ -53,7 +53,7 @@ public class EnvironmentVariableNodePropertyTest extends HudsonTestCase { } /** - * Both slave and master properties are available, but slave properties have priority + * Both agent and master properties are available, but agent properties have priority */ public void testSlaveAndMasterPropertyOnSlave() throws Exception { jenkins.getGlobalNodeProperties().replaceBy( @@ -67,8 +67,8 @@ public class EnvironmentVariableNodePropertyTest extends HudsonTestCase { } /** - * Slave and master properties and parameters are available. - * Priority: parameters > slave > master + * Agent and master properties and parameters are available. + * Priority: parameters > agent > master * @throws Exception */ public void testSlaveAndMasterPropertyAndParameterOnSlave() diff --git a/test/src/test/java/hudson/tasks/EnvVarsInConfigTasksTest.java b/test/src/test/java/hudson/tasks/EnvVarsInConfigTasksTest.java index 22d16c43b5..d9488bad52 100644 --- a/test/src/test/java/hudson/tasks/EnvVarsInConfigTasksTest.java +++ b/test/src/test/java/hudson/tasks/EnvVarsInConfigTasksTest.java @@ -78,7 +78,7 @@ public class EnvVarsInConfigTasksTest extends HudsonTestCase { project.setScm(new ExtractResourceSCM(getClass().getResource( "/simple-projects.zip"))); - // test the regular slave - variable not expanded + // test the regular agent - variable not expanded project.setAssignedLabel(slaveRegular.getSelfLabel()); FreeStyleBuild build = project.scheduleBuild2(0).get(); System.out.println(build.getDisplayName() + " completed"); @@ -89,7 +89,7 @@ public class EnvVarsInConfigTasksTest extends HudsonTestCase { System.out.println(buildLogRegular); assertTrue(buildLogRegular.contains(DUMMY_LOCATION_VARNAME)); - // test the slave with prepared environment + // test the agent with prepared environment project.setAssignedLabel(slaveEnv.getSelfLabel()); build = project.scheduleBuild2(0).get(); System.out.println(build.getDisplayName() + " completed"); @@ -119,7 +119,7 @@ public class EnvVarsInConfigTasksTest extends HudsonTestCase { new Ant("-Dtest.property=cor${" + DUMMY_LOCATION_VARNAME + "}rect", "varAnt", "", buildFile, "")); - // test the regular slave - variable not expanded + // test the regular agent - variable not expanded project.setAssignedLabel(slaveRegular.getSelfLabel()); FreeStyleBuild build = project.scheduleBuild2(0).get(); System.out.println(build.getDisplayName() + " completed"); @@ -129,7 +129,7 @@ public class EnvVarsInConfigTasksTest extends HudsonTestCase { String buildLogRegular = getBuildLog(build); assertTrue(buildLogRegular.contains(Ant_ExecutableNotFound("varAnt"))); - // test the slave with prepared environment + // test the agent with prepared environment project.setAssignedLabel(slaveEnv.getSelfLabel()); build = project.scheduleBuild2(0).get(); System.out.println(build.getDisplayName() + " completed"); @@ -158,7 +158,7 @@ public class EnvVarsInConfigTasksTest extends HudsonTestCase { + DUMMY_LOCATION_VARNAME + "}", "", "", false)); - // test the regular slave - variable not expanded + // test the regular agent - variable not expanded project.setAssignedLabel(slaveRegular.getSelfLabel()); FreeStyleBuild build = project.scheduleBuild2(0).get(); System.out.println(build.getDisplayName() + " completed"); @@ -169,7 +169,7 @@ public class EnvVarsInConfigTasksTest extends HudsonTestCase { System.out.println(buildLogRegular); assertTrue(buildLogRegular.contains(DUMMY_LOCATION_VARNAME)); - // test the slave with prepared environment + // test the agent with prepared environment project.setAssignedLabel(slaveEnv.getSelfLabel()); build = project.scheduleBuild2(0).get(); System.out.println(build.getDisplayName() + " completed"); @@ -191,7 +191,7 @@ public class EnvVarsInConfigTasksTest extends HudsonTestCase { project.setMaven("varMaven"); project.setGoals("clean${" + DUMMY_LOCATION_VARNAME + "}"); - // test the regular slave - variable not expanded + // test the regular agent - variable not expanded project.setAssignedLabel(slaveRegular.getSelfLabel()); MavenModuleSetBuild build = project.scheduleBuild2(0).get(); System.out.println(build.getDisplayName() + " completed"); @@ -201,7 +201,7 @@ public class EnvVarsInConfigTasksTest extends HudsonTestCase { String buildLogRegular = getBuildLog(build); System.out.println(buildLogRegular); - // test the slave with prepared environment + // test the agent with prepared environment project.setAssignedLabel(slaveEnv.getSelfLabel()); build = project.scheduleBuild2(0).get(); System.out.println(build.getDisplayName() + " completed"); diff --git a/test/src/test/java/hudson/util/ProcessTreeKillerTest.java b/test/src/test/java/hudson/util/ProcessTreeKillerTest.java index 5382c6f134..43d376e095 100644 --- a/test/src/test/java/hudson/util/ProcessTreeKillerTest.java +++ b/test/src/test/java/hudson/util/ProcessTreeKillerTest.java @@ -158,7 +158,7 @@ public class ProcessTreeKillerTest { pb.command("sleep", "5m"); } - // Create a slave so we can tell it to kill the process + // Create an agent so we can tell it to kill the process Slave s = j.createSlave(); s.toComputer().connect(false).get(); diff --git a/test/src/test/java/jenkins/model/UnlabeledLoadStatisticsTest.java b/test/src/test/java/jenkins/model/UnlabeledLoadStatisticsTest.java index 41bb8100a2..d0711adf59 100644 --- a/test/src/test/java/jenkins/model/UnlabeledLoadStatisticsTest.java +++ b/test/src/test/java/jenkins/model/UnlabeledLoadStatisticsTest.java @@ -61,7 +61,7 @@ public class UnlabeledLoadStatisticsTest { assertEquals("Queue must be empty when the test starts", 0, queue.getBuildableItems().size()); assertEquals("Statistics must return 0 when the test starts", 0, unlabeledLoad.computeQueueLength()); - // Disable builds by default, create a slave to prevent assigning of "master" labels + // Disable builds by default, create an agent to prevent assigning of "master" labels j.jenkins.setNumExecutors(0); DumbSlave slave = j.createOnlineSlave(new LabelAtom("testLabel")); slave.setMode(Node.Mode.EXCLUSIVE); diff --git a/test/src/test/java/jenkins/security/Security218Test.java b/test/src/test/java/jenkins/security/Security218Test.java index b3cfc83d43..2259c41a39 100644 --- a/test/src/test/java/jenkins/security/Security218Test.java +++ b/test/src/test/java/jenkins/security/Security218Test.java @@ -67,7 +67,7 @@ public class Security218Test implements Serializable { } /** - * The attack scenario here is that a master sends a normal command to a slave and a slave + * The attack scenario here is that a master sends a normal command to an agent and a slave * inserts a malicious response. */ @SuppressWarnings("ConstantConditions") @@ -88,7 +88,7 @@ public class Security218Test implements Serializable { // TODO: reconcile this duplicate with JnlpAccessWithSecuredHudsonTest /** - * Creates a new slave that needs to be launched via JNLP. + * Creates a new agent that needs to be launched via JNLP. * * @see #launchJnlpSlave(Slave) */ @@ -100,7 +100,7 @@ public class Security218Test implements Serializable { // TODO: reconcile this duplicate with JnlpAccessWithSecuredHudsonTest /** - * Launch a JNLP slave created by {@link #createJnlpSlave(String)} + * Launch a JNLP agent created by {@link #createJnlpSlave(String)} */ public Channel launchJnlpSlave(Slave slave) throws Exception { j.createWebClient().goTo("computer/"+slave.getNodeName()+"/slave-agent.jnlp?encrypt=true", "application/octet-stream"); @@ -124,7 +124,7 @@ public class Security218Test implements Serializable { Thread.sleep(100); } - throw new AssertionError("JNLP slave agent failed to connect"); + throw new AssertionError("The JNLP agent failed to connect"); } @After diff --git a/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java b/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java index 37af2825c0..73777e731b 100644 --- a/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java +++ b/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java @@ -175,7 +175,7 @@ public class AdminFilePathFilterTest { } private void checkSlave_can_readFile(Slave s, FilePath target) throws Exception { - // slave can read file from userContent + // The agent can read file from userContent String content = s.getChannel().call(new ReadFileS2MCallable(target)); // and the master can directly reach it assertEquals(target.readToString(), content); diff --git a/test/src/test/resources/hudson/model/node.xml b/test/src/test/resources/hudson/model/node.xml index 0a7827642f..acf86684a4 100644 --- a/test/src/test/resources/hudson/model/node.xml +++ b/test/src/test/resources/hudson/model/node.xml @@ -1,7 +1,7 @@ SlaveFromXML - XML slave description + XML agent description /user/hudson/workspace 42 NORMAL diff --git a/war/src/main/webapp/help/system-config/master-slave/availability_ja.html b/war/src/main/webapp/help/system-config/master-slave/availability_ja.html index fc897d3090..9d42f48bb4 100644 --- a/war/src/main/webapp/help/system-config/master-slave/availability_ja.html +++ b/war/src/main/webapp/help/system-config/master-slave/availability_ja.html @@ -12,15 +12,15 @@

diff --git a/war/src/main/webapp/help/system-config/master-slave/clock.html b/war/src/main/webapp/help/system-config/master-slave/clock.html index 2c064e58aa..04176edd84 100644 --- a/war/src/main/webapp/help/system-config/master-slave/clock.html +++ b/war/src/main/webapp/help/system-config/master-slave/clock.html @@ -1,5 +1,5 @@
Many aspects of a build are sensitive to the clock, and therefore if the clock of the machine - that Jenkins runs and that of the slave differ significantly, it can cause mysterious problems. + that Jenkins runs and that of the agent differ significantly, it can cause mysterious problems. Consider synchronizing clocks between machines by NTP.
diff --git a/war/src/main/webapp/help/system-config/master-slave/jnlp-tunnel.html b/war/src/main/webapp/help/system-config/master-slave/jnlp-tunnel.html index 3c2b46412f..e67bf39334 100644 --- a/war/src/main/webapp/help/system-config/master-slave/jnlp-tunnel.html +++ b/war/src/main/webapp/help/system-config/master-slave/jnlp-tunnel.html @@ -1,5 +1,5 @@
- When a slave is launched through JNLP, the slave agent attempts to connect to a specific TCP port of Jenkins + When an agent is launched through JNLP, the agent agent attempts to connect to a specific TCP port of Jenkins to establish a communication channel. But some security sensitive network can prevent you from making this connection. This can also happen when Jenkins runs behind a load balancer, @@ -9,8 +9,8 @@

This tunneling option allows you to route this connection to another host/port, and useful for those situations. The field can either take "HOST:PORT", ":PORT", or "HOST:". In the first format, - JNLP slave agent will connect to the given TCP port on the given host, and assume that you've configured your - network so that this port forwards the connection to Jenkins' JNLP slave TCP port. + JNLP agent will connect to the given TCP port on the given host, and assume that you've configured your + network so that this port forwards the connection to Jenkins' JNLP agent TCP port.

In the latter two formats, the default host name and port number (that is, the host name that Jenkins runs, diff --git a/war/src/main/webapp/help/system-config/master-slave/jnlpSecurity.html b/war/src/main/webapp/help/system-config/master-slave/jnlpSecurity.html index 3e9492c429..04e54ddc10 100644 --- a/war/src/main/webapp/help/system-config/master-slave/jnlpSecurity.html +++ b/war/src/main/webapp/help/system-config/master-slave/jnlpSecurity.html @@ -25,10 +25,10 @@

In this mode, Jenkins provides the JNLP launch link on the computer view. - The link will always be available whenever the slave is offline. + The link will always be available whenever the agent is offline.
WARNING! In this mode security is disabled. Thus slaves can be started by anyone - that can access this server. Slaves have the ability to execute arbitrary code on the + that can access this server. Agents have the ability to execute arbitrary code on the master. Do not select this option unless you are fully aware of the risks
@@ -38,7 +38,7 @@
In this mode, Jenkins provides the JNLP launch link on both the computer view and the Build Executor Status sidepanel. - The link will always be available whenever the slave is offline. + The link will always be available whenever the agent is offline. diff --git a/war/src/main/webapp/help/system-config/master-slave/numExecutors.html b/war/src/main/webapp/help/system-config/master-slave/numExecutors.html index fc4c879803..da09a380ad 100644 --- a/war/src/main/webapp/help/system-config/master-slave/numExecutors.html +++ b/war/src/main/webapp/help/system-config/master-slave/numExecutors.html @@ -9,6 +9,6 @@ for I/O.

- Setting this value to 0 is useful to remove a disabled slave from Jenkins temporarily without + Setting this value to 0 is useful to remove a disabled agent from Jenkins temporarily without losing other configuration information.

\ No newline at end of file -- GitLab From 2a39a8410178f3793b36878369dbf296ec63821d Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 15 May 2018 11:09:01 -0400 Subject: [PATCH 010/424] =?UTF-8?q?If=20we=20are=20moments=20away=20from?= =?UTF-8?q?=20exiting=20the=20quiet=20period,=20we=20might=20as=20well=20c?= =?UTF-8?q?laim=20we=20are=20in=20it,=20because=20otherwise=20we=20just=20?= =?UTF-8?q?say=20=C2=AF\=5F(=E3=83=84)=5F/=C2=AF=20which=20is=20less=20hel?= =?UTF-8?q?pful.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/src/main/java/hudson/model/Queue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java index 0938ac9cb0..e065bd4f0c 100644 --- a/core/src/main/java/hudson/model/Queue.java +++ b/core/src/main/java/hudson/model/Queue.java @@ -2487,7 +2487,7 @@ public class Queue extends ResourceController implements Saveable { public CauseOfBlockage getCauseOfBlockage() { long diff = timestamp.getTimeInMillis() - System.currentTimeMillis(); - if (diff > 0) + if (diff >= 0) return CauseOfBlockage.fromMessage(Messages._Queue_InQuietPeriod(Util.getTimeSpanString(diff))); else return CauseOfBlockage.fromMessage(Messages._Queue_Unknown()); -- GitLab From 8a91a0bd375c7c3bc731a62ae94648d5082f0b00 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 21 Aug 2018 14:41:54 -0400 Subject: [PATCH 011/424] JENKINS-53077 - don't process empty responseText The goal of this function appears to be to replace one content tree with another content tree. If the response is empty, that doesn't result in a content tree, and thus one can't use replaceChild. --- war/src/main/webapp/scripts/hudson-behavior.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index a05dea7c90..3a1d8a9c13 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -1515,6 +1515,10 @@ function refreshPart(id,url) { window.clearInterval(intervalID); return; } + if (!rsp.responseText) { + console.log("Failed to retrieve response for ID " + id + ", perhaps Jenkins is unavailable"); + return; + } var p = hist.up(); var div = document.createElement('div'); -- GitLab From 2f168f531b30fccb152b265e052e94b88eed5f78 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Fri, 14 Sep 2018 13:25:16 -0400 Subject: [PATCH 012/424] markdown --- CONTRIBUTING.md | 56 +++++++++++++++++++++++++++++++------------------ README.md | 2 +- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d419bd03a9..ab038c69f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ This page provides information about contributing code to the Jenkins core codebase. -:exclamation: There's a lot more to the Jenkins project than just code. For information on contributing to the Jenkins project overall, check out https://jenkins.io/participate/. +:exclamation: There's a lot more to the Jenkins project than just code. For information on contributing to the Jenkins project overall, check out [Participate]. ## Getting started @@ -10,32 +10,32 @@ This page provides information about contributing code to the Jenkins core codeb 2. Clone the forked repository to your machine 3. Install the development tools. In order to develop Jenkins, you need the following tools: * Java Development Kit (JDK) 8. - - In Jenkins project we usually use [OpenJDK](http://openjdk.java.net/), + - In Jenkins project we usually use [OpenJDK], but you can use other JDKs as well. - - Java 9 is **not supported** in Jenkins. - * Maven 3.5.3 or above. You can download it [here](https://maven.apache.org/download.cgi) - * Any IDE which supports importing Maven projects -4. Setup your development environment as described in [Preparing for Plugin Development](https://jenkins.io/doc/developer/tutorial/prepare/) + - Java 9+ is **not supported** in Jenkins. + * Maven 3.5.3 or above. You can [download maven]. + * Any IDE which supports importing Maven projects. +4. Setup your development environment as described in [Preparing for Plugin Development] If you want to contribute to Jenkins or just learn about the project, you can start by fixing some easier issues. In the Jenkins issue tracker we mark such issues as `newbie-friendly`. You can find them -using [this query](https://issues.jenkins-ci.org/issues/?jql=project%20%3D%20JENKINS%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20component%20%3D%20core%20AND%20labels%20in%20(newbie-friendly)). +using this query for [newbie friendly issues]. ## Building and Debugging The build flow for Jenkins core is built around Maven. -Building and debugging process is described [here](https://jenkins.io/doc/developer/building/). +There is a description of the [building and debugging process]. If you want simply to have the `jenkins.war` file as fast as possible without tests, run: mvn clean package -pl war -am -DskipTests -Dfindbugs.skip The WAR file will be created in `war/target/jenkins.war`. -After that you can start Jenkins using Java CLI ([guide](https://wiki.jenkins.io/display/JENKINS/Starting+and+Accessing+Jenkins)). +After that you can start Jenkins using Java CLI ([guide]). If you want to debug this WAR file without using Maven plugins, -You can just start the executable with [Remote Debug Flags](https://stackoverflow.com/questions/975271/remote-debugging-a-java-application) +You can just start the executable with [Remote Debug Flags] and then attach IDE Debugger to it. ## Testing changes @@ -47,7 +47,7 @@ Most of the tests will be launched by the continuous integration instance, so there is no strict need to run full test suites before proposing a pull request. In addition to the included tests, you can also find extra integration and UI -tests in the [Acceptance Test Harness (ATH)](https://github.com/jenkinsci/acceptance-test-harness) repository. +tests in the [Acceptance Test Harness (ATH)] repository. If you propose complex UI changes, you should create new ATH tests for them. ## Proposing Changes @@ -63,8 +63,8 @@ It is a good practice is to create branches instead of pushing to master. 3. Select `jenkinsci` as _base fork_ and `master` as `base`, then click _Create Pull Request_ * We integrate all changes into the master branch towards the Weekly releases * After that the changes may be backported to the current LTS baseline by the LTS Team. - The backporting process is described [here](https://jenkins.io/download/lts/). -4. Fill in the Pull Request description according to the [proposed template](.github/PULL_REQUEST_TEMPLATE.md). + Read more about the [backporting process] +4. Fill in the Pull Request description according to the [proposed template]. 5. Click _Create Pull Request_ 6. Wait for CI results/reviews, process the feedback. * If you do not get feedback after 3 days, feel free to ping `@jenkinsci/code-reviewers` to CC. @@ -77,25 +77,25 @@ There is no additional action required from pull request authors at this point. ## Copyright -Jenkins core is licensed under [MIT license](./LICENSE.txt), with a few exceptions in bundled classes. +Jenkins core is licensed under [MIT license], with a few exceptions in bundled classes. We consider all contributions as MIT unless it's explicitly stated otherwise. MIT-incompatible code contributions will be rejected. Contributions under MIT-compatible licenses may be also rejected if they are not ultimately necessary. -We **Do NOT** require pull request submitters to sign the [contributor agreement](https://wiki.jenkins.io/display/JENKINS/Copyright+on+source+code) +We **Do NOT** require pull request submitters to sign the [contributor agreement] as long as the code is licensed under MIT and merged by one of the contributors with the signed agreement. We still encourage people to sign the contributor agreement if they intend to submit more than a few pull requests. Signing is also a mandatory prerequisite for getting merge/push permissions to core repositories -and for joining teams like [Jenkins Security Team](https://jenkins.io/security/#team). +and for joining teams like [Jenkins Security Team]. ## Continuous Integration The Jenkins project has a Continuous Integration server... powered by Jenkins, of course. -It is located at [ci.jenkins.io](https://ci.jenkins.io/). +It is located at [ci.jenkins.io]. -The Jenkins project uses [Jenkins Pipeline](https://jenkins.io/doc/book/pipeline/) to run builds. -The code for the core build flow is stored in the [Jenkinsfile](./Jenkinsfile) in the repository root. +The Jenkins project uses [Jenkins Pipeline] to run builds. +The code for the core build flow is stored in the [Jenkinsfile] in the repository root. If you want to update that build flow (e.g. "add more checks"), just submit a pull request. @@ -106,4 +106,20 @@ just submit a pull request. * [Beginners Guide To Contributing](https://wiki.jenkins.io/display/JENKINS/Beginners+Guide+to+Contributing) * [List of newbie-friendly issues in the core](https://issues.jenkins-ci.org/issues/?jql=project%20%3D%20JENKINS%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20component%20%3D%20core%20AND%20labels%20in%20(newbie-friendly)) - +[download maven]: https://maven.apache.org/download.cgi +[Preparing for Plugin Development]: https://jenkins.io/doc/developer/tutorial/prepare/ +[newbie friendly issues]: https://issues.jenkins-ci.org/issues/?jql=project%20%3D%20JENKINS%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20component%20%3D%20core%20AND%20labels%20in%20(newbie-friendly) +[OpenJDK]: http://openjdk.java.net/ +[Participate]: https://jenkins.io/participate/ +[building and debugging process]: https://jenkins.io/doc/developer/building/ +[guide]: https://wiki.jenkins.io/display/JENKINS/Starting+and+Accessing+Jenkins +[Remote Debug Flags]: https://stackoverflow.com/questions/975271/remote-debugging-a-java-application +[Acceptance Test Harness (ATH)]: https://github.com/jenkinsci/acceptance-test-harness +[backporting process]: https://jenkins.io/download/lts/ +[proposed template]: .github/PULL_REQUEST_TEMPLATE.md +[MIT license]: ./LICENSE.txt +[contributor agreement]: https://wiki.jenkins.io/display/JENKINS/Copyright+on+source+code +[Jenkins Security Team]: https://jenkins.io/security/#team +[ci.jenkins.io]: https://ci.jenkins.io/ +[Jenkins Pipeline]: https://jenkins.io/doc/book/pipeline/ +[Jenkinsfile]: ./Jenkinsfile \ No newline at end of file diff --git a/README.md b/README.md index 6dc6c47ece..7ef6c08a8e 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Non-source downloads such as WAR files and several Linux packages can be found o Our latest and greatest source of Jenkins can be found on [GitHub]. Fork us! # Contributing to Jenkins -Follow [contributing](CONTRIBUTING.md) file. +Follow the [contributing](CONTRIBUTING.md) file. # News and Website All information about Jenkins can be found on our [website]. Follow us on Twitter [@jenkinsci]. -- GitLab From 3fa7427c1764fb8531157da16557757b67a1a49e Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Mon, 11 Sep 2017 02:42:34 +0200 Subject: [PATCH 013/424] [JENKINS-20679] Add support for plugin Java requirement metadata --- core/src/main/java/hudson/PluginWrapper.java | 23 ++++++++ .../main/java/hudson/model/UpdateSite.java | 54 +++++++++++++++++++ .../main/resources/hudson/Messages.properties | 13 ++--- .../resources/hudson/PluginManager/_table.css | 3 ++ .../hudson/PluginManager/table.jelly | 6 +++ .../hudson/PluginManager/table.properties | 7 +++ 6 files changed, 100 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index 363d588f0a..75322ab32a 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -457,6 +457,20 @@ public class PluginWrapper implements Comparable, ModelObject { return null; } + /** + * Returns the required Java version of this plugin. + * @return the required Java version of this plugin. + * + * @since TODO + */ + @Exported + public @CheckForNull String getRequiredJavaVersion() { + String v = manifest.getMainAttributes().getValue("Minimum-Java-Version"); + if (v!= null) return v; + + return null; + } + /** * Returns the version number of this plugin */ @@ -600,6 +614,15 @@ public class PluginWrapper implements Comparable, ModelObject { versionDependencyError(Messages.PluginWrapper_obsoleteCore(Jenkins.getVersion().toString(), requiredCoreVersion), Jenkins.getVersion().toString(), requiredCoreVersion); } } + + String requiredJavaVersion = getRequiredJavaVersion(); + if (requiredJavaVersion != null) { + // TODO replace with calls to Runtime.version() once we're on Java 9 + VersionNumber actualVersion = new VersionNumber(System.getProperty("java.version")); + if (actualVersion.isOlderThan(new VersionNumber(requiredJavaVersion))) { + dependencyErrors.add(Messages.PluginWrapper_obsoleteJava(actualVersion.toString(), requiredJavaVersion)); + } + } } // make sure dependencies exist for (Dependency d : dependencies) { diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index 0a27890a12..b3ff312768 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -948,6 +948,13 @@ public class UpdateSite { */ @Exported public final String requiredCore; + /** + * Version of Java this plugin requires to run. + * + * @since TODO + */ + @Exported + public final String minimumJavaVersion; /** * Categories for grouping plugins, taken from labels assigned to wiki page. * Can be null. @@ -974,6 +981,8 @@ public class UpdateSite { this.title = get(o,"title"); this.excerpt = get(o,"excerpt"); this.compatibleSinceVersion = get(o,"compatibleSinceVersion"); + this.minimumJavaVersion = get(o,"minimumJavaVersion"); + this.requiredCore = get(o,"requiredCore"); this.categories = o.has("labels") ? (String[])o.getJSONArray("labels").toArray(new String[0]) : null; for(Object jo : o.getJSONArray("dependencies")) { @@ -1098,6 +1107,19 @@ public class UpdateSite { } } + /** + * @since TODO + * @return + */ + public boolean isForNewerJava() { + try { + return minimumJavaVersion != null && new VersionNumber(minimumJavaVersion).isNewerThan( + new VersionNumber(System.getProperty("java.version"))); + } catch (NumberFormatException nfe) { + return false; // plugin doesn't declare a minimum Java version + } + } + public VersionNumber getNeededDependenciesRequiredCore() { VersionNumber versionNumber = null; try { @@ -1112,6 +1134,29 @@ public class UpdateSite { return versionNumber; } + /** + * @since TODO + * @return + */ + public VersionNumber getNeededDependenciesRequiredJava() { + VersionNumber versionNumber = null; + try { + versionNumber = minimumJavaVersion == null ? null : new VersionNumber(minimumJavaVersion); + } catch (NumberFormatException nfe) { + // unable to parse version + } + for (Plugin p: getNeededDependencies()) { + VersionNumber v = p.getNeededDependenciesRequiredJava(); + if (v == null) { + continue; + } + if (versionNumber == null || v.isNewerThan(versionNumber)) { + versionNumber = v; + } + } + return versionNumber; + } + public boolean isNeededDependenciesForNewerJenkins() { return isNeededDependenciesForNewerJenkins(new PluginManager.MetadataCache()); } @@ -1128,6 +1173,15 @@ public class UpdateSite { }); } + public boolean isNeededDependenciesForNewerJava() { + for (Plugin p: getNeededDependencies()) { + if (p.isForNewerJava() || p.isNeededDependenciesForNewerJava()) { + return true; + } + } + return false; + } + /** * If at least some of the plugin's needed dependencies are already installed, and the new version of the * needed dependencies plugin have a "compatibleSinceVersion" diff --git a/core/src/main/resources/hudson/Messages.properties b/core/src/main/resources/hudson/Messages.properties index 80ecb8db83..1666f15cc5 100644 --- a/core/src/main/resources/hudson/Messages.properties +++ b/core/src/main/resources/hudson/Messages.properties @@ -75,13 +75,14 @@ ProxyConfiguration.Success=Success Functions.NoExceptionDetails=No Exception details -PluginWrapper.missing={0} v{1} is missing. To fix, install v{1} or later. -PluginWrapper.failed_to_load_plugin={0} v{1} failed to load. -PluginWrapper.failed_to_load_dependency={0} v{1} failed to load. Fix this plugin first. -PluginWrapper.disabledAndObsolete={0} v{1} is disabled and older than required. To fix, install v{2} or later and enable it. +PluginWrapper.missing={0} version {1} is missing. To fix, install version {1} or later. +PluginWrapper.failed_to_load_plugin={0} version {1} failed to load. +PluginWrapper.failed_to_load_dependency={0} version {1} failed to load. Fix this plugin first. +PluginWrapper.disabledAndObsolete={0} version {1} is disabled and older than required. To fix, install version {2} or later and enable it. PluginWrapper.disabled={0} is disabled. To fix, enable it. -PluginWrapper.obsolete={0} v{1} is older than required. To fix, install v{2} or later. -PluginWrapper.obsoleteCore=You must update Jenkins from v{0} to v{1} or later to run this plugin. +PluginWrapper.obsolete={0} version {1} is older than required. To fix, install version {2} or later. +PluginWrapper.obsoleteCore=You must update Jenkins from version {0} to version {1} or later to run this plugin. +PluginWrapper.obsoleteJava=You must update Java from version {0} to version {1} or later to run this plugin. PluginWrapper.PluginWrapperAdministrativeMonitor.DisplayName=Plugins Failed To Load TcpSlaveAgentListener.PingAgentProtocol.displayName=Ping protocol diff --git a/core/src/main/resources/hudson/PluginManager/_table.css b/core/src/main/resources/hudson/PluginManager/_table.css index 86125969bb..90b80cfd19 100644 --- a/core/src/main/resources/hudson/PluginManager/_table.css +++ b/core/src/main/resources/hudson/PluginManager/_table.css @@ -8,3 +8,6 @@ margin: 1em; text-align: right; } +.compatWarning, .securityWarning { + font-weight: bold; +} \ No newline at end of file diff --git a/core/src/main/resources/hudson/PluginManager/table.jelly b/core/src/main/resources/hudson/PluginManager/table.jelly index 26dafbc2fc..1480b90742 100644 --- a/core/src/main/resources/hudson/PluginManager/table.jelly +++ b/core/src/main/resources/hudson/PluginManager/table.jelly @@ -106,12 +106,18 @@ THE SOFTWARE.
${%coreWarning(p.requiredCore)}
+ +
${%javaWarning(p.minimumJavaVersion)}
+
${%depCompatWarning}
${%depCoreWarning(p.getNeededDependenciesRequiredCore().toString())}
+ +
${%depJavaWarning(p.getNeededDependenciesRequiredJava().toString())}
+
${%securityWarning}
    diff --git a/core/src/main/resources/hudson/PluginManager/table.properties b/core/src/main/resources/hudson/PluginManager/table.properties index 63c161534c..7d2edec8a6 100644 --- a/core/src/main/resources/hudson/PluginManager/table.properties +++ b/core/src/main/resources/hudson/PluginManager/table.properties @@ -26,6 +26,9 @@ compatWarning=\ coreWarning=\ Warning: This plugin is built for Jenkins {0} or newer. \ Jenkins will refuse to load this plugin if installed. +javaWarning=\ + Warning: This plugin requires Java {0} or newer. \ + Jenkins will refuse to load this plugin if installed. depCompatWarning=\ Warning: This plugin requires dependent plugins be upgraded and at least one of these dependent plugins claims to use a different settings format than the installed version. \ Jobs using that plugin may need to be reconfigured, and/or you may not be able to cleanly revert to the prior version without manually restoring old settings. \ @@ -34,6 +37,10 @@ depCoreWarning=\ Warning: This plugin requires dependent plugins that require Jenkins {0} or newer. \ Jenkins will refuse to load the dependent plugins requiring a newer version of Jenkins, \ and in turn loading this plugin will fail. +depJavaWarning=\ + Warning: This plugin requires dependent plugins that require Java {0} or newer. \ + Jenkins will refuse to load the dependent plugins requiring a newer version of Jenkins, \ + and in turn loading this plugin will fail. securityWarning=\ Warning: This plugin version may not be safe to use. Please review the following security notices: -- GitLab From e65ceca2f8a6bf09b5028d2e1967405fdb038e23 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Mon, 11 Sep 2017 03:57:32 +0200 Subject: [PATCH 014/424] Rename 'minimumJavaVersion' to 'requiredJava' --- core/src/main/java/hudson/model/UpdateSite.java | 8 ++++---- core/src/main/resources/hudson/PluginManager/table.jelly | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index b3ff312768..34a8c04b2d 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -954,7 +954,7 @@ public class UpdateSite { * @since TODO */ @Exported - public final String minimumJavaVersion; + public final String requiredJava; /** * Categories for grouping plugins, taken from labels assigned to wiki page. * Can be null. @@ -981,7 +981,7 @@ public class UpdateSite { this.title = get(o,"title"); this.excerpt = get(o,"excerpt"); this.compatibleSinceVersion = get(o,"compatibleSinceVersion"); - this.minimumJavaVersion = get(o,"minimumJavaVersion"); + this.requiredJava = get(o,"requiredJava"); this.requiredCore = get(o,"requiredCore"); this.categories = o.has("labels") ? (String[])o.getJSONArray("labels").toArray(new String[0]) : null; @@ -1113,7 +1113,7 @@ public class UpdateSite { */ public boolean isForNewerJava() { try { - return minimumJavaVersion != null && new VersionNumber(minimumJavaVersion).isNewerThan( + return requiredJava != null && new VersionNumber(requiredJava).isNewerThan( new VersionNumber(System.getProperty("java.version"))); } catch (NumberFormatException nfe) { return false; // plugin doesn't declare a minimum Java version @@ -1141,7 +1141,7 @@ public class UpdateSite { public VersionNumber getNeededDependenciesRequiredJava() { VersionNumber versionNumber = null; try { - versionNumber = minimumJavaVersion == null ? null : new VersionNumber(minimumJavaVersion); + versionNumber = requiredJava == null ? null : new VersionNumber(requiredJava); } catch (NumberFormatException nfe) { // unable to parse version } diff --git a/core/src/main/resources/hudson/PluginManager/table.jelly b/core/src/main/resources/hudson/PluginManager/table.jelly index 1480b90742..4667cee418 100644 --- a/core/src/main/resources/hudson/PluginManager/table.jelly +++ b/core/src/main/resources/hudson/PluginManager/table.jelly @@ -107,7 +107,7 @@ THE SOFTWARE.
    ${%coreWarning(p.requiredCore)}
    -
    ${%javaWarning(p.minimumJavaVersion)}
    +
    ${%javaWarning(p.requiredJava)}
    ${%depCompatWarning}
    -- GitLab From d1c972a055aff07b5cd5476d647d35405375e8f5 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Mon, 11 Sep 2017 20:25:42 +0200 Subject: [PATCH 015/424] Adapt tests to changed localizable strings --- core/src/test/java/hudson/PluginWrapperTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/hudson/PluginWrapperTest.java b/core/src/test/java/hudson/PluginWrapperTest.java index e15c803dca..bc93164ff9 100644 --- a/core/src/test/java/hudson/PluginWrapperTest.java +++ b/core/src/test/java/hudson/PluginWrapperTest.java @@ -53,7 +53,7 @@ public class PluginWrapperTest { pw.resolvePluginDependencies(); fail(); } catch (IOException ex) { - assertContains(ex, "fake v42 failed to load", "update Jenkins from v2.0 to v3.0"); + assertContains(ex, "fake version 42 failed to load", "update Jenkins from version 2.0 to version 3.0"); } } @@ -64,7 +64,7 @@ public class PluginWrapperTest { pw.resolvePluginDependencies(); fail(); } catch (IOException ex) { - assertContains(ex, "dependee v42 failed to load", "dependency v42 is missing. To fix, install v42 or later"); + assertContains(ex, "dependee version 42 failed to load", "dependency version 42 is missing. To fix, install version 42 or later"); } } @@ -76,7 +76,7 @@ public class PluginWrapperTest { pw.resolvePluginDependencies(); fail(); } catch (IOException ex) { - assertContains(ex, "dependee v42 failed to load", "dependency v3 is older than required. To fix, install v5 or later"); + assertContains(ex, "dependee version 42 failed to load", "dependency version 3 is older than required. To fix, install version 5 or later"); } } @@ -88,7 +88,7 @@ public class PluginWrapperTest { pw.resolvePluginDependencies(); fail(); } catch (IOException ex) { - assertContains(ex, "dependee v42 failed to load", "dependency v5 failed to load. Fix this plugin first"); + assertContains(ex, "dependee version 42 failed to load", "dependency version 5 failed to load. Fix this plugin first"); } } -- GitLab From 725fb3026bf288709b07a6dc555eb2cadf1b07cd Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Mon, 11 Sep 2017 20:30:08 +0200 Subject: [PATCH 016/424] Update Javadocs and comments --- core/src/main/java/hudson/PluginManager.java | 2 ++ core/src/main/java/hudson/model/UpdateSite.java | 17 +++++++++++++++-- .../resources/hudson/PluginManager/_table.css | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index be83e07456..fdee127447 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -1719,6 +1719,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas if (toInstall.isForNewerHudson()) { LOGGER.log(WARNING, "{0}@{1} was built for a newer Jenkins", new Object[] {toInstall.name, toInstall.version}); } + // TODO check Java dependency jobs.add(toInstall.deploy(true)); } else if (pw.isOlderThan(requestedPlugin.getValue())) { // upgrade UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey()); @@ -1736,6 +1737,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas if (toInstall.isForNewerHudson()) { LOGGER.log(WARNING, "{0}@{1} was built for a newer Jenkins", new Object[] {toInstall.name, toInstall.version}); } + // TODO check Java dependency if (!toInstall.isCompatibleWithInstalledVersion()) { LOGGER.log(WARNING, "{0}@{1} is incompatible with the installed @{2}", new Object[] {toInstall.name, toInstall.version, pw.getVersion()}); } diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index 34a8c04b2d..0812aa7c44 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -1108,8 +1108,11 @@ public class UpdateSite { } /** + * Returns true iff the plugin declares a minimum Java version and it's newer than what the Jenkins master is running on. + * + * @return true iff the plugin declares a minimum Java version and it's newer than what the Jenkins master is running on. + * * @since TODO - * @return */ public boolean isForNewerJava() { try { @@ -1135,8 +1138,11 @@ public class UpdateSite { } /** + * Returns the minimum Java version needed to use the plugin and all its dependencies. + * + * @return the minimum Java version needed to use the plugin and all its dependencies. + * * @since TODO - * @return */ public VersionNumber getNeededDependenciesRequiredJava() { VersionNumber versionNumber = null; @@ -1173,6 +1179,13 @@ public class UpdateSite { }); } + /** + * Returns true iff this plugin or any of its dependencies require a newer Java than Jenkins is running on. + * + * @return true iff this plugin or any of its dependencies require a newer Java than Jenkins is running on. + * + * @since TODO + */ public boolean isNeededDependenciesForNewerJava() { for (Plugin p: getNeededDependencies()) { if (p.isForNewerJava() || p.isNeededDependenciesForNewerJava()) { diff --git a/core/src/main/resources/hudson/PluginManager/_table.css b/core/src/main/resources/hudson/PluginManager/_table.css index 90b80cfd19..9107c2be8b 100644 --- a/core/src/main/resources/hudson/PluginManager/_table.css +++ b/core/src/main/resources/hudson/PluginManager/_table.css @@ -10,4 +10,4 @@ } .compatWarning, .securityWarning { font-weight: bold; -} \ No newline at end of file +} -- GitLab From f55864fe4973f310059c62437c3b9b0b30bb9d91 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Mon, 11 Sep 2017 20:35:48 +0200 Subject: [PATCH 017/424] Add Java version check to setup wizard and installNecessaryPlugins I'm not sure what the log message is supposed to be for, but other checks also log, so apply the same behavior for Java dependency. --- core/src/main/java/hudson/PluginManager.java | 8 ++++++-- core/src/main/java/jenkins/install/SetupWizard.java | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index fdee127447..dcf02ccd27 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -1719,7 +1719,9 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas if (toInstall.isForNewerHudson()) { LOGGER.log(WARNING, "{0}@{1} was built for a newer Jenkins", new Object[] {toInstall.name, toInstall.version}); } - // TODO check Java dependency + if (toInstall.isForNewerJava()) { + LOGGER.log(WARNING, "{0}@{1} was built for a newer Java", new Object[] {toInstall.name, toInstall.version}); + } jobs.add(toInstall.deploy(true)); } else if (pw.isOlderThan(requestedPlugin.getValue())) { // upgrade UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey()); @@ -1737,7 +1739,9 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas if (toInstall.isForNewerHudson()) { LOGGER.log(WARNING, "{0}@{1} was built for a newer Jenkins", new Object[] {toInstall.name, toInstall.version}); } - // TODO check Java dependency + if (toInstall.isForNewerJava()) { + LOGGER.log(WARNING, "{0}@{1} was built for a newer Java", new Object[] {toInstall.name, toInstall.version}); + } if (!toInstall.isCompatibleWithInstalledVersion()) { LOGGER.log(WARNING, "{0}@{1} is incompatible with the installed @{2}", new Object[] {toInstall.name, toInstall.version, pw.getVersion()}); } diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index 3ae5a0ea24..30214ea4bd 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -500,7 +500,7 @@ public class SetupWizard extends PageDecorator { for (UpdateSite site : jenkins.getUpdateCenter().getSiteList()) { UpdateSite.Plugin sitePlug = site.getPlugin(pluginName); if (sitePlug != null - && !sitePlug.isForNewerHudson() + && !sitePlug.isForNewerHudson() && !sitePlug.isForNewerJava() && !sitePlug.isNeededDependenciesForNewerJenkins()) { foundCompatibleVersion = true; break; -- GitLab From 5cdbf39d9c5faa406ea4d0bf6ed2b51c17b93fd8 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 20 Sep 2018 13:44:08 -0700 Subject: [PATCH 018/424] Use java.specification.version --- core/src/main/java/hudson/PluginWrapper.java | 9 +++------ core/src/main/java/hudson/model/UpdateSite.java | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index 75322ab32a..ef73feea6f 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -465,10 +465,7 @@ public class PluginWrapper implements Comparable, ModelObject { */ @Exported public @CheckForNull String getRequiredJavaVersion() { - String v = manifest.getMainAttributes().getValue("Minimum-Java-Version"); - if (v!= null) return v; - - return null; + return manifest.getMainAttributes().getValue("Minimum-Java-Version"); } /** @@ -617,8 +614,8 @@ public class PluginWrapper implements Comparable, ModelObject { String requiredJavaVersion = getRequiredJavaVersion(); if (requiredJavaVersion != null) { - // TODO replace with calls to Runtime.version() once we're on Java 9 - VersionNumber actualVersion = new VersionNumber(System.getProperty("java.version")); + // TODO replace with calls to Runtime.version() once we're on Java 9+ + VersionNumber actualVersion = new VersionNumber(System.getProperty("java.specification.version")); if (actualVersion.isOlderThan(new VersionNumber(requiredJavaVersion))) { dependencyErrors.add(Messages.PluginWrapper_obsoleteJava(actualVersion.toString(), requiredJavaVersion)); } diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index 0812aa7c44..b1950c8131 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -1117,7 +1117,7 @@ public class UpdateSite { public boolean isForNewerJava() { try { return requiredJava != null && new VersionNumber(requiredJava).isNewerThan( - new VersionNumber(System.getProperty("java.version"))); + new VersionNumber(System.getProperty("java.specification.version"))); } catch (NumberFormatException nfe) { return false; // plugin doesn't declare a minimum Java version } -- GitLab From e364a84ee553a9aa0288ef2c2a1effd113503ed6 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 20 Sep 2018 13:46:54 -0700 Subject: [PATCH 019/424] Adapt to newer core internals --- core/src/main/java/hudson/PluginWrapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index ef73feea6f..9e5ee42f32 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -617,7 +617,7 @@ public class PluginWrapper implements Comparable, ModelObject { // TODO replace with calls to Runtime.version() once we're on Java 9+ VersionNumber actualVersion = new VersionNumber(System.getProperty("java.specification.version")); if (actualVersion.isOlderThan(new VersionNumber(requiredJavaVersion))) { - dependencyErrors.add(Messages.PluginWrapper_obsoleteJava(actualVersion.toString(), requiredJavaVersion)); + versionDependencyError(Messages.PluginWrapper_obsoleteJava(actualVersion.toString(), requiredJavaVersion), actualVersion.toString(), requiredJavaVersion); } } } -- GitLab From e11217be5574c3ed64ae75874346940808dbdd96 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 8 Oct 2018 11:26:47 -0700 Subject: [PATCH 020/424] [maven-release-plugin] prepare release jenkins-2.138.2 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index d54dac5918..53952fff5b 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.2 cli diff --git a/core/pom.xml b/core/pom.xml index ce5f2b00f1..1013d2690a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.2 jenkins-core diff --git a/pom.xml b/pom.xml index a7f887c22c..1dfa979405 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.2 pom Jenkins main module @@ -59,7 +59,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - ${scmTag} + jenkins-2.138.2 diff --git a/test/pom.xml b/test/pom.xml index 4f9dae71fa..cd700b8b5e 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.2 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index a2a1e2502a..2c3cb40f55 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.2 jenkins-war -- GitLab From dacd550921d7f9bc3ec5dec614bfd0b7faf3f6eb Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 8 Oct 2018 11:26:54 -0700 Subject: [PATCH 021/424] [maven-release-plugin] prepare for next development iteration --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 6 +++--- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 53952fff5b..d54dac5918 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.138.2 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 1013d2690a..ce5f2b00f1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.138.2 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index 1dfa979405..27b38be197 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.138.2 + ${revision}${changelist} pom Jenkins main module @@ -59,7 +59,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.138.2 + ${scmTag} @@ -75,7 +75,7 @@ THE SOFTWARE. - 2.138.2 + 2.138.3 -SNAPSHOT + ${%Create an account! [Jenkins]} @@ -292,6 +293,11 @@ 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..32e7585385 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/login.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/login.jelly @@ -50,6 +50,7 @@ THE SOFTWARE. + @@ -145,8 +146,9 @@ THE SOFTWARE. -- GitLab From 1cc16a5a30a2731081380d53c955dc10058ed738 Mon Sep 17 00:00:00 2001 From: charanb1r Date: Thu, 8 Nov 2018 15:57:03 -0800 Subject: [PATCH 032/424] JENKINS-53284 Fix warning message for changing workspace directories --- core/src/main/java/jenkins/model/Jenkins.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index c5ccf765f3..d53d81f926 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3033,9 +3033,16 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } String newWorkspacesDir = SystemProperties.getString(WORKSPACES_DIR_PROP); - if (newWorkspacesDir != null && !workspaceDir.equals(newWorkspacesDir)) { - LOGGER.log(Level.WARNING, "Changing workspaces directories from {0} to {1}. Beware that no automated data migration will occur.", - new String[]{workspaceDir, newWorkspacesDir}); + if (newWorkspacesDir != null) { + Level level; + if(!workspaceDir.equals(newWorkspacesDir)) { + level = Level.INFO; + } + else { + level = Level.WARNING; + } + LOGGER.log(level, "Changing workspaces directories from {0} to {1}. Beware that no automated data migration will occur.", + new String[]{workspaceDir, newWorkspacesDir}); workspaceDir = newWorkspacesDir; mustSave = true; } else if (!isDefaultWorkspaceDir()) { -- GitLab From f7c10b8ca51eba3c91799c74c918bfd408485b7c Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 8 Nov 2018 16:57:42 -0800 Subject: [PATCH 033/424] [maven-release-plugin] prepare release jenkins-2.138.3 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index d54dac5918..7ab2118d3f 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.3 cli diff --git a/core/pom.xml b/core/pom.xml index 0886950dfe..3a2a44950e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.3 jenkins-core diff --git a/pom.xml b/pom.xml index 27b38be197..b27bfc5220 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.3 pom Jenkins main module @@ -59,7 +59,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - ${scmTag} + jenkins-2.138.3 diff --git a/test/pom.xml b/test/pom.xml index 4f9dae71fa..59823fae4b 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.3 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index a2a1e2502a..d9dd140625 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.138.3 jenkins-war -- GitLab From e8d38bc4e90518d7a06c5e78a431aa38ecba9e03 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 8 Nov 2018 16:57:49 -0800 Subject: [PATCH 034/424] [maven-release-plugin] prepare for next development iteration --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 6 +++--- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 7ab2118d3f..d54dac5918 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.138.3 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 3a2a44950e..0886950dfe 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.138.3 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index b27bfc5220..e7dde3ccfa 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.138.3 + ${revision}${changelist} pom Jenkins main module @@ -59,7 +59,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.138.3 + ${scmTag} @@ -75,7 +75,7 @@ THE SOFTWARE. - 2.138.3 + 2.138.4 -SNAPSHOT + none + true + + + 11 + + diff --git a/src/findbugs/findbugs-excludes.xml b/src/findbugs/findbugs-excludes.xml index 781617524e..f8e3d3061a 100644 --- a/src/findbugs/findbugs-excludes.xml +++ b/src/findbugs/findbugs-excludes.xml @@ -10,6 +10,9 @@ + + + diff --git a/test/pom.xml b/test/pom.xml index fb587c3640..696e338cbd 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -53,7 +53,7 @@ THE SOFTWARE. ${project.groupId} jenkins-test-harness - 2.36 + 2.41.1 test -- GitLab From 32686a3496c9168cce13a66f5c34a104658eda78 Mon Sep 17 00:00:00 2001 From: charanb1r Date: Tue, 13 Nov 2018 10:28:31 -0800 Subject: [PATCH 040/424] JENKINS-53284 Fix warning message for changing workspace directories --- ...insBuildsAndWorkspacesDirectoriesTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java index 54e494dce2..60753e42cc 100644 --- a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java +++ b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java @@ -25,7 +25,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; +import java.util.logging.LogRecord; import java.util.stream.Stream; +import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; @@ -45,6 +47,7 @@ import static org.junit.Assume.assumeFalse; public class JenkinsBuildsAndWorkspacesDirectoriesTest { private static final String LOG_WHEN_CHANGING_BUILDS_DIR = "Changing builds directories from "; + private static final String LOG_WHEN_CHANGING_WORKSPACES_DIR = "Changing workspaces directories from "; @Rule public RestartableJenkinsRule story = new RestartableJenkinsRule(); @@ -66,6 +69,37 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { Stream.of(Jenkins.BUILDS_DIR_PROP, Jenkins.WORKSPACES_DIR_PROP) .forEach(System::clearProperty); } + + @Issue("JENKINS-53284") + @Test + public void changeWorkspacesDirLog() throws Exception { + loggerRule.record(Jenkins.class, Level.WARNING) + .record(Jenkins.class, Level.INFO).capture(1000); + + story.then(step -> { + assertFalse(logWasFound(LOG_WHEN_CHANGING_WORKSPACES_DIR)); + setWorkspacesDirProperty("testdir1"); + }); + + story.then(step -> { + assertTrue(logLevelWasFound(LOG_WHEN_CHANGING_WORKSPACES_DIR, + Level.INFO)); + setWorkspacesDirProperty("testdir2"); + }); + + story.then(step -> { + assertTrue(logLevelWasFound(LOG_WHEN_CHANGING_WORKSPACES_DIR, + Level.WARNING)); + setWorkspacesDirProperty("testdir3"); + }); + + story.then(step -> { + assertTrue(logLevelWasFound(LOG_WHEN_CHANGING_WORKSPACES_DIR, + Level.WARNING)); + }); + + } + @Issue("JENKINS-50164") @Test @@ -258,6 +292,15 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { .anyMatch(record -> record.getMessage().contains(searched)); } + private boolean logLevelWasFound(String searched, Level level) { + return loggerRule + .getRecords() + .stream() + .filter(record -> record.getMessage().contains(searched) + && record.getLevel().equals(level)) + .collect(Collectors.toList()).size() > 0; + } + @Test @Issue("JENKINS-12251") public void testItemFullNameExpansion() throws Exception { -- GitLab From 6cf67767b80b2f458a00eef6842b6f9fb42a43c5 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Wed, 14 Nov 2018 17:28:48 +0100 Subject: [PATCH 041/424] [JENKINS-53716] - Introduce JDK8-only tests, move ysoserial tests there --- pom.xml | 10 + test-jdk8/pom.xml | 40 ++ .../security/Security218BlackBoxTest.java | 0 .../jenkins/security/Security218CliTest.java | 0 .../jenkins/security/Security232Test.java | 0 .../jenkins/security/security218/Payload.java | 0 .../security218/ysoserial/Deserializer.java | 0 .../ExecBlockingSecurityManager.java | 0 .../ysoserial/GeneratePayload.java | 0 .../security218/ysoserial/Serializer.java | 0 .../exploit/JRMPClassLoadingListener.java | 0 .../ysoserial/exploit/JRMPClient.java | 0 .../ysoserial/exploit/JRMPListener.java | 0 .../security218/ysoserial/exploit/JSF.java | 0 .../ysoserial/exploit/JenkinsCLI.java | 0 .../ysoserial/exploit/JenkinsListener.java | 0 .../ysoserial/exploit/JenkinsReverse.java | 0 .../ysoserial/exploit/RMIRegistryExploit.java | 0 .../ysoserial/payloads/CommonsBeanutils1.java | 0 .../payloads/CommonsCollections1.java | 0 .../payloads/CommonsCollections2.java | 0 .../payloads/CommonsCollections3.java | 0 .../payloads/CommonsCollections4.java | 0 .../payloads/CommonsCollections5.java | 0 .../payloads/CommonsCollections6.java | 0 .../payloads/DynamicDependencies.java | 0 .../ysoserial/payloads/FileUpload1.java | 0 .../ysoserial/payloads/Groovy1.java | 0 .../ysoserial/payloads/JRMPClient.java | 0 .../ysoserial/payloads/JRMPListener.java | 0 .../security218/ysoserial/payloads/JSON1.java | 0 .../ysoserial/payloads/Jdk7u21.java | 0 .../payloads/JsonLibSignedObject.java | 0 .../security218/ysoserial/payloads/Ldap.java | 0 .../ysoserial/payloads/ObjectPayload.java | 0 .../payloads/ReleaseableObjectPayload.java | 0 .../ysoserial/payloads/Spring1.java | 0 .../ysoserial/payloads/Spring2.java | 0 .../payloads/annotation/Dependencies.java | 0 .../payloads/annotation/PayloadTest.java | 0 .../ysoserial/payloads/util/ClassFiles.java | 0 .../ysoserial/payloads/util/Gadgets.java | 0 .../ysoserial/payloads/util/JavaVersion.java | 0 .../payloads/util/PayloadRunner.java | 0 .../ysoserial/payloads/util/Reflections.java | 0 .../secmgr/DelegateSecurityManager.java | 0 .../secmgr/ExecCheckingSecurityManager.java | 0 .../secmgr/ThreadLocalSecurityManager.java | 0 .../ysoserial/util/ClassFiles.java | 0 .../security218/ysoserial/util/Gadgets.java | 0 .../ysoserial/util/PayloadRunner.java | 0 .../ysoserial/util/Reflections.java | 0 .../ysoserial/util/Serializables.java | 0 test-pom/pom.xml | 378 ++++++++++++++++++ test/pom.xml | 341 +--------------- 55 files changed, 430 insertions(+), 339 deletions(-) create mode 100644 test-jdk8/pom.xml rename {test => test-jdk8}/src/test/java/jenkins/security/Security218BlackBoxTest.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/Security218CliTest.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/Security232Test.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/Payload.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/Deserializer.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/ExecBlockingSecurityManager.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/GeneratePayload.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/Serializer.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClassLoadingListener.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClient.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPListener.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/exploit/JSF.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsCLI.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsListener.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsReverse.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/exploit/RMIRegistryExploit.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsBeanutils1.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections1.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections2.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections3.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections4.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections5.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections6.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/DynamicDependencies.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/FileUpload1.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/Groovy1.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPClient.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPListener.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/JSON1.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/Jdk7u21.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/JsonLibSignedObject.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/Ldap.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/ObjectPayload.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/ReleaseableObjectPayload.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring1.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring2.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/Dependencies.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/PayloadTest.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/util/ClassFiles.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Gadgets.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/util/JavaVersion.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/util/PayloadRunner.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Reflections.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/secmgr/DelegateSecurityManager.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/secmgr/ExecCheckingSecurityManager.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/secmgr/ThreadLocalSecurityManager.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/util/ClassFiles.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/util/Gadgets.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/util/PayloadRunner.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/util/Reflections.java (100%) rename {test => test-jdk8}/src/test/java/jenkins/security/security218/ysoserial/util/Serializables.java (100%) create mode 100644 test-pom/pom.xml diff --git a/pom.xml b/pom.xml index 6f305512f0..9d8fcba4d1 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ THE SOFTWARE. core war + test-pom test cli @@ -771,5 +772,14 @@ THE SOFTWARE. 11 + + jdk8 + + test-jdk8 + + + 1.8 + + diff --git a/test-jdk8/pom.xml b/test-jdk8/pom.xml new file mode 100644 index 0000000000..6fb4010cf5 --- /dev/null +++ b/test-jdk8/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + + org.jenkins-ci.main + jenkins-test-parent + ${revision}${changelist} + ../test-pom + + + jenkins-test-jdk8 + + JDK8-only tests for Jenkins core + JDK8-only Functional tests for Jenkins core + + diff --git a/test/src/test/java/jenkins/security/Security218BlackBoxTest.java b/test-jdk8/src/test/java/jenkins/security/Security218BlackBoxTest.java similarity index 100% rename from test/src/test/java/jenkins/security/Security218BlackBoxTest.java rename to test-jdk8/src/test/java/jenkins/security/Security218BlackBoxTest.java diff --git a/test/src/test/java/jenkins/security/Security218CliTest.java b/test-jdk8/src/test/java/jenkins/security/Security218CliTest.java similarity index 100% rename from test/src/test/java/jenkins/security/Security218CliTest.java rename to test-jdk8/src/test/java/jenkins/security/Security218CliTest.java diff --git a/test/src/test/java/jenkins/security/Security232Test.java b/test-jdk8/src/test/java/jenkins/security/Security232Test.java similarity index 100% rename from test/src/test/java/jenkins/security/Security232Test.java rename to test-jdk8/src/test/java/jenkins/security/Security232Test.java diff --git a/test/src/test/java/jenkins/security/security218/Payload.java b/test-jdk8/src/test/java/jenkins/security/security218/Payload.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/Payload.java rename to test-jdk8/src/test/java/jenkins/security/security218/Payload.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/Deserializer.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/Deserializer.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/Deserializer.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/Deserializer.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/ExecBlockingSecurityManager.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/ExecBlockingSecurityManager.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/ExecBlockingSecurityManager.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/ExecBlockingSecurityManager.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/GeneratePayload.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/GeneratePayload.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/GeneratePayload.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/GeneratePayload.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/Serializer.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/Serializer.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/Serializer.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/Serializer.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClassLoadingListener.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClassLoadingListener.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClassLoadingListener.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClassLoadingListener.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClient.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClient.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClient.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPClient.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPListener.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPListener.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPListener.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JRMPListener.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/exploit/JSF.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JSF.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/exploit/JSF.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JSF.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsCLI.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsCLI.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsCLI.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsCLI.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsListener.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsListener.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsListener.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsListener.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsReverse.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsReverse.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsReverse.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/JenkinsReverse.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/exploit/RMIRegistryExploit.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/RMIRegistryExploit.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/exploit/RMIRegistryExploit.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/exploit/RMIRegistryExploit.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsBeanutils1.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsBeanutils1.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsBeanutils1.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsBeanutils1.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections1.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections1.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections1.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections1.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections2.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections2.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections2.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections2.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections3.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections3.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections3.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections3.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections4.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections4.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections4.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections4.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections5.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections5.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections5.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections5.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections6.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections6.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections6.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/CommonsCollections6.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/DynamicDependencies.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/DynamicDependencies.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/DynamicDependencies.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/DynamicDependencies.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/FileUpload1.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/FileUpload1.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/FileUpload1.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/FileUpload1.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/Groovy1.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Groovy1.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/Groovy1.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Groovy1.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPClient.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPClient.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPClient.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPClient.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPListener.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPListener.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPListener.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/JRMPListener.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/JSON1.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/JSON1.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/JSON1.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/JSON1.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/Jdk7u21.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Jdk7u21.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/Jdk7u21.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Jdk7u21.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/JsonLibSignedObject.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/JsonLibSignedObject.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/JsonLibSignedObject.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/JsonLibSignedObject.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/Ldap.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Ldap.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/Ldap.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Ldap.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/ObjectPayload.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/ObjectPayload.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/ObjectPayload.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/ObjectPayload.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/ReleaseableObjectPayload.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/ReleaseableObjectPayload.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/ReleaseableObjectPayload.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/ReleaseableObjectPayload.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring1.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring1.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring1.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring1.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring2.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring2.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring2.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/Spring2.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/Dependencies.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/Dependencies.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/Dependencies.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/Dependencies.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/PayloadTest.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/PayloadTest.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/PayloadTest.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/annotation/PayloadTest.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/ClassFiles.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/ClassFiles.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/ClassFiles.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/ClassFiles.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Gadgets.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Gadgets.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Gadgets.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Gadgets.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/JavaVersion.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/JavaVersion.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/JavaVersion.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/JavaVersion.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/PayloadRunner.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/PayloadRunner.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/PayloadRunner.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/PayloadRunner.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Reflections.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Reflections.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Reflections.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/payloads/util/Reflections.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/secmgr/DelegateSecurityManager.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/secmgr/DelegateSecurityManager.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/secmgr/DelegateSecurityManager.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/secmgr/DelegateSecurityManager.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/secmgr/ExecCheckingSecurityManager.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/secmgr/ExecCheckingSecurityManager.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/secmgr/ExecCheckingSecurityManager.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/secmgr/ExecCheckingSecurityManager.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/secmgr/ThreadLocalSecurityManager.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/secmgr/ThreadLocalSecurityManager.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/secmgr/ThreadLocalSecurityManager.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/secmgr/ThreadLocalSecurityManager.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/util/ClassFiles.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/ClassFiles.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/util/ClassFiles.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/ClassFiles.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/util/Gadgets.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/Gadgets.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/util/Gadgets.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/Gadgets.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/util/PayloadRunner.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/PayloadRunner.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/util/PayloadRunner.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/PayloadRunner.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/util/Reflections.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/Reflections.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/util/Reflections.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/Reflections.java diff --git a/test/src/test/java/jenkins/security/security218/ysoserial/util/Serializables.java b/test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/Serializables.java similarity index 100% rename from test/src/test/java/jenkins/security/security218/ysoserial/util/Serializables.java rename to test-jdk8/src/test/java/jenkins/security/security218/ysoserial/util/Serializables.java diff --git a/test-pom/pom.xml b/test-pom/pom.xml new file mode 100644 index 0000000000..100e83563b --- /dev/null +++ b/test-pom/pom.xml @@ -0,0 +1,378 @@ + + + + 4.0.0 + + + org.jenkins-ci.main + jenkins-parent + ${revision}${changelist} + + + jenkins-test-parent + + Jenkins core tests POM + Parent POM for Functional tests for Jenkins core + pom + + + 2 + false + + + + + + ${project.groupId} + jenkins-war + ${project.version} + executable-war + test + + + ${project.groupId} + jenkins-test-harness + 2.41.1 + test + + + ${project.groupId} + jenkins-war + + + + + ${project.groupId} + jenkins-test-harness-tools + 2.0 + test + + + ${project.groupId} + maven-plugin + ${maven-plugin.version} + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpcore + + + commons-codec + commons-codec + + + com.google.inject + guice + + + org.apache.ant + ant + + + + + org.jenkins-ci.plugins + matrix-auth + 1.0.2 + + + org.jenkins-ci.plugins + antisamy-markup-formatter + 1.0 + test + + + org.jenkins-ci.plugins + matrix-project + ${matrix-project.version} + + + org.jenkins-ci.plugins + junit + 1.6 + test + + + org.jenkins-ci.plugins + structs + 1.2 + test + + + org.jvnet.mock-javamail + mock-javamail + 1.7 + + + javax.mail + mail + + + + + org.hamcrest + hamcrest-core + 1.3 + + + xalan + xalan + 2.7.2 + + + xml-apis + xml-apis + + + + + + org.jvnet.hudson + netx + 0.5-hudson-2 + + + org.easymock + easymock + 2.4 + + + org.mockito + mockito-core + test + + + org.reflections + reflections + 0.9.9 + + + com.google.guava + guava + + + com.google.code.findbugs + jsr305 + + + + + org.codehaus.geb + geb-implicit-assertions + 0.7.2 + + + org.javassist + javassist + 3.19.0-GA + test + + + org.apache.commons + commons-collections4 + 4.0 + test + + + org.awaitility + awaitility + 3.0.0 + test + + + org.objenesis + objenesis + test + + + + + + + org.jenkins-ci.tools + maven-hpi-plugin + true + + + org.kohsuke.stapler + maven-stapler-plugin + + true + + + maven-dependency-plugin + + + old-remoting-for-test + generate-test-resources + + + copy + + + + + org.jenkins-ci.main + remoting + ${remoting.minimum.supported.version} + jar + ${project.build.outputDirectory}/old-remoting + remoting-minimal-supported.jar + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${jacocoSurefireArgs} -Dfile.encoding=UTF-8 -Xmx1g -Djdk.net.URLClassPath.disableClassPathURLCheck=true + + + true + ${mavenDebug} + ${project.build.directory} + + false + ${concurrency} + + + + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + + org.apache.maven:maven-embedder + org.codehaus.plexus:plexus-classworlds + org.apache.maven:maven-core + org.apache.maven:maven-aether-provider + org.codehaus.plexus:plexus-utils + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + -XDignore.symbol.file + true + + + + + + + + + light-test + + true + + + + + smoke-test + + jenkins.model.StartupTest,jenkins.model.JenkinsTest,jenkins.install.*,jenkins.security.CustomClassFilterTest,hudson.AboutJenkinsTest,hudson.ClassicPluginStrategyTest,hudson.LauncherTest,hudson.slaves.JNLPLauncherTest,hudson.model.RunTest,hudson.model.UserTest,hudson.model.FreeStyleProjectTest,hudson.model.HudsonTest,hudson.model.ComputerTest,hudson.CLI.BuildCommandTest#sync,*.smokes + + + + all-tests + + + !test + + + + true + 4 + 100 + + + + jacoco + + + + org.jacoco + jacoco-maven-plugin + 0.6.3.201306030806 + + + + pre-unit-test + + prepare-agent + + + + ${project.build.directory}/coverage-reports/jacoco-ut.exec + jacocoSurefireArgs + + + + + post-unit-test + test + + report + + + + ${project.build.directory}/coverage-reports/jacoco-ut.exec + + ${project.reporting.outputDirectory}/jacoco-ut + + + + + + + + + diff --git a/test/pom.xml b/test/pom.xml index 696e338cbd..4c5abecc6f 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -27,8 +27,9 @@ THE SOFTWARE. org.jenkins-ci.main - jenkins-parent + jenkins-test-parent ${revision}${changelist} + ../test-pom jenkins-test @@ -36,342 +37,4 @@ THE SOFTWARE. Tests for Jenkins core Functional tests for Jenkins core - - 2 - false - - - - - - ${project.groupId} - jenkins-war - ${project.version} - executable-war - test - - - ${project.groupId} - jenkins-test-harness - 2.41.1 - test - - - ${project.groupId} - jenkins-war - - - - - ${project.groupId} - jenkins-test-harness-tools - 2.0 - test - - - ${project.groupId} - maven-plugin - ${maven-plugin.version} - - - org.apache.httpcomponents - httpclient - - - org.apache.httpcomponents - httpcore - - - commons-codec - commons-codec - - - com.google.inject - guice - - - org.apache.ant - ant - - - - - org.jenkins-ci.plugins - matrix-auth - 1.0.2 - - - org.jenkins-ci.plugins - antisamy-markup-formatter - 1.0 - test - - - org.jenkins-ci.plugins - matrix-project - ${matrix-project.version} - - - org.jenkins-ci.plugins - junit - 1.6 - test - - - org.jenkins-ci.plugins - structs - 1.2 - test - - - org.jvnet.mock-javamail - mock-javamail - 1.7 - - - javax.mail - mail - - - - - org.hamcrest - hamcrest-core - 1.3 - - - xalan - xalan - 2.7.2 - - - xml-apis - xml-apis - - - - - - org.jvnet.hudson - netx - 0.5-hudson-2 - - - org.easymock - easymock - 2.4 - - - org.mockito - mockito-core - test - - - org.reflections - reflections - 0.9.9 - - - com.google.guava - guava - - - com.google.code.findbugs - jsr305 - - - - - org.codehaus.geb - geb-implicit-assertions - 0.7.2 - - - org.javassist - javassist - 3.19.0-GA - test - - - org.apache.commons - commons-collections4 - 4.0 - test - - - org.awaitility - awaitility - 3.0.0 - test - - - org.objenesis - objenesis - test - - - - - - - org.jenkins-ci.tools - maven-hpi-plugin - true - - - org.kohsuke.stapler - maven-stapler-plugin - - true - - - maven-dependency-plugin - - - old-remoting-for-test - generate-test-resources - - - copy - - - - - org.jenkins-ci.main - remoting - ${remoting.minimum.supported.version} - jar - ${project.build.outputDirectory}/old-remoting - remoting-minimal-supported.jar - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${jacocoSurefireArgs} -Dfile.encoding=UTF-8 -Xmx1g -Djdk.net.URLClassPath.disableClassPathURLCheck=true - - - true - ${mavenDebug} - ${project.build.directory} - - false - ${concurrency} - - - - maven-deploy-plugin - - true - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - - - org.apache.maven:maven-embedder - org.codehaus.plexus:plexus-classworlds - org.apache.maven:maven-core - org.apache.maven:maven-aether-provider - org.codehaus.plexus:plexus-utils - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - -XDignore.symbol.file - true - - - - - - - - - light-test - - true - - - - - smoke-test - - jenkins.model.StartupTest,jenkins.model.JenkinsTest,jenkins.install.*,jenkins.security.CustomClassFilterTest,hudson.AboutJenkinsTest,hudson.ClassicPluginStrategyTest,hudson.LauncherTest,hudson.slaves.JNLPLauncherTest,hudson.model.RunTest,hudson.model.UserTest,hudson.model.FreeStyleProjectTest,hudson.model.HudsonTest,hudson.model.ComputerTest,hudson.CLI.BuildCommandTest#sync,*.smokes - - - - all-tests - - - !test - - - - true - 4 - 100 - - - - jacoco - - - - org.jacoco - jacoco-maven-plugin - 0.6.3.201306030806 - - - - pre-unit-test - - prepare-agent - - - - ${project.build.directory}/coverage-reports/jacoco-ut.exec - jacocoSurefireArgs - - - - - post-unit-test - test - - report - - - - ${project.build.directory}/coverage-reports/jacoco-ut.exec - - ${project.reporting.outputDirectory}/jacoco-ut - - - - - - - - -- GitLab From f79dbe56eecce91975b82d599ea70394cae272e2 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Thu, 15 Nov 2018 12:00:25 -0600 Subject: [PATCH 042/424] Fix jelly variable usage --- .../hudson/security/HudsonPrivateSecurityRealm/signup.jelly | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly index e698166a23..ff5d9ea136 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly @@ -295,7 +295,7 @@ THE SOFTWARE.
-- GitLab From 4aaa6ff92beee69689248f7ccf4ada7b5fa37b4e Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Thu, 15 Nov 2018 12:01:20 -0600 Subject: [PATCH 043/424] Fix other jelly variable --- core/src/main/resources/jenkins/model/Jenkins/login.jelly | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/jenkins/model/Jenkins/login.jelly b/core/src/main/resources/jenkins/model/Jenkins/login.jelly index 32e7585385..7fbe3dc26f 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/login.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/login.jelly @@ -147,7 +147,7 @@ THE SOFTWARE. -- GitLab From a23ce4354d2eb7e8adf2e047038300836d2cca33 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Thu, 15 Nov 2018 14:01:44 -0600 Subject: [PATCH 044/424] Disable frequently flaky test --- core/src/test/java/hudson/UtilTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/test/java/hudson/UtilTest.java b/core/src/test/java/hudson/UtilTest.java index 7aae66de48..f537bc7b9b 100644 --- a/core/src/test/java/hudson/UtilTest.java +++ b/core/src/test/java/hudson/UtilTest.java @@ -57,6 +57,7 @@ import org.apache.commons.io.FileUtils; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.Assume; +import org.junit.Ignore; import org.junit.Test; import org.jvnet.hudson.test.Issue; @@ -352,6 +353,7 @@ public class UtilTest { } @Test + @Ignore("Frequently flaky test") public void testDeleteContentsRecursive_onWindows() throws Exception { Assume.assumeTrue(Functions.isWindows()); final File dir = tmp.newFolder(); -- GitLab From 3d2d1dc2ddec143cc510adcf4e912aaa2ef3e3b0 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Thu, 15 Nov 2018 14:58:16 -0600 Subject: [PATCH 045/424] Add test retry logic Signed-off-by: Matt Sicker --- core/src/test/java/hudson/UtilTest.java | 6 +- core/src/test/java/jenkins/junit/Retry.java | 46 +++++++++++++ .../test/java/jenkins/junit/RetryRule.java | 65 +++++++++++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 core/src/test/java/jenkins/junit/Retry.java create mode 100644 core/src/test/java/jenkins/junit/RetryRule.java diff --git a/core/src/test/java/hudson/UtilTest.java b/core/src/test/java/hudson/UtilTest.java index f537bc7b9b..712151e36d 100644 --- a/core/src/test/java/hudson/UtilTest.java +++ b/core/src/test/java/hudson/UtilTest.java @@ -53,11 +53,12 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.*; +import jenkins.junit.Retry; +import jenkins.junit.RetryRule; import org.apache.commons.io.FileUtils; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.Assume; -import org.junit.Ignore; import org.junit.Test; import org.jvnet.hudson.test.Issue; @@ -74,6 +75,7 @@ import com.google.common.collect.Lists; public class UtilTest { @Rule public TemporaryFolder tmp = new TemporaryFolder(); + @Rule public RetryRule retry = new RetryRule(); @Test public void testReplaceMacro() { @@ -353,7 +355,7 @@ public class UtilTest { } @Test - @Ignore("Frequently flaky test") + @Retry public void testDeleteContentsRecursive_onWindows() throws Exception { Assume.assumeTrue(Functions.isWindows()); final File dir = tmp.newFolder(); diff --git a/core/src/test/java/jenkins/junit/Retry.java b/core/src/test/java/jenkins/junit/Retry.java new file mode 100644 index 0000000000..affe1f17b5 --- /dev/null +++ b/core/src/test/java/jenkins/junit/Retry.java @@ -0,0 +1,46 @@ +/* + * The MIT License + * + * Copyright (c) 2018 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.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Combined with {@link RetryRule}, retries failing test methods. Note that tests that fail with + * {@link org.junit.AssumptionViolatedException} are not retried. + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface Retry { + /** + * Number of times to retry test method or individual tests in a class before giving up. + * Set this to 0 to disable retries (e.g., to override a test class's @Retry annotation or similar). + */ + int value() default 2; +} diff --git a/core/src/test/java/jenkins/junit/RetryRule.java b/core/src/test/java/jenkins/junit/RetryRule.java new file mode 100644 index 0000000000..5697c91998 --- /dev/null +++ b/core/src/test/java/jenkins/junit/RetryRule.java @@ -0,0 +1,65 @@ +/* + * The MIT License + * + * Copyright (c) 2018 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.junit; + +import org.junit.AssumptionViolatedException; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +public class RetryRule implements TestRule { + + @Override + public Statement apply(Statement base, Description description) { + Retry retryForAllMethods = description.getTestClass().getAnnotation(Retry.class); + Retry retryMethod = description.getAnnotation(Retry.class); + int retries = 0; + if (retryMethod != null) { + retries += retryMethod.value(); + } else if (retryForAllMethods != null) { + retries += retryForAllMethods.value(); + } + int maxEvaluationAttempts = retries + 1; + return new Statement() { + @Override + public void evaluate() throws Throwable { + Throwable caught = null; + for (int i = 0; i < maxEvaluationAttempts; i++) { + try { + base.evaluate(); + return; + } catch (AssumptionViolatedException e) { + return; + } catch (Throwable e) { + e.printStackTrace(); + caught = e; + } + } + System.err.println(description.getDisplayName() + " retry count exhausted"); + throw caught; + } + }; + } +} -- GitLab From a0741943ddfc52f8f5ecc12e8c8d38b54c2867a8 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Thu, 15 Nov 2018 15:04:29 -0600 Subject: [PATCH 046/424] Simplify expressions Signed-off-by: Matt Sicker --- core/src/test/java/jenkins/junit/RetryRule.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/jenkins/junit/RetryRule.java b/core/src/test/java/jenkins/junit/RetryRule.java index 5697c91998..722b4389ae 100644 --- a/core/src/test/java/jenkins/junit/RetryRule.java +++ b/core/src/test/java/jenkins/junit/RetryRule.java @@ -37,9 +37,9 @@ public class RetryRule implements TestRule { Retry retryMethod = description.getAnnotation(Retry.class); int retries = 0; if (retryMethod != null) { - retries += retryMethod.value(); + retries = retryMethod.value(); } else if (retryForAllMethods != null) { - retries += retryForAllMethods.value(); + retries = retryForAllMethods.value(); } int maxEvaluationAttempts = retries + 1; return new Statement() { -- GitLab From ce1c5834c36d95c554d8b5f89e6bd6a477b7c7b6 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Thu, 15 Nov 2018 15:30:14 -0600 Subject: [PATCH 047/424] Revert "Simplify expressions" This reverts commit a0741943ddfc52f8f5ecc12e8c8d38b54c2867a8. --- core/src/test/java/jenkins/junit/RetryRule.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/jenkins/junit/RetryRule.java b/core/src/test/java/jenkins/junit/RetryRule.java index 722b4389ae..5697c91998 100644 --- a/core/src/test/java/jenkins/junit/RetryRule.java +++ b/core/src/test/java/jenkins/junit/RetryRule.java @@ -37,9 +37,9 @@ public class RetryRule implements TestRule { Retry retryMethod = description.getAnnotation(Retry.class); int retries = 0; if (retryMethod != null) { - retries = retryMethod.value(); + retries += retryMethod.value(); } else if (retryForAllMethods != null) { - retries = retryForAllMethods.value(); + retries += retryForAllMethods.value(); } int maxEvaluationAttempts = retries + 1; return new Statement() { -- GitLab From 5638a1115aaffca710871b9fcb49e8005740d923 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Thu, 15 Nov 2018 15:30:26 -0600 Subject: [PATCH 048/424] Revert "Add test retry logic" This reverts commit 3d2d1dc2ddec143cc510adcf4e912aaa2ef3e3b0. --- core/src/test/java/hudson/UtilTest.java | 6 +- core/src/test/java/jenkins/junit/Retry.java | 46 ------------- .../test/java/jenkins/junit/RetryRule.java | 65 ------------------- 3 files changed, 2 insertions(+), 115 deletions(-) delete mode 100644 core/src/test/java/jenkins/junit/Retry.java delete mode 100644 core/src/test/java/jenkins/junit/RetryRule.java diff --git a/core/src/test/java/hudson/UtilTest.java b/core/src/test/java/hudson/UtilTest.java index 712151e36d..f537bc7b9b 100644 --- a/core/src/test/java/hudson/UtilTest.java +++ b/core/src/test/java/hudson/UtilTest.java @@ -53,12 +53,11 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.*; -import jenkins.junit.Retry; -import jenkins.junit.RetryRule; import org.apache.commons.io.FileUtils; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.Assume; +import org.junit.Ignore; import org.junit.Test; import org.jvnet.hudson.test.Issue; @@ -75,7 +74,6 @@ import com.google.common.collect.Lists; public class UtilTest { @Rule public TemporaryFolder tmp = new TemporaryFolder(); - @Rule public RetryRule retry = new RetryRule(); @Test public void testReplaceMacro() { @@ -355,7 +353,7 @@ public class UtilTest { } @Test - @Retry + @Ignore("Frequently flaky test") public void testDeleteContentsRecursive_onWindows() throws Exception { Assume.assumeTrue(Functions.isWindows()); final File dir = tmp.newFolder(); diff --git a/core/src/test/java/jenkins/junit/Retry.java b/core/src/test/java/jenkins/junit/Retry.java deleted file mode 100644 index affe1f17b5..0000000000 --- a/core/src/test/java/jenkins/junit/Retry.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2018 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.junit; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Combined with {@link RetryRule}, retries failing test methods. Note that tests that fail with - * {@link org.junit.AssumptionViolatedException} are not retried. - */ -@Target({ElementType.METHOD, ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -public @interface Retry { - /** - * Number of times to retry test method or individual tests in a class before giving up. - * Set this to 0 to disable retries (e.g., to override a test class's @Retry annotation or similar). - */ - int value() default 2; -} diff --git a/core/src/test/java/jenkins/junit/RetryRule.java b/core/src/test/java/jenkins/junit/RetryRule.java deleted file mode 100644 index 5697c91998..0000000000 --- a/core/src/test/java/jenkins/junit/RetryRule.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2018 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.junit; - -import org.junit.AssumptionViolatedException; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -public class RetryRule implements TestRule { - - @Override - public Statement apply(Statement base, Description description) { - Retry retryForAllMethods = description.getTestClass().getAnnotation(Retry.class); - Retry retryMethod = description.getAnnotation(Retry.class); - int retries = 0; - if (retryMethod != null) { - retries += retryMethod.value(); - } else if (retryForAllMethods != null) { - retries += retryForAllMethods.value(); - } - int maxEvaluationAttempts = retries + 1; - return new Statement() { - @Override - public void evaluate() throws Throwable { - Throwable caught = null; - for (int i = 0; i < maxEvaluationAttempts; i++) { - try { - base.evaluate(); - return; - } catch (AssumptionViolatedException e) { - return; - } catch (Throwable e) { - e.printStackTrace(); - caught = e; - } - } - System.err.println(description.getDisplayName() + " retry count exhausted"); - throw caught; - } - }; - } -} -- GitLab From b67f7230675be8e51688bb75d10a59febdd62d85 Mon Sep 17 00:00:00 2001 From: charanb1r Date: Thu, 15 Nov 2018 13:45:54 -0800 Subject: [PATCH 049/424] JENKINS-53284 Fix warning message for changing workspace directories --- ...enkinsBuildsAndWorkspacesDirectoriesTest.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java index 60753e42cc..d8199663d1 100644 --- a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java +++ b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java @@ -82,19 +82,19 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { }); story.then(step -> { - assertTrue(logLevelWasFound(LOG_WHEN_CHANGING_WORKSPACES_DIR, + assertTrue(logWasFoundAtLevel(LOG_WHEN_CHANGING_WORKSPACES_DIR, Level.INFO)); setWorkspacesDirProperty("testdir2"); }); story.then(step -> { - assertTrue(logLevelWasFound(LOG_WHEN_CHANGING_WORKSPACES_DIR, + assertTrue(logWasFoundAtLevel(LOG_WHEN_CHANGING_WORKSPACES_DIR, Level.WARNING)); setWorkspacesDirProperty("testdir3"); }); story.then(step -> { - assertTrue(logLevelWasFound(LOG_WHEN_CHANGING_WORKSPACES_DIR, + assertTrue(logWasFoundAtLevel(LOG_WHEN_CHANGING_WORKSPACES_DIR, Level.WARNING)); }); @@ -292,12 +292,10 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { .anyMatch(record -> record.getMessage().contains(searched)); } - private boolean logLevelWasFound(String searched, Level level) { - return loggerRule - .getRecords() - .stream() - .filter(record -> record.getMessage().contains(searched) - && record.getLevel().equals(level)) + private boolean logWasFoundAtLevel(String searched, Level level) { + return loggerRule.getRecords().stream() + .filter(record -> record.getMessage().contains(searched)) + .filter(record -> record.getLevel().equals(level)) .collect(Collectors.toList()).size() > 0; } -- GitLab From e258171e5d58aedf4a11842b27438dcaf64a6af2 Mon Sep 17 00:00:00 2001 From: Stephen Connolly Date: Fri, 16 Nov 2018 11:52:25 +0000 Subject: [PATCH 050/424] Remove left-over debugging statement --- core/src/main/resources/hudson/PluginManager/_table.js | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/resources/hudson/PluginManager/_table.js b/core/src/main/resources/hudson/PluginManager/_table.js index b75704beb1..4aca54fa7d 100644 --- a/core/src/main/resources/hudson/PluginManager/_table.js +++ b/core/src/main/resources/hudson/PluginManager/_table.js @@ -32,7 +32,6 @@ Behaviour.specify("#filter-box", '_table', 0, function(e) { ? items[i].cells[1].getAttribute('data-id') : items[i].getAttribute("name"); if (visible && name != null) { - console.log(name); if (encountered[name]) { visible = false; } -- GitLab From b090bc1a23ed7a6f3de910599b51537647697ac2 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Fri, 16 Nov 2018 13:36:48 +0100 Subject: [PATCH 051/424] [JENKINS-54599] - Upgrade the Maven Jenkins Dev plugin from 9.4.5.v20170502 to 9.4.12.v20180830 (#3758) * [JENKINS-54599] - Upgrade the Maven Jenkins Dev plugin from 9.4.5.v20170502 to 9.4.12.v20180830 * [JENKINS-54599] - Pick up the final release of Maven HPI Dev Plugin --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 47c16272a8..537812926e 100644 --- a/pom.xml +++ b/pom.xml @@ -468,7 +468,7 @@ THE SOFTWARE. org.jenkins-ci.tools maven-jenkins-dev-plugin - 9.4.5.v20170502-jenkins-1 + 9.4.12.v20180830-jenkins-2 org.jvnet.updatecenter2 -- GitLab From 5f5037291b051586a4889137f646b9c1077f6e9a Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Fri, 16 Nov 2018 14:53:40 +0100 Subject: [PATCH 052/424] [JENKINS-53863] - Fix the license header --- test-jdk8/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-jdk8/pom.xml b/test-jdk8/pom.xml index 6fb4010cf5..70c4870837 100644 --- a/test-jdk8/pom.xml +++ b/test-jdk8/pom.xml @@ -2,7 +2,7 @@ @@ -179,6 +179,11 @@ THE SOFTWARE. tests test + + io.jenkins.stapler + jenkins-stapler-support + 1.0 + org.hamcrest hamcrest-library diff --git a/core/src/main/java/hudson/ProxyConfiguration.java b/core/src/main/java/hudson/ProxyConfiguration.java index d8c0d032af..c93703b452 100644 --- a/core/src/main/java/hudson/ProxyConfiguration.java +++ b/core/src/main/java/hudson/ProxyConfiguration.java @@ -50,6 +50,7 @@ import java.util.List; import java.util.regex.Pattern; import javax.annotation.CheckForNull; import jenkins.model.Jenkins; +import jenkins.security.stapler.StaplerAccessibleType; import jenkins.util.JenkinsJVM; import jenkins.util.SystemProperties; import org.apache.commons.httpclient.Credentials; @@ -78,6 +79,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST; * * @see jenkins.model.Jenkins#proxy */ +@StaplerAccessibleType public final class ProxyConfiguration extends AbstractDescribableImpl implements Saveable, Serializable { /** * Holds a default TCP connect timeout set on all connections returned from this class, diff --git a/core/src/main/java/hudson/TcpSlaveAgentListener.java b/core/src/main/java/hudson/TcpSlaveAgentListener.java index f0a9c03700..053a2a5702 100644 --- a/core/src/main/java/hudson/TcpSlaveAgentListener.java +++ b/core/src/main/java/hudson/TcpSlaveAgentListener.java @@ -34,6 +34,7 @@ import javax.annotation.Nullable; import hudson.model.AperiodicWork; import jenkins.model.Jenkins; import jenkins.model.identity.InstanceIdentityProvider; +import jenkins.security.stapler.StaplerAccessibleType; import jenkins.slaves.RemotingVersionInfo; import jenkins.util.SystemProperties; import hudson.slaves.OfflineCause; @@ -82,6 +83,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; * @author Kohsuke Kawaguchi * @see AgentProtocol */ +@StaplerAccessibleType public final class TcpSlaveAgentListener extends Thread { private final ServerSocketChannel serverSocket; diff --git a/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java b/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java index 38a9215c22..7f2287ad0a 100644 --- a/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java +++ b/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java @@ -26,6 +26,7 @@ package hudson.diagnosis; import hudson.Extension; import hudson.Util; import hudson.model.AdministrativeMonitor; +import jenkins.security.stapler.StaplerDispatchable; import org.jenkinsci.Symbol; import org.kohsuke.stapler.HttpRedirect; import org.kohsuke.stapler.HttpResponse; @@ -70,6 +71,7 @@ public class ReverseProxySetupMonitor extends AdministrativeMonitor { return new HttpRedirect(redirect); } + @StaplerDispatchable public void getTestForReverseProxySetup(String rest) { Jenkins j = Jenkins.getInstance(); String inferred = j.getRootUrlFromRequest() + "manage"; diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 68a1e9e03b..97e826fe1d 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -30,6 +30,7 @@ import hudson.EnvVars; import hudson.Extension; import hudson.Launcher.ProcStarter; import hudson.slaves.Cloud; +import jenkins.security.stapler.StaplerDispatchable; import jenkins.util.SystemProperties; import hudson.Util; import hudson.cli.declarative.CLIResolver; @@ -958,6 +959,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * Gets the read-only snapshot view of all {@link Executor}s. */ @Exported + @StaplerDispatchable public List getExecutors() { return new ArrayList(executors); } @@ -966,6 +968,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * Gets the read-only snapshot view of all {@link OneOffExecutor}s. */ @Exported + @StaplerDispatchable public List getOneOffExecutors() { return new ArrayList(oneOffExecutors); } diff --git a/core/src/main/java/hudson/model/ModelObject.java b/core/src/main/java/hudson/model/ModelObject.java index 5467b2a7a7..f66d0fbd1b 100644 --- a/core/src/main/java/hudson/model/ModelObject.java +++ b/core/src/main/java/hudson/model/ModelObject.java @@ -23,6 +23,8 @@ */ package hudson.model; +import jenkins.security.stapler.StaplerAccessibleType; + /** * A model object has a human readable name. * @@ -32,6 +34,7 @@ package hudson.model; * * @author Kohsuke Kawaguchi */ +@StaplerAccessibleType public interface ModelObject { String getDisplayName(); } diff --git a/core/src/main/java/hudson/model/ParameterValue.java b/core/src/main/java/hudson/model/ParameterValue.java index 6cd1f46a19..fd01e5019d 100644 --- a/core/src/main/java/hudson/model/ParameterValue.java +++ b/core/src/main/java/hudson/model/ParameterValue.java @@ -39,6 +39,7 @@ import java.util.logging.Logger; import javax.annotation.CheckForNull; import jenkins.model.Jenkins; +import jenkins.security.stapler.StaplerAccessibleType; import net.sf.json.JSONObject; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; @@ -75,6 +76,7 @@ import org.kohsuke.stapler.export.ExportedBean; * @see ParametersAction */ @ExportedBean(defaultVisibility=3) +@StaplerAccessibleType public abstract class ParameterValue implements Serializable { private static final Logger LOGGER = Logger.getLogger(ParameterValue.class.getName()); diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java index a1b4225e99..8f1a024123 100644 --- a/core/src/main/java/hudson/model/Queue.java +++ b/core/src/main/java/hudson/model/Queue.java @@ -69,6 +69,7 @@ import java.nio.file.Files; import hudson.util.Futures; import jenkins.security.QueueItemAuthenticatorProvider; +import jenkins.security.stapler.StaplerAccessibleType; import jenkins.util.SystemProperties; import jenkins.util.Timer; import hudson.triggers.SafeTimerTask; @@ -1993,6 +1994,7 @@ public class Queue extends ResourceController implements Saveable { * Implementation must have executorCell.jelly, which is * used to render the HTML that indicates this executable is executing. */ + @StaplerAccessibleType public interface Executable extends Runnable { /** * Task from which this executable was created. diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index 097b1d9b2e..b067f686f9 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -33,6 +33,8 @@ import hudson.ProxyConfiguration; import hudson.security.ACLContext; import java.nio.file.Files; import java.nio.file.InvalidPathException; + +import jenkins.security.stapler.StaplerDispatchable; import jenkins.util.SystemProperties; import hudson.Util; import hudson.XmlFile; @@ -317,6 +319,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas * can be empty but never null. Oldest entries first. */ @Exported + @StaplerDispatchable public List getJobs() { synchronized (jobs) { return new ArrayList(jobs); @@ -517,6 +520,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas * @return * can be empty but never null. */ + @StaplerDispatchable // referenced by _api.jelly public PersistedList getSites() { return sites; } diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 50228d0647..1a7dc1ae98 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -63,6 +63,7 @@ import jenkins.model.item_category.Categories; import jenkins.model.item_category.Category; import jenkins.model.item_category.ItemCategory; import jenkins.scm.RunWithSCM; +import jenkins.security.stapler.StaplerAccessibleType; import jenkins.util.ProgressiveRendering; import jenkins.util.xml.XMLUtils; @@ -700,6 +701,7 @@ public abstract class View extends AbstractModelObject implements AccessControll } @ExportedBean + @StaplerAccessibleType public static final class People { @Exported public final List users; diff --git a/core/src/main/java/hudson/security/AuthorizationStrategy.java b/core/src/main/java/hudson/security/AuthorizationStrategy.java index a273e984b2..4c246a40d4 100644 --- a/core/src/main/java/hudson/security/AuthorizationStrategy.java +++ b/core/src/main/java/hudson/security/AuthorizationStrategy.java @@ -36,6 +36,7 @@ import java.util.Collections; import javax.annotation.Nonnull; import jenkins.model.Jenkins; +import jenkins.security.stapler.StaplerAccessibleType; import net.sf.json.JSONObject; import org.acegisecurity.Authentication; @@ -62,6 +63,7 @@ import org.kohsuke.stapler.StaplerRequest; * @author Kohsuke Kawaguchi * @see SecurityRealm */ +@StaplerAccessibleType public abstract class AuthorizationStrategy extends AbstractDescribableImpl implements ExtensionPoint { /** * Returns the instance of {@link ACL} where all the other {@link ACL} instances diff --git a/core/src/main/java/hudson/security/csrf/CrumbIssuer.java b/core/src/main/java/hudson/security/csrf/CrumbIssuer.java index 1f3c68c743..a16f48689a 100644 --- a/core/src/main/java/hudson/security/csrf/CrumbIssuer.java +++ b/core/src/main/java/hudson/security/csrf/CrumbIssuer.java @@ -9,6 +9,7 @@ import javax.servlet.ServletRequest; import hudson.init.Initializer; import jenkins.model.Jenkins; +import jenkins.security.stapler.StaplerAccessibleType; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.WebApp; @@ -40,6 +41,7 @@ import org.kohsuke.stapler.StaplerResponse; * @see Wikipedia: Cross site request forgery */ @ExportedBean +@StaplerAccessibleType public abstract class CrumbIssuer implements Describable, ExtensionPoint { private static final String CRUMB_ATTRIBUTE = CrumbIssuer.class.getName() + "_crumb"; diff --git a/core/src/main/java/jenkins/diagnosis/HsErrPidList.java b/core/src/main/java/jenkins/diagnosis/HsErrPidList.java index da7f0cde60..c38bdb06f4 100644 --- a/core/src/main/java/jenkins/diagnosis/HsErrPidList.java +++ b/core/src/main/java/jenkins/diagnosis/HsErrPidList.java @@ -11,6 +11,7 @@ import java.nio.file.InvalidPathException; import java.nio.file.OpenOption; import java.nio.file.StandardOpenOption; import jenkins.model.Jenkins; +import jenkins.security.stapler.StaplerDispatchable; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.types.FileSet; @@ -94,6 +95,7 @@ public class HsErrPidList extends AdministrativeMonitor { /** * Expose files to the URL. */ + @StaplerDispatchable public List getFiles() { return files; } diff --git a/core/src/main/java/jenkins/install/InstallState.java b/core/src/main/java/jenkins/install/InstallState.java index 939c45f828..c0fa6367b7 100644 --- a/core/src/main/java/jenkins/install/InstallState.java +++ b/core/src/main/java/jenkins/install/InstallState.java @@ -32,6 +32,7 @@ import hudson.ExtensionPoint; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; +import jenkins.security.stapler.StaplerAccessibleType; import org.apache.commons.lang.StringUtils; /** * Jenkins install state. @@ -44,6 +45,7 @@ import org.apache.commons.lang.StringUtils; * * @author tom.fennelly@gmail.com */ +@StaplerAccessibleType public class InstallState implements ExtensionPoint { /** * Need InstallState != NEW for tests by default diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 20f397d7bc..ded0efaa2a 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -37,7 +37,11 @@ import hudson.*; import hudson.Launcher.LocalLauncher; import jenkins.AgentProtocol; import jenkins.diagnostics.URICheckEncodingMonitor; +import jenkins.security.stapler.DoActionFilter; +import jenkins.security.stapler.StaplerFilteredActionListener; +import jenkins.security.stapler.StaplerDispatchable; import jenkins.security.RedactSecretJsonInErrorMessageSanitizer; +import jenkins.security.stapler.TypedFilter; import jenkins.util.SystemProperties; import hudson.cli.declarative.CLIMethod; import hudson.cli.declarative.CLIResolver; @@ -895,6 +899,16 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve webApp.setClassLoader(pluginManager.uberClassLoader); webApp.setJsonInErrorMessageSanitizer(RedactSecretJsonInErrorMessageSanitizer.INSTANCE); + TypedFilter typedFilter = new TypedFilter(); + webApp.setFilterForGetMethods(typedFilter); + webApp.setFilterForFields(typedFilter); + webApp.setFilterForDoActions(new DoActionFilter()); + + StaplerFilteredActionListener actionListener = new StaplerFilteredActionListener(); + webApp.setFilteredGetterTriggerListener(actionListener); + webApp.setFilteredDoActionTriggerListener(actionListener); + webApp.setFilteredFieldTriggerListener(actionListener); + adjuncts = new AdjunctManager(servletContext, pluginManager.uberClassLoader,"adjuncts/"+SESSION_HASH, TimeUnit.DAYS.toMillis(365)); ClassFilterImpl.register(); @@ -1643,6 +1657,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve save(); } + @StaplerDispatchable public FederatedLoginService getFederatedLoginService(String name) { for (FederatedLoginService fls : FederatedLoginService.all()) { if (fls.getUrlName().equals(name)) @@ -2601,6 +2616,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * * @since 1.349 */ + @StaplerDispatchable public ExtensionList getExtensionList(String extensionType) throws ClassNotFoundException { return getExtensionList(pluginManager.uberClassLoader.loadClass(extensionType)); } @@ -2970,6 +2986,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } // if no finger print matches, display "not found page". + @StaplerDispatchable public Object getFingerprint( String md5sum ) throws IOException { Fingerprint r = fingerprintMap.get(md5sum); if(r==null) return new NoFingerprintMatch(md5sum); @@ -4040,6 +4057,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * End point that intentionally throws an exception to test the error behaviour. * @since 1.467 */ + @StaplerDispatchable public void doException() { throw new RuntimeException(); } @@ -4588,6 +4606,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * Plugins who wish to contribute boxes on the side panel can add widgets * by {@code getWidgets().add(new MyWidget())} from {@link Plugin#start()}. */ + @StaplerDispatchable // some plugins use this to add views to widgets public List getWidgets() { return widgets; } diff --git a/core/src/main/java/jenkins/security/stapler/DoActionFilter.java b/core/src/main/java/jenkins/security/stapler/DoActionFilter.java new file mode 100644 index 0000000000..288398bcba --- /dev/null +++ b/core/src/main/java/jenkins/security/stapler/DoActionFilter.java @@ -0,0 +1,133 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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 hudson.ExtensionList; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.Function; +import org.kohsuke.stapler.FunctionList; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.interceptor.InterceptorAnnotation; + +import javax.annotation.Nonnull; +import java.lang.annotation.Annotation; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +@Restricted(NoExternalUse.class) +public class DoActionFilter implements FunctionList.Filter { + private static final Logger LOGGER = Logger.getLogger(DoActionFilter.class.getName()); + + /** + * if a method has "do" as name (not possible in pure Java but doable in Groovy or other JVM languages) + * the new system does not consider it as a web method. + *

+ * Use @WebMethod(name="") or doIndex in such case. + */ + private static final Pattern DO_METHOD_REGEX = Pattern.compile("^do[^a-z].*"); + + public boolean keep(@Nonnull Function m) { + + if (m.getAnnotation(StaplerNotDispatchable.class) != null) { + return false; + } + + if (m.getAnnotation(StaplerDispatchable.class) != null) { + return true; + } + + String methodName = m.getName(); + String signature = m.getSignature(); + + // check whitelist + ExtensionList whitelistProviders = ExtensionList.lookup(RoutingDecisionProvider.class); + if (whitelistProviders.size() > 0) { + for (RoutingDecisionProvider provider : whitelistProviders) { + RoutingDecisionProvider.Decision methodDecision = provider.decide(signature); + if (methodDecision == RoutingDecisionProvider.Decision.ACCEPTED) { + LOGGER.log(Level.CONFIG, "Action " + signature + " is acceptable because it is whitelisted by " + provider); + return true; + } + if (methodDecision == RoutingDecisionProvider.Decision.REJECTED) { + LOGGER.log(Level.CONFIG, "Action " + signature + " is not acceptable because it is blacklisted by " + provider); + return false; + } + } + } + + if (methodName.equals("doDynamic")) { + // reject doDynamic because it's treated separately by Stapler. + return false; + } + + for (Annotation a : m.getAnnotations()) { + if (WebMethodConstants.WEB_METHOD_ANNOTATION_NAMES.contains(a.annotationType().getName())) { + return true; + } + if (a.annotationType().getAnnotation(InterceptorAnnotation.class) != null) { + // This is a Stapler interceptor annotation like RequirePOST or JsonResponse + return true; + } + } + + // there is rarely more than two annotations in a method signature + for (Annotation[] perParameterAnnotation : m.getParameterAnnotations()) { + for (Annotation annotation : perParameterAnnotation) { + if (WebMethodConstants.WEB_METHOD_PARAMETER_ANNOTATION_NAMES.contains(annotation.annotationType().getName())) { + return true; + } + } + } + + if (!DO_METHOD_REGEX.matcher(methodName).matches()) { + return false; + } + + // after the method name check to avoid allowing methods that are meant to be used by routable ones + // normally they should be private in such case + for (Class parameterType : m.getParameterTypes()) { + if (WebMethodConstants.WEB_METHOD_PARAMETERS_NAMES.contains(parameterType.getName())) { + return true; + } + } + + Class returnType = m.getReturnType(); + if (HttpResponse.class.isAssignableFrom(returnType)) { + return true; + } + + // as HttpResponseException inherits from RuntimeException, + // there is no requirement for the developer to explicitly checks it. + Class[] checkedExceptionTypes = m.getCheckedExceptionTypes(); + for (Class checkedExceptionType : checkedExceptionTypes) { + if (HttpResponse.class.isAssignableFrom(checkedExceptionType)) { + return true; + } + } + + return false; + } +} diff --git a/core/src/main/java/jenkins/security/stapler/RoutingDecisionProvider.java b/core/src/main/java/jenkins/security/stapler/RoutingDecisionProvider.java new file mode 100644 index 0000000000..c7cab91839 --- /dev/null +++ b/core/src/main/java/jenkins/security/stapler/RoutingDecisionProvider.java @@ -0,0 +1,38 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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 hudson.ExtensionPoint; + +import javax.annotation.Nonnull; + +public abstract class RoutingDecisionProvider implements ExtensionPoint { + enum Decision { + ACCEPTED, + REJECTED, + UNKNOWN + } + + @Nonnull public abstract Decision decide(@Nonnull String signature); +} diff --git a/core/src/main/java/jenkins/security/stapler/StaplerFilteredActionListener.java b/core/src/main/java/jenkins/security/stapler/StaplerFilteredActionListener.java new file mode 100644 index 0000000000..bdc329775f --- /dev/null +++ b/core/src/main/java/jenkins/security/stapler/StaplerFilteredActionListener.java @@ -0,0 +1,76 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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 org.kohsuke.accmod.Restricted; +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.FilteredDoActionTriggerListener; +import org.kohsuke.stapler.event.FilteredFieldTriggerListener; +import org.kohsuke.stapler.event.FilteredGetterTriggerListener; +import org.kohsuke.stapler.lang.FieldRef; + +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 + */ +@Restricted(NoExternalUse.class) +public class StaplerFilteredActionListener implements FilteredDoActionTriggerListener, FilteredGetterTriggerListener, FilteredFieldTriggerListener { + 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. " + + "If you consider it safe to use, add the following to the whitelist: \"{1}\". " + + "Learn more: https://jenkins.io/redirect/stapler-routing"; + + @Override + public boolean onDoActionTrigger(Function f, StaplerRequest req, StaplerResponse rsp, Object node) { + LOGGER.log(Level.WARNING, LOG_MESSAGE, new Object[]{ + req.getPathInfo(), + f.getSignature() + }); + return false; + } + + @Override + public boolean onGetterTrigger(Function f, StaplerRequest req, StaplerResponse rsp, Object node, String expression) { + LOGGER.log(Level.WARNING, LOG_MESSAGE, new Object[]{ + req.getPathInfo(), + f.getSignature() + }); + return false; + } + + @Override + public boolean onFieldTrigger(FieldRef f, StaplerRequest req, StaplerResponse staplerResponse, Object node, String expression) { + LOGGER.log(Level.WARNING, LOG_MESSAGE, new Object[]{ + req.getPathInfo(), + f.getSignature() + }); + return false; + } +} diff --git a/core/src/main/java/jenkins/security/stapler/StaticRoutingDecisionProvider.java b/core/src/main/java/jenkins/security/stapler/StaticRoutingDecisionProvider.java new file mode 100644 index 0000000000..cbb5876015 --- /dev/null +++ b/core/src/main/java/jenkins/security/stapler/StaticRoutingDecisionProvider.java @@ -0,0 +1,266 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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 hudson.BulkChange; +import hudson.Extension; +import hudson.ExtensionList; +import hudson.model.Saveable; +import jenkins.model.Jenkins; +import jenkins.util.SystemProperties; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.Function; +import org.kohsuke.stapler.WebApp; +import org.kohsuke.stapler.lang.FieldRef; + +import javax.annotation.Nonnull; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Fill the list of getter methods that are whitelisted for Stapler + * Each item in the set are formatted to correspond exactly to what {@link Function#getDisplayName()} returns + */ +@Restricted(NoExternalUse.class) +@Extension +public class StaticRoutingDecisionProvider extends RoutingDecisionProvider implements Saveable { + private static final Logger LOGGER = Logger.getLogger(StaticRoutingDecisionProvider.class.getName()); + + private Set whitelistSignaturesFromFixedList; + private Set whitelistSignaturesFromUserControlledList; + + private Set blacklistSignaturesFromFixedList; + private Set blacklistSignaturesFromUserControlledList; + + public StaticRoutingDecisionProvider() { + reload(); + } + + /** + * Return the singleton instance of this class, typically for script console use + */ + public static StaticRoutingDecisionProvider get() { + return ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class); + } + + /** + * @see Function#getSignature() + * @see FieldRef#getSignature() + */ + @Nonnull + public synchronized Decision decide(@Nonnull String signature) { + if (whitelistSignaturesFromFixedList == null || whitelistSignaturesFromUserControlledList == null || + blacklistSignaturesFromFixedList == null || blacklistSignaturesFromUserControlledList == null) { + reload(); + } + + LOGGER.log(Level.CONFIG, "Checking whitelist for " + signature); + + // priority to blacklist + if (blacklistSignaturesFromFixedList.contains(signature) || blacklistSignaturesFromUserControlledList.contains(signature)) { + return Decision.REJECTED; + } + + if (whitelistSignaturesFromFixedList.contains(signature) || whitelistSignaturesFromUserControlledList.contains(signature)) { + return Decision.ACCEPTED; + } + + return Decision.UNKNOWN; + } + + public synchronized void reload() { + reloadFromDefault(); + reloadFromUserControlledList(); + + resetMetaClassCache(); + } + + @VisibleForTesting + synchronized void resetAndSave(){ + this.whitelistSignaturesFromFixedList = new HashSet<>(); + this.whitelistSignaturesFromUserControlledList = new HashSet<>(); + this.blacklistSignaturesFromFixedList = new HashSet<>(); + this.blacklistSignaturesFromUserControlledList = new HashSet<>(); + + this.save(); + } + + private void resetMetaClassCache() { + // to allow the change to be effective, i.e. rebuild the MetaClass using the new whitelist + WebApp.get(Jenkins.get().servletContext).clearMetaClassCache(); + } + + private synchronized void reloadFromDefault() { + try (InputStream is = StaticRoutingDecisionProvider.class.getResourceAsStream("default-whitelist.txt")) { + whitelistSignaturesFromFixedList = new HashSet<>(); + blacklistSignaturesFromFixedList = new HashSet<>(); + + parseFileIntoList( + IOUtils.readLines(is, StandardCharsets.UTF_8), + whitelistSignaturesFromFixedList, + blacklistSignaturesFromFixedList + ); + } catch (IOException e) { + throw new ExceptionInInitializerError(e); + } + + LOGGER.log(Level.FINE, "Found {0} getter in the standard whitelist", whitelistSignaturesFromFixedList.size()); + } + + public synchronized StaticRoutingDecisionProvider add(@Nonnull String signature) { + if (this.whitelistSignaturesFromUserControlledList.add(signature)) { + LOGGER.log(Level.INFO, "Signature [{0}] added to the whitelist", signature); + save(); + resetMetaClassCache(); + } else { + LOGGER.log(Level.INFO, "Signature [{0}] was already present in the whitelist", signature); + } + return this; + } + + public synchronized StaticRoutingDecisionProvider addBlacklistSignature(@Nonnull String signature) { + if (this.blacklistSignaturesFromUserControlledList.add(signature)) { + LOGGER.log(Level.INFO, "Signature [{0}] added to the blacklist", signature); + save(); + resetMetaClassCache(); + } else { + LOGGER.log(Level.INFO, "Signature [{0}] was already present in the blacklist", signature); + } + return this; + } + + public synchronized StaticRoutingDecisionProvider remove(@Nonnull String signature) { + if (this.whitelistSignaturesFromUserControlledList.remove(signature)) { + LOGGER.log(Level.INFO, "Signature [{0}] removed from the whitelist", signature); + save(); + resetMetaClassCache(); + } else { + LOGGER.log(Level.INFO, "Signature [{0}] was not present in the whitelist", signature); + } + return this; + } + + public synchronized StaticRoutingDecisionProvider removeBlacklistSignature(@Nonnull String signature) { + if (this.blacklistSignaturesFromUserControlledList.remove(signature)) { + LOGGER.log(Level.INFO, "Signature [{0}] removed from the blacklist", signature); + save(); + resetMetaClassCache(); + } else { + LOGGER.log(Level.INFO, "Signature [{0}] was not present in the blacklist", signature); + } + return this; + } + + /** + * Saves the configuration info to the disk. + */ + public synchronized void save() { + if (BulkChange.contains(this)) { + return; + } + + File file = getConfigFile(); + try { + List allSignatures = new ArrayList<>(whitelistSignaturesFromUserControlledList); + blacklistSignaturesFromUserControlledList.stream() + .map(signature -> "!" + signature) + .forEach(allSignatures::add); + + FileUtils.writeLines(file, allSignatures); + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Failed to save " + file.getAbsolutePath(), e); + } + } + + /** + * Loads the data from the disk into this object. + * + *

+ * The constructor of the derived class must call this method. + * (If we do that in the base class, the derived class won't + * get a chance to set default values.) + */ + private synchronized void reloadFromUserControlledList() { + File file = getConfigFile(); + if (!file.exists()) { + if ((whitelistSignaturesFromUserControlledList != null && whitelistSignaturesFromUserControlledList.isEmpty()) || + (blacklistSignaturesFromUserControlledList != null && blacklistSignaturesFromUserControlledList.isEmpty())) { + LOGGER.log(Level.INFO, "No whitelist source file found at " + file + " so resetting user-controlled whitelist"); + } + whitelistSignaturesFromUserControlledList = new HashSet<>(); + blacklistSignaturesFromUserControlledList = new HashSet<>(); + return; + } + + LOGGER.log(Level.INFO, "Whitelist source file found at " + file); + + try { + whitelistSignaturesFromUserControlledList = new HashSet<>(); + blacklistSignaturesFromUserControlledList = new HashSet<>(); + + parseFileIntoList( + FileUtils.readLines(file, StandardCharsets.UTF_8), + whitelistSignaturesFromUserControlledList, + blacklistSignaturesFromUserControlledList + ); + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Failed to load " + file.getAbsolutePath(), e); + } + } + + private File getConfigFile() { + return new File(WHITELIST_PATH == null ? new File(Jenkins.get().getRootDir(), "stapler-whitelist.txt").toString() : WHITELIST_PATH); + } + + private void parseFileIntoList(List lines, Set whitelist, Set blacklist){ + lines.stream() + .filter(line -> !line.matches("#.*|\\s*")) + .forEach(line -> { + if (line.startsWith("!")) { + String withoutExclamation = line.substring(1); + if (!withoutExclamation.isEmpty()) { + blacklist.add(withoutExclamation); + } + } else { + whitelist.add(line); + } + }); + } + + /** Allow script console access */ + public static String WHITELIST_PATH = SystemProperties.getString(StaticRoutingDecisionProvider.class.getName() + ".whitelist"); + +} diff --git a/core/src/main/java/jenkins/security/stapler/TypedFilter.java b/core/src/main/java/jenkins/security/stapler/TypedFilter.java new file mode 100644 index 0000000000..6f9bdb4019 --- /dev/null +++ b/core/src/main/java/jenkins/security/stapler/TypedFilter.java @@ -0,0 +1,276 @@ +package jenkins.security.stapler; + +import hudson.ExtensionList; +import jenkins.util.SystemProperties; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.Function; +import org.kohsuke.stapler.FunctionList; +import org.kohsuke.stapler.StaplerFallback; +import org.kohsuke.stapler.StaplerOverridable; +import org.kohsuke.stapler.StaplerProxy; +import org.kohsuke.stapler.WebApp; +import org.kohsuke.stapler.interceptor.InterceptorAnnotation; +import org.kohsuke.stapler.lang.FieldRef; + +import javax.annotation.Nonnull; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +@Restricted(NoExternalUse.class) +public class TypedFilter implements FieldRef.Filter, FunctionList.Filter { + private static final Logger LOGGER = Logger.getLogger(TypedFilter.class.getName()); + + private static final Map, Boolean> staplerCache = new HashMap<>(); + + private boolean isClassAcceptable(Class clazz) { + if (clazz.isArray()) { + // special case to allow klass.isArray() dispatcher + Class elementClazz = clazz.getComponentType(); + // does not seem possible to fall in an infinite loop since array cannot be recursively defined + if (isClassAcceptable(elementClazz)) { + LOGGER.log(Level.FINE, + "Class {0} is acceptable because it is an Array of acceptable elements {1}", + new Object[]{clazz.getName(), elementClazz.getName()} + ); + return true; + } else { + LOGGER.log(Level.FINE, + "Class {0} is not acceptable because it is an Array of non-acceptable elements {1}", + new Object[]{clazz.getName(), elementClazz.getName()} + ); + return false; + } + } + return SKIP_TYPE_CHECK || isStaplerRelevantCached(clazz); + } + + private static boolean isStaplerRelevantCached(@Nonnull Class clazz) { + if (staplerCache.containsKey(clazz)) { + return staplerCache.get(clazz); + } + boolean ret = isStaplerRelevant(clazz); + + staplerCache.put(clazz, ret); + return ret; + } + + @Restricted(NoExternalUse.class) + public static boolean isStaplerRelevant(@Nonnull Class clazz) { + return isSpecificClassStaplerRelevant(clazz) || isSuperTypesStaplerRelevant(clazz); + } + + private static boolean isSuperTypesStaplerRelevant(@Nonnull Class clazz) { + Class superclass = clazz.getSuperclass(); + if (superclass != null && isStaplerRelevantCached(superclass)) { + return true; + } + for (Class interfaceClass : clazz.getInterfaces()) { + if (isStaplerRelevantCached(interfaceClass)) { + return true; + } + } + return false; + } + + private static boolean isSpecificClassStaplerRelevant(@Nonnull Class clazz) { + if (clazz.isAnnotationPresent(StaplerAccessibleType.class)) { + return true; + } + + // Classes implementing these Stapler types can be considered routable + if (StaplerProxy.class.isAssignableFrom(clazz)) { + return true; + } + if (StaplerFallback.class.isAssignableFrom(clazz)) { + return true; + } + if (StaplerOverridable.class.isAssignableFrom(clazz)) { + return true; + } + + for (Method m : clazz.getMethods()) { + if (isRoutableMethod(m)) { + return true; + } + } + + return false; + } + + private static boolean isRoutableMethod(@Nonnull Method m) { + for (Annotation a : m.getDeclaredAnnotations()) { + if (WebMethodConstants.WEB_METHOD_ANNOTATION_NAMES.contains(a.annotationType().getName())) { + return true; + } + if (a.annotationType().isAnnotationPresent(InterceptorAnnotation.class)) { + // This is a Stapler interceptor annotation like RequirePOST or JsonResponse + return true; + } + } + + for (Annotation[] set : m.getParameterAnnotations()) { + for (Annotation a : set) { + if (WebMethodConstants.WEB_METHOD_PARAMETER_ANNOTATION_NAMES.contains(a.annotationType().getName())) { + return true; + } + } + } + + for (Class parameterType : m.getParameterTypes()) { + if (WebMethodConstants.WEB_METHOD_PARAMETERS_NAMES.contains(parameterType.getName())) { + return true; + } + } + + return WebApp.getCurrent().getFilterForDoActions().keep(new Function.InstanceFunction(m)); + } + + @Override + public boolean keep(@Nonnull FieldRef fieldRef) { + + if (fieldRef.getAnnotation(StaplerNotDispatchable.class) != null) { + // explicitly marked as an invalid field + return false; + } + + if (fieldRef.getAnnotation(StaplerDispatchable.class) != null) { + // explicitly marked as a valid field + return true; + } + + String signature = fieldRef.getSignature(); + + // check whitelist + ExtensionList decisionProviders = ExtensionList.lookup(RoutingDecisionProvider.class); + if (decisionProviders.size() > 0) { + for (RoutingDecisionProvider provider : decisionProviders) { + RoutingDecisionProvider.Decision fieldDecision = provider.decide(signature); + if (fieldDecision == RoutingDecisionProvider.Decision.ACCEPTED) { + LOGGER.log(Level.CONFIG, "Field {0} is acceptable because it is whitelisted by {1}", new Object[]{signature, provider}); + return true; + } + if (fieldDecision == RoutingDecisionProvider.Decision.REJECTED) { + LOGGER.log(Level.CONFIG, "Field {0} is not acceptable because it is blacklisted by {1}", new Object[]{signature, provider}); + return false; + } + Class type = fieldRef.getReturnType(); + if (type != null) { + String typeSignature = "class " + type.getCanonicalName(); + RoutingDecisionProvider.Decision fieldTypeDecision = provider.decide(typeSignature); + if (fieldTypeDecision == RoutingDecisionProvider.Decision.ACCEPTED) { + LOGGER.log(Level.CONFIG, "Field {0} is acceptable because its type is whitelisted by {1}", new Object[]{signature, provider}); + return true; + } + if (fieldTypeDecision == RoutingDecisionProvider.Decision.REJECTED) { + LOGGER.log(Level.CONFIG, "Field {0} is not acceptable because its type is blacklisted by {1}", new Object[]{signature, provider}); + return false; + } + } + } + } + + if (PROHIBIT_STATIC_ACCESS && fieldRef.isStatic()) { + // unless whitelisted or marked as routable, reject static fields + return false; + } + + + Class returnType = fieldRef.getReturnType(); + + boolean isOk = isClassAcceptable(returnType); + LOGGER.log(Level.FINE, "Field analyzed: {0} => {1}", new Object[]{fieldRef.getName(), isOk}); + return isOk; + } + + @Override + public boolean keep(@Nonnull Function function) { + + if (function.getAnnotation(StaplerNotDispatchable.class) != null) { + // explicitly marked as an invalid getter + return false; + } + + if (function.getAnnotation(StaplerDispatchable.class) != null) { + // explicitly marked as a valid getter + return true; + } + + String signature = function.getSignature(); + + // check whitelist + ExtensionList decision = ExtensionList.lookup(RoutingDecisionProvider.class); + if (decision.size() > 0) { + for (RoutingDecisionProvider provider : decision) { + RoutingDecisionProvider.Decision methodDecision = provider.decide(signature); + if (methodDecision == RoutingDecisionProvider.Decision.ACCEPTED) { + LOGGER.log(Level.CONFIG, "Function {0} is acceptable because it is whitelisted by {1}", new Object[]{signature, provider}); + return true; + } + if (methodDecision == RoutingDecisionProvider.Decision.REJECTED) { + LOGGER.log(Level.CONFIG, "Function {0} is not acceptable because it is blacklisted by {1}", new Object[]{signature, provider}); + return false; + } + + Class type = function.getReturnType(); + if (type != null) { + String typeSignature = "class " + type.getCanonicalName(); + RoutingDecisionProvider.Decision returnTypeDecision = provider.decide(typeSignature); + if (returnTypeDecision == RoutingDecisionProvider.Decision.ACCEPTED) { + LOGGER.log(Level.CONFIG, "Function {0} is acceptable because its type is whitelisted by {1}", new Object[]{signature, provider}); + return true; + } + if (returnTypeDecision == RoutingDecisionProvider.Decision.REJECTED) { + LOGGER.log(Level.CONFIG, "Function {0} is not acceptable because its type is blacklisted by {1}", new Object[]{signature, provider}); + return false; + } + } + } + } + + if (PROHIBIT_STATIC_ACCESS && function.isStatic()) { + // unless whitelisted or marked as routable, reject static methods + return false; + } + + if (function.getName().equals("getDynamic")) { + Class[] parameterTypes = function.getParameterTypes(); + if (parameterTypes.length > 0 && parameterTypes[0] == String.class) { + // While this is more general than what Stapler can invoke on these types, + // The above is the only criterion for Stapler to attempt dispatch. + // Therefore prohibit this as a regular getter. + return false; + } + } + + if (function.getName().equals("getStaplerFallback") && function.getParameterTypes().length == 0) { + // A parameter-less #getStaplerFallback() implements special fallback behavior for the + // StaplerFallback interface. We do not check for the presence of the interface on the current + // class, or the return type, as that could change since the implementing component was last built. + return false; + } + + if (function.getName().equals("getTarget") && function.getParameterTypes().length == 0) { + // A parameter-less #getTarget() implements special redirection behavior for the + // StaplerProxy interface. We do not check for the presence of the interface on the current + // class, or the return type, as that could change since the implementing component was last built. + return false; + } + + Class returnType = function.getReturnType(); + + boolean isOk = isClassAcceptable(returnType); + LOGGER.log(Level.FINE, "Function analyzed: {0} => {1}", new Object[]{signature, isOk}); + return isOk; + } + + @Restricted(NoExternalUse.class) + public static boolean SKIP_TYPE_CHECK = SystemProperties.getBoolean(TypedFilter.class.getName() + ".skipTypeCheck"); + + @Restricted(NoExternalUse.class) + public static boolean PROHIBIT_STATIC_ACCESS = SystemProperties.getBoolean(TypedFilter.class.getName() + ".prohibitStaticAccess", true); +} diff --git a/core/src/main/java/jenkins/security/stapler/WebMethodConstants.java b/core/src/main/java/jenkins/security/stapler/WebMethodConstants.java new file mode 100644 index 0000000000..d8f678e4b0 --- /dev/null +++ b/core/src/main/java/jenkins/security/stapler/WebMethodConstants.java @@ -0,0 +1,101 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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 org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.AncestorInPath; +import org.kohsuke.stapler.Header; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.WebMethod; +import org.kohsuke.stapler.bind.JavaScriptMethod; +import org.kohsuke.stapler.json.JsonBody; +import org.kohsuke.stapler.json.SubmittedForm; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Restricted(NoExternalUse.class) +final class WebMethodConstants { + /** + * If a method has at least one of those parameters, it is considered as an implicit web method + */ + private static final List> WEB_METHOD_PARAMETERS = Collections.unmodifiableList(Arrays.asList( + StaplerRequest.class, + HttpServletRequest.class, + StaplerResponse.class, + HttpServletResponse.class + )); + + static final Set WEB_METHOD_PARAMETERS_NAMES = Collections.unmodifiableSet( + WEB_METHOD_PARAMETERS.stream() + .map(Class::getName) + .collect(Collectors.toSet()) + ); + + /** + * If a method is annotated with one of those annotations, + * the method is considered as an explicit web method + */ + static final List> WEB_METHOD_ANNOTATIONS = Collections.singletonList( + WebMethod.class + // plus every annotation that's annotated with InterceptorAnnotation + // JavaScriptMethod.class not taken here because it's a special case + ); + + static final Set WEB_METHOD_ANNOTATION_NAMES; + static { + Set webMethodAnnotationNames = WEB_METHOD_ANNOTATIONS.stream() + .map(Class::getName) + .collect(Collectors.toSet()); + webMethodAnnotationNames.add(JavaScriptMethod.class.getName()); + WEB_METHOD_ANNOTATION_NAMES = Collections.unmodifiableSet(webMethodAnnotationNames); + } + + /** + * If at least one parameter of the method is annotated with one of those annotations, + * the method is considered as an implicit web method + */ + private static final List> WEB_METHOD_PARAMETER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList( + QueryParameter.class, + AncestorInPath.class, + Header.class, + JsonBody.class, + SubmittedForm.class + )); + + static final Set WEB_METHOD_PARAMETER_ANNOTATION_NAMES = Collections.unmodifiableSet( + WEB_METHOD_PARAMETER_ANNOTATIONS.stream() + .map(Class::getName) + .collect(Collectors.toSet()) + ); +} diff --git a/core/src/main/resources/jenkins/security/stapler/default-whitelist.txt b/core/src/main/resources/jenkins/security/stapler/default-whitelist.txt new file mode 100644 index 0000000000..0938363779 --- /dev/null +++ b/core/src/main/resources/jenkins/security/stapler/default-whitelist.txt @@ -0,0 +1,177 @@ +# This file contains the built-in whitelist for Stapler request dispatching. +# It's a tool for retaining compatibility with unusual plugin behavior after introducing the SECURITY-595 security fix. +# To provide your own custom whitelist, create/edit $JENKINS_HOME/stapler-whitelist.txt + +# Determine the whitelist entries for methods in a known class from the script console: +# com.acme.package.ClassName.class.methods.each { +# println new org.kohsuke.stapler.Function.InstanceFunction(it).signature +# } +# com.acme.package.ClassName.class.fields.each { +# println org.kohsuke.stapler.lang.FieldRef.wrap(it).signature +# } +# return + +####################################################################################################################### +###################################################### Whitelist ###################################################### +####################################################################################################################### + + +###################### +# Credentials Plugin # +###################### +# Used where credentials are used (e.g. SCM config), without this, 'Add' button will break as its dialog is at: +# /descriptor/….CredentialsSelectHelper/resolver/….CredentialsSelectHelper$SystemContextResolver/provider/….SystemCredentialsProvider$ProviderImpl/context/jenkins/dialog +method com.cloudbees.plugins.credentials.CredentialsSelectHelper getResolver java.lang.String +method com.cloudbees.plugins.credentials.CredentialsSelectHelper$WrappedContextResolver getProvider java.lang.String +# Used by Credentials Plugin's FingerprintTest and Git Plugin's GitSCMTest, as well as others: +method com.cloudbees.plugins.credentials.CredentialsStoreAction$DomainWrapper getCredentials + + +################ +# JUnit Plugin # +################ +# Allow various #getHistory() as these only have resources and #getGraph() +class hudson.tasks.junit.History + + +################## +# Metrics Plugin # +################## +# Method returns Object for no clear reason +method jenkins.metrics.api.MetricsRootAction getCurrentUser + + +######################### +# Pipeline Plugin Suite # +######################### +# Used in the 'Pipeline Steps' UI, the Execution has Nodes but no UI of its own +method org.jenkinsci.plugins.workflow.job.WorkflowRun getExecution +# FlowGraphTable only has a Jelly view, and nothing else that would indicate Stapler-routability +method org.jenkinsci.plugins.workflow.job.views.FlowGraphTableAction getFlowGraph + + +############################ +# Maven Integration Plugin # +############################ +# Advertised in https://github.com/jenkinsci/maven-plugin/blob/7ac83fa85fda0c4d1d02663059644f0655823879/src/main/resources/hudson/maven/reporters/MavenArtifactRecord/_api.jelly#L31 +field hudson.maven.reporters.MavenArtifactRecord attachedArtifacts + + +########################################### +# Static Analysis Plugins (analysis-core) # +########################################### +# Methods return Object for no clear reason +method hudson.plugins.analysis.core.AbstractProjectAction getTrendGraph +method hudson.plugins.analysis.core.AbstractProjectAction getTrendDetails +method hudson.plugins.analysis.core.AbstractProjectAction getTrendDetails org.kohsuke.stapler.StaplerRequest org.kohsuke.stapler.StaplerResponse + + +########################### +# Blue Ocean Plugin Suite # +########################### +# Methods return Object for no clear reason +method io.jenkins.blueocean.service.embedded.rest.AbstractRunImpl getLog +method io.jenkins.blueocean.service.embedded.rest.QueuedBlueRun getLog +method io.jenkins.blueocean.rest.impl.pipeline.PipelineNodeImpl getLog +method io.jenkins.blueocean.rest.impl.pipeline.PipelineStepImpl getLog + + +########################## +# Promoted Builds Plugin # +########################## +# Only subtypes look Stapler-relevant +method hudson.plugins.promoted_builds.PromotionProcess getPromotionCondition java.lang.String + + +########################## +# Robot Framework Plugin # +########################## +# Unsure whether this is needed, but RobotSuiteResult is supposed to be reachable. +method hudson.plugins.robot.model.RobotResult getSuites + + +######################## +# Maven Invoker Plugin # +######################## +# MavenInvokerResult only has an index view, and nothing else that would indicate Stapler-routability +method org.jenkinsci.plugins.maveninvoker.MavenInvokerBuildAction getResult java.lang.String + + +########################### +# Cloud Statistics Plugin # +########################### +# Used via CloudStatistics#getUrl(ProvisioningActivity, PhaseExecution, PhaseExecutionAttachment) for attempts.groovy +method org.jenkinsci.plugins.cloudstats.CloudStatistics getActivity java.lang.String +method org.jenkinsci.plugins.cloudstats.ProvisioningActivity getPhase java.lang.String + + +#################### +# SLOCCount Plugin # +#################### +# Support various getters in the same type +class hudson.plugins.sloccount.SloccountResult + + +############################## +# Project Inheritance Plugin # +############################## +# do* methods with no indication they're supposed to be routable (return String, no args, no annotations) +method hudson.plugins.project_inheritance.projects.InheritanceProject doGetParamDefaultsAsXML +method hudson.plugins.project_inheritance.projects.InheritanceProject doGetParamExpansionsAsXML +method hudson.plugins.project_inheritance.projects.InheritanceProject doGetVersionsAsCompressedXML +method hudson.plugins.project_inheritance.projects.InheritanceProject doGetVersionsAsXML +method hudson.plugins.project_inheritance.projects.InheritanceProject doRenderSVGRelationGraph + + +############################################### +# Changes since last successfull build Plugin # (sic) +############################################### +# Only has an index.jelly view, so needs to be explicit +class com.cloudbees.jenkins.plugins.changelog.Changes + + +################### +# Fitnesse Plugin # +################### +# return hudson.plugins.fitnesse.History with only getters for hudson.util.Graph, like analysis-core +method hudson.plugins.fitnesse.FitnesseProjectAction getTrend + + +########################### +# Build Time Blame Plugin # +########################### +# Only has resources and a getter for a Graph, so needs whitelisting when not scanning +class org.jenkins.ci.plugins.buildtimeblame.analysis.BlameReport + + +########################## +# Azure VM Agents Plugin # +########################## +# Declared to return String, no args, and always returns null…? +method com.microsoft.azure.vmagent.AzureVMAgentTemplate$DescriptorImpl doFillImageReferenceTypeItems + + +################################ +# TestComplete support plug-in # +################################ +method com.smartbear.jenkins.plugins.testcomplete.TcSummaryAction getReports + + +##################### +# Job Cacher Plugin # +##################### +# UI linking to this is ArbitraryFileCache/cache-entry.jelly, used from CacheProjectAction/index.jelly, and ultimately handled by ArbitraryFileCache#doDynamic +method jenkins.plugins.jobcacher.CacheProjectAction getCaches + + +######################### +# Dimensions SCM Plugin # +######################### +# Needs whitelisting due to not following the usual naming scheme +method hudson.plugins.dimensionsscm.DimensionsSCM$DescriptorImpl domanadatoryFieldCheck org.kohsuke.stapler.StaplerRequest org.kohsuke.stapler.StaplerResponse + + +############################## +# Google Health Check Plugin # +############################## +method com.google.jenkins.plugins.health.lib.DerivedPageAction getZone java.lang.String diff --git a/core/src/test/java/jenkins/security/stapler/StaplerSignaturesTest.java b/core/src/test/java/jenkins/security/stapler/StaplerSignaturesTest.java new file mode 100644 index 0000000000..1e7a791561 --- /dev/null +++ b/core/src/test/java/jenkins/security/stapler/StaplerSignaturesTest.java @@ -0,0 +1,116 @@ +package jenkins.security.stapler; + +import org.junit.Assert; +import org.junit.Test; +import org.kohsuke.stapler.Function; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.json.JsonResponse; +import org.kohsuke.stapler.lang.FieldRef; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class StaplerSignaturesTest { + @Test + public void testSignaturesSimple() throws Exception { + Set methodSignatures = Arrays.stream(SomeClass.class.getMethods()).map(it -> new Function.InstanceFunction(it).getSignature()).collect(Collectors.toSet()); + Assert.assertEquals(SomeClass.METHOD_SIGNATURES, methodSignatures); + + Set fieldSignatures = Arrays.stream(SomeClass.class.getFields()).map(it -> FieldRef.wrap(it).getSignature()).collect(Collectors.toSet()); + Assert.assertEquals(SomeClass.FIELD_SIGNATURES, fieldSignatures); + } + + @Test + public void testSignaturesInheritance() throws Exception { + Set methodSignatures = Arrays.stream(SomeSubclass.class.getMethods()).map(it -> new Function.InstanceFunction(it).getSignature()).collect(Collectors.toSet()); + Assert.assertEquals(SomeSubclass.METHOD_SIGNATURES, methodSignatures); + + Set fieldSignatures = Arrays.stream(SomeSubclass.class.getFields()).map(it -> FieldRef.wrap(it).getSignature()).collect(Collectors.toSet()); + Assert.assertEquals(SomeSubclass.FIELD_SIGNATURES, fieldSignatures); + } + + public static class SomeClass { + static Set METHOD_SIGNATURES = new HashSet<>(Arrays.asList( + "method jenkins.security.stapler.StaplerSignaturesTest$SomeClass getFoo", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeClass getFoo java.lang.String", + "staticMethod jenkins.security.stapler.StaplerSignaturesTest$SomeClass getFoo int", + "staticMethod jenkins.security.stapler.StaplerSignaturesTest$SomeClass getFoo long", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeClass getFoo jenkins.security.stapler.StaplerSignaturesTest$SomeClass", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeClass doFoo org.kohsuke.stapler.StaplerRequest org.kohsuke.stapler.StaplerResponse", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeClass doWhatever java.lang.String", + "method java.lang.Object getClass", + "method java.lang.Object equals java.lang.Object", + "method java.lang.Object hashCode", + "method java.lang.Object notify", + "method java.lang.Object notifyAll", + "method java.lang.Object toString", + "method java.lang.Object wait long int", + "method java.lang.Object wait long", + "method java.lang.Object wait" + )); + public void getFoo() {} + public void getFoo(String arg) {} + public static void getFoo(int arg) {} + public static void getFoo(long arg) {} + public void getFoo(SomeClass arg) {} + public void doFoo(StaplerRequest req, StaplerResponse rsp) {} + @StaplerDispatchable @JsonResponse + public void doWhatever(@QueryParameter String arg) {} + + static Set FIELD_SIGNATURES = new HashSet<>(Arrays.asList( + "field jenkins.security.stapler.StaplerSignaturesTest$SomeClass whatever", + "field jenkins.security.stapler.StaplerSignaturesTest$SomeClass thing", + "staticField jenkins.security.stapler.StaplerSignaturesTest$SomeClass staticField", + "field jenkins.security.stapler.StaplerSignaturesTest$SomeClass stringList" + )); + public String whatever; + public Object thing; + public static Object staticField; + public List stringList; + + } + + public static class SomeSubclass extends SomeClass { + static Set METHOD_SIGNATURES = new HashSet<>(Arrays.asList( + "method jenkins.security.stapler.StaplerSignaturesTest$SomeSubclass getFoo", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeSubclass subtypeExclusive", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeSubclass subtypeExclusive java.lang.String", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeSubclass varargMethod [Ljava.lang.String;", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeClass getFoo java.lang.String", + "staticMethod jenkins.security.stapler.StaplerSignaturesTest$SomeClass getFoo int", + "staticMethod jenkins.security.stapler.StaplerSignaturesTest$SomeClass getFoo long", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeClass getFoo jenkins.security.stapler.StaplerSignaturesTest$SomeClass", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeClass doFoo org.kohsuke.stapler.StaplerRequest org.kohsuke.stapler.StaplerResponse", + "method jenkins.security.stapler.StaplerSignaturesTest$SomeClass doWhatever java.lang.String", + "method java.lang.Object getClass", + "method java.lang.Object equals java.lang.Object", + "method java.lang.Object hashCode", + "method java.lang.Object notify", + "method java.lang.Object notifyAll", + "method java.lang.Object toString", + "method java.lang.Object wait long int", + "method java.lang.Object wait long", + "method java.lang.Object wait" + )); + public void getFoo() {} + public void subtypeExclusive(){} + public void subtypeExclusive(String arg){} + public void varargMethod(String... args){} + + static Set FIELD_SIGNATURES = new HashSet<>(Arrays.asList( + "field jenkins.security.stapler.StaplerSignaturesTest$SomeSubclass whatever", + "field jenkins.security.stapler.StaplerSignaturesTest$SomeClass whatever", + "field jenkins.security.stapler.StaplerSignaturesTest$SomeClass thing", + "staticField jenkins.security.stapler.StaplerSignaturesTest$SomeSubclass staticField", + "staticField jenkins.security.stapler.StaplerSignaturesTest$SomeClass staticField", + "field jenkins.security.stapler.StaplerSignaturesTest$SomeClass stringList" + )); + public String whatever; + public static Object staticField; + } +} diff --git a/test/pom.xml b/test/pom.xml index 975aea364d..37da267d6c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -68,6 +68,12 @@ THE SOFTWARE. 2.0 test + + org.jenkins-ci.plugins + cloudbees-folder + 6.3 + test + ${project.groupId} maven-plugin diff --git a/test/src/test/java/hudson/model/ViewTest.java b/test/src/test/java/hudson/model/ViewTest.java index 4de9409993..3d73a15d6d 100644 --- a/test/src/test/java/hudson/model/ViewTest.java +++ b/test/src/test/java/hudson/model/ViewTest.java @@ -23,10 +23,13 @@ */ package hudson.model; +import com.cloudbees.hudson.plugins.folder.Folder; import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.html.DomNodeUtil; import com.gargoylesoftware.htmlunit.util.NameValuePair; import jenkins.model.Jenkins; +import org.jenkins.ui.icon.Icon; +import org.jenkins.ui.icon.IconSet; import org.jvnet.hudson.test.Issue; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.HttpMethod; @@ -194,6 +197,18 @@ public class ViewTest { @Issue("JENKINS-9367") @Test public void allImagesCanBeLoaded() throws Exception { User.get("user", true); + + // as long as the cloudbees-folder is included as test dependency, its Folder will load icon + boolean folderPluginActive = (j.jenkins.getPlugin("cloudbees-folder") != null); + // link to Folder class is done here to ensure if we remove the dependency, this code will fail and so will be removed + boolean folderPluginClassesLoaded = (j.jenkins.getDescriptor(Folder.class) != null); + // this could be written like this to avoid the hard dependency: + // boolean folderPluginClassesLoaded = (j.jenkins.getDescriptor("com.cloudbees.hudson.plugins.folder.Folder") != null); + if (!folderPluginActive && folderPluginClassesLoaded) { + // reset the icon added by Folder because the plugin resources are not reachable + IconSet.icons.addIcon(new Icon("icon-folder icon-md", "24x24/folder.gif", "width: 24px; height: 24px;")); + } + WebClient webClient = j.createWebClient(); webClient.getOptions().setJavaScriptEnabled(false); j.assertAllImageLoadSuccessfully(webClient.goTo("asynchPeople")); diff --git a/test/src/test/java/hudson/util/FormFieldValidatorTest.java b/test/src/test/java/hudson/util/FormFieldValidatorTest.java index 3aaabd067c..ccab8b91de 100644 --- a/test/src/test/java/hudson/util/FormFieldValidatorTest.java +++ b/test/src/test/java/hudson/util/FormFieldValidatorTest.java @@ -59,7 +59,7 @@ public class FormFieldValidatorTest { return true; } - public void doCheckXyz() { + public FormValidation doCheckXyz() { throw new Error("doCheckXyz is broken"); } } diff --git a/test/src/test/java/jenkins/security/stapler/CustomRoutingDecisionProviderTest.java b/test/src/test/java/jenkins/security/stapler/CustomRoutingDecisionProviderTest.java new file mode 100644 index 0000000000..aef0510fbb --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/CustomRoutingDecisionProviderTest.java @@ -0,0 +1,114 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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.Page; +import hudson.model.UnprotectedRootAction; +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 org.kohsuke.stapler.Stapler; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.WebMethod; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@Issue("SECURITY-400") +@For(RoutingDecisionProvider.class) +public class CustomRoutingDecisionProviderTest { + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @TestExtension("customRoutingWhitelistProvider") + public static class XxxBlacklister extends RoutingDecisionProvider { + @Override + public Decision decide(@Nonnull String signature) { + if (signature.contains("xxx")) { + return Decision.REJECTED; + } + return Decision.UNKNOWN; + } + } + + @TestExtension + public static class OneMethodIsBlacklisted implements UnprotectedRootAction { + @Override + public @CheckForNull String getUrlName() { + return "custom"; + } + + @Override + public String getDisplayName() { + return null; + } + + @Override + public String getIconFileName() { + return null; + } + + public StaplerAbstractTest.Renderable getLegitGetter() { + return new StaplerAbstractTest.Renderable(); + } + + public StaplerAbstractTest.Renderable getLegitxxxGetter() { + return new StaplerAbstractTest.Renderable(); + } + } + + private static class Renderable { + public void doIndex() {replyOk();} + + @WebMethod(name = "valid") + public void valid() {replyOk();} + } + + private static void replyOk() { + StaplerResponse resp = Stapler.getCurrentResponse(); + try { + resp.getWriter().write("ok"); + resp.flushBuffer(); + } catch (IOException e) {} + } + + @Test + public void customRoutingWhitelistProvider() throws Exception { + Page okPage = j.createWebClient().goTo("custom/legitGetter", null); + assertThat(okPage.getWebResponse().getStatusCode(), is(200)); + + JenkinsRule.WebClient wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + Page errorPage = wc.goTo("custom/legitxxxGetter", null); + assertThat(errorPage.getWebResponse().getStatusCode(), is(404)); + } +} diff --git a/test/src/test/java/jenkins/security/stapler/DoActionFilterTest.java b/test/src/test/java/jenkins/security/stapler/DoActionFilterTest.java new file mode 100644 index 0000000000..7d9b008b1b --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/DoActionFilterTest.java @@ -0,0 +1,738 @@ +package jenkins.security.stapler; + +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; +import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebRequest; +import com.gargoylesoftware.htmlunit.util.NameValuePair; +import net.sf.json.JSONArray; +import net.sf.json.JSONObject; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.AncestorInPath; +import org.kohsuke.stapler.CapturedParameterNames; +import org.kohsuke.stapler.CrumbIssuer; +import org.kohsuke.stapler.Header; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.HttpResponses; +import org.kohsuke.stapler.LimitedTo; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.RequestImpl; +import org.kohsuke.stapler.ResponseImpl; +import org.kohsuke.stapler.StaplerProxy; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.WebMethod; +import org.kohsuke.stapler.bind.JavaScriptMethod; +import org.kohsuke.stapler.interceptor.JsonOutputFilter; +import org.kohsuke.stapler.interceptor.RequirePOST; +import org.kohsuke.stapler.interceptor.RespondSuccess; +import org.kohsuke.stapler.json.JsonBody; +import org.kohsuke.stapler.json.JsonResponse; +import org.kohsuke.stapler.json.SubmittedForm; +import org.kohsuke.stapler.verb.DELETE; +import org.kohsuke.stapler.verb.GET; +import org.kohsuke.stapler.verb.POST; +import org.kohsuke.stapler.verb.PUT; + +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * To check the previous behavior you can use: + *

+ * {@link org.kohsuke.stapler.MetaClass#LEGACY_WEB_METHOD_MODE} = true;
+ * 
+ * It will disable the usage of {@link DoActionFilter} + */ +@Issue("SECURITY-400") +public class DoActionFilterTest extends StaplerAbstractTest { + + @TestExtension + public static class TestAccessModifierUrl extends AbstractUnprotectedRootAction { + public TestAccessModifier getPublic() {return new TestAccessModifier();} + + protected TestAccessModifier getProtected() {return new TestAccessModifier();} + + TestAccessModifier getInternal() {return new TestAccessModifier();} + + private TestAccessModifier getPrivate() {return new TestAccessModifier();} + + public class TestAccessModifier { + @GET + public String doValue() { + return "hello"; + } + } + } + + @Test + public void testProtectedMethodDispatch() throws Exception { + try { + wc.goTo("testAccessModifierUrl/public/value", null); + } catch (FailingHttpStatusCodeException e) { + fail("should have access to a public method"); + } + try { + wc.goTo("testAccessModifierUrl/protected/value", null); + fail("should not have allowed protected access"); + } catch (FailingHttpStatusCodeException x) { + assertEquals(HttpServletResponse.SC_NOT_FOUND, x.getStatusCode()); + } + try { + wc.goTo("testAccessModifierUrl/internal/value", null); + fail("should not have allowed internal access"); + } catch (FailingHttpStatusCodeException x) { + assertEquals(HttpServletResponse.SC_NOT_FOUND, x.getStatusCode()); + } + try { + wc.goTo("testAccessModifierUrl/private/value", null); + fail("should not have allowed private access"); + } catch (FailingHttpStatusCodeException x) { + assertEquals(HttpServletResponse.SC_NOT_FOUND, x.getStatusCode()); + } + } + + //================================= doXxx methods ================================= + + @TestExtension + public static class TestNewRulesOk extends AbstractUnprotectedRootAction { + /* + * Method signature + */ + + public static void doStaticWithRequest(StaplerRequest request) { replyOk(); } + + public void doWithRequest(StaplerRequest request) { replyOk(); } + + public void doWithHttpRequest(HttpServletRequest request) { replyOk(); } + + // the return type is not taken into consideration if it's not a HttpResponse, it will not prevent the method + // to be considered as a web method + public String doWithRequestAndReturnString(StaplerRequest request) { return "ok"; } + + public void doWithResponse(StaplerResponse response) { replyOk(); } + + public void doWithHttpResponse(HttpServletResponse response) { replyOk(); } + + public void doWithThrowHttpResponseException() throws HttpResponses.HttpResponseException { replyOk(); } + + // special cases, child of above classes, normally reachable, as it satisfies the contract + // that requires to throw an exception that is an HttpResponseException + public void doWithThrowHttpResponseExceptionChild() throws HttpResponseExceptionChild { replyOk(); } + + // the declared exception just has to implement HttpResponse + public void doWithThrowExceptionImplementingOnlyHttpResponse() throws ExceptionImplementingOnlyHttpResponse { replyOk(); } + + public void doWithThrowOtherException() throws IOException { replyOk(); } + + public HttpResponse doWithReturnHttpResponse() { return HttpResponses.plainText("ok"); } + + public HttpResponseChild doWithReturnHttpResponseChild() { return new HttpResponseChild(); } + + /* + * Method annotations + */ + + @WebMethod(name = "webMethodUrl") + public void doWebMethod() { replyOk(); } + + // not requiring to have doXxx when using WebMethod + @WebMethod(name = "webMethodUrl2") + public void webMethod() { replyOk(); } + + @GET + public void doAnnotatedGet() { replyOk(); } + + @POST + public void doAnnotatedPost() { replyOk(); } + + @PUT + public void doAnnotatedPut() { replyOk(); } + + @DELETE + public void doAnnotatedDelete() { replyOk(); } + + @RequirePOST + public void doAnnotatedRequirePost() { replyOk(); } + + @JavaScriptMethod + public void annotatedJavascriptScriptMethod() { replyOk(); } + + @RespondSuccess + public void doAnnotatedResponseSuccess() { replyOk(); } + + @JsonResponse // does not support list + public Map doAnnotatedJsonResponse() { + return new HashMap() {{ + put("a", "b"); + }}; + } + + @LimitedTo("admin") + public void doAnnotatedLimitedTo() { replyOk(); } + + /* + * Parameter annotation + */ + + public void doAnnotatedParamQueryParameter(@QueryParameter String value) { replyOk(); } + + public void doAnnotatedParamAncestorInPath(@AncestorInPath DoActionFilterTest parent) { replyOk(); } + + public void doAnnotatedParamHeader(@Header("test-header") String testHeader) { replyOk(); } + + public void doAnnotatedParamJsonBody(@JsonBody Map names) { replyOk(); } + + public void doAnnotatedParamSubmittedForm(@SubmittedForm JSONObject form) { replyOk(); } + + /* + * Parameter annotation + */ + + public void do_CallMeBecauseOfMyUnderscore(StaplerRequest request) { replyOk(); } + + public void do$CallMeBecauseOfMyDollar(StaplerRequest request) { replyOk(); } + } + + public static class HttpResponseChild implements HttpResponse { + @Override + public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException { + replyOk(); + } + } + + public static abstract class HttpResponseExceptionChild extends HttpResponses.HttpResponseException { + } + + public static class ExceptionImplementingOnlyHttpResponse extends RuntimeException implements HttpResponse { + @Override + public void generateResponse(StaplerRequest staplerRequest, StaplerResponse staplerResponse, Object o) throws IOException, ServletException { + replyOk(); + } + } + + //########### actual test methods ########### + @Test + public void testMethodSignatureOk_staticWithRequest() throws Exception { + assertReachable("testNewRulesOk/staticWithRequest/"); + } + + @Test + public void testMethodSignatureOk_withRequest() throws Exception { + assertReachable("testNewRulesOk/withRequest/"); + } + + @Test + public void testMethodSignatureOk_withRequestAndReturnString() throws Exception { + assertReachable("testNewRulesOk/withRequestAndReturnString/"); + } + + @Test + public void testMethodSignatureOk_withHttpRequest() throws Exception { + assertReachable("testNewRulesOk/withHttpRequest/"); + } + + @Test + public void testMethodSignatureOk_withHttpResponse() throws Exception { + assertReachable("testNewRulesOk/withHttpResponse/"); + } + + @Test + public void testMethodSignatureOk_withResponse() throws Exception { + assertReachable("testNewRulesOk/withResponse/"); + } + + @Test + public void testMethodSignatureOk_withThrowHttpResponseException() throws Exception { + assertReachable("testNewRulesOk/withThrowHttpResponseException/"); + } + + @Test + public void testMethodSignatureOk_withThrowHttpResponseExceptionChild() throws Exception { + assertReachable("testNewRulesOk/withThrowHttpResponseExceptionChild/"); + } + + @Test + public void testMethodSignatureOk_withThrowExceptionImplementingOnlyHttpResponse() throws Exception { + assertReachable("testNewRulesOk/withThrowExceptionImplementingOnlyHttpResponse/"); + } + + @Test + public void testMethodSignatureOk_withThrowOtherException() throws Exception { + assertNotReachable("testNewRulesOk/withThrowOtherException/"); + } + + @Test + public void testMethodSignatureOk_withReturnHttpResponse() throws Exception { + assertReachable("testNewRulesOk/withReturnHttpResponse/"); + } + + @Test + public void testMethodSignatureOk_withReturnHttpResponseChild() throws Exception { + assertReachable("testNewRulesOk/withReturnHttpResponseChild/"); + } + + @Test + public void testAnnotatedMethodOk_webMethodUrl() throws Exception { + assertReachable("testNewRulesOk/webMethodUrl/"); + } + + @Test + public void testAnnotatedMethodOk_webMethodUrl2() throws Exception { + assertReachable("testNewRulesOk/webMethodUrl2/"); + } + + @Test + public void testAnnotatedMethodOk_annotatedGet() throws Exception { + assertReachable("testNewRulesOk/annotatedGet/"); + } + + @Test + public void testAnnotatedMethodOk_annotatedPost() throws Exception { + WebRequest settings = new WebRequest(new URL(j.getURL(), "testNewRulesOk/annotatedPost/")); + settings.setHttpMethod(HttpMethod.POST); + settings.setRequestBody(""); + assertReachableWithSettings(settings); + } + + @Test + public void testAnnotatedMethodOk_annotatedPut() throws Exception { + WebRequest settings = new WebRequest(new URL(j.getURL(), "testNewRulesOk/annotatedPut/")); + settings.setHttpMethod(HttpMethod.PUT); + settings.setRequestBody(""); + assertReachableWithSettings(settings); + } + + @Test + public void testAnnotatedMethodOk_annotatedDelete() throws Exception { + assertReachable("testNewRulesOk/annotatedDelete/", HttpMethod.DELETE); + } + + @Test + public void testAnnotatedMethodOk_annotatedRequirePost() throws Exception { + WebRequest settings = new WebRequest(new URL(j.getURL(), "testNewRulesOk/annotatedRequirePost/")); + settings.setHttpMethod(HttpMethod.POST); + settings.setRequestBody(""); + assertReachableWithSettings(settings); + } + + @Test + public void testAnnotatedMethodOk_annotatedJavascriptScriptMethod() throws Exception { + webApp.setCrumbIssuer(new CrumbIssuer() { + @Override + public String issueCrumb(StaplerRequest request) { + return "test"; + } + + @Override + public void validateCrumb(StaplerRequest request, String submittedCrumb) { + // no exception thrown = validated + } + }); + + + WebRequest settings = new WebRequest(new URL(j.getURL(), "testNewRulesOk/annotatedJavascriptScriptMethod/")); + settings.setAdditionalHeader("Content-Type", "application/x-stapler-method-invocation"); + settings.setHttpMethod(HttpMethod.POST); + settings.setRequestBody(JSONArray.fromObject(Arrays.asList()).toString()); + assertReachableWithSettings(settings); + } + + @Test + public void testAnnotatedMethodOk_annotatedResponseSuccess() throws Exception { + assertReachable("testNewRulesOk/annotatedResponseSuccess/"); + } + + @Test + public void testAnnotatedMethodOk_annotatedJsonResponse() throws Exception { + WebRequest settings = new WebRequest(new URL(j.getURL(), "testNewRulesOk/annotatedJsonResponse/")); + settings.setHttpMethod(HttpMethod.POST); + settings.setRequestBody(JSONObject.fromObject(Collections.emptyMap()).toString()); + Page page = wc.getPage(settings); + assertEquals(200, page.getWebResponse().getStatusCode()); + } + + @Test + public void testAnnotatedMethodOk_annotatedLimitedTo() throws Exception { + try { + wc.getPage(new URL(j.getURL(), "testNewRulesOk/annotatedLimitedTo/")); + fail(); + } catch (FailingHttpStatusCodeException e) { + assertEquals(500, e.getStatusCode()); + assertTrue(e.getResponse().getContentAsString().contains("Needs to be in role")); + } + } + + @Test + public void testAnnotatedParameterOk_annotatedParamQueryParameter() throws Exception { + // parameter is optional by default + assertReachable("testNewRulesOk/annotatedParamQueryParameter/"); + assertReachable("testNewRulesOk/annotatedParamQueryParameter/?value=test"); + } + + @Test + public void testAnnotatedParameterOk_annotatedParamAncestorInPath() throws Exception { + assertReachable("testNewRulesOk/annotatedParamAncestorInPath/"); + } + + @Test + public void testAnnotatedParameterOk_annotatedParamHeader() throws Exception { + WebRequest settings = new WebRequest(new URL(j.getURL(), "testNewRulesOk/annotatedParamHeader/")); + settings.setAdditionalHeader("test-header", "TestBrowser"); + assertReachableWithSettings(settings); + } + + @Test + public void testAnnotatedParameterOk_annotatedParamJsonBody() throws Exception { + WebRequest settings = new WebRequest(new URL(j.getURL(), "testNewRulesOk/annotatedParamJsonBody/")); + // WebClient forces us to use POST to have the possibility to send requestBody + settings.setHttpMethod(HttpMethod.POST); + settings.setAdditionalHeader("Content-Type", "application/json"); + settings.setRequestBody(JSONObject.fromObject(new HashMap() {{ + put("name", "Test"); + }}).toString()); + assertReachableWithSettings(settings); + } + + @Test + public void testAnnotatedParameterOk_annotatedParamSubmittedForm() throws Exception { + WebRequest settings = new WebRequest(new URL(j.getURL(), "testNewRulesOk/annotatedParamSubmittedForm/")); + settings.setHttpMethod(HttpMethod.POST); + + settings.setRequestParameters(Arrays.asList( + new NameValuePair( + "json", + JSONObject.fromObject(new HashMap() {{ + put("name", "Test"); + }}).toString() + ) + )); + assertReachableWithSettings(settings); + } + + @Test + public void testOk__CallMeBecauseOfMyUnderscore() throws Exception { + assertReachable("testNewRulesOk/_CallMeBecauseOfMyUnderscore/"); + } + + @Test + public void testOk_$CallMeBecauseOfMyDollar() throws Exception { + assertReachable("testNewRulesOk/$CallMeBecauseOfMyDollar/"); + } + + @TestExtension + public static class TestNewRulesOkDynamic extends AbstractUnprotectedRootAction { + // sufficiently magical name to be reached + public void doDynamic() { replyOk(); } + } + + + @TestExtension + public static class TestNewRulesOkIndex extends AbstractUnprotectedRootAction { + // considered as index + @WebMethod(name = "") + public void methodWithoutNameEqualIndex() { replyOk(); } + } + + @TestExtension + public static class TestNewRulesOkDoIndex extends AbstractUnprotectedRootAction { + public void doIndex() { replyOk(); } + } + + @Test + public void testSpecialCasesOk() throws Exception { + assertReachable("testNewRulesOkDynamic/anyString/"); + assertReachable("testNewRulesOkIndex/"); + assertReachable("testNewRulesOkDoIndex/"); + } + + // those methods are accepted in legacy system but potentially dangerous + @TestExtension + public static class TestNewRulesNotOk extends AbstractUnprotectedRootAction { + // do not respect the do[^a-z].* format + public void dontCallMeBecauseOfMyDont(StaplerRequest request) { replyOk(); } + + // do not seem to be an expected web method, in case a developer has such methods, + // addition of WebMethod annotation is sufficient + public void doSomething() { replyOk(); } + + // returning a String is not sufficient to be considered as a web method + public String doReturnString() { return "ok"; } + + // returning a super class of HttpResponse is not sufficient + public Object doReturnObject() { return "ok"; } + } + + @Test + public void testNotOk_ntCallMeBecauseOfMyDont() throws Exception { + assertNotReachable("testNewRulesNotOk/ntCallMeBecauseOfMyDont/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNotOk_something() throws Exception { + assertNotReachable("testNewRulesNotOk/something/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNotOk_returnString() throws Exception { + assertNotReachable("testNewRulesNotOk/returnString/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNotOk_returnObject() throws Exception { + assertNotReachable("testNewRulesNotOk/returnObject/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @TestExtension + public static class TestNewRulesNotOkSpecialCases extends AbstractUnprotectedRootAction { + public void doWithServletRequest(ServletRequest request) { replyOk(); } + + public void doWithServletResponse(ServletResponse response) { replyOk(); } + + // special cases, child of above classes + public void doWithRequestImpl(RequestImpl request) { replyOk(); } + + public void doWithResponseImpl(ResponseImpl response) { replyOk(); } + + public void doWithRequestAndResponse(RequestAndResponse requestAndResponse) { replyOk(); } + + // special case to keep Groovy parameter name, but does not seem to indicate it's automatically a web method + @CapturedParameterNames({"req"}) + public void doAnnotatedResponseSuccess(Object req) { replyOk(); } + +// // as mentioned in its documentation, it requires to have JavaScriptMethod, that has its own test +// @JsonOutputFilter +// public void doAnnotatedJsonOutputFilter() { replyOk(); } + } + + public static abstract class RequestAndResponse implements StaplerRequest, StaplerResponse { + @Override + public CollectionAndEnumeration getHeaderNames() { + return null; + } + + @Override + public CollectionAndEnumeration getHeaders(String name) { + return null; + } + + public static abstract class CollectionAndEnumeration implements Collection, Enumeration { + } + } + + @Test + public void testNotOkSpecialCases_withServletRequest() throws Exception { + assertNotReachable("testNewRulesNotOkSpecialCases/withServletRequest/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNotOkSpecialCases_withServletResponse() throws Exception { + assertNotReachable("testNewRulesNotOkSpecialCases/withServletResponse/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNotOkSpecialCases_withRequestImpl() throws Exception { + assertNotReachable("testNewRulesNotOkSpecialCases/withRequestImpl/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNotOkSpecialCases_withResponseImpl() throws Exception { + assertNotReachable("testNewRulesNotOkSpecialCases/withResponseImpl/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNotOkSpecialCases_withRequestAndResponse() throws Exception { + assertNotReachable("testNewRulesNotOkSpecialCases/withRequestAndResponse/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNotOkSpecialCases_annotatedResponseSuccess() throws Exception { + assertNotReachable("testNewRulesNotOkSpecialCases/annotatedResponseSuccess/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + // now JsonOutputFilter is accepted as a web method annotation +// @Test +// public void testNotOkSpecialCases_annotatedJsonOutputFilter() throws Exception { +// assertNotReachable("testNewRulesNotOkSpecialCases/annotatedJsonOutputFilter/"); +// assertDoActionRequestWasBlockedAndResetFlag(); +// } + + //================================= class inheritance ================================= + + public static class A { + public void doNotAnnotatedAtAll() { replyOk(); } + + @WebMethod(name = "onlyAnnotatedInA") + public void doOnlyAnnotatedInA() { replyOk(); } + + public void doOnlyAnnotatedInB() { replyOk(); } + + @WebMethod(name = "onlyAnnotatedInA-notOverrided") + public void doOnlyAnnotatedInANotOverrided() { replyOk(); } + + @WebMethod(name = "annotatedButDifferent1") + public void doAnnotatedButDifferent() { replyOk(); } + } + + public static class B extends A { + @Override + public void doNotAnnotatedAtAll() { replyOk(); } + + public void doOnlyAnnotatedInA() { replyOk(); } + + @WebMethod(name = "onlyAnnotatedInB") + public void doOnlyAnnotatedInB() { replyOk(); } + + // doOnlyAnnotatedInANotOverrided: not overrided + + @WebMethod(name = "annotatedButDifferent2") + public void doAnnotatedButDifferent() { replyOk(); } + } + + @TestExtension + public static class ABCase extends AbstractUnprotectedRootAction implements StaplerProxy { + @Override + public B getTarget() { + return new B(); + } + } + + @Test + public void testClassInheritance_notAnnotatedAtAll() throws Exception { + assertNotReachable("aBCase/notAnnotatedAtAll/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testClassInheritance_onlyAnnotatedInA() throws Exception { + assertReachable("aBCase/onlyAnnotatedInA/"); + } + + @Test + public void testClassInheritance_onlyAnnotatedInB() throws Exception { + assertReachable("aBCase/onlyAnnotatedInB/"); + } + + @Test + public void testClassInheritance_onlyAnnotatedInANotOverrided() throws Exception { + assertNotReachable("aBCase/onlyAnnotatedInANotOverrided/"); + } + + @Test + public void testClassInheritance_annotatedButDifferent1() throws Exception { + // only the last webMethod annotation is used + //TODO it breaks the Liskov substitutability +// assertReachable("b/annotatedButDifferent1/"); + assertNotReachable("aBCase/annotatedButDifferent1/"); + } + + @Test + public void testClassInheritance_annotatedButDifferent2() throws Exception { + assertReachable("aBCase/annotatedButDifferent2/"); + } + + //================================= interface implementation ================================= + public interface I { + void doNotAnnotated(); + + @WebMethod(name = "annotatedBoth") + void doAnnotatedBoth(); + + @WebMethod(name = "annotatedOnlyI") + void doAnnotatedOnlyI(); + + void doAnnotatedOnlyJ(); + + @WebMethod(name = "annotatedButDifferent1") + void doAnnotatedButDifferent(); + } + + public static class J implements I { + @Override + public void doNotAnnotated() { replyOk(); } + + @Override + @WebMethod(name = "annotatedBoth") + public void doAnnotatedBoth() { replyOk(); } + + @Override + public void doAnnotatedOnlyI() { replyOk(); } + + @Override + @WebMethod(name = "annotatedOnlyJ") + public void doAnnotatedOnlyJ() { replyOk(); } + + @Override + @WebMethod(name = "annotatedButDifferent2") + public void doAnnotatedButDifferent() { replyOk(); } + } + + @TestExtension + public static class IJCase extends AbstractUnprotectedRootAction implements StaplerProxy { + @Override + public J getTarget() { + return new J(); + } + } + + @Test + public void testInterfaceImplementation_notAnnotated() throws Exception { + assertNotReachable("iJCase/notAnnotated/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testInterfaceImplementation_annotatedBoth() throws Exception { + assertReachable("iJCase/annotatedBoth/"); + } + + @Test + public void testInterfaceImplementation_annotatedOnlyI() throws Exception { + assertReachable("iJCase/annotatedOnlyI/"); + } + + @Test + public void testInterfaceImplementation_annotatedOnlyJ() throws Exception { + assertReachable("iJCase/annotatedOnlyJ/"); + } + + @Test + public void testInterfaceImplementation_annotatedButDifferent1() throws Exception { + // only the last webMethod annotation is used + //TODO it breaks the Liskov substitutability + // assertReachable("j/annotatedButDifferent1/"); + assertNotReachable("iJCase/annotatedButDifferent1/"); + } + + @Test + public void testInterfaceImplementation_annotatedButDifferent2() throws Exception { + assertReachable("iJCase/annotatedButDifferent2/"); + } +} diff --git a/test/src/test/java/jenkins/security/stapler/DynamicTest.java b/test/src/test/java/jenkins/security/stapler/DynamicTest.java new file mode 100644 index 0000000000..ca0d597874 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/DynamicTest.java @@ -0,0 +1,73 @@ +package jenkins.security.stapler; + +import hudson.model.UnprotectedRootAction; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.HttpResponses; +import org.kohsuke.stapler.StaplerRequest; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import javax.annotation.CheckForNull; +import java.util.Arrays; +import java.util.stream.Stream; + +@Issue("SECURITY-400") +public class DynamicTest { + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Test + public void testRequestsDispatchedToEligibleDynamic() throws Exception { + JenkinsRule.WebClient wc = j.createWebClient(); + Stream.of("whatever", "displayName", "iconFileName", "urlName", "response1", "response2").forEach(url -> + { + try { + assertThat(wc.goTo("root/" + url).getWebResponse().getContentAsString(), containsString(url)); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + @TestExtension + public static class Root implements UnprotectedRootAction { + + @CheckForNull + @Override + public String getIconFileName() { + return null; + } + + @CheckForNull + @Override + public String getDisplayName() { + return null; + } + + @StaplerNotDispatchable + public HttpResponse getResponse1() { + return null; + } + + @StaplerNotDispatchable + public HttpResponse doResponse2() { + return null; + } + + public void doDynamic(StaplerRequest req) { + throw HttpResponses.errorWithoutStack(200, req.getRestOfPath()); + } + + @CheckForNull + @Override + public String getUrlName() { + return "root"; + } + } +} diff --git a/test/src/test/java/jenkins/security/stapler/GetterMethodFilterTest.java b/test/src/test/java/jenkins/security/stapler/GetterMethodFilterTest.java new file mode 100644 index 0000000000..7c5753c137 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/GetterMethodFilterTest.java @@ -0,0 +1,500 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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.cloudbees.hudson.plugins.folder.Folder; +import hudson.model.TopLevelItem; +import hudson.model.View; +import jenkins.model.Jenkins; +import org.junit.Test; +import org.jvnet.hudson.test.For; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.TestExtension; + +import java.awt.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertFalse; + +/** + * To check the previous behavior you can use: + *
+ * {@link org.kohsuke.stapler.MetaClass#LEGACY_GETTER_MODE} = true;
+ * 
+ * It will disable the usage of {@link TypedFilter} + */ +@Issue("SECURITY-400") +@For(TypedFilter.class) +public class GetterMethodFilterTest extends StaplerAbstractTest { + + @TestExtension + public static class TestWithReturnJavaPlatformObject extends AbstractUnprotectedRootAction { + public static boolean called = false; + + public String getString() { return "a";} + + // cannot provide side-effect since the String has no side-effect methods + public Object getObjectString() { return "a";} + + // but it opens wide range of potentially dangerous classes + public Object getObjectCustom() { + return new Object() { + // in order to provide a web entry-point + public void doIndex() { + replyOk(); + } + }; + } + + public Point getPoint() { return new Point(1, 2);} + + public Point getPointCustomChild() { + return new Point() { + // in order to provide a web entry-point + public void doIndex() { + replyOk(); + } + }; + } + + public Point getPointWithListener() { + return new Point() { + @Override + public double getX() { + // just to demonstrate the potential side-effect + called = true; + return super.getX(); + } + }; + } + } + + @Test + public void testWithReturnJavaPlatformObject_string() throws Exception { + assertNotReachable("testWithReturnJavaPlatformObject/string/"); + } + + @Test + public void testWithReturnJavaPlatformObject_objectString() throws Exception { + assertNotReachable("testWithReturnJavaPlatformObject/objectString/"); + } + + @Test + public void testWithReturnJavaPlatformObject_objectCustom() throws Exception { + assertNotReachable("testWithReturnJavaPlatformObject/objectCustom/"); + } + + @Test + public void testWithReturnJavaPlatformObject_point() throws Exception { + assertNotReachable("testWithReturnJavaPlatformObject/point/"); + } + + // previously reachable and so potentially open to future security vulnerability + @Test + public void testWithReturnJavaPlatformObject_pointCustomChild() throws Exception { + assertNotReachable("testWithReturnJavaPlatformObject/pointCustomChild/"); + } + + @Test + public void testWithReturnJavaPlatformObject_pointWithListener() throws Exception { + TestWithReturnJavaPlatformObject.called = false; + assertFalse(TestWithReturnJavaPlatformObject.called); + // could potentially trigger some side-effects + assertNotReachable("testWithReturnJavaPlatformObject/pointWithListener/x/"); + assertFalse(TestWithReturnJavaPlatformObject.called); + } + + @TestExtension + public static class TestWithReturnMultiple extends AbstractUnprotectedRootAction { + public List getList() { + return Arrays.asList(new Renderable(), new Renderable()); + } + + // as we cannot determine the element class due to type erasure, this is reachable + public List getListOfPoint() { + return Collections.singletonList(new RenderablePoint()); + } + + public List> getListOfList() { + return Collections.singletonList(Arrays.asList(new Renderable(), new Renderable())); + } + + public Renderable[] getArray() { return new Renderable[]{new Renderable(), new Renderable()};} + + // will not be accepted since the componentType is from JVM + public Point[] getArrayOfPoint() { + return new Point[]{new Point() { + public void doIndex() {replyOk();} + }}; + } + + public Renderable[][] getArrayOfArray() { + return new Renderable[][]{ + new Renderable[]{new Renderable(), new Renderable()} + }; + } + + @SuppressWarnings("unchecked") + public List[] getArrayOfList() { + List list = Arrays.asList(new Renderable(), new Renderable()); + return (List[]) Collections.singletonList(list).toArray(new List[0]); + } + + public List getListOfArray() { + return Collections.singletonList( + new Renderable[]{new Renderable(), new Renderable()} + ); + } + + public Map getMap() { + return new HashMap() {{ + put("a", new Renderable()); + }}; + } + } + + @Test + public void testWithReturnMultiple_list() throws Exception { + assertNotReachable("testWithReturnMultiple/list/"); + assertNotReachable("testWithReturnMultiple/list/0/"); + assertNotReachable("testWithReturnMultiple/list/1/"); + assertNotReachable("testWithReturnMultiple/list/2/"); + } + + @Test + public void testWithReturnMultiple_listOfPoint() throws Exception { + assertNotReachable("testWithReturnMultiple/listOfPoint/"); + assertNotReachable("testWithReturnMultiple/listOfPoint/0/"); + assertNotReachable("testWithReturnMultiple/listOfPoint/1/"); + } + + @Test + public void testWithReturnMultiple_listOfList() throws Exception { + assertNotReachable("testWithReturnMultiple/listOfList/"); + assertNotReachable("testWithReturnMultiple/listOfList/0/"); + assertNotReachable("testWithReturnMultiple/listOfList/1/"); + assertNotReachable("testWithReturnMultiple/listOfList/0/0/"); + assertNotReachable("testWithReturnMultiple/listOfList/0/1/"); + assertNotReachable("testWithReturnMultiple/listOfList/0/2/"); + } + + @Test + public void testWithReturnMultiple_array() throws Exception { + assertNotReachable("testWithReturnMultiple/array/"); + assertReachable("testWithReturnMultiple/array/0/"); + assertReachable("testWithReturnMultiple/array/1/"); + assertNotReachable("testWithReturnMultiple/array/2/"); + } + + @Test + public void testWithReturnMultiple_arrayOfPoint() throws Exception { + assertNotReachable("testWithReturnMultiple/arrayOfPoint/"); + assertNotReachable("testWithReturnMultiple/arrayOfPoint/0/"); + assertNotReachable("testWithReturnMultiple/arrayOfPoint/1/"); + } + + @Test + public void testWithReturnMultiple_arrayOfArray() throws Exception { + assertNotReachable("testWithReturnMultiple/arrayOfArray/"); + assertNotReachable("testWithReturnMultiple/arrayOfArray/0/"); + assertNotReachable("testWithReturnMultiple/arrayOfArray/1/"); + assertReachable("testWithReturnMultiple/arrayOfArray/0/0/"); + assertReachable("testWithReturnMultiple/arrayOfArray/0/1/"); + assertNotReachable("testWithReturnMultiple/arrayOfArray/0/2/"); + } + + @Test + public void testWithReturnMultiple_arrayOfList() throws Exception { + assertNotReachable("testWithReturnMultiple/arrayOfList/"); + assertNotReachable("testWithReturnMultiple/arrayOfList/0/"); + assertNotReachable("testWithReturnMultiple/arrayOfList/1/"); + assertNotReachable("testWithReturnMultiple/arrayOfList/0/0/"); + assertNotReachable("testWithReturnMultiple/arrayOfList/0/1/"); + assertNotReachable("testWithReturnMultiple/arrayOfList/0/2/"); + } + + @Test + public void testWithReturnMultiple_listOfArray() throws Exception { + assertNotReachable("testWithReturnMultiple/listOfArray/"); + assertNotReachable("testWithReturnMultiple/listOfArray/0/"); + assertNotReachable("testWithReturnMultiple/listOfArray/1/"); + assertNotReachable("testWithReturnMultiple/listOfArray/0/0/"); + assertNotReachable("testWithReturnMultiple/listOfArray/0/1/"); + assertNotReachable("testWithReturnMultiple/listOfArray/0/2/"); + } + + @Test + public void testWithReturnMultiple_map() throws Exception { + assertNotReachable("testWithReturnMultiple/map/"); + assertNotReachable("testWithReturnMultiple/map/a/"); + assertNotReachable("testWithReturnMultiple/map/b/"); + } + + @TestExtension + public static class TestWithReturnCoreObject extends AbstractUnprotectedRootAction { + public View.People getPeople() { + // provide an index jelly view + return new View.People(Jenkins.getInstance()); + } + } + + @Test + public void testWithReturnCoreObject_people() throws Exception { + assertReachableWithoutOk("testWithReturnCoreObject/people/"); + } + + @Test + public void testTopLevelItemIsLegal() throws Exception { + TopLevelItem item = j.createFreeStyleProject(); + assertReachableWithoutOk("job/" + item.getName()); + } + + @TestExtension + public static class TestWithReturnPluginObject extends AbstractUnprotectedRootAction { + public Folder getFolder() { + return new Folder(Jenkins.getInstance(), "testFolder"); + } + } + + @Test + public void testWithReturnPluginObject_folder() throws Exception { + // the search part is just to get something from the call + assertReachableWithoutOk("testWithReturnPluginObject/folder/search/suggest/?query=xxx"); + } + + // full package name just to be explicit + @TestExtension + public static class TestWithReturnThirdPartyObject extends AbstractUnprotectedRootAction { + public org.apache.commons.codec.binary.Base64 getBase64() { + return new org.apache.commons.codec.binary.Base64(); + } + + public org.apache.commons.codec.Encoder getEncoder() { + return new org.apache.commons.codec.binary.Base64(); + } + + public org.apache.commons.codec.Encoder getEncoderCustomChild() { + return new org.apache.commons.codec.Encoder() { + @Override + public Object encode(Object source) throws org.apache.commons.codec.EncoderException { + // it's not about implementation... + return null; + } + + public void doIndex() { + // it's about sending a message + replyOk(); + } + }; + } + } + + // the class itself was reachable but no more interaction are available and so return 404 + + @Test + public void testWithReturnThirdPartyObject_base32() throws Exception { + assertNotReachable("testWithReturnThirdPartyObject/base32/"); + } + + // the class itself was reachable but no more interaction are available and so return 404, + // in case there is some callable methods, we could create some side-effect even we got 404 + @Test + public void testWithReturnThirdPartyObject_encoder() throws Exception { + assertNotReachable("testWithReturnThirdPartyObject/encoder/"); + } + + // as we add a entry-point in the class, now it can propose some interaction, + // dangerous behavior that is not prohibited + @Test + public void testWithReturnThirdPartyObject_encoderCustomChild() throws Exception { + assertNotReachable("testWithReturnThirdPartyObject/encoderCustomChild/"); + } + + + //================================= getter methods with primitives ================================= + + @TestExtension + public static class TestWithReturnPrimitives extends AbstractUnprotectedRootAction { + public int getInteger() { return 1;} + + public Integer getIntegerObject() { return 1;} + + public long getLong() { return 1L;} + + public Long getLongObject() { return 1L;} + + public short getShort() { return (short) 1;} + + public Short getShortObject() { return 1;} + + public byte getByte() { return (byte) 1;} + + public Byte getByteObject() { return (byte) 1;} + + public boolean getBoolean() { return true;} + + public Boolean getBooleanObject() { return Boolean.TRUE;} + + public char getChar() { return 'a';} + + public Character getCharObject() { return 'a';} + + public float getFloat() { return 1.0f;} + + public Float getFloatObject() { return 1.0f;} + + public double getDouble() { return 1.0;} + + public Double getDoubleObject() { return 1.0;} + + public void getVoid() { } + + public Void getVoidObject() { return null; } + } + + @Test + public void testTestWithReturnPrimitives_integer() throws Exception { + assertNotReachable("testWithReturnPrimitives/integer/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_integerObject() throws Exception { + assertNotReachable("testWithReturnPrimitives/integerObject/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_long() throws Exception { + assertNotReachable("testWithReturnPrimitives/long/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_longObject() throws Exception { + assertNotReachable("testWithReturnPrimitives/longObject/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_short() throws Exception { + assertNotReachable("testWithReturnPrimitives/short/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_shortObject() throws Exception { + assertNotReachable("testWithReturnPrimitives/shortObject/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_byte() throws Exception { + assertNotReachable("testWithReturnPrimitives/byte/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_byteObject() throws Exception { + assertNotReachable("testWithReturnPrimitives/byteObject/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_boolean() throws Exception { + assertNotReachable("testWithReturnPrimitives/boolean/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_booleanObject() throws Exception { + assertNotReachable("testWithReturnPrimitives/booleanObject/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_char() throws Exception { + assertNotReachable("testWithReturnPrimitives/char/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_charObject() throws Exception { + assertNotReachable("testWithReturnPrimitives/charObject/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_float() throws Exception { + assertNotReachable("testWithReturnPrimitives/float/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_floatObject() throws Exception { + assertNotReachable("testWithReturnPrimitives/floatObject/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_double() throws Exception { + assertNotReachable("testWithReturnPrimitives/double/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_doubleObject() throws Exception { + assertNotReachable("testWithReturnPrimitives/doubleObject/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_void() throws Exception { + assertNotReachable("testWithReturnPrimitives/void/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void testTestWithReturnPrimitives_voidObject() throws Exception { + assertNotReachable("testWithReturnPrimitives/voidObject/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + //================================= getter methods ================================= + + @TestExtension + public static class TestWithReturnWithinStaplerScope extends DoActionFilterTest.AbstractUnprotectedRootAction { + public Renderable getRenderable() { return new Renderable();} + } + + @Test + public void testWithReturnWithinStaplerScope_renderable() throws Exception { + assertReachable("testWithReturnWithinStaplerScope/renderable/"); + assertReachable("testWithReturnWithinStaplerScope/renderable/valid/"); + } +} diff --git a/test/src/test/java/jenkins/security/stapler/JenkinsSupportAnnotationsTest.java b/test/src/test/java/jenkins/security/stapler/JenkinsSupportAnnotationsTest.java new file mode 100644 index 0000000000..82a0408da4 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/JenkinsSupportAnnotationsTest.java @@ -0,0 +1,26 @@ +package jenkins.security.stapler; + +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.recipes.WithPlugin; + +@Issue("SECURITY-400") +@For({StaplerDispatchable.class, StaplerAccessibleType.class}) +public class JenkinsSupportAnnotationsTest { + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Test + @WithPlugin("annotations-test.hpi") + public void testPluginWithAnnotations() throws Exception { + // test fails if TypedFilter ignores @StaplerDispatchable + j.createWebClient().goTo("annotationsTest/whatever", ""); + + // test fails if TypedFilter ignores @StaplerAccessibleType + j.createWebClient().goTo("annotationsTest/transit/response", ""); + } +} diff --git a/test/src/test/java/jenkins/security/stapler/PreventRoutingTest.java b/test/src/test/java/jenkins/security/stapler/PreventRoutingTest.java new file mode 100644 index 0000000000..b8c11cada8 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/PreventRoutingTest.java @@ -0,0 +1,120 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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 org.junit.Ignore; +import org.junit.Test; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.Ancestor; +import org.kohsuke.stapler.HttpResponses; +import org.kohsuke.stapler.Stapler; +import org.kohsuke.stapler.StaplerProxy; +import org.kohsuke.stapler.StaplerRequest; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import java.util.List; + +public class PreventRoutingTest extends StaplerAbstractTest { + + @TestExtension + public static class TargetNull extends AbstractUnprotectedRootAction implements StaplerProxy { + @Override + public @CheckForNull String getUrlName() { + return "target-null"; + } + + @Override + public Object getTarget() { + // in case of null, it's "this" that is considered + return null; + } + + public Renderable getLegitRoutable(){ + return new Renderable(); + } + } + @Test + // TODO un-ignore once we use a Stapler release with the fix for this + @Ignore("Does not behave as intended before https://github.com/stapler/stapler/pull/149") + public void getTargetNull_isNotRoutable() throws Exception { + assertNotReachable("target-null/legitRoutable"); + } + + @TestExtension + public static class TargetNewObject extends AbstractUnprotectedRootAction implements StaplerProxy { + @Override + public @CheckForNull String getUrlName() { + return "target-new-object"; + } + + @Override + public Object getTarget() { + // Object is not routable + return new Object(); + } + + public Renderable getLegitRoutable(){ + return new Renderable(); + } + } + @Test + public void getTargetNewObject_isNotRoutable() throws Exception { + assertNotReachable("target-new-object/legitRoutable"); + } + + @TestExtension + public static class NotARequest extends AbstractUnprotectedRootAction { + @Override + public @CheckForNull String getUrlName() { + return "not-a-request"; + } + + public Renderable getLegitRoutable(){ + notStaplerGetter(this); + return new Renderable(); + } + + // just to validate it's ok + public Renderable getLegitRoutable2(){ + return new Renderable(); + } + } + + private static void notStaplerGetter(@Nonnull Object o){ + StaplerRequest req = Stapler.getCurrentRequest(); + if (req != null) { + List ancestors = req.getAncestors(); + if (!ancestors.isEmpty() && ancestors.get(ancestors.size() - 1).getObject() == o) { + throw HttpResponses.notFound(); + } + } + } + + @Test + public void regularGetter_notARequest() throws Exception { + assertReachable("not-a-request/legitRoutable2"); + assertNotReachable("not-a-request/legitRoutable"); + } +} diff --git a/test/src/test/java/jenkins/security/stapler/Security400Test.java b/test/src/test/java/jenkins/security/stapler/Security400Test.java new file mode 100644 index 0000000000..afb4db75aa --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/Security400Test.java @@ -0,0 +1,609 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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.cloudbees.hudson.plugins.folder.computed.FolderCron; +import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebRequest; +import hudson.Launcher; +import hudson.model.AbstractBuild; +import hudson.model.AsyncPeriodicWork; +import hudson.model.BuildListener; +import hudson.model.Descriptor; +import hudson.model.FreeStyleBuild; +import hudson.model.FreeStyleProject; +import hudson.model.PeriodicWork; +import hudson.model.Result; +import hudson.model.TaskListener; +import hudson.model.queue.QueueTaskFuture; +import hudson.security.FullControlOnceLoggedInAuthorizationStrategy; +import hudson.security.HudsonPrivateSecurityRealm; +import hudson.tasks.Builder; +import jenkins.model.Jenkins; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockAuthorizationStrategy; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.WebApp; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * To check the previous behavior you can use: + *
+ * {@link org.kohsuke.stapler.MetaClass#LEGACY_WEB_METHOD_MODE} = true;
+ * {@link org.kohsuke.stapler.MetaClass#LEGACY_GETTER_MODE} = true;
+ * 
+ */ +@Issue("SECURITY-400") +public class Security400Test { + @Rule + public JenkinsRule j = new JenkinsRule(); + + private static boolean filteredDoActionTriggered = false; + + @Before + public void prepareFilterListener(){ + WebApp webApp = WebApp.get(j.jenkins.servletContext); + webApp.setFilteredDoActionTriggerListener((f, req, rsp, node) -> { + filteredDoActionTriggered = true; + return false; + }); + webApp.setFilteredGetterTriggerListener((f, req, rsp, node, expression) -> { + filteredDoActionTriggered = true; + return false; + }); + } + + @After + public void resetFilter(){ + filteredDoActionTriggered = false; + } + + private void assertRequestWasBlockedAndResetFlag(){ + assertTrue("No request was blocked", filteredDoActionTriggered); + filteredDoActionTriggered = false; + } + + private void assertRequestWasNotBlocked(){ + assertFalse("There was at least a request that was blocked", filteredDoActionTriggered); + } + + @Test + @Issue("SECURITY-391") + public void asyncDoRun() throws Exception { + j.createWebClient().assertFails("extensionList/" + AsyncPeriodicWork.class.getName() + "/" + Work.class.getName() + "/run", HttpURLConnection.HTTP_NOT_FOUND); + Thread.sleep(1000); // give the thread a moment to finish + assertFalse("should never have run", ran); + } + + private static boolean ran; + + @TestExtension("asyncDoRun") + public static class Work extends AsyncPeriodicWork { + public Work() { + super("Test"); + } + + @Override + public long getRecurrencePeriod() { + return Long.MAX_VALUE; // do not run after init() + } + + @Override + protected void execute(TaskListener listener) throws IOException, InterruptedException { + ran = true; + } + } + + // require a dependency on cloudbees-folder-plugin + @Test + @Issue("SECURITY-397") + // particular case of SECURITY-391 + public void folderCronDoRun() throws Exception { + j.createWebClient().assertFails("extensionList/" + PeriodicWork.class.getName() + "/" + FolderCron.class.getName() + "/run", HttpURLConnection.HTTP_NOT_FOUND); + assertRequestWasBlockedAndResetFlag(); + } + + /** + * replacement of "computers/0/executors/0/contextClassLoader/context/handlers/0/sessionManager/stop" attack + */ + @Test + @Issue("SECURITY-404") + public void avoidDangerousAccessToSession() throws Exception { + j.jenkins.setCrumbIssuer(null); + + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy( + new MockAuthorizationStrategy() + .grant(Jenkins.ADMINISTER).everywhere().to("admin") + .grant(Jenkins.READ).everywhere().to("user") + ); + + JenkinsRule.WebClient wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + wc.login("admin"); + + JenkinsRule.WebClient wc2 = j.createWebClient(); + wc2.getOptions().setThrowExceptionOnFailingStatusCode(false); + wc2.login("user"); + + Page page; + + page = wc.goTo("whoAmI/api/xml/", null); + System.out.println(page.getWebResponse().getContentAsString()); + assertThat(page.getWebResponse().getContentAsString(), containsString("false")); + + page = wc2.goTo("whoAmI/api/xml/", null); + System.out.println(page.getWebResponse().getContentAsString()); + assertThat(page.getWebResponse().getContentAsString(), containsString("false")); + + assertRequestWasNotBlocked(); + + // the doXxx fix prevents the doStop to be executed + // and in addition the getXxx fix prevents the getContextHandler to be used as navigation + + // the first beans/0 return the HashedSession + // the second beans/0 return the HashSessionManager + page = wc2.goTo("adjuncts//webApp/context/contextHandler/beans/0/beans/0/stop", null); + // other possible path + // page = wc.goTo("adjuncts//webApp/someStapler/currentRequest/session/servletContext/contextHandler/beans/0/beans/0/stop", null); + // page = wc.goTo("adjuncts//webApp/someStapler/currentRequest/servletContext/contextHandler/beans/0/beans/0/stop", null); + +// assertEquals(404, page.getWebResponse().getStatusCode()); +// assertRequestWasBlockedAndResetFlag(); + // getWebApp is now forbidden + assertEquals(403, page.getWebResponse().getStatusCode()); + + // if the call was successful, both are disconnected and anonymous would have been true + + page = wc.goTo("whoAmI/api/xml/", null); + System.out.println(page.getWebResponse().getContentAsString()); + assertThat(page.getWebResponse().getContentAsString(), containsString("false")); + + page = wc2.goTo("whoAmI/api/xml/", null); + System.out.println(page.getWebResponse().getContentAsString()); + assertThat(page.getWebResponse().getContentAsString(), containsString("false")); + + assertRequestWasNotBlocked(); + + // similar approach but different impact: + // can put null into desired session key (no impact yet) + // session impl. is HashedSession + // page = wc.goTo("adjuncts//webApp/someStapler/currentRequest/session/putOrRemove/ACEGI_SECURITY_CONTEXT/", null); + } + + @Test + @Issue("SECURITY-404") + public void ensureDoStopStillReachable() throws Exception { + j.jenkins.setCrumbIssuer(null); + JenkinsRule.WebClient wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + // used as a reference passed to the build step + AtomicInteger atomicResult = new AtomicInteger(0); + FreeStyleProject p = j.createFreeStyleProject(); + + final Semaphore semaphore = new Semaphore(0); + + p.getBuildersList().add(new SemaphoredBuilder(semaphore, atomicResult)); + + // to be sure to reach the correct one + j.jenkins.setNumExecutors(1); + + { // preliminary test, calling the stop method without any executor results in 404 + WebRequest request = new WebRequest(new URL(j.getURL() + "computers/0/executors/0/stop"), HttpMethod.POST); + Page page = wc.getPage(request); + assertEquals(404, page.getWebResponse().getStatusCode()); + assertRequestWasNotBlocked(); + } + + { // first try, we let the build finishes normally + QueueTaskFuture futureBuild = p.scheduleBuild2(0); + futureBuild.waitForStart(); + + // let the build finishes + semaphore.release(1); + j.assertBuildStatus(Result.SUCCESS, futureBuild); + assertEquals(1, atomicResult.get()); + } + + { // second try, we need to reach the stop method in executor to interrupt the build + atomicResult.set(0); + assertEquals(0, atomicResult.get()); + QueueTaskFuture futureBuild = p.scheduleBuild2(0); + futureBuild.waitForStart(); + + WebRequest request = new WebRequest(new URL(j.getURL() + "computers/0/executors/0/stop"), HttpMethod.POST); + Page page = wc.getPage(request); + assertEquals(404, page.getWebResponse().getStatusCode()); + assertRequestWasNotBlocked(); + + j.assertBuildStatus(Result.FAILURE, futureBuild); + assertEquals(3, atomicResult.get()); + } + } + + @Test + @Issue("SECURITY-404") + public void anonCannotReadTextConsole() throws Exception { + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + FullControlOnceLoggedInAuthorizationStrategy authorizationStrategy = new FullControlOnceLoggedInAuthorizationStrategy(); + authorizationStrategy.setAllowAnonymousRead(false); + j.jenkins.setAuthorizationStrategy(authorizationStrategy); + + JenkinsRule.WebClient wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + FreeStyleProject p = j.createFreeStyleProject(); + + Semaphore semaphore = new Semaphore(0); + + p.getBuildersList().add(new SemaphoredBuilder(semaphore, new AtomicInteger(0))); + + // to be sure to reach the correct one + j.jenkins.setNumExecutors(1); + + { // preliminary test, calling the consoleText method without any executor results in 404 + Page page = wc.goTo("computers/0/executors/0/currentExecutable/consoleText", null); + checkPageIsRedirectedToLogin(page); + assertRequestWasNotBlocked(); + } + + { // as Connected User, we start the build and try to get the console, ensure current expected behavior still works + wc.login("foo"); + + QueueTaskFuture futureBuild = p.scheduleBuild2(0); + futureBuild.waitForStart(); + + Page page = wc.goTo("computers/0/executors/0/currentExecutable/consoleText", null); + assertEquals(200, page.getWebResponse().getStatusCode()); + assertThat(page.getWebResponse().getContentAsString(), containsString(SemaphoredBuilder.START_MESSAGE)); + assertRequestWasNotBlocked(); + + semaphore.release(1); + j.assertBuildStatus(Result.SUCCESS, futureBuild); + } + + { // as Anonymous, we start the build and try to get the console + wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + QueueTaskFuture futureBuild = p.scheduleBuild2(0); + futureBuild.waitForStart(); + + Page page = wc.goTo("computers/0/executors/0/currentExecutable/consoleText", null); + checkPageIsRedirectedToLogin(page); + assertThat(page.getWebResponse().getContentAsString(), not(containsString(SemaphoredBuilder.START_MESSAGE))); + assertRequestWasNotBlocked(); + + semaphore.release(1); + j.assertBuildStatus(Result.SUCCESS, futureBuild); + } + } + + + @Test + @Issue("SECURITY-404") + public void anonCannotAccessExecutorApi() throws Exception { + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + FullControlOnceLoggedInAuthorizationStrategy authorizationStrategy = new FullControlOnceLoggedInAuthorizationStrategy(); + authorizationStrategy.setAllowAnonymousRead(false); + j.jenkins.setAuthorizationStrategy(authorizationStrategy); + + JenkinsRule.WebClient wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + FreeStyleProject p = j.createFreeStyleProject(); + + Semaphore semaphore = new Semaphore(0); + + p.getBuildersList().add(new SemaphoredBuilder(semaphore, new AtomicInteger(0))); + + // to be sure to reach the correct one + j.jenkins.setNumExecutors(1); + + { + Page page = wc.goTo("computers/0/executors/0/api/xml", null); + checkPageIsRedirectedToLogin(page); + assertRequestWasNotBlocked(); + } + + { // as Connected User, we start the build and can access the executor api + QueueTaskFuture futureBuild = p.scheduleBuild2(0); + futureBuild.waitForStart(); + + wc.login("foo"); + Page page = wc.goTo("computers/0/executors/0/api/xml", null); + assertEquals(200, page.getWebResponse().getStatusCode()); + assertThat(page.getWebResponse().getContentAsString(), containsString(p.getUrl())); + assertRequestWasNotBlocked(); + + semaphore.release(1); + j.assertBuildStatus(Result.SUCCESS, futureBuild); + + wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + } + + { // as Anonymous, we start the build and cannot access the executor api + QueueTaskFuture futureBuild = p.scheduleBuild2(0); + futureBuild.waitForStart(); + + Page page = wc.goTo("computers/0/executors/0/api/xml", null); + checkPageIsRedirectedToLogin(page); + assertThat(page.getWebResponse().getContentAsString(), not(containsString(p.getUrl()))); + assertRequestWasNotBlocked(); + + semaphore.release(1); + j.assertBuildStatus(Result.SUCCESS, futureBuild); + } + } + + @Test + @Issue("SECURITY-404") + public void anonCannotAccessJenkinsItemMap() throws Exception { + JenkinsRule.WebClient wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + FreeStyleProject p = j.createFreeStyleProject(); + + { // try to access /itemMap/ + wc.login("foo"); + Page page = wc.goTo("itemMap/" + p.getName() + "/api/xml", null); + assertEquals(404, page.getWebResponse().getStatusCode()); + assertThat(page.getWebResponse().getContentAsString(), not(containsString(" build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { + try { + listener.getLogger().println(START_MESSAGE); + boolean result = semaphore.tryAcquire(20, TimeUnit.SECONDS); + if (result) { + listener.getLogger().println("permit acquired"); + atomicInteger.set(1); + return true; + } else { + atomicInteger.set(2); + return false; + } + } catch (InterruptedException e) { + atomicInteger.set(3); + return false; + } + } + + @TestExtension + public static class DescriptorImpl extends Descriptor {} + } + + // currently there is no other way to reach logRecorderManager in core / or plugin + @Test + @Issue("SECURITY-471") + public void ensureLogRecordManagerAccessibleOnlyByAdmin() throws Exception { + j.jenkins.setCrumbIssuer(null); + + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy( + new MockAuthorizationStrategy() + .grant(Jenkins.ADMINISTER).everywhere().to("admin") + .grant(Jenkins.READ).everywhere().to("user") + ); + + String logNameForAdmin = "testLoggerAdmin"; + String logNameForUser = "testLoggerUser"; + + { // admin can do everything + JenkinsRule.WebClient wc = j.createWebClient(); + wc.login("admin"); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + // ensure the logger does not exist before the creation + assertEquals(404, wc.goTo("log/" + logNameForAdmin + "/autoCompleteLoggerName/?value=a", null).getWebResponse().getStatusCode()); + assertRequestWasNotBlocked(); + + WebRequest request = new WebRequest(new URL(j.getURL() + "log/newLogRecorder/?name=" + logNameForAdmin), HttpMethod.POST); + + wc.getOptions().setRedirectEnabled(false); + Page page = wc.getPage(request); + assertEquals(302, page.getWebResponse().getStatusCode()); + assertRequestWasNotBlocked(); + + // after creation the logger exists + j.assertGoodStatus(wc.goTo("log/" + logNameForAdmin + "/autoCompleteLoggerName/?value=a", null)); + assertRequestWasNotBlocked(); + + assertEquals(404, wc.goTo("log/" + "nonExistingName" + "/autoCompleteLoggerName/?value=a", null).getWebResponse().getStatusCode()); + assertRequestWasNotBlocked(); + } + + { // user is blocked + JenkinsRule.WebClient wc = j.createWebClient(); + wc.login("user"); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + // no right to check the existence of a logger + assertEquals(403, wc.goTo("log/" + logNameForUser + "/autoCompleteLoggerName/?value=a", null).getWebResponse().getStatusCode()); + assertRequestWasNotBlocked(); + + WebRequest request = new WebRequest(new URL(j.getURL() + "log/newLogRecorder/?name=" + logNameForUser), HttpMethod.POST); + + wc.getOptions().setRedirectEnabled(false); + Page page = wc.getPage(request); + assertEquals(403, page.getWebResponse().getStatusCode()); + assertRequestWasNotBlocked(); + + // after the failed attempt, the logger is not created + assertEquals(403, wc.goTo("log/" + logNameForUser + "/autoCompleteLoggerName/?value=a", null).getWebResponse().getStatusCode()); + assertRequestWasNotBlocked(); + } + + { // admin can check the non-existence after user failed creation also + + JenkinsRule.WebClient wc = j.createWebClient(); + wc.login("admin"); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + // ensure the logger was not created by the user (check in case the request returned 403 but created the logger silently) + assertEquals(404, wc.goTo("log/" + logNameForUser + "/autoCompleteLoggerName/?value=a", null).getWebResponse().getStatusCode()); + assertRequestWasNotBlocked(); + } + } + + @Test + public void anonCannotHaveTheListOfUsers() throws Exception { + j.jenkins.setCrumbIssuer(null); + + FullControlOnceLoggedInAuthorizationStrategy authorizationStrategy = new FullControlOnceLoggedInAuthorizationStrategy(); + j.jenkins.setAuthorizationStrategy(authorizationStrategy); + + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + j.jenkins.setSecurityRealm(securityRealm); + securityRealm.createAccount("admin", "admin"); + securityRealm.createAccount("secretUser", "secretUser"); + + { // admin should have access to the user list + JenkinsRule.WebClient wc = j.createWebClient(); + wc.login("admin"); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + Page page = wc.goTo("securityRealm"); + assertEquals(200, page.getWebResponse().getStatusCode()); + assertThat(page.getWebResponse().getContentAsString(), containsString("secretUser")); + assertRequestWasNotBlocked(); + } + + // with or without the anonymousRead, anonymous are not allowed to have access to + // list of users in securityRealm + authorizationStrategy.setAllowAnonymousRead(true); + { // without any read permission the anon have access to the user list + JenkinsRule.WebClient wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + wc.getOptions().setRedirectEnabled(false); + + Page page = wc.goTo("securityRealm/", null); + checkPageIsRedirectedToLogin(page); + assertThat(page.getWebResponse().getContentAsString(), not(containsString("secretUser"))); + assertRequestWasNotBlocked(); + + page = wc.goTo("asynchPeople/", null); + assertEquals(200, page.getWebResponse().getStatusCode()); + // javascript will load the user list asynch + assertThat(page.getWebResponse().getContentAsString(), containsString("Includes all known")); + assertRequestWasNotBlocked(); + } + + authorizationStrategy.setAllowAnonymousRead(false); + { // and with restriction, the anonymous users cannot read the user list + JenkinsRule.WebClient wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + wc.getOptions().setRedirectEnabled(false); + + Page page = wc.goTo("securityRealm/", null); + checkPageIsRedirectedToLogin(page); + assertThat(page.getWebResponse().getContentAsString(), not(containsString("secretUser"))); + assertRequestWasNotBlocked(); + + // with the restriction we disallow the anon to even read the list of all users + page = wc.goTo("asynchPeople/", null); + checkPageIsRedirectedToLogin(page); + assertThat(page.getWebResponse().getContentAsString(), not(containsString("secretUser"))); + assertRequestWasNotBlocked(); + } + } + + @Test + @Issue("SECURITY-722") + public void noAccessToAllUsers() throws Exception { + j.jenkins.setCrumbIssuer(null); + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + j.jenkins.setSecurityRealm(securityRealm); + securityRealm.createAccount("admin", "admin"); + + j.jenkins.setAuthorizationStrategy( + new MockAuthorizationStrategy() + .grant(Jenkins.ADMINISTER).everywhere().to("admin") + ); + + { // neither anon have access to the allUsers end point + JenkinsRule.WebClient wc = j.createWebClient(); + // anonymous user + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + Page page = wc.goTo("securityRealm/allUsers/" + 0 + "/descriptorByName/jenkins.security.ApiTokenProperty/help/apiToken/"); + assertEquals(404, page.getWebResponse().getStatusCode()); + assertRequestWasBlockedAndResetFlag(); + } + + { // nor the admin have that access + JenkinsRule.WebClient wc = j.createWebClient(); + wc.login("admin"); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + Page page = wc.goTo("securityRealm/allUsers/" + 0 + "/descriptorByName/jenkins.security.ApiTokenProperty/help/apiToken/"); + assertEquals(404, page.getWebResponse().getStatusCode()); + assertRequestWasBlockedAndResetFlag(); + } + } + + // // does not work in 2.60 since the method was added in 2.91+ + // String newLogin = "newUser"; + // j.createWebClient().goTo("securityRealm/allUsers/0/orCreateByIdOrFullName/" + newLogin + "/"); + + private void checkPageIsRedirectedToLogin(Page page) { + assertEquals(200, page.getWebResponse().getStatusCode()); + assertThat(page.getUrl().getPath(), containsString("login")); + assertThat(page.getUrl().getQuery(), containsString("from")); + } +} diff --git a/test/src/test/java/jenkins/security/stapler/StaplerAbstractTest.java b/test/src/test/java/jenkins/security/stapler/StaplerAbstractTest.java new file mode 100644 index 0000000000..ccc40a4f37 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/StaplerAbstractTest.java @@ -0,0 +1,205 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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.FailingHttpStatusCodeException; +import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebRequest; +import hudson.model.UnprotectedRootAction; +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.ClassRule; +import org.jvnet.hudson.test.JenkinsRule; +import org.kohsuke.stapler.Stapler; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.WebApp; +import org.kohsuke.stapler.WebMethod; + +import javax.annotation.CheckForNull; +import java.awt.*; +import java.io.IOException; +import java.net.URL; + +import static org.hamcrest.CoreMatchers.startsWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public abstract class StaplerAbstractTest { + @ClassRule + public static JenkinsRule rule = new JenkinsRule(); + protected JenkinsRule j; + + protected JenkinsRule.WebClient wc; + + protected WebApp webApp; + + protected static boolean filteredGetMethodTriggered = false; + protected static boolean filteredDoActionTriggered = false; + protected static boolean filteredFieldTriggered = false; + + @Before + public void setUp() throws Exception { + j = rule; + j.jenkins.setCrumbIssuer(null); + wc = j.createWebClient(); + + this.webApp = (WebApp) j.jenkins.servletContext.getAttribute(WebApp.class.getName()); + + webApp.setFilteredGetterTriggerListener((f, req, rst, node, expression) -> { + filteredGetMethodTriggered = true; + return false; + }); + webApp.setFilteredDoActionTriggerListener((f, req, rsp, node) -> { + filteredDoActionTriggered = true; + return false; + }); + webApp.setFilteredFieldTriggerListener((f, req, rsp, node, expression) -> { + filteredFieldTriggered = true; + return false; + }); + + filteredGetMethodTriggered = false; + filteredDoActionTriggered = false; + filteredFieldTriggered = false; + } + + //================================= utility class ================================= + + protected static class AbstractUnprotectedRootAction implements UnprotectedRootAction { + @Override + public @CheckForNull String getIconFileName() { + return null; + } + + @Override + public @CheckForNull String getDisplayName() { + return null; + } + + @Override + public @CheckForNull String getUrlName() { + return StringUtils.uncapitalize(this.getClass().getSimpleName()); + } + } + + public static final String RENDERABLE_CLASS_SIGNATURE = "class jenkins.security.stapler.StaplerAbstractTest.Renderable"; + protected static class Renderable { + + public void doIndex() {replyOk();} + + @WebMethod(name = "valid") + public void valid() {replyOk();} + } + + protected static class ParentRenderable { + public Renderable getRenderable(){ + return new Renderable(); + } + } + + protected static class RenderablePoint extends Point { + public void doIndex() {replyOk();} + } + + //================================= utility methods ================================= + + protected static void replyOk() { + StaplerResponse resp = Stapler.getCurrentResponse(); + try { + resp.getWriter().write("ok"); + resp.flushBuffer(); + } catch (IOException e) {} + } + + //================================= testing methods ================================= + + protected void assertGetMethodRequestWasBlockedAndResetFlag() { + assertTrue("No get method request was blocked", filteredGetMethodTriggered); + filteredGetMethodTriggered = false; + } + protected void assertDoActionRequestWasBlockedAndResetFlag() { + assertTrue("No do action request was blocked", filteredDoActionTriggered); + filteredDoActionTriggered = false; + } + protected void assertFieldRequestWasBlockedAndResetFlag() { + assertTrue("No field request was blocked", filteredFieldTriggered); + filteredFieldTriggered = false; + } + protected void assertGetMethodActionRequestWasNotBlocked() { + assertFalse("There was at least one get method request that was blocked", filteredGetMethodTriggered); + } + protected void assertDoActionRequestWasNotBlocked() { + assertFalse("There was at least one do action request that was blocked", filteredDoActionTriggered); + } + protected void assertFieldRequestWasNotBlocked() { + assertFalse("There was at least one field request that was blocked", filteredFieldTriggered); + } + + protected void assertReachable(String url, HttpMethod method) throws IOException { + try { + Page page = wc.getPage(new WebRequest(new URL(j.getURL(), url), method)); + assertEquals(200, page.getWebResponse().getStatusCode()); + assertThat(page.getWebResponse().getContentAsString(), startsWith("ok")); + + assertDoActionRequestWasNotBlocked(); + assertGetMethodActionRequestWasNotBlocked(); + assertFieldRequestWasNotBlocked(); + } catch (FailingHttpStatusCodeException e) { + fail("Url " + url + " should be reachable, received " + e.getMessage() + " (" + e.getStatusCode() + ") instead."); + } + } + + protected void assertReachable(String url) throws IOException { + assertReachable(url, HttpMethod.GET); + } + + protected void assertReachableWithSettings(WebRequest request) throws IOException { + Page page = wc.getPage(request); + assertEquals(200, page.getWebResponse().getStatusCode()); + assertEquals("ok", page.getWebResponse().getContentAsString()); + assertDoActionRequestWasNotBlocked(); + } + + protected void assertReachableWithoutOk(String url) throws IOException { + try { + Page page = wc.getPage(new URL(j.getURL(), url)); + assertEquals(200, page.getWebResponse().getStatusCode()); + } catch (FailingHttpStatusCodeException e) { + fail("Url " + url + " should be reachable, received " + e.getMessage() + " (" + e.getStatusCode() + ") instead."); + } + } + + protected void assertNotReachable(String url) throws IOException { + try { + wc.getPage(new URL(j.getURL(), url)); + fail("Url " + url + " is reachable but should not be, an not-found error is expected"); + } catch (FailingHttpStatusCodeException e) { + assertEquals("Url " + url + " returns an error different from 404", 404, e.getResponse().getStatusCode()); + } + } +} diff --git a/test/src/test/java/jenkins/security/stapler/StaplerRoutableActionTest.java b/test/src/test/java/jenkins/security/stapler/StaplerRoutableActionTest.java new file mode 100644 index 0000000000..3a645ad680 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/StaplerRoutableActionTest.java @@ -0,0 +1,90 @@ +package jenkins.security.stapler; + +import org.junit.Test; +import org.jvnet.hudson.test.For; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.HttpResponses; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.WebMethod; + +@Issue("SECURITY-400") +@For({StaplerDispatchable.class, StaplerNotDispatchable.class, DoActionFilter.class}) +public class StaplerRoutableActionTest extends StaplerAbstractTest { + + @TestExtension + public static class TestNewRulesRoutableAction extends AbstractUnprotectedRootAction { + // StaplerDispatchable is not enough, the method needs to have at least either a name starting with do* or a WebMethod annotation + @StaplerDispatchable + public void notDoName() { replyOk(); } + + @StaplerDispatchable // could be used to indicate that's a web method, without having to use @WebMethod + public void doWebMethod1() { replyOk(); } + + // without annotation, returnType, parameter, exception => not a web method + public void doWebMethod2() { replyOk(); } + + public void doWebMethod3() throws HttpResponses.HttpResponseException { + replyOk(); + } + + public void doWebMethod4(StaplerRequest request) { + replyOk(); + } + + public void doWebMethod5(@QueryParameter String foo) { + replyOk(); + } + } + + @Test + public void testNewRulesRoutableAction_notDoName() throws Exception { + assertNotReachable("testNewRulesRoutableAction/notDoName/"); + // not even considered as a blocked action because the filter is not even called, they are lacking do* or @WebMethod + // assertDoActionRequestWasBlockedAndResetFlag(); + assertNotReachable("testNewRulesRoutableAction/tDoName/"); + // assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNewRulesRoutableAction_webMethod1() throws Exception { + assertReachable("testNewRulesRoutableAction/webMethod1/"); + } + + @Test + public void testNewRulesRoutableAction_webMethod3Through5() throws Exception { + assertReachable("testNewRulesRoutableAction/webMethod3/"); + assertReachable("testNewRulesRoutableAction/webMethod4/"); + assertReachable("testNewRulesRoutableAction/webMethod5/"); + } + + @Test + public void testNewRulesRoutableAction_webMethod2() throws Exception { + assertNotReachable("testNewRulesRoutableAction/webMethod2/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @TestExtension + public static class TestNewRulesNonroutableAction extends AbstractUnprotectedRootAction { + @StaplerNotDispatchable + public void doWebMethod1() { replyOk(); } + + @StaplerNotDispatchable + @WebMethod(name = "webMethod2") + public void doWebMethod2() { replyOk(); } + } + + @Test + public void testNewRulesNonroutableAction_webMethod1() throws Exception { + assertNotReachable("testNewRulesNonroutableAction/webMethod1/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @Test + public void testNewRulesNonroutableAction_webMethod2() throws Exception { + // priority of negative over positive + assertNotReachable("testNewRulesNonroutableAction/webMethod2/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } +} diff --git a/test/src/test/java/jenkins/security/stapler/StaplerRoutableFieldTest.java b/test/src/test/java/jenkins/security/stapler/StaplerRoutableFieldTest.java new file mode 100644 index 0000000000..f278603fb5 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/StaplerRoutableFieldTest.java @@ -0,0 +1,156 @@ +package jenkins.security.stapler; + +import org.junit.Test; +import org.jvnet.hudson.test.For; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.TestExtension; + +@Issue("SECURITY-595") +@For({StaplerDispatchable.class, StaplerNotDispatchable.class, TypedFilter.class}) +public class StaplerRoutableFieldTest extends StaplerAbstractTest { + @TestExtension + public static class TestRootAction extends AbstractUnprotectedRootAction { + @Override + public String getUrlName() { + return "test"; + } + + public Renderable renderableNotAnnotated = new Renderable(); + + public ParentRenderable parentRenderableNotAnnotated = new ParentRenderable(); + + public Object objectNotAnnotated = new Renderable(); + + @StaplerDispatchable + public Renderable renderableAnnotatedOk = new Renderable(); + + @StaplerDispatchable + public ParentRenderable parentRenderableAnnotatedOk = new ParentRenderable(); + + @StaplerDispatchable + public Object objectAnnotatedOk = new Renderable(); + + @StaplerNotDispatchable + public Renderable renderableAnnotatedKo = new Renderable(); + + @StaplerNotDispatchable + public Object objectAnnotatedKo = new Renderable(); + + @StaplerDispatchable + @StaplerNotDispatchable + public Renderable renderableDoubleAnnotated = new Renderable(); + + @StaplerDispatchable + @StaplerNotDispatchable + public Object objectDoubleAnnotated = new Renderable(); + + public static Renderable staticRenderableNotAnnotated = new Renderable(); + + public static Object staticObjectNotAnnotated = new Renderable(); + + @StaplerDispatchable + public static Renderable staticRenderableAnnotatedOk = new Renderable(); + + @StaplerDispatchable + public static Object staticObjectAnnotatedOk = new Renderable(); + } + + @Test + public void testFieldNotAnnotated() throws Exception { + assertReachable("test/renderableNotAnnotated/"); + assertReachable("test/renderableNotAnnotated/valid/"); + + assertNotReachable("test/parentRenderableNotAnnotated/"); + assertNotReachable("test/parentRenderableNotAnnotated/renderable/"); + assertNotReachable("test/parentRenderableNotAnnotated/renderable/valid/"); + + assertNotReachable("test/objectNotAnnotated/"); + assertNotReachable("test/objectNotAnnotated/valid/"); + } + + @Test + public void testFieldNotAnnotated_escapeHatch() throws Exception { + boolean currentValue = TypedFilter.SKIP_TYPE_CHECK; + try { + TypedFilter.SKIP_TYPE_CHECK = true; + // to apply the new configuration + webApp.clearMetaClassCache(); + + assertReachable("test/renderableNotAnnotated/"); + assertReachable("test/renderableNotAnnotated/valid/"); + + assertNotReachable("test/parentRenderableNotAnnotated/"); + assertReachable("test/parentRenderableNotAnnotated/renderable/"); + assertReachable("test/parentRenderableNotAnnotated/renderable/valid/"); + } finally { + TypedFilter.SKIP_TYPE_CHECK = currentValue; + // to reset the configuration + webApp.clearMetaClassCache(); + } + } + + @Test + public void testFieldAnnotatedOk() throws Exception { + assertReachable("test/renderableAnnotatedOk/"); + assertReachable("test/renderableAnnotatedOk/valid/"); + + assertReachable("test/objectAnnotatedOk/"); + assertReachable("test/objectAnnotatedOk/valid/"); + } + + @Test + public void testFieldAnnotatedKo() throws Exception { + assertNotReachable("test/renderableAnnotatedKo/"); + assertNotReachable("test/renderableAnnotatedKo/valid/"); + + assertNotReachable("test/objectAnnotatedKo/"); + assertNotReachable("test/objectAnnotatedKo/valid/"); + } + + @Test + public void testFieldDoubleAnnotated() throws Exception { + assertNotReachable("test/renderableDoubleAnnotated/"); + assertNotReachable("test/renderableDoubleAnnotated/valid/"); + + assertNotReachable("test/objectDoubleAnnotated/"); + assertNotReachable("test/objectDoubleAnnotated/valid/"); + } + + @Test + public void testStaticFieldNotAnnotated() throws Exception { + assertNotReachable("test/staticRenderableNotAnnotated/"); + assertNotReachable("test/staticRenderableNotAnnotated/valid/"); + + assertNotReachable("test/staticObjectNotAnnotated/"); + assertNotReachable("test/staticObjectNotAnnotated/valid/"); + } + + @Test + public void testStaticFieldNotAnnotated_escapeHatch() throws Exception { + boolean currentValue = TypedFilter.PROHIBIT_STATIC_ACCESS; + try { + TypedFilter.PROHIBIT_STATIC_ACCESS = false; + // to apply the new configuration + webApp.clearMetaClassCache(); + + assertReachable("test/staticRenderableNotAnnotated/"); + assertReachable("test/staticRenderableNotAnnotated/valid/"); + + assertNotReachable("test/staticObjectNotAnnotated/"); + assertNotReachable("test/staticObjectNotAnnotated/valid/"); + } finally { + TypedFilter.PROHIBIT_STATIC_ACCESS = currentValue; + // to reset the configuration + webApp.clearMetaClassCache(); + } + } + + @Test + public void testStaticFieldAnnotatedOk() throws Exception { + assertReachable("test/staticRenderableAnnotatedOk/"); + assertReachable("test/staticRenderableAnnotatedOk/valid/"); + + assertReachable("test/staticObjectAnnotatedOk/"); + assertReachable("test/staticObjectAnnotatedOk/valid/"); + } +} diff --git a/test/src/test/java/jenkins/security/stapler/StaplerRoutableGetterTest.java b/test/src/test/java/jenkins/security/stapler/StaplerRoutableGetterTest.java new file mode 100644 index 0000000000..620573ac87 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/StaplerRoutableGetterTest.java @@ -0,0 +1,172 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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 org.junit.Test; +import org.jvnet.hudson.test.For; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.TestExtension; + +@Issue("SECURITY-400") +@For({StaplerDispatchable.class, StaplerNotDispatchable.class, TypedFilter.class}) +public class StaplerRoutableGetterTest extends StaplerAbstractTest { + @TestExtension + public static class TestRootAction extends AbstractUnprotectedRootAction { + @Override + public String getUrlName() { + return "test"; + } + + public Object getFalseWithoutAnnotation() { + return new Renderable(); + } + + @StaplerDispatchable + public Object getFalseWithAnnotation() { + return new Renderable(); + } + + public Renderable getTrueWithoutAnnotation() { + return new Renderable(); + } + + @StaplerNotDispatchable + public Renderable getTrueWithAnnotation() { + return new Renderable(); + } + + @StaplerDispatchable + @StaplerNotDispatchable + public Renderable getPriorityToNegative() { + return new Renderable(); + } + } + + @Test + public void testForceGetterMethod() throws Exception { + assertNotReachable("test/falseWithoutAnnotation/"); + assertNotReachable("test/falseWithoutAnnotation/valid/"); + + filteredGetMethodTriggered = false; + + assertReachable("test/falseWithAnnotation/"); + assertReachable("test/falseWithAnnotation/valid/"); + } + + @Test + public void testForceNotGetterMethod() throws Exception { + assertReachable("test/trueWithoutAnnotation/"); + assertReachable("test/trueWithoutAnnotation/valid/"); + assertNotReachable("test/trueWithAnnotation/"); + assertNotReachable("test/trueWithAnnotation/valid/"); + } + + @Test + public void testPriorityIsNegative() throws Exception { + assertNotReachable("test/priorityToNegative/"); + } + + public static class TestRootActionParent extends AbstractUnprotectedRootAction { + @StaplerNotDispatchable + public Renderable getParentKoButChildOk() { + return new Renderable(); + } + + @StaplerNotDispatchable + public Renderable getParentKoButChildNone() { + return new Renderable(); + } + + public Renderable getParentNoneButChildOk() { + return new Renderable(); + } + + public Renderable getParentNoneButChildKo() { + return new Renderable(); + } + + @StaplerDispatchable + public Renderable getParentOkButChildKo() { + return new Renderable(); + } + + @StaplerDispatchable + public Renderable getParentOkButChildNone() { + return new Renderable(); + } + } + + @TestExtension + public static class TestRootActionChild extends TestRootActionParent { + @Override + public String getUrlName() { + return "test-child"; + } + + @StaplerDispatchable + public Renderable getParentKoButChildOk() { + return new Renderable(); + } + + public Renderable getParentKoButChildNone() { + return new Renderable(); + } + + @StaplerDispatchable + public Renderable getParentNoneButChildOk() { + return new Renderable(); + } + + @StaplerNotDispatchable + public Renderable getParentNoneButChildKo() { + return new Renderable(); + } + + @StaplerNotDispatchable + public Renderable getParentOkButChildKo() { + return new Renderable(); + } + + public Renderable getParentOkButChildNone() { + return new Renderable(); + } + } + + @Test + public void testInheritanceOfAnnotation_childHasLastWord() throws Exception { + assertNotReachable("test-child/parentKoButChildOk/"); + assertNotReachable("test-child/parentKoButChildNone/"); + + filteredGetMethodTriggered = false; + + assertReachable("test-child/parentNoneButChildOk/"); + + assertNotReachable("test-child/parentNoneButChildKo/"); + assertNotReachable("test-child/parentOkButChildKo/"); + + filteredGetMethodTriggered = false; + + assertReachable("test-child/parentOkButChildNone/"); + } +} diff --git a/test/src/test/java/jenkins/security/stapler/StaticRoutingDecisionProvider2Test.java b/test/src/test/java/jenkins/security/stapler/StaticRoutingDecisionProvider2Test.java new file mode 100644 index 0000000000..06ebd0557e --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/StaticRoutingDecisionProvider2Test.java @@ -0,0 +1,236 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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 org.apache.commons.io.FileUtils; +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.recipes.LocalData; + +import java.io.File; + +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * Due to the fact we are using a @ClassRule for the other tests to improve performance, + * we cannot use @LocalData to test the loading of the whitelist as that annotation seem to not work with @ClassRule. + */ +@Issue("SECURITY-400") +@For(StaticRoutingDecisionProvider.class) +public class StaticRoutingDecisionProvider2Test { + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Test + @LocalData("whitelist_empty") + public void userControlledWhitelist_empty_Loading() throws Exception { + StaticRoutingDecisionProvider wl = new StaticRoutingDecisionProvider(); + assertThat( + wl.decide("public java.lang.Object jenkins.security.stapler.StaticRoutingDecisionProviderTest$ContentProvider.getObjectCustom()"), + is(RoutingDecisionProvider.Decision.UNKNOWN) + ); + assertThat( + wl.decide("blabla"), + is(RoutingDecisionProvider.Decision.UNKNOWN) + ); + } + + @Test + @LocalData("whitelist_monoline") + public void userControlledWhitelist_monoline_Loading() throws Exception { + StaticRoutingDecisionProvider wl = new StaticRoutingDecisionProvider(); + assertThat( + wl.decide("method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ContentProvider getObjectCustom"), + is(RoutingDecisionProvider.Decision.ACCEPTED) + ); + assertThat( + wl.decide("blabla"), + is(RoutingDecisionProvider.Decision.UNKNOWN) + ); + } + + @Test + @LocalData("whitelist_multiline") + public void userControlledWhitelist_multiline_Loading() throws Exception { + StaticRoutingDecisionProvider wl = new StaticRoutingDecisionProvider(); + assertThat( + wl.decide("method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ContentProvider getObjectCustom"), + is(RoutingDecisionProvider.Decision.ACCEPTED) + ); + assertThat( + wl.decide("method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ContentProvider getObjectCustom2"), + is(RoutingDecisionProvider.Decision.ACCEPTED) + ); + assertThat( + wl.decide("blabla"), + is(RoutingDecisionProvider.Decision.UNKNOWN) + ); + } + + @Test + @LocalData("comment_ignored") + public void userControlledWhitelist_commentsAreIgnored() throws Exception { + StaticRoutingDecisionProvider wl = new StaticRoutingDecisionProvider(); + assertThat(wl.decide("this line is not read"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + assertThat(wl.decide("not-this-one"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + assertThat(wl.decide("neither"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + assertThat(wl.decide("finally-not"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + + assertThat(wl.decide("this-one-is"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + assertThat(wl.decide("this-one-also"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + } + + @Test + @LocalData("whitelist_emptyline") + public void userControlledWhitelist_emptyLinesAreIgnored() throws Exception { + StaticRoutingDecisionProvider wl = new StaticRoutingDecisionProvider(); + assertThat(wl.decide("signature-1"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + assertThat(wl.decide("signature-2"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + assertThat(wl.decide("signature-3"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + // neither the empty line or an exclamation mark followed by nothing or spaces are not considered + assertThat(wl.decide(""), is(RoutingDecisionProvider.Decision.UNKNOWN)); + } + + @Test + @LocalData("greylist_multiline") + public void userControlledWhitelist_whiteAndBlack() throws Exception { + StaticRoutingDecisionProvider wl = new StaticRoutingDecisionProvider(); + assertThat(wl.decide("signature-1-ok"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + assertThat(wl.decide("signature-3-ok"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + + assertThat(wl.decide("signature-2-not-ok"), is(RoutingDecisionProvider.Decision.REJECTED)); + assertThat(wl.decide("signature-4-not-ok"), is(RoutingDecisionProvider.Decision.REJECTED)); + + // the exclamation mark is not used + assertThat(wl.decide("!signature-2-not-ok"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + } + + @Test + public void defaultList() throws Exception { + StaticRoutingDecisionProvider wl = new StaticRoutingDecisionProvider(); + + assertThat( + wl.decide("method io.jenkins.blueocean.service.embedded.rest.AbstractRunImpl getLog"), + is(RoutingDecisionProvider.Decision.ACCEPTED) + ); + assertThat( + wl.decide("method io.jenkins.blueocean.rest.impl.pipeline.PipelineNodeImpl getLog"), + is(RoutingDecisionProvider.Decision.ACCEPTED) + ); + assertThat( + wl.decide("method io.jenkins.blueocean.rest.impl.pipeline.PipelineStepImpl getLog"), + is(RoutingDecisionProvider.Decision.ACCEPTED) + ); + + assertThat(wl.decide("method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ContentProvider getObjectCustom"), + is(RoutingDecisionProvider.Decision.UNKNOWN) + ); + assertThat(wl.decide("blabla"), + is(RoutingDecisionProvider.Decision.UNKNOWN) + ); + } + + @Test + public void userControlledWhitelist_savedCorrectly() throws Exception { + File whitelistUserControlledList = new File(j.jenkins.getRootDir(), "stapler-whitelist.txt"); + + assertFalse(whitelistUserControlledList.exists()); + + StaticRoutingDecisionProvider wl = new StaticRoutingDecisionProvider(); + + assertFalse(whitelistUserControlledList.exists()); + + assertThat(wl.decide("nothing"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + + wl.save(); + assertTrue(whitelistUserControlledList.exists()); + assertThat(FileUtils.readFileToString(whitelistUserControlledList), is("")); + + wl.add("white-1"); + + assertThat(wl.decide("white-1"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + + assertTrue(whitelistUserControlledList.exists()); + assertThat(FileUtils.readFileToString(whitelistUserControlledList), containsString("white-1")); + { + StaticRoutingDecisionProvider temp = new StaticRoutingDecisionProvider(); + assertThat(temp.decide("white-1"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + } + + wl.addBlacklistSignature("black-2"); + + assertThat(wl.decide("white-1"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + assertThat(wl.decide("black-2"), is(RoutingDecisionProvider.Decision.REJECTED)); + assertThat(FileUtils.readFileToString(whitelistUserControlledList), allOf( + containsString("white-1"), + containsString("!black-2") + )); + + { + StaticRoutingDecisionProvider temp = new StaticRoutingDecisionProvider(); + assertThat(temp.decide("white-1"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + assertThat(temp.decide("black-2"), is(RoutingDecisionProvider.Decision.REJECTED)); + } + + wl.removeBlacklistSignature("black-2"); + + assertThat(wl.decide("white-1"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + assertThat(wl.decide("black-2"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + assertThat(FileUtils.readFileToString(whitelistUserControlledList), allOf( + containsString("white-1"), + not(containsString("black-2")) + )); + + { + StaticRoutingDecisionProvider temp = new StaticRoutingDecisionProvider(); + assertThat(temp.decide("white-1"), is(RoutingDecisionProvider.Decision.ACCEPTED)); + assertThat(temp.decide("black-2"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + } + + wl.remove("white-1"); + + assertThat(wl.decide("white-1"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + assertThat(wl.decide("black-2"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + assertThat(FileUtils.readFileToString(whitelistUserControlledList), allOf( + not(containsString("white-1")), + not(containsString("black-2")) + )); + + { + StaticRoutingDecisionProvider temp = new StaticRoutingDecisionProvider(); + assertThat(temp.decide("white-1"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + assertThat(temp.decide("black-2"), is(RoutingDecisionProvider.Decision.UNKNOWN)); + } + } +} diff --git a/test/src/test/java/jenkins/security/stapler/StaticRoutingDecisionProviderTest.java b/test/src/test/java/jenkins/security/stapler/StaticRoutingDecisionProviderTest.java new file mode 100644 index 0000000000..3ffde59698 --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/StaticRoutingDecisionProviderTest.java @@ -0,0 +1,513 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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 hudson.ExtensionList; +import hudson.model.FreeStyleProject; +import jenkins.model.Jenkins; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.WebMethod; + +import javax.annotation.CheckForNull; +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Set; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@Issue("SECURITY-400") +public class StaticRoutingDecisionProviderTest extends StaplerAbstractTest { + @TestExtension + public static class ContentProvider extends AbstractUnprotectedRootAction { + // simulate side effect + public static boolean called = false; + public static boolean called2 = false; + + public FreeStyleProject getJob() { + called = true; + return (FreeStyleProject) Jenkins.get().getItem("testProject"); + } + + public String getString() { + called = true; + return "a"; + } + + // cannot provide side-effect since the String has no side-effect methods + public Object getObjectString() { + called = true; + return "a"; + } + + public static String OBJECT_CUSTOM_SIGNATURE = "method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ContentProvider getObjectCustom"; + + // but it opens wide range of potentially dangerous classes + public Object getObjectCustom() { + called = true; + return new Object() { + // in order to provide a web entry-point + public void doIndex() { + called2 = true; + replyOk(); + } + }; + } + } + + @Before + public void preparation() throws Exception { + ContentProvider.called = false; + ContentProvider.called2 = false; + } + + @Before + public void resetWhitelist() throws Exception { + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).resetAndSave(); + } + + @Test + public void test_job_index() throws Exception { + j.createFreeStyleProject("testProject"); + assertReachableWithoutOk("contentProvider/job/"); + assertTrue(ContentProvider.called); + } + + @Test + public void test_string() throws Exception { + assertNotReachable("contentProvider/string/"); + assertFalse(ContentProvider.called); + } + + @Test + public void test_objectString() throws Exception { + assertNotReachable("contentProvider/objectString/"); + assertFalse(ContentProvider.called); + } + + @Test + public void test_objectCustom() throws Exception { + assertNotReachable("contentProvider/objectCustom/"); + assertFalse(ContentProvider.called); + } + + //for more test about the whitelist initial loading, please refer to StaticRoutingDecisionProvider2Test + @Test + public void test_objectCustom_withUserControlledSavedWhitelist() throws Throwable { + String whitelist = ContentProvider.OBJECT_CUSTOM_SIGNATURE + "\n"; + File whitelistFile = new File(j.jenkins.getRootDir(), "stapler-whitelist.txt"); + FileUtils.write(whitelistFile, whitelist); + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).reload(); + try { + assertNotReachable("contentProvider/objectString/"); + assertFalse(ContentProvider.called); + assertGetMethodRequestWasBlockedAndResetFlag(); + assertReachable("contentProvider/objectCustom/"); + assertTrue(ContentProvider.called); + } finally { + whitelistFile.delete(); + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).reload(); + } + } + + @Test + public void test_objectCustom_withUserControlledEditedWhitelist() throws Exception { + try { + assertNotReachable("contentProvider/objectString/"); + assertFalse(ContentProvider.called); + assertNotReachable("contentProvider/objectCustom/"); + assertFalse(ContentProvider.called); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).add(ContentProvider.OBJECT_CUSTOM_SIGNATURE); + + assertNotReachable("contentProvider/objectString/"); + assertFalse(ContentProvider.called); + assertFalse(ContentProvider.called2); + assertGetMethodRequestWasBlockedAndResetFlag(); + + assertReachable("contentProvider/objectCustom/"); + assertTrue(ContentProvider.called); + assertTrue(ContentProvider.called2); + + ContentProvider.called = false; + ContentProvider.called2 = false; + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).remove(ContentProvider.OBJECT_CUSTOM_SIGNATURE); + + assertNotReachable("contentProvider/objectString/"); + assertFalse(ContentProvider.called); + assertNotReachable("contentProvider/objectCustom/"); + assertFalse(ContentProvider.called); + } finally { + //TODO check if the file is created per test or in general + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).reload(); + } + } + + @Test + public void test_objectCustom_withStandardWhitelist() throws Exception { + assertNotReachable("contentProvider/objectString/"); + assertFalse(ContentProvider.called); + assertGetMethodRequestWasBlockedAndResetFlag(); + assertNotReachable("contentProvider/objectCustom/"); + assertFalse(ContentProvider.called); + + StaticRoutingDecisionProvider whitelist = ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class); + + {// add entry in the set loaded from the standard whitelist file and reload + Method resetMetaClassCache = StaticRoutingDecisionProvider.class.getDeclaredMethod("resetMetaClassCache"); + resetMetaClassCache.setAccessible(true); + + Field field = StaticRoutingDecisionProvider.class.getDeclaredField("whitelistSignaturesFromFixedList"); + field.setAccessible(true); + @SuppressWarnings("unchecked") + Set standardWhitelist = (Set) field.get(whitelist); + + standardWhitelist.add(ContentProvider.OBJECT_CUSTOM_SIGNATURE); + // just call this method to avoid to reload the file and so override our new signature + resetMetaClassCache.invoke(whitelist); + } + + assertNotReachable("contentProvider/objectString/"); + assertFalse(ContentProvider.called); + assertFalse(ContentProvider.called2); + assertGetMethodRequestWasBlockedAndResetFlag(); + assertReachable("contentProvider/objectCustom/"); + assertTrue(ContentProvider.called); + assertTrue(ContentProvider.called2); + + {// reset to previous state + ContentProvider.called = false; + ContentProvider.called2 = false; + + whitelist.reload(); + } + + assertNotReachable("contentProvider/objectString/"); + assertFalse(ContentProvider.called); + assertNotReachable("contentProvider/objectCustom/"); + assertFalse(ContentProvider.called); + } + + @TestExtension + public static class ActionWithWhitelist extends AbstractUnprotectedRootAction { + @Override + public @CheckForNull String getUrlName() { + return "do-action"; + } + + public static String DO_ACTION_SIGNATURE = "method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ActionWithWhitelist doAction org.kohsuke.stapler.StaplerRequest"; + + public void doAction(StaplerRequest request) { + replyOk(); + } + + public static String DO_ACTION_STAPLER_ROUTABLE_SIGNATURE = "method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ActionWithWhitelist doActionWithStaplerDispatchable org.kohsuke.stapler.StaplerRequest"; + + @StaplerDispatchable + public void doActionWithStaplerDispatchable(StaplerRequest request) { + replyOk(); + } + + public static String DO_ACTION_STAPLER_NONROUTABLE_SIGNATURE = "method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ActionWithWhitelist doActionWithStaplerNotDispatchable org.kohsuke.stapler.StaplerRequest"; + + @StaplerNotDispatchable + public void doActionWithStaplerNotDispatchable(StaplerRequest request) { + replyOk(); + } + + public static String DO_ACTION_STAPLER_WEBMETHOD_SIGNATURE = "method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ActionWithWhitelist doActionWithWebMethod org.kohsuke.stapler.StaplerRequest"; + + @WebMethod(name = "actionWithWebMethod") + public void doActionWithWebMethod(StaplerRequest request) { + replyOk(); + } + } + + @Test + public void doAction_regular() throws Exception { + assertReachable("do-action/action/"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).add(ActionWithWhitelist.DO_ACTION_SIGNATURE); + + assertReachable("do-action/action/"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).remove(ActionWithWhitelist.DO_ACTION_SIGNATURE); + + assertReachable("do-action/action/"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(ActionWithWhitelist.DO_ACTION_SIGNATURE); + + assertNotReachable("do-action/action/"); + assertDoActionRequestWasBlockedAndResetFlag(); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).removeBlacklistSignature(ActionWithWhitelist.DO_ACTION_SIGNATURE); + + assertReachable("do-action/action/"); + } + + @Test + public void doAction_actionWithStaplerDispatchable() throws Exception { + assertReachable("do-action/actionWithStaplerDispatchable/"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(ActionWithWhitelist.DO_ACTION_STAPLER_ROUTABLE_SIGNATURE); + + assertReachable("do-action/actionWithStaplerDispatchable/"); + } + + @Test + public void doAction_actionWithWebMethod() throws Exception { + assertReachable("do-action/actionWithWebMethod/"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(ActionWithWhitelist.DO_ACTION_STAPLER_WEBMETHOD_SIGNATURE); + + assertNotReachable("do-action/actionWithWebMethod/"); + assertDoActionRequestWasBlockedAndResetFlag(); + } + + @TestExtension + public static class GetterWithWhitelist extends AbstractUnprotectedRootAction { + @Override + public @CheckForNull String getUrlName() { + return "getter"; + } + + public static String GET_ITEM_SIGNATURE = "method jenkins.security.stapler.StaticRoutingDecisionProviderTest$GetterWithWhitelist getItem"; + + public Renderable getItem() { + return new Renderable(); + } + + public static String GET_ITEM_STAPLER_ROUTABLE_SIGNATURE = "method jenkins.security.stapler.StaticRoutingDecisionProviderTest$GetterWithWhitelist getItemWithStaplerDispatchable"; + + @StaplerDispatchable + public Renderable getItemWithStaplerDispatchable() { + return new Renderable(); + } + + public static String GET_ITEM_STAPLER_NONROUTABLE_SIGNATURE = "method jenkins.security.stapler.StaticRoutingDecisionProviderTest$GetterWithWhitelist getItemWithStaplerNotDispatchable"; + + @StaplerNotDispatchable + public Renderable getItemWithStaplerNotDispatchable() { + return new Renderable(); + } + } + + @Test + public void getItem_regular() throws Exception { + assertReachable("getter/item/"); + assertReachable("getter/item/valid"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(GetterWithWhitelist.GET_ITEM_SIGNATURE); + + assertNotReachable("getter/item/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + assertNotReachable("getter/item/valid"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @Test + public void getItem_getterWithStaplerDispatchable() throws Exception { + assertReachable("getter/itemWithStaplerDispatchable/"); + assertReachable("getter/itemWithStaplerDispatchable/valid"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(GetterWithWhitelist.GET_ITEM_STAPLER_ROUTABLE_SIGNATURE); + + // Annotation overrides whitelist/blacklist + assertReachable("getter/itemWithStaplerDispatchable/"); + assertReachable("getter/itemWithStaplerDispatchable/valid"); + } + + @Test + public void getItem_getterWithStaplerNotDispatchable() throws Exception { + assertNotReachable("getter/itemWithStaplerNotDispatchable/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + assertNotReachable("getter/itemWithStaplerNotDispatchable/valid"); + assertGetMethodRequestWasBlockedAndResetFlag(); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).add(GetterWithWhitelist.GET_ITEM_STAPLER_NONROUTABLE_SIGNATURE); + + // Annotation overrides whitelist/blacklist + assertNotReachable("getter/itemWithStaplerNotDispatchable/"); + assertGetMethodRequestWasBlockedAndResetFlag(); + assertNotReachable("getter/itemWithStaplerNotDispatchable/valid"); + assertGetMethodRequestWasBlockedAndResetFlag(); + } + + @TestExtension + public static class FieldWithWhitelist extends AbstractUnprotectedRootAction { + @Override + public @CheckForNull String getUrlName() { + return "field"; + } + + public static String FIELD_SIGNATURE = "field jenkins.security.stapler.StaticRoutingDecisionProviderTest$FieldWithWhitelist renderable"; + + public Renderable renderable = new Renderable(); + + public static String FIELD_STAPLER_ROUTABLE_SIGNATURE = "field jenkins.security.stapler.StaticRoutingDecisionProviderTest$FieldWithWhitelist renderableWithStaplerDispatchable"; + + @StaplerDispatchable + public Renderable renderableWithStaplerDispatchable = new Renderable(); + + public static String FIELD_STAPLER_NONROUTABLE_SIGNATURE = "field jenkins.security.stapler.StaticRoutingDecisionProviderTest$FieldWithWhitelist renderableWithStaplerNotDispatchable"; + + @StaplerNotDispatchable + public Renderable renderableWithStaplerNotDispatchable = new Renderable(); + + public static String FIELD_STATIC_SIGNATURE = "staticField jenkins.security.stapler.StaticRoutingDecisionProviderTest$FieldWithWhitelist staticRenderable"; + + public static Renderable staticRenderable = new Renderable(); + + public static String FIELD_STATIC_STAPLER_ROUTABLE_SIGNATURE = "staticField jenkins.security.stapler.StaticRoutingDecisionProviderTest$FieldWithWhitelist staticRenderableWithStaplerDispatchable"; + + @StaplerDispatchable + public static Renderable staticRenderableWithStaplerDispatchable = new Renderable(); + + public static String FIELD_STATIC_STAPLER_NONROUTABLE_SIGNATURE = "staticField jenkins.security.stapler.StaticRoutingDecisionProviderTest$FieldWithWhitelist staticRenderableWithStaplerNotDispatchable"; + + @StaplerNotDispatchable + public static Renderable staticRenderableWithStaplerNotDispatchable = new Renderable(); + } + + @Test + public void field_regular() throws Exception { + assertReachable("field/renderable/"); + assertReachable("field/renderable/valid"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(FieldWithWhitelist.FIELD_SIGNATURE); + + assertNotReachable("field/renderable/"); + assertFieldRequestWasBlockedAndResetFlag(); + assertNotReachable("field/renderable/valid"); + assertFieldRequestWasBlockedAndResetFlag(); + } + + @Test + public void field_regular_returnType() throws Exception { + assertReachable("field/renderable/"); + assertReachable("field/renderable/valid"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(RENDERABLE_CLASS_SIGNATURE); + + assertNotReachable("field/renderable/"); + assertFieldRequestWasBlockedAndResetFlag(); + assertNotReachable("field/renderable/valid"); + assertFieldRequestWasBlockedAndResetFlag(); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).removeBlacklistSignature(RENDERABLE_CLASS_SIGNATURE); + + assertReachable("field/renderable/"); + assertReachable("field/renderable/valid"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).add(RENDERABLE_CLASS_SIGNATURE); + // method is checked first as it's more specific + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(FieldWithWhitelist.FIELD_SIGNATURE); + + assertNotReachable("field/renderable/"); + assertFieldRequestWasBlockedAndResetFlag(); + assertNotReachable("field/renderable/valid"); + assertFieldRequestWasBlockedAndResetFlag(); + + // reverse, now we blacklist the type but whitelist the method => it's ok + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).remove(RENDERABLE_CLASS_SIGNATURE); + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).removeBlacklistSignature(FieldWithWhitelist.FIELD_SIGNATURE); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(RENDERABLE_CLASS_SIGNATURE); + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).add(FieldWithWhitelist.FIELD_SIGNATURE); + + assertReachable("field/renderable/"); + assertReachable("field/renderable/valid"); + } + + @Test + public void field_withStaplerDispatchable() throws Exception { + assertReachable("field/renderableWithStaplerDispatchable/"); + assertReachable("field/renderableWithStaplerDispatchable/valid"); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(FieldWithWhitelist.FIELD_STAPLER_ROUTABLE_SIGNATURE); + + assertReachable("field/renderableWithStaplerDispatchable/"); + } + + @Test + public void field_withStaplerNotDispatchable() throws Exception { + assertNotReachable("field/renderableWithStaplerNotDispatchable/"); + assertFieldRequestWasBlockedAndResetFlag(); + assertNotReachable("field/renderableWithStaplerNotDispatchable/valid"); + assertFieldRequestWasBlockedAndResetFlag(); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).add(FieldWithWhitelist.FIELD_STAPLER_NONROUTABLE_SIGNATURE); + + assertNotReachable("field/renderableWithStaplerNotDispatchable/"); + assertFieldRequestWasBlockedAndResetFlag(); + assertNotReachable("field/renderableWithStaplerNotDispatchable/valid"); + assertFieldRequestWasBlockedAndResetFlag(); + } + + @Test + public void fieldStatic_regular() throws Exception { + assertNotReachable("field/staticRenderable/"); + assertFieldRequestWasBlockedAndResetFlag(); + assertNotReachable("field/staticRenderable/valid"); + assertFieldRequestWasBlockedAndResetFlag(); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).add(FieldWithWhitelist.FIELD_STATIC_SIGNATURE); + + assertReachable("field/staticRenderable/"); + assertReachable("field/staticRenderable/valid"); + } + + @Test + public void fieldStatic_withStaplerDispatchable() throws Exception { + assertReachable("field/staticRenderableWithStaplerDispatchable/"); + assertReachable("field/staticRenderableWithStaplerDispatchable/valid"); + + // doesn't do anything + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).addBlacklistSignature(FieldWithWhitelist.FIELD_STATIC_STAPLER_ROUTABLE_SIGNATURE); + + assertReachable("field/staticRenderableWithStaplerDispatchable/"); + } + + @Test + public void fieldStatic_withStaplerNotDispatchable() throws Exception { + assertNotReachable("field/staticRenderableWithStaplerNotDispatchable/"); + assertFieldRequestWasBlockedAndResetFlag(); + assertNotReachable("field/staticRenderableWithStaplerNotDispatchable/valid"); + assertFieldRequestWasBlockedAndResetFlag(); + + ExtensionList.lookupSingleton(StaticRoutingDecisionProvider.class).add(FieldWithWhitelist.FIELD_STATIC_STAPLER_NONROUTABLE_SIGNATURE); + + assertNotReachable("field/staticRenderableWithStaplerNotDispatchable/"); + assertFieldRequestWasBlockedAndResetFlag(); + assertNotReachable("field/staticRenderableWithStaplerNotDispatchable/valid"); + assertFieldRequestWasBlockedAndResetFlag(); + } +} \ No newline at end of file diff --git a/test/src/test/java/jenkins/security/stapler/TypedFilterTest.java b/test/src/test/java/jenkins/security/stapler/TypedFilterTest.java new file mode 100644 index 0000000000..ba9f6b4a4a --- /dev/null +++ b/test/src/test/java/jenkins/security/stapler/TypedFilterTest.java @@ -0,0 +1,209 @@ +package jenkins.security.stapler; + +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.StaplerProxy; +import org.kohsuke.stapler.StaplerRequest; + +@Issue("SECURITY-400") +public class TypedFilterTest extends StaplerAbstractTest { + @TestExtension + public static class GetTarget1 extends AbstractUnprotectedRootAction { + public Renderable getTarget(){ + return new Renderable(); + } + } + @Test + public void getTarget_withoutArg_isNotRoutableDirectly() throws Exception { + assertNotReachable("getTarget1/target/"); + } + + @TestExtension + public static class GetTarget2 extends AbstractUnprotectedRootAction { + @StaplerDispatchable + public Renderable getTarget(){ + return new Renderable(); + } + } + @Test + public void getTarget_withoutArg_isRoutableWithAnnotation() throws Exception { + assertReachable("getTarget2/target/"); + } + + @TestExtension + public static class GetTarget3 extends AbstractUnprotectedRootAction { + @StaplerNotDispatchable + public Renderable getTarget(){ + return new Renderable(); + } + } + @Test + public void getTarget_withArg_isNotRoutableWithStaplerNotDispatchable() throws Exception { + assertNotReachable("getTarget3/target/"); + } + + @TestExtension + public static class GetTarget4 extends AbstractUnprotectedRootAction { + public Renderable getTarget(StaplerRequest req){ + return new Renderable(); + } + } + @Test + public void getTarget_withArg_isRoutable() throws Exception { + assertReachable("getTarget4/target/"); + } + + @TestExtension + public static class GetStaplerFallback1 extends AbstractUnprotectedRootAction { + public Renderable getStaplerFallback(){ + return new Renderable(); + } + } + @Test + public void getStaplerFallback_withoutArg_isNotRoutableDirectly() throws Exception { + assertNotReachable("getStaplerFallback1/staplerFallback/"); + } + + @TestExtension + public static class GetStaplerFallback2 extends AbstractUnprotectedRootAction { + @StaplerDispatchable + public Renderable getStaplerFallback(){ + return new Renderable(); + } + } + @Test + public void getStaplerFallback_withoutArg_isRoutableWithAnnotation() throws Exception { + assertReachable("getStaplerFallback2/staplerFallback/"); + } + + @TestExtension + public static class GetStaplerFallback3 extends AbstractUnprotectedRootAction { + @StaplerNotDispatchable + public Renderable getStaplerFallback(){ + return new Renderable(); + } + } + @Test + public void getStaplerFallback_withArg_isNotRoutableWithStaplerNotDispatchable() throws Exception { + assertNotReachable("getStaplerFallback3/staplerFallback/"); + } + + @TestExtension + public static class GetStaplerFallback4 extends AbstractUnprotectedRootAction { + public Renderable getStaplerFallback(StaplerRequest req){ + return new Renderable(); + } + } + @Test + public void getStaplerFallback_withArg_isRoutable() throws Exception { + assertReachable("getStaplerFallback4/staplerFallback/"); + } + + public static class TypeImplementingStaplerProxy implements StaplerProxy { + @Override + public Object getTarget() { + return new Renderable(); + } + } + public static class TypeExtendingTypeImplementingStaplerProxy extends TypeImplementingStaplerProxy { + } + // FIXME @StaplerNotDispatchable + public static class TypeImplementingStaplerProxy2 implements StaplerProxy { + @Override + public Object getTarget() { + return new Renderable(); + } + } + public static class TypeExtendingTypeImplementingStaplerProxy2 extends TypeImplementingStaplerProxy2 { + } + + @TestExtension + public static class GetTypeImplementingStaplerProxy extends AbstractUnprotectedRootAction { + public TypeImplementingStaplerProxy getTypeImplementingStaplerProxy(){ + return new TypeImplementingStaplerProxy(); + } + public TypeExtendingTypeImplementingStaplerProxy getTypeExtendingTypeImplementingStaplerProxy(){ + return new TypeExtendingTypeImplementingStaplerProxy(); + } + public TypeImplementingStaplerProxy2 getTypeImplementingStaplerProxy2(){ + return new TypeImplementingStaplerProxy2(); + } + public TypeExtendingTypeImplementingStaplerProxy2 getTypeExtendingTypeImplementingStaplerProxy2(){ + return new TypeExtendingTypeImplementingStaplerProxy2(); + } + } + + @Test + public void typeImplementingStaplerProxy_isRoutableByDefault() throws Exception { + assertReachable("getTypeImplementingStaplerProxy/typeImplementingStaplerProxy/"); + assertReachable("getTypeImplementingStaplerProxy/typeImplementingStaplerProxy/valid"); + } + @Test + public void typeExtendingParentImplementingStaplerProxy_isRoutableByDefault() throws Exception { + assertReachable("getTypeImplementingStaplerProxy/typeExtendingTypeImplementingStaplerProxy/"); + assertReachable("getTypeImplementingStaplerProxy/typeExtendingTypeImplementingStaplerProxy/valid/"); + } + @Test + public void typeImplementingStaplerProxy_isNotRoutableWithNonroutable() throws Exception { + //TODO no way to avoid routability if implementing StaplerProxy +// assertNotReachable("getTypeImplementingStaplerProxy/typeImplementingStaplerProxy2/"); +// assertNotReachable("getTypeImplementingStaplerProxy/typeImplementingStaplerProxy2/valid/"); + } + @Test + public void typeExtendingParentImplementingStaplerProxy_isNotRoutableWithNonroutable() throws Exception { + //TODO no way to avoid routability if super type implementing StaplerProxy +// assertNotReachable("getTypeImplementingStaplerProxy/typeExtendingTypeImplementingStaplerProxy2/"); +// assertNotReachable("getTypeImplementingStaplerProxy/typeExtendingTypeImplementingStaplerProxy2/valid/"); + } + + @TestExtension + public static class GetDynamic1 extends AbstractUnprotectedRootAction { + public Renderable getDynamic(){ + return new Renderable(); + } + } + @Test + public void getDynamic_withoutArg_isRoutable() throws Exception { + assertReachable("getDynamic1/dynamic/"); + assertNotReachable("getDynamic1//"); + } + + @TestExtension + public static class GetDynamic2 extends AbstractUnprotectedRootAction { + public Renderable getDynamic(String someArgs){ + return new Renderable(); + } + } + @Test + public void getDynamic_withArgStartingWithString_isRoutable() throws Exception { + // dynamic is "just" a subcase of regular getDynamic usage + assertReachable("getDynamic2/dynamic/"); + assertReachable("getDynamic2//"); + } + + @TestExtension + public static class GetDynamic3 extends AbstractUnprotectedRootAction { + public Renderable getDynamic(StaplerRequest req, String someArgs){ + return new Renderable(); + } + } + @Test + public void getDynamic_withArgNotStartingWithString_isNotRoutable() throws Exception { + assertNotReachable("getDynamic3/dynamic/"); + assertNotReachable("getDynamic3//"); + } + + @TestExtension + public static class GetDynamic4 extends AbstractUnprotectedRootAction { + public Renderable getDynamic(StaplerRequest req){ + return new Renderable(); + } + } + @Test + public void getDynamic_withArgNotIncludingString_isRoutable() throws Exception { + assertReachable("getDynamic4/dynamic/"); + // there is no magic here, as the string argument is missing, just a regular getter + assertNotReachable("getDynamic4//"); + } +} diff --git a/test/src/test/resources/hudson/model/UsageStatisticsTest/jobs.json b/test/src/test/resources/hudson/model/UsageStatisticsTest/jobs.json index 83beb88336..60d1288406 100644 --- a/test/src/test/resources/hudson/model/UsageStatisticsTest/jobs.json +++ b/test/src/test/resources/hudson/model/UsageStatisticsTest/jobs.json @@ -1 +1 @@ -{"hudson-matrix-MatrixProject":0,"hudson-maven-MavenModuleSet":0,"hudson-model-FreeStyleProject":0,"org-jvnet-hudson-test-MockFolder":0,"org-jvnet-hudson-test-SecuredMockFolder":0} \ No newline at end of file +{"com-cloudbees-hudson-plugins-folder-Folder":0,"hudson-matrix-MatrixProject":0,"hudson-maven-MavenModuleSet":0,"hudson-model-FreeStyleProject":0,"org-jvnet-hudson-test-MockFolder":0,"org-jvnet-hudson-test-SecuredMockFolder":0} \ No newline at end of file diff --git a/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/comment_ignored/stapler-whitelist.txt b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/comment_ignored/stapler-whitelist.txt new file mode 100644 index 0000000000..e8e8b3f4be --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/comment_ignored/stapler-whitelist.txt @@ -0,0 +1,6 @@ +# this line is not read +this-one-is +# not-this-one +#neither +this-one-also +# finally-not diff --git a/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/greylist_multiline/stapler-whitelist.txt b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/greylist_multiline/stapler-whitelist.txt new file mode 100644 index 0000000000..0979aa0565 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/greylist_multiline/stapler-whitelist.txt @@ -0,0 +1,4 @@ +signature-1-ok +!signature-2-not-ok +signature-3-ok +!signature-4-not-ok diff --git a/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_empty/stapler-whitelist.txt b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_empty/stapler-whitelist.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_emptyline/stapler-whitelist.txt b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_emptyline/stapler-whitelist.txt new file mode 100644 index 0000000000..b11fc15592 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_emptyline/stapler-whitelist.txt @@ -0,0 +1,9 @@ +signature-1 +# just an empty line + +signature-2 +# space after the exclamation mark +! +# no space +! +signature-3 diff --git a/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_monoline/stapler-whitelist.txt b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_monoline/stapler-whitelist.txt new file mode 100644 index 0000000000..96b147fc19 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_monoline/stapler-whitelist.txt @@ -0,0 +1 @@ +method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ContentProvider getObjectCustom diff --git a/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_multiline/stapler-whitelist.txt b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_multiline/stapler-whitelist.txt new file mode 100644 index 0000000000..1d36e5a638 --- /dev/null +++ b/test/src/test/resources/jenkins/security/stapler/StaticRoutingDecisionProvider2Test/whitelist_multiline/stapler-whitelist.txt @@ -0,0 +1,2 @@ +method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ContentProvider getObjectCustom +method jenkins.security.stapler.StaticRoutingDecisionProviderTest$ContentProvider getObjectCustom2 diff --git a/test/src/test/resources/plugins/annotations-test-sources.jar b/test/src/test/resources/plugins/annotations-test-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..a320c1c60d1a4d4345fcbbdf14d6fc9bee9fb39a GIT binary patch literal 3229 zcmbVOdpMM78-JY#O%bN)utl3TV@^m4kuQfCV-UlZv^!*ICT1oxPHA&W>r|W4!J)R5 zvB)vsqEHb^W~3U5eAXd+QBrDBYir*zF>e}Od-c56bG^?W_wRS#&;302bN^gP3W{m~ z3007XysSQrD9mNJibhgKlZJdercJ34`*?xk|Z*d}Fuy^4k462oX zr`!!MH0d1-MhW-^cS8#P>qd}65d=g>a_i7wFxHWbCX(fnEf8zg2IQb7*Ph}H86Zc~ zN7A=&dH>(f^NVvTYcokOTW|b$uzyeF~gaW``=cnhj5CMg?~< zYXdG6z)5G}<~LB67%*tey>uq$3x-;s8nOdJ0zL&~o#%Yw`dirmA10H<_2JT4Ob(aE z;X*u$PXNTsjz!>Vc8ftip)}?fj8}hdOlM&~ZVUT)v~!)A{3|>pcnl{7hnJB7iVV6WB=8K>^c&W%z{p@Z4>f^>~e%VI5Bw_6wD&=&o#$ zyc?;#e4l4x;tQ`HxRS?I?ZP#BZPBiy$y$+?CKh`mdFm%)@aw+sRU07e-|638n6=e2 zuuW6p#X2ItYd1CZw83vD?(9@{*cZtyD{Im&oMKz)#`zQe)QHHwtRB<=<1StBd`;O) zMC<|UR;{9#TWAbAbmf?p$nTb6$K@oIM_ZA`JE+byIn4X`&TDdSx36d;`w6M`JE9rg(tF5avJfGPqU7~gFv6~Faml>G zpBO|Tel&_2O44JzxmSOBPnQw$oT|_=`MmGAO7^>nWiHi!U(>aUtwq$wnLmHC^;e$) zK%hitjI!HH;Te>i$q~1Q3$fT-*yHM#?Y)-0r#DuQE>6yUc;U!7QI_@9i&mLwQJsO9 zMnC5xSH?pxr0?4O*D7%}LXT8ZMdHJLwacBHA8mx+03Pxta86ed(~lMo-nhWPi1M;K zj)LZV3IXHnj3%^`3xs4MnS>S?lzHF`P-KtF%7IJ7W_0<08xX4zwIm9={3raX>Eefy zO5N=NDUuL0 zZ$qnZ9N|s{MJzp=a;IO@)62@35py3g#I33DBx~L#)G4k>YBP;H{Z?q*OV1-^l)9&(dXa2 z(V7fAzRyyAtS;SY`@334a}gHbRfjUQTd!4N9!}-aii?jwe0%s8C(BJenuD&qx3_D3 zg|{r!voRltc_CC@{5BwD|7&=I!#MK7YQMh53|=u(DO0OjIWPC;{~W}aVV5&-<7%5U z^>SX*Hl)&;#>)4mU+zdige{q99PYg2a91bEN^=QAr$h%AVS3Z6kmuWqGqG`mH#gna zvpiGtvjg(AQjDqr%xOp6y~&0rA;0IYtlhZ%SW4rAlh&oBwmhdeyW%uqZ#L;0^sW)= z4}bn*R`68IKX0gjl7u@8Z!SW&DCQ6$ zimP5*AL{;K9u-aT&(n|Td0$Yu=xN487{%0#i$E&9zoJX*Tt?e^0GSfn)0lB}ZO5@k zcY9CPnx~gi^NO}>H}*zZUhr4;9@ZbrEVa0bsd;Al?D|UM_Q&w24lK>WEdTrtMCyxj z^U_mZ1JuoMjI(ffsjk&;YCD^9$cYjEW}7EOzDO98`%VNTQrn# zu51u%6e(dWNI0AH8O0*a!(6afvq-@upMzy8nECm{8b-?Z{9o}wHanweq*qLqrkQb( z6SD>EMl$jbVVqatNKz`f$Fh2<)CmTZQgmr&g#u-DMJX@~1kO1N8R>)sE7|_X9trg$ h>j{zid7$#uexwhID@jRNObG|ShoELJ0h>Gk`~xZ@NR-pmq*rK&n=_*&+cIyQ`-CDt&u&_=?tA2k z(i#=}iHy9%vAk=kx=*UGzG$roZa6jvCVBj0i$jWvE}|PA9~yEzoM^UI_&F z{CJp1Umh8hU}1o(V3W*9M@T@~C&-fkC{*Oxy!z5fc9mpqp7z9b8a^Hl#l0XWv?~g9 zl83c4yjXt=Recn&Yr{r)=MBKrQlkc6JNDivC-koK_gCF-ClEe)N(_)pjqK{HcTP5K zro;#*u?=@zz0g$Z72|=Ed~*Z&%e{Y(0{pXkRYisVB?y0)LjIGKmAS!R!WFL=*G%#p zuG2rl^$X9++|by@(b$pE)!M2yW<)lC8LjUa&=VjfC*OisRXIP@%i(PW33wNX{$;6d z7n9u9Hae$j+&`EjTo~zo7@~2t!1=>kYyuH=cG!3ddb-$J+n10xy7I{ zWt}8J0Z%5=K${eUVj*NO#yttv)?_xX!kMa2{9vNI637I#*6jJ}6Y-9aQTIK!#9xYR;5S+4YnSJHWRRE7`=OfzX3?=5)wdNr?AJR8ui$iKJ_9F_;A?#i)yT`en)HXv}t?rQrd+Y@G@pJ(Yc zr$w03KlyG7FXm#{T&E+-3`x=2C-v$}#!kK(Rd|?}fBhJoM!d06*wvNDRDTcgD?;%9 zvrb}{ALw7agn+Qef`B0Ti|_SqY;1x0KyzCgM+Ts=BaqQT-=RzufqlvnpIC})fc1T} z*Gl4nrjaS`q*|;M+v_1@lb~XwH$is!DF-#o>WJi<Q1XhMX(542md3s+2cVTfqaT3tq;(%eSfGFHx?+CX;z&@o({iQXdAb?Y9H-aB8E@W3ly>1;8?ggO(+RbrcA zUyT$nbA??YBq4-d2oc2U@^@I)s#~jUU#fWixC7*jq+8)ybtB5v}>E_ zVndi|Je>3sh|!^?VV${GbSo!{)|i>avbrC+aJeikHuI+@vW;ot?a9z#x2IzVjGDn zyw$-Psr1U2&WR=JTSnD+l`@E%>aXV#w@Dk0rotHuNYT_SQ;0EiD4ETEfbry#3@Y`g ze2#Av7=q4F$1&b>gjS%#F4~MLLJfTn0==X`$-QMdE^C(IuEDmMf3vv6v$m_aqI}7- zF*wd%o6soQxG@IHmcFDik-6LPRNT45psbh04<9!%`m)rA`?}-vBRs_|F@awyp+970 z>k|PQ!a(=PAvIHW@%I?Wtm~B~ zdr-4xrK?-4ONB#uAR$O~deY#14v*yu;wFF9x!Alec&KEwtvZcutDHoFAnVLw=fMA7 ztpT=JT)FWEzvmkfFNTFGiL$G%z;{U2QJDh%_tKdcn&QHtO9aQMC9H{-85|A_J%q@j zgc-Y^!gY!`@;Qa6G7n1RF1pf+OCTG9doBt9w{?D&b0LYK7D@ZVQa&olelM`B({z}k z#o>3#$|eQ4&gSDiDyKP*J4^eE?@lpWP)b>r#C<<**E1I|B5EfJgx!g7=R zrtZ?d*YCTi2x1LA3yhw|lpLjIV(R4G9Pb9!;NO*z#jS0Px6aw~q?J*1Nf}x@=i4oI zS7SO8-G*%Rp~b(_V_1(5?c;hK7lnxG(Q&-(#u_xN)cUdoxXEdhvSC~S<&iWgSnpxi z@}tCO8GxQIMcR$i3|0SFJUKfdX9@^uwxj;OWtpHP;TJH7m!q7059P;b>iUlI5#8Z{ zrQ=L0JS#a~PhK0A>Q-W!L%k3I#~3Z~LC)f3n~1xTDdh3d!ZmZ}0C>&aRPnl z3`9WM!V7@2fN;UgDu!YL3xrI`ZsRB0q&`?M3=Hqp6*Z_Txjt}IT3mppTXMObh>@$c z;0azmHqp9pu-%$Vwq78dq38I-aS@t~T~geQys16xjwxk6e|%CGb_!+jiOcb0ZV*VRdJu zUgGW7UbDulm2&_<9IdzRt&F$JIcTRZh$f^i*^k&dVSO_2xK%OCW52G(0!PZF(KbCL z0zdDpujhW=5sGm%88)BfcEl2na|6$7mL5KF0z~6WUzH|ur$frdV`?V#)b~QlS47fI zMcQzG+8h0P98$w*D80`r);42I!VE+^6gUu1<#z5&$(%9i99-MWBASKk!Fq^hnV^83 z{NNNx5I7N0TE*e4Dq?kP+!P?Gn9S$5_7{EMg+2zYOxCVk$=2B@sZivrXr7zPst;FG7R3KuXS&+6G4Fgks6S*}-LgDs*{m_*4LS#`KGJQ8AYI zy!8Vl>(g@$DAF_=5H8&E3dG6xjvnjgd-~Rv%@5|xn{Qd(mnjt81NAABSL%@9NV_)L zHc^QXYUmP#<^hH`p3nsEIg!G|CzFKMrL(FAgGqx*kHoViT-T<=n z-B`b)11!LO6r5!j&V>{^D;Pp8{nSA58FOqUGL)+@(^IKcNkg=!9JVkWyu?=}HOyoa zH94P6d9PT}beAZ8Oki1OB`=lh{)uaZN6N#<+u0wJ-I%1Lwj;LZXx~C)5qMn??Z%DD@$+Y<4?W zMdvtFvvO=cQ}%1LDi;h3N{C@^o}8@B@ehgJoUy>K^1*ov=tMuI zRiQyNlOG$ed_(uk$Y#!)7#+=dI*fQn^JqpN_B8UfuJ_A>(tVJ_8>@A#Vc!qz?Z~5O1maFKj^e{{wC7oQ!d;Ao1GFg23((Nlsa27%qX@wC zuP@DB9s)x&rC#F`@F5?Va-42-L@}8}Wm}uxjnV9Y$TOmAULs6$p3&fU?9m*v(D@aj z8d29pIq)>TF1i(UI&JOYE4G8S=Tx6!3F~ty()EpV4hW{AP&nTfpFRg8px&`*CV!TY z3n~JTMx*-+pQ&e<A6ojR&K8pBz4n0$mJ(W4jniDr0^u8h9i>{-qp)`4 zr}IP$x^z*|tw$bpr^&8%M8Y&U1QkVxtte1{6sP-waGGs<9!O?dXNk#4-Ik_Q9b1q8 zJVm61F(NR+XwUta#PA)IQ*20a;t-!~v1DY;-eBQm?UESFpp= zg?Nj7v;LicLhs#Ca@%?+*XrVN--p3mpvFVszIKdiYD`KrmZqU_Vu;Q{teRtMptb@% z>o6yyQ~D5yy7HzT=1Xl_ECx-RE*nLx%zI88X{nGU^7>NLflSxqHGro?wHYEjldC55 zC)Gg{OZo&`li_ODejK(1iu<5`MepfE6w6Zsi}5HJ8vJ3^wXp#HBn^V^K_wQUt@q#S z{3}e9&I;e$bqYw!07YS&qtH1H@vt!Pr#mC~Hph&j-Q62_W|o zn_Jz^u)!ZP?O9Szz!D5f;08&Y(GAB3f;JMU3{>LzbFx;%Ui(hA!Qcgn7*K_;n8pF{ z`81qkF1}H{!}-^2L(SdGe6q0tQlYu?ou-UFj^T zLrK#o#IAdTa!lNhWxL-S@SIhSd>CG~U;L<8EZTT_3Dy~SYB|fvg8=W}ehRI}__800 z02IjEAAM1p3VF6$_kKPd$}bg?_q+4wI$o#{YsjqJCyC;wm*3LowBgtbRupc_arAgk zn{*%!KrNF3l51u2b&^$=RwTW;IA)sFR{0D2>aa($0n3fV?`2Jj~)JMWxDAm-eV36S-6SV&&T z8tts9z+xKoOO6bU4%7AKN}@?9+gmu8E0{=75^crI#o;7UU~lGOuZ{Oc9c(UkS55Kp zg~c;tnQre3T2GT(C1WYvW_xPC(XIbhlSR^21Z}}cuYMriC{H`s-)@H=B6E7fk1KSy zmH5pF8;2r+!jyt)D{qf-hXNa?UiRb?a6mHV+~xxA(aqLXj@!eua?w^RD=(tCl43a@ z%9Y-|(hh9mmSaqK3-oo=%_iVbZJ?Ytq4D2mb zrD3o_=2p}*F2y8TH>B|Fe6LleSTi^6X$)-&-o#2=ywGu%ZK^5Zqg|3Q*dCHVbgpvDZU>c}Sc^krJh#^QkgNGP_}t9l~*gH)gW@to8g46YJU z@!PgUpB!AP8pR)h9#5NyNC1hJ`Nqgep=CTDiNQ3kVwXMS|uvSyK6`Y4_`l-rI z_qG*Bnd!P<4@8jiG`Q+(dRt%9hW)zfr8Q37LvyXg3Rnx=HOL)KOuSB)y6>=2NvJCf zc+)~2%xLi~Ji}@D(~BS%Dl`vy%uJY@26q_I8<=8I%H-mnMry7zZd`a~tvP(3(|*Du ze>})o)W)HchGm|I>5VAs^D8gQfb@jrgMCoE*799JM;gaMsLP%#Na6vB18Z27c=xg8 zJXa4-l~-VgBL9Zgs@x})YW5-H@$O>6W~f|!orIXQ34dB#oe)rU@XjX#(g1@@F~-or z!NT62iWxmn3A-I*LY>J(I9lbYbQaG?wGBi4%wpWUj<>1ChMoT;bAGm_YyWftlXzoF zBE?kY_`dy`ZgpI%oOJ^_Fg;_ZK$SSGw5-B#(SEqvn59oEazomdzbj+dftPc)T_zr@ zOv(~FNoAWUm9x12)HdX33M!d}_f>|MM5FUwyKFiZf*430)H3TF?w*2ukvRG0#1xdR z6MPWQ4L9G}w%lN6E;}5?_R7s-pO}en)q;*NjX8gD2Q1KM$R^{Gj|&`0%xq45&{p1j zqBcp?ZmUwsKTZ+Yt?%HrebT1(4OoH(d_&`7b1YwP;x-?OO7 zhJ6qb-gz&Adnr5RdliP1N&<=5g4iu40?poW z-x%^hel7({!cnD`LtE3~ozC0aNYL47;ytR%gU$?jy7ztZ_t>#1w-tg3-L>929~?5t z(0xkJ%vRZ+4cFyr#Uc8}%*Fs*hk+}j2v=9csI5&R%Oq4( Jj4%4p0hv6eNv&or z2P^7@;^7q}JpXpaOg3tGtr#cl@=;m))v^2SMmVto|*>w>w z{flvAJd!wwWvR>v$RK)mIn*}7Nuy(-ss7Y@ZALx2vv|cl;_-2BYK6~(+}phPQJIc^ z_po-*e5Q2w&PQOgOxFFY>eCrWJ?YNP-0biWjAebq zr4?Dfx2@)4V;1SvS0RVM^R}RKK?EB&!Yws6`~k#Yr`%Z72ED_3>qr#mP`U`KOEzla&y}qAJXtT2R4b{ z#VF(kABO{a;rjQVB>tCdXvo=>;5sh{iQPla?kXd)*w z>~}aVJ)CW{E|??>gLtDG#u}H|WQ#T>w=ieA@$|)Zgm%sD7+~uIu}j~Rlm_2T?*kT-(I!k=inVsvtJ}R(1yVNwm9I~v zr~%s*vZvRshaF#8;r4?jgjbGcWYZ06D!tZsCgE~RciL=JGts}^acug`eIJ4Bt z2%Ku&;F+9rf4n%J=t(_aF7jCTYWUF^LS%Y#qxSI`|0Lh07qBlNk!=Zgayk3#EH~bb zJ0s`21!*Rdgq3s6rCNTgmC7+oH0cI=c3C3}gs})lsHHf5d?NDA7XbhO8a*;BEm`g~W(s<%c{xnTQHk?(Iq=Y9vJZ%KyaR)!A5AEVO= zKq*ukC=QAq=~EeKZwHI_~)7S-}wa8=S|5!xgTqN zXJeax?qL2dO!jwSb6dtg_A!ihR!*kR`nF=!p1oK6)VO(ox>y7@-Egn3PC~j3#lWpHj&NCYeo*f z-iyohY5HPhj1iO{Sxt?vr(93O6b<-`+hC!X%w0)nBaf+nU|d_p*z-XibtFrx-L^>b zTrn~)bkK5$vSZZoxa#E_mzT%P-2_ER^KmbuZ^`H147M0d_46X-Fg!^VIqBsGDnK~7 zU!ru2pWx<_8LjoC2+e6wU%wua6oiJfwC!=-{*5b zh2l9;5nfWT;fFXb1!%^I0X7#wJvzrq(oRUa4vpL4b~i7mI5}|-KF<5ldX!#D$!`iY z^Evx+F1qDM*k;^!Z#`z?s;xn-y7TGp;lCAkhy2(Ld|=k|Q+izO#W?(AC*a@<+%MZpspwkt==90xu)F79 zjJ^8OL=fhB0r#)u)y;WD*byaM5V4aP4dU3XM+k&s zjutJ1N|r-ZDs=8QT~0}x8e;|dxSSTGpR_F^jUH&?dcPVLsK&vF5s~f7p=aGTe^ub$ zHLiT@Tp}+GI2kjal-+m)q}$3euxwtkq5{5io%T6S>~k^2Bj#S?0PF2p^PBk>&ENFyZ`=Q#!2FHy z2I}`4|Fr$ry!*2W{VUpE``O=Urs)5Q_W$W_e*^#Bu|I3lKgLhT-sAl#G`}j;FE9OF z`Dcy#txWU!Z_59+V*T%UKWo-+JoEnu@2{SGu3*0m{QDaA%S4`E<9}!1kBatZJ3o&f o{+a-O+u0)gPaN~NiTt$lI~~YN!NC56M1H;!pZ`tGL_dH1KWZtvF8}}l literal 0 HcmV?d00001 -- GitLab From 7bae6dd6ef23af988801d38dc0f8f693bc6283f8 Mon Sep 17 00:00:00 2001 From: Wadeck Follonier Date: Thu, 22 Nov 2018 11:57:29 +0100 Subject: [PATCH 069/424] [SECURITY-1072] Make ApiTokenStats work --- core/src/main/java/hudson/model/User.java | 6 +- .../main/java/hudson/model/UserIdMapper.java | 3 +- .../jenkins/security/ApiTokenProperty.java | 2 +- .../security/apitoken/ApiTokenStats.java | 111 +++++++++-- .../security/apitoken/ApiTokenStatsTest.java | 47 ++--- .../apitoken/ApiTokenStatsRestartTest.java | 187 ++++++++++++++++++ 6 files changed, 314 insertions(+), 42 deletions(-) create mode 100644 test/src/test/java/jenkins/security/apitoken/ApiTokenStatsRestartTest.java diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java index a3f12394f6..8355aa7f4b 100644 --- a/core/src/main/java/hudson/model/User.java +++ b/core/src/main/java/hudson/model/User.java @@ -739,14 +739,16 @@ public class User extends AbstractModelObject implements AccessControlled, Descr * Returns the folder that store all the user information. * Useful for plugins to save a user-specific file aside the config.xml. * Exposes implementation details that may be subject to change. + * + * @return The folder containing the user configuration files or {@code null} if the user was not yet saved. * * @since 2.129 */ - public File getUserFolder() { + public @CheckForNull File getUserFolder() { return getExistingUserFolder(); } - private File getExistingUserFolder() { + private @CheckForNull File getExistingUserFolder() { return UserIdMapper.getInstance().getDirectory(id); } diff --git a/core/src/main/java/hudson/model/UserIdMapper.java b/core/src/main/java/hudson/model/UserIdMapper.java index d0e394f88b..d3cd8c0342 100644 --- a/core/src/main/java/hudson/model/UserIdMapper.java +++ b/core/src/main/java/hudson/model/UserIdMapper.java @@ -34,6 +34,7 @@ import jenkins.model.IdStrategy; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; +import javax.annotation.CheckForNull; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -75,7 +76,7 @@ public class UserIdMapper { return usersDirectory; } - File getDirectory(String userId) { + @CheckForNull File getDirectory(String userId) { String directoryName = idToDirectoryNameMap.get(getIdStrategy().keyFor(userId)); return directoryName == null ? null : new File(usersDirectory, directoryName); } diff --git a/core/src/main/java/jenkins/security/ApiTokenProperty.java b/core/src/main/java/jenkins/security/ApiTokenProperty.java index 3ffab72616..b23605846f 100644 --- a/core/src/main/java/jenkins/security/ApiTokenProperty.java +++ b/core/src/main/java/jenkins/security/ApiTokenProperty.java @@ -128,7 +128,7 @@ public class ApiTokenProperty extends UserProperty { this.tokenStore = new ApiTokenStore(); } if(this.tokenStats == null){ - this.tokenStats = ApiTokenStats.load(user.getUserFolder()); + this.tokenStats = ApiTokenStats.load(user); } if(this.apiToken != null){ this.tokenStore.regenerateTokenFromLegacyIfRequired(this.apiToken); diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java index f92fd8e701..86d71b1861 100644 --- a/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java @@ -23,14 +23,17 @@ */ package jenkins.security.apitoken; +import com.google.common.annotations.VisibleForTesting; import hudson.BulkChange; import hudson.Util; import hudson.XmlFile; import hudson.model.Saveable; +import hudson.model.User; import hudson.model.listeners.SaveableListener; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import java.io.File; import java.io.IOException; @@ -54,9 +57,13 @@ public class ApiTokenStats implements Saveable { */ private List tokenStats; - private transient File parent; + private transient User user; - public ApiTokenStats() { + @VisibleForTesting + transient File parent; + + @VisibleForTesting + ApiTokenStats() { this.init(); } @@ -94,6 +101,13 @@ public class ApiTokenStats implements Saveable { this.tokenStats = new ArrayList<>(temp.values()); } + /** + * @deprecated use {@link #load(User)} instead of {@link #load(File)} + * The method will be removed in a later version as it's an internal one + */ + @Deprecated + // to force even if someone wants to remove the one from the class + @Restricted(NoExternalUse.class) void setParent(@Nonnull File parent) { this.parent = parent; } @@ -165,7 +179,17 @@ public class ApiTokenStats implements Saveable { if (BulkChange.contains(this)) return; - XmlFile configFile = getConfigFile(parent); + /* + * Note: the userFolder should never be null at this point. + * The userFolder could be null during User creation with the new storage approach + * but when this code is called, from token used / removed, the folder exists. + */ + File userFolder = getUserFolder(); + if (userFolder == null) { + return; + } + + XmlFile configFile = getConfigFile(userFolder); try { configFile.write(this); SaveableListener.fireOnChange(this, configFile); @@ -174,25 +198,41 @@ public class ApiTokenStats implements Saveable { } } + private @CheckForNull File getUserFolder(){ + File userFolder = parent; + if (userFolder == null && this.user != null) { + userFolder = user.getUserFolder(); + if (userFolder == null) { + LOGGER.log(Level.INFO, "No user folder yet for user {0}", user.getId()); + return null; + } + this.parent = userFolder; + } + + return userFolder; + } + /** * Loads the data from the disk into the new object. *

* If the file is not present, a fresh new instance is created. + * + * @deprecated use {@link #load(User)} instead + * The method will be removed in a later version as it's an internal one */ - public static @Nonnull ApiTokenStats load(@Nonnull File parent) { + @Deprecated + // to force even if someone wants to remove the one from the class + @Restricted(NoExternalUse.class) + public static @Nonnull ApiTokenStats load(@CheckForNull File parent) { // even if we are not using statistics, we load the existing one in case the configuration // is enabled afterwards to avoid erasing data - XmlFile file = getConfigFile(parent); - ApiTokenStats apiTokenStats; - if (file.exists()) { - try { - apiTokenStats = (ApiTokenStats) file.unmarshal(ApiTokenStats.class); - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Failed to load " + file, e); - apiTokenStats = new ApiTokenStats(); - } - } else { + if (parent == null) { + return new ApiTokenStats(); + } + + ApiTokenStats apiTokenStats = internalLoad(parent); + if (apiTokenStats == null) { apiTokenStats = new ApiTokenStats(); } @@ -200,7 +240,48 @@ public class ApiTokenStats implements Saveable { return apiTokenStats; } - protected static XmlFile getConfigFile(File parent) { + /** + * Loads the data from the user folder into the new object. + *

+ * If the folder does not exist yet, a fresh new instance is created. + */ + public static @Nonnull ApiTokenStats load(@Nonnull User user) { + // even if we are not using statistics, we load the existing one in case the configuration + // is enabled afterwards to avoid erasing data + + ApiTokenStats apiTokenStats = null; + + File userFolder = user.getUserFolder(); + if (userFolder != null) { + apiTokenStats = internalLoad(userFolder); + } + + if (apiTokenStats == null) { + apiTokenStats = new ApiTokenStats(); + } + + apiTokenStats.user = user; + + return apiTokenStats; + } + + @VisibleForTesting + static @CheckForNull ApiTokenStats internalLoad(@Nonnull File userFolder) { + ApiTokenStats apiTokenStats = null; + XmlFile statsFile = getConfigFile(userFolder); + if (statsFile.exists()) { + try { + apiTokenStats = (ApiTokenStats) statsFile.unmarshal(ApiTokenStats.class); + apiTokenStats.parent = userFolder; + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Failed to load " + statsFile, e); + } + } + + return apiTokenStats; + } + + protected static @Nonnull XmlFile getConfigFile(@Nonnull File parent) { return new XmlFile(new File(parent, "apiTokenStats.xml")); } diff --git a/core/src/test/java/jenkins/security/apitoken/ApiTokenStatsTest.java b/core/src/test/java/jenkins/security/apitoken/ApiTokenStatsTest.java index 6649702190..266899ad28 100644 --- a/core/src/test/java/jenkins/security/apitoken/ApiTokenStatsTest.java +++ b/core/src/test/java/jenkins/security/apitoken/ApiTokenStatsTest.java @@ -25,26 +25,21 @@ package jenkins.security.apitoken; import hudson.XmlFile; import org.apache.commons.io.FileUtils; -import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Mockito; -import org.mockito.internal.matchers.LessOrEqual; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; -import java.time.temporal.TemporalAccessor; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -82,8 +77,7 @@ public class ApiTokenStatsTest { final String ID_2 = "other-uuid"; { // empty stats can be saved - ApiTokenStats tokenStats = new ApiTokenStats(); - tokenStats.setParent(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); // can remove an id that does not exist tokenStats.removeId(ID_1); @@ -92,7 +86,7 @@ public class ApiTokenStatsTest { } { // and then loaded, empty stats is empty - ApiTokenStats tokenStats = ApiTokenStats.load(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); assertNotNull(tokenStats); ApiTokenStats.SingleTokenStats stats = tokenStats.findTokenStatsById(ID_1); @@ -103,7 +97,7 @@ public class ApiTokenStatsTest { Date lastUsage; { // then re-notify the same token - ApiTokenStats tokenStats = ApiTokenStats.load(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); ApiTokenStats.SingleTokenStats stats = tokenStats.updateUsageForId(ID_1); assertEquals(1, stats.getUseCounter()); @@ -118,7 +112,7 @@ public class ApiTokenStatsTest { Thread.sleep(10); { // then re-notify the same token - ApiTokenStats tokenStats = ApiTokenStats.load(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); ApiTokenStats.SingleTokenStats stats = tokenStats.updateUsageForId(ID_1); assertEquals(2, stats.getUseCounter()); @@ -129,7 +123,7 @@ public class ApiTokenStatsTest { } { // check all tokens have separate stats, try with another ID - ApiTokenStats tokenStats = ApiTokenStats.load(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); { ApiTokenStats.SingleTokenStats stats = tokenStats.findTokenStatsById(ID_2); @@ -146,7 +140,7 @@ public class ApiTokenStatsTest { } { // reload the stats, check the counter are correct - ApiTokenStats tokenStats = ApiTokenStats.load(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); ApiTokenStats.SingleTokenStats stats_1 = tokenStats.findTokenStatsById(ID_1); assertEquals(2, stats_1.getUseCounter()); @@ -157,7 +151,7 @@ public class ApiTokenStatsTest { } { // after a removal, the existing must keep its value - ApiTokenStats tokenStats = ApiTokenStats.load(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); ApiTokenStats.SingleTokenStats stats_1 = tokenStats.findTokenStatsById(ID_1); assertEquals(0, stats_1.getUseCounter()); @@ -168,7 +162,7 @@ public class ApiTokenStatsTest { @Test public void testResilientIfFileDoesNotExist() throws Exception { - ApiTokenStats tokenStats = ApiTokenStats.load(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); assertNotNull(tokenStats); } @@ -179,8 +173,7 @@ public class ApiTokenStatsTest { final String ID_3 = UUID.randomUUID().toString(); { // put counter to 4 for ID_1 and to 2 for ID_2 and 1 for ID_3 - ApiTokenStats tokenStats = new ApiTokenStats(); - tokenStats.setParent(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); tokenStats.updateUsageForId(ID_1); tokenStats.updateUsageForId(ID_1); @@ -201,7 +194,7 @@ public class ApiTokenStatsTest { } { - ApiTokenStats tokenStats = ApiTokenStats.load(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); assertNotNull(tokenStats); ApiTokenStats.SingleTokenStats stats_1 = tokenStats.findTokenStatsById(ID_1); @@ -228,16 +221,15 @@ public class ApiTokenStatsTest { /* D */ createSingleTokenStatsByReflection(ID, "2018-05-01 09:10:59.235", 1) ); - ApiTokenStats stats = new ApiTokenStats(); + ApiTokenStats stats = createFromFile(tmp.getRoot()); Field field = ApiTokenStats.class.getDeclaredField("tokenStats"); field.setAccessible(true); field.set(stats, tokenStatsList); - stats.setParent(tmp.getRoot()); stats.save(); } { // reload to see the effect - ApiTokenStats stats = ApiTokenStats.load(tmp.getRoot()); + ApiTokenStats stats = createFromFile(tmp.getRoot()); ApiTokenStats.SingleTokenStats tokenStats = stats.findTokenStatsById(ID); // must be D (as it was the last updated one) assertThat(tokenStats.getUseCounter(), equalTo(1)); @@ -293,8 +285,7 @@ public class ApiTokenStatsTest { @Test public void testDayDifference() throws Exception { final String ID = UUID.randomUUID().toString(); - ApiTokenStats tokenStats = new ApiTokenStats(); - tokenStats.setParent(tmp.getRoot()); + ApiTokenStats tokenStats = createFromFile(tmp.getRoot()); ApiTokenStats.SingleTokenStats stats = tokenStats.updateUsageForId(ID); assertThat(stats.getNumDaysUse(), lessThan(1L)); @@ -311,4 +302,14 @@ public class ApiTokenStatsTest { assertThat(stats.getNumDaysUse(), greaterThanOrEqualTo(2L)); } + + private ApiTokenStats createFromFile(File file){ + ApiTokenStats result = ApiTokenStats.internalLoad(file); + if (result == null) { + result = new ApiTokenStats(); + result.parent = file; + } + + return result; + } } diff --git a/test/src/test/java/jenkins/security/apitoken/ApiTokenStatsRestartTest.java b/test/src/test/java/jenkins/security/apitoken/ApiTokenStatsRestartTest.java new file mode 100644 index 0000000000..b2f322a171 --- /dev/null +++ b/test/src/test/java/jenkins/security/apitoken/ApiTokenStatsRestartTest.java @@ -0,0 +1,187 @@ +/* + * The MIT License + * + * Copyright (c) 2018, 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.apitoken; + +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; +import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebRequest; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSpan; +import com.gargoylesoftware.htmlunit.util.NameValuePair; +import com.gargoylesoftware.htmlunit.xml.XmlPage; +import hudson.model.User; +import jenkins.security.ApiTokenProperty; +import net.sf.json.JSONObject; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runners.model.Statement; +import org.jvnet.hudson.test.For; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.JenkinsRule.WebClient; +import org.jvnet.hudson.test.RestartableJenkinsRule; + +import java.io.File; +import java.net.URL; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReference; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.xml.HasXPath.hasXPath; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@For(ApiTokenStats.class) +public class ApiTokenStatsRestartTest { + + @Rule + public RestartableJenkinsRule rr = new RestartableJenkinsRule(); + + @Test + @Issue("SECURITY-1072") + public void roundtripWithRestart() throws Exception { + AtomicReference tokenValue = new AtomicReference<>(); + AtomicReference tokenUuid = new AtomicReference<>(); + String TOKEN_NAME = "New Token Name"; + int NUM_CALL_WITH_TOKEN = 5; + + rr.addStep(new Statement() { + @Override + public void evaluate() throws Throwable { + JenkinsRule j = rr.j; + j.jenkins.setCrumbIssuer(null); + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + + User u = User.getById("foo", true); + + ApiTokenProperty t = u.getProperty(ApiTokenProperty.class); + assertNotNull(t.getTokenStore()); + assertNotNull(t.getTokenStats()); + + // test the authentication via Token + WebClient wc = j.createWebClient().withBasicCredentials(u.getId()); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + + WebRequest request = new WebRequest(new URL(j.getURL() + "user/" + u.getId() + "/descriptorByName/" + ApiTokenProperty.class.getName() + "/generateNewToken"), HttpMethod.POST); + request.setRequestParameters(Arrays.asList(new NameValuePair("newTokenName", TOKEN_NAME))); + + Page page = wc.getPage(request); + assertEquals(200, page.getWebResponse().getStatusCode()); + String responseContent = page.getWebResponse().getContentAsString(); + JSONObject jsonObject = JSONObject.fromObject(responseContent); + JSONObject jsonData = jsonObject.getJSONObject("data"); + String tokenName = jsonData.getString("tokenName"); + tokenValue.set(jsonData.getString("tokenValue")); + tokenUuid.set(jsonData.getString("tokenUuid")); + + assertEquals(TOKEN_NAME, tokenName); + + WebClient restWc = j.createWebClient().withBasicCredentials(u.getId(), tokenValue.get()); + checkUserIsConnected(restWc, u.getId()); + + HtmlPage config = wc.goTo(u.getUrl() + "/configure"); + assertEquals(200, config.getWebResponse().getStatusCode()); + assertThat(config.getWebResponse().getContentAsString(), containsString(tokenUuid.get())); + assertThat(config.getWebResponse().getContentAsString(), containsString(tokenName)); + + // one is already done with checkUserIsConnected + for (int i = 1; i < NUM_CALL_WITH_TOKEN; i++) { + restWc.goToXml("whoAmI/api/xml"); + } + + HtmlPage configWithStats = wc.goTo(u.getUrl() + "/configure"); + assertEquals(200, configWithStats.getWebResponse().getStatusCode()); + HtmlSpan useCounterSpan = configWithStats.getDocumentElement().getOneHtmlElementByAttribute("span", "class", "token-use-counter"); + assertThat(useCounterSpan.getTextContent(), containsString("" + NUM_CALL_WITH_TOKEN)); + + File apiTokenStatsFile = new File(u.getUserFolder(), "apiTokenStats.xml"); + assertTrue("apiTokenStats.xml file should exist", apiTokenStatsFile.exists()); + } + }); + + rr.addStep(new Statement() { + @Override + public void evaluate() throws Throwable { + JenkinsRule j = rr.j; + j.jenkins.setCrumbIssuer(null); + + User u = User.getById("foo", false); + assertNotNull(u); + + WebClient wc = j.createWebClient().login(u.getId()); + checkUserIsConnected(wc, u.getId()); + + HtmlPage config = wc.goTo(u.getUrl() + "/configure"); + assertEquals(200, config.getWebResponse().getStatusCode()); + assertThat(config.getWebResponse().getContentAsString(), containsString(tokenUuid.get())); + assertThat(config.getWebResponse().getContentAsString(), containsString(TOKEN_NAME)); + HtmlSpan useCounterSpan = config.getDocumentElement().getOneHtmlElementByAttribute("span", "class", "token-use-counter"); + assertThat(useCounterSpan.getTextContent(), containsString("" + NUM_CALL_WITH_TOKEN)); + + revokeToken(wc, u.getId(), tokenUuid.get()); + + // token is no more valid + WebClient restWc = j.createWebClient().withBasicCredentials(u.getId(), tokenValue.get()); + checkUserIsNotConnected(restWc); + + HtmlPage configWithoutToken = wc.goTo(u.getUrl() + "/configure"); + assertEquals(200, configWithoutToken.getWebResponse().getStatusCode()); + assertThat(configWithoutToken.getWebResponse().getContentAsString(), not(containsString(tokenUuid.get()))); + assertThat(configWithoutToken.getWebResponse().getContentAsString(), not(containsString(TOKEN_NAME))); + } + }); + } + + private void checkUserIsConnected(WebClient wc, String username) throws Exception { + XmlPage xmlPage = wc.goToXml("whoAmI/api/xml"); + assertThat(xmlPage, hasXPath("//name", is(username))); + assertThat(xmlPage, hasXPath("//anonymous", is("false"))); + assertThat(xmlPage, hasXPath("//authenticated", is("true"))); + assertThat(xmlPage, hasXPath("//authority", is("authenticated"))); + } + + private void checkUserIsNotConnected(WebClient wc) throws Exception { + try { + wc.goToXml("whoAmI/api/xml"); + fail(); + } catch (FailingHttpStatusCodeException e) { + assertEquals(401, e.getStatusCode()); + } + } + + private void revokeToken(WebClient wc, String login, String tokenUuid) throws Exception { + WebRequest request = new WebRequest( + new URL(rr.j.getURL(), "user/" + login + "/descriptorByName/" + ApiTokenProperty.class.getName() + "/revoke/?tokenUuid=" + tokenUuid), + HttpMethod.POST + ); + Page p = wc.getPage(request); + assertEquals(200, p.getWebResponse().getStatusCode()); + } +} -- GitLab From 6aa9aa308151e003e4318adc8bd10952472f4ed1 Mon Sep 17 00:00:00 2001 From: Inetov Date: Fri, 23 Nov 2018 13:37:49 +0300 Subject: [PATCH 070/424] some fixes in russian translation --- .../resources/hudson/model/AbstractBuild/index_ru.properties | 2 +- .../resources/hudson/model/AbstractBuild/tasks_ru.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/resources/hudson/model/AbstractBuild/index_ru.properties b/core/src/main/resources/hudson/model/AbstractBuild/index_ru.properties index 361f9b7b23..398bc5012c 100644 --- a/core/src/main/resources/hudson/model/AbstractBuild/index_ru.properties +++ b/core/src/main/resources/hudson/model/AbstractBuild/index_ru.properties @@ -21,7 +21,7 @@ # THE SOFTWARE. on=\u043d\u0430 -startedAgo=\u0417\u0430\u043f\u0443\u0449\u0435\u043d\u0430 {0} \u0434\u043d\u0435\u0439 \u043d\u0430\u0437\u0430\u0434. +startedAgo=\u0417\u0430\u043F\u0443\u0449\u0435\u043D\u0430 {0} \u043D\u0430\u0437\u0430\u0434 Build=\u0421\u0431\u043e\u0440\u043a\u0430 Build\ Artifacts=\u0410\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u0441\u0431\u043e\u0440\u043a\u0438 Changes\ in\ dependency=\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u0445 diff --git a/core/src/main/resources/hudson/model/AbstractBuild/tasks_ru.properties b/core/src/main/resources/hudson/model/AbstractBuild/tasks_ru.properties index b8211ffb90..6836f42e6f 100644 --- a/core/src/main/resources/hudson/model/AbstractBuild/tasks_ru.properties +++ b/core/src/main/resources/hudson/model/AbstractBuild/tasks_ru.properties @@ -21,10 +21,10 @@ # THE SOFTWARE. Back\ to\ Project=\u041D\u0430\u0437\u0430\u0434 \u043A \u043F\u0440\u043E\u0435\u043A\u0442\u0443 -Changes=\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F. +Changes=\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F Console\ Output=\u0412\u044B\u0432\u043E\u0434 \u043D\u0430 \u043A\u043E\u043D\u0441\u043E\u043B\u044C View\ as\ plain\ text=\u0412\u044B\u0432\u0435\u0441\u0442\u0438 \u0431\u0435\u0437 \u0444\u043E\u0440\u043C\u0430\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F Edit\ Build\ Information=\u0420\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044E \u0441\u0431\u043E\u0440\u043A\u0438 -Status=\u0421\u0442\u0430\u0442\u0443\u0441. +Status=\u0421\u0442\u0430\u0442\u0443\u0441 View\ Build\ Information=\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0418\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044E \u043E \u0421\u0431\u043E\u0440\u043A\u0435 raw=\u043D\u0435 \u0444\u043E\u0440\u043C\u0430\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0432\u0438\u0434 -- GitLab From 9494748f93193f268c3e291e83ddff1ba9ef5123 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Fri, 23 Nov 2018 00:27:45 +0100 Subject: [PATCH 071/424] Add missing dependency on cloudbees-folder in test This got lost in the upmerge due a bad merge after test was split up into multiple modules --- test-pom/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test-pom/pom.xml b/test-pom/pom.xml index 100e83563b..2cc12fad52 100644 --- a/test-pom/pom.xml +++ b/test-pom/pom.xml @@ -68,6 +68,12 @@ THE SOFTWARE. jenkins-test-harness-tools 2.0 test + + + org.jenkins-ci.plugins + cloudbees-folder + 6.3 + test ${project.groupId} -- GitLab From b0d1501c3508443c91b149b3d4d12b53c955ffdc Mon Sep 17 00:00:00 2001 From: charanbir Date: Sat, 24 Nov 2018 01:40:18 -0800 Subject: [PATCH 072/424] [JENKINS-45318] - Add dependancy on net.i2p.crypto to Jenkins CLI (#3764) * JENKINS-45318 Add dependancy on net.i2p.crypto inside sshd module * JENKINS-45318 Add dependancy on net.i2p.crypto inside sshd module * [JENKINS-45318] - Add reference to the ticket --- cli/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cli/pom.xml b/cli/pom.xml index 670e416a26..0a5aa6a5c3 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -66,6 +66,12 @@ 1.7.0 true + + + net.i2p.crypto + eddsa + 0.3.0 + org.slf4j slf4j-jdk14 -- GitLab From 217b29342137b49d7368207404a980a33fbe9093 Mon Sep 17 00:00:00 2001 From: Mark Waite Date: Sat, 24 Nov 2018 13:22:43 -0700 Subject: [PATCH 073/424] [JENKINS-52282] Add isJavaWebStartSupported to JNLPLauncher (#3766) * [JENKINS-52282] Add isJavaWebStartSupported to JNLPLauncher Seems cleaner to use JNLPLauncher than to use Functions. Narrows the scope of the method so that callers know it is specific to JNLPLauncher. * Include hyperlink to jenkins.io Java Web Start redirect page --- .../main/java/hudson/slaves/JNLPLauncher.java | 15 ++++++++++ .../hudson/slaves/JNLPLauncher/main.jelly | 29 ++++++++++++++----- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/hudson/slaves/JNLPLauncher.java b/core/src/main/java/hudson/slaves/JNLPLauncher.java index bed08613d8..808fdef131 100644 --- a/core/src/main/java/hudson/slaves/JNLPLauncher.java +++ b/core/src/main/java/hudson/slaves/JNLPLauncher.java @@ -221,4 +221,19 @@ public class JNLPLauncher extends ComputerLauncher { } } + /** + * Returns true if Java Web Start button should be displayed. + * Java Web Start is only supported when the Jenkins server is + * running with Java 8. Earlier Java versions are not supported by Jenkins. + * Later Java versions do not support Java Web Start. + * + * This flag is checked in {@code config.jelly} before displaying the + * Java Web Start button. + * @return {@code true} if Java Web Start button should be displayed. + * @since FIXME + */ + @Restricted(NoExternalUse.class) // Jelly use + public boolean isJavaWebStartSupported() { + return System.getProperty("java.version", "1.8").startsWith("1.8"); + } } diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly b/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly index 606a52e932..29507de2f6 100644 --- a/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly @@ -37,14 +37,27 @@ THE SOFTWARE. ${%Connect agent to Jenkins one of these ways:}

    -
  • -

    - - ${%launch agent} - - ${%Launch agent from browser} -

    -
  • + + +
  • +

    + + ${%launch agent} + + ${%Launch agent from browser} +

    +
  • +
    + +
  • +

    + + ${%Java Web Start is not available for the JVM version running Jenkins} + +

    +
  • +
    +
  • -- GitLab From 68af8c83c10ac2a4ca42cebfe053f06931415bb6 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Sun, 25 Nov 2018 14:59:59 +0100 Subject: [PATCH 074/424] [JENKINS-45318] - Update the SSHD module from 2.4 to 2.5 --- war/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/war/pom.xml b/war/pom.xml index 7cd7dad40e..6ed9553904 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -130,7 +130,7 @@ THE SOFTWARE. org.jenkins-ci.modules sshd - 2.4 + 2.5 org.jenkins-ci.ui -- GitLab From fbeccd60ded529d8a24175e560d2fe922f5e289e Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sun, 25 Nov 2018 14:13:59 -0800 Subject: [PATCH 075/424] [maven-release-plugin] prepare release jenkins-2.153 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test-jdk8/pom.xml | 2 +- test-pom/pom.xml | 2 +- test/pom.xml | 2 +- war/pom.xml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 0a5aa6a5c3..ddf40b9a34 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.153 cli diff --git a/core/pom.xml b/core/pom.xml index cbbf3f3061..6c0d31b202 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.153 jenkins-core diff --git a/pom.xml b/pom.xml index 37be34c92f..c8f01865d5 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.153 pom Jenkins main module @@ -60,7 +60,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - ${scmTag} + jenkins-2.153 diff --git a/test-jdk8/pom.xml b/test-jdk8/pom.xml index 70c4870837..d648c51607 100644 --- a/test-jdk8/pom.xml +++ b/test-jdk8/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-test-parent - ${revision}${changelist} + 2.153 ../test-pom diff --git a/test-pom/pom.xml b/test-pom/pom.xml index 100e83563b..3e29095e77 100644 --- a/test-pom/pom.xml +++ b/test-pom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.153 jenkins-test-parent diff --git a/test/pom.xml b/test/pom.xml index 4c5abecc6f..440c8f6cbf 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-test-parent - ${revision}${changelist} + 2.153 ../test-pom diff --git a/war/pom.xml b/war/pom.xml index 7cd7dad40e..4d0b9fed1d 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.153 jenkins-war -- GitLab From d2a0cef28e62dd8acc68bf6a3fc201741dc16217 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sun, 25 Nov 2018 14:14:26 -0800 Subject: [PATCH 076/424] [maven-release-plugin] prepare for next development iteration --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 6 +++--- test-jdk8/pom.xml | 2 +- test-pom/pom.xml | 2 +- test/pom.xml | 2 +- war/pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index ddf40b9a34..0a5aa6a5c3 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.153 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 6c0d31b202..cbbf3f3061 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.153 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index c8f01865d5..2f27b8821f 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.153 + ${revision}${changelist} pom Jenkins main module @@ -60,7 +60,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.153 + ${scmTag} @@ -76,7 +76,7 @@ THE SOFTWARE. - 2.153 + 2.154 -SNAPSHOT - 3.27 + 3.28 3.4 -- GitLab From fd19c9a6934f48f54269cac31cced64b19b2f319 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Tue, 11 Dec 2018 00:49:20 +0100 Subject: [PATCH 117/424] Update triggerRemotely documentation --- war/src/main/webapp/help/project-config/triggerRemotely.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/war/src/main/webapp/help/project-config/triggerRemotely.html b/war/src/main/webapp/help/project-config/triggerRemotely.html index c015f0ec46..08da667af0 100644 --- a/war/src/main/webapp/help/project-config/triggerRemotely.html +++ b/war/src/main/webapp/help/project-config/triggerRemotely.html @@ -9,4 +9,8 @@

    You'll need to provide an authorization token in the form of a string so that only those who know it would be able to remotely trigger this project's builds.

    +

    This is most useful when your Jenkins instance grants read access to this job to anonymous users.

    +

    When that's not the case, Jenkins will reject requests sent to the trigger URL even when the correct token is specified.

    +

    To solve this, the HTTP requests needs to be authenticated as a user with the necessary read permission for the job -- but then you could probably just grant this user the permission to build this anyway.

    +

    Another option is to use the Build Token Root Plugin, that provides additional URL endpoints to trigger builds using this token, and doesn't require the otherwise necessary Overall/Read and Job/Read permissions to do so.

    -- GitLab From 0ee685eeecc88b5eef595ddc352261e07473dfe6 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Tue, 11 Dec 2018 09:57:12 +0100 Subject: [PATCH 118/424] Un-ignore test that was fixed Fixed in https://github.com/jenkinsci/jenkins/pull/3795 --- core/src/test/java/hudson/FilePathSEC904Test.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/test/java/hudson/FilePathSEC904Test.java b/core/src/test/java/hudson/FilePathSEC904Test.java index 3293046823..0fd94d0179 100644 --- a/core/src/test/java/hudson/FilePathSEC904Test.java +++ b/core/src/test/java/hudson/FilePathSEC904Test.java @@ -36,7 +36,6 @@ import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; -import org.junit.Ignore; //TODO to be merged back in FilePathTest after security release public class FilePathSEC904Test { @@ -44,7 +43,6 @@ public class FilePathSEC904Test { @Rule public TemporaryFolder temp = new TemporaryFolder(); - @Ignore("./../workspace fails on CI") @Test @Issue("SECURITY-904") public void isDescendant_regularFiles() throws IOException, InterruptedException { -- GitLab From 99835d6247b50b3f966efa8813af877f39c046d8 Mon Sep 17 00:00:00 2001 From: Wadeck Follonier Date: Tue, 11 Dec 2018 10:48:08 +0100 Subject: [PATCH 119/424] Revert the whitespace addition as well --- core/src/test/java/hudson/FilePathSEC904Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/hudson/FilePathSEC904Test.java b/core/src/test/java/hudson/FilePathSEC904Test.java index 0fd94d0179..a9b4d7afcc 100644 --- a/core/src/test/java/hudson/FilePathSEC904Test.java +++ b/core/src/test/java/hudson/FilePathSEC904Test.java @@ -42,7 +42,7 @@ public class FilePathSEC904Test { @Rule public TemporaryFolder temp = new TemporaryFolder(); - + @Test @Issue("SECURITY-904") public void isDescendant_regularFiles() throws IOException, InterruptedException { -- GitLab From 25823d91ce380efbec6b8022ca049d3d5bf78d7a Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 11 Dec 2018 09:30:32 -0500 Subject: [PATCH 120/424] Clarifying reason for suppression. --- test/src/test/java/hudson/PluginSEC925Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/test/java/hudson/PluginSEC925Test.java b/test/src/test/java/hudson/PluginSEC925Test.java index be9d483297..ab3fb94243 100644 --- a/test/src/test/java/hudson/PluginSEC925Test.java +++ b/test/src/test/java/hudson/PluginSEC925Test.java @@ -19,7 +19,7 @@ public class PluginSEC925Test { @Rule public JenkinsRule r = new JenkinsRule(); - @Ignore("TODO observed to fail in CI with 404") + @Ignore("TODO observed to fail in CI with 404 due to external UC issues") @Test @Issue("SECURITY-925") public void preventTimestamp2_toBeServed() throws Exception { -- GitLab From 67c19e899030442cad8baa6956287c6c8ce26017 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 11 Dec 2018 15:17:14 -0500 Subject: [PATCH 121/424] Rewriting old data monitor text --- .../OldDataMonitor/manage.properties | 31 +++++++------ .../OldDataMonitor/manage_ar.properties | 4 -- .../OldDataMonitor/manage_bg.properties | 46 ------------------- .../OldDataMonitor/manage_da.properties | 22 --------- .../OldDataMonitor/manage_de.properties | 26 ----------- .../OldDataMonitor/manage_es.properties | 20 -------- .../OldDataMonitor/manage_fi.properties | 3 -- .../OldDataMonitor/manage_fr.properties | 4 -- .../OldDataMonitor/manage_it.properties | 30 ------------ .../OldDataMonitor/manage_ja.properties | 21 --------- .../OldDataMonitor/manage_nl.properties | 1 - .../OldDataMonitor/manage_pt.properties | 18 -------- .../OldDataMonitor/manage_pt_BR.properties | 29 ------------ .../OldDataMonitor/manage_sr.properties | 5 -- .../OldDataMonitor/manage_zh_TW.properties | 21 --------- 15 files changed, 16 insertions(+), 265 deletions(-) diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.properties index 8cd60fb9e4..7198c1165b 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.properties @@ -22,30 +22,31 @@ blurb.1=\ When there are changes in how data is stored on disk, Jenkins uses the following strategy: \ - data is migrated to the new structure when it is loaded, but the file is not resaved in the \ - new format. This allows for downgrading Jenkins if needed. However, it can also leave data \ - on disk in the old format indefinitely. The table below lists files containing such data, \ - and the Jenkins version(s) where the data structure was changed. + data is migrated to the new structure when it is loaded, but items/records are not resaved in \ + the new format. This allows for downgrading Jenkins if necessary. However, it can also leave \ + data on disk in the old format indefinitely. The table below lists items/records containing \ + such data, and the Jenkins version(s) where the data structure was changed. blurb.2=\ Sometimes errors occur while reading data (if a plugin adds some data and that plugin is \ later disabled, if migration code is not written for structure changes, or if Jenkins is \ downgraded after it has already written data not readable by the older version). \ These errors are logged, but the unreadable data is then skipped over, allowing Jenkins to \ - startup and function properly. + start up and function properly. blurb.3=\ - The form below may be used to resave these files in the current format. Doing so means a \ - downgrade to a Jenkins release older than the selected version will not be able to read the \ - data stored in the new format. Note that simply using Jenkins to create and configure jobs \ - and run builds can save data that may not be readable by older Jenkins releases, even when \ - this form is not used. Also if any unreadable data errors are reported in the right side \ - of the table above, note that this data will be lost when the file is resaved. + The form below may be used to update these items/records to the current format. Doing so means a \ + downgrade to a Jenkins release or plugin older than the selected version will not be able to \ + read the data stored in the new format. Note that simply using Jenkins to create and \ + configure jobs and run builds can save data that may not be readable by older Jenkins or \ + plugin versions, even when this form is not used. Also if any unreadable data errors are \ + reported in the right side of the table above, note that this data will be lost when the \ + item is resaved. blurb.4=\ Eventually the code supporting these data migrations may be removed. Compatibility will be \ - retained for at least 150 releases since the structure change. Versions older than this are \ - in bold above, and it is recommended to resave these files. + retained for at least 150 Jenkins releases after the structure change. Versions older than \ + this are listed above in bold, and it is recommended you resave these items/records. blurb.5=\ (downgrade as far back as the selected version may still be possible) blurb.6=\ - It is acceptable to leave unreadable data in these files, as Jenkins will safely ignore it. \ + It is ok to leave unreadable data in these items/records, as Jenkins will simply ignore it. \ To avoid the log messages at Jenkins startup you can permanently delete the unreadable data \ - by resaving these files using the button below. + by resaving these items/records using the button below the list. diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_ar.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_ar.properties index ef8848abae..2c63664427 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_ar.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_ar.properties @@ -8,8 +8,4 @@ No\ old\ data\ was\ found.=\u0644\u0645 \u064A\u062A\u0645 \u0625\u064A\u062C\u0 Type=\u0627\u0644\u0646\u0648\u0639 Unreadable\ Data=\u0628\u064A\u0627\u0646\u0627\u062A \u063A\u064A\u0631 \u0645\u0642\u0631\u0648\u0621\u0629 Version=\u0631\u0642\u0645 \u0627\u0644\u0625\u0635\u062F\u0627\u0631 -blurb.1=\u0639\u0646\u062F \u062D\u062F\u0648\u062B \u062A\u063A\u064A\u064A\u0631 \u0628\u0628\u0646\u064A\u0629 \u062A\u062E\u0632\u064A\u0646 \u0627\u0644\u0645\u0639\u0644\u0648\u0645\u0627\u062A \u0639\u0644\u0649 \u0627\u0644\u0642\u0631\u0635\u060C \u064A\u0642\u0648\u0645 \u062C\u0646\u0643\u0646\u0632 \u0628\u0627\u062A\u0628\u0627\u0639 \u0627\u0644\u0627\u0633\u062A\u0631\u0627\u062A\u064A\u062C\u064A\u0629 \u0627\u0644\u062A\u0627\u0644\u064A\u0629: \u0627\u0644\u0645\u0639\u0644\u0648\u0645\u0627\u062A \u062A\u0631\u062D\u0644 \u0625\u0644\u0649 \u0627\u0644\u0628\u0646\u064A\u0629 \u0627\u0644\u062C\u062F\u064A\u062F\u0629 \u0639\u0646\u062F \u062A\u062D\u0645\u064A\u0644\u0647\u0627\u060C \u0648\u0644\u0643\u0646 \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0644\u0646 \u064A\u0639\u0627\u062F \u062D\u0641\u0638\u0647\u0627 \u0639\u0644\u0649 \u0634\u0643\u0644 \u0627\u0644\u0628\u0646\u064A\u0629 \u0627\u0644\u062C\u062F\u064A\u062F\u0629. \u0647\u0630\u0627 \u064A\u0633\u0645\u062D \u0628\u0625\u0639\u0627\u062F\u0629 \u0644\u062C\u0646\u0643\u0646\u0632 \u0628\u0627\u0644\u0639\u0648\u062F\u0629 \u0644\u0644\u0633\u0627\u0628\u0642 \u0639\u0646\u062F \u0627\u0644\u062D\u0627\u062C\u0629. \u0648\u0644\u0643\u0646\u0647\u0627 \u062A\u0628\u0642\u064A \u0639\u0644\u0649 \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u0645\u062D\u062A\u0648\u064A\u0629 \u0639\u0644\u0649 \u0628\u064A\u0627\u0646\u0627\u062A \u0628\u0627\u0644\u0628\u0646\u064A\u0629 \u0627\u0644\u0642\u062F\u064A\u0645\u0629. \u0627\u0644\u062C\u062F\u0648\u0644 \u0628\u0627\u0644\u0623\u0633\u0641\u0644 \u064A\u0633\u0631\u062F \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u0645\u062D\u062A\u0648\u064A\u0629 \u0639\u0644\u0649 \u062A\u0644\u0643 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A\u060C \u0631\u0642\u0645 \u0625\u0635\u062F\u0627\u0631 \u062C\u0646\u0643\u0646\u0632 \u0627\u0644\u0630\u064A \u062D\u062F\u062B \u0641\u064A\u0647 \u0627\u0644\u062A\u063A\u064A\u064A\u0631. blurb.2=\u0623\u062D\u064A\u0627\u0646\u0627\u064B \u062A\u062D\u062F\u062B \u0623\u062E\u0637\u0627\u0621 \u0639\u0646\u062F \u0642\u0631\u0627\u0621\u0629 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A (\u0639\u0646\u062F\u0645\u0627 \u062A\u0636\u064A\u0641 \u0625\u0636\u0627\u0641\u0629 \u062C\u0646\u0643\u0646\u0632 \u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0644\u0627\u062D\u0642\u0627\u064B \u064A\u062A\u0645 \u062A\u0639\u0637\u064A\u0644 \u0627\u0644\u0625\u0636\u0627\u0641\u0629\u060C \u0625\u0630\u0627 \u0644\u0645 \u064A\u062A\u0645 \u0643\u062A\u0627\u0628\u0629 \u0646\u0635 \u0645\u0635\u062F\u0631\u064A \u0644\u062A\u0631\u062D\u064A\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0644\u0644\u0628\u0646\u064A\u0629 \u0627\u0644\u062C\u062F\u064A\u062F\u0629\u060C \u0623\u0648 \u0625\u0630\u0627 \u062A\u0645 \u0627\u0633\u062A\u0631\u062C\u0627\u0639 \u062C\u0646\u0643\u0646\u0632 \u0644\u0646\u0633\u062E\u0629 \u0633\u0627\u0628\u0642\u0629 \u0628\u0639\u062F \u0623\u0646 \u062A\u0645 \u062D\u0641\u0638 \u0628\u064A\u0627\u0646\u0627\u062A \u0644\u0646 \u062A\u0633\u062A\u0637\u064A\u0639 \u0627\u0644\u0646\u0633\u062E\u0629 \u0627\u0644\u0633\u0627\u0628\u0642\u0629 \u0642\u0631\u0627\u0621\u062A\u0647\u0627). \u0647\u0630\u0647 \u0627\u0644\u0623\u062E\u0637\u0627\u0621 \u064A\u062A\u0645 \u062A\u0633\u062C\u064A\u0644\u0647\u0627\u060C ,\u0648\u064A\u062A\u0645 \u062A\u062C\u0627\u0647\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u062A\u064A \u0644\u0627\u064A\u0645\u0643\u0646 \u0642\u0631\u0627\u0621\u062A\u0647\u0627\u060C \u0645\u0645\u0627 \u064A\u0633\u0645\u062D \u0644\u062C\u0646\u0643\u0646\u0632 \u0628\u0645\u062A\u0627\u0628\u0639\u0629 \u0627\u0644\u0639\u0645\u0644 \u0628\u0634\u0643\u0644 \u062C\u064A\u062F. -blurb.3=\u0627\u0644\u0646\u0645\u0648\u0630\u062C \u0628\u0627\u0644\u0623\u0633\u0641\u0644 \u064A\u0645\u0643\u0646 \u0627\u0633\u062A\u062E\u062F\u0627\u0645\u0647 \u0644\u0625\u0639\u0627\u062F\u0629 \u062D\u0641\u0638 \u0647\u0630\u0647 \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0628\u0627\u0644\u0628\u0646\u064A\u0629 \u0627\u0644\u062C\u062F\u064A\u062F\u0629. \u0628\u0647\u0630\u0647 \u0627\u0644\u0637\u0631\u064A\u0642\u0629 \u0644\u0646 \u064A\u0633\u062A\u0637\u064A\u0639 \u062C\u0646\u0643\u0646\u0632 \u0642\u0631\u0627\u0621\u0629 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0628\u0627\u0644\u0628\u0646\u064A\u0629 \u0627\u0644\u062C\u062F\u064A\u062F\u0629 \u0641\u064A \u062D\u0627\u0644 \u0627\u0633\u062A\u0631\u062C\u0627\u0639\u0647 \u0644\u0646\u0633\u062E\u0629 \u0633\u0627\u0628\u0642\u0629. \u0627\u0646\u062A\u0628\u0647 \u0623\u0646\u0647 \u0628\u0645\u062C\u0631\u062F \u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u062C\u0646\u0643\u0646\u0632 \u0644\u0644\u0628\u0646\u0627\u0621 \u0648\u0636\u0628\u0637 \u0627\u0644\u0628\u0646\u0627\u0621 \u064A\u0645\u0643\u0646 \u0623\u0646 \u064A\u0646\u062A\u062C \u0628\u062D\u0641\u0638 \u0628\u064A\u0627\u0646\u0627\u062A \u0644\u0627\u064A\u0645\u0643\u0646 \u0642\u0631\u0627\u0621\u062A\u0647\u0627 \u0641\u064A \u0627\u0644\u0646\u0633\u062E \u0627\u0644\u0633\u0627\u0628\u0642\u0629 \u0644\u062C\u0646\u0643\u0646\u0632. \u0648\u0641\u064A \u062D\u0627\u0644 \u062D\u062F\u0648\u062B \u0623\u064A \u062E\u0637\u0623 \u0648\u0637\u0628\u0639\u0647 \u0641\u064A \u0627\u0644\u062C\u062F\u0648\u0644 \u0628\u0627\u0644\u0623\u0639\u0644\u0649 \u0633\u064A\u062A\u0645 \u0645\u0633\u062D \u0647\u0630\u0647 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0639\u0646\u062F \u0625\u0639\u0627\u062F\u0629 \u062D\u0641\u0638 \u0627\u0644\u0645\u0644\u0641. -blurb.4=\u0628\u0634\u0643\u0644 \u062F\u0648\u0631\u064A \u064A\u062A\u0645 \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0646\u0635 \u0627\u0644\u0645\u0635\u062F\u0631\u064A \u0644\u062F\u0639\u0645 \u062A\u0631\u062D\u064A\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A. \u0627\u0644\u062A\u0648\u0627\u0641\u0642\u064A\u0629 \u062A\u0628\u0642\u0649 \u062D\u062A\u0649 150 \u0625\u0635\u062F\u0627\u0631 \u0639\u0644\u0649 \u0627\u0644\u0623\u0642\u0644 \u0645\u0646\u0630 \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u0646\u064A\u0629. \u0627\u0644\u0625\u0635\u062F\u0627\u0631\u0627\u062A \u0627\u0644\u0623\u0642\u062F\u0645 \u0645\u0645\u064A\u0632\u0629 \u0628\u0627\u0644\u062E\u0637 \u0627\u0644\u0639\u0631\u064A\u0636 \u0628\u0627\u0644\u0623\u0639\u0644\u0649\u060C \u0648\u0645\u0646 \u0627\u0644\u0645\u0633\u062A\u062D\u0633\u0646 \u062D\u0641\u0638 \u0647\u0630\u0647 \u0627\u0644\u0645\u0644\u0641\u0627\u062A. -blurb.6=\u0645\u0646 \u0627\u0644\u0645\u0642\u0628\u0648\u0644 \u062A\u0631\u0643 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u063A\u064A\u0631 \u0645\u0642\u0631\u0648\u0621\u0629 \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u0645\u0644\u0641\u0627\u062A\u060C \u062D\u064A\u062B \u064A\u062A\u062C\u0627\u0647\u0644\u0647\u0627 \u062C\u0646\u0643\u0646\u0632 \u0628\u0634\u0643\u0644 \u0622\u0645\u0646. \u0644\u062A\u062C\u0646\u0628 \u0631\u0633\u0627\u0626\u0644 \u0627\u0644\u0633\u062C\u0644 \u0639\u0646\u062F \u0628\u062F\u0621 \u062A\u0634\u063A\u064A\u0644 \u062C\u0646\u0643\u0646\u0632 \u0628\u0625\u0645\u0643\u0627\u0646\u0643 \u062D\u0630\u0641 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u063A\u064A\u0631 \u0645\u0642\u0631\u0648\u0621\u0629 \u0628\u0634\u0643\u0644 \u0646\u0647\u0627\u0626\u064A \u0639\u0646 \u0637\u0631\u064A\u0642 \u0625\u0639\u0627\u062F\u0629 \u062D\u0641\u0638 \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0627\u0644\u0632\u0631 \u0628\u0627\u0644\u0623\u0633\u0641\u0644. diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_bg.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_bg.properties index ab631fc123..d2d172a036 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_bg.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_bg.properties @@ -26,58 +26,12 @@ Name=\ # Eventually the code supporting these data migrations may be removed. Compatibility will be \ # retained for at least 150 releases since the structure change. Versions older than this are \ # in bold above, and it is recommended to resave these files. -blurb.4=\ - \u0421\u043b\u0435\u0434 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e \u0432\u0440\u0435\u043c\u0435 \u043a\u043e\u0434\u044a\u0442 \u0437\u0430 \u0442\u0435\u0437\u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 \u0434\u0430\u043d\u043d\u0438 \u0449\u0435 \u0431\u044a\u0434\u0435 \u0438\u0437\u0442\u0440\u0438\u0442.\ - \u0421\u044a\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0442\u0430 \u0449\u0435 \u0431\u044a\u0434\u0435 \u0437\u0430\u043f\u0430\u0437\u0435\u043d\u0430 \u0437\u0430 \u043f\u043e\u043d\u0435 150 \u0438\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043b\u0435\u0434 \u043f\u0440\u043e\u043c\u044f\u043d\u0430\u0442\u0430 \u043f\u043e\ - \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430\u0442\u0430. \u0412\u0435\u0440\u0441\u0438\u0438\u0442\u0435, \u043f\u043e \u0440\u0430\u043d\u043d\u0438 \u043e\u0442 \u0442\u043e\u0432\u0430, \u0441\u0430 \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u0440\u043d\u043e. \u0417\u0430 \u043f\u0440\u0435\u043f\u043e\u0440\u044a\u0447\u0432\u0430\u043d\u0435 \u0435 \u0434\u0430\ - \u0437\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0444\u0430\u0439\u043b\u043e\u0432\u0435\u0442\u0435 \u043d\u0430\u043d\u043e\u0432\u043e. Upgrade=\ \u041e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435 Manage\ Old\ Data=\ \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u0441\u0442\u0430\u0440\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u0438 Discard\ Unreadable\ Data=\ \u041e\u0442\u0445\u0432\u044a\u0440\u043b\u044f\u043d\u0435 \u043d\u0430 \u0434\u0430\u043d\u043d\u0438\u0442\u0435, \u043a\u043e\u0438\u0442\u043e \u043d\u0435 \u043c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0441\u0435 \u043f\u0440\u043e\u0447\u0435\u0442\u0430\u0442 -# \ -# When there are changes in how data is stored on disk, Jenkins uses the following strategy: \ -# data is migrated to the new structure when it is loaded, but the file is not resaved in the \ -# new format. This allows for downgrading Jenkins if needed. However, it can also leave data \ -# on disk in the old format indefinitely. The table below lists files containing such data, \ -# and the Jenkins version(s) where the data structure was changed. -blurb.1=\ - \u041f\u0440\u0438 \u043f\u0440\u043e\u043c\u044f\u043d\u0430 \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430\u0442\u0430 \u043d\u0430 \u0444\u0430\u0439\u043b\u043e\u0432\u0435\u0442\u0435 Jenkins \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u0441\u043b\u0435\u0434\u043d\u0430\u0442\u0430 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f:\ - \u0434\u0430\u043d\u043d\u0438\u0442\u0435 \u0441\u0435 \u043c\u0438\u0433\u0440\u0438\u0440\u0430\u0442 \u043a\u044a\u043c \u043d\u043e\u0432\u0430\u0442\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u0438 \u0437\u0430\u0440\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0444\u0430\u0439\u043b\u0430, \u043d\u043e \u0442\u043e\u0439\ - \u043e\u0441\u0442\u0430\u0432\u0430 \u0441\u044a\u0441 \u0441\u0442\u0430\u0440\u0430\u0442\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430. \u0422\u043e\u0432\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0432\u0430 \u0432\u0440\u044a\u0449\u0430\u043d\u0435 \u043a\u044a\u043c \u043f\u0440\u0435\u0434\u0438\u0448\u043d\u0430\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0430\ - Jenkins, \u0430\u043a\u043e \u0441\u0435 \u043d\u0430\u043b\u0430\u0433\u0430. \u0422\u043e\u0432\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0432\u0430 \u0434\u0430\u043d\u043d\u0438\u0442\u0435 \u0434\u0430 \u043e\u0441\u0442\u0430\u043d\u0430\u0442 \u0432 \u0441\u0442\u0430\u0440\u0438\u044f \u0444\u043e\u0440\u043c\u0430\u0442 \u0437\u0430\ - \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u043d\u043e \u0434\u044a\u043b\u0433\u043e \u0432\u0440\u0435\u043c\u0435. \u0422\u0430\u0431\u043b\u0438\u0446\u0430\u0442\u0430 \u043f\u043e-\u0434\u043e\u043b\u0443 \u0441\u044a\u0434\u044a\u0440\u0436\u0430 \u0444\u0430\u0439\u043b\u043e\u0432\u0435\u0442\u0435 \u0441 \u0442\u0430\u043a\u0438\u0432\u0430 \u0434\u0430\u043d\u043d\u0438,\ - \u043a\u0430\u043a\u0442\u043e \u0438 \u0432\u0435\u0440\u0441\u0438\u044f\u0442\u0430 \u043d\u0430 Jenkins, \u043f\u0440\u0438 \u043a\u043e\u044f\u0442\u043e \u0435 \u0441\u043c\u0435\u043d\u0435\u043d \u0444\u043e\u0440\u043c\u0430\u0442\u044a\u0442 \u043d\u0430 \u0434\u0430\u043d\u043d\u0438\u0442\u0435. -# \ -# (downgrade as far back as the selected version may still be possible) -blurb.5=\ - (\u0432\u0440\u044a\u0449\u0430\u043d\u0435 \u043a\u044a\u043c \u043d\u0430\u0439-\u0441\u0442\u0430\u0440\u0430\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u0435\u043d\u0430 \u043e\u0442 \u0438\u0437\u0431\u0440\u0430\u043d\u0430\u0442\u0430) -# \ -# The form below may be used to resave these files in the current format. Doing so means a \ -# downgrade to a Jenkins release older than the selected version will not be able to read the \ -# data stored in the new format. Note that simply using Jenkins to create and configure jobs \ -# and run builds can save data that may not be readable by older Jenkins releases, even when \ -# this form is not used. Also if any unreadable data errors are reported in the right side \ -# of the table above, note that this data will be lost when the file is resaved. -blurb.3=\ - \u0424\u043e\u0440\u043c\u0443\u043b\u044f\u0440\u044a\u0442 \u043f\u043e-\u0434\u043e\u043b\u0443 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0432\u0430 \u0434\u0430 \u043f\u0440\u0435\u0437\u0430\u043f\u0438\u0448\u0435\u0442\u0435 \u0442\u0435\u0437\u0438 \u0444\u0430\u0439\u043b\u043e\u0432\u0435 \u0432 \u043d\u043e\u0432\u0438\u044f \u0444\u043e\u0440\u043c\u0430\u0442.\ - \u0410\u043a\u043e \u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442\u0435 \u0442\u043e\u0432\u0430 \u0438 \u0432\u044a\u0440\u043d\u0435\u0442\u0435 Jenkins \u043a\u044a\u043c \u0432\u0435\u0440\u0441\u0438\u044f \u043f\u0440\u0435\u0434\u0438 \u0438\u0437\u0431\u0440\u0430\u043d\u0430\u0442\u0430, \u0434\u0430\u043d\u043d\u0438\u0442\u0435 \u043d\u044f\u043c\u0430\ - \u0434\u0430 \u0441\u0435 \u043f\u0440\u043e\u0447\u0435\u0442\u0430\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u043d\u043e. \u0414\u043e\u0440\u0438 \u0431\u0435\u0437 \u0434\u0430 \u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u0444\u043e\u0440\u043c\u0443\u043b\u044f\u0440 \u0435 \u0432\u044a\u0437\u043c\u043e\u0436\u043d\u043e \u043d\u0435\u0449\u043e\ - \u043f\u043e\u0434\u043e\u0431\u043d\u043e \u0434\u0430 \u0441\u0435 \u0441\u043b\u0443\u0447\u0438 \u2014 \u0430\u043a\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u044a\u0437\u0434\u0430\u0434\u0435\u0442\u0435 \u043d\u043e\u0432\u0438 \u0437\u0430\u0434\u0430\u043d\u0438\u044f, \u043f\u0440\u043e\u043c\u0435\u043d\u0438\u0442\u0435 \u0438\u043b\u0438\ - \u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0442\u0435 \u0432\u0435\u0447\u0435 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0438. \u0410\u043a\u043e \u0432 \u0434\u044f\u0441\u043d\u0430\u0442\u0430 \u0441\u0442\u0440\u0430\u043d\u0430 \u043d\u0430 \u0433\u043e\u0440\u043d\u0430\u0442\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u0430\ - \u0434\u043e\u043a\u043b\u0430\u0434\u0432\u0430\u043d\u0438 \u0433\u0440\u0435\u0448\u043a\u0438 \u043f\u0440\u0438 \u0447\u0435\u0442\u0435\u043d\u0435 \u043d\u0430 \u0434\u0430\u043d\u043d\u0438, \u0434\u0430\u043d\u043d\u0438\u0442\u0435 \u0449\u0435 \u0431\u044a\u0434\u0430\u0442 \u0438\u0437\u0433\u0443\u0431\u0435\u043d\u0438, \u0430\u043a\u043e\ - \u043f\u0440\u0435\u0437\u0430\u043f\u0438\u0448\u0435\u0442\u0435 \u0444\u0430\u0439\u043b\u0430. -# \ -# It is acceptable to leave unreadable data in these files, as Jenkins will safely ignore it. \ -# To avoid the log messages at Jenkins startup you can permanently delete the unreadable data \ -# by resaving these files using the button below. -blurb.6=\ - \u041d\u0435 \u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0434\u0430 \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u0438, \u043a\u043e\u0438\u0442\u043e \u043d\u0435 \u043c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0441\u0435 \u043f\u0440\u043e\u0447\u0435\u0442\u0430\u0442 \u0432 \u0442\u0435\u0437\u0438 \u0444\u0430\u0439\u043b\u043e\u0432\u0435,\ - \u0437\u0430\u0449\u043e\u0442\u043e Jenkins \u0449\u0435 \u0433\u0438 \u043f\u0440\u0435\u0441\u043a\u043e\u0447\u0438. \u0410\u043a\u043e \u043d\u0435 \u0438\u0441\u043a\u0430\u0442\u0435 \u043f\u043e\u0432\u0435\u0447\u0435 \u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u0442\u0435 \u0442\u0435\u0437\u0438\ - \u0441\u044a\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u0438\u0437\u0442\u0440\u0438\u0435\u0442\u0435 \u043d\u0435\u0447\u0435\u0442\u0438\u043c\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u0438 \u043a\u0430\u0442\u043e \u043f\u0440\u0435\u0437\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0444\u0430\u0439\u043b\u043e\u0432\u0435\u0442\u0435 \u0441\ - \u0431\u0443\u0442\u043e\u043d\u0430 \u043e\u0442\u0434\u043e\u043b\u0443. Unreadable\ Data=\ \u0414\u0430\u043d\u043d\u0438, \u043a\u043e\u0438\u0442\u043e \u043d\u0435 \u043c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0441\u0435 \u043f\u0440\u043e\u0447\u0435\u0442\u0430\u0442 No\ old\ data\ was\ found.=\ diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_da.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_da.properties index 84edc8be83..cc8e7307af 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_da.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_da.properties @@ -29,32 +29,10 @@ og k\u00f8re videre (omend uden adgang til ul\u00e6selig data)). Error=Fejl Type=Type Unreadable\ Data=Ul\u00e6selig data -blurb.1=\ -N\u00e5r der er \u00e6ndringer i hvordan data bliver gemt p\u00e5 disk benytter Jenkins f\u00f8lgende strategi: \ -data bliver migreret til den nye struktur under indl\u00e6sning, men filen bliver ikke gemt/overskrevet \ -i det nye format. Dette tillader nedgradering af Jenkins om n\u00f8dvendigt. Men denne strategi kan ogs\u00e5 \ -efterlade data p\u00e5 disken i det gamle format p\u00e5 ubestemt tid. Tabellen herunder lister de filer der \ -indeholder s\u00e5dan data samt Jenkins versionen hvor data-strukturen blev \u00e6ndret. Resave\ data\ files\ with\ structure\ changes\ no\ newer\ than\ Jenkins=Gem datafilerne med strukturelle \u00e6ndringer ikke nyere end Jenkins No\ old\ data\ was\ found.=Ingen gamle data blev fundet -blurb.6=\ -Det er acceptabelt at gemme ul\u00e6selig data i disse filer, da Jenkins blot ignorerer dem. \ -For at undg\u00e5 logbeskeder under opstart af Jenkins kan du permanent slette ul\u00e6selig data \ -ved at gemme disse filer med nedenst\u00e5ende knap. Discard\ Unreadable\ Data=Smid ul\u00e6selig data v\u00e6k. -blurb.4=\ -P\u00e5 et tidspunkt vil koden der underst\u00f8tter disse gamle datastrukturer blive slettet, som led i kodeoprydning. \ -Kompatibilitet vil blive bibeholdt i mindst 150 versioner. Versioner \u00e6ldre end dette er vist i fed herover, \ -og det anbefales p\u00e5 det kraftigste at gemme disse filer i det nyeste format. Version=Version Upgrade=Opdater -blurb.5=(Nedgradering s\u00e5 langt tilbage som den valgte version er (m\u00e5ske) stadig muligt) -blurb.3=\ -Form''en herunder kan benyttes til at gemme disse filer i det korrekte format. At g\u00f8re dette \ -indeb\u00e6rer at hvis du efterf\u00f8lgende nedgraderer Jenkins til en version \u00e6ldre end den valgte version \ -vil denne \u00e6ldre version af Jenkins ikke l\u00e6ngere kunne l\u00e6se data''ene i det nyere format. \ -Bem\u00e6rk dog at daglig brug af Jenkins, s\u00e5som oprettelse og konfiguration af jobs snildt kan gemme \ -data i formater der ikke vil v\u00e6re l\u00e6selige af \u00e6ldre versioner af Jenkins. Bem\u00e6rk ogs\u00e5 \ -at ul\u00e6selig data vist i h\u00f8jre side af tabellen herover vil g\u00e5 tabt n\u00e5r du gemmer filen i det nye format. Name=Navn Manage\ Old\ Data=H\u00e5ndter Gamle Data diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_de.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_de.properties index 80bca0260a..75c3c6c776 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_de.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_de.properties @@ -31,14 +31,6 @@ Unreadable\ Data=Nicht lesbare Daten Discard\ Unreadable\ Data=Nicht lesbare Daten entfernen Upgrade=Aktualisieren Version=Version -blurb.1=\ - ndert sich die Struktur von Konfigurationsdateien, geht Jenkins folgendermaen vor: \ - Dateien werden beim Einlesen in den Speicher in das neue Datenformat migriert, aber \ - nicht automatisch auf Festplatte zurckgeschrieben. Die Konfigurationsdateien bleiben also \ - unverndert. Dies ermglicht bei Problemen ein Jenkins-Downgrade zu einer frheren \ - Version. Auf der anderen Seite knnen dadurch Dateien endlos in lngst veralteten \ - Formaten verbleiben. Die folgende Tabelle zeigt Dateien, die veraltete Strukturen verwenden, \ - sowie die Jenkins-Version(en), in denen die Datenstruktur verndert wurde. blurb.2=\ Beim Einlesen von Konfigurationsdateien knnen Fehler auftreten, z.B. wenn ein Plugin \ Daten hinzufgt und spter deaktiviert wird, wenn kein Migrationscode fr Strukturnderungen \ @@ -46,21 +38,3 @@ blurb.2=\ Version bereits Dateien mit einer neuen Struktur geschrieben hatte. Diese Fehler werden beim \ Hochfahren von Jenkins zwar protokolliert, die nicht-lesbaren Daten werden aber einfach \ bersprungen, damit Jenkins trotzdem starten und arbeiten kann. -blurb.3=\ - Mit der untenstehenden Funktion knnen Sie diese Datein im aktuellen Format neu abspeichern. \ - Damit entfllt die Mglichzeit, auf eine ltere als die ausgewhlte Jenkins-Version zurckzukehren. \ - Auch wenn Sie Konfigurationen bestehender Elemente ndern, werden diese Daten im neuen \ - Format gespeichert, was ein spteres Downgrade ausschliet. Nicht-lesbare Daten, die in der \ - Tabelle rechts dargestellt sind, werden bei der Aktualisierung dauerhaft entfernt. -blurb.4=\ - Langfristig wird Migrationscode zum Lesen veralteter Datenformate auch wieder entfernt werden. \ - Die Kompatibilitt wird mindestens 150 Releases nach nderung des Datenformates gewhrleistet. \ - Versionen, die noch lter sind, werden fett dargestellt. Es wird emfohlen, diese Dateien neu \ - abzuspeichern. -blurb.5=\ - (ein Downgrade bis zur ausgewhlten Version knnte immer noch mglich sein) -blurb.6=\ - Nicht-lesbare Daten stellen kein Problem dar, da Jenkins sie einfach ignoriert. \ - Um jedoch lange Protokolle mit zahlreichen Warnungen whrend des Hochfahrens von Jenkins zu \ - vermeiden, knnen Sie nicht-lesbare Daten dauerhaft entfernen, indem Sie diese ber die \ - untenstehende Funktion neu abspeichern lassen. \ No newline at end of file diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_es.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_es.properties index 61d2c09641..27a05769db 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_es.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_es.properties @@ -20,32 +20,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -blurb.1=\ - Cuando se producen cambios en la forma de almacenar datos en disco, Jenkins utiliza la siguiente estrategia: \ - los datos son migrados a la nueva estructura cuando son cargados, pero el fichero de datos no se salva con \ - el nuevo formato. Esto facilita el poder degradar Jenkins a la versin antigua. La tabla de abajo muestra los \ - ficheros que contienen estos datos y la versin de Jenkins a partir de la cual la estructura ha cambiado. blurb.2=\ Algunas veces se producen errores mientras se leen datos, por ejemplo cuando un plugin aade \ datos y despus se desactiva, o cuando no hay cdigo que actualice los datos cuando cambia la \ estructura, o cuando Jenkins es degradado a una versin anterior. \ Jenkins avisa de todos estos errores y no carga los datos, lo que le permite arrancar y \ funcionar adecuadamente. -blurb.3=\ - El formulario de abajo se puede usar para decirle a Jenkins que salve los datos en el formato \ - actual, lo que implica que Jenkins no se podr degradar a una versin anterior. Si se listaran \ - errores de datos ilegibles en la tabla, esos datos no seran guardados cuando el fichero se guarde \ - con el nuevo formato. -blurb.4=\ - El cdigo que soporta la migracin de datos podr ser borrado eventualmente. La compatibilidad \ - ser mantenida durante al menos 150 versiones desde que la estructura cambie. Las versiones \ - anteriores estan en negrilla, y se recomienda salvar nuevamente estos ficheros. -blurb.5=\ - (ser posible degradar Jenkins hasta la version seleccionada). -blurb.6=\ - Es posible dejar datos ilegibles en estos ficheros porque Jenkins los ignorar. Para evitar \ - mensajes de error cuando Jenkins arranque, puedes borrar estos datos permanentemente pulsando \ - el botn de abajo. Manage\ Old\ Data=Gestin de datos antiguos Type=Tipo Name=Nombre diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_fi.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_fi.properties index 2d1a819b3f..5c17587bf1 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_fi.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_fi.properties @@ -25,7 +25,4 @@ Name=Nimi No\ old\ data\ was\ found.=Vanhassa muodossa talletettua tietoa ei l\u00F6ytynyt. Type=Tyyppi Version=Versio -blurb.1=Kun levylle kirjoitettava tieto muuttuu, Jenkins tekee sen seuraavasti: tiedot siirret\u00E4\u00E4n uuteen muotoon luettaessa levylt\u00E4, mutta tietoja ei tallenneta uudessa muodossa. T\u00E4m\u00E4 tekee mahdolliseksi palata vanhaan Jenkinsin versioon jos tarve tulee. T\u00E4m\u00E4n takia tiedot my\u00F6s j\u00E4\u00E4v\u00E4t levylle vanhaan muotoon kunnes ne k\u00E4yd\u00E4\u00E4n erikseen muuttamassa. Allaoleva taulukko luettelee tiedostot, joissa on vanhaa tietoa ja miss\u00E4 Jenkinsin versiossa tietorakenne muuttui. blurb.2=Joskus tietoja luettaessa tapahtuu virheit\u00E4 (esim. jos joku laajennus on lis\u00E4nyt tietoja ja t\u00E4m\u00E4 laajennus on my\u00F6hemmin k\u00E4\u00E4nnetty pois p\u00E4\u00E4lt\u00E4 tai Jenkins on vaihdettu vanhempaan versioon sen j\u00E4lkeen kun se on kirjoittanut tietoja levylle muotoon, jota vanhempi versio ei osaa viel\u00E4 lukea. Jotta Jenkins silti k\u00E4ynnistyisi ja toimisi kunnolla, n\u00E4m\u00E4 virheet kirjoitetaan lokiiin ja sitten hyp\u00E4t\u00E4\u00E4n niiden tietojen yli, joita ei pystyt\u00E4 lukemaan. -blurb.3=Allaolevalla lomakkeella voidaan tallentaa n\u00E4m\u00E4 tiedostot nykyiseen muotoon. N\u00E4it\u00E4 tietoja ei pystyt\u00E4 lukemaan jos Jenkins vaihdetaan vanhempaan versioon. Huomaa, ett\u00E4 jo pelk\u00E4st\u00E4\u00E4n uusien t\u00F6iden luominen, olemassaolevien t\u00F6iden muokkaus tai uusien k\u00E4\u00E4nn\u00F6sten ajaminen saattaa tallettaa tietoja muodossa, jota vanhemmat Jenkinsin versiot eiv\u00E4t pysty lukemaan (vaikkei t\u00E4t\u00E4 lomaketta olisikaan k\u00E4ytetty.) Huomaa my\u00F6s, ett\u00E4 jos taulun oikeassa reunassa on kerrottu lukuvirheist\u00E4, kyseiset tiedot katoavat kun tiedosto tallennetaan. -blurb.4=Jossain vaiheessa tietoja uuteen muotoon siirt\u00E4v\u00E4 koodi poistetaan. Yhteensopivuutta yll\u00E4pidet\u00E4\u00E4n ainakin 150 versiota tietorakenteen muuttamisen j\u00E4lkeen. T\u00E4t\u00E4 vanhemmat versiot on yll\u00E4 esitetty lihavoituna ja n\u00E4iden tiedostojen tallentaminen on suositeltavaa. diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_fr.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_fr.properties index a29bc218a9..8a48f02141 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_fr.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_fr.properties @@ -29,8 +29,4 @@ Type=Type Unreadable\ Data=Donn\u00E9es illisibles Upgrade=Mettre \u00E0 jour Version=Version -blurb.1=Quand il y a des changements dans la fa\u00E7on dont les donn\u00E9es sont stock\u00E9es sur le disque, Jenkins utilise la strat\u00E9gie suivante : les donn\u00E9es sont traduites dans le nouveau format lorsqu''elles sont charg\u00E9es, mais le fichier n''est pas sauvegard\u00E9 dans le nouveau format. Cela permet de r\u00E9trograder la version de Jenkins si besoin. Cependant, les donn\u00E9es peuvent \u00EAtre conserv\u00E9es dans l''ancien format ind\u00E9finiment. Le tableau ci-dessous affiche les fichiers concern\u00E9s, et la version de Jenkins \u00E0 partir de laquelle le format a chang\u00E9. blurb.2=Quelques fois, des erreurs surviennent \u00E0 lecture des donn\u00E9es (si un plugin ajoute des donn\u00E9es et qu''il est ensuite d\u00E9sactiv\u00E9, si le code de traduction des donn\u00E9es n''est pas \u00E9crit, ou si Jenkins est r\u00E9trograd\u00E9 apr\u00E8s qu''il ait \u00E9crit des donn\u00E9es non lisibles par l''ancienne version). Ces erreurs sont trac\u00E9es, mais les donn\u00E9es illisibles ne sont pas prises en compte, permettant le d\u00E9marrage et le fonctionnement normal de Jenkins. -blurb.3=Le formulaire suivant peut \u00EAtre utilis\u00E9 pour traduire ces fichiers dans le format courant. Cela implique qu''une r\u00E9trogradation de Jenkins \u00E0 une version ant\u00E9rieure ne sera pas capable de lire les donn\u00E9es dans ce nouveau format. Notez que le simple fait d''utiliser Jenkins pour de nouveaux jobs et constructions g\u00E9n\u00E9rera des fichiers qui ne seront pas lisibles par d''anciennes versions de format, m\u00EAme si ce formulaire n''est pas utilis\u00E9. De plus, si des erreurs de lecture sont affich\u00E9es dans la partie droite du tableau, notez que ces donn\u00E9es seront perdues sir le fichier est traduit. -blurb.4=Le code supportant ces traductions de donn\u00E9es peut \u00EAtre \u00E9ventuellement supprim\u00E9. La compatibilit\u00E9 sera maintenue pour au moins 150 versions apr\u00E8s le changement de format. Les version plus anciennes sont indiqu\u00E9es en gras ci-dessus, et il est recommand\u00E9 de traduire ces fichiers. -blurb.6=Il est possible de laisser les donn\u00E9es illisibles dans ces fichiers, car Jenkins les ignorera. Pour \u00E9viter les messages de traces au d\u00E9marrage de Jenkins, vous pouvez supprimer de fa\u00E7on d\u00E9ifnitivement les donn\u00E9es illisibles en traduisant les fichiers en utilisant le bouton ci-dessous. diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_it.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_it.properties index a92c6c8caf..f472c5839e 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_it.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_it.properties @@ -1,11 +1,3 @@ -blurb.1=\ - Quando vi sono dei cambiamenti nel modo in cui i dati sono salvati su disco, Jenkins \ - utilizza la seguente strategia: i dati sono migrati alla loro nuova struttura quando \ - vengono caricati, ma il file non salvato nuovamente nel nuovo formato. Ci consente \ - di effettuare il downgrade di Jenkins se necessario. Tuttavia, ci pu anche lasciare \ - dei dati su disco nel vecchio formato indefinitamente. La tabella sottostante elenca \ - i file che contengono tali dati e le versioni di Jenkins in cui stata modificata la \ - struttura dati. blurb.2=\ A volte si verificano errori durante la lettura dei dati (se un plugin aggiunge dei dati \ e tale plugin viene disabilitato in un secondo momento, se non stato scritto codice per \ @@ -13,28 +5,6 @@ blurb.2=\ downgrade di Jenkins dopo che questo ha gi scritto dati non leggibili dalla vecchia \ versione). Questi errori sono registrati, ma i dati non leggibili vengono saltati, \ consentendo a Jenkins di avviarsi e funzionare correttamente. -blurb.3=\ - Il modulo sottostante pu essere utilizzato per salvare nuovamente questi file nel \ - formato corrente. Eseguire tale operazione comporter che un downgrade a una versione di \ - Jenkins pi vecchie della versione selezionata non sar in grado di leggere i dati \ - memorizzati nel nuovo formato. Si noti che il semplice utilizzo di Jenkins per creare \ - e configurare processi ed eseguire build pu salvare dati che potrebbero non essere \ - leggibili da versioni di Jenkins pi vecchie, anche nel caso in cui non si utilizzi \ - questo modulo. Inoltre, se vi sono degli errori relativi a dati non leggibili nella \ - parte destra della tabella soprastante, si noti che tali dati andranno perduti quando il \ - file sar salvato nuovamente. -blurb.4=\ - Prima o poi il codice per il supporto di queste migrazioni dati potrebbe essere rimosso. \ - La compatibilit sar mantenuta per almeno 150 versioni a partire dalla modifica della \ - struttura. Le versioni pi vecchie di queste sono evidenziate sopra in grassetto, e si \ - raccomanda di salvare nuovamente questi file. -blurb.5=\ - (potrebbe essere ancora possibile eseguire il downgrade fino alla versione selezionata) -blurb.6=\ - accettabile lasciare dati non leggibili in tali file, in quanto Jenkins li ignorer \ - in modo sicuro. Per evitare i messaggi di log all''avvio di Jenkins si possono eliminare \ - definitivamente i dati non leggibili salvando nuovamente questi file utilizzando il \ - pulsante sottostante. Manage\ Old\ Data=Gestisci dati vecchi Type=Tipo Name=Nome diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_ja.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_ja.properties index 8b1bf4a49b..1d8dedfcce 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_ja.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_ja.properties @@ -20,32 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -blurb.1=\ - \u30c7\u30fc\u30bf\u306e\u4fdd\u5b58\u5f62\u5f0f\u306b\u5909\u66f4\u304c\u3042\u308b\u5834\u5408\u3001Jenkins\u306f\u6b21\u306e\u51e6\u7406\u3092\u884c\u3044\u307e\u3059\u3002\ - \u30ed\u30fc\u30c9\u6642\u306b\u65b0\u3057\u3044\u5f62\u5f0f\u306b\u79fb\u884c\u3057\u307e\u3059\u304c\u3001\u65b0\u3057\u3044\u5f62\u5f0f\u3067\u306f\u4fdd\u5b58\u3057\u307e\u305b\u3093\u3002\ - \u3053\u308c\u306f\u3001\u5fc5\u8981\u304c\u3042\u308c\u3070Jenkins\u3092\u30c0\u30a6\u30f3\u30b0\u30ec\u30fc\u30c9\u3067\u304d\u308b\u3088\u3046\u306b\u3059\u308b\u305f\u3081\u3067\u3059\u304c\u3001\ - \u3044\u3064\u307e\u3067\u3082\u30c7\u30a3\u30b9\u30af\u4e0a\u306b\u53e4\u3044\u5f62\u5f0f\u3067\u30c7\u30fc\u30bf\u3092\u6b8b\u3057\u305f\u307e\u307e\u306b\u3057\u3066\u3057\u307e\u3044\u307e\u3059\u3002\ - \u6b21\u306e\u8868\u306b\u306f\u3001\u305d\u306e\u3088\u3046\u306a\u30c7\u30fc\u30bf\u3092\u542b\u3080\u30d5\u30a1\u30a4\u30eb\u3068\u3001\u30c7\u30fc\u30bf\u306e\u5f62\u5f0f\u304c\u5909\u66f4\u306b\u306a\u3063\u305fJenkins\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u8868\u793a\u3055\u308c\u3066\u3044\u307e\u3059\u3002 blurb.2=\ \u30c7\u30fc\u30bf\u30ed\u30fc\u30c9\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3059\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\ (\u30d7\u30e9\u30b0\u30a4\u30f3\u304c\u3042\u308b\u30c7\u30fc\u30bf\u3092\u8ffd\u52a0\u3057\u305f\u5f8c\u306b\u305d\u306e\u30d7\u30e9\u30b0\u30a4\u30f3\u304c\u7121\u52b9\u306b\u306a\u3063\u305f\u3001\u5f62\u5f0f\u5909\u66f4\u306b\u5bfe\u5fdc\u3059\u308b\u51e6\u7406\u304c\u5b9f\u88c5\u3055\u308c\u3066\u3044\u306a\u3044\u3001\ \u65e7\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u8aad\u3081\u306a\u3044\u30c7\u30fc\u30bf\u3092\u3059\u3067\u306b\u66f8\u304d\u8fbc\u3093\u3060\u5f8c\u306bJenkins\u3092\u30c0\u30a6\u30f3\u30b0\u30ec\u30fc\u30c9\u3057\u305f\u306a\u3069)\u3002\ Jenkins\u304c\u8d77\u52d5\u3057\u6b63\u5e38\u306b\u52d5\u4f5c\u3059\u308b\u3088\u3046\u306b\u3001\u3053\u308c\u3089\u306e\u30a8\u30e9\u30fc\u3092\u30ed\u30b0\u306b\u51fa\u529b\u3057\u3001\u8aad\u3081\u306a\u3044\u30c7\u30fc\u30bf\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002 -blurb.3=\ - \u6b21\u306e\u30d5\u30a9\u30fc\u30e0\u3092\u5229\u7528\u3057\u3066\u3053\u308c\u3089\u306e\u53e4\u3044\u30d5\u30a1\u30a4\u30eb\u3092\u73fe\u5728\u306e\u5f62\u5f0f\u3067\u518d\u4fdd\u5b58\u3057\u307e\u3059\u3002\ - \u518d\u4fdd\u5b58\u3059\u308b\u3068\u3001\u9078\u629e\u3057\u305f\u30d0\u30fc\u30b8\u30e7\u30f3\u3088\u308a\u53e4\u3044\u3001\u65b0\u3057\u3044\u5f62\u5f0f\u3067\u4fdd\u5b58\u3055\u308c\u305f\u30c7\u30fc\u30bf\u3092\u8aad\u3081\u306a\u3044Jenkins\u306b\u30c0\u30a6\u30f3\u30b0\u30ec\u30fc\u30c9\u3059\u308b\u3053\u3068\u306b\u306a\u308a\u307e\u3059\u3002 - \u3053\u306e\u30d5\u30a9\u30fc\u30e0\u3092\u4f7f\u7528\u3057\u306a\u304f\u3066\u3082\u3001Jenkins\u3092\u4f7f\u7528\u3057\u3066\u30b8\u30e7\u30d6\u306e\u4f5c\u6210\u3001\u8a2d\u5b9a\u304a\u3088\u3073\u5b9f\u884c\u3092\u884c\u3048\u3070\u3001 \ - \u53e4\u3044Jenkins\u3067\u306f\u8aad\u3081\u306a\u3044\u30c7\u30fc\u30bf\u3092\u4fdd\u5b58\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u307e\u305f\u3001\u8aad\u3081\u306a\u3044\u30c7\u30fc\u30bf\u304c\u8868\u793a\u3055\u308c\u3066\u3082\u3001\ - \u30d5\u30a1\u30a4\u30eb\u3092\u518d\u4fdd\u5b58\u3059\u308b\u3068\u524a\u9664\u3055\u308c\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -blurb.4=\ - \u30c7\u30fc\u30bf\u79fb\u884c\u3092\u30b5\u30dd\u30fc\u30c8\u3059\u308b\u30b3\u30fc\u30c9\u306f\u3001\u3044\u3064\u304b\u524a\u9664\u3055\u308c\u307e\u3059\u3002\ - \u5f62\u5f0f\u306e\u5909\u66f4\u304c\u3042\u3063\u305f\u30ea\u30ea\u30fc\u30b9\u304b\u3089\u5c11\u306a\u304f\u3068\u3082150\u30ea\u30ea\u30fc\u30b9\u306b\u3064\u3044\u3066\u306f\u3001\u4e92\u63db\u6027\u306f\u4fdd\u8a3c\u3055\u308c\u307e\u3059\u3002\ - \u305d\u308c\u3088\u308a\u3082\u53e4\u3044\u30d0\u30fc\u30b8\u30e7\u30f3\u306f\u30dc\u30fc\u30eb\u30c9\u3067\u8868\u793a\u3055\u308c\u307e\u3059\u3002\u305d\u308c\u3089\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u518d\u4fdd\u5b58\u3059\u308b\u3088\u3046\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -blurb.5=\ - (\u9078\u629e\u3055\u308c\u305f\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u53ef\u80fd\u306a\u9650\u308a\u30c0\u30a6\u30f3\u30b0\u30ec\u30fc\u30c9\u3057\u307e\u3059) -blurb.6=\ - Jenkins\u306f\u8aad\u3081\u306a\u3044\u30c7\u30fc\u30bf\u3092\u7121\u8996\u3059\u308b\u306e\u3067\u3001\u305d\u306e\u307e\u307e\u306b\u3057\u3066\u304a\u304f\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002\ - Jenkins\u8d77\u52d5\u6642\u306b\u30ed\u30b0\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u51fa\u529b\u3057\u306a\u3044\u3088\u3046\u306b\u3001\u30dc\u30bf\u30f3\u3092\u62bc\u4e0b\u3057\u3066\u8aad\u3081\u306a\u3044\u30c7\u30fc\u30bf\u3092\u4e8b\u524d\u306b\u524a\u9664\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002 Manage\ Old\ Data=\u65e7\u30c7\u30fc\u30bf\u306e\u7ba1\u7406 Type=\u578b Name=\u540d\u524d diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_nl.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_nl.properties index 76231c51b7..64c15c5c6d 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_nl.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_nl.properties @@ -4,5 +4,4 @@ Manage\ Old\ Data=Oude gegevens beheren Name=Naam No\ old\ data\ was\ found.=Er werden geen oude gegevens gevonden. Version=Versie -blurb.1=Bij veranderingen in de opslagwijze van gegevens op schijf gebruikt Jenkins de volgende aanpak: gegevens worden bij het laden naar de nieuwe structuur gemigreerd, maar het bestand wordt niet opnieuw in de nieuwe structuur opgeslagen. Op deze manier blijft ''downgraden'' van Jenkins mogelijk. Ook is het mogelijk gegevens onbeperkt lang in de oude structuur op te slaan. In de onderstaande tabel staan deze gegevens samen met de Jenkins-versie(s) waarvan de gegevensstructuur is gewijzigd. blurb.2=Soms ontstaan fouten bij het lezen van gegevens (als een plugin gegevens toevoegd waarna deze plugin uitgeschakeld wordt, als migragiecode niet geschreven is voor structuurwijzigingen of als Jenkins gedowngrade is nadat er gegevens zijn opgeslgen die onleesbaar zijn voor de oudere versie). Deze fouten worden gelogd, maar de onleesbare gegevens worden overgeslagen zodat Jenkins toch op kan starten en juist kan functioneren. diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_pt.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_pt.properties index 9228d86864..7f3656053f 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_pt.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_pt.properties @@ -21,15 +21,9 @@ # THE SOFTWARE. Type=Tipo -blurb.6=\u00c9 aceit\u00e1vel deixar esses dados ileg\u00edveis nesses arquivos,o Jenkins vai ignor\u00e1-los. \ -Para evitar essas mensagens no log de startup do Jenkins, voc\u00ea pode remover esses dados regravando os arquivos usando o bot\u00e3o abaixo. Discard\ Unreadable\ Data=Descartar dados ileg\u00edveis -blurb.4=Eventualmente o c\u00f3digo que suporta essas migra\u00e7\u00f5es de dados pode ser removido. A compatibilidade ser\u00e1 \ -mantida por pelo menos 150 releases antes de uma mudan\u00e7a de estrutura. As vers\u00f5es mais antigas que essa \ -em negrito abaixo, \u00e9 recomendado que regrave esses arquivos. Version=Vers\u00e3o Upgrade=Upgrade -blurb.5=(downgrade assim que for poss\u00edvel com a vers\u00e3o selecionada) Resave\ data\ files\ with\ structure\ changes\ no\ newer\ than\ Jenkins=Regravar os arquivos de dados com a mudan\u00e7a de estrutura sem ser mais nova que o Jenkins blurb.2=Algumas vezes ocorre erro ao ler os dados (se o plugin adiciona algum dado que depois \u00e9 \ desativado, se o c\u00f3digo de migra\u00e7\u00e3o n\u00e3o estiver escrito com as mudan\u00e7as de estrutura, ou se o Jenkins sofreu \ @@ -38,19 +32,7 @@ Esses erros s\u00e3o logados,mas o dado ileg\u00edvel ser\u00e1 ignorado, permit funcionar corretamente. Error=Erro Unreadable\ Data=Dado ileg\u00edvel -blurb.1=Quando existirem mudan\u00e7as em como o dado \u00e9 armazenado no disco, o Jenkins usa a seguinte estrat\u00e9gia: \ -o dado \u00e9 migrado para a nova estrutura quando for carregado, mas o arquivo n\u00e3o \u00e9 regravado no novo formato. \ -Isso permite um downgrade do Jenkins se necess\u00e1rio, entretando pode tamb\u00e9m deixar dados no disco em formato \ -antigo por tempo indeterminado. A tabela abaixo lista os arquivos que cont\u00e9m esses dados, \ -as vers\u00f5es do Jenkins e quando a estrutura de dados foi alterada. - No\ old\ data\ was\ found.=Nenhum dado antigo foi encontrado. -blurb.3=O formul\u00e1rio abaixo pode ser usado para regravar os arquivos no formato atual. Fazer isso significa \ -fazer um downgrade para uma vers\u00e3o do Jenkins mais antiga do que a selecionada,ele n\u00e3o conseguir\u00e1 ler os dados \ -no novo formato. Note que simplesmemnte usando o Jenkins para criar e configurar jobs \ -e rodar builds pode gravar dados que n\u00e3o ser\u00e3o lidos por vers\u00f5es antigas do Jenkins, mesmo quando \ -esse formul\u00e1rio n\u00e3o for usado. Se algum dado ileg\u00edvel der erro no lado direito da tabela \ -abaixo, esse dado ser\u00e1 perdido quando o arquivo for regravado. Name=Nome Manage\ Old\ Data=Gerenciar dado antigo diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_pt_BR.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_pt_BR.properties index 284fc1b089..c88f15197a 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_pt_BR.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_pt_BR.properties @@ -20,19 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -# \ -# When there are changes in how data is stored on disk, Jenkins uses the following strategy: \ -# data is migrated to the new structure when it is loaded, but the file is not resaved in the \ -# new format. This allows for downgrading Jenkins if needed. However, it can also leave data \ -# on disk in the old format indefinitely. The table below lists files containing such data, \ -# and the Jenkins version(s) where the data structure was changed. -blurb.1=Quando existem mudan\u00E7as em como os dados s\u00E3o armazenados no disco, o Jenkins usa a seguinte estrat\u00E9gia: os dados s\u00E3o migrados para a nova estrutura quando o Jenkins \u00E9 carregado, mas o arquivo n\u00E3o \u00E9 salvo novamente no novo formato. Isto permite o downgrade do Jenkins se necess\u00E1rio. Entretanto, ele tamb\u00E9m pode ser deixado no disco no formato antigo. A tabela abaixo lista os arquivos contendo tais dados, e a(s) vers\u00E3o(\u00F5es) do Jenkins onde a estrutura de dados foi alterada. -# \ -# Sometimes errors occur while reading data (if a plugin adds some data and that plugin is \ -# later disabled, if migration code is not written for structure changes, or if Jenkins is \ -# downgraded after it has already written data not readable by the older version). \ -# These errors are logged, but the unreadable data is then skipped over, allowing Jenkins to \ -# startup and function properly. blurb.2=Algumas vezes ocorrem erros enquanto lendo dados (se um plugin adiciona algum dado e este plugin \u00E9 desativado posteriormente, se o c\u00F3digo de migra\u00E7\u00E3o n\u00E3o suporta mudan\u00E7as de estrutura, ou se for feito um downgrade do Jenkins ap\u00F3s ele j\u00E1 ter escrito dados n\u00E3o suportados por vers\u00F5es anteriores). Estes erros s\u00E3o registrados no log, mas os dados ileg\u00EDveis ignorados, permitindo que o Jenkins seja iniciado e funcione apropriadamente. # \ # The form below may be used to resave these files in the current format. Doing so means a \ @@ -41,22 +28,6 @@ blurb.2=Algumas vezes ocorrem erros enquanto lendo dados (se um plugin adiciona # and run builds can save data that may not be readable by older Jenkins releases, even when \ # this form is not used. Also if any unreadable data errors are reported in the right side \ # of the table above, note that this data will be lost when the file is resaved. -blurb.3=O formul\u00E1rio abaixo pode ser usado para salvar novamente estes arquivos no formato atual. Fazer isso significa que um downgrade para uma vers\u00E3o do Jenkins mais antiga do que a selecionada n\u00E3o ser\u00E1 capaz de ler os dados armazenados no novo formato. Note que simplesmente usando o Jenkins para criar e configurar jobs e executar builds pode salvar dados que n\u00E3o podem ser lidos por vers\u00F5es anteriores do Jenkins,mesmo quando este formul\u00E1rio n\u00E3o \u00E9 usado. Tamb\u00E9m se qualquer erro de dado ileg\u00EDvel for reportado no lado direito da tabela acima, estes dados ser\u00E3o perdidos quando o arquivo for salvo novamente. -# \ -# Eventually the code supporting these data migrations may be removed. Compatibility will be \ -# retained for at least 150 releases since the structure change. Versions older than this are \ -# in bold above, and it is recommended to resave these files. -blurb.4=Eventualmente o c\u00F3digo que suporta a migra\u00E7\u00E3o de dados pode ser removido. A compatibilidade ser\u00E1 mantida ao menos por 150 vers\u00F5es desde a mudan\u00E7a na estrutura. Vers\u00F5es mais antigas que esta ent\u00E3o em negrito, e \u00E9 recomendado salvar novamente estes arquivos. -# \ -# (downgrade as far back as the selected version may still be possible) -blurb.5=\ - (\u00c9 poss\u00edvel fazer o downgrade do Jenkins at\u00e9 a vers\u00e3o selecionada) -# \ -# It is acceptable to leave unreadable data in these files, as Jenkins will safely ignore it. \ -# To avoid the log messages at Jenkins startup you can permanently delete the unreadable data \ -# by resaving these files using the button below. -blurb.6=\u00C9 aceit\u00E1vel deixar dados ileg\u00EDveis nestes arquivos, porque o Jenkins ir\u00E1 ignor\u00E1-los. Para evitar mensagens de erro na inicializa\u00E7\u00E3o do Jenkins voc\u00EA pode excluir permanentemente os dados ileg\u00EDveis usando o bot\u00E3o abaixo. - Type=Tipo Discard\ Unreadable\ Data=Descartar dados ileg\u00EDveis Version=Vers\u00E3o diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_sr.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_sr.properties index 501dad149a..a4d662d56a 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_sr.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_sr.properties @@ -1,18 +1,13 @@ # This file is under the MIT License by authors Manage\ Old\ Data=\u0423\u0440\u0435\u0434\u0438 \u0441\u0442\u0430\u0440\u0435 \u043F\u043E\u0434\u0430\u0442\u043A\u0435 -blurb.1=\u041A\u0430\u0434\u0430 \u0438\u043C\u0430 \u043F\u0440\u043E\u043C\u0435\u043D\u0435 \u0443 \u0442\u043E\u043C\u0435 \u043A\u0430\u043A\u043E \u0441y \u043F\u043E\u0434\u0430\u0446\u0438 \u0443\u0447\u0443\u0432\u0430\u043D\u0438 \u043D\u0430 \u0434\u0438\u0441\u043A\u0443, Jenkins \u043A\u043E\u0440\u0438\u0441\u0442\u0438 \u0441\u043B\u0435\u0434\u0435\u045B\u0443 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0458\u0443: \u043F\u043E\u0434\u0430\u0446\u0438 \u0441\u0443 \u043F\u0440\u0435\u043D\u0435\u0442\u0438 \u0443 \u043D\u043E\u0432\u0443 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0437 \u043A\u0430\u0434\u0430 \u0458\u0435 \u0443\u0447\u0438\u0442\u0430\u043D, \u0430\u043B\u0438 \u0434\u0430\u0442\u043E\u0442\u0435\u043A\u0430 \u043D\u0438\u0458\u0435 \u0441\u0430\u0447\u0443\u0432\u0430\u043D\u0430 \u0443 \u043D\u043E\u0432\u043E\u043C \u0444\u043E\u0440\u043C\u0430\u0442\u0443. \u0422\u043E \u0432\u0430\u043C \u043E\u043C\u043E\u0433\u0443\u045B\u0430\u0432\u0430 \u0434\u0430 \u0441\u0435 \u0432\u0440\u0430\u0442\u0438 \u0432\u0435\u0440\u0437\u0438\u0458\u0430 Jenkins \u0430\u043A\u043E \u0431\u0443\u0434\u0435 \u043F\u043E\u0442\u0440\u0435\u0431\u043D\u043E. \u041C\u0435\u0452\u0443\u0442\u0438\u043C, \u043E\u043D \u0442\u0430\u043A\u043E\u0452\u0435 \u043C\u043E\u0436\u0435 \u043F\u0438\u0441\u0430\u0442\u0438 \u043D\u043E\u0432\u0435 \u043F\u043E\u0434\u0430\u0442\u043A\u0435 \u043D\u0430 \u0434\u0438\u0441\u043A\u0443 \u0443 \u0441\u0442\u0430\u0440\u043E\u043C \u0444\u043E\u0440\u043C\u0430\u0442\u0443 \u043D\u0430 \u043D\u0435\u043E\u0434\u0440\u0435\u0452\u0435\u043D\u043E \u0432\u0440\u0435\u043C\u0435. \u0423 \u0442\u0430\u0431\u0435\u043B\u0438 \u0438\u0441\u043F\u043E\u0434 \u0458\u0435 \u0441\u043F\u0438\u0441\u0430\u043A \u0434\u0430\u0442\u043E\u0442\u0435\u043A\u0430 \u043A\u043E\u0458\u0438 \u0441\u0430\u0434\u0440\u0436\u0435 \u0442\u0430\u043A\u0432\u0435 \u043F\u043E\u0434\u0430\u0442\u043A\u0435, \u0438 \u0432\u0435\u0440\u0437\u0438\u0458\u0430 Jenkins, \u0433\u0434\u0435 \u0458\u0435 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u043F\u0440\u043E\u043C\u0435\u045A\u0435\u043D\u0430. blurb.2=\u041F\u043E\u043D\u0435\u043A\u0430\u0434 \u0441\u0435 \u043F\u043E\u0458\u0430\u0432\u0435 \u0433\u0440\u0435\u0448\u043A\u0435 \u043F\u0440\u0438\u043B\u0438\u043A\u043E\u043C \u0447\u0438\u0442\u0430\u045A\u0430 \u043F\u043E\u0434\u0430\u0442\u0430\u043A\u0430 (\u0430\u043A\u043E \u043D\u043F\u0440 \u043C\u043E\u0434\u0443\u043B\u0430 \u0431\u0443\u0434\u0435 \u043A\u0430\u0441\u043D\u0438\u0458\u0435 \u0438\u0441\u043A\u0459\u0443\u0447\u0435\u043D\u0430, \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u043E\u043D\u0438 \u043A\u043E\u0434\u0435\u043A\u0441 \u043D\u0430\u043F\u0438\u0441\u0430\u043D \u043D\u0435 \u043F\u0440\u0435\u043F\u043E\u0437\u043D\u0430 \u043F\u0440\u043E\u043C\u0435\u043D\u0435 \u0443 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0438, \u0438\u043B\u0438 \u0430\u043A\u043E \u0458\u0435 Jenkins \u0432\u0440\u0430\u045B\u0435\u043D \u043F\u0440\u0435\u0442\u0445\u043E\u0434\u043D\u043E\u0458 \u0432\u0435\u0440\u0437\u0438\u0458\u0438 \u043D\u0430\u043A\u043E\u043D \u0448\u0442\u043E \u0431\u0438 \u043D\u0435\u043A\u0438 \u043F\u043E\u0434\u0430\u0446\u0438 \u043D\u0435\u0431\u0438 \u043C\u043E\u0433\u043B\u0438 \u0431\u0438\u0442\u0438 \u0443\u0447\u0438\u0442\u0430\u043D\u0438). \u041E\u0432\u0435 \u0433\u0440\u0435\u0448\u043A\u0435 \u0441\u0443 \u0441\u0430\u0447\u0443\u0432\u0430\u043D\u0435, \u0430\u043B\u0438 \u043D\u0435\u043E\u0447\u0438\u0442\u0459\u0438\u0432\u0438 \u043F\u043E\u0434\u0430\u0446\u0438 \u0441\u0435 \u043F\u0440\u0435\u0434\u0441\u043A\u0430\u0447\u0443. Type=\u0422\u0438\u043F Name=\u0418\u043C\u0435 Version=\u0412\u0435\u0440\u0437\u0438\u0458\u0430 -blurb.3=\u041F\u0440\u0430\u0442\u0435\u045B\u0438 \u0444\u043E\u0440\u043C\u0443\u043B\u0430\u0440 \u043C\u043E\u0436\u0435 \u0441\u0435 \u043A\u043E\u0440\u0438\u0441\u0442\u0438\u0442\u0438 \u0437\u0430 \u043F\u043E\u043D\u043E\u0432\u043E \u0441\u0430\u0447\u0443\u0432\u0430\u045A\u0435 \u0434\u0430\u0442\u043E\u0442\u0435\u043A\u0430 \u0443 \u0442\u0440\u0435\u043D\u0443\u0442\u043D\u043E\u043C \u0444\u043E\u0440\u043C\u0430\u0442\u0443, \u043A\u043E\u0458\u0438 \u043D\u0435\u043C\u043E\u0436\u0435 \u0431\u0438\u0442\u0438 \u0443\u0447\u0438\u0442\u0430\u043D \u0441\u0442\u0430\u0440\u0438\u0458\u0438\u043C \u0432\u0435\u0440\u0437\u0438\u0458\u0430\u043C\u0430 Jenkins. \u041D\u043E\u0440\u043C\u0430\u043B\u043D\u0430 \u0443\u043F\u043E\u0442\u0440\u0435\u0431\u0430 \u0442\u0430\u043A\u043E\u0452\u0435 \u043C\u043E\u0436\u0435 \u043F\u0440\u043E\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u043F\u043E\u0434\u0430\u0442\u043A\u0435 \u043A\u043E\u0458\u0435 \u043D\u0435\u043C\u043E\u0433\u0443 \u0431\u0438\u0442\u0438 \u0443\u0447\u0438\u0442\u0430\u043D\u0438 \u0441\u0442\u0430\u0440\u0438\u0458\u0438\u043C \u0432\u0435\u0440\u0437\u0438\u0458\u0430\u043C\u0430 Jenkins. \u0421\u0432\u0438 \u043D\u0435\u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u0440\u0435\u0431\u0430\u0447\u0435\u043D\u0438 \u043F\u043E\u0434\u0430\u0446\u0438 \u045B\u0435 \u0431\u0438\u0442\u0438 \u0438\u0437\u0431\u0440\u0438\u0441\u0430\u043D\u0438 \u043D\u0430\u043A\u043E\u043D \u0441\u0430\u0447\u0443\u0432\u0430\u045A\u0430. -blurb.4=\ \u041E\u0432\u0430 \u0441\u043F\u043E\u0441\u043E\u0431\u043D\u043E\u0441\u0442 \u043C\u043E\u0436\u0435 \u0435\u0432\u0435\u043D\u0442\u0443\u0430\u043B\u043D\u043E \u0431\u0438\u0442\u0438 \u0443\u043A\u043B\u045A\u0435\u043D\u0430, \u043C\u0435\u0452\u0443\u0442\u0438\u043C \u043A\u043E\u043C\u043F\u0430\u0442\u0438\u0431\u0438\u043B\u043D\u043E\u0441\u0442 \u045B\u0435 \u0431\u0438\u0442\u0438 \u043E\u0434\u0440\u0436\u0430\u043D \u0434\u043E \u043D\u0430\u0458\u043C\u0430\u045A\u0435 150 \u0438\u0437\u0434\u0430\u045A\u0430 \u043F\u043E\u0441\u043B\u0435 \u0438\u043A\u0430\u043A\u0432\u0438\u0445 \u043F\u0440\u043E\u043C\u0435\u043D\u0430. \u0421\u0442\u0430\u0440\u0438\u0458\u0435 \u0432\u0435\u0440\u0437\u0438\u0458\u0435 \u0441\u0443 \u043E\u0437\u043D\u0430\u0447\u0435\u043D\u0438 \u043C\u0430\u0441\u043D\u0438\u043C \u0441\u043B\u043E\u0432\u0438\u043C\u0430. \u041F\u0440\u0435\u043F\u043E\u0440\u0443\u0447\u0443\u0458\u0435 \u0441\u0435 \u0441\u0430\u0447\u0443\u0432\u0430\u045A\u0435 \u043E\u0432\u0438\u0445 \u0434\u0430\u0442\u043E\u0442\u0435\u043A\u0430. Resave\ data\ files\ with\ structure\ changes\ no\ newer\ than\ Jenkins=\u041F\u043E\u043D\u043E\u0432\u043E \u0441\u0430\u0447\u0443\u0432\u0430\u0458 \u0434\u0430\u0442\u043E\u0442\u0435\u043A\u0435 \u0441\u0430 \u043F\u0440\u043E\u043C\u0435\u043D\u0430\u043C\u0430 \u043A\u043E\u0458\u0435 \u043D\u0438\u0441\u0443 \u043D\u043E\u0432\u0438\u0458\u0430 \u043E\u0434 Jenkins -blurb.5=(\u0432\u0440\u0430\u045B\u0430\u045A\u0435 \u043D\u0430 \u043E\u0434\u0430\u0431\u0440\u0430\u043D\u0443 \u0432\u0435\u0440\u0437\u0438\u0458\u0443 \u045B\u0435 \u0431\u0438\u0442\u0438 \u043C\u043E\u0433\u0443\u045B\u0435) Upgrade=\u0410\u0436\u0443\u0440\u0438\u0440\u0430\u0458 No\ old\ data\ was\ found.=\u0417\u0430\u0441\u0442\u0430\u0440\u0435\u043B\u0438 \u043F\u043E\u0434\u0430\u0446\u0438 \u043D\u0438\u0441\u0443 \u043F\u0440\u043E\u043D\u0430\u0452\u0435\u043D\u0438. Unreadable\ Data=\u041D\u0435\u043E\u0447\u0438\u0442\u0459\u0438\u0432\u0438 \u043F\u043E\u0434\u0430\u0446\u0438 -blurb.6=\u041C\u043E\u0436\u0435 \u0441\u0435 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u0438 \u043D\u0435\u0432\u0430\u0436\u0435\u045B\u0435 \u043F\u043E\u0434\u0430\u0442\u043A\u0435 \u0443 \u043E\u0432\u0438\u043C \u0434\u0430\u0442\u043E\u0442\u0435\u043A\u0430\u043C\u0430, \u0437\u0430\u0448\u0442\u043E Jenkins \u0438\u0445 \u043D\u0435 \u0437\u0430\u0431\u0435\u043B\u0435\u0436\u0438. \u041F\u043E\u043D\u043E\u0432\u043E \u0441\u0430\u0447\u0443\u0432\u0430\u0458\u0442\u0435 \u043F\u043E\u0434\u0430\u0442\u043A\u0435 \u0434\u0430 \u0431\u0438\u0441\u0442\u0435 \u0438\u0437\u0431\u0435\u0433\u043B\u0438 \u0436\u0443\u0440\u043D\u0430\u043B \u043F\u043E\u0440\u0443\u043A\u0435 \u043D\u0430\u043A\u043E\u043D \u043F\u043E\u043A\u0440\u0435\u0442\u0430\u045A\u0430. Error=\u0413\u0440\u0435\u0448\u043A\u0430 Discard\ Unreadable\ Data=\u041E\u0434\u0431\u0430\u0446\u0438 \u043D\u0435\u0447\u0438\u0459\u0438\u0432\u0435 \u043F\u043E\u0434\u0430\u0442\u043A\u0435 diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_zh_TW.properties b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_zh_TW.properties index 709b731a3f..f6ad024f78 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_zh_TW.properties +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage_zh_TW.properties @@ -21,32 +21,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -blurb.1=\ - \u5132\u5b58\u8cc7\u6599\u7684\u65b9\u5f0f\u6539\u8b8a\u6642\uff0cJenkins \u4f7f\u7528\u4ee5\u4e0b\u7b56\u7565\u8655\u7406: \ - \u8cc7\u6599\u8f09\u5165\u6642\u6703\u88ab\u8f49\u79fb\u6210\u65b0\u7684\u7d50\u69cb\uff0c\u4f46\u4e0d\u6703\u4ee5\u65b0\u683c\u5f0f\u91cd\u65b0\u5132\u5b58\uff0c\u5fc5\u8981\u6642\u624d\u80fd\u5012\u56de\u820a\u7248 Jenkins\u3002\ - \u7136\u800c\uff0c\u8cc7\u6599\u5c31\u6709\u53ef\u80fd\u88ab\u6c38\u9060\u4ee5\u820a\u683c\u5f0f\u5132\u5b58\u5728\u78c1\u789f\u4e0a\u3002\ - \u4e0b\u8868\u5217\u51fa\u7684\u5c31\u662f\u9019\u985e\u8cc7\u6599\uff0c\u4ee5\u53ca Jenknins \u8cc7\u6599\u7d50\u69cb\u8b8a\u66f4\u7684\u7248\u672c\u3002 blurb.2=\ \u8b80\u53d6\u8cc7\u6599\u6642\u4e5f\u6709\u53ef\u80fd\u767c\u751f\u932f\u8aa4 \ (\u4f8b\u5982: \u67d0\u500b\u5916\u639b\u7a0b\u5f0f\u65b0\u589e\u8cc7\u6599\u9032\u4f86\uff0c\u4f46\u662f\u8a72\u5916\u639b\u7a0b\u5f0f\u5f8c\u4f86\u88ab\u95dc\u9589\u4e86; \ \u7d50\u69cb\u8b8a\u66f4\u6642\u6c92\u6709\u5beb\u597d\u5c0d\u61c9\u7684\u8f49\u79fb\u7a0b\u5f0f; \u6216 Jenkins \u88ab\u964d\u7248\u524d\u5df2\u7d93\u5beb\u5165\u4e86\u820a\u7248\u7121\u6cd5\u8b80\u53d6\u7684\u8cc7\u6599)\u3002\ \u9019\u4e9b\u932f\u8aa4\u90fd\u6703\u88ab\u8a18\u9304\u4e0b\u4f86\uff0c\u4f46\u662f\u8cc7\u6599\u6703\u76f4\u63a5\u88ab\u7565\u904e\uff0c\u8b93 Jenkins \u53ef\u4ee5\u7e7c\u7e8c\u555f\u52d5\u4e26\u9806\u5229\u904b\u4f5c\u3002 -blurb.3=\ - \u4e0b\u5217\u8868\u55ae\u53ef\u4ee5\u8b93\u60a8\u5c07\u6a94\u6848\u4ee5\u73fe\u884c\u683c\u5f0f\u91cd\u65b0\u5132\u5b58\u3002\ - \u4f46\u662f\u91cd\u65b0\u5132\u5b58\u5f8c\u6bd4\u6307\u5b9a\u7248\u672c\u9084\u8981\u820a\u7684 Jenkins \u5c07\u5c31\u6c92\u8fa6\u6cd5\u8b80\u9019\u4e9b\u8cc7\u6599\u4e86\u3002\ - \u6709\u4e00\u9ede\u8981\u6ce8\u610f\uff0c\u5c31\u7b97\u60a8\u90fd\u6c92\u7528\u5230\u9019\u4efd\u8868\u55ae\uff0c\u55ae\u7d14\u7684\u900f\u904e Jenkins \u5efa\u7acb\u6216\u662f\u8a2d\u5b9a\u5de5\u4f5c\u53ca\u57f7\u884c\u5efa\u7f6e\u4f5c\u696d\uff0c\ - \u4e5f\u90fd\u6709\u53ef\u80fd\u5132\u5b58\u5230\u820a\u7248 Jenkins \u7121\u6cd5\u8b80\u53d6\u7684\u8cc7\u6599\u3002\ - \u6b64\u5916\uff0c\u5728\u91cd\u65b0\u5132\u5b58\u6642\uff0c\u4e0a\u8868\u53f3\u5074\u5217\u51fa\u7684\u76f8\u95dc\u932f\u8aa4\u8cc7\u6599\u90fd\u6703\u907a\u5931\u3002 -blurb.4=\ - \u8cc7\u6599\u8f49\u79fb\u7684\u8655\u7406\u529f\u80fd\u65e5\u5f8c\u53ef\u80fd\u6703\u88ab\u79fb\u9664\uff0c\u4f46\u662f\u8cc7\u6599\u7d50\u69cb\u6539\u8b8a\u7684 150 \u7248\u4e4b\u5167\u6211\u5011\u6703\u76e1\u91cf\u78ba\u4fdd\u76f8\u5bb9\u6027\u3002\ - \u8d85\u904e\u9019\u500b\u7248\u672c\u7684\u8cc7\u6599\u6703\u4ee5\u7c97\u9ad4\u986f\u793a\uff0c\u5efa\u8b70\u60a8\u91cd\u65b0\u5132\u5b58\u9019\u4e9b\u6a94\u6848\u3002 -blurb.5=\ - (\u964d\u5230\u9078\u53d6\u7248\u672c\u5f8c\u7684\u7248\u672c\u61c9\u8a72\u4e0d\u6703\u6709\u4ec0\u9ebc\u554f\u984c) -blurb.6=\ - \u4fdd\u7559\u9019\u4e9b\u7121\u6cd5\u8b80\u53d6\u7684\u8cc7\u6599\u4e26\u4e0d\u6703\u6709\u4ec0\u9ebc\u5927\u7919\uff0cJenkins \u6703\u5b89\u5168\u7684\u5ffd\u7565\u5b83\u5011\u3002\ - \u4e0d\u904e\u70ba\u4e86\u907f\u514d\u6bcf\u6b21 Jenkins \u555f\u52d5\u6642\u90fd\u6703\u5217\u51fa\u76f8\u95dc\u7684\u8a0a\u606f\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u4e0b\u65b9\u7684\u6309\u9215\u91cd\u65b0\u5132\u5b58\u6a94\u6848\uff0c\ - \u6c38\u4e45\u522a\u9664\u90a3\u4e9b\u7121\u6cd5\u8b80\u53d6\u7684\u8cc7\u6599\u3002 - Manage\ Old\ Data=\u7ba1\u7406\u820a\u7248\u8cc7\u6599 Type=\u985e\u578b Name=\u540d\u7a31 -- GitLab From c8b34f34726c0d1df0890660a6d36e08658921cd Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Tue, 11 Dec 2018 21:23:25 +0100 Subject: [PATCH 122/424] Use mdash Co-Authored-By: daniel-beck --- war/src/main/webapp/help/project-config/triggerRemotely.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/war/src/main/webapp/help/project-config/triggerRemotely.html b/war/src/main/webapp/help/project-config/triggerRemotely.html index 08da667af0..10de9a24f5 100644 --- a/war/src/main/webapp/help/project-config/triggerRemotely.html +++ b/war/src/main/webapp/help/project-config/triggerRemotely.html @@ -11,6 +11,6 @@ trigger this project's builds.

    This is most useful when your Jenkins instance grants read access to this job to anonymous users.

    When that's not the case, Jenkins will reject requests sent to the trigger URL even when the correct token is specified.

    -

    To solve this, the HTTP requests needs to be authenticated as a user with the necessary read permission for the job -- but then you could probably just grant this user the permission to build this anyway.

    +

    To solve this, the HTTP requests needs to be authenticated as a user with the necessary read permission for the job — but then you could probably just grant this user the permission to build this anyway.

    Another option is to use the Build Token Root Plugin, that provides additional URL endpoints to trigger builds using this token, and doesn't require the otherwise necessary Overall/Read and Job/Read permissions to do so.

    -- GitLab From 064daa7f6f15d7dc80b145435085fd99d8d33386 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 11 Dec 2018 16:03:51 -0500 Subject: [PATCH 123/424] Scope upgrade text to upgrade case blurb.3 and blurb.4 are specifically talking about a table "below", but without this change, the table is actually above. With this change, the blurbs only appear when they are applicable. --- .../resources/hudson/diagnosis/OldDataMonitor/manage.jelly | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly index 56587c3092..e6eecb28f2 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly @@ -59,11 +59,12 @@ THE SOFTWARE. -

    ${%blurb.3}

    -

    ${%blurb.4}

    +

    ${%Old Data Format}

    +

    ${%blurb.3}

    +

    ${%blurb.4}

    ${%Resave data files with structure changes no newer than Jenkins} + + + +
    + +
    + + + + diff --git a/core/src/main/resources/lib/form/secretTextarea.properties b/core/src/main/resources/lib/form/secretTextarea.properties new file mode 100644 index 0000000000..ebd0491095 --- /dev/null +++ b/core/src/main/resources/lib/form/secretTextarea.properties @@ -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. +# + +Add=Add +Replace=Replace +EnterSecret=Enter New Secret Below +Concealed=Concealed for Confidentiality +NoStoredValue=No Stored Value diff --git a/core/src/main/resources/lib/form/secretTextarea/secret.css b/core/src/main/resources/lib/form/secretTextarea/secret.css new file mode 100644 index 0000000000..4960c64aed --- /dev/null +++ b/core/src/main/resources/lib/form/secretTextarea/secret.css @@ -0,0 +1,77 @@ +/* + * 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. + */ + +.secret-header { + border: 1px solid #ccc; + border-radius: 3px; + background: #f9f9f9; + display: flex; + justify-content: space-around; +} + +.secret-header:not(:only-child) { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.secret-header > div { + flex-grow: 1; + display: inline-flex; + align-items: center; + padding: 1.5em 1.75em; +} + +.secret-legend > svg { + margin-right: 1em; +} + +.secret-update { + justify-content: flex-end; +} + +.secret-input { + border: solid 1px #ccc; + border-top: none; + border-radius: 0 0 3px 3px; +} + +.secret-input textarea { + width: 100%; + font-family: monospace; + border: none; + padding: 1em; +} + +.secret input[type='button'] { + background: #4b99d0; + color: #fff; + border-radius: 4px; + border: none; + padding: 1em 2em; +} + +.secret input[type='button']:hover { + background: #5092be; + cursor: pointer; +} diff --git a/core/src/main/resources/lib/form/secretTextarea/secret.js b/core/src/main/resources/lib/form/secretTextarea/secret.js new file mode 100644 index 0000000000..c2b3121440 --- /dev/null +++ b/core/src/main/resources/lib/form/secretTextarea/secret.js @@ -0,0 +1,77 @@ +/* + * 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. + */ + +Behaviour.specify('.secret', 'secret-button', 0, function (e) { + var secretUpdateBtn = e.querySelector('.secret-update-btn'); + if (secretUpdateBtn === null) return; + + var id = 'secret-' + (iota++); + var name = e.getAttribute('data-name'); + var placeholder = e.getAttribute('data-placeholder'); + var prompt = e.getAttribute('data-prompt'); + + var appendSecretInput = function () { + var textarea = document.createElement('textarea'); + textarea.setAttribute('id', id); + textarea.setAttribute('name', name); + if (placeholder !== null && placeholder !== '') { + textarea.setAttribute('placeholder', placeholder); + } + var secretInput = document.createElement('div'); + secretInput.setAttribute('class', 'secret-input'); + secretInput.appendChild(textarea); + e.appendChild(secretInput); + } + + var clearSecretValue = function () { + var secretValue = e.querySelector('input[type="hidden"]'); + if (secretValue !== null) { + secretValue.parentNode.removeChild(secretValue); + } + } + + var replaceUpdateButton = function () { + var secretLabel = document.createElement('label'); + secretLabel.setAttribute('for', id); + secretLabel.appendChild(document.createTextNode(prompt)); + secretUpdateBtn.parentNode.replaceChild(secretLabel, secretUpdateBtn); + } + + var removeSecretLegendLabel = function () { + var secretLegend = e.querySelector('.secret-legend'); + var secretLegendText = secretLegend.querySelector('span'); + if (secretLegendText !== null) { + secretLegend.removeChild(secretLegendText); + } + } + + secretUpdateBtn.onclick = function () { + appendSecretInput(); + clearSecretValue(); + replaceUpdateButton(); + removeSecretLegendLabel(); + // fix UI bug when DOM changes + Event.fire(window, 'jenkins:bottom-sticker-adjust'); + }; +}); diff --git a/test/src/test/java/lib/form/SecretTextareaTest.java b/test/src/test/java/lib/form/SecretTextareaTest.java new file mode 100644 index 0000000000..93cc37bda5 --- /dev/null +++ b/test/src/test/java/lib/form/SecretTextareaTest.java @@ -0,0 +1,175 @@ +/* + * 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 lib.form; + +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlHiddenInput; +import com.gargoylesoftware.htmlunit.html.HtmlTextInput; +import hudson.model.AbstractProject; +import hudson.model.Project; +import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.Builder; +import hudson.util.Secret; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.JenkinsRule.WebClient; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; +import org.xml.sax.SAXException; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + +public class SecretTextareaTest { + + private Project project; + private WebClient wc; + + @Rule public JenkinsRule j = new JenkinsRule(); + + @Before + public void setUp() throws IOException { + project = j.createFreeStyleProject(); + project.getBuildersList().add(TestBuilder.newDefault()); + wc = j.createWebClient(); + } + + @Test + public void addEmptySecret() throws Exception { + j.configRoundtrip(project); + assertTestBuilderDataBoundEqual(TestBuilder.newDefault()); + } + + @Test + public void addSecret() throws Exception { + setProjectSecret("testValue"); + assertTestBuilderDataBoundEqual(TestBuilder.fromString("testValue")); + } + + @Test + public void addSecretAndUpdateDescription() throws Exception { + setProjectSecret("Original Value"); + assertTestBuilderDataBoundEqual(TestBuilder.fromString("Original Value")); + HtmlForm configForm = goToConfigForm(); + HtmlTextInput description = configForm.getInputByName("_.description"); + description.setText("New description"); + j.submit(configForm); + assertTestBuilderDataBoundEqual(TestBuilder.fromStringWithDescription("Original Value", "New description")); + } + + @Test + public void addSecretAndUpdateSecretWithEmptyValue() throws Exception { + setProjectSecret("First"); + assertTestBuilderDataBoundEqual(TestBuilder.fromString("First")); + HtmlForm configForm = goToConfigForm(); + String hiddenValue = getHiddenSecretValue(configForm); + assertNotNull(hiddenValue); + assertNotEquals("First", hiddenValue); + assertEquals("First", Secret.fromString(hiddenValue).getPlainText()); + clickSecretUpdateButton(configForm); + j.submit(configForm); + assertTestBuilderDataBoundEqual(TestBuilder.fromString("")); + } + + private void assertTestBuilderDataBoundEqual(TestBuilder other) throws Exception { + j.assertEqualDataBoundBeans(other, project.getBuildersList().get(TestBuilder.class)); + } + + private void setProjectSecret(String secret) throws Exception { + HtmlForm configForm = goToConfigForm(); + clickSecretUpdateButton(configForm); + configForm.getTextAreaByName("_.secret").setText(secret); + j.submit(configForm); + } + + private HtmlForm goToConfigForm() throws IOException, SAXException { + return wc.getPage(project, "configure").getFormByName("config"); + } + + private static void clickSecretUpdateButton(HtmlForm configForm) throws IOException { + configForm.getOneHtmlElementByAttribute("input", "class", "secret-update-btn").click(); + } + + private static String getHiddenSecretValue(HtmlForm configForm) { + HtmlHiddenInput hiddenSecret = configForm.getInputByName("_.secret"); + return hiddenSecret == null ? null : hiddenSecret.getValueAttribute(); + } + + public static class TestBuilder extends Builder { + private final Secret secret; + private String description = ""; + + private static TestBuilder newDefault() { + return new TestBuilder(null); + } + + private static TestBuilder fromString(String secret) { + return new TestBuilder(Secret.fromString(secret)); + } + + private static TestBuilder fromStringWithDescription(String secret, String description) { + TestBuilder b = fromString(secret); + b.setDescription(description); + return b; + } + + @DataBoundConstructor + public TestBuilder(Secret secret) { + this.secret = secret; + } + + public Secret getSecret() { + return secret; + } + + public String getDescription() { + return description; + } + + @DataBoundSetter + public void setDescription(String description) { + this.description = description; + } + + @TestExtension + public static class DescriptorImpl extends BuildStepDescriptor { + @Override + public String getDisplayName() { + return "Test Secret"; + } + + @Override + public boolean isApplicable(Class jobType) { + return true; + } + } + } +} \ No newline at end of file diff --git a/test/src/test/resources/lib/form/SecretTextareaTest/TestBuilder/config.jelly b/test/src/test/resources/lib/form/SecretTextareaTest/TestBuilder/config.jelly new file mode 100644 index 0000000000..dfd396de7d --- /dev/null +++ b/test/src/test/resources/lib/form/SecretTextareaTest/TestBuilder/config.jelly @@ -0,0 +1,33 @@ + + + + + + + + + + + -- GitLab From ec245d1e73c55aed41cc27b478943f8313386fa1 Mon Sep 17 00:00:00 2001 From: Gavin Mogan Date: Thu, 4 Apr 2019 11:42:15 -0700 Subject: [PATCH 417/424] merge the two form parameters with a & instead of just concatinating them --- war/src/main/js/util/jenkins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/war/src/main/js/util/jenkins.js b/war/src/main/js/util/jenkins.js index 155374992b..cc962deff4 100644 --- a/war/src/main/js/util/jenkins.js +++ b/war/src/main/js/util/jenkins.js @@ -284,7 +284,7 @@ exports.buildFormPost = function($form) { var wnd = exports.getWindow($form); var form = $form[0]; if(wnd.buildFormTree(form)) { - return $form.serialize() + jquery.param({ + return $form.serialize() + "&" + jquery.param({ 'core:apply': '', 'Submit': 'Save', 'json': $form.find('input[name=json]').val() -- GitLab From 03720d6c839c8c5209d815e8317d6a1bdea5461f Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 12 Jan 2017 10:06:18 +0100 Subject: [PATCH 418/424] [FIX JENKINS-40750] Remove cc.xml from core --- core/src/main/java/hudson/Functions.java | 27 ++--------- .../resources/hudson/model/View/builds.jelly | 3 -- .../resources/hudson/model/View/cc.xml.jelly | 48 ------------------- 3 files changed, 5 insertions(+), 73 deletions(-) delete mode 100644 core/src/main/resources/hudson/model/View/cc.xml.jelly diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 76fdf2f813..279d62295d 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -1556,30 +1556,13 @@ public class Functions { /** * Converts the Hudson build status to CruiseControl build status, * which is either Success, Failure, Exception, or Unknown. + * + * @deprecated This functionality has been moved to ccxml plugin. */ + @Deprecated + @Restricted(DoNotUse.class) + @RestrictedSince("since TODO") public static String toCCStatus(Item i) { - if (i instanceof Job) { - Job j = (Job) i; - switch (j.getIconColor()) { - case ABORTED: - case ABORTED_ANIME: - case RED: - case RED_ANIME: - case YELLOW: - case YELLOW_ANIME: - return "Failure"; - case BLUE: - case BLUE_ANIME: - return "Success"; - case DISABLED: - case DISABLED_ANIME: - case GREY: - case GREY_ANIME: - case NOTBUILT: - case NOTBUILT_ANIME: - return "Unknown"; - } - } return "Unknown"; } diff --git a/core/src/main/resources/hudson/model/View/builds.jelly b/core/src/main/resources/hudson/model/View/builds.jelly index 51c5eebc35..398ccff65f 100644 --- a/core/src/main/resources/hudson/model/View/builds.jelly +++ b/core/src/main/resources/hudson/model/View/builds.jelly @@ -38,9 +38,6 @@ THE SOFTWARE.
    - diff --git a/core/src/main/resources/hudson/model/View/cc.xml.jelly b/core/src/main/resources/hudson/model/View/cc.xml.jelly deleted file mode 100644 index b83ec111d1..0000000000 --- a/core/src/main/resources/hudson/model/View/cc.xml.jelly +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file -- GitLab From b206e3c58814e4413dd173c93717735ec5c9e244 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 4 Apr 2019 22:04:10 +0200 Subject: [PATCH 419/424] Remove UI helper function --- core/src/main/java/hudson/Functions.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 279d62295d..67e9398c23 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -2054,14 +2054,4 @@ public class Functions { return true; } } - - @Restricted(NoExternalUse.class) // for cc.xml.jelly - public static Collection getCCItems(View v) { - if (Stapler.getCurrentRequest().getParameter("recursive") != null) { - return v.getOwner().getItemGroup().getAllItems(TopLevelItem.class); - } else { - return v.getItems(); - } - } - } -- GitLab From 38faed357c7c5c3948164a70e71c39d48fd53c96 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sun, 7 Apr 2019 01:32:46 -0700 Subject: [PATCH 420/424] [maven-release-plugin] prepare release jenkins-2.171 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test-jdk8/pom.xml | 2 +- test-pom/pom.xml | 2 +- test/pom.xml | 2 +- war/pom.xml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index b02df3123e..ca82cffe9e 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.171 cli diff --git a/core/pom.xml b/core/pom.xml index 388f0a7930..28e72298d3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.171 jenkins-core diff --git a/pom.xml b/pom.xml index 6e9b1e1b1e..7d121bd651 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.171 pom Jenkins main module @@ -60,7 +60,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - ${scmTag} + jenkins-2.171 diff --git a/test-jdk8/pom.xml b/test-jdk8/pom.xml index 70c4870837..232004c136 100644 --- a/test-jdk8/pom.xml +++ b/test-jdk8/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-test-parent - ${revision}${changelist} + 2.171 ../test-pom diff --git a/test-pom/pom.xml b/test-pom/pom.xml index c6514ccfdb..433305fa14 100644 --- a/test-pom/pom.xml +++ b/test-pom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.171 jenkins-test-parent diff --git a/test/pom.xml b/test/pom.xml index 4c5abecc6f..2b0940fac9 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-test-parent - ${revision}${changelist} + 2.171 ../test-pom diff --git a/war/pom.xml b/war/pom.xml index bc8f98da18..ad55f5a840 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.171 jenkins-war -- GitLab From 32b4bfb3a23bd186f4f9486185c031c7f406b858 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sun, 7 Apr 2019 01:32:54 -0700 Subject: [PATCH 421/424] [maven-release-plugin] prepare for next development iteration --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 6 +++--- test-jdk8/pom.xml | 2 +- test-pom/pom.xml | 2 +- test/pom.xml | 2 +- war/pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index ca82cffe9e..b02df3123e 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.171 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 28e72298d3..388f0a7930 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.171 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index 7d121bd651..b2ba0dae7f 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.171 + ${revision}${changelist} pom Jenkins main module @@ -60,7 +60,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.171 + ${scmTag} @@ -76,7 +76,7 @@ THE SOFTWARE. - 2.171 + 2.172 -SNAPSHOT