From be7de92a31c224f36c01d1d06de1bb53e6e1161b Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 4 Nov 2021 18:56:45 +0100 Subject: [PATCH 01/58] Deprecate `SlaveToMasterFileCallable`, log warning --- .../jenkins/SlaveToMasterFileCallable.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/src/main/java/jenkins/SlaveToMasterFileCallable.java b/core/src/main/java/jenkins/SlaveToMasterFileCallable.java index 236bd738f2cc..d4c78dc5d07e 100644 --- a/core/src/main/java/jenkins/SlaveToMasterFileCallable.java +++ b/core/src/main/java/jenkins/SlaveToMasterFileCallable.java @@ -1,18 +1,46 @@ package jenkins; import hudson.FilePath.FileCallable; +import hudson.Main; +import java.util.logging.Level; +import java.util.logging.Logger; import jenkins.security.Roles; +import jenkins.util.JenkinsJVM; import org.jenkinsci.remoting.RoleChecker; /** * {@link FileCallable}s that can be executed on the master, sent by the agent. * Note that any serializable fields must either be defined in your plugin or included in the stock JEP-200 whitelist. + * Additionally, this callable can be called with any {@link hudson.FilePath}, it is your responsibility to validate it + * in {@link #invoke(java.io.File, hudson.remoting.VirtualChannel)}. * @since 1.587 / 1.580.1 + * @deprecated Use {@link jenkins.security.SlaveToMasterCallable} instead (and only if you really have to), and think + * carefully about the security implications. + * + * @see jenkins.security.SlaveToMasterCallable + * @see org.jenkinsci.remoting.RoleSensitive */ +@Deprecated public abstract class SlaveToMasterFileCallable implements FileCallable { + + public static final Logger LOGGER = Logger.getLogger(SlaveToMasterFileCallable.class.getName()); + @Override public void checkRoles(RoleChecker checker) throws SecurityException { + warnOnController(); checker.check(this, Roles.MASTER); } + + protected Object readResolve() { + warnOnController(); + return this; + } + + private void warnOnController() { + if (JenkinsJVM.isJenkinsJVM() && (Main.isUnitTest || Main.isDevelopmentMode)) { // No point in spamming admins who cannot do anything + LOGGER.log(Level.WARNING, "SlaveToMasterFileCallable is deprecated. '" + this + "' should be replaced. See https://www.jenkins.io/doc/developer/security/remoting-callables/"); + } + } + private static final long serialVersionUID = 1L; } From ce5100c963bfe09f0f4a502856c6608f90c15bc5 Mon Sep 17 00:00:00 2001 From: NotMyFault Date: Wed, 10 Nov 2021 11:30:07 +0100 Subject: [PATCH 02/58] fix: Add missing SVG for parameterized jobs --- .../java/org/jenkins/ui/icon/IconSet.java | 3 +- .../images/24x24/document-properties.gif | Bin 1257 -> 0 bytes .../images/24x24/document-properties.png | Bin 732 -> 0 bytes .../images/svgs/document-properties.svg | 114 ++++++++++++++++++ 4 files changed, 115 insertions(+), 2 deletions(-) delete mode 100644 war/src/main/webapp/images/24x24/document-properties.gif delete mode 100644 war/src/main/webapp/images/24x24/document-properties.png create mode 100644 war/src/main/webapp/images/svgs/document-properties.svg diff --git a/core/src/main/java/org/jenkins/ui/icon/IconSet.java b/core/src/main/java/org/jenkins/ui/icon/IconSet.java index ff20a4f1a4b3..6d40bc819009 100644 --- a/core/src/main/java/org/jenkins/ui/icon/IconSet.java +++ b/core/src/main/java/org/jenkins/ui/icon/IconSet.java @@ -318,7 +318,6 @@ private static String toNormalizedIconUrl(String url) { icons.addIcon(new Icon("icon-computer-flash icon-md", "24x24/computer-flash.gif", Icon.ICON_MEDIUM_STYLE)); icons.addIcon(new BuildStatusIcon("icon-disabled icon-md", "build-status/build-status-sprite.svg#last-disabled", Icon.ICON_MEDIUM_STYLE)); icons.addIcon(new BuildStatusIcon("icon-disabled-anime icon-md", "build-status/build-status-sprite.svg#last-disabled", Icon.ICON_MEDIUM_STYLE, true)); - icons.addIcon(new Icon("icon-document-properties icon-md", "24x24/document-properties.gif", Icon.ICON_MEDIUM_STYLE)); icons.addIcon(new Icon("icon-empty icon-md", "24x24/empty.gif", Icon.ICON_MEDIUM_STYLE)); icons.addIcon(new Icon("icon-green icon-md", "24x24/green.gif", Icon.ICON_MEDIUM_STYLE)); icons.addIcon(new Icon("icon-green-anime icon-md", "24x24/green_anime.gif", Icon.ICON_MEDIUM_STYLE)); @@ -335,7 +334,6 @@ private static String toNormalizedIconUrl(String url) { icons.addIcon(new BuildStatusIcon("icon-red-anime icon-md", "build-status/build-status-sprite.svg#last-failed", Icon.ICON_MEDIUM_STYLE, true)); icons.addIcon(new BuildStatusIcon("icon-yellow icon-md", "build-status/build-status-sprite.svg#last-unstable", Icon.ICON_MEDIUM_STYLE)); icons.addIcon(new BuildStatusIcon("icon-yellow-anime icon-md", "build-status/build-status-sprite.svg#last-unstable", Icon.ICON_MEDIUM_STYLE, true)); - icons.addIcon(new Icon("icon-document-properties icon-md", "24x24/document-properties.png", Icon.ICON_MEDIUM_STYLE)); icons.addIcon(new Icon("icon-empty icon-md", "24x24/empty.png", Icon.ICON_MEDIUM_STYLE)); icons.addIcon(new Icon("icon-grey icon-md", "24x24/grey.png", Icon.ICON_MEDIUM_STYLE)); @@ -471,6 +469,7 @@ private static void initializeSVGs() { images.add("user"); images.add("video"); images.add("warning"); + images.add("document-properties"); Map materialIcons = new HashMap<>(); materialIcons.put("help", "svg-sprite-action-symbol.svg#ic_help_24px"); diff --git a/war/src/main/webapp/images/24x24/document-properties.gif b/war/src/main/webapp/images/24x24/document-properties.gif deleted file mode 100644 index 788e138ea195145961a59b8a42a76854314569b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1257 zcmdthiBDSv90%}rBiI3xa!k?T!hns!K(jd#aT->JK(`SaugpXfijYk+lLbjIY8qiq zQHNuAOy`b{YcRUmz;GbPdcx}~Jt&lx9`EdXee{MN924;M-|(AHzWM(CfL|rA;*}CX z1|x&vnqd6=>(v{#{^;xyb@z1k^mg{#?HU;98xZ&ZCAlvdd@wj9dHDC>qmkj!u}5PQ zqvPZMOg~*t(Ht-W!6*bHFpRwdD>Vz;Sj1e%3A(#`v3A$07MxZ!};3PdToWz_2>LgJofjjB#!iX50bm0W)BwYkS z;Ut9ml-s-Hq50fi zuiG1IkJsn%`DtFC-{%i_{Q+Mf;PVH4ff#>a*&hu0gUf+s+DdSR7FrI4LM!2wP!e@X)j#q zCXB{Hh=F_Z>x4>E~}qnv>%)l;vMstCR2Ibcs2@&g2Y!TvCpx{W$mF2nHUK z07nvwhAr7WMfW)u6iF|&^YP=UEWY$w&A00WgDGa8c)Ip{iIKaLE#W2|60_NiWB&oZ C({JPe diff --git a/war/src/main/webapp/images/24x24/document-properties.png b/war/src/main/webapp/images/24x24/document-properties.png deleted file mode 100644 index b35f26b28798cb378490d8bd020b930687d4a37e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 732 zcmV<20wev2P)Oxw+*GkQP~c`#YT-T^Na6IU7l* zrze`BDnjwx>{+0)vhs|X0YnKBgF=?1Mnd30R#(-Ky}GhWk*B7{pE3tS2m6H(WC{>e zPfm`D3QAg+zz!VVY!WhQo#2le%>gM*w<7ba7 z(*}WUZSBb*7PHvF46YQGs;Y&BMFj=dpXw)=quzye@PEXJH5_flZ$e$ijm|8?(?B4F~=P#eh=;`gvdfwjNUTtml*SR_R zIRV5ZhVV);g}Sx%wZekJj~_qKG?U2`78aalPU`C#XqtHYne=1NB7vY4YHVyeJ~_U5 z^G0u9pY2$L@Ukwz84$K+R@qZ;NiWL)Of%+3$eSq+qA6r@7~eu@bI9ozh6;K zZO6Ra*|EO1USC&F{@mlQC~8kn4xBy!b0B(R z*49^5O@&_xCDKX_4Gw{dnyQzTR@}dLmqCB5xw$!tv+aBvy-nZ;5q}3%L~FY^v{bVI O0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Document Properties + + + document + settings + preferences + properties + tweak + + + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 485c9b096635ca4261f4581833e4a733e80abfe0 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Tue, 14 Dec 2021 10:49:51 -0800 Subject: [PATCH 03/58] Fix `XXE_SAXPARSER` SpotBugs violations (#6052) --- core/src/main/java/hudson/PluginManager.java | 6 +++++- core/src/main/java/hudson/XmlFile.java | 13 ++++++------- core/src/test/java/hudson/PluginManagerTest.java | 2 +- src/spotbugs/spotbugs-excludes.xml | 7 ------- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 3dbb793b19c9..528cdce64b32 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -110,6 +110,7 @@ import java.util.stream.Collectors; import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.xml.XMLConstants; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import jenkins.ClassLoaderReflectionToolkit; @@ -2108,7 +2109,10 @@ public HttpResponse doInstallNecessaryPlugins(StaplerRequest req) throws IOExcep public Map parseRequestedPlugins(InputStream configXml) throws IOException { final Map requestedPlugins = new TreeMap<>(); try { - SAXParserFactory.newInstance().newSAXParser().parse(configXml, new DefaultHandler() { + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + spf.newSAXParser().parse(configXml, new DefaultHandler() { @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { String plugin = attributes.getValue("plugin"); if (plugin == null) { diff --git a/core/src/main/java/hudson/XmlFile.java b/core/src/main/java/hudson/XmlFile.java index 9a36d4484d20..46e70835a23a 100644 --- a/core/src/main/java/hudson/XmlFile.java +++ b/core/src/main/java/hudson/XmlFile.java @@ -52,6 +52,7 @@ import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; +import javax.xml.XMLConstants; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.io.IOUtils; @@ -320,7 +321,11 @@ class Eureka extends SAXException { try (InputStream in = Files.newInputStream(file.toPath())) { InputSource input = new InputSource(file.toURI().toASCIIString()); input.setByteStream(in); - JAXP.newSAXParser().parse(input,new DefaultHandler() { + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + spf.setNamespaceAware(true); + spf.newSAXParser().parse(input, new DefaultHandler() { private Locator loc; @Override public void setDocumentLocator(Locator locator) { @@ -372,13 +377,7 @@ private void attempt() throws Eureka { private static final Logger LOGGER = Logger.getLogger(XmlFile.class.getName()); - private static final SAXParserFactory JAXP = SAXParserFactory.newInstance(); - private static final HierarchicalStreamDriver DEFAULT_DRIVER = XStream2.getDefaultDriver(); private static final XStream DEFAULT_XSTREAM = new XStream2(DEFAULT_DRIVER); - - static { - JAXP.setNamespaceAware(true); - } } diff --git a/core/src/test/java/hudson/PluginManagerTest.java b/core/src/test/java/hudson/PluginManagerTest.java index af9aef0135b5..9fcb0310d55d 100644 --- a/core/src/test/java/hudson/PluginManagerTest.java +++ b/core/src/test/java/hudson/PluginManagerTest.java @@ -85,7 +85,7 @@ public void parseInvalidRequestedPlugins() throws Exception { () -> pluginManager.parseRequestedPlugins(new ByteArrayInputStream(evilXML.getBytes())), "XML contains an external entity, but no exception was thrown."); assertThat(ex.getCause(), instanceOf(SAXException.class)); - assertThat(ex.getCause().getMessage(), containsString("Refusing to resolve entity with publicId(null) and systemId (file:///)")); + assertThat(ex.getCause().getMessage(), containsString("DOCTYPE is disallowed")); } @Test diff --git a/src/spotbugs/spotbugs-excludes.xml b/src/spotbugs/spotbugs-excludes.xml index 19f12efdc543..01404a8bec24 100644 --- a/src/spotbugs/spotbugs-excludes.xml +++ b/src/spotbugs/spotbugs-excludes.xml @@ -667,13 +667,6 @@ - - - - - - - From bf2a9b3b2ef26455e28067884113cc355648151a Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Tue, 14 Dec 2021 13:57:21 -0800 Subject: [PATCH 04/58] Bump `jenkins-test-harness` from 1666.vd1360abbfe9e to 1670.v5a5dbf551c41 (#6054) --- test/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pom.xml b/test/pom.xml index 62d2526a4840..28924f241558 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -81,7 +81,7 @@ THE SOFTWARE. ${project.groupId} jenkins-test-harness - 1666.vd1360abbfe9e + 1670.v5a5dbf551c41 test From 80b54ce0cc0cacc73c5ea81e083fd3f8b2d179e8 Mon Sep 17 00:00:00 2001 From: offa Date: Wed, 15 Dec 2021 15:14:50 +0000 Subject: [PATCH 05/58] Add nullability annotations to parameter definitions (#5945) --- .../hudson/model/BooleanParameterDefinition.java | 6 ++++-- .../hudson/model/ChoiceParameterDefinition.java | 13 +++++++------ .../java/hudson/model/FileParameterDefinition.java | 6 ++++-- .../hudson/model/PasswordParameterDefinition.java | 7 +++++-- .../java/hudson/model/RunParameterDefinition.java | 8 +++++--- .../hudson/model/SimpleParameterDefinition.java | 6 ++++-- .../hudson/model/StringParameterDefinition.java | 12 +++++++----- .../java/hudson/model/TextParameterDefinition.java | 6 ++++-- 8 files changed, 40 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/hudson/model/BooleanParameterDefinition.java b/core/src/main/java/hudson/model/BooleanParameterDefinition.java index 99e0ced4bb6a..2c1cd6279110 100644 --- a/core/src/main/java/hudson/model/BooleanParameterDefinition.java +++ b/core/src/main/java/hudson/model/BooleanParameterDefinition.java @@ -23,6 +23,8 @@ */ package hudson.model; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import java.util.Objects; import net.sf.json.JSONObject; @@ -43,11 +45,11 @@ public class BooleanParameterDefinition extends SimpleParameterDefinition { * @since 2.281 */ @DataBoundConstructor - public BooleanParameterDefinition(String name) { + public BooleanParameterDefinition(@NonNull String name) { super(name); } - public BooleanParameterDefinition(String name, boolean defaultValue, String description) { + public BooleanParameterDefinition(@NonNull String name, boolean defaultValue, @CheckForNull String description) { this(name); setDefaultValue(defaultValue); setDescription(description); diff --git a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java index 286d12505a76..c83d27de63b1 100644 --- a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java +++ b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java @@ -36,26 +36,26 @@ public class ChoiceParameterDefinition extends SimpleParameterDefinition { private /* quasi-final */ List choices; private final String defaultValue; - public static boolean areValidChoices(String choices) { + public static boolean areValidChoices(@NonNull String choices) { String strippedChoices = choices.trim(); return !StringUtils.isEmpty(strippedChoices) && strippedChoices.split(CHOICES_DELIMITER).length > 0; } - public ChoiceParameterDefinition(@NonNull String name, @NonNull String choices, String description) { + public ChoiceParameterDefinition(@NonNull String name, @NonNull String choices, @CheckForNull String description) { super(name, description); setChoicesText(choices); defaultValue = null; } - public ChoiceParameterDefinition(@NonNull String name, @NonNull String[] choices, String description) { + public ChoiceParameterDefinition(@NonNull String name, @NonNull String[] choices, @CheckForNull String description) { super(name, description); this.choices = Stream.of(choices).map(Util::fixNull).collect(Collectors.toCollection(ArrayList::new)); defaultValue = null; } - private ChoiceParameterDefinition(@NonNull String name, @NonNull List choices, String defaultValue, String description) { + private ChoiceParameterDefinition(@NonNull String name, @NonNull List choices, String defaultValue, @CheckForNull String description) { super(name, description); - this.choices = choices; + this.choices = Util.fixNull(choices); this.defaultValue = defaultValue; } @@ -110,7 +110,7 @@ public void setChoices(Object choices) { throw new IllegalArgumentException("expected String or List, but got " + choices.getClass().getName()); } - private void setChoicesText(String choices) { + private void setChoicesText(@NonNull String choices) { this.choices = Arrays.asList(choices.split(CHOICES_DELIMITER)); } @@ -124,6 +124,7 @@ public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) { } } + @NonNull @Exported public List getChoices() { return choices; diff --git a/core/src/main/java/hudson/model/FileParameterDefinition.java b/core/src/main/java/hudson/model/FileParameterDefinition.java index 84e29f67b1b3..2d40c2d2fcab 100644 --- a/core/src/main/java/hudson/model/FileParameterDefinition.java +++ b/core/src/main/java/hudson/model/FileParameterDefinition.java @@ -23,6 +23,8 @@ */ package hudson.model; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.cli.CLICommand; import java.io.File; @@ -50,11 +52,11 @@ public class FileParameterDefinition extends ParameterDefinition { * @since 2.281 */ @DataBoundConstructor - public FileParameterDefinition(String name) { + public FileParameterDefinition(@NonNull String name) { super(name); } - public FileParameterDefinition(String name, String description) { + public FileParameterDefinition(@NonNull String name, @CheckForNull String description) { this(name); setDescription(description); } diff --git a/core/src/main/java/hudson/model/PasswordParameterDefinition.java b/core/src/main/java/hudson/model/PasswordParameterDefinition.java index 05008c365108..b5422725984a 100644 --- a/core/src/main/java/hudson/model/PasswordParameterDefinition.java +++ b/core/src/main/java/hudson/model/PasswordParameterDefinition.java @@ -23,6 +23,8 @@ */ package hudson.model; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.util.Secret; import java.util.Objects; @@ -48,14 +50,14 @@ public class PasswordParameterDefinition extends SimpleParameterDefinition { private Secret defaultValue; @Deprecated - public PasswordParameterDefinition(String name, String defaultValue, String description) { + public PasswordParameterDefinition(@NonNull String name, @CheckForNull String defaultValue, @CheckForNull String description) { super(name, description); this.defaultValue = Secret.fromString(defaultValue); } // TODO consider switching @DataBoundConstructor to a PasswordParameterDefinition(String) overload @DataBoundConstructor - public PasswordParameterDefinition(String name, Secret defaultValueAsSecret, String description) { + public PasswordParameterDefinition(@NonNull String name, @CheckForNull Secret defaultValueAsSecret, @CheckForNull String description) { super(name, description); this.defaultValue = defaultValueAsSecret; } @@ -90,6 +92,7 @@ public ParameterValue getDefaultParameterValue() { return new PasswordParameterValue(getName(), getDefaultValue(), getDescription()); } + @NonNull public String getDefaultValue() { return Secret.toString(defaultValue); } diff --git a/core/src/main/java/hudson/model/RunParameterDefinition.java b/core/src/main/java/hudson/model/RunParameterDefinition.java index 33b5d64f3bf4..56e6f4e77594 100644 --- a/core/src/main/java/hudson/model/RunParameterDefinition.java +++ b/core/src/main/java/hudson/model/RunParameterDefinition.java @@ -23,6 +23,8 @@ */ package hudson.model; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.util.EnumConverter; import hudson.util.RunList; @@ -67,7 +69,7 @@ public String getName() { * @since 1.517 */ @DataBoundConstructor - public RunParameterDefinition(String name, String projectName, String description, RunParameterFilter filter) { + public RunParameterDefinition(@NonNull String name, String projectName, @CheckForNull String description, @CheckForNull RunParameterFilter filter) { super(name, description); this.projectName = projectName; this.runId = null; @@ -78,12 +80,12 @@ public RunParameterDefinition(String name, String projectName, String descriptio * @deprecated as of 1.517 */ @Deprecated - public RunParameterDefinition(String name, String projectName, String description) { + public RunParameterDefinition(@NonNull String name, String projectName, @CheckForNull String description) { // delegate to updated constructor with additional RunParameterFilter parameter defaulted to ALL. this(name, projectName, description, RunParameterFilter.ALL); } - private RunParameterDefinition(String name, String projectName, String runId, String description, RunParameterFilter filter) { + private RunParameterDefinition(@NonNull String name, String projectName, String runId, @CheckForNull String description, @CheckForNull RunParameterFilter filter) { super(name, description); this.projectName = projectName; this.runId = runId; diff --git a/core/src/main/java/hudson/model/SimpleParameterDefinition.java b/core/src/main/java/hudson/model/SimpleParameterDefinition.java index 0cbc6ddc6817..0be175bb4a86 100644 --- a/core/src/main/java/hudson/model/SimpleParameterDefinition.java +++ b/core/src/main/java/hudson/model/SimpleParameterDefinition.java @@ -1,5 +1,7 @@ package hudson.model; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.cli.CLICommand; import java.io.IOException; import org.kohsuke.stapler.DataBoundConstructor; @@ -11,7 +13,7 @@ * @author Kohsuke Kawaguchi */ public abstract class SimpleParameterDefinition extends ParameterDefinition { - protected SimpleParameterDefinition(String name) { + protected SimpleParameterDefinition(@NonNull String name) { super(name); } @@ -19,7 +21,7 @@ protected SimpleParameterDefinition(String name) { * @deprecated Prefer {@link #SimpleParameterDefinition(String)} with a {@link DataBoundConstructor} and allow {@link #setDescription} to be used as needed */ @Deprecated - protected SimpleParameterDefinition(String name, String description) { + protected SimpleParameterDefinition(@NonNull String name, @CheckForNull String description) { super(name, description); } diff --git a/core/src/main/java/hudson/model/StringParameterDefinition.java b/core/src/main/java/hudson/model/StringParameterDefinition.java index ff44daa3540d..23a7c2794baf 100644 --- a/core/src/main/java/hudson/model/StringParameterDefinition.java +++ b/core/src/main/java/hudson/model/StringParameterDefinition.java @@ -23,6 +23,7 @@ */ package hudson.model; +import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import hudson.Util; @@ -47,24 +48,24 @@ public class StringParameterDefinition extends SimpleParameterDefinition { * @since 2.281 */ @DataBoundConstructor - public StringParameterDefinition(String name) { + public StringParameterDefinition(@NonNull String name) { super(name); } - public StringParameterDefinition(String name, String defaultValue, String description, boolean trim) { + public StringParameterDefinition(@NonNull String name, @CheckForNull String defaultValue, @CheckForNull String description, boolean trim) { this(name); setDefaultValue(defaultValue); setDescription(description); setTrim(trim); } - public StringParameterDefinition(String name, String defaultValue, String description) { + public StringParameterDefinition(@NonNull String name, @CheckForNull String defaultValue, @CheckForNull String description) { this(name); setDefaultValue(defaultValue); setDescription(description); } - public StringParameterDefinition(String name, String defaultValue) { + public StringParameterDefinition(@NonNull String name, @CheckForNull String defaultValue) { this(name); setDefaultValue(defaultValue); } @@ -79,6 +80,7 @@ public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) { } } + @NonNull public String getDefaultValue() { return defaultValue; } @@ -96,7 +98,7 @@ public String getDefaultValue4Build() { } @DataBoundSetter - public void setDefaultValue(String defaultValue) { + public void setDefaultValue(@CheckForNull String defaultValue) { this.defaultValue = Util.fixEmpty(defaultValue); } diff --git a/core/src/main/java/hudson/model/TextParameterDefinition.java b/core/src/main/java/hudson/model/TextParameterDefinition.java index 5235dbd33b3d..d639b88b6d92 100644 --- a/core/src/main/java/hudson/model/TextParameterDefinition.java +++ b/core/src/main/java/hudson/model/TextParameterDefinition.java @@ -23,6 +23,8 @@ */ package hudson.model; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; import java.util.Objects; import net.sf.json.JSONObject; @@ -39,11 +41,11 @@ public class TextParameterDefinition extends StringParameterDefinition { * @since 2.281 */ @DataBoundConstructor - public TextParameterDefinition(String name) { + public TextParameterDefinition(@NonNull String name) { super(name); } - public TextParameterDefinition(String name, String defaultValue, String description) { + public TextParameterDefinition(@NonNull String name, @CheckForNull String defaultValue, @CheckForNull String description) { this(name); setDefaultValue(defaultValue); setDescription(description); From f19cf6dd6841ef06ea100e728f7d1e26be0b0e9e Mon Sep 17 00:00:00 2001 From: Marat Talipov Date: Wed, 15 Dec 2021 20:15:22 +0500 Subject: [PATCH 06/58] Translate `NewView` to ru (#5997) Co-authored-by: Marat Talipov --- core/src/main/resources/jenkins/model/Messages_ru.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/resources/jenkins/model/Messages_ru.properties b/core/src/main/resources/jenkins/model/Messages_ru.properties index 5fe1b314824c..255a60bca237 100644 --- a/core/src/main/resources/jenkins/model/Messages_ru.properties +++ b/core/src/main/resources/jenkins/model/Messages_ru.properties @@ -55,3 +55,4 @@ PatternProjectNamingStrategy.NamePatternInvalidSyntax=\u041D\u0435\u043A\u043E\u ParameterizedJobMixIn.build_with_parameters=\u0421\u043E\u0431\u0440\u0430\u0442\u044C \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438 CLI.disable-job.shortDescription=\u0417\u0430\u043F\u0440\u0435\u0442\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 CLI.enable-job.shortDescription=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 +NewViewLink.NewView=\u041d\u043e\u0432\u043e\u0435\u0020\u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \ No newline at end of file From ad7dbbd4cc2c641150390d2cb3ce7e1f6c32c479 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 15 Dec 2021 10:24:45 -0800 Subject: [PATCH 07/58] Bump Groovy from 2.4.12 to 2.4.21 (#5939) --- .github/dependabot.yml | 1 + bom/pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index aef9bce64eff..9cec7116807d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,6 +12,7 @@ updates: # see https://github.com/jenkinsci/jenkins/pull/5112#issuecomment-744429487 and https://github.com/jenkinsci/jenkins/pull/5116#issuecomment-744526638 # it would be good to update it at some point, but requires significant testing - dependency-name: "org.codehaus.groovy:groovy-all" + versions: [">=2.5.0"] # see https://github.com/jenkinsci/jenkins/pull/5184 should be updated with groovy-all - dependency-name: "org.fusesource.jansi:jansi" # see https://github.com/jenkinsci/jenkins/pull/5144#pullrequestreview-559661934 diff --git a/bom/pom.xml b/bom/pom.xml index 2584d153c91c..bb8ab745f8fd 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -42,7 +42,7 @@ THE SOFTWARE. 9.2 1.7.32 1593.v0e838714faae - 2.4.12 + 2.4.21 From 960925fc5055208d2b64a0388161ddcbcd5916aa Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 15 Dec 2021 10:25:02 -0800 Subject: [PATCH 08/58] Fix various test bugs seen when running on Windows (#6029) --- .../test/java/hudson/PluginManagerTest.java | 2 +- .../java/hudson/cli/BuildCommandTest.java | 1 + .../hudson/cli/DisablePluginCommandTest.java | 6 ++++-- .../java/hudson/cli/RunRangeCommand2Test.java | 4 +++- .../java/hudson/model/AbstractItemTest.java | 3 ++- .../test/java/hudson/model/JobQueueTest.java | 11 +++++++--- test/src/test/java/hudson/model/JobTest.java | 6 +++--- .../test/java/hudson/model/ProjectTest.java | 3 ++- .../test/java/hudson/model/RunMapTest.java | 2 ++ .../java/hudson/tasks/BuildTriggerTest.java | 13 ++++++++---- .../java/hudson/util/ProcessTreeTest.java | 5 ++--- .../jenkins/cli/StopBuildsCommandTest.java | 15 ++++++++++--- .../jenkins/security/Security2455Test.java | 18 +++++++--------- .../security/stapler/Security914Test.java | 21 +++++++++---------- 14 files changed, 67 insertions(+), 43 deletions(-) diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index 0455fd8c44a6..adc30e6aa6cc 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -194,7 +194,7 @@ public void startPlugin(PluginWrapper plugin) throws Exception { // plugins should be already visible in the UberClassLoader assertFalse(activePlugins.isEmpty()); - uberClassLoader.loadClass("hudson.plugins.tasks.Messages"); + assertNotNull(uberClassLoader.loadClass("htmlpublisher.HtmlPublisher")); super.startPlugin(plugin); } diff --git a/test/src/test/java/hudson/cli/BuildCommandTest.java b/test/src/test/java/hudson/cli/BuildCommandTest.java index 7e03e9fe0baf..c187a5205a84 100644 --- a/test/src/test/java/hudson/cli/BuildCommandTest.java +++ b/test/src/test/java/hudson/cli/BuildCommandTest.java @@ -107,6 +107,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen started.block(); assertTrue(p.getBuildByNumber(1).isBuilding()); completed.signal(); + j.waitForCompletion(p.getBuildByNumber(1)); } /** diff --git a/test/src/test/java/hudson/cli/DisablePluginCommandTest.java b/test/src/test/java/hudson/cli/DisablePluginCommandTest.java index d0d8bb69b678..d95055bab95c 100644 --- a/test/src/test/java/hudson/cli/DisablePluginCommandTest.java +++ b/test/src/test/java/hudson/cli/DisablePluginCommandTest.java @@ -148,10 +148,11 @@ public void restartAfterDisable() { @Test @Issue("JENKINS-27177") @WithPlugin("dependee-0.0.2.hpi") - public void notRestartAfterDisablePluginWithoutArgumentRestart() { + public void notRestartAfterDisablePluginWithoutArgumentRestart() throws Exception { assertThat(disablePluginsCLiCommand("dependee"), succeeded()); assertPluginDisabled("dependee"); assertJenkinsNotInQuietMode(); + j.waitUntilNoActivity(); } /** @@ -171,7 +172,7 @@ public void returnCodeDisableInvalidPlugin() { @Test @Issue("JENKINS-27177") @WithPlugin("dependee-0.0.2.hpi") - public void disableAlreadyDisabledPluginNotRestart() throws IOException { + public void disableAlreadyDisabledPluginNotRestart() throws Exception { // Disable before the command call disablePlugin("dependee"); @@ -179,6 +180,7 @@ public void disableAlreadyDisabledPluginNotRestart() throws IOException { assertThat(disablePluginsCLiCommand("-restart", "dependee"), succeeded()); assertPluginDisabled("dependee"); assertJenkinsNotInQuietMode(); + j.waitUntilNoActivity(); } /** diff --git a/test/src/test/java/hudson/cli/RunRangeCommand2Test.java b/test/src/test/java/hudson/cli/RunRangeCommand2Test.java index a5826875ce5a..99cfdfb70775 100644 --- a/test/src/test/java/hudson/cli/RunRangeCommand2Test.java +++ b/test/src/test/java/hudson/cli/RunRangeCommand2Test.java @@ -32,6 +32,7 @@ import static org.hamcrest.Matchers.equalTo; import hudson.Functions; +import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import hudson.model.Item; import hudson.model.labels.LabelAtom; @@ -83,7 +84,7 @@ public class RunRangeCommand2Test { @Test public void dummyRangeShouldSuccessEvenTheBuildIsRunning() throws Exception { FreeStyleProject project = j.createFreeStyleProject("aProject"); project.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo 1\r\nping -n 10 127.0.0.1 >nul") : new Shell("echo 1\nsleep 10s")); - assertThat("Job wasn't scheduled properly", project.scheduleBuild(0), equalTo(true)); + FreeStyleBuild build = project.scheduleBuild2(0).waitForStart(); // Wait until classProject is started (at least 1s) while(!project.isBuilding()) { @@ -101,6 +102,7 @@ public class RunRangeCommand2Test { .invokeWithArgs("aProject", "1"); assertThat(result, succeeded()); assertThat(result.stdout(), containsString("Builds: 1" + System.lineSeparator())); + j.waitForCompletion(build); } @Test public void dummyRangeShouldSuccessEvenTheBuildIsStuckInTheQueue() throws Exception { diff --git a/test/src/test/java/hudson/model/AbstractItemTest.java b/test/src/test/java/hudson/model/AbstractItemTest.java index 525959a3e3a9..96c91a719fc2 100644 --- a/test/src/test/java/hudson/model/AbstractItemTest.java +++ b/test/src/test/java/hudson/model/AbstractItemTest.java @@ -76,8 +76,9 @@ public void checkRenameValidity() throws Exception { j.jenkins.setProjectNamingStrategy(new ProjectNamingStrategy.PatternProjectNamingStrategy("bar", "", false)); assertThat(checkNameAndReturnError(p, "foo1"), equalTo(jenkins.model.Messages.Hudson_JobNameConventionNotApplyed("foo1", "bar"))); - p.scheduleBuild2(0).waitForStart(); + FreeStyleBuild b = p.scheduleBuild2(0).waitForStart(); assertThat(checkNameAndReturnError(p, "bar"), equalTo(Messages.Job_NoRenameWhileBuilding())); + j.assertBuildStatusSuccess(j.waitForCompletion(b)); } @Test diff --git a/test/src/test/java/hudson/model/JobQueueTest.java b/test/src/test/java/hudson/model/JobQueueTest.java index 6da7bb0f4f3e..8c7c7f503da9 100644 --- a/test/src/test/java/hudson/model/JobQueueTest.java +++ b/test/src/test/java/hudson/model/JobQueueTest.java @@ -7,6 +7,7 @@ import hudson.model.Queue.BlockedItem; import hudson.model.Queue.WaitingItem; import hudson.model.listeners.RunListener; +import hudson.model.queue.QueueTaskFuture; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -68,7 +69,7 @@ public void buildPendingWhenBuildInPostProduction() throws Exception { project.getBuildersList().add(new SleepBuilder(1000)); //Kick the first Build - project.scheduleBuild2(1); + FreeStyleBuild b1 = project.scheduleBuild2(1).waitForStart(); int count =0; //Now, Wait for run to be in POST_PRODUCTION stage while(!JobQueueTest.fireCompletedFlag && count<100) { @@ -76,9 +77,10 @@ public void buildPendingWhenBuildInPostProduction() throws Exception { count++; } + QueueTaskFuture b2 = null; if(JobQueueTest.fireCompletedFlag) { //Schedule the build for the project and this build should be in Queue since the state is POST_PRODUCTION - project.scheduleBuild2(0); + b2 = project.scheduleBuild2(0); assertTrue(project.isInQueue()); //That means it's pending or it's waiting or blocked j.jenkins.getQueue().maintain(); while(j.jenkins.getQueue().getItem(project) instanceof WaitingItem) { @@ -106,6 +108,9 @@ public void buildPendingWhenBuildInPostProduction() throws Exception { else { fail("The maximum attempts for checking if the job is in COMPLETED State have reached"); } - Thread.sleep(1000); //Sleep till job completes. + j.assertBuildStatusSuccess(b1); + if (b2 != null) { + j.assertBuildStatusSuccess(b2); + } } } diff --git a/test/src/test/java/hudson/model/JobTest.java b/test/src/test/java/hudson/model/JobTest.java index 6c73c911bc59..bc3f25048695 100644 --- a/test/src/test/java/hudson/model/JobTest.java +++ b/test/src/test/java/hudson/model/JobTest.java @@ -507,9 +507,9 @@ public void interruptOnDelete() throws Exception { p.delete(); long end = System.nanoTime(); assertThat(end - start, Matchers.lessThan(TimeUnit.SECONDS.toNanos(1))); - assertThat(build1.getResult(), Matchers.is(Result.ABORTED)); - assertThat(build2.getResult(), Matchers.is(Result.ABORTED)); - assertThat(build3.isCancelled(), Matchers.is(true)); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(build1)); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(build2)); + assertTrue(build3.isCancelled()); } @Issue("SECURITY-1868") diff --git a/test/src/test/java/hudson/model/ProjectTest.java b/test/src/test/java/hudson/model/ProjectTest.java index e9a37de1dd70..85e350f90b14 100644 --- a/test/src/test/java/hudson/model/ProjectTest.java +++ b/test/src/test/java/hudson/model/ProjectTest.java @@ -388,8 +388,9 @@ public void testGetCauseOfBlockage() throws Exception { b2.get(); downstream.setBlockBuildWhenUpstreamBuilding(true); - waitForStart(p); + QueueTaskFuture b3 = waitForStart(p); assertInstanceOf("Build can not start because build of upstream project has not finished.", downstream.getCauseOfBlockage(), BecauseOfUpstreamBuildInProgress.class); + b3.get(); } private static final Logger LOGGER = Logger.getLogger(ProjectTest.class.getName()); diff --git a/test/src/test/java/hudson/model/RunMapTest.java b/test/src/test/java/hudson/model/RunMapTest.java index 4d1bc00cd5b6..3dd69e257a40 100644 --- a/test/src/test/java/hudson/model/RunMapTest.java +++ b/test/src/test/java/hudson/model/RunMapTest.java @@ -59,6 +59,8 @@ public class RunMapTest { b1 = p.getBuildByNumber(1); assertSame(b1.getNextBuild(), b2); assertSame(b2.getPreviousBuild(), b1); + b2.doStop(); + r.assertBuildStatus(Result.ABORTED, r.waitForCompletion(b2)); } @Issue("JENKINS-27530") diff --git a/test/src/test/java/hudson/tasks/BuildTriggerTest.java b/test/src/test/java/hudson/tasks/BuildTriggerTest.java index 3334658a47c7..a7efe039ae7a 100644 --- a/test/src/test/java/hudson/tasks/BuildTriggerTest.java +++ b/test/src/test/java/hudson/tasks/BuildTriggerTest.java @@ -105,11 +105,13 @@ private void doTriggerTest(boolean evenWhenUnstable, Result triggerResult, // First build should not trigger downstream job FreeStyleBuild b = p.scheduleBuild2(0).get(); assertNoDownstreamBuild(dp, b); + j.assertBuildStatus(dontTriggerResult, b); // Next build should trigger downstream job p.getBuildersList().replace(new MockBuilder(triggerResult)); b = p.scheduleBuild2(0).get(); - assertDownstreamBuild(dp, b); + j.assertBuildStatus(triggerResult, b); + j.assertBuildStatusSuccess(j.waitForCompletion(assertDownstreamBuild(dp, b))); } private void assertNoDownstreamBuild(FreeStyleProject dp, Run b) throws Exception { @@ -122,9 +124,12 @@ private void assertNoDownstreamBuild(FreeStyleProject dp, Run b) throws Exc private FreeStyleBuild assertDownstreamBuild(FreeStyleProject dp, Run b) throws Exception { // Wait for downstream build - for (int i = 0; dp.getLastBuild()==null && i < 20; i++) Thread.sleep(100); - assertNotNull("downstream build didn't run.. upstream log: " + b.getLog(), dp.getLastBuild()); - return dp.getLastBuild(); + FreeStyleBuild result = null; + for (int i = 0; (result = dp.getLastBuild()) == null && i < 25; i++) { + Thread.sleep(250); + } + assertNotNull("downstream build didn't run.. upstream log: " + b.getLog(), result); + return result; } @Test diff --git a/test/src/test/java/hudson/util/ProcessTreeTest.java b/test/src/test/java/hudson/util/ProcessTreeTest.java index 81412b3434aa..aa187bb45181 100644 --- a/test/src/test/java/hudson/util/ProcessTreeTest.java +++ b/test/src/test/java/hudson/util/ProcessTreeTest.java @@ -56,12 +56,11 @@ public void manualAbortProcess() throws Exception { project.getBuildersList().add(new Maven("install", "maven")); // build the project, wait until tests are running, then cancel. - project.scheduleBuild2(0).waitForStart(); + FreeStyleBuild b = project.scheduleBuild2(0).waitForStart(); - FreeStyleBuild b = project.getLastBuild(); b.doStop(); - Thread.sleep(1000); + j.waitForCompletion(b); // will fail (at least on windows) if test process is still running b.getWorkspace().deleteRecursive(); diff --git a/test/src/test/java/jenkins/cli/StopBuildsCommandTest.java b/test/src/test/java/jenkins/cli/StopBuildsCommandTest.java index a5e607b85b0b..7de846f7bd3f 100644 --- a/test/src/test/java/jenkins/cli/StopBuildsCommandTest.java +++ b/test/src/test/java/jenkins/cli/StopBuildsCommandTest.java @@ -30,6 +30,7 @@ import hudson.Functions; import hudson.cli.CLICommand; import hudson.cli.CLICommandInvoker; +import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import hudson.model.Item; import hudson.tasks.BatchFile; @@ -129,7 +130,7 @@ public void shouldReportBuildStopError() throws Exception { grant(Jenkins.READ).everywhere().toEveryone(). grant(Item.READ).onItems(project).toEveryone(). grant(Item.CANCEL).onItems(project).toAuthenticated()); - project.scheduleBuild2(0).waitForStart(); + FreeStyleBuild build = project.scheduleBuild2(0).waitForStart(); final String stdout = runWith(Collections.singletonList(TEST_JOB_NAME)).stdout(); @@ -137,6 +138,9 @@ public void shouldReportBuildStopError() throws Exception { equalTo("Exception occurred while trying to stop build '#1' for job 'jobName'. " + "Exception class: AccessDeniedException3, message: anonymous is missing the Job/Cancel permission" + LN + "No builds stopped" + LN)); + + build.doStop(); + j.waitForCompletion(build); } @Test @@ -152,8 +156,8 @@ public void shouldStopSecondJobEvenIfFirstStopFailed() throws Exception { grant(Item.CANCEL).onItems(restrictedProject).toAuthenticated(). grant(Item.CANCEL).onItems(project).toEveryone()); - restrictedProject.scheduleBuild2(0).waitForStart(); - project.scheduleBuild2(0).waitForStart(); + FreeStyleBuild b1 = restrictedProject.scheduleBuild2(0).waitForStart(); + FreeStyleBuild b2 = project.scheduleBuild2(0).waitForStart(); final String stdout = runWith(asList(TEST_JOB_NAME, TEST_JOB_NAME_2)).stdout(); @@ -161,6 +165,11 @@ public void shouldStopSecondJobEvenIfFirstStopFailed() throws Exception { equalTo("Exception occurred while trying to stop build '#1' for job 'jobName'. " + "Exception class: AccessDeniedException3, message: anonymous is missing the Job/Cancel permission" + LN + "Build '#1' stopped for job 'jobName2'" + LN)); + + b1.doStop(); + b2.doStop(); + j.waitForCompletion(b1); + j.waitForCompletion(b2); } private CLICommandInvoker.Result runWith(final List jobNames) throws Exception { diff --git a/test/src/test/java/jenkins/security/Security2455Test.java b/test/src/test/java/jenkins/security/Security2455Test.java index 71613ceb60d3..77c5035ed875 100644 --- a/test/src/test/java/jenkins/security/Security2455Test.java +++ b/test/src/test/java/jenkins/security/Security2455Test.java @@ -36,6 +36,7 @@ import jenkins.SoloFilePathFilter; import jenkins.agents.AgentComputerUtil; import jenkins.security.s2m.AdminWhitelistRule; +import jenkins.security.s2m.RunningBuildFilePathFilter; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.NullOutputStream; import org.junit.Before; @@ -53,11 +54,8 @@ @Issue("SECURITY-2455") public class Security2455Test { - // TODO After merge, reference the class directly - private static final String SECURITY_2428_KILLSWITCH = "jenkins.security.s2m.RunningBuildFilePathFilter.FAIL"; - @Rule - public final FlagRule flagRule = FlagRule.systemProperty(SECURITY_2428_KILLSWITCH, "false"); + public final FlagRule flagRule = FlagRule.systemProperty(RunningBuildFilePathFilter.class.getName() + ".FAIL", "false"); @Rule public JenkinsRule j = new JenkinsRule(); @@ -78,13 +76,13 @@ public void mkdirsParentsTest() { final File buildStuff = new File(j.jenkins.getRootDir(), "job/nonexistent/builds/1/foo/bar"); logging.capture(10); SecurityException ex = assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkdirsParentsCallable(buildStuff))); - assertThat(logging, recorded(containsString("foo/bar"))); - assertThat(ex.getMessage(), not(containsString("foo/bar"))); // test error redaction + assertThat(logging, recorded(containsString("foo" + File.separator + "bar"))); + assertThat(ex.getMessage(), not(containsString("foo" + File.separator + "bar"))); // test error redaction SoloFilePathFilter.REDACT_ERRORS = false; try { SecurityException ex2 = assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkdirsParentsCallable(buildStuff))); - assertThat(ex2.getMessage(), containsString("foo/bar")); // test error redaction + assertThat(ex2.getMessage(), containsString("foo" + File.separator + "bar")); // test error redaction } finally { SoloFilePathFilter.REDACT_ERRORS = true; } @@ -493,7 +491,7 @@ public void testCreateTempFile() { assertTrue(cause instanceof SecurityException); assertThat(cause.getMessage(), not(containsString("prefix"))); // redacted assertThat(logging, recorded(containsString("'create'"))); - assertThat(logging, recorded(containsString("/prefix-security-check-dummy-suffix"))); + assertThat(logging, recorded(containsString(File.separator + "prefix-security-check-dummy-suffix"))); assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix"))); } private static class CreateTempFileCaller extends MasterToSlaveCallable { @@ -526,7 +524,7 @@ public void testCreateTextTempFile() { assertTrue(cause instanceof SecurityException); assertThat(cause.getMessage(), not(containsString("prefix"))); // redacted assertThat(logging, recorded(containsString("'create'"))); - assertThat(logging, recorded(containsString("/prefix-security-check-dummy-suffix"))); + assertThat(logging, recorded(containsString(File.separator + "prefix-security-check-dummy-suffix"))); assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix"))); } private static class CreateTextTempFileCaller extends MasterToSlaveCallable { @@ -558,7 +556,7 @@ public void testCreateTempDir() { assertTrue(cause instanceof SecurityException); assertThat(cause.getMessage(), not(containsString("prefix"))); // redacted assertThat(logging, recorded(containsString("'mkdirs'"))); - assertThat(logging, recorded(containsString("/prefix.suffix-security-test"))); // weird but that's what it looks like + assertThat(logging, recorded(containsString(File.separator + "prefix.suffix-security-test"))); // weird but that's what it looks like assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix"))); } private static class CreateTempDirCaller extends MasterToSlaveCallable { diff --git a/test/src/test/java/jenkins/security/stapler/Security914Test.java b/test/src/test/java/jenkins/security/stapler/Security914Test.java index 3bbeb36d98c5..3b66d4da6595 100644 --- a/test/src/test/java/jenkins/security/stapler/Security914Test.java +++ b/test/src/test/java/jenkins/security/stapler/Security914Test.java @@ -25,18 +25,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.WebRequest; import hudson.Functions; import java.net.HttpURLConnection; import java.net.URL; -import org.junit.Assume; 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.TestPluginManager; +import org.jvnet.hudson.test.recipes.WithPlugin; @Issue("SECURITY-914") public class Security914Test { @@ -45,12 +46,11 @@ public class Security914Test { public JenkinsRule j = new JenkinsRule(); @Test + @WithPlugin("credentials.hpi") public void cannotUseInvalidLocale_toTraverseFolder() throws Exception { - Assume.assumeTrue(Functions.isWindows()); + assumeTrue(Functions.isWindows()); - if (j.jenkins.pluginManager.getPlugin("credentials") == null) { - ((TestPluginManager) j.jenkins.pluginManager).installDetachedPlugin("credentials"); - } + assertNotNull(j.getPluginManager().getPlugin("credentials")); j.createWebClient().goTo("plugin/credentials/images/24x24/credentials.png", "image/png"); JenkinsRule.WebClient wc = j.createWebClient() @@ -67,12 +67,11 @@ public void cannotUseInvalidLocale_toTraverseFolder() throws Exception { } @Test + @WithPlugin("credentials.hpi") public void cannotUseInvalidLocale_toAnyFileInSystem() throws Exception { - Assume.assumeTrue(Functions.isWindows()); - - if (j.jenkins.pluginManager.getPlugin("credentials") == null) { - ((TestPluginManager) j.jenkins.pluginManager).installDetachedPlugin("credentials"); - } + assumeTrue(Functions.isWindows()); + + assertNotNull(j.getPluginManager().getPlugin("credentials")); j.createWebClient().goTo("plugin/credentials/images/24x24/credentials.png", "image/png"); JenkinsRule.WebClient wc = j.createWebClient() From c3e22d57db2961767dfb9c6a7adf64f324b952e4 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 15 Dec 2021 15:22:47 -0800 Subject: [PATCH 09/58] Fix `IS2_INCONSISTENT_SYNC` SpotBugs violations (#6047) --- core/src/main/java/hudson/model/Fingerprint.java | 5 ++++- core/src/main/java/hudson/model/View.java | 4 ++-- .../hudson/security/csrf/DefaultCrumbIssuer.java | 2 +- .../slaves/SimpleScheduledRetentionStrategy.java | 2 +- .../main/java/hudson/tasks/Fingerprinter.java | 4 +++- .../main/java/hudson/tools/ToolInstallation.java | 2 ++ core/src/main/java/hudson/util/ProcessTree.java | 2 +- .../hudson/util/SequentialExecutionQueue.java | 4 +++- .../main/java/jenkins/model/RunIdMigrator.java | 6 ++++-- .../jenkins/security/apitoken/ApiTokenStats.java | 2 ++ src/spotbugs/spotbugs-excludes.xml | 16 ---------------- 11 files changed, 23 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java index 3d49b1977b1f..19c8a17feced 100644 --- a/core/src/main/java/hudson/model/Fingerprint.java +++ b/core/src/main/java/hudson/model/Fingerprint.java @@ -33,6 +33,7 @@ import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.BulkChange; import hudson.Extension; import hudson.ExtensionList; @@ -961,7 +962,8 @@ public RangeSet getRangeSet(Job job) { /** * Gets the sorted list of job names where this jar is used. */ - public @NonNull List getJobs() { + @NonNull + public synchronized List getJobs() { List r = new ArrayList<>(usages.keySet()); Collections.sort(r); return r; @@ -1024,6 +1026,7 @@ public synchronized void add(@NonNull String jobFullName, int n) throws IOExcept } // JENKINS-49588 + @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "nothing should be competing with XStream during deserialization") protected Object readResolve() { if (usages == null) { usages = new Hashtable<>(); diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index fc94dd9af333..9b0160f4de02 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -291,12 +291,12 @@ public List getOwnerViewActions() { * Message displayed in the top page. Can be null. Includes HTML. */ @Exported - public String getDescription() { + public synchronized String getDescription() { return description; } @DataBoundSetter - public void setDescription(String description) { + public synchronized void setDescription(String description) { this.description = description; } diff --git a/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java b/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java index cd1c0f795701..e625759f37fd 100644 --- a/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java +++ b/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java @@ -57,7 +57,7 @@ private Object readResolve() { return this; } - private void initializeMessageDigest() { + private synchronized void initializeMessageDigest() { try { md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { diff --git a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java index a8055a7a4ea4..aafcf0226f40 100644 --- a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java @@ -239,7 +239,7 @@ public void run() { return 1; } - private boolean isOnlineScheduled() { + private synchronized boolean isOnlineScheduled() { updateStartStopWindow(); long now = System.currentTimeMillis(); return (lastStart < now && lastStop > now) || (nextStart < now && nextStop > now); diff --git a/core/src/main/java/hudson/tasks/Fingerprinter.java b/core/src/main/java/hudson/tasks/Fingerprinter.java index 397d41b874d7..e3dc7fc355cf 100644 --- a/core/src/main/java/hudson/tasks/Fingerprinter.java +++ b/core/src/main/java/hudson/tasks/Fingerprinter.java @@ -390,7 +390,9 @@ public void add(Map moreRecords) { Map r = new HashMap<>(record); r.putAll(moreRecords); record = compact(r); - ref = null; + synchronized (this) { + ref = null; + } } @Override diff --git a/core/src/main/java/hudson/tools/ToolInstallation.java b/core/src/main/java/hudson/tools/ToolInstallation.java index 7472c009d01d..9f9818dff217 100644 --- a/core/src/main/java/hudson/tools/ToolInstallation.java +++ b/core/src/main/java/hudson/tools/ToolInstallation.java @@ -27,6 +27,7 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.DescriptorExtensionList; import hudson.EnvVars; import hudson.Extension; @@ -223,6 +224,7 @@ protected String translateFor(Node node, TaskListener log) throws IOException, I /** * Invoked by XStream when this object is read into memory. */ + @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "nothing should be competing with XStream during deserialization") protected Object readResolve() { if (properties != null) { for (ToolProperty p : properties) { diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 2a0d3c6e2358..1a5bfc5774f2 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -1727,7 +1727,7 @@ public synchronized EnvVars getEnvironmentVariables() { @Override @NonNull - public List getArguments() { + public synchronized List getArguments() { if(arguments !=null) return arguments; parse(); diff --git a/core/src/main/java/hudson/util/SequentialExecutionQueue.java b/core/src/main/java/hudson/util/SequentialExecutionQueue.java index fd275c8468b5..309465db307c 100644 --- a/core/src/main/java/hudson/util/SequentialExecutionQueue.java +++ b/core/src/main/java/hudson/util/SequentialExecutionQueue.java @@ -107,7 +107,9 @@ private QueueEntry(Runnable item) { // Caller must have a lock private void submit() { submissionTime = System.currentTimeMillis(); - executors.submit(this); + synchronized (SequentialExecutionQueue.this) { + executors.submit(this); + } } @Override diff --git a/core/src/main/java/jenkins/model/RunIdMigrator.java b/core/src/main/java/jenkins/model/RunIdMigrator.java index 774cc17c7426..a1486464dce9 100644 --- a/core/src/main/java/jenkins/model/RunIdMigrator.java +++ b/core/src/main/java/jenkins/model/RunIdMigrator.java @@ -123,8 +123,10 @@ private void save(File dir) { File f = new File(dir, MAP_FILE); try (AtomicFileWriter w = new AtomicFileWriter(f)) { try { - for (Map.Entry entry : idToNumber.entrySet()) { - w.write(entry.getKey() + ' ' + entry.getValue() + '\n'); + synchronized (this) { + for (Map.Entry entry : idToNumber.entrySet()) { + w.write(entry.getKey() + ' ' + entry.getValue() + '\n'); + } } w.commit(); } finally { diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java index 42a12b9f817c..deda472e48e8 100644 --- a/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java @@ -26,6 +26,7 @@ import com.google.common.annotations.VisibleForTesting; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.BulkChange; import hudson.Util; import hudson.XmlFile; @@ -161,6 +162,7 @@ public synchronized void removeAllExcept(@NonNull String tokenUuid) { } + @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "access is in fact synchronized") private synchronized SingleTokenStats updateUsageForIdIfNeeded(@NonNull String tokenUuid) { SingleTokenStats stats = findById(tokenUuid) .orElseGet(() -> { diff --git a/src/spotbugs/spotbugs-excludes.xml b/src/spotbugs/spotbugs-excludes.xml index 01404a8bec24..b86bdf5f8f91 100644 --- a/src/spotbugs/spotbugs-excludes.xml +++ b/src/spotbugs/spotbugs-excludes.xml @@ -176,22 +176,6 @@ - - - - - - - - - - - - - - - - From 9838bd0cf8dfe29fdc5468b08d4dc833e37d7424 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 15 Dec 2021 15:22:57 -0800 Subject: [PATCH 10/58] Bump Apache Commons Validator from 1.6 to 1.7 (#6048) --- .../validator/routines/DomainValidator.java | 443 ++++++++--- .../routines/InetAddressValidator.java | 32 +- .../validator/routines/RegexValidator.java | 6 +- .../validator/routines/UrlValidator.java | 115 +-- .../apache/commons/validator/ResultPair.java | 32 + .../routines/DomainValidatorTest.java | 639 ++++++++++++++++ .../routines/InetAddressValidatorTest.java | 651 ++++++++++++++++ .../routines/RegexValidatorTest.java | 280 +++++++ .../validator/routines/UrlValidatorTest.java | 719 ++++++++++++++++++ 9 files changed, 2760 insertions(+), 157 deletions(-) create mode 100644 core/src/test/java/jenkins/org/apache/commons/validator/ResultPair.java create mode 100644 core/src/test/java/jenkins/org/apache/commons/validator/routines/DomainValidatorTest.java create mode 100644 core/src/test/java/jenkins/org/apache/commons/validator/routines/InetAddressValidatorTest.java create mode 100644 core/src/test/java/jenkins/org/apache/commons/validator/routines/RegexValidatorTest.java create mode 100644 core/src/test/java/jenkins/org/apache/commons/validator/routines/UrlValidatorTest.java diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java index e05e155a68d4..3a6d5d273bbc 100644 --- a/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java @@ -14,12 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +/* Copied from commons-validator:commons-validator:1.7, with [PATCH] modifications */ package jenkins.org.apache.commons.validator.routines; import java.io.Serializable; import java.net.IDN; import java.util.Arrays; +import java.util.List; import java.util.Locale; import jenkins.util.MemoryReductionUtil; import org.kohsuke.accmod.Restricted; @@ -62,7 +63,7 @@ * {@link java.net.InetAddress} for that functionality.) *

* - * @version $Revision: 1781829 $ + * @version $Revision$ * @since Validator 1.4 */ //[PATCH] @@ -70,6 +71,7 @@ // end of [PATCH] public class DomainValidator implements Serializable { + /** Maximum allowable length ({@value}) of a domain name */ private static final int MAX_DOMAIN_LENGTH = 253; private static final long serialVersionUID = -4407125112880174009L; @@ -92,19 +94,30 @@ public class DomainValidator implements Serializable { private static final String DOMAIN_NAME_REGEX = "^(?:" + DOMAIN_LABEL_REGEX + "\\.)+" + "(" + TOP_LABEL_REGEX + ")\\.?$"; + private static final String UNEXPECTED_ENUM_VALUE = "Unexpected enum value: "; + private final boolean allowLocal; - /** - * Singleton instance of this validator, which - * doesn't consider local addresses as valid. - */ - private static final DomainValidator DOMAIN_VALIDATOR = new DomainValidator(false); + private static class LazyHolder { // IODH + + /** + * Singleton instance of this validator, which + * doesn't consider local addresses as valid. + */ + private static final DomainValidator DOMAIN_VALIDATOR = new DomainValidator(false); + + /** + * Singleton instance of this validator, which does + * consider local addresses valid. + */ + private static final DomainValidator DOMAIN_VALIDATOR_WITH_LOCAL = new DomainValidator(true); + + } - /** - * Singleton instance of this validator, which does - * consider local addresses valid. + /* + * The above instances must only be returned via the getInstance() methods. + * This is to ensure that the override data arrays are properly protected. */ - private static final DomainValidator DOMAIN_VALIDATOR_WITH_LOCAL = new DomainValidator(true); /** * RegexValidator for matching domains. @@ -125,7 +138,7 @@ public class DomainValidator implements Serializable { */ public static synchronized DomainValidator getInstance() { inUse = true; - return DOMAIN_VALIDATOR; + return LazyHolder.DOMAIN_VALIDATOR; } /** @@ -137,14 +150,121 @@ public static synchronized DomainValidator getInstance() { public static synchronized DomainValidator getInstance(boolean allowLocal) { inUse = true; if(allowLocal) { - return DOMAIN_VALIDATOR_WITH_LOCAL; + return LazyHolder.DOMAIN_VALIDATOR_WITH_LOCAL; } - return DOMAIN_VALIDATOR; + return LazyHolder.DOMAIN_VALIDATOR; } - /** Private constructor. */ + /** + * Returns a new instance of this validator. + * The user can provide a list of {@link Item} entries which can + * be used to override the generic and country code lists. + * Note that any such entries override values provided by the + * {@link #updateTLDOverride(DomainValidator.ArrayType, String[])} method + * If an entry for a particular type is not provided, then + * the class override (if any) is retained. + * + * @param allowLocal Should local addresses be considered valid? + * @param items - array of {@link Item} entries + * @return an instance of this validator + * @since 1.7 + */ + public static synchronized DomainValidator getInstance(boolean allowLocal, List items) { + inUse = true; + return new DomainValidator(allowLocal, items); + } + + // intance variables allowing local overrides + final String[] mycountryCodeTLDsMinus; + final String[] mycountryCodeTLDsPlus; + final String[] mygenericTLDsPlus; + final String[] mygenericTLDsMinus; + final String[] mylocalTLDsPlus; + final String[] mylocalTLDsMinus; + /* + * N.B. It is vital that instances are immutable. + * This is because the default instances are shared. + */ + + // N.B. The constructors are deliberately private to avoid possible problems with unsafe publication. + // It is vital that the static override arrays are not mutable once they have been used in an instance + // The arrays could be copied into the instance variables, however if the static array were changed it could + // result in different settings for the shared default instances + + /** + * Private constructor. + */ private DomainValidator(boolean allowLocal) { this.allowLocal = allowLocal; + // link to class overrides + mycountryCodeTLDsMinus = countryCodeTLDsMinus; + mycountryCodeTLDsPlus = countryCodeTLDsPlus; + mygenericTLDsPlus = genericTLDsPlus; + mygenericTLDsMinus = genericTLDsMinus; + mylocalTLDsPlus = localTLDsPlus; + mylocalTLDsMinus = localTLDsMinus; + } + + /** + * Private constructor, allowing local overrides + * @since 1.7 + */ + private DomainValidator(boolean allowLocal, List items) { + this.allowLocal = allowLocal; + + // default to class overrides + String[] ccMinus = countryCodeTLDsMinus; + String[] ccPlus = countryCodeTLDsPlus; + String[] genMinus = genericTLDsMinus; + String[] genPlus = genericTLDsPlus; + String[] localMinus = localTLDsMinus; + String[] localPlus = localTLDsPlus; + + // apply the instance overrides + for(Item item: items) { + String [] copy = new String[item.values.length]; + // Comparisons are always done with lower-case entries + for (int i = 0; i < item.values.length; i++) { + copy[i] = item.values[i].toLowerCase(Locale.ENGLISH); + } + Arrays.sort(copy); + switch(item.type) { + case COUNTRY_CODE_MINUS: { + ccMinus = copy; + break; + } + case COUNTRY_CODE_PLUS: { + ccPlus = copy; + break; + } + case GENERIC_MINUS: { + genMinus = copy; + break; + } + case GENERIC_PLUS: { + genPlus = copy; + break; + } + case LOCAL_MINUS: { + localMinus = copy; + break; + } + case LOCAL_PLUS: { + localPlus = copy; + break; + } + default: + break; + } + } + + // init the instance overrides + mycountryCodeTLDsMinus = ccMinus; + mycountryCodeTLDsPlus = ccPlus; + mygenericTLDsMinus = genMinus; + mygenericTLDsPlus = genPlus; + mylocalTLDsMinus = localMinus; + mylocalTLDsPlus = localPlus; } /** @@ -174,7 +294,7 @@ public boolean isValid(String domain) { } // package protected for unit test access - // must agree with isValidRootUrl() above + // must agree with isValid() above final boolean isValidDomainSyntax(String domain) { if (domain == null) { return false; @@ -196,11 +316,14 @@ final boolean isValidDomainSyntax(String domain) { * Returns true if the specified {@code String} matches any * IANA-defined top-level domain. Leading dots are ignored if present. * The search is case-insensitive. + *

+ * If allowLocal is true, the TLD is checked using {@link #isValidLocalTld(String)}. + * The TLD is then checked against {@link #isValidInfrastructureTld(String)}, + * {@link #isValidGenericTld(String)} and {@link #isValidCountryCodeTld(String)} * @param tld the parameter to check for TLD status, not null * @return true if the parameter is a TLD */ public boolean isValidTld(String tld) { - tld = unicodeToASCII(tld); if(allowLocal && isValidLocalTld(tld)) { return true; } @@ -230,8 +353,8 @@ public boolean isValidInfrastructureTld(String iTld) { */ public boolean isValidGenericTld(String gTld) { final String key = chompLeadingDot(unicodeToASCII(gTld).toLowerCase(Locale.ENGLISH)); - return (arrayContains(GENERIC_TLDS, key) || arrayContains(genericTLDsPlus, key)) - && !arrayContains(genericTLDsMinus, key); + return (arrayContains(GENERIC_TLDS, key) || arrayContains(mygenericTLDsPlus, key)) + && !arrayContains(mygenericTLDsMinus, key); } /** @@ -243,8 +366,8 @@ public boolean isValidGenericTld(String gTld) { */ public boolean isValidCountryCodeTld(String ccTld) { final String key = chompLeadingDot(unicodeToASCII(ccTld).toLowerCase(Locale.ENGLISH)); - return (arrayContains(COUNTRY_CODE_TLDS, key) || arrayContains(countryCodeTLDsPlus, key)) - && !arrayContains(countryCodeTLDsMinus, key); + return (arrayContains(COUNTRY_CODE_TLDS, key) || arrayContains(mycountryCodeTLDsPlus, key)) + && !arrayContains(mycountryCodeTLDsMinus, key); } /** @@ -256,7 +379,18 @@ public boolean isValidCountryCodeTld(String ccTld) { */ public boolean isValidLocalTld(String lTld) { final String key = chompLeadingDot(unicodeToASCII(lTld).toLowerCase(Locale.ENGLISH)); - return arrayContains(LOCAL_TLDS, key); + return (arrayContains(LOCAL_TLDS, key) || arrayContains(mylocalTLDsPlus, key)) + && !arrayContains(mylocalTLDsMinus, key); + } + + /** + * Does this instance allow local addresses? + * + * @return true if local addresses are allowed. + * @since 1.7 + */ + public boolean isAllowLocal() { + return this.allowLocal; } private String chompLeadingDot(String str) { @@ -288,7 +422,7 @@ private String chompLeadingDot(String str) { // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search private static final String[] GENERIC_TLDS = new String[] { - // Taken from Version 2017020400, Last Updated Sat Feb 4 07:07:01 2017 UTC + // Taken from Version 2020073100, Last Updated Fri Jul 31 07:07:01 2020 UTC "aaa", // aaa American Automobile Association, Inc. "aarp", // aarp AARP "abarth", // abarth Fiat Chrysler Automobiles N.V. @@ -304,7 +438,7 @@ private String chompLeadingDot(String str) { "accountant", // accountant dot Accountant Limited "accountants", // accountants Knob Town, LLC "aco", // aco ACO Severin Ahlmann GmbH & Co. KG - "active", // active The Active Network, Inc +// "active", // active The Active Network, Inc "actor", // actor United TLD Holdco Ltd. "adac", // adac Allgemeiner Deutscher Automobil-Club e.V. (ADAC) "ads", // ads Charleston Road Registry Inc. @@ -314,10 +448,11 @@ private String chompLeadingDot(String str) { "aetna", // aetna Aetna Life Insurance Company "afamilycompany", // afamilycompany Johnson Shareholdings, Inc. "afl", // afl Australian Football League + "africa", // africa ZA Central Registry NPC trading as Registry.Africa "agakhan", // agakhan Fondation Aga Khan (Aga Khan Foundation) "agency", // agency Steel Falls, LLC "aig", // aig American International Group, Inc. - "aigo", // aigo aigo Digital Technology Co,Ltd. +// "aigo", // aigo aigo Digital Technology Co,Ltd. [Not assigned as of Jul 25] "airbus", // airbus Airbus S.A.S. "airforce", // airforce United TLD Holdco Ltd. "airtel", // airtel Bharti Airtel Limited @@ -330,6 +465,7 @@ private String chompLeadingDot(String str) { "ally", // ally Ally Financial Inc. "alsace", // alsace REGION D ALSACE "alstom", // alstom ALSTOM + "amazon", // amazon Amazon Registry Services, Inc. "americanexpress", // americanexpress American Express Travel Related Services Company, Inc. "americanfamily", // americanfamily AmFam, Inc. "amex", // amex American Express Travel Related Services Company, Inc. @@ -345,6 +481,7 @@ private String chompLeadingDot(String str) { "app", // app Charleston Road Registry Inc. "apple", // apple Apple Inc. "aquarelle", // aquarelle Aquarelle.com + "arab", // arab League of Arab States "aramco", // aramco Aramco Services Company "archi", // archi STARTING DOT LIMITED "army", // army United TLD Holdco Ltd. @@ -406,14 +543,14 @@ private String chompLeadingDot(String str) { "biz", // biz Neustar, Inc. "black", // black Afilias Limited "blackfriday", // blackfriday Uniregistry, Corp. - "blanco", // blanco BLANCO GmbH + Co KG +// "blanco", // blanco BLANCO GmbH + Co KG "blockbuster", // blockbuster Dish DBS Corporation "blog", // blog Knock Knock WHOIS There, LLC "bloomberg", // bloomberg Bloomberg IP Holdings LLC "blue", // blue Afilias Limited "bms", // bms Bristol-Myers Squibb Company "bmw", // bmw Bayerische Motoren Werke Aktiengesellschaft - "bnl", // bnl Banca Nazionale del Lavoro +// "bnl", // bnl Banca Nazionale del Lavoro "bnpparibas", // bnpparibas BNP Paribas "boats", // boats DERBoats, LLC "boehringer", // boehringer Boehringer Ingelheim International GmbH @@ -423,7 +560,7 @@ private String chompLeadingDot(String str) { "boo", // boo Charleston Road Registry Inc. "book", // book Amazon Registry Services, Inc. "booking", // booking Booking.com B.V. - "boots", // boots THE BOOTS COMPANY PLC +// "boots", // boots THE BOOTS COMPANY PLC "bosch", // bosch Robert Bosch GMBH "bostik", // bostik Bostik SA "boston", // boston Boston TLD Management, LLC @@ -464,7 +601,7 @@ private String chompLeadingDot(String str) { "career", // career dotCareer LLC "careers", // careers Wild Corner, LLC "cars", // cars Uniregistry, Corp. - "cartier", // cartier Richemont DNS Inc. +// "cartier", // cartier Richemont DNS Inc. "casa", // casa Top Level Domain Holdings Limited "case", // case CNH Industrial N.V. "caseih", // caseih CNH Industrial N.V. @@ -485,14 +622,15 @@ private String chompLeadingDot(String str) { "cfd", // cfd DOTCFD REGISTRY LTD "chanel", // chanel Chanel International B.V. "channel", // channel Charleston Road Registry Inc. + "charity", // charity Corn Lake, LLC "chase", // chase JPMorgan Chase & Co. "chat", // chat Sand Fields, LLC "cheap", // cheap Sand Cover, LLC "chintai", // chintai CHINTAI Corporation - "chloe", // chloe Richemont DNS Inc. +// "chloe", // chloe Richemont DNS Inc. (Not assigned) "christmas", // christmas Uniregistry, Corp. "chrome", // chrome Charleston Road Registry Inc. - "chrysler", // chrysler FCA US LLC. +// "chrysler", // chrysler FCA US LLC. "church", // church Holly Fileds, LLC "cipriani", // cipriani Hotel Cipriani Srl "circle", // circle Amazon Registry Services, Inc. @@ -538,6 +676,7 @@ private String chompLeadingDot(String str) { "coupon", // coupon Amazon Registry Services, Inc. "coupons", // coupons Black Island, LLC "courses", // courses OPEN UNIVERSITIES AUSTRALIA PTY LTD + "cpa", // cpa American Institute of Certified Public Accountants "credit", // credit Snow Shadow, LLC "creditcard", // creditcard Binky Frostbite, LLC "creditunion", // creditunion CUNA Performance Resources, LLC @@ -587,9 +726,9 @@ private String chompLeadingDot(String str) { "dnp", // dnp Dai Nippon Printing Co., Ltd. "docs", // docs Charleston Road Registry Inc. "doctor", // doctor Brice Trail, LLC - "dodge", // dodge FCA US LLC. +// "dodge", // dodge FCA US LLC. "dog", // dog Koko Mill, LLC - "doha", // doha Communications Regulatory Authority (CRA) +// "doha", // doha Communications Regulatory Authority (CRA) "domains", // domains Sugar Cross, LLC // "doosan", // doosan Doosan Corporation (retired) "dot", // dot Dish DBS Corporation @@ -599,7 +738,7 @@ private String chompLeadingDot(String str) { "dubai", // dubai Dubai Smart Government Department "duck", // duck Johnson Shareholdings, Inc. "dunlop", // dunlop The Goodyear Tire & Rubber Company - "duns", // duns The Dun & Bradstreet Corporation +// "duns", // duns The Dun & Bradstreet Corporation "dupont", // dupont E. I. du Pont de Nemours and Company "durban", // durban ZA Central Registry NPC trading as ZA Central Registry "dvag", // dvag Deutsche Vermögensberatung Aktiengesellschaft DVAG @@ -616,18 +755,19 @@ private String chompLeadingDot(String str) { "engineer", // engineer United TLD Holdco Ltd. "engineering", // engineering Romeo Canyon "enterprises", // enterprises Snow Oaks, LLC - "epost", // epost Deutsche Post AG +// "epost", // epost Deutsche Post AG "epson", // epson Seiko Epson Corporation "equipment", // equipment Corn Station, LLC "ericsson", // ericsson Telefonaktiebolaget L M Ericsson "erni", // erni ERNI Group Holding AG "esq", // esq Charleston Road Registry Inc. "estate", // estate Trixy Park, LLC - "esurance", // esurance Esurance Insurance Company + // "esurance", // esurance Esurance Insurance Company (not assigned as at Version 2020062100) + "etisalat", // etisalat Emirates Telecommunic "eurovision", // eurovision European Broadcasting Union (EBU) "eus", // eus Puntueus Fundazioa "events", // events Pioneer Maple, LLC - "everbank", // everbank EverBank +// "everbank", // everbank EverBank "exchange", // exchange Spring Falls, LLC "expert", // expert Magic Pass, LLC "exposed", // exposed Victor Beach, LLC @@ -701,6 +841,7 @@ private String chompLeadingDot(String str) { "games", // games United TLD Holdco Ltd. "gap", // gap The Gap, Inc. "garden", // garden Top Level Domain Holdings Limited + "gay", // gay Top Level Design, LLC "gbiz", // gbiz Charleston Road Registry Inc. "gdn", // gdn Joint Stock Company "Navigation-information systems" "gea", // gea GEA Group Aktiengesellschaft @@ -726,7 +867,7 @@ private String chompLeadingDot(String str) { "goldpoint", // goldpoint YODOBASHI CAMERA CO.,LTD. "golf", // golf Lone Falls, LLC "goo", // goo NTT Resonant Inc. - "goodhands", // goodhands Allstate Fire and Casualty Insurance Company +// "goodhands", // goodhands Allstate Fire and Casualty Insurance Company "goodyear", // goodyear The Goodyear Tire & Rubber Company "goog", // goog Charleston Road Registry Inc. "google", // google Charleston Road Registry Inc. @@ -738,6 +879,7 @@ private String chompLeadingDot(String str) { "gratis", // gratis Pioneer Tigers, LLC "green", // green Afilias Limited "gripe", // gripe Corn Sunset, LLC + "grocery", // grocery Wal-Mart Stores, Inc. "group", // group Romeo Town, LLC "guardian", // guardian The Guardian Life Insurance Company of America "gucci", // gucci Guccio Gucci S.p.a. @@ -772,18 +914,19 @@ private String chompLeadingDot(String str) { "homes", // homes DERHomes, LLC "homesense", // homesense The TJX Companies, Inc. "honda", // honda Honda Motor Co., Ltd. - "honeywell", // honeywell Honeywell GTLD LLC +// "honeywell", // honeywell Honeywell GTLD LLC "horse", // horse Top Level Domain Holdings Limited "hospital", // hospital Ruby Pike, LLC "host", // host DotHost Inc. "hosting", // hosting Uniregistry, Corp. "hot", // hot Amazon Registry Services, Inc. "hoteles", // hoteles Travel Reservations SRL + "hotels", // hotels Booking.com B.V. "hotmail", // hotmail Microsoft Corporation "house", // house Sugar Park, LLC "how", // how Charleston Road Registry Inc. "hsbc", // hsbc HSBC Holdings PLC - "htc", // htc HTC corporation +// "htc", // htc HTC corporation (Not assigned) "hughes", // hughes Hughes Satellite Systems Corporation "hyatt", // hyatt Hyatt GTLD, L.L.C. "hyundai", // hyundai Hyundai Motor Company @@ -799,6 +942,7 @@ private String chompLeadingDot(String str) { "imdb", // imdb Amazon Registry Services, Inc. "immo", // immo Auburn Bloom, LLC "immobilien", // immobilien United TLD Holdco Ltd. + "inc", // inc Intercap Holdings Inc. "industries", // industries Outer House, LLC "infiniti", // infiniti NISSAN MOTOR CO., LTD. "info", // info Afilias Limited @@ -814,14 +958,14 @@ private String chompLeadingDot(String str) { "investments", // investments Holly Glen, LLC "ipiranga", // ipiranga Ipiranga Produtos de Petroleo S.A. "irish", // irish Dot-Irish LLC - "iselect", // iselect iSelect Ltd +// "iselect", // iselect iSelect Ltd "ismaili", // ismaili Fondation Aga Khan (Aga Khan Foundation) "ist", // ist Istanbul Metropolitan Municipality "istanbul", // istanbul Istanbul Metropolitan Municipality / Medya A.S. "itau", // itau Itau Unibanco Holding S.A. "itv", // itv ITV Services Limited "iveco", // iveco CNH Industrial N.V. - "iwc", // iwc Richemont DNS Inc. +// "iwc", // iwc Richemont DNS Inc. "jaguar", // jaguar Jaguar Land Rover Ltd "java", // java Oracle Corporation "jcb", // jcb JCB Co., Ltd. @@ -830,7 +974,7 @@ private String chompLeadingDot(String str) { "jetzt", // jetzt New TLD Company AB "jewelry", // jewelry Wild Bloom, LLC "jio", // jio Affinity Names, Inc. - "jlc", // jlc Richemont DNS Inc. +// "jlc", // jlc Richemont DNS Inc. "jll", // jll Jones Lang LaSalle Incorporated "jmp", // jmp Matrix IP LLC "jnj", // jnj Johnson & Johnson Services, Inc. @@ -864,12 +1008,12 @@ private String chompLeadingDot(String str) { "kuokgroup", // kuokgroup Kerry Trading Co. Limited "kyoto", // kyoto Academic Institution: Kyoto Jyoho Gakuen "lacaixa", // lacaixa CAIXA D'ESTALVIS I PENSIONS DE BARCELONA - "ladbrokes", // ladbrokes LADBROKES INTERNATIONAL PLC +// "ladbrokes", // ladbrokes LADBROKES INTERNATIONAL PLC "lamborghini", // lamborghini Automobili Lamborghini S.p.A. "lamer", // lamer The Estée Lauder Companies Inc. "lancaster", // lancaster LANCASTER "lancia", // lancia Fiat Chrysler Automobiles N.V. - "lancome", // lancome L'Oréal +// "lancome", // lancome L'Oréal "land", // land Pine Moon, LLC "landrover", // landrover Jaguar Land Rover Ltd "lanxess", // lanxess LANXESS Corporation @@ -887,7 +1031,7 @@ private String chompLeadingDot(String str) { "lego", // lego LEGO Juris A/S "lexus", // lexus TOYOTA MOTOR CORPORATION "lgbt", // lgbt Afilias Limited - "liaison", // liaison Liaison Technologies, Incorporated +// "liaison", // liaison Liaison Technologies, Incorporated "lidl", // lidl Schwarz Domains und Services GmbH & Co. KG "life", // life Trixy Oaks, LLC "lifeinsurance", // lifeinsurance American Council of Life Insurers @@ -904,6 +1048,8 @@ private String chompLeadingDot(String str) { "live", // live United TLD Holdco Ltd. "living", // living Lifestyle Domain Holdings, Inc. "lixil", // lixil LIXIL Group Corporation + "llc", // llc Afilias plc + "llp", // llp Dot Registry LLC "loan", // loan dot Loan Limited "loans", // loans June Woods, LLC "locker", // locker Dish DBS Corporation @@ -930,6 +1076,7 @@ private String chompLeadingDot(String str) { "man", // man MAN SE "management", // management John Goodbye, LLC "mango", // mango PUNTO FA S.L. + "map", // map Charleston Road Registry Inc. "market", // market Unitied TLD Holdco, Ltd "marketing", // marketing Fern Pass, LLC "markets", // markets DOTMARKETS REGISTRY LTD @@ -938,8 +1085,8 @@ private String chompLeadingDot(String str) { "maserati", // maserati Fiat Chrysler Automobiles N.V. "mattel", // mattel Mattel Sites, Inc. "mba", // mba Lone Hollow, LLC - "mcd", // mcd McDonald’s Corporation - "mcdonalds", // mcdonalds McDonald’s Corporation +// "mcd", // mcd McDonald’s Corporation (Not assigned) +// "mcdonalds", // mcdonalds McDonald’s Corporation (Not assigned) "mckinsey", // mckinsey McKinsey Holdings, Inc. "med", // med Medistry LLC "media", // media Grand Glen, LLC @@ -949,7 +1096,8 @@ private String chompLeadingDot(String str) { "memorial", // memorial Dog Beach, LLC "men", // men Exclusive Registry Limited "menu", // menu Wedding TLD2, LLC - "meo", // meo PT Comunicacoes S.A. +// "meo", // meo PT Comunicacoes S.A. + "merckmsd", // merckmsd MSD Registry Holdings, Inc. "metlife", // metlife MetLife Services and Solutions, LLC "miami", // miami Top Level Domain Holdings Limited "microsoft", // microsoft Microsoft Corporation @@ -963,7 +1111,7 @@ private String chompLeadingDot(String str) { "mma", // mma MMA IARD "mobi", // mobi Afilias Technologies Limited dba dotMobi "mobile", // mobile Dish DBS Corporation - "mobily", // mobily GreenTech Consultancy Company W.L.L. +// "mobily", // mobily GreenTech Consultancy Company W.L.L. "moda", // moda United TLD Holdco Ltd. "moe", // moe Interlink Co., Ltd. "moi", // moi Amazon Registry Services, Inc. @@ -971,8 +1119,8 @@ private String chompLeadingDot(String str) { "monash", // monash Monash University "money", // money Outer McCook, LLC "monster", // monster Monster Worldwide, Inc. - "montblanc", // montblanc Richemont DNS Inc. - "mopar", // mopar FCA US LLC. +// "montblanc", // montblanc Richemont DNS Inc. (Not assigned) +// "mopar", // mopar FCA US LLC. "mormon", // mormon IRI Domain Management, LLC ("Applicant") "mortgage", // mortgage United TLD Holdco, Ltd "moscow", // moscow Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) @@ -980,16 +1128,16 @@ private String chompLeadingDot(String str) { "motorcycles", // motorcycles DERMotorcycles, LLC "mov", // mov Charleston Road Registry Inc. "movie", // movie New Frostbite, LLC - "movistar", // movistar Telefónica S.A. +// "movistar", // movistar Telefónica S.A. "msd", // msd MSD Registry Holdings, Inc. "mtn", // mtn MTN Dubai Limited - "mtpc", // mtpc Mitsubishi Tanabe Pharma Corporation +// "mtpc", // mtpc Mitsubishi Tanabe Pharma Corporation (Retired) "mtr", // mtr MTR Corporation Limited "museum", // museum Museum Domain Management Association "mutual", // mutual Northwestern Mutual MU TLD Registry, LLC // "mutuelle", // mutuelle Fédération Nationale de la Mutualité Française (Retired) "nab", // nab National Australia Bank Limited - "nadex", // nadex Nadex Domains, Inc +// "nadex", // nadex Nadex Domains, Inc "nagoya", // nagoya GMO Registry, Inc. "name", // name VeriSign Information Services, Inc. "nationwide", // nationwide Nationwide Mutual Insurance Company @@ -1048,16 +1196,16 @@ private String chompLeadingDot(String str) { "orange", // orange Orange Brand Services Limited "org", // org Public Interest Registry (PIR) "organic", // organic Afilias Limited - "orientexpress", // orientexpress Orient Express +// "orientexpress", // orientexpress Orient Express (retired 2017-04-11) "origins", // origins The Estée Lauder Companies Inc. "osaka", // osaka Interlink Co., Ltd. "otsuka", // otsuka Otsuka Holdings Co., Ltd. "ott", // ott Dish DBS Corporation "ovh", // ovh OVH SAS "page", // page Charleston Road Registry Inc. - "pamperedchef", // pamperedchef The Pampered Chef, Ltd. +// "pamperedchef", // pamperedchef The Pampered Chef, Ltd. (Not assigned) "panasonic", // panasonic Panasonic Corporation - "panerai", // panerai Richemont DNS Inc. +// "panerai", // panerai Richemont DNS Inc. "paris", // paris City of Paris "pars", // pars Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. "partners", // partners Magic Glen, LLC @@ -1069,13 +1217,14 @@ private String chompLeadingDot(String str) { "pet", // pet Afilias plc "pfizer", // pfizer Pfizer Inc. "pharmacy", // pharmacy National Association of Boards of Pharmacy + "phd", // phd Charleston Road Registry Inc. "philips", // philips Koninklijke Philips N.V. "phone", // phone Dish DBS Corporation "photo", // photo Uniregistry, Corp. "photography", // photography Sugar Glen, LLC "photos", // photos Sea Corner, LLC "physio", // physio PhysBiz Pty Ltd - "piaget", // piaget Richemont DNS Inc. +// "piaget", // piaget Richemont DNS Inc. "pics", // pics Uniregistry, Corp. "pictet", // pictet Pictet Europe S.A. "pictures", // pictures Foggy Sky, LLC @@ -1147,7 +1296,7 @@ private String chompLeadingDot(String str) { "rich", // rich I-REGISTRY Ltd., Niederlassung Deutschland "richardli", // richardli Pacific Century Asset Management (HK) Limited "ricoh", // ricoh Ricoh Company, Ltd. - "rightathome", // rightathome Johnson Shareholdings, Inc. + // "rightathome", // rightathome Johnson Shareholdings, Inc. (retired 2020-07-31) "ril", // ril Reliance Industries Limited "rio", // rio Empresa Municipal de Informática SA - IPLANRIO "rip", // rip United TLD Holdco Ltd. @@ -1158,6 +1307,7 @@ private String chompLeadingDot(String str) { "rogers", // rogers Rogers Communications Canada Inc. "room", // room Amazon Registry Services, Inc. "rsvp", // rsvp Charleston Road Registry Inc. + "rugby", // rugby World Rugby Strategic Developments Limited "ruhr", // ruhr regiodot GmbH & Co. KG "run", // run Snow Park, LLC "rwe", // rwe RWE AG @@ -1174,7 +1324,7 @@ private String chompLeadingDot(String str) { "sandvikcoromant", // sandvikcoromant Sandvik AB "sanofi", // sanofi Sanofi "sap", // sap SAP AG - "sapo", // sapo PT Comunicacoes S.A. +// "sapo", // sapo PT Comunicacoes S.A. "sarl", // sarl Delta Orchard, LLC "sas", // sas Research IP LLC "save", // save Amazon Registry Services, Inc. @@ -1191,8 +1341,9 @@ private String chompLeadingDot(String str) { "schwarz", // schwarz Schwarz Domains und Services GmbH & Co. KG "science", // science dot Science Limited "scjohnson", // scjohnson Johnson Shareholdings, Inc. - "scor", // scor SCOR SE + // "scor", // scor SCOR SE (not assigned as at Version 2020062100) "scot", // scot Dot Scot Registry Limited + "search", // search Charleston Road Registry Inc. "seat", // seat SEAT, S.A. (Sociedad Unipersonal) "secure", // secure Amazon Registry Services, Inc. "security", // security XYZ.COM LLC @@ -1242,18 +1393,19 @@ private String chompLeadingDot(String str) { "sony", // sony Sony Corporation "soy", // soy Charleston Road Registry Inc. "space", // space DotSpace Inc. - "spiegel", // spiegel SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG +// "spiegel", // spiegel SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG + "sport", // sport Global Association of International Sports Federations (GAISF) "spot", // spot Amazon Registry Services, Inc. "spreadbetting", // spreadbetting DOTSPREADBETTING REGISTRY LTD "srl", // srl InterNetX Corp. - "srt", // srt FCA US LLC. +// "srt", // srt FCA US LLC. "stada", // stada STADA Arzneimittel AG "staples", // staples Staples, Inc. "star", // star Star India Private Limited - "starhub", // starhub StarHub Limited +// "starhub", // starhub StarHub Limited "statebank", // statebank STATE BANK OF INDIA "statefarm", // statefarm State Farm Mutual Automobile Insurance Company - "statoil", // statoil Statoil ASA +// "statoil", // statoil Statoil ASA "stc", // stc Saudi Telecom Company "stcgroup", // stcgroup Saudi Telecom Company "stockholm", // stockholm Stockholms kommun @@ -1274,7 +1426,7 @@ private String chompLeadingDot(String str) { "swiftcover", // swiftcover Swiftcover Insurance Services Limited "swiss", // swiss Swiss Confederation "sydney", // sydney State of New South Wales, Department of Premier and Cabinet - "symantec", // symantec Symantec Corporation +// "symantec", // symantec Symantec Corporation [Not assigned as of Jul 25] "systems", // systems Dash Cypress, LLC "tab", // tab Tabcorp Holdings Limited "taipei", // taipei Taipei City Government @@ -1282,7 +1434,7 @@ private String chompLeadingDot(String str) { "taobao", // taobao Alibaba Group Holding Limited "target", // target Target Domain Holdings, LLC "tatamotors", // tatamotors Tata Motors Ltd - "tatar", // tatar Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic" + "tatar", // tatar LLC "Coordination Center of Regional Domain of Tatarstan Republic" "tattoo", // tattoo Uniregistry, Corp. "tax", // tax Storm Orchard, LLC "taxi", // taxi Pine Falls, LLC @@ -1292,8 +1444,8 @@ private String chompLeadingDot(String str) { "tech", // tech Dot Tech LLC "technology", // technology Auburn Falls, LLC "tel", // tel Telnic Ltd. - "telecity", // telecity TelecityGroup International Limited - "telefonica", // telefonica Telefónica S.A. +// "telecity", // telecity TelecityGroup International Limited +// "telefonica", // telefonica Telefónica S.A. "temasek", // temasek Temasek Holdings (Private) Limited "tennis", // tennis Cotton Bloom, LLC "teva", // teva Teva Pharmaceutical Industries Limited @@ -1338,7 +1490,7 @@ private String chompLeadingDot(String str) { "tvs", // tvs T V SUNDRAM IYENGAR & SONS PRIVATE LIMITED "ubank", // ubank National Australia Bank Limited "ubs", // ubs UBS AG - "uconnect", // uconnect FCA US LLC. +// "uconnect", // uconnect FCA US LLC. "unicom", // unicom China United Network Communications Corporation Limited "university", // university Little Station, LLC "uno", // uno Dot Latin LLC @@ -1362,8 +1514,8 @@ private String chompLeadingDot(String str) { "virgin", // virgin Virgin Enterprises Limited "visa", // visa Visa Worldwide Pte. Limited "vision", // vision Koko Station, LLC - "vista", // vista Vistaprint Limited - "vistaprint", // vistaprint Vistaprint Limited +// "vista", // vista Vistaprint Limited +// "vistaprint", // vistaprint Vistaprint Limited "viva", // viva Saudi Telecom Company "vivo", // vivo Telefonica Brasil S.A. "vlaanderen", // vlaanderen DNS.be vzw @@ -1380,7 +1532,7 @@ private String chompLeadingDot(String str) { "walter", // walter Sandvik AB "wang", // wang Zodiac Registry Limited "wanggou", // wanggou Amazon Registry Services, Inc. - "warman", // warman Weir Group IP Limited +// "warman", // warman Weir Group IP Limited "watch", // watch Sand Shadow, LLC "watches", // watches Richemont DNS Inc. "weather", // weather The Weather Channel, LLC @@ -1445,6 +1597,7 @@ private String chompLeadingDot(String str) { "xn--c1avg", // орг Public Interest Registry "xn--c2br7g", // नेट VeriSign Sarl "xn--cck2b3b", // ストア Amazon Registry Services, Inc. + "xn--cckwcxetd", // アマゾン Amazon Registry Services, Inc. "xn--cg4bki", // 삼성 SAMSUNG SDS CO., LTD "xn--czr694b", // 商标 HU YI GLOBAL INFORMATION RESOURCES(HOLDING) COMPANY.HONGKONG LIMITED "xn--czrs0t", // 商店 Wild Island, LLC @@ -1452,7 +1605,7 @@ private String chompLeadingDot(String str) { "xn--d1acj3b", // дети The Foundation for Network Initiatives “The Smart Internet” "xn--eckvdtc9d", // ポイント Amazon Registry Services, Inc. "xn--efvy88h", // 新闻 Xinhua News Agency Guangdong Branch 新华通讯社广东分社 - "xn--estv75g", // 工行 Industrial and Commercial Bank of China Limited +// "xn--estv75g", // 工行 Industrial and Commercial Bank of China Limited "xn--fct429k", // 家電 Amazon Registry Services, Inc. "xn--fhbei", // كوم VeriSign Sarl "xn--fiq228c5hs", // 中文网 TLD REGISTRY LIMITED @@ -1468,15 +1621,17 @@ private String chompLeadingDot(String str) { "xn--imr513n", // 餐厅 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY. HONGKONG LIMITED "xn--io0a7i", // 网络 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center) "xn--j1aef", // ком VeriSign Sarl + "xn--jlq480n2rg", // 亚马逊 Amazon Registry Services, Inc. "xn--jlq61u9w7b", // 诺基亚 Nokia Corporation "xn--jvr189m", // 食品 Amazon Registry Services, Inc. "xn--kcrx77d1x4a", // 飞利浦 Koninklijke Philips N.V. - "xn--kpu716f", // 手表 Richemont DNS Inc. +// "xn--kpu716f", // 手表 Richemont DNS Inc. [Not assigned as of Jul 25] "xn--kput3i", // 手机 Beijing RITT-Net Technology Development Co., Ltd "xn--mgba3a3ejt", // ارامكو Aramco Services Company "xn--mgba7c0bbn0a", // العليان Crescent Holding GmbH + "xn--mgbaakc7dvf", // اتصالات Emirates Telecommunications Corporation (trading as Etisalat) "xn--mgbab2bd", // بازار CORE Association - "xn--mgbb9fbpob", // موبايلي GreenTech Consultancy Company W.L.L. +// "xn--mgbb9fbpob", // موبايلي GreenTech Consultancy Company W.L.L. "xn--mgbca7dzdo", // ابوظبي Abu Dhabi Systems and Information Centre "xn--mgbi4ecexp", // كاثوليك Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) "xn--mgbt3dhd", // همراه Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. @@ -1484,11 +1639,13 @@ private String chompLeadingDot(String str) { "xn--mxtq1m", // 政府 Net-Chinese Co., Ltd. "xn--ngbc5azd", // شبكة International Domain Registry Pty. Ltd. "xn--ngbe9e0a", // بيتك Kuwait Finance House + "xn--ngbrx", // عرب League of Arab States "xn--nqv7f", // 机构 Public Interest Registry "xn--nqv7fs00ema", // 组织机构 Public Interest Registry "xn--nyqy26a", // 健康 Stable Tone Limited + "xn--otu796d", // 招聘 Dot Trademark TLD Holding Company Limited "xn--p1acf", // рус Rusnames Limited - "xn--pbt977c", // 珠宝 Richemont DNS Inc. +// "xn--pbt977c", // 珠宝 Richemont DNS Inc. [Not assigned as of Jul 25] "xn--pssy2u", // 大拿 VeriSign Sarl "xn--q9jyb4c", // みんな Charleston Road Registry Inc. "xn--qcka1pmc", // グーグル Charleston Road Registry Inc. @@ -1507,7 +1664,7 @@ private String chompLeadingDot(String str) { "xn--w4rs40l", // 嘉里 Kerry Trading Co. Limited "xn--xhq521b", // 广东 Guangzhou YU Wei Information Technology Co., Ltd. "xn--zfr164b", // 政务 China Organizational Name Administration Center - "xperia", // xperia Sony Mobile Communications AB +// "xperia", // xperia Sony Mobile Communications AB "xxx", // xxx ICM Registry LLC "xyz", // xyz XYZ.COM LLC "yachts", // yachts DERYachts, LLC @@ -1524,13 +1681,14 @@ private String chompLeadingDot(String str) { "zara", // zara Industria de Diseño Textil, S.A. (INDITEX, S.A.) "zero", // zero Amazon Registry Services, Inc. "zip", // zip Charleston Road Registry Inc. - "zippo", // zippo Zadco Company +// "zippo", // zippo Zadco Company "zone", // zone Outer Falls, LLC "zuerich", // zuerich Kanton Zürich (Canton of Zurich) }; // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search private static final String[] COUNTRY_CODE_TLDS = new String[] { + // Taken from Version 2020051000, Last Updated Sun May 10 07:07:01 2020 UTC "ac", // Ascension Island "ad", // Andorra "ae", // United Arab Emirates @@ -1737,6 +1895,7 @@ private String chompLeadingDot(String str) { "sn", // Senegal "so", // Somalia "sr", // Suriname + "ss", // ss National Communication Authority (NCA) "st", // São Tomé and Príncipe "su", // Soviet Union (deprecated) "sv", // El Salvador @@ -1775,7 +1934,10 @@ private String chompLeadingDot(String str) { "vu", // Vanuatu "wf", // Wallis and Futuna "ws", // Samoa (formerly Western Samoa) + "xn--2scrj9c", // ಭಾರತ National Internet eXchange of India "xn--3e0b707e", // 한국 KISA (Korea Internet & Security Agency) + "xn--3hcrj9c", // ଭାରତ National Internet eXchange of India + "xn--45br5cyl", // ভাৰত National Internet eXchange of India "xn--45brj9c", // ভারত National Internet Exchange of India "xn--54b7fta0cc", // বাংলা Posts and Telecommunications Division "xn--80ao21a", // қаз Association of IT Companies of Kazakhstan @@ -1789,7 +1951,9 @@ private String chompLeadingDot(String str) { "xn--fpcrj9c3d", // భారత్ National Internet Exchange of India "xn--fzc2c9e2c", // ලංකා LK Domain Registry "xn--gecrj9c", // ભારત National Internet Exchange of India + "xn--h2breg3eve", // भारतम् National Internet eXchange of India "xn--h2brj9c", // भारत National Internet Exchange of India + "xn--h2brj9c8c", // भारोत National Internet eXchange of India "xn--j1amh", // укр Ukrainian Network Information Centre (UANIC), Inc. "xn--j6w193g", // 香港 Hong Kong Internet Registration Corporation Ltd. "xn--kprw13d", // 台湾 Taiwan Network Information Center (TWNIC) @@ -1799,10 +1963,15 @@ private String chompLeadingDot(String str) { "xn--mgb9awbf", // عمان Telecommunications Regulatory Authority (TRA) "xn--mgba3a4f16a", // ایران Institute for Research in Fundamental Sciences (IPM) "xn--mgbaam7a8h", // امارات Telecommunications Regulatory Authority (TRA) + "xn--mgbah1a3hjkrd", // موريتانيا Université de Nouakchott Al Aasriya + "xn--mgbai9azgqp6j", // پاکستان National Telecommunication Corporation "xn--mgbayh7gpa", // الاردن National Information Technology Center (NITC) + "xn--mgbbh1a", // بارت National Internet eXchange of India "xn--mgbbh1a71e", // بھارت National Internet Exchange of India "xn--mgbc0a9azcg", // المغرب Agence Nationale de Réglementation des Télécommunications (ANRT) + "xn--mgbcpq6gpa1a", // البحرين Telecommunications Regulatory Authority (TRA) "xn--mgberp4a5d4ar", // السعودية Communications and Information Technology Commission + "xn--mgbgu82a", // ڀارت National Internet eXchange of India "xn--mgbpl2fh", // ????? Sudan Internet Society "xn--mgbtx2b", // عراق Communications and Media Commission (CMC) "xn--mgbx4cd0ab", // مليسيا MYNIC Berhad @@ -1812,7 +1981,10 @@ private String chompLeadingDot(String str) { "xn--ogbpf8fl", // سورية National Agency for Network Services (NANS) "xn--p1ai", // рф Coordination Center for TLD RU "xn--pgbs0dh", // تونس Agence Tunisienne d'Internet + "xn--q7ce6a", // ລາວ Lao National Internet Center (LANIC) + "xn--qxa6a", // ευ EURid vzw/asbl "xn--qxam", // ελ ICS-FORTH GR + "xn--rvc1e0am3e", // ഭാരതം National Internet eXchange of India "xn--s9brj9c", // ਭਾਰਤ National Internet Exchange of India "xn--wgbh1c", // مصر National Telecommunication Regulatory Authority - NTRA "xn--wgbl6a", // قطر Communications Regulatory Authority @@ -1846,22 +2018,29 @@ private String chompLeadingDot(String str) { private static boolean inUse = false; /* - * These arrays are mutable, but they don't need to be volatile. - * They can only be updated by the updateTLDOverride method, and any readers must get an instance + * These arrays are mutable. + * They can only be updated by the updateTLDOverride method, and readers must first get an instance * using the getInstance methods which are all (now) synchronised. + * The only other access is via getTLDEntries which is now synchronised. */ // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search - private static volatile String[] countryCodeTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + private static String[] countryCodeTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static String[] genericTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search - private static volatile String[] genericTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + private static String[] countryCodeTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search - private static volatile String[] countryCodeTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + private static String[] genericTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search - private static volatile String[] genericTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + private static String[] localTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static String[] localTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + /** * enum used by {@link DomainValidator#updateTLDOverride(DomainValidator.ArrayType, String[])} * to determine which override array to update / fetch @@ -1884,17 +2063,37 @@ public enum ArrayType { /** Get a copy of the infrastructure table */ INFRASTRUCTURE_RO, /** Get a copy of the local table */ - LOCAL_RO + LOCAL_RO, + /** + * Update (or get a copy of) the LOCAL_TLDS_PLUS table containing additional local TLDs + * @since 1.7 + */ + LOCAL_PLUS, + /** + * Update (or get a copy of) the LOCAL_TLDS_MINUS table containing deleted local TLDs + * @since 1.7 + */ + LOCAL_MINUS } - // For use by unit test code only - static synchronized void clearTLDOverrides() { - inUse = false; - countryCodeTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; - countryCodeTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; - genericTLDsPlus = MemoryReductionUtil.EMPTY_STRING_ARRAY; - genericTLDsMinus = MemoryReductionUtil.EMPTY_STRING_ARRAY; + /** + * Used to specify overrides when creating a new class. + * @since 1.7 + */ + public static class Item { + final ArrayType type; + final String[] values; + /** + * + * @param type ArrayType, e.g. GENERIC_PLUS, LOCAL_PLUS + * @param values array of TLDs. Will be lower-cased and sorted + */ + public Item(ArrayType type, String[] values) { + this.type = type; + this.values = values; // no need to copy here + } } + /** * Update one of the TLD override arrays. * This must only be done at program startup, before any instances are accessed using getInstance. @@ -1912,6 +2111,8 @@ static synchronized void clearTLDOverrides() { *

  • COUNTRY_CODE_PLUS
  • *
  • GENERIC_MINUS
  • *
  • GENERIC_PLUS
  • + *
  • LOCAL_MINUS
  • + *
  • LOCAL_PLUS
  • * * @param tlds the array of TLDs, must not be null * @throws IllegalStateException if the method is called after getInstance @@ -1941,24 +2142,30 @@ public static synchronized void updateTLDOverride(DomainValidator.ArrayType tabl case GENERIC_PLUS: genericTLDsPlus = copy; break; + case LOCAL_MINUS: + localTLDsMinus = copy; + break; + case LOCAL_PLUS: + localTLDsPlus = copy; + break; case COUNTRY_CODE_RO: case GENERIC_RO: case INFRASTRUCTURE_RO: case LOCAL_RO: throw new IllegalArgumentException("Cannot update the table: " + table); default: - throw new IllegalArgumentException("Unexpected enum value: " + table); + throw new IllegalArgumentException(UNEXPECTED_ENUM_VALUE + table); } } /** - * Get a copy of the internal array. + * Get a copy of a class level internal array. * @param table the array type (any of the enum values) * @return a copy of the array * @throws IllegalArgumentException if the table type is unexpected (should not happen) * @since 1.5.1 */ - public static String [] getTLDEntries(DomainValidator.ArrayType table) { + public static synchronized String [] getTLDEntries(ArrayType table) { final String[] array; switch(table) { case COUNTRY_CODE_MINUS: @@ -1973,6 +2180,12 @@ public static synchronized void updateTLDOverride(DomainValidator.ArrayType tabl case GENERIC_PLUS: array = genericTLDsPlus; break; + case LOCAL_MINUS: + array = localTLDsMinus; + break; + case LOCAL_PLUS: + array = localTLDsPlus; + break; case GENERIC_RO: array = GENERIC_TLDS; break; @@ -1986,7 +2199,41 @@ public static synchronized void updateTLDOverride(DomainValidator.ArrayType tabl array = LOCAL_TLDS; break; default: - throw new IllegalArgumentException("Unexpected enum value: " + table); + throw new IllegalArgumentException(UNEXPECTED_ENUM_VALUE + table); + } + return Arrays.copyOf(array, array.length); // clone the array + } + + /** + * Get a copy of an instance level internal array. + * @param table the array type (any of the enum values) + * @return a copy of the array + * @throws IllegalArgumentException if the table type is unexpected, e.g. GENERIC_RO + * @since 1.7 + */ + public String [] getOverrides(ArrayType table) { + final String[] array; + switch(table) { + case COUNTRY_CODE_MINUS: + array = mycountryCodeTLDsMinus; + break; + case COUNTRY_CODE_PLUS: + array = mycountryCodeTLDsPlus; + break; + case GENERIC_MINUS: + array = mygenericTLDsMinus; + break; + case GENERIC_PLUS: + array = mygenericTLDsPlus; + break; + case LOCAL_MINUS: + array = mylocalTLDsMinus; + break; + case LOCAL_PLUS: + array = mylocalTLDsPlus; + break; + default: + throw new IllegalArgumentException(UNEXPECTED_ENUM_VALUE + table); } return Arrays.copyOf(array, array.length); // clone the array } diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java index 5915bb8b2a55..ffebcbebac70 100644 --- a/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +/* Copied from commons-validator:commons-validator:1.7, with [PATCH] modifications */ package jenkins.org.apache.commons.validator.routines; import java.io.Serializable; @@ -33,7 +33,7 @@ * This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} method. *

    * - * @version $Revision: 1783032 $ + * @version $Revision$ * @since Validator 1.4 */ //[PATCH] @@ -131,6 +131,34 @@ public boolean isValidInet4Address(String inet4Address) { * @since 1.4.1 */ public boolean isValidInet6Address(String inet6Address) { + String[] parts; + // remove prefix size. This will appear after the zone id (if any) + parts = inet6Address.split("/", -1); + if (parts.length > 2) { + return false; // can only have one prefix specifier + } + if (parts.length == 2) { + if (parts[1].matches("\\d{1,3}")) { // Need to eliminate signs + int bits = Integer.parseInt(parts[1]); // cannot fail because of RE check + if (bits < 0 || bits > 128) { + return false; // out of range + } + } else { + return false; // not a valid number + } + } + // remove zone-id + parts = parts[0].split("%", -1); + if (parts.length > 2) { + return false; + } else if (parts.length == 2){ + // The id syntax is implemenatation independent, but it presumably cannot allow: + // whitespace, '/' or '%' + if (!parts[1].matches("[^\\s/%]+")) { + return false; // invalid id + } + } + inet6Address = parts[0]; boolean containsCompressedZeroes = inet6Address.contains("::"); if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) { return false; diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java index 3b08c1da07dc..3fd140711c99 100644 --- a/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +/* Copied from commons-validator:commons-validator:1.7, with [PATCH] modifications */ package jenkins.org.apache.commons.validator.routines; import java.io.Serializable; @@ -43,7 +43,7 @@ *
  • Validate {@code true} or {@code false}:
  • *
  • *
      - *
    • {@code boolean valid = validator.isValidRootUrl(value);}
    • + *
    • {@code boolean valid = validator.isValid(value);}
    • *
    *
  • *
  • Validate returning an aggregated String of the matched groups:
  • @@ -67,7 +67,7 @@ * to the {@link Pattern} API are safe to use in a multi-threaded environment. *

    * - * @version $Revision: 1739356 $ + * @version $Revision$ * @since Validator 1.4 */ //[PATCH] diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java index 5fc7c4500077..5272e3a2e9d9 100644 --- a/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +/* Copied from commons-validator:commons-validator:1.7, with [PATCH] modifications */ package jenkins.org.apache.commons.validator.routines; import java.io.Serializable; @@ -50,7 +50,7 @@ * * String[] schemes = {"http","https"}. * UrlValidator urlValidator = new UrlValidator(schemes); - * if (urlValidator.isValidRootUrl("ftp://foo.bar.com/")) { + * if (urlValidator.isValid("ftp://foo.bar.com/")) { * System.out.println("url is valid"); * } else { * System.out.println("url is invalid"); @@ -60,7 +60,7 @@ * If instead the default constructor is used. * * UrlValidator urlValidator = new UrlValidator(); - * if (urlValidator.isValidRootUrl("ftp://foo.bar.com/")) { + * if (urlValidator.isValid("ftp://foo.bar.com/")) { * System.out.println("url is valid"); * } else { * System.out.println("url is invalid"); @@ -74,7 +74,7 @@ * Uniform Resource Identifiers (URI): Generic Syntax * * - * @version $Revision: 1783203 $ + * @version $Revision$ * @since Validator 1.4 */ //[PATCH] @@ -110,30 +110,6 @@ public class UrlValidator implements Serializable { */ public static final long ALLOW_LOCAL_URLS = 1 << 3; // CHECKSTYLE IGNORE MagicNumber - /** - * This expression derived/taken from the BNF for URI (RFC2396). - */ - private static final String URL_REGEX = - "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"; - // 12 3 4 5 6 7 8 9 - private static final Pattern URL_PATTERN = Pattern.compile(URL_REGEX); - - /** - * Schema/Protocol (ie. http:, ftp:, file:, etc). - */ - private static final int PARSE_URL_SCHEME = 2; - - /** - * Includes hostname/ip and port number. - */ - private static final int PARSE_URL_AUTHORITY = 4; - - private static final int PARSE_URL_PATH = 5; - - private static final int PARSE_URL_QUERY = 7; - - private static final int PARSE_URL_FRAGMENT = 9; - /** * Protocol scheme (e.g. http, ftp, https). */ @@ -144,7 +120,8 @@ public class UrlValidator implements Serializable { // TODO does not allow for optional userinfo. // Validation of character set is done by isValidAuthority private static final String AUTHORITY_CHARS_REGEX = "\\p{Alnum}\\-\\."; // allows for IPV4 but not IPV6 - private static final String IPV6_REGEX = "[0-9a-fA-F:]+"; // do this as separate match because : could cause ambiguity with port prefix + // Allow for IPv4 mapped addresses: ::FFF:123.123.123.123 + private static final String IPV6_REGEX = "::FFFF:(?:\\d{1,3}\\.){3}\\d{1,3}|[0-9a-fA-F:]+"; // do this as separate match because : could cause ambiguity with port prefix // userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" @@ -211,6 +188,8 @@ public static UrlValidator getInstance() { return DEFAULT_URL_VALIDATOR; } + private final DomainValidator domainValidator; + /** * Create a UrlValidator with default properties. */ @@ -274,7 +253,29 @@ public UrlValidator(RegexValidator authorityValidator, long options) { * enables both of those options. */ public UrlValidator(String[] schemes, RegexValidator authorityValidator, long options) { + this(schemes, authorityValidator, options, DomainValidator.getInstance(isOn(ALLOW_LOCAL_URLS, options))); + } + + /** + * Customizable constructor. Validation behavior is modified by passing in options. + * @param schemes the set of valid schemes. Ignored if the ALLOW_ALL_SCHEMES option is set. + * @param authorityValidator Regular expression validator used to validate the authority part + * @param options Validation options. Set using the public constants of this class. + * To set multiple options, simply add them together: + *

    {@code ALLOW_2_SLASHES + NO_FRAGMENTS}

    + * enables both of those options. + * @param domainValidator the DomainValidator to use; must agree with ALLOW_LOCAL_URLS setting + * @since 1.7 + */ + public UrlValidator(String[] schemes, RegexValidator authorityValidator, long options, DomainValidator domainValidator) { this.options = options; + if (domainValidator == null) { + throw new IllegalArgumentException("DomainValidator must not be null"); + } + if (domainValidator.isAllowLocal() != ((options & ALLOW_LOCAL_URLS) > 0)){ + throw new IllegalArgumentException("DomainValidator disagrees with ALLOW_LOCAL_URLS setting"); + } + this.domainValidator = domainValidator; if (isOn(ALLOW_ALL_SCHEMES)) { allowedSchemes = Collections.emptySet(); @@ -306,41 +307,40 @@ public boolean isValid(String value) { return false; } - // Check the whole url address structure - Matcher urlMatcher = URL_PATTERN.matcher(value); - if (!urlMatcher.matches()) { + URI uri; // ensure value is a valid URI + try { + uri = new URI(value); + } catch (URISyntaxException e) { return false; } + // OK, perfom additional validation - String scheme = urlMatcher.group(PARSE_URL_SCHEME); + String scheme = uri.getScheme(); if (!isValidScheme(scheme)) { return false; } - String authority = urlMatcher.group(PARSE_URL_AUTHORITY); - if ("file".equals(scheme)) {// Special case - file: allows an empty authority - if (authority != null) { - if (authority.contains(":")) { // but cannot allow trailing : - return false; - } - } - // drop through to continue validation - } else { // not file: + String authority = uri.getRawAuthority(); + if ("file".equals(scheme) && (authority == null || "".equals(authority))) {// Special case - file: allows an empty authority + return true; // this is a local file - nothing more to do here + } else if ("file".equals(scheme) && authority != null && authority.contains(":")) { + return false; + } else { // Validate the authority if (!isValidAuthority(authority)) { return false; } } - if (!isValidPath(urlMatcher.group(PARSE_URL_PATH))) { + if (!isValidPath(uri.getRawPath())) { return false; } - if (!isValidQuery(urlMatcher.group(PARSE_URL_QUERY))) { + if (!isValidQuery(uri.getRawQuery())) { return false; } - if (!isValidFragment(urlMatcher.group(PARSE_URL_FRAGMENT))) { + if (!isValidFragment(uri.getRawFragment())) { return false; } @@ -361,7 +361,6 @@ protected boolean isValidScheme(String scheme) { return false; } - // TODO could be removed if external schemes were checked in the ctor before being stored if (!SCHEME_PATTERN.matcher(scheme).matches()) { return false; } @@ -411,8 +410,7 @@ protected boolean isValidAuthority(String authority) { String hostLocation = authorityMatcher.group(PARSE_AUTHORITY_HOST_IP); // check if authority is hostname or IP address: // try a hostname first since that's much more likely - DomainValidator domainValidator = DomainValidator.getInstance(isOn(ALLOW_LOCAL_URLS)); - if (!domainValidator.isValid(hostLocation)) { + if (!this.domainValidator.isValid(hostLocation)) { // try an IPv4 address InetAddressValidator inetAddressValidator = InetAddressValidator.getInstance(); if (!inetAddressValidator.isValidInet4Address(hostLocation)) { @@ -456,7 +454,8 @@ protected boolean isValidPath(String path) { } try { - URI uri = new URI(null,null,path,null); + // Don't omit host otherwise leading path may be taken as host if it starts with // + URI uri = new URI(null,"localhost",path,null); String norm = uri.normalize().getPath(); if (norm.startsWith("/../") // Trying to go via the parent dir || norm.equals("/..")) { // Trying to go to the parent dir @@ -531,6 +530,19 @@ private boolean isOn(long flag) { return (options & flag) > 0; } + /** + * Tests whether the given flag is on. If the flag is not a power of 2 + * (e.g. 3) this tests whether the combination of flags is on. + * + * @param flag Flag value to check. + * @param options what to check + * + * @return whether the specified flag value is on. + */ + private static boolean isOn(long flag, long options) { + return (options & flag) > 0; + } + /** * Tests whether the given flag is off. If the flag is not a power of 2 * (ie. 3) this tests whether the combination of flags is off. @@ -542,9 +554,4 @@ private boolean isOn(long flag) { private boolean isOff(long flag) { return (options & flag) == 0; } - - // Unit test access to pattern matcher - Matcher matchURL(String value) { - return URL_PATTERN.matcher(value); - } } diff --git a/core/src/test/java/jenkins/org/apache/commons/validator/ResultPair.java b/core/src/test/java/jenkins/org/apache/commons/validator/ResultPair.java new file mode 100644 index 000000000000..f31c35bf1c84 --- /dev/null +++ b/core/src/test/java/jenkins/org/apache/commons/validator/ResultPair.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jenkins.org.apache.commons.validator; + +/** + * Groups tests and expected results. + * + * @version $Revision$ + */ + public class ResultPair { + public final String item; + public final boolean valid; + + public ResultPair(String item, boolean valid) { + this.item = item; + this.valid = valid; //Whether the individual part of url is valid. + } + } diff --git a/core/src/test/java/jenkins/org/apache/commons/validator/routines/DomainValidatorTest.java b/core/src/test/java/jenkins/org/apache/commons/validator/routines/DomainValidatorTest.java new file mode 100644 index 000000000000..2cdd2c5880db --- /dev/null +++ b/core/src/test/java/jenkins/org/apache/commons/validator/routines/DomainValidatorTest.java @@ -0,0 +1,639 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jenkins.org.apache.commons.validator.routines; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.HttpURLConnection; +import java.net.IDN; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jenkins.org.apache.commons.validator.routines.DomainValidator.ArrayType; +import junit.framework.TestCase; + +/** + * Tests for the DomainValidator. + * + * @version $Revision$ + */ +public class DomainValidatorTest extends TestCase { + + private DomainValidator validator; + + @Override + public void setUp() { + validator = DomainValidator.getInstance(); + } + + public void testValidDomains() { + assertTrue("apache.org should validate", validator.isValid("apache.org")); + assertTrue("www.google.com should validate", validator.isValid("www.google.com")); + + assertTrue("test-domain.com should validate", validator.isValid("test-domain.com")); + assertTrue("test---domain.com should validate", validator.isValid("test---domain.com")); + assertTrue("test-d-o-m-ain.com should validate", validator.isValid("test-d-o-m-ain.com")); + assertTrue("two-letter domain label should validate", validator.isValid("as.uk")); + + assertTrue("case-insensitive ApAchE.Org should validate", validator.isValid("ApAchE.Org")); + + assertTrue("single-character domain label should validate", validator.isValid("z.com")); + + assertTrue("i.have.an-example.domain.name should validate", validator.isValid("i.have.an-example.domain.name")); + } + + public void testInvalidDomains() { + assertFalse("bare TLD .org shouldn't validate", validator.isValid(".org")); + assertFalse("domain name with spaces shouldn't validate", validator.isValid(" apache.org ")); + assertFalse("domain name containing spaces shouldn't validate", validator.isValid("apa che.org")); + assertFalse("domain name starting with dash shouldn't validate", validator.isValid("-testdomain.name")); + assertFalse("domain name ending with dash shouldn't validate", validator.isValid("testdomain-.name")); + assertFalse("domain name starting with multiple dashes shouldn't validate", validator.isValid("---c.com")); + assertFalse("domain name ending with multiple dashes shouldn't validate", validator.isValid("c--.com")); + assertFalse("domain name with invalid TLD shouldn't validate", validator.isValid("apache.rog")); + + assertFalse("URL shouldn't validate", validator.isValid("http://www.apache.org")); + assertFalse("Empty string shouldn't validate as domain name", validator.isValid(" ")); + assertFalse("Null shouldn't validate as domain name", validator.isValid(null)); + } + + public void testTopLevelDomains() { + // infrastructure TLDs + assertTrue(".arpa should validate as iTLD", validator.isValidInfrastructureTld(".arpa")); + assertFalse(".com shouldn't validate as iTLD", validator.isValidInfrastructureTld(".com")); + + // generic TLDs + assertTrue(".name should validate as gTLD", validator.isValidGenericTld(".name")); + assertFalse(".us shouldn't validate as gTLD", validator.isValidGenericTld(".us")); + + // country code TLDs + assertTrue(".uk should validate as ccTLD", validator.isValidCountryCodeTld(".uk")); + assertFalse(".org shouldn't validate as ccTLD", validator.isValidCountryCodeTld(".org")); + + // case-insensitive + assertTrue(".COM should validate as TLD", validator.isValidTld(".COM")); + assertTrue(".BiZ should validate as TLD", validator.isValidTld(".BiZ")); + + // corner cases + assertFalse("invalid TLD shouldn't validate", validator.isValid(".nope")); // TODO this is not guaranteed invalid forever + assertFalse("empty string shouldn't validate as TLD", validator.isValid("")); + assertFalse("null shouldn't validate as TLD", validator.isValid(null)); + } + + public void testAllowLocal() { + DomainValidator noLocal = DomainValidator.getInstance(false); + DomainValidator allowLocal = DomainValidator.getInstance(true); + + // Default is false, and should use singletons + assertEquals(noLocal, validator); + + // Default won't allow local + assertFalse("localhost.localdomain should validate", noLocal.isValid("localhost.localdomain")); + assertFalse("localhost should validate", noLocal.isValid("localhost")); + + // But it may be requested + assertTrue("localhost.localdomain should validate", allowLocal.isValid("localhost.localdomain")); + assertTrue("localhost should validate", allowLocal.isValid("localhost")); + assertTrue("hostname should validate", allowLocal.isValid("hostname")); + assertTrue("machinename should validate", allowLocal.isValid("machinename")); + + // Check the localhost one with a few others + assertTrue("apache.org should validate", allowLocal.isValid("apache.org")); + assertFalse("domain name with spaces shouldn't validate", allowLocal.isValid(" apache.org ")); + } + + public void testIDN() { + assertTrue("b\u00fccher.ch in IDN should validate", validator.isValid("www.xn--bcher-kva.ch")); + } + + public void testIDNJava6OrLater() { + String version = System.getProperty("java.version"); + if (version.compareTo("1.6") < 0) { + System.out.println("Cannot run Unicode IDN tests"); + return; // Cannot run the test + } // xn--d1abbgf6aiiy.xn--p1ai http://президент.рф + assertTrue("b\u00fccher.ch should validate", validator.isValid("www.b\u00fccher.ch")); + assertTrue("xn--d1abbgf6aiiy.xn--p1ai should validate", validator.isValid("xn--d1abbgf6aiiy.xn--p1ai")); + assertTrue("президент.рф should validate", validator.isValid("президент.рф")); + assertFalse("www.\uFFFD.ch FFFD should fail", validator.isValid("www.\uFFFD.ch")); + } + + // RFC2396: domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + public void testRFC2396domainlabel() { // use fixed valid TLD + assertTrue("a.ch should validate", validator.isValid("a.ch")); + assertTrue("9.ch should validate", validator.isValid("9.ch")); + assertTrue("az.ch should validate", validator.isValid("az.ch")); + assertTrue("09.ch should validate", validator.isValid("09.ch")); + assertTrue("9-1.ch should validate", validator.isValid("9-1.ch")); + assertFalse("91-.ch should not validate", validator.isValid("91-.ch")); + assertFalse("-.ch should not validate", validator.isValid("-.ch")); + } + + // RFC2396 toplabel = alpha | alpha *( alphanum | "-" ) alphanum + public void testRFC2396toplabel() { + // These tests use non-existent TLDs so currently need to use a package protected method + assertTrue("a.c (alpha) should validate", validator.isValidDomainSyntax("a.c")); + assertTrue("a.cc (alpha alpha) should validate", validator.isValidDomainSyntax("a.cc")); + assertTrue("a.c9 (alpha alphanum) should validate", validator.isValidDomainSyntax("a.c9")); + assertTrue("a.c-9 (alpha - alphanum) should validate", validator.isValidDomainSyntax("a.c-9")); + assertTrue("a.c-z (alpha - alpha) should validate", validator.isValidDomainSyntax("a.c-z")); + + assertFalse("a.9c (alphanum alpha) should fail", validator.isValidDomainSyntax("a.9c")); + assertFalse("a.c- (alpha -) should fail", validator.isValidDomainSyntax("a.c-")); + assertFalse("a.- (-) should fail", validator.isValidDomainSyntax("a.-")); + assertFalse("a.-9 (- alphanum) should fail", validator.isValidDomainSyntax("a.-9")); + } + + public void testDomainNoDots() {// rfc1123 + assertTrue("a (alpha) should validate", validator.isValidDomainSyntax("a")); + assertTrue("9 (alphanum) should validate", validator.isValidDomainSyntax("9")); + assertTrue("c-z (alpha - alpha) should validate", validator.isValidDomainSyntax("c-z")); + + assertFalse("c- (alpha -) should fail", validator.isValidDomainSyntax("c-")); + assertFalse("-c (- alpha) should fail", validator.isValidDomainSyntax("-c")); + assertFalse("- (-) should fail", validator.isValidDomainSyntax("-")); + } + + public void testValidator297() { + assertTrue("xn--d1abbgf6aiiy.xn--p1ai should validate", validator.isValid("xn--d1abbgf6aiiy.xn--p1ai")); // This uses a valid TLD + } + + // labels are a max of 63 chars and domains 253 + public void testValidator306() { + final String longString = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789A"; + assertEquals(63, longString.length()); // 26 * 2 + 11 + + assertTrue("63 chars label should validate", validator.isValidDomainSyntax(longString+".com")); + assertFalse("64 chars label should fail", validator.isValidDomainSyntax(longString+"x.com")); + + assertTrue("63 chars TLD should validate", validator.isValidDomainSyntax("test."+longString)); + assertFalse("64 chars TLD should fail", validator.isValidDomainSyntax("test.x"+longString)); + + final String longDomain = + longString + + "." + longString + + "." + longString + + "." + longString.substring(0,61) + ; + assertEquals(253, longDomain.length()); + assertTrue("253 chars domain should validate", validator.isValidDomainSyntax(longDomain)); + assertFalse("254 chars domain should fail", validator.isValidDomainSyntax(longDomain+"x")); + } + + // Check that IDN.toASCII behaves as it should (when wrapped by DomainValidator.unicodeToASCII) + // Tests show that method incorrectly trims a trailing "." character + public void testUnicodeToASCII() { + String[] asciidots = { + "", + ",", + ".", // fails IDN.toASCII, but should pass wrapped version + "a.", // ditto + "a.b", + "a..b", + "a...b", + ".a", + "..a", + }; + for(String s : asciidots) { + assertEquals(s,DomainValidator.unicodeToASCII(s)); + } + // RFC3490 3.1. 1) +// Whenever dots are used as label separators, the following +// characters MUST be recognized as dots: U+002E (full stop), U+3002 +// (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61 +// (halfwidth ideographic full stop). + final String[][] otherDots = { + {"b\u3002", "b.",}, + {"b\uFF0E", "b.",}, + {"b\uFF61", "b.",}, + {"\u3002", ".",}, + {"\uFF0E", ".",}, + {"\uFF61", ".",}, + }; + for(String[] s : otherDots) { + assertEquals(s[1],DomainValidator.unicodeToASCII(s[0])); + } + } + + // Check if IDN.toASCII is broken or not + public void testIsIDNtoASCIIBroken() { + System.out.println(">>DomainValidatorTest.testIsIDNtoASCIIBroken()"); + final String input = "."; + final boolean ok = input.equals(IDN.toASCII(input)); + System.out.println("IDN.toASCII is " + (ok? "OK" : "BROKEN")); + String[] props = { + "java.version", // Java Runtime Environment version + "java.vendor", // Java Runtime Environment vendor + "java.vm.specification.version", // Java Virtual Machine specification version + "java.vm.specification.vendor", // Java Virtual Machine specification vendor + "java.vm.specification.name", // Java Virtual Machine specification name + "java.vm.version", // Java Virtual Machine implementation version + "java.vm.vendor", // Java Virtual Machine implementation vendor + "java.vm.name", // Java Virtual Machine implementation name + "java.specification.version", // Java Runtime Environment specification version + "java.specification.vendor", // Java Runtime Environment specification vendor + "java.specification.name", // Java Runtime Environment specification name + "java.class.version", // Java class format version number + }; + for(String t : props) { + System.out.println(t + "=" + System.getProperty(t)); + } + System.out.println("< ianaTlds = new HashSet<>(); // keep for comparison with array contents + DomainValidator dv = DomainValidator.getInstance(); + File txtFile = new File("target/tlds-alpha-by-domain.txt"); + long timestamp = download(txtFile, "https://data.iana.org/TLD/tlds-alpha-by-domain.txt", 0L); + final File htmlFile = new File("target/tlds-alpha-by-domain.html"); + // N.B. sometimes the html file may be updated a day or so after the txt file + // if the txt file contains entries not found in the html file, try again in a day or two + download(htmlFile,"https://www.iana.org/domains/root/db", timestamp); + + BufferedReader br = new BufferedReader(new FileReader(txtFile)); + String line; + final String header; + line = br.readLine(); // header + if (line.startsWith("# Version ")) { + header = line.substring(2); + } else { + br.close(); + throw new IOException("File does not have expected Version header"); + } + final boolean generateUnicodeTlds = false; // Change this to generate Unicode TLDs as well + + // Parse html page to get entries + Map htmlInfo = getHtmlInfo(htmlFile); + Map missingTLD = new TreeMap<>(); // stores entry and comments as String[] + Map missingCC = new TreeMap<>(); + while((line = br.readLine()) != null) { + if (!line.startsWith("#")) { + final String unicodeTld; // only different from asciiTld if that was punycode + final String asciiTld = line.toLowerCase(Locale.ENGLISH); + if (line.startsWith("XN--")) { + unicodeTld = IDN.toUnicode(line); + } else { + unicodeTld = asciiTld; + } + if (!dv.isValidTld(asciiTld)) { + String [] info = htmlInfo.get(asciiTld); + if (info != null) { + String type = info[0]; + String comment = info[1]; + if ("country-code".equals(type)) { // Which list to use? + missingCC.put(asciiTld, unicodeTld + " " + comment); + if (generateUnicodeTlds) { + missingCC.put(unicodeTld, asciiTld + " " + comment); + } + } else { + missingTLD.put(asciiTld, unicodeTld + " " + comment); + if (generateUnicodeTlds) { + missingTLD.put(unicodeTld, asciiTld + " " + comment); + } + } + } else { + System.err.println("Expected to find HTML info for "+ asciiTld); + } + } + ianaTlds.add(asciiTld); + // Don't merge these conditions; generateUnicodeTlds is final so needs to be separate to avoid a warning + if (generateUnicodeTlds) { + if (!unicodeTld.equals(asciiTld)) { + ianaTlds.add(unicodeTld); + } + } + } + } + br.close(); + // List html entries not in TLD text list + for(String key : (new TreeMap<>(htmlInfo)).keySet()) { + if (!ianaTlds.contains(key)) { + if (isNotInRootZone(key)) { + System.out.println("INFO: HTML entry not yet in root zone: "+key); + } else { + System.err.println("WARN: Expected to find text entry for html: "+key); + } + } + } + if (!missingTLD.isEmpty()) { + printMap(header, missingTLD, "TLD"); + } + if (!missingCC.isEmpty()) { + printMap(header, missingCC, "CC"); + } + // Check if internal tables contain any additional entries + isInIanaList("INFRASTRUCTURE_TLDS", ianaTlds); + isInIanaList("COUNTRY_CODE_TLDS", ianaTlds); + isInIanaList("GENERIC_TLDS", ianaTlds); + // Don't check local TLDS isInIanaList("LOCAL_TLDS", ianaTlds); + System.out.println("Finished checks"); + } + + private static void printMap(final String header, Map map, String string) { + System.out.println("Entries missing from "+ string +" List\n"); + if (header != null) { + System.out.println(" // Taken from " + header); + } + for (Map.Entry me : map.entrySet()) { + System.out.println(" \"" + me.getKey() + "\", // " + me.getValue()); + } + System.out.println("\nDone"); + } + + private static Map getHtmlInfo(final File f) throws IOException { + final Map info = new HashMap<>(); + +// .ax + final Pattern domain = Pattern.compile(".*country-code + final Pattern type = Pattern.compile("\\s+([^<]+)"); +// +// Ålands landskapsregering + final Pattern comment = Pattern.compile("\\s+([^<]+)"); + + final BufferedReader br = new BufferedReader(new FileReader(f)); + String line; + while((line=br.readLine())!=null){ + Matcher m = domain.matcher(line); + if (m.lookingAt()) { + String dom = m.group(1); + String typ = "??"; + String com = "??"; + line = br.readLine(); + while (line.matches("^\\s*$")) { // extra blank lines introduced + line = br.readLine(); + } + Matcher t = type.matcher(line); + if (t.lookingAt()) { + typ = t.group(1); + line = br.readLine(); + if (line.matches("\\s+.*")){ + line = br.readLine(); + } + line = br.readLine(); + } + // Should have comment; is it wrapped? + while(!line.matches(".*.*")){ + line += " " +br.readLine(); + } + Matcher n = comment.matcher(line); + if (n.lookingAt()) { + com = n.group(1); + } + // Don't save unused entries + if (com.contains("Not assigned") || com.contains("Retired") || typ.equals("test")) { +// System.out.println("Ignored: " + typ + " " + dom + " " +com); + } else { + info.put(dom.toLowerCase(Locale.ENGLISH), new String[]{typ, com}); +// System.out.println("Storing: " + typ + " " + dom + " " +com); + } + } else { + System.err.println("Unexpected type: " + line); + } + } + } + br.close(); + return info; + } + + /* + * Download a file if it is more recent than our cached copy. + * Unfortunately the server does not seem to honour If-Modified-Since for the + * Html page, so we check if it is newer than the txt file and skip download if so + */ + private static long download(File f, String tldurl, long timestamp) throws IOException { + final int HOUR = 60*60*1000; // an hour in ms + final long modTime; + // For testing purposes, don't download files more than once an hour + if (f.canRead()) { + modTime = f.lastModified(); + if (modTime > System.currentTimeMillis()-HOUR) { + System.out.println("Skipping download - found recent " + f); + return modTime; + } + } else { + modTime = 0; + } + HttpURLConnection hc = (HttpURLConnection) new URL(tldurl).openConnection(); + if (modTime > 0) { + SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");//Sun, 06 Nov 1994 08:49:37 GMT + String since = sdf.format(new Date(modTime)); + hc.addRequestProperty("If-Modified-Since", since); + System.out.println("Found " + f + " with date " + since); + } + if (hc.getResponseCode() == 304) { + System.out.println("Already have most recent " + tldurl); + } else { + System.out.println("Downloading " + tldurl); + byte[] buff = new byte[1024]; + InputStream is = hc.getInputStream(); + + FileOutputStream fos = new FileOutputStream(f); + int len; + while((len=is.read(buff)) != -1) { + fos.write(buff, 0, len); + } + fos.close(); + is.close(); + System.out.println("Done"); + } + return f.lastModified(); + } + + /** + * Check whether the domain is in the root zone currently. + * Reads the URL http://www.iana.org/domains/root/db/*domain*.html + * (using a local disk cache) + * and checks for the string "This domain is not present in the root zone at this time." + * @param domain the domain to check + * @return true if the string is found + */ + private static boolean isNotInRootZone(String domain) { + String tldurl = "http://www.iana.org/domains/root/db/" + domain + ".html"; + File rootCheck = new File("target","tld_" + domain + ".html"); + BufferedReader in = null; + try { + download(rootCheck, tldurl, 0L); + in = new BufferedReader(new FileReader(rootCheck)); + String inputLine; + while ((inputLine = in.readLine()) != null) { + if (inputLine.contains("This domain is not present in the root zone at this time.")) { + return true; + } + } + in.close(); + } catch (IOException e) { + } finally { + closeQuietly(in); + } + return false; + } + + private static void closeQuietly(Closeable in) { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + + // isInIanaList and isSorted are split into two methods. + // If/when access to the arrays is possible without reflection, the intermediate + // methods can be dropped + private static boolean isInIanaList(String arrayName, Set ianaTlds) throws Exception { + Field f = DomainValidator.class.getDeclaredField(arrayName); + final boolean isPrivate = Modifier.isPrivate(f.getModifiers()); + if (isPrivate) { + f.setAccessible(true); + } + String[] array = (String[]) f.get(null); + try { + return isInIanaList(arrayName, array, ianaTlds); + } finally { + if (isPrivate) { + f.setAccessible(false); + } + } + } + + private static boolean isInIanaList(String name, String [] array, Set ianaTlds) { + for (String s : array) { + if (!ianaTlds.contains(s)) { + System.out.println(name + " contains unexpected value: " + s); + } + } + return true; + } + + private static boolean isSortedLowerCase(String arrayName) throws Exception { + Field f = DomainValidator.class.getDeclaredField(arrayName); + final boolean isPrivate = Modifier.isPrivate(f.getModifiers()); + if (isPrivate) { + f.setAccessible(true); + } + String[] array = (String[]) f.get(null); + try { + return isSortedLowerCase(arrayName, array); + } finally { + if (isPrivate) { + f.setAccessible(false); + } + } + } + + private static boolean isLowerCase(String string) { + return string.equals(string.toLowerCase(Locale.ENGLISH)); + } + + // Check if an array is strictly sorted - and lowerCase + private static boolean isSortedLowerCase(String name, String [] array) { + boolean sorted = true; + boolean strictlySorted = true; + final int length = array.length; + boolean lowerCase = isLowerCase(array[length-1]); // Check the last entry + for(int i = 0; i < length-1; i++) { // compare all but last entry with next + final String entry = array[i]; + final String nextEntry = array[i+1]; + final int cmp = entry.compareTo(nextEntry); + if (cmp > 0) { // out of order + System.out.println("Out of order entry: " + entry + " < " + nextEntry + " in " + name); + sorted = false; + } else if (cmp == 0) { + strictlySorted = false; + System.out.println("Duplicated entry: " + entry + " in " + name); + } + if (!isLowerCase(entry)) { + System.out.println("Non lowerCase entry: " + entry + " in " + name); + lowerCase = false; + } + } + return sorted && strictlySorted && lowerCase; + } +} diff --git a/core/src/test/java/jenkins/org/apache/commons/validator/routines/InetAddressValidatorTest.java b/core/src/test/java/jenkins/org/apache/commons/validator/routines/InetAddressValidatorTest.java new file mode 100644 index 000000000000..866c39cab190 --- /dev/null +++ b/core/src/test/java/jenkins/org/apache/commons/validator/routines/InetAddressValidatorTest.java @@ -0,0 +1,651 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jenkins.org.apache.commons.validator.routines; + +import junit.framework.TestCase; + +/** + * Test cases for InetAddressValidator. + * + * @version $Revision$ + */ +public class InetAddressValidatorTest extends TestCase { + + private InetAddressValidator validator; + + /** + * Constructor. + */ + public InetAddressValidatorTest(String name) { + super(name); + } + + @Override + protected void setUp() { + validator = new InetAddressValidator(); + } + + /** + * Test IPs that point to real, well-known hosts (without actually looking them up). + */ + public void testInetAddressesFromTheWild() { + assertTrue("www.apache.org IP should be valid", validator.isValid("140.211.11.130")); + assertTrue("www.l.google.com IP should be valid", validator.isValid("72.14.253.103")); + assertTrue("fsf.org IP should be valid", validator.isValid("199.232.41.5")); + assertTrue("appscs.ign.com IP should be valid", validator.isValid("216.35.123.87")); + } + + public void testVALIDATOR_335() { + assertTrue("2001:0438:FFFE:0000:0000:0000:0000:0A35 should be valid", validator.isValid("2001:0438:FFFE:0000:0000:0000:0000:0A35")); + } + + public void testVALIDATOR_419() { + String addr; + addr = "0:0:0:0:0:0:13.1.68.3"; + assertTrue(addr, validator.isValid(addr)); + addr = "0:0:0:0:0:FFFF:129.144.52.38"; + assertTrue(addr, validator.isValid(addr)); + addr = "::13.1.68.3"; + assertTrue(addr, validator.isValid(addr)); + addr = "::FFFF:129.144.52.38"; + assertTrue(addr, validator.isValid(addr)); + + addr = "::ffff:192.168.1.1:192.168.1.1"; + assertFalse(addr, validator.isValid(addr)); + addr = "::192.168.1.1:192.168.1.1"; + assertFalse(addr, validator.isValid(addr)); + } + + /** + * Inet6Address may also contain a scope id + */ + public void testVALIDATOR_445() { + String [] valid = { + "2001:0000:1234:0000:0000:C1C0:ABCD:0876", + "2001:0000:1234:0000:0000:C1C0:ABCD:0876/123", + "2001:0000:1234:0000:0000:C1C0:ABCD:0876/0", + "2001:0000:1234:0000:0000:C1C0:ABCD:0876%0", + "2001:0000:1234:0000:0000:C1C0:ABCD:0876%abcdefgh", + }; + String [] invalid = { + "2001:0000:1234:0000:0000:C1C0:ABCD:0876/129", // too big + "2001:0000:1234:0000:0000:C1C0:ABCD:0876/-0", // sign not allowed + "2001:0000:1234:0000:0000:C1C0:ABCD:0876/+0", // sign not allowed + "2001:0000:1234:0000:0000:C1C0:ABCD:0876/10O", // non-digit + "2001:0000:1234:0000:0000:C1C0:ABCD:0876/0%0", // /bits before %node-id + "2001:0000:1234:0000:0000:C1C0:ABCD:0876%abc defgh", // space in node id + "2001:0000:1234:0000:0000:C1C0:ABCD:0876%abc%defgh", // '%' in node id + }; + for(String item : valid) { + assertTrue(String.format("%s should be valid", item), validator.isValid(item)); + } + for(String item : invalid) { + assertFalse(String.format("%s should be invalid", item), validator.isValid(item)); + } + } + + /** + * Test valid and invalid IPs from each address class. + */ + public void testInetAddressesByClass() { + assertTrue("class A IP should be valid", validator.isValid("24.25.231.12")); + assertFalse("illegal class A IP should be invalid", validator.isValid("2.41.32.324")); + + assertTrue("class B IP should be valid", validator.isValid("135.14.44.12")); + assertFalse("illegal class B IP should be invalid", validator.isValid("154.123.441.123")); + + assertTrue("class C IP should be valid", validator.isValid("213.25.224.32")); + assertFalse("illegal class C IP should be invalid", validator.isValid("201.543.23.11")); + + assertTrue("class D IP should be valid", validator.isValid("229.35.159.6")); + assertFalse("illegal class D IP should be invalid", validator.isValid("231.54.11.987")); + + assertTrue("class E IP should be valid", validator.isValid("248.85.24.92")); + assertFalse("illegal class E IP should be invalid", validator.isValid("250.21.323.48")); + } + + /** + * Test reserved IPs. + */ + public void testReservedInetAddresses() { + assertTrue("localhost IP should be valid", validator.isValid("127.0.0.1")); + assertTrue("broadcast IP should be valid", validator.isValid("255.255.255.255")); + } + + /** + * Test obviously broken IPs. + */ + public void testBrokenInetAddresses() { + assertFalse("IP with characters should be invalid", validator.isValid("124.14.32.abc")); + // TODO: there is some debate as to whether leading zeros should be allowed + // They are ambiguous: does the leading 0 mean octal? + assertFalse("IP with leading zeroes should be invalid", validator.isValid("124.14.32.01")); + assertFalse("IP with three groups should be invalid", validator.isValid("23.64.12")); + assertFalse("IP with five groups should be invalid", validator.isValid("26.34.23.77.234")); + assertFalse("IP empty string should be invalid", validator.isValidInet6Address(""));// empty string + } + + /** + * Test IPv6 addresses. + *

    These tests were ported from a + * Perl script.

    + * + */ + public void testIPv6() { + // The original Perl script contained a lot of duplicate tests. + // I removed the duplicates I noticed, but there may be more. + assertFalse("IPV6 empty string should be invalid", validator.isValidInet6Address(""));// empty string + assertTrue("IPV6 ::1 should be valid", validator.isValidInet6Address("::1"));// loopback, compressed, non-routable + assertTrue("IPV6 :: should be valid", validator.isValidInet6Address("::"));// unspecified, compressed, non-routable + assertTrue("IPV6 0:0:0:0:0:0:0:1 should be valid", validator.isValidInet6Address("0:0:0:0:0:0:0:1"));// loopback, full + assertTrue("IPV6 0:0:0:0:0:0:0:0 should be valid", validator.isValidInet6Address("0:0:0:0:0:0:0:0"));// unspecified, full + assertTrue("IPV6 2001:DB8:0:0:8:800:200C:417A should be valid", validator.isValidInet6Address("2001:DB8:0:0:8:800:200C:417A"));// unicast, full + assertTrue("IPV6 FF01:0:0:0:0:0:0:101 should be valid", validator.isValidInet6Address("FF01:0:0:0:0:0:0:101"));// multicast, full + assertTrue("IPV6 2001:DB8::8:800:200C:417A should be valid", validator.isValidInet6Address("2001:DB8::8:800:200C:417A"));// unicast, compressed + assertTrue("IPV6 FF01::101 should be valid", validator.isValidInet6Address("FF01::101"));// multicast, compressed + assertFalse("IPV6 2001:DB8:0:0:8:800:200C:417A:221 should be invalid", validator.isValidInet6Address("2001:DB8:0:0:8:800:200C:417A:221"));// unicast, full + assertFalse("IPV6 FF01::101::2 should be invalid", validator.isValidInet6Address("FF01::101::2"));// multicast, compressed + assertTrue("IPV6 fe80::217:f2ff:fe07:ed62 should be valid", validator.isValidInet6Address("fe80::217:f2ff:fe07:ed62")); + assertTrue("IPV6 2001:0000:1234:0000:0000:C1C0:ABCD:0876 should be valid", validator.isValidInet6Address("2001:0000:1234:0000:0000:C1C0:ABCD:0876")); + assertTrue("IPV6 3ffe:0b00:0000:0000:0001:0000:0000:000a should be valid", validator.isValidInet6Address("3ffe:0b00:0000:0000:0001:0000:0000:000a")); + assertTrue("IPV6 FF02:0000:0000:0000:0000:0000:0000:0001 should be valid", validator.isValidInet6Address("FF02:0000:0000:0000:0000:0000:0000:0001")); + assertTrue("IPV6 0000:0000:0000:0000:0000:0000:0000:0001 should be valid", validator.isValidInet6Address("0000:0000:0000:0000:0000:0000:0000:0001")); + assertTrue("IPV6 0000:0000:0000:0000:0000:0000:0000:0000 should be valid", validator.isValidInet6Address("0000:0000:0000:0000:0000:0000:0000:0000")); + assertFalse("IPV6 02001:0000:1234:0000:0000:C1C0:ABCD:0876 should be invalid", validator.isValidInet6Address("02001:0000:1234:0000:0000:C1C0:ABCD:0876")); // extra 0 not allowed! + assertFalse("IPV6 2001:0000:1234:0000:00001:C1C0:ABCD:0876 should be invalid", validator.isValidInet6Address("2001:0000:1234:0000:00001:C1C0:ABCD:0876")); // extra 0 not allowed! + assertFalse("IPV6 2001:0000:1234:0000:0000:C1C0:ABCD:0876 0 should be invalid", validator.isValidInet6Address("2001:0000:1234:0000:0000:C1C0:ABCD:0876 0")); // junk after valid address + assertFalse("IPV6 2001:0000:1234: 0000:0000:C1C0:ABCD:0876 should be invalid", validator.isValidInet6Address("2001:0000:1234: 0000:0000:C1C0:ABCD:0876")); // internal space + assertFalse("IPV6 3ffe:0b00:0000:0001:0000:0000:000a should be invalid", validator.isValidInet6Address("3ffe:0b00:0000:0001:0000:0000:000a")); // seven segments + assertFalse("IPV6 FF02:0000:0000:0000:0000:0000:0000:0000:0001 should be invalid", validator.isValidInet6Address("FF02:0000:0000:0000:0000:0000:0000:0000:0001")); // nine segments + assertFalse("IPV6 3ffe:b00::1::a should be invalid", validator.isValidInet6Address("3ffe:b00::1::a")); // double "::" + assertFalse("IPV6 ::1111:2222:3333:4444:5555:6666:: should be invalid", validator.isValidInet6Address("::1111:2222:3333:4444:5555:6666::")); // double "::" + assertTrue("IPV6 2::10 should be valid", validator.isValidInet6Address("2::10")); + assertTrue("IPV6 ff02::1 should be valid", validator.isValidInet6Address("ff02::1")); + assertTrue("IPV6 fe80:: should be valid", validator.isValidInet6Address("fe80::")); + assertTrue("IPV6 2002:: should be valid", validator.isValidInet6Address("2002::")); + assertTrue("IPV6 2001:db8:: should be valid", validator.isValidInet6Address("2001:db8::")); + assertTrue("IPV6 2001:0db8:1234:: should be valid", validator.isValidInet6Address("2001:0db8:1234::")); + assertTrue("IPV6 ::ffff:0:0 should be valid", validator.isValidInet6Address("::ffff:0:0")); + assertTrue("IPV6 1:2:3:4:5:6:7:8 should be valid", validator.isValidInet6Address("1:2:3:4:5:6:7:8")); + assertTrue("IPV6 1:2:3:4:5:6::8 should be valid", validator.isValidInet6Address("1:2:3:4:5:6::8")); + assertTrue("IPV6 1:2:3:4:5::8 should be valid", validator.isValidInet6Address("1:2:3:4:5::8")); + assertTrue("IPV6 1:2:3:4::8 should be valid", validator.isValidInet6Address("1:2:3:4::8")); + assertTrue("IPV6 1:2:3::8 should be valid", validator.isValidInet6Address("1:2:3::8")); + assertTrue("IPV6 1:2::8 should be valid", validator.isValidInet6Address("1:2::8")); + assertTrue("IPV6 1::8 should be valid", validator.isValidInet6Address("1::8")); + assertTrue("IPV6 1::2:3:4:5:6:7 should be valid", validator.isValidInet6Address("1::2:3:4:5:6:7")); + assertTrue("IPV6 1::2:3:4:5:6 should be valid", validator.isValidInet6Address("1::2:3:4:5:6")); + assertTrue("IPV6 1::2:3:4:5 should be valid", validator.isValidInet6Address("1::2:3:4:5")); + assertTrue("IPV6 1::2:3:4 should be valid", validator.isValidInet6Address("1::2:3:4")); + assertTrue("IPV6 1::2:3 should be valid", validator.isValidInet6Address("1::2:3")); + assertTrue("IPV6 ::2:3:4:5:6:7:8 should be valid", validator.isValidInet6Address("::2:3:4:5:6:7:8")); + assertTrue("IPV6 ::2:3:4:5:6:7 should be valid", validator.isValidInet6Address("::2:3:4:5:6:7")); + assertTrue("IPV6 ::2:3:4:5:6 should be valid", validator.isValidInet6Address("::2:3:4:5:6")); + assertTrue("IPV6 ::2:3:4:5 should be valid", validator.isValidInet6Address("::2:3:4:5")); + assertTrue("IPV6 ::2:3:4 should be valid", validator.isValidInet6Address("::2:3:4")); + assertTrue("IPV6 ::2:3 should be valid", validator.isValidInet6Address("::2:3")); + assertTrue("IPV6 ::8 should be valid", validator.isValidInet6Address("::8")); + assertTrue("IPV6 1:2:3:4:5:6:: should be valid", validator.isValidInet6Address("1:2:3:4:5:6::")); + assertTrue("IPV6 1:2:3:4:5:: should be valid", validator.isValidInet6Address("1:2:3:4:5::")); + assertTrue("IPV6 1:2:3:4:: should be valid", validator.isValidInet6Address("1:2:3:4::")); + assertTrue("IPV6 1:2:3:: should be valid", validator.isValidInet6Address("1:2:3::")); + assertTrue("IPV6 1:2:: should be valid", validator.isValidInet6Address("1:2::")); + assertTrue("IPV6 1:: should be valid", validator.isValidInet6Address("1::")); + assertTrue("IPV6 1:2:3:4:5::7:8 should be valid", validator.isValidInet6Address("1:2:3:4:5::7:8")); + assertFalse("IPV6 1:2:3::4:5::7:8 should be invalid", validator.isValidInet6Address("1:2:3::4:5::7:8")); // Double "::" + assertFalse("IPV6 12345::6:7:8 should be invalid", validator.isValidInet6Address("12345::6:7:8")); + assertTrue("IPV6 1:2:3:4::7:8 should be valid", validator.isValidInet6Address("1:2:3:4::7:8")); + assertTrue("IPV6 1:2:3::7:8 should be valid", validator.isValidInet6Address("1:2:3::7:8")); + assertTrue("IPV6 1:2::7:8 should be valid", validator.isValidInet6Address("1:2::7:8")); + assertTrue("IPV6 1::7:8 should be valid", validator.isValidInet6Address("1::7:8")); + // IPv4 addresses as dotted-quads + assertTrue("IPV6 1:2:3:4:5:6:1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3:4:5:6:1.2.3.4")); + assertTrue("IPV6 1:2:3:4:5::1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3:4:5::1.2.3.4")); + assertTrue("IPV6 1:2:3:4::1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3:4::1.2.3.4")); + assertTrue("IPV6 1:2:3::1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3::1.2.3.4")); + assertTrue("IPV6 1:2::1.2.3.4 should be valid", validator.isValidInet6Address("1:2::1.2.3.4")); + assertTrue("IPV6 1::1.2.3.4 should be valid", validator.isValidInet6Address("1::1.2.3.4")); + assertTrue("IPV6 1:2:3:4::5:1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3:4::5:1.2.3.4")); + assertTrue("IPV6 1:2:3::5:1.2.3.4 should be valid", validator.isValidInet6Address("1:2:3::5:1.2.3.4")); + assertTrue("IPV6 1:2::5:1.2.3.4 should be valid", validator.isValidInet6Address("1:2::5:1.2.3.4")); + assertTrue("IPV6 1::5:1.2.3.4 should be valid", validator.isValidInet6Address("1::5:1.2.3.4")); + assertTrue("IPV6 1::5:11.22.33.44 should be valid", validator.isValidInet6Address("1::5:11.22.33.44")); + assertFalse("IPV6 1::5:400.2.3.4 should be invalid", validator.isValidInet6Address("1::5:400.2.3.4")); + assertFalse("IPV6 1::5:260.2.3.4 should be invalid", validator.isValidInet6Address("1::5:260.2.3.4")); + assertFalse("IPV6 1::5:256.2.3.4 should be invalid", validator.isValidInet6Address("1::5:256.2.3.4")); + assertFalse("IPV6 1::5:1.256.3.4 should be invalid", validator.isValidInet6Address("1::5:1.256.3.4")); + assertFalse("IPV6 1::5:1.2.256.4 should be invalid", validator.isValidInet6Address("1::5:1.2.256.4")); + assertFalse("IPV6 1::5:1.2.3.256 should be invalid", validator.isValidInet6Address("1::5:1.2.3.256")); + assertFalse("IPV6 1::5:300.2.3.4 should be invalid", validator.isValidInet6Address("1::5:300.2.3.4")); + assertFalse("IPV6 1::5:1.300.3.4 should be invalid", validator.isValidInet6Address("1::5:1.300.3.4")); + assertFalse("IPV6 1::5:1.2.300.4 should be invalid", validator.isValidInet6Address("1::5:1.2.300.4")); + assertFalse("IPV6 1::5:1.2.3.300 should be invalid", validator.isValidInet6Address("1::5:1.2.3.300")); + assertFalse("IPV6 1::5:900.2.3.4 should be invalid", validator.isValidInet6Address("1::5:900.2.3.4")); + assertFalse("IPV6 1::5:1.900.3.4 should be invalid", validator.isValidInet6Address("1::5:1.900.3.4")); + assertFalse("IPV6 1::5:1.2.900.4 should be invalid", validator.isValidInet6Address("1::5:1.2.900.4")); + assertFalse("IPV6 1::5:1.2.3.900 should be invalid", validator.isValidInet6Address("1::5:1.2.3.900")); + assertFalse("IPV6 1::5:300.300.300.300 should be invalid", validator.isValidInet6Address("1::5:300.300.300.300")); + assertFalse("IPV6 1::5:3000.30.30.30 should be invalid", validator.isValidInet6Address("1::5:3000.30.30.30")); + assertFalse("IPV6 1::400.2.3.4 should be invalid", validator.isValidInet6Address("1::400.2.3.4")); + assertFalse("IPV6 1::260.2.3.4 should be invalid", validator.isValidInet6Address("1::260.2.3.4")); + assertFalse("IPV6 1::256.2.3.4 should be invalid", validator.isValidInet6Address("1::256.2.3.4")); + assertFalse("IPV6 1::1.256.3.4 should be invalid", validator.isValidInet6Address("1::1.256.3.4")); + assertFalse("IPV6 1::1.2.256.4 should be invalid", validator.isValidInet6Address("1::1.2.256.4")); + assertFalse("IPV6 1::1.2.3.256 should be invalid", validator.isValidInet6Address("1::1.2.3.256")); + assertFalse("IPV6 1::300.2.3.4 should be invalid", validator.isValidInet6Address("1::300.2.3.4")); + assertFalse("IPV6 1::1.300.3.4 should be invalid", validator.isValidInet6Address("1::1.300.3.4")); + assertFalse("IPV6 1::1.2.300.4 should be invalid", validator.isValidInet6Address("1::1.2.300.4")); + assertFalse("IPV6 1::1.2.3.300 should be invalid", validator.isValidInet6Address("1::1.2.3.300")); + assertFalse("IPV6 1::900.2.3.4 should be invalid", validator.isValidInet6Address("1::900.2.3.4")); + assertFalse("IPV6 1::1.900.3.4 should be invalid", validator.isValidInet6Address("1::1.900.3.4")); + assertFalse("IPV6 1::1.2.900.4 should be invalid", validator.isValidInet6Address("1::1.2.900.4")); + assertFalse("IPV6 1::1.2.3.900 should be invalid", validator.isValidInet6Address("1::1.2.3.900")); + assertFalse("IPV6 1::300.300.300.300 should be invalid", validator.isValidInet6Address("1::300.300.300.300")); + assertFalse("IPV6 1::3000.30.30.30 should be invalid", validator.isValidInet6Address("1::3000.30.30.30")); + assertFalse("IPV6 ::400.2.3.4 should be invalid", validator.isValidInet6Address("::400.2.3.4")); + assertFalse("IPV6 ::260.2.3.4 should be invalid", validator.isValidInet6Address("::260.2.3.4")); + assertFalse("IPV6 ::256.2.3.4 should be invalid", validator.isValidInet6Address("::256.2.3.4")); + assertFalse("IPV6 ::1.256.3.4 should be invalid", validator.isValidInet6Address("::1.256.3.4")); + assertFalse("IPV6 ::1.2.256.4 should be invalid", validator.isValidInet6Address("::1.2.256.4")); + assertFalse("IPV6 ::1.2.3.256 should be invalid", validator.isValidInet6Address("::1.2.3.256")); + assertFalse("IPV6 ::300.2.3.4 should be invalid", validator.isValidInet6Address("::300.2.3.4")); + assertFalse("IPV6 ::1.300.3.4 should be invalid", validator.isValidInet6Address("::1.300.3.4")); + assertFalse("IPV6 ::1.2.300.4 should be invalid", validator.isValidInet6Address("::1.2.300.4")); + assertFalse("IPV6 ::1.2.3.300 should be invalid", validator.isValidInet6Address("::1.2.3.300")); + assertFalse("IPV6 ::900.2.3.4 should be invalid", validator.isValidInet6Address("::900.2.3.4")); + assertFalse("IPV6 ::1.900.3.4 should be invalid", validator.isValidInet6Address("::1.900.3.4")); + assertFalse("IPV6 ::1.2.900.4 should be invalid", validator.isValidInet6Address("::1.2.900.4")); + assertFalse("IPV6 ::1.2.3.900 should be invalid", validator.isValidInet6Address("::1.2.3.900")); + assertFalse("IPV6 ::300.300.300.300 should be invalid", validator.isValidInet6Address("::300.300.300.300")); + assertFalse("IPV6 ::3000.30.30.30 should be invalid", validator.isValidInet6Address("::3000.30.30.30")); + assertTrue("IPV6 fe80::217:f2ff:254.7.237.98 should be valid", validator.isValidInet6Address("fe80::217:f2ff:254.7.237.98")); + assertTrue("IPV6 ::ffff:192.168.1.26 should be valid", validator.isValidInet6Address("::ffff:192.168.1.26")); + assertFalse("IPV6 2001:1:1:1:1:1:255Z255X255Y255 should be invalid", validator.isValidInet6Address("2001:1:1:1:1:1:255Z255X255Y255")); // garbage instead of "." in IPv4 + assertFalse("IPV6 ::ffff:192x168.1.26 should be invalid", validator.isValidInet6Address("::ffff:192x168.1.26")); // ditto + assertTrue("IPV6 ::ffff:192.168.1.1 should be valid", validator.isValidInet6Address("::ffff:192.168.1.1")); + assertTrue("IPV6 0:0:0:0:0:0:13.1.68.3 should be valid", validator.isValidInet6Address("0:0:0:0:0:0:13.1.68.3"));// IPv4-compatible IPv6 address, full, deprecated + assertTrue("IPV6 0:0:0:0:0:FFFF:129.144.52.38 should be valid", validator.isValidInet6Address("0:0:0:0:0:FFFF:129.144.52.38"));// IPv4-mapped IPv6 address, full + assertTrue("IPV6 ::13.1.68.3 should be valid", validator.isValidInet6Address("::13.1.68.3"));// IPv4-compatible IPv6 address, compressed, deprecated + assertTrue("IPV6 ::FFFF:129.144.52.38 should be valid", validator.isValidInet6Address("::FFFF:129.144.52.38"));// IPv4-mapped IPv6 address, compressed + assertTrue("IPV6 fe80:0:0:0:204:61ff:254.157.241.86 should be valid", validator.isValidInet6Address("fe80:0:0:0:204:61ff:254.157.241.86")); + assertTrue("IPV6 fe80::204:61ff:254.157.241.86 should be valid", validator.isValidInet6Address("fe80::204:61ff:254.157.241.86")); + assertTrue("IPV6 ::ffff:12.34.56.78 should be valid", validator.isValidInet6Address("::ffff:12.34.56.78")); + assertFalse("IPV6 ::ffff:2.3.4 should be invalid", validator.isValidInet6Address("::ffff:2.3.4")); + assertFalse("IPV6 ::ffff:257.1.2.3 should be invalid", validator.isValidInet6Address("::ffff:257.1.2.3")); + assertFalse("IPV6 1.2.3.4 should be invalid", validator.isValidInet6Address("1.2.3.4")); + assertFalse("IPV6 1.2.3.4:1111:2222:3333:4444::5555 should be invalid", validator.isValidInet6Address("1.2.3.4:1111:2222:3333:4444::5555")); + assertFalse("IPV6 1.2.3.4:1111:2222:3333::5555 should be invalid", validator.isValidInet6Address("1.2.3.4:1111:2222:3333::5555")); + assertFalse("IPV6 1.2.3.4:1111:2222::5555 should be invalid", validator.isValidInet6Address("1.2.3.4:1111:2222::5555")); + assertFalse("IPV6 1.2.3.4:1111::5555 should be invalid", validator.isValidInet6Address("1.2.3.4:1111::5555")); + assertFalse("IPV6 1.2.3.4::5555 should be invalid", validator.isValidInet6Address("1.2.3.4::5555")); + assertFalse("IPV6 1.2.3.4:: should be invalid", validator.isValidInet6Address("1.2.3.4::")); + // Testing IPv4 addresses represented as dotted-quads + // Leading zeroes in IPv4 addresses not allowed: some systems treat the leading "0" in ".086" as the start of an octal number + // Update: The BNF in RFC-3986 explicitly defines the dec-octet (for IPv4 addresses) not to have a leading zero + assertFalse("IPV6 fe80:0000:0000:0000:0204:61ff:254.157.241.086 should be invalid", validator.isValidInet6Address("fe80:0000:0000:0000:0204:61ff:254.157.241.086")); + assertTrue("IPV6 ::ffff:192.0.2.128 should be valid", validator.isValidInet6Address("::ffff:192.0.2.128")); // but this is OK, since there's a single digit + assertFalse("IPV6 XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4 should be invalid", validator.isValidInet6Address("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:00.00.00.00 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:00.00.00.00")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:000.000.000.000 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:000.000.000.000")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:256.256.256.256 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:256.256.256.256")); + assertTrue("IPV6 fe80:0000:0000:0000:0204:61ff:fe9d:f156 should be valid", validator.isValidInet6Address("fe80:0000:0000:0000:0204:61ff:fe9d:f156")); + assertTrue("IPV6 fe80:0:0:0:204:61ff:fe9d:f156 should be valid", validator.isValidInet6Address("fe80:0:0:0:204:61ff:fe9d:f156")); + assertTrue("IPV6 fe80::204:61ff:fe9d:f156 should be valid", validator.isValidInet6Address("fe80::204:61ff:fe9d:f156")); + assertFalse("IPV6 : should be invalid", validator.isValidInet6Address(":")); + assertTrue("IPV6 ::ffff:c000:280 should be valid", validator.isValidInet6Address("::ffff:c000:280")); + assertFalse("IPV6 1111:2222:3333:4444::5555: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::5555:")); + assertFalse("IPV6 1111:2222:3333::5555: should be invalid", validator.isValidInet6Address("1111:2222:3333::5555:")); + assertFalse("IPV6 1111:2222::5555: should be invalid", validator.isValidInet6Address("1111:2222::5555:")); + assertFalse("IPV6 1111::5555: should be invalid", validator.isValidInet6Address("1111::5555:")); + assertFalse("IPV6 ::5555: should be invalid", validator.isValidInet6Address("::5555:")); + assertFalse("IPV6 ::: should be invalid", validator.isValidInet6Address(":::")); + assertFalse("IPV6 1111: should be invalid", validator.isValidInet6Address("1111:")); + assertFalse("IPV6 :1111:2222:3333:4444::5555 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::5555")); + assertFalse("IPV6 :1111:2222:3333::5555 should be invalid", validator.isValidInet6Address(":1111:2222:3333::5555")); + assertFalse("IPV6 :1111:2222::5555 should be invalid", validator.isValidInet6Address(":1111:2222::5555")); + assertFalse("IPV6 :1111::5555 should be invalid", validator.isValidInet6Address(":1111::5555")); + assertFalse("IPV6 :::5555 should be invalid", validator.isValidInet6Address(":::5555")); + assertTrue("IPV6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 should be valid", validator.isValidInet6Address("2001:0db8:85a3:0000:0000:8a2e:0370:7334")); + assertTrue("IPV6 2001:db8:85a3:0:0:8a2e:370:7334 should be valid", validator.isValidInet6Address("2001:db8:85a3:0:0:8a2e:370:7334")); + assertTrue("IPV6 2001:db8:85a3::8a2e:370:7334 should be valid", validator.isValidInet6Address("2001:db8:85a3::8a2e:370:7334")); + assertTrue("IPV6 2001:0db8:0000:0000:0000:0000:1428:57ab should be valid", validator.isValidInet6Address("2001:0db8:0000:0000:0000:0000:1428:57ab")); + assertTrue("IPV6 2001:0db8:0000:0000:0000::1428:57ab should be valid", validator.isValidInet6Address("2001:0db8:0000:0000:0000::1428:57ab")); + assertTrue("IPV6 2001:0db8:0:0:0:0:1428:57ab should be valid", validator.isValidInet6Address("2001:0db8:0:0:0:0:1428:57ab")); + assertTrue("IPV6 2001:0db8:0:0::1428:57ab should be valid", validator.isValidInet6Address("2001:0db8:0:0::1428:57ab")); + assertTrue("IPV6 2001:0db8::1428:57ab should be valid", validator.isValidInet6Address("2001:0db8::1428:57ab")); + assertTrue("IPV6 2001:db8::1428:57ab should be valid", validator.isValidInet6Address("2001:db8::1428:57ab")); + assertTrue("IPV6 ::ffff:0c22:384e should be valid", validator.isValidInet6Address("::ffff:0c22:384e")); + assertTrue("IPV6 2001:0db8:1234:0000:0000:0000:0000:0000 should be valid", validator.isValidInet6Address("2001:0db8:1234:0000:0000:0000:0000:0000")); + assertTrue("IPV6 2001:0db8:1234:ffff:ffff:ffff:ffff:ffff should be valid", validator.isValidInet6Address("2001:0db8:1234:ffff:ffff:ffff:ffff:ffff")); + assertTrue("IPV6 2001:db8:a::123 should be valid", validator.isValidInet6Address("2001:db8:a::123")); + assertFalse("IPV6 123 should be invalid", validator.isValidInet6Address("123")); + assertFalse("IPV6 ldkfj should be invalid", validator.isValidInet6Address("ldkfj")); + assertFalse("IPV6 2001::FFD3::57ab should be invalid", validator.isValidInet6Address("2001::FFD3::57ab")); + assertFalse("IPV6 2001:db8:85a3::8a2e:37023:7334 should be invalid", validator.isValidInet6Address("2001:db8:85a3::8a2e:37023:7334")); + assertFalse("IPV6 2001:db8:85a3::8a2e:370k:7334 should be invalid", validator.isValidInet6Address("2001:db8:85a3::8a2e:370k:7334")); + assertFalse("IPV6 1:2:3:4:5:6:7:8:9 should be invalid", validator.isValidInet6Address("1:2:3:4:5:6:7:8:9")); + assertFalse("IPV6 1::2::3 should be invalid", validator.isValidInet6Address("1::2::3")); + assertFalse("IPV6 1:::3:4:5 should be invalid", validator.isValidInet6Address("1:::3:4:5")); + assertFalse("IPV6 1:2:3::4:5:6:7:8:9 should be invalid", validator.isValidInet6Address("1:2:3::4:5:6:7:8:9")); + assertTrue("IPV6 1111:2222:3333:4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888")); + assertTrue("IPV6 1111:2222:3333:4444:5555:6666:7777:: should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777::")); + assertTrue("IPV6 1111:2222:3333:4444:5555:6666:: should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666::")); + assertTrue("IPV6 1111:2222:3333:4444:5555:: should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555::")); + assertTrue("IPV6 1111:2222:3333:4444:: should be valid", validator.isValidInet6Address("1111:2222:3333:4444::")); + assertTrue("IPV6 1111:2222:3333:: should be valid", validator.isValidInet6Address("1111:2222:3333::")); + assertTrue("IPV6 1111:2222:: should be valid", validator.isValidInet6Address("1111:2222::")); + assertTrue("IPV6 1111:: should be valid", validator.isValidInet6Address("1111::")); + assertTrue("IPV6 1111:2222:3333:4444:5555:6666::8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666::8888")); + assertTrue("IPV6 1111:2222:3333:4444:5555::8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555::8888")); + assertTrue("IPV6 1111:2222:3333:4444::8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::8888")); + assertTrue("IPV6 1111:2222:3333::8888 should be valid", validator.isValidInet6Address("1111:2222:3333::8888")); + assertTrue("IPV6 1111:2222::8888 should be valid", validator.isValidInet6Address("1111:2222::8888")); + assertTrue("IPV6 1111::8888 should be valid", validator.isValidInet6Address("1111::8888")); + assertTrue("IPV6 ::8888 should be valid", validator.isValidInet6Address("::8888")); + assertTrue("IPV6 1111:2222:3333:4444:5555::7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555::7777:8888")); + assertTrue("IPV6 1111:2222:3333:4444::7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::7777:8888")); + assertTrue("IPV6 1111:2222:3333::7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333::7777:8888")); + assertTrue("IPV6 1111:2222::7777:8888 should be valid", validator.isValidInet6Address("1111:2222::7777:8888")); + assertTrue("IPV6 1111::7777:8888 should be valid", validator.isValidInet6Address("1111::7777:8888")); + assertTrue("IPV6 ::7777:8888 should be valid", validator.isValidInet6Address("::7777:8888")); + assertTrue("IPV6 1111:2222:3333:4444::6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::6666:7777:8888")); + assertTrue("IPV6 1111:2222:3333::6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333::6666:7777:8888")); + assertTrue("IPV6 1111:2222::6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222::6666:7777:8888")); + assertTrue("IPV6 1111::6666:7777:8888 should be valid", validator.isValidInet6Address("1111::6666:7777:8888")); + assertTrue("IPV6 ::6666:7777:8888 should be valid", validator.isValidInet6Address("::6666:7777:8888")); + assertTrue("IPV6 1111:2222:3333::5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222:3333::5555:6666:7777:8888")); + assertTrue("IPV6 1111:2222::5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222::5555:6666:7777:8888")); + assertTrue("IPV6 1111::5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111::5555:6666:7777:8888")); + assertTrue("IPV6 ::5555:6666:7777:8888 should be valid", validator.isValidInet6Address("::5555:6666:7777:8888")); + assertTrue("IPV6 1111:2222::4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111:2222::4444:5555:6666:7777:8888")); + assertTrue("IPV6 1111::4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111::4444:5555:6666:7777:8888")); + assertTrue("IPV6 ::4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("::4444:5555:6666:7777:8888")); + assertTrue("IPV6 1111::3333:4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("1111::3333:4444:5555:6666:7777:8888")); + assertTrue("IPV6 ::3333:4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("::3333:4444:5555:6666:7777:8888")); + assertTrue("IPV6 ::2222:3333:4444:5555:6666:7777:8888 should be valid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:7777:8888")); + assertTrue("IPV6 1111:2222:3333:4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:123.123.123.123")); + assertTrue("IPV6 1111:2222:3333:4444:5555::123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333:4444:5555::123.123.123.123")); + assertTrue("IPV6 1111:2222:3333:4444::123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::123.123.123.123")); + assertTrue("IPV6 1111:2222:3333::123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333::123.123.123.123")); + assertTrue("IPV6 1111:2222::123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222::123.123.123.123")); + assertTrue("IPV6 1111::123.123.123.123 should be valid", validator.isValidInet6Address("1111::123.123.123.123")); + assertTrue("IPV6 ::123.123.123.123 should be valid", validator.isValidInet6Address("::123.123.123.123")); + assertTrue("IPV6 1111:2222:3333:4444::6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333:4444::6666:123.123.123.123")); + assertTrue("IPV6 1111:2222:3333::6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333::6666:123.123.123.123")); + assertTrue("IPV6 1111:2222::6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222::6666:123.123.123.123")); + assertTrue("IPV6 1111::6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111::6666:123.123.123.123")); + assertTrue("IPV6 ::6666:123.123.123.123 should be valid", validator.isValidInet6Address("::6666:123.123.123.123")); + assertTrue("IPV6 1111:2222:3333::5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222:3333::5555:6666:123.123.123.123")); + assertTrue("IPV6 1111:2222::5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222::5555:6666:123.123.123.123")); + assertTrue("IPV6 1111::5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111::5555:6666:123.123.123.123")); + assertTrue("IPV6 ::5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("::5555:6666:123.123.123.123")); + assertTrue("IPV6 1111:2222::4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111:2222::4444:5555:6666:123.123.123.123")); + assertTrue("IPV6 1111::4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111::4444:5555:6666:123.123.123.123")); + assertTrue("IPV6 ::4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("::4444:5555:6666:123.123.123.123")); + assertTrue("IPV6 1111::3333:4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("1111::3333:4444:5555:6666:123.123.123.123")); + assertTrue("IPV6 ::2222:3333:4444:5555:6666:123.123.123.123 should be valid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:123.123.123.123")); + // Trying combinations of "0" and "::" + // These are all syntactically correct, but are bad form + // because "0" adjacent to "::" should be combined into "::" + assertTrue("IPV6 ::0:0:0:0:0:0:0 should be valid", validator.isValidInet6Address("::0:0:0:0:0:0:0")); + assertTrue("IPV6 ::0:0:0:0:0:0 should be valid", validator.isValidInet6Address("::0:0:0:0:0:0")); + assertTrue("IPV6 ::0:0:0:0:0 should be valid", validator.isValidInet6Address("::0:0:0:0:0")); + assertTrue("IPV6 ::0:0:0:0 should be valid", validator.isValidInet6Address("::0:0:0:0")); + assertTrue("IPV6 ::0:0:0 should be valid", validator.isValidInet6Address("::0:0:0")); + assertTrue("IPV6 ::0:0 should be valid", validator.isValidInet6Address("::0:0")); + assertTrue("IPV6 ::0 should be valid", validator.isValidInet6Address("::0")); + assertTrue("IPV6 0:0:0:0:0:0:0:: should be valid", validator.isValidInet6Address("0:0:0:0:0:0:0::")); + assertTrue("IPV6 0:0:0:0:0:0:: should be valid", validator.isValidInet6Address("0:0:0:0:0:0::")); + assertTrue("IPV6 0:0:0:0:0:: should be valid", validator.isValidInet6Address("0:0:0:0:0::")); + assertTrue("IPV6 0:0:0:0:: should be valid", validator.isValidInet6Address("0:0:0:0::")); + assertTrue("IPV6 0:0:0:: should be valid", validator.isValidInet6Address("0:0:0::")); + assertTrue("IPV6 0:0:: should be valid", validator.isValidInet6Address("0:0::")); + assertTrue("IPV6 0:: should be valid", validator.isValidInet6Address("0::")); + // Invalid data + assertFalse("IPV6 XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX should be invalid", validator.isValidInet6Address("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX")); + // Too many components + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:8888:9999 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888:9999")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:8888:: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888::")); + assertFalse("IPV6 ::2222:3333:4444:5555:6666:7777:8888:9999 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:7777:8888:9999")); + // Too few components + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666")); + assertFalse("IPV6 1111:2222:3333:4444:5555 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555")); + assertFalse("IPV6 1111:2222:3333:4444 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444")); + assertFalse("IPV6 1111:2222:3333 should be invalid", validator.isValidInet6Address("1111:2222:3333")); + assertFalse("IPV6 1111:2222 should be invalid", validator.isValidInet6Address("1111:2222")); + assertFalse("IPV6 1111 should be invalid", validator.isValidInet6Address("1111")); + // Missing : + assertFalse("IPV6 11112222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("11112222:3333:4444:5555:6666:7777:8888")); + assertFalse("IPV6 1111:22223333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:22223333:4444:5555:6666:7777:8888")); + assertFalse("IPV6 1111:2222:33334444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:33334444:5555:6666:7777:8888")); + assertFalse("IPV6 1111:2222:3333:44445555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:44445555:6666:7777:8888")); + assertFalse("IPV6 1111:2222:3333:4444:55556666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:55556666:7777:8888")); + assertFalse("IPV6 1111:2222:3333:4444:5555:66667777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:66667777:8888")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:77778888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:77778888")); + // Missing : intended for :: + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888:")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:")); + assertFalse("IPV6 1111:2222:3333:4444:5555: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:")); + assertFalse("IPV6 1111:2222:3333:4444: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:")); + assertFalse("IPV6 1111:2222:3333: should be invalid", validator.isValidInet6Address("1111:2222:3333:")); + assertFalse("IPV6 1111:2222: should be invalid", validator.isValidInet6Address("1111:2222:")); + assertFalse("IPV6 :8888 should be invalid", validator.isValidInet6Address(":8888")); + assertFalse("IPV6 :7777:8888 should be invalid", validator.isValidInet6Address(":7777:8888")); + assertFalse("IPV6 :6666:7777:8888 should be invalid", validator.isValidInet6Address(":6666:7777:8888")); + assertFalse("IPV6 :5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":5555:6666:7777:8888")); + assertFalse("IPV6 :4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":4444:5555:6666:7777:8888")); + assertFalse("IPV6 :3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":3333:4444:5555:6666:7777:8888")); + assertFalse("IPV6 :2222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":2222:3333:4444:5555:6666:7777:8888")); + assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666:7777:8888")); + // ::: + assertFalse("IPV6 :::2222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::2222:3333:4444:5555:6666:7777:8888")); + assertFalse("IPV6 1111:::3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:::3333:4444:5555:6666:7777:8888")); + assertFalse("IPV6 1111:2222:::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:::4444:5555:6666:7777:8888")); + assertFalse("IPV6 1111:2222:3333:::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:::5555:6666:7777:8888")); + assertFalse("IPV6 1111:2222:3333:4444:::6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:::6666:7777:8888")); + assertFalse("IPV6 1111:2222:3333:4444:5555:::7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:::7777:8888")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:::8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:::8888")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:::")); + // Double :: + assertFalse("IPV6 ::2222::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("::2222::4444:5555:6666:7777:8888")); + assertFalse("IPV6 ::2222:3333::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("::2222:3333::5555:6666:7777:8888")); + assertFalse("IPV6 ::2222:3333:4444::6666:7777:8888 should be invalid", validator.isValidInet6Address("::2222:3333:4444::6666:7777:8888")); + assertFalse("IPV6 ::2222:3333:4444:5555::7777:8888 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555::7777:8888")); + assertFalse("IPV6 ::2222:3333:4444:5555:7777::8888 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:7777::8888")); + assertFalse("IPV6 ::2222:3333:4444:5555:7777:8888:: should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:7777:8888::")); + assertFalse("IPV6 1111::3333::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address("1111::3333::5555:6666:7777:8888")); + assertFalse("IPV6 1111::3333:4444::6666:7777:8888 should be invalid", validator.isValidInet6Address("1111::3333:4444::6666:7777:8888")); + assertFalse("IPV6 1111::3333:4444:5555::7777:8888 should be invalid", validator.isValidInet6Address("1111::3333:4444:5555::7777:8888")); + assertFalse("IPV6 1111::3333:4444:5555:6666::8888 should be invalid", validator.isValidInet6Address("1111::3333:4444:5555:6666::8888")); + assertFalse("IPV6 1111::3333:4444:5555:6666:7777:: should be invalid", validator.isValidInet6Address("1111::3333:4444:5555:6666:7777::")); + assertFalse("IPV6 1111:2222::4444::6666:7777:8888 should be invalid", validator.isValidInet6Address("1111:2222::4444::6666:7777:8888")); + assertFalse("IPV6 1111:2222::4444:5555::7777:8888 should be invalid", validator.isValidInet6Address("1111:2222::4444:5555::7777:8888")); + assertFalse("IPV6 1111:2222::4444:5555:6666::8888 should be invalid", validator.isValidInet6Address("1111:2222::4444:5555:6666::8888")); + assertFalse("IPV6 1111:2222::4444:5555:6666:7777:: should be invalid", validator.isValidInet6Address("1111:2222::4444:5555:6666:7777::")); + assertFalse("IPV6 1111:2222:3333::5555::7777:8888 should be invalid", validator.isValidInet6Address("1111:2222:3333::5555::7777:8888")); + assertFalse("IPV6 1111:2222:3333::5555:6666::8888 should be invalid", validator.isValidInet6Address("1111:2222:3333::5555:6666::8888")); + assertFalse("IPV6 1111:2222:3333::5555:6666:7777:: should be invalid", validator.isValidInet6Address("1111:2222:3333::5555:6666:7777::")); + assertFalse("IPV6 1111:2222:3333:4444::6666::8888 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::6666::8888")); + assertFalse("IPV6 1111:2222:3333:4444::6666:7777:: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::6666:7777::")); + assertFalse("IPV6 1111:2222:3333:4444:5555::7777:: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555::7777::")); + // Too many components" + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666::1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666::1.2.3.4")); + assertFalse("IPV6 ::2222:3333:4444:5555:6666:7777:1.2.3.4 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:7777:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:1.2.3.4.5 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:1.2.3.4.5")); + // Too few components + assertFalse("IPV6 1111:2222:3333:4444:5555:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:4444:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:1.2.3.4")); + assertFalse("IPV6 1111:2222:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:1.2.3.4")); + assertFalse("IPV6 1111:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:1.2.3.4")); + assertFalse("IPV6 1.2.3.4 should be invalid", validator.isValidInet6Address("1.2.3.4")); + // Missing : + assertFalse("IPV6 11112222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("11112222:3333:4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 1111:22223333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:22223333:4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 1111:2222:33334444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:33334444:5555:6666:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:44445555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:44445555:6666:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:4444:55556666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:55556666:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:4444:5555:66661.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:66661.2.3.4")); + // Missing . + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:255255.255.255 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:255255.255.255")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:255.255255.255 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:255.255255.255")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:255.255.255255 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:255.255.255255")); + // Missing : intended for :: + assertFalse("IPV6 :1.2.3.4 should be invalid", validator.isValidInet6Address(":1.2.3.4")); + assertFalse("IPV6 :6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":6666:1.2.3.4")); + assertFalse("IPV6 :5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":5555:6666:1.2.3.4")); + assertFalse("IPV6 :4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 :3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":3333:4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 :2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":2222:3333:4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666:1.2.3.4")); + // ::: + assertFalse("IPV6 :::2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::2222:3333:4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 1111:::3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:::3333:4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 1111:2222:::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:::4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:::5555:6666:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:4444:::6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:::6666:1.2.3.4")); + assertFalse("IPV6 1111:2222:3333:4444:5555:::1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:::1.2.3.4")); + // Double :: + assertFalse("IPV6 ::2222::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("::2222::4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 ::2222:3333::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("::2222:3333::5555:6666:1.2.3.4")); + assertFalse("IPV6 ::2222:3333:4444::6666:1.2.3.4 should be invalid", validator.isValidInet6Address("::2222:3333:4444::6666:1.2.3.4")); + assertFalse("IPV6 ::2222:3333:4444:5555::1.2.3.4 should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555::1.2.3.4")); + assertFalse("IPV6 1111::3333::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111::3333::5555:6666:1.2.3.4")); + assertFalse("IPV6 1111::3333:4444::6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111::3333:4444::6666:1.2.3.4")); + assertFalse("IPV6 1111::3333:4444:5555::1.2.3.4 should be invalid", validator.isValidInet6Address("1111::3333:4444:5555::1.2.3.4")); + assertFalse("IPV6 1111:2222::4444::6666:1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222::4444::6666:1.2.3.4")); + assertFalse("IPV6 1111:2222::4444:5555::1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222::4444:5555::1.2.3.4")); + assertFalse("IPV6 1111:2222:3333::5555::1.2.3.4 should be invalid", validator.isValidInet6Address("1111:2222:3333::5555::1.2.3.4")); + // Missing parts + assertFalse("IPV6 ::. should be invalid", validator.isValidInet6Address("::.")); + assertFalse("IPV6 ::.. should be invalid", validator.isValidInet6Address("::..")); + assertFalse("IPV6 ::... should be invalid", validator.isValidInet6Address("::...")); + assertFalse("IPV6 ::1... should be invalid", validator.isValidInet6Address("::1...")); + assertFalse("IPV6 ::1.2.. should be invalid", validator.isValidInet6Address("::1.2..")); + assertFalse("IPV6 ::1.2.3. should be invalid", validator.isValidInet6Address("::1.2.3.")); + assertFalse("IPV6 ::.2.. should be invalid", validator.isValidInet6Address("::.2..")); + assertFalse("IPV6 ::.2.3. should be invalid", validator.isValidInet6Address("::.2.3.")); + assertFalse("IPV6 ::.2.3.4 should be invalid", validator.isValidInet6Address("::.2.3.4")); + assertFalse("IPV6 ::..3. should be invalid", validator.isValidInet6Address("::..3.")); + assertFalse("IPV6 ::..3.4 should be invalid", validator.isValidInet6Address("::..3.4")); + assertFalse("IPV6 ::...4 should be invalid", validator.isValidInet6Address("::...4")); + // Extra : in front + assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:7777:: should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666:7777::")); + assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:: should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666::")); + assertFalse("IPV6 :1111:2222:3333:4444:5555:: should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555::")); + assertFalse("IPV6 :1111:2222:3333:4444:: should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::")); + assertFalse("IPV6 :1111:2222:3333:: should be invalid", validator.isValidInet6Address(":1111:2222:3333::")); + assertFalse("IPV6 :1111:2222:: should be invalid", validator.isValidInet6Address(":1111:2222::")); + assertFalse("IPV6 :1111:: should be invalid", validator.isValidInet6Address(":1111::")); + assertFalse("IPV6 :1111:2222:3333:4444:5555:6666::8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666::8888")); + assertFalse("IPV6 :1111:2222:3333:4444:5555::8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555::8888")); + assertFalse("IPV6 :1111:2222:3333:4444::8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::8888")); + assertFalse("IPV6 :1111:2222:3333::8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333::8888")); + assertFalse("IPV6 :1111:2222::8888 should be invalid", validator.isValidInet6Address(":1111:2222::8888")); + assertFalse("IPV6 :1111::8888 should be invalid", validator.isValidInet6Address(":1111::8888")); + assertFalse("IPV6 :::8888 should be invalid", validator.isValidInet6Address(":::8888")); + assertFalse("IPV6 :1111:2222:3333:4444:5555::7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555::7777:8888")); + assertFalse("IPV6 :1111:2222:3333:4444::7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::7777:8888")); + assertFalse("IPV6 :1111:2222:3333::7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333::7777:8888")); + assertFalse("IPV6 :1111:2222::7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222::7777:8888")); + assertFalse("IPV6 :1111::7777:8888 should be invalid", validator.isValidInet6Address(":1111::7777:8888")); + assertFalse("IPV6 :::7777:8888 should be invalid", validator.isValidInet6Address(":::7777:8888")); + assertFalse("IPV6 :1111:2222:3333:4444::6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::6666:7777:8888")); + assertFalse("IPV6 :1111:2222:3333::6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333::6666:7777:8888")); + assertFalse("IPV6 :1111:2222::6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222::6666:7777:8888")); + assertFalse("IPV6 :1111::6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111::6666:7777:8888")); + assertFalse("IPV6 :::6666:7777:8888 should be invalid", validator.isValidInet6Address(":::6666:7777:8888")); + assertFalse("IPV6 :1111:2222:3333::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222:3333::5555:6666:7777:8888")); + assertFalse("IPV6 :1111:2222::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222::5555:6666:7777:8888")); + assertFalse("IPV6 :1111::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111::5555:6666:7777:8888")); + assertFalse("IPV6 :::5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::5555:6666:7777:8888")); + assertFalse("IPV6 :1111:2222::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111:2222::4444:5555:6666:7777:8888")); + assertFalse("IPV6 :1111::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111::4444:5555:6666:7777:8888")); + assertFalse("IPV6 :::4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::4444:5555:6666:7777:8888")); + assertFalse("IPV6 :1111::3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":1111::3333:4444:5555:6666:7777:8888")); + assertFalse("IPV6 :::3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::3333:4444:5555:6666:7777:8888")); + assertFalse("IPV6 :::2222:3333:4444:5555:6666:7777:8888 should be invalid", validator.isValidInet6Address(":::2222:3333:4444:5555:6666:7777:8888")); + assertFalse("IPV6 :1111:2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 :1111:2222:3333:4444:5555::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444:5555::1.2.3.4")); + assertFalse("IPV6 :1111:2222:3333:4444::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::1.2.3.4")); + assertFalse("IPV6 :1111:2222:3333::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333::1.2.3.4")); + assertFalse("IPV6 :1111:2222::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222::1.2.3.4")); + assertFalse("IPV6 :1111::1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::1.2.3.4")); + assertFalse("IPV6 :::1.2.3.4 should be invalid", validator.isValidInet6Address(":::1.2.3.4")); + assertFalse("IPV6 :1111:2222:3333:4444::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333:4444::6666:1.2.3.4")); + assertFalse("IPV6 :1111:2222:3333::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333::6666:1.2.3.4")); + assertFalse("IPV6 :1111:2222::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222::6666:1.2.3.4")); + assertFalse("IPV6 :1111::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::6666:1.2.3.4")); + assertFalse("IPV6 :::6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::6666:1.2.3.4")); + assertFalse("IPV6 :1111:2222:3333::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222:3333::5555:6666:1.2.3.4")); + assertFalse("IPV6 :1111:2222::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222::5555:6666:1.2.3.4")); + assertFalse("IPV6 :1111::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::5555:6666:1.2.3.4")); + assertFalse("IPV6 :::5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::5555:6666:1.2.3.4")); + assertFalse("IPV6 :1111:2222::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111:2222::4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 :1111::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 :::4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 :1111::3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":1111::3333:4444:5555:6666:1.2.3.4")); + assertFalse("IPV6 :::2222:3333:4444:5555:6666:1.2.3.4 should be invalid", validator.isValidInet6Address(":::2222:3333:4444:5555:6666:1.2.3.4")); + // Extra : at end + assertFalse("IPV6 1111:2222:3333:4444:5555:6666:7777::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:7777:::")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666:::")); + assertFalse("IPV6 1111:2222:3333:4444:5555::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:::")); + assertFalse("IPV6 1111:2222:3333:4444::: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:::")); + assertFalse("IPV6 1111:2222:3333::: should be invalid", validator.isValidInet6Address("1111:2222:3333:::")); + assertFalse("IPV6 1111:2222::: should be invalid", validator.isValidInet6Address("1111:2222:::")); + assertFalse("IPV6 1111::: should be invalid", validator.isValidInet6Address("1111:::")); + assertFalse("IPV6 1111:2222:3333:4444:5555:6666::8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555:6666::8888:")); + assertFalse("IPV6 1111:2222:3333:4444:5555::8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555::8888:")); + assertFalse("IPV6 1111:2222:3333:4444::8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::8888:")); + assertFalse("IPV6 1111:2222:3333::8888: should be invalid", validator.isValidInet6Address("1111:2222:3333::8888:")); + assertFalse("IPV6 1111:2222::8888: should be invalid", validator.isValidInet6Address("1111:2222::8888:")); + assertFalse("IPV6 1111::8888: should be invalid", validator.isValidInet6Address("1111::8888:")); + assertFalse("IPV6 ::8888: should be invalid", validator.isValidInet6Address("::8888:")); + assertFalse("IPV6 1111:2222:3333:4444:5555::7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444:5555::7777:8888:")); + assertFalse("IPV6 1111:2222:3333:4444::7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::7777:8888:")); + assertFalse("IPV6 1111:2222:3333::7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333::7777:8888:")); + assertFalse("IPV6 1111:2222::7777:8888: should be invalid", validator.isValidInet6Address("1111:2222::7777:8888:")); + assertFalse("IPV6 1111::7777:8888: should be invalid", validator.isValidInet6Address("1111::7777:8888:")); + assertFalse("IPV6 ::7777:8888: should be invalid", validator.isValidInet6Address("::7777:8888:")); + assertFalse("IPV6 1111:2222:3333:4444::6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333:4444::6666:7777:8888:")); + assertFalse("IPV6 1111:2222:3333::6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333::6666:7777:8888:")); + assertFalse("IPV6 1111:2222::6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222::6666:7777:8888:")); + assertFalse("IPV6 1111::6666:7777:8888: should be invalid", validator.isValidInet6Address("1111::6666:7777:8888:")); + assertFalse("IPV6 ::6666:7777:8888: should be invalid", validator.isValidInet6Address("::6666:7777:8888:")); + assertFalse("IPV6 1111:2222:3333::5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222:3333::5555:6666:7777:8888:")); + assertFalse("IPV6 1111:2222::5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222::5555:6666:7777:8888:")); + assertFalse("IPV6 1111::5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111::5555:6666:7777:8888:")); + assertFalse("IPV6 ::5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("::5555:6666:7777:8888:")); + assertFalse("IPV6 1111:2222::4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111:2222::4444:5555:6666:7777:8888:")); + assertFalse("IPV6 1111::4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111::4444:5555:6666:7777:8888:")); + assertFalse("IPV6 ::4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("::4444:5555:6666:7777:8888:")); + assertFalse("IPV6 1111::3333:4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("1111::3333:4444:5555:6666:7777:8888:")); + assertFalse("IPV6 ::3333:4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("::3333:4444:5555:6666:7777:8888:")); + assertFalse("IPV6 ::2222:3333:4444:5555:6666:7777:8888: should be invalid", validator.isValidInet6Address("::2222:3333:4444:5555:6666:7777:8888:")); + assertTrue("IPV6 0:a:b:c:d:e:f:: should be valid", validator.isValidInet6Address("0:a:b:c:d:e:f::")); + assertTrue("IPV6 ::0:a:b:c:d:e:f should be valid", validator.isValidInet6Address("::0:a:b:c:d:e:f")); // syntactically correct, but bad form (::0:... could be combined) + assertTrue("IPV6 a:b:c:d:e:f:0:: should be valid", validator.isValidInet6Address("a:b:c:d:e:f:0::")); + assertFalse("IPV6 ':10.0.0.1 should be invalid", validator.isValidInet6Address("':10.0.0.1")); + } +} + + diff --git a/core/src/test/java/jenkins/org/apache/commons/validator/routines/RegexValidatorTest.java b/core/src/test/java/jenkins/org/apache/commons/validator/routines/RegexValidatorTest.java new file mode 100644 index 000000000000..bb05321fb323 --- /dev/null +++ b/core/src/test/java/jenkins/org/apache/commons/validator/routines/RegexValidatorTest.java @@ -0,0 +1,280 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jenkins.org.apache.commons.validator.routines; + +import java.util.Arrays; +import java.util.regex.PatternSyntaxException; +import junit.framework.TestCase; + +/** + * Test Case for RegexValidatorTest. + * + * @version $Revision$ + * @since Validator 1.4 + */ +public class RegexValidatorTest extends TestCase { + + private static final String REGEX = "^([abc]*)(?:\\-)([DEF]*)(?:\\-)([123]*)$"; + + private static final String COMPONENT_1 = "([abc]{3})"; + private static final String COMPONENT_2 = "([DEF]{3})"; + private static final String COMPONENT_3 = "([123]{3})"; + private static final String SEPARATOR_1 = "(?:\\-)"; + private static final String SEPARATOR_2 = "(?:\\s)"; + private static final String REGEX_1 = "^" + COMPONENT_1 + SEPARATOR_1 + COMPONENT_2 + SEPARATOR_1 + COMPONENT_3 + "$"; + private static final String REGEX_2 = "^" + COMPONENT_1 + SEPARATOR_2 + COMPONENT_2 + SEPARATOR_2 + COMPONENT_3 + "$"; + private static final String REGEX_3 = "^" + COMPONENT_1 + COMPONENT_2 + COMPONENT_3 + "$"; + private static final String[] MULTIPLE_REGEX = new String[] {REGEX_1, REGEX_2, REGEX_3}; + + /** + * Constrct a new test case. + * @param name The name of the test + */ + public RegexValidatorTest(String name) { + super(name); + } + + /** + * Test instance methods with single regular expression. + */ + public void testSingle() { + RegexValidator sensitive = new RegexValidator(REGEX); + RegexValidator insensitive = new RegexValidator(REGEX, false); + + // isValid() + assertTrue("Sensitive isValid() valid", sensitive.isValid("ac-DE-1")); + assertFalse("Sensitive isValid() invalid", sensitive.isValid("AB-de-1")); + assertTrue("Insensitive isValid() valid", insensitive.isValid("AB-de-1")); + assertFalse("Insensitive isValid() invalid", insensitive.isValid("ABd-de-1")); + + // validate() + assertEquals("Sensitive validate() valid", "acDE1", sensitive.validate("ac-DE-1")); + assertNull("Sensitive validate() invalid", sensitive.validate("AB-de-1")); + assertEquals("Insensitive validate() valid", "ABde1", insensitive.validate("AB-de-1")); + assertNull("Insensitive validate() invalid", insensitive.validate("ABd-de-1")); + + // match() + checkArray("Sensitive match() valid", new String[] {"ac", "DE", "1"}, sensitive.match("ac-DE-1")); + checkArray("Sensitive match() invalid", null, sensitive.match("AB-de-1")); + checkArray("Insensitive match() valid", new String[] {"AB", "de", "1"}, insensitive.match("AB-de-1")); + checkArray("Insensitive match() invalid", null, insensitive.match("ABd-de-1")); + assertEquals("validate one", "ABC", (new RegexValidator("^([A-Z]*)$")).validate("ABC")); + checkArray("match one", new String[] {"ABC"}, (new RegexValidator("^([A-Z]*)$")).match("ABC")); + } + + /** + * Test with multiple regular expressions (case sensitive). + */ + public void testMultipleSensitive() { + + // ------------ Set up Sensitive Validators + RegexValidator multiple = new RegexValidator(MULTIPLE_REGEX); + RegexValidator single1 = new RegexValidator(REGEX_1); + RegexValidator single2 = new RegexValidator(REGEX_2); + RegexValidator single3 = new RegexValidator(REGEX_3); + + // ------------ Set up test values + String value = "aac FDE 321"; + String expect = "aacFDE321"; + String[] array = new String[] {"aac", "FDE", "321"}; + + // isValid() + assertTrue("Sensitive isValid() Multiple", multiple.isValid(value)); + assertFalse("Sensitive isValid() 1st", single1.isValid(value)); + assertTrue("Sensitive isValid() 2nd", single2.isValid(value)); + assertFalse("Sensitive isValid() 3rd", single3.isValid(value)); + + // validate() + assertEquals("Sensitive validate() Multiple", expect, multiple.validate(value)); + assertNull("Sensitive validate() 1st", single1.validate(value)); + assertEquals("Sensitive validate() 2nd", expect, single2.validate(value)); + assertNull("Sensitive validate() 3rd", single3.validate(value)); + + // match() + checkArray("Sensitive match() Multiple", array, multiple.match(value)); + checkArray("Sensitive match() 1st", null, single1.match(value)); + checkArray("Sensitive match() 2nd", array, single2.match(value)); + checkArray("Sensitive match() 3rd", null, single3.match(value)); + + // All invalid + value = "AAC*FDE*321"; + assertFalse("isValid() Invalid", multiple.isValid(value)); + assertNull("validate() Invalid", multiple.validate(value)); + assertNull("match() Multiple", multiple.match(value)); + } + + /** + * Test with multiple regular expressions (case in-sensitive). + */ + public void testMultipleInsensitive() { + + // ------------ Set up In-sensitive Validators + RegexValidator multiple = new RegexValidator(MULTIPLE_REGEX, false); + RegexValidator single1 = new RegexValidator(REGEX_1, false); + RegexValidator single2 = new RegexValidator(REGEX_2, false); + RegexValidator single3 = new RegexValidator(REGEX_3, false); + + // ------------ Set up test values + String value = "AAC FDE 321"; + String expect = "AACFDE321"; + String[] array = new String[] {"AAC", "FDE", "321"}; + + // isValid() + assertTrue("isValid() Multiple", multiple.isValid(value)); + assertFalse("isValid() 1st", single1.isValid(value)); + assertTrue("isValid() 2nd", single2.isValid(value)); + assertFalse("isValid() 3rd", single3.isValid(value)); + + // validate() + assertEquals("validate() Multiple", expect, multiple.validate(value)); + assertNull("validate() 1st", single1.validate(value)); + assertEquals("validate() 2nd", expect, single2.validate(value)); + assertNull("validate() 3rd", single3.validate(value)); + + // match() + checkArray("match() Multiple", array, multiple.match(value)); + checkArray("match() 1st", null, single1.match(value)); + checkArray("match() 2nd", array, single2.match(value)); + checkArray("match() 3rd", null, single3.match(value)); + + // All invalid + value = "AAC*FDE*321"; + assertFalse("isValid() Invalid", multiple.isValid(value)); + assertNull("validate() Invalid", multiple.validate(value)); + assertNull("match() Multiple", multiple.match(value)); + } + + /** + * Test Null value + */ + public void testNullValue() { + + RegexValidator validator = new RegexValidator(REGEX); + assertFalse("Instance isValid()", validator.isValid(null)); + assertNull("Instance validate()", validator.validate(null)); + assertNull("Instance match()", validator.match(null)); + } + + /** + * Test exceptions + */ + public void testMissingRegex() { + + // Single Regular Expression - null + try { + new RegexValidator((String)null); + fail("Single Null - expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Single Null", "Regular expression[0] is missing", e.getMessage()); + } + + // Single Regular Expression - Zero Length + try { + new RegexValidator(""); + fail("Single Zero Length - expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Single Zero Length", "Regular expression[0] is missing", e.getMessage()); + } + + // Multiple Regular Expression - Null array + try { + new RegexValidator((String[])null); + fail("Null Array - expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Null Array", "Regular expressions are missing", e.getMessage()); + } + + // Multiple Regular Expression - Zero Length array + try { + new RegexValidator(new String[0]); + fail("Zero Length Array - expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Zero Length Array", "Regular expressions are missing", e.getMessage()); + } + + // Multiple Regular Expression - Array has Null + String[] expressions = new String[] {"ABC", null}; + try { + new RegexValidator(expressions); + fail("Array has Null - expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Array has Null", "Regular expression[1] is missing", e.getMessage()); + } + + // Multiple Regular Expression - Array has Zero Length + expressions = new String[] {"", "ABC"}; + try { + new RegexValidator(expressions); + fail("Array has Zero Length - expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Array has Zero Length", "Regular expression[0] is missing", e.getMessage()); + } + } + + /** + * Test exceptions + */ + public void testExceptions() { + String invalidRegex = "^([abCD12]*$"; + try { + new RegexValidator(invalidRegex); + } catch (PatternSyntaxException e) { + // expected + } + } + + /** + * Test toString() method + */ + public void testToString() { + RegexValidator single = new RegexValidator(REGEX); + assertEquals("Single", "RegexValidator{" + REGEX + "}", single.toString()); + + RegexValidator multiple = new RegexValidator(new String[] {REGEX, REGEX}); + assertEquals("Multiple", "RegexValidator{" + REGEX + "," + REGEX + "}", multiple.toString()); + } + + /** + * Compare two arrays + * @param label Label for the test + * @param expect Expected array + * @param result Actual array + */ + private void checkArray(String label, String[] expect, String[] result) { + + // Handle nulls + if (expect == null || result == null) { + if (expect == null && result == null) { + return; // valid, both null + } else { + fail(label + " Null expect=" + Arrays.toString(expect) + " result=" + Arrays.toString(result)); + } + return; // not strictly necessary, but prevents possible NPE below + } + + // Check Length + if (expect.length != result.length) { + fail(label + " Length expect=" + expect.length + " result=" + result.length); + } + + // Check Values + for (int i = 0; i < expect.length; i++) { + assertEquals(label +" value[" + i + "]", expect[i], result[i]); + } + } + +} diff --git a/core/src/test/java/jenkins/org/apache/commons/validator/routines/UrlValidatorTest.java b/core/src/test/java/jenkins/org/apache/commons/validator/routines/UrlValidatorTest.java new file mode 100644 index 000000000000..dcf8c16208ba --- /dev/null +++ b/core/src/test/java/jenkins/org/apache/commons/validator/routines/UrlValidatorTest.java @@ -0,0 +1,719 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jenkins.org.apache.commons.validator.routines; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import jenkins.org.apache.commons.validator.ResultPair; +import org.junit.Before; +import org.junit.Test; + +/** + * Performs Validation Test for url validations. + * + * @version $Revision$ + */ +public class UrlValidatorTest { + + private final boolean printStatus = false; + private final boolean printIndex = false;//print index that indicates current scheme,host,port,path, query test were using. + + @Before + public void setUp() { + for (int index = 0; index < testPartsIndex.length - 1; index++) { + testPartsIndex[index] = 0; + } + } + + @Test + public void testIsValid() { + testIsValid(testUrlParts, UrlValidator.ALLOW_ALL_SCHEMES); + setUp(); + long options = + UrlValidator.ALLOW_2_SLASHES + + UrlValidator.ALLOW_ALL_SCHEMES + + UrlValidator.NO_FRAGMENTS; + + testIsValid(testUrlPartsOptions, options); + } + + @Test + public void testIsValidScheme() { + if (printStatus) { + System.out.print("\n testIsValidScheme() "); + } + //UrlValidator urlVal = new UrlValidator(schemes,false,false,false); + UrlValidator urlVal = new UrlValidator(schemes, 0); + for (ResultPair testPair : testScheme) { + boolean result = urlVal.isValidScheme(testPair.item); + assertEquals(testPair.item, testPair.valid, result); + if (printStatus) { + if (result == testPair.valid) { + System.out.print('.'); + } else { + System.out.print('X'); + } + } + } + if (printStatus) { + System.out.println(); + } + + } + + /** + * Create set of tests by taking the testUrlXXX arrays and + * running through all possible permutations of their combinations. + * + * @param testObjects Used to create a url. + */ + public void testIsValid(Object[] testObjects, long options) { + UrlValidator urlVal = new UrlValidator(null, null, options); + assertTrue(urlVal.isValid("http://www.google.com")); + assertTrue(urlVal.isValid("http://www.google.com/")); + int statusPerLine = 60; + int printed = 0; + if (printIndex) { + statusPerLine = 6; + } + do { + StringBuilder testBuffer = new StringBuilder(); + boolean expected = true; + for (int testPartsIndexIndex = 0; testPartsIndexIndex < testPartsIndex.length; ++testPartsIndexIndex) { + int index = testPartsIndex[testPartsIndexIndex]; + ResultPair[] part = (ResultPair[]) testObjects[testPartsIndexIndex]; + testBuffer.append(part[index].item); + expected &= part[index].valid; + } + String url = testBuffer.toString(); + boolean result = urlVal.isValid(url); + assertEquals(url, expected, result); + if (printStatus) { + if (printIndex) { + System.out.print(testPartsIndextoString()); + } else { + if (result == expected) { + System.out.print('.'); + } else { + System.out.print('X'); + } + } + printed++; + if (printed == statusPerLine) { + System.out.println(); + printed = 0; + } + } + } while (incrementTestPartsIndex(testPartsIndex, testObjects)); + if (printStatus) { + System.out.println(); + } + } + + @Test + public void testValidator202() { + String[] schemes = {"http","https"}; + UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.NO_FRAGMENTS); + assertTrue(urlValidator.isValid("http://l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.l.org")); + } + + @Test + public void testValidator204() { + String[] schemes = {"http","https"}; + UrlValidator urlValidator = new UrlValidator(schemes); + assertTrue(urlValidator.isValid("http://tech.yahoo.com/rc/desktops/102;_ylt=Ao8yevQHlZ4On0O3ZJGXLEQFLZA5")); + } + + @Test + public void testValidator218() { + UrlValidator validator = new UrlValidator(UrlValidator.ALLOW_2_SLASHES); + assertTrue("parentheses should be valid in URLs", + validator.isValid("http://somewhere.com/pathxyz/file(1).html")); + } + + @Test + public void testValidator235() { + String version = System.getProperty("java.version"); + if (version.compareTo("1.6") < 0) { + System.out.println("Cannot run Unicode IDN tests"); + return; // Cannot run the test + } + UrlValidator validator = new UrlValidator(); + assertTrue("xn--d1abbgf6aiiy.xn--p1ai should validate", validator.isValid("http://xn--d1abbgf6aiiy.xn--p1ai")); + assertTrue("президент.рф should validate", validator.isValid("http://президент.рф")); + assertTrue("www.b\u00fccher.ch should validate", validator.isValid("http://www.b\u00fccher.ch")); + assertFalse("www.\uFFFD.ch FFFD should fail", validator.isValid("http://www.\uFFFD.ch")); + assertTrue("www.b\u00fccher.ch should validate", validator.isValid("ftp://www.b\u00fccher.ch")); + assertFalse("www.\uFFFD.ch FFFD should fail", validator.isValid("ftp://www.\uFFFD.ch")); + } + + @Test + public void testValidator248() { + RegexValidator regex = new RegexValidator(new String[] {"localhost", ".*\\.my-testing"}); + UrlValidator validator = new UrlValidator(regex, 0); + + assertTrue("localhost URL should validate", + validator.isValid("http://localhost/test/index.html")); + assertTrue("first.my-testing should validate", + validator.isValid("http://first.my-testing/test/index.html")); + assertTrue("sup3r.my-testing should validate", + validator.isValid("http://sup3r.my-testing/test/index.html")); + + assertFalse("broke.my-test should not validate", + validator.isValid("http://broke.my-test/test/index.html")); + + assertTrue("www.apache.org should still validate", + validator.isValid("http://www.apache.org/test/index.html")); + + // Now check using options + validator = new UrlValidator(UrlValidator.ALLOW_LOCAL_URLS); + + assertTrue("localhost URL should validate", + validator.isValid("http://localhost/test/index.html")); + + assertTrue("machinename URL should validate", + validator.isValid("http://machinename/test/index.html")); + + assertTrue("www.apache.org should still validate", + validator.isValid("http://www.apache.org/test/index.html")); + } + + @Test + public void testValidator288() { + UrlValidator validator = new UrlValidator(UrlValidator.ALLOW_LOCAL_URLS); + + assertTrue("hostname should validate", + validator.isValid("http://hostname")); + + assertTrue("hostname with path should validate", + validator.isValid("http://hostname/test/index.html")); + + assertTrue("localhost URL should validate", + validator.isValid("http://localhost/test/index.html")); + + assertFalse("first.my-testing should not validate", + validator.isValid("http://first.my-testing/test/index.html")); + + assertFalse("broke.hostname should not validate", + validator.isValid("http://broke.hostname/test/index.html")); + + assertTrue("www.apache.org should still validate", + validator.isValid("http://www.apache.org/test/index.html")); + + // Turn it off, and check + validator = new UrlValidator(0); + + assertFalse("hostname should no longer validate", + validator.isValid("http://hostname")); + + assertFalse("localhost URL should no longer validate", + validator.isValid("http://localhost/test/index.html")); + + assertTrue("www.apache.org should still validate", + validator.isValid("http://www.apache.org/test/index.html")); + } + + @Test + public void testValidator276() { + // file:// isn't allowed by default + UrlValidator validator = new UrlValidator(); + + assertTrue("http://apache.org/ should be allowed by default", + validator.isValid("http://www.apache.org/test/index.html")); + + assertFalse("file:///c:/ shouldn't be allowed by default", + validator.isValid("file:///C:/some.file")); + + assertFalse("file:///c:\\ shouldn't be allowed by default", + validator.isValid("file:///C:\\some.file")); + + assertFalse("file:///etc/ shouldn't be allowed by default", + validator.isValid("file:///etc/hosts")); + + assertFalse("file://localhost/etc/ shouldn't be allowed by default", + validator.isValid("file://localhost/etc/hosts")); + + assertFalse("file://localhost/c:/ shouldn't be allowed by default", + validator.isValid("file://localhost/c:/some.file")); + + // Turn it on, and check + // Note - we need to enable local urls when working with file: + validator = new UrlValidator(new String[] {"http","file"}, UrlValidator.ALLOW_LOCAL_URLS); + + assertTrue("http://apache.org/ should be allowed by default", + validator.isValid("http://www.apache.org/test/index.html")); + + assertTrue("file:///c:/ should now be allowed", + validator.isValid("file:///C:/some.file")); + + assertFalse("file:///c:\\ should not be allowed", // Only allow forward slashes + validator.isValid("file:///C:\\some.file")); + + assertTrue("file:///etc/ should now be allowed", + validator.isValid("file:///etc/hosts")); + + assertTrue("file://localhost/etc/ should now be allowed", + validator.isValid("file://localhost/etc/hosts")); + + assertTrue("file://localhost/c:/ should now be allowed", + validator.isValid("file://localhost/c:/some.file")); + + // These are never valid + assertFalse("file://c:/ shouldn't ever be allowed, needs file:///c:/", + validator.isValid("file://C:/some.file")); + + assertFalse("file://c:\\ shouldn't ever be allowed, needs file:///c:/", + validator.isValid("file://C:\\some.file")); + } + + @Test + public void testValidator391OK() { + String[] schemes = {"file"}; + UrlValidator urlValidator = new UrlValidator(schemes); + assertTrue(urlValidator.isValid("file:///C:/path/to/dir/")); + } + + @Test + public void testValidator391FAILS() { + String[] schemes = {"file"}; + UrlValidator urlValidator = new UrlValidator(schemes); + assertTrue(urlValidator.isValid("file:/C:/path/to/dir/")); + } + + @Test + public void testValidator309() { + UrlValidator urlValidator = new UrlValidator(); + assertTrue(urlValidator.isValid("http://sample.ondemand.com/")); + assertTrue(urlValidator.isValid("hTtP://sample.ondemand.CoM/")); + assertTrue(urlValidator.isValid("httpS://SAMPLE.ONEMAND.COM/")); + urlValidator = new UrlValidator(new String[] {"HTTP","HTTPS"}); + assertTrue(urlValidator.isValid("http://sample.ondemand.com/")); + assertTrue(urlValidator.isValid("hTtP://sample.ondemand.CoM/")); + assertTrue(urlValidator.isValid("httpS://SAMPLE.ONEMAND.COM/")); + } + + @Test + public void testValidator339(){ + UrlValidator urlValidator = new UrlValidator(); + assertTrue(urlValidator.isValid("http://www.cnn.com/WORLD/?hpt=sitenav")); // without + assertTrue(urlValidator.isValid("http://www.cnn.com./WORLD/?hpt=sitenav")); // with + assertFalse(urlValidator.isValid("http://www.cnn.com../")); // doubly dotty + assertFalse(urlValidator.isValid("http://www.cnn.invalid/")); + assertFalse(urlValidator.isValid("http://www.cnn.invalid./")); // check . does not affect invalid domains + } + + @Test + public void testValidator339IDN(){ + UrlValidator urlValidator = new UrlValidator(); + assertTrue(urlValidator.isValid("http://президент.рф/WORLD/?hpt=sitenav")); // without + assertTrue(urlValidator.isValid("http://президент.рф./WORLD/?hpt=sitenav")); // with + assertFalse(urlValidator.isValid("http://президент.рф..../")); // very dotty + assertFalse(urlValidator.isValid("http://президент.рф.../")); // triply dotty + assertFalse(urlValidator.isValid("http://президент.рф../")); // doubly dotty + } + + @Test + public void testValidator342(){ + UrlValidator urlValidator = new UrlValidator(); + assertTrue(urlValidator.isValid("http://example.rocks/")); + assertTrue(urlValidator.isValid("http://example.rocks")); + } + + @Test + public void testValidator411(){ + UrlValidator urlValidator = new UrlValidator(); + assertTrue(urlValidator.isValid("http://example.rocks:/")); + assertTrue(urlValidator.isValid("http://example.rocks:0/")); + assertTrue(urlValidator.isValid("http://example.rocks:65535/")); + assertFalse(urlValidator.isValid("http://example.rocks:65536/")); + assertFalse(urlValidator.isValid("http://example.rocks:100000/")); + } + + @Test + public void testValidator464() { + String[] schemes = {"file"}; + UrlValidator urlValidator = new UrlValidator(schemes); + String fileNAK = "file://bad ^ domain.com/label/test"; + assertFalse(fileNAK, urlValidator.isValid(fileNAK)); + } + + @Test + public void testValidator452(){ + UrlValidator urlValidator = new UrlValidator(); + assertTrue(urlValidator.isValid("http://[::FFFF:129.144.52.38]:80/index.html")); + } + + @Test(expected = IllegalArgumentException.class) + public void testValidator473_1() { // reject null DomainValidator + new UrlValidator(new String[]{}, null, 0L, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testValidator473_2() { // reject null DomainValidator with mismatched allowLocal + List items = new ArrayList<>(); + new UrlValidator(new String[]{}, null, 0L, DomainValidator.getInstance(true, items)); + } + + @Test(expected = IllegalArgumentException.class) + public void testValidator473_3() { // reject null DomainValidator with mismatched allowLocal + List items = new ArrayList<>(); + new UrlValidator(new String[]{}, null, UrlValidator.ALLOW_LOCAL_URLS, DomainValidator.getInstance(false, items)); + } + + static boolean incrementTestPartsIndex(int[] testPartsIndex, Object[] testParts) { + boolean carry = true; //add 1 to lowest order part. + boolean maxIndex = true; + for (int testPartsIndexIndex = testPartsIndex.length - 1; testPartsIndexIndex >= 0; --testPartsIndexIndex) { + int index = testPartsIndex[testPartsIndexIndex]; + ResultPair[] part = (ResultPair[]) testParts[testPartsIndexIndex]; + maxIndex &= (index == (part.length - 1)); + if (carry) { + if (index < part.length - 1) { + index++; + testPartsIndex[testPartsIndexIndex] = index; + carry = false; + } else { + testPartsIndex[testPartsIndexIndex] = 0; + carry = true; + } + } + } + + + return (!maxIndex); + } + + private String testPartsIndextoString() { + StringBuilder carryMsg = new StringBuilder("{"); + for (int testPartsIndexIndex = 0; testPartsIndexIndex < testPartsIndex.length; ++testPartsIndexIndex) { + carryMsg.append(testPartsIndex[testPartsIndexIndex]); + if (testPartsIndexIndex < testPartsIndex.length - 1) { + carryMsg.append(','); + } else { + carryMsg.append('}'); + } + } + return carryMsg.toString(); + + } + + @Test + public void testValidateUrl() { + assertTrue(true); + } + + @Test + public void testValidator290() { + UrlValidator validator = new UrlValidator(); + assertTrue(validator.isValid("http://xn--h1acbxfam.idn.icann.org/")); +// assertTrue(validator.isValid("http://xn--e1afmkfd.xn--80akhbyknj4f")); + // Internationalized country code top-level domains + assertTrue(validator.isValid("http://test.xn--lgbbat1ad8j")); //Algeria + assertTrue(validator.isValid("http://test.xn--fiqs8s")); // China + assertTrue(validator.isValid("http://test.xn--fiqz9s")); // China + assertTrue(validator.isValid("http://test.xn--wgbh1c")); // Egypt + assertTrue(validator.isValid("http://test.xn--j6w193g")); // Hong Kong + assertTrue(validator.isValid("http://test.xn--h2brj9c")); // India + assertTrue(validator.isValid("http://test.xn--mgbbh1a71e")); // India + assertTrue(validator.isValid("http://test.xn--fpcrj9c3d")); // India + assertTrue(validator.isValid("http://test.xn--gecrj9c")); // India + assertTrue(validator.isValid("http://test.xn--s9brj9c")); // India + assertTrue(validator.isValid("http://test.xn--xkc2dl3a5ee0h")); // India + assertTrue(validator.isValid("http://test.xn--45brj9c")); // India + assertTrue(validator.isValid("http://test.xn--mgba3a4f16a")); // Iran + assertTrue(validator.isValid("http://test.xn--mgbayh7gpa")); // Jordan + assertTrue(validator.isValid("http://test.xn--mgbc0a9azcg")); // Morocco + assertTrue(validator.isValid("http://test.xn--ygbi2ammx")); // Palestinian Territory + assertTrue(validator.isValid("http://test.xn--wgbl6a")); // Qatar + assertTrue(validator.isValid("http://test.xn--p1ai")); // Russia + assertTrue(validator.isValid("http://test.xn--mgberp4a5d4ar")); // Saudi Arabia + assertTrue(validator.isValid("http://test.xn--90a3ac")); // Serbia + assertTrue(validator.isValid("http://test.xn--yfro4i67o")); // Singapore + assertTrue(validator.isValid("http://test.xn--clchc0ea0b2g2a9gcd")); // Singapore + assertTrue(validator.isValid("http://test.xn--3e0b707e")); // South Korea + assertTrue(validator.isValid("http://test.xn--fzc2c9e2c")); // Sri Lanka + assertTrue(validator.isValid("http://test.xn--xkc2al3hye2a")); // Sri Lanka + assertTrue(validator.isValid("http://test.xn--ogbpf8fl")); // Syria + assertTrue(validator.isValid("http://test.xn--kprw13d")); // Taiwan + assertTrue(validator.isValid("http://test.xn--kpry57d")); // Taiwan + assertTrue(validator.isValid("http://test.xn--o3cw4h")); // Thailand + assertTrue(validator.isValid("http://test.xn--pgbs0dh")); // Tunisia + assertTrue(validator.isValid("http://test.xn--mgbaam7a8h")); // United Arab Emirates + // Proposed internationalized ccTLDs +// assertTrue(validator.isValid("http://test.xn--54b7fta0cc")); // Bangladesh +// assertTrue(validator.isValid("http://test.xn--90ae")); // Bulgaria +// assertTrue(validator.isValid("http://test.xn--node")); // Georgia +// assertTrue(validator.isValid("http://test.xn--4dbrk0ce")); // Israel +// assertTrue(validator.isValid("http://test.xn--mgb9awbf")); // Oman +// assertTrue(validator.isValid("http://test.xn--j1amh")); // Ukraine +// assertTrue(validator.isValid("http://test.xn--mgb2ddes")); // Yemen + // Test TLDs +// assertTrue(validator.isValid("http://test.xn--kgbechtv")); // Arabic +// assertTrue(validator.isValid("http://test.xn--hgbk6aj7f53bba")); // Persian +// assertTrue(validator.isValid("http://test.xn--0zwm56d")); // Chinese +// assertTrue(validator.isValid("http://test.xn--g6w251d")); // Chinese +// assertTrue(validator.isValid("http://test.xn--80akhbyknj4f")); // Russian +// assertTrue(validator.isValid("http://test.xn--11b5bs3a9aj6g")); // Hindi +// assertTrue(validator.isValid("http://test.xn--jxalpdlp")); // Greek +// assertTrue(validator.isValid("http://test.xn--9t4b11yi5a")); // Korean +// assertTrue(validator.isValid("http://test.xn--deba0ad")); // Yiddish +// assertTrue(validator.isValid("http://test.xn--zckzah")); // Japanese +// assertTrue(validator.isValid("http://test.xn--hlcj6aya9esc7a")); // Tamil + } + + @Test + public void testValidator361() { + UrlValidator validator = new UrlValidator(); + assertTrue(validator.isValid("http://hello.tokyo/")); + } + + @Test + public void testValidator363(){ + UrlValidator urlValidator = new UrlValidator(); + assertTrue(urlValidator.isValid("http://www.example.org/a/b/hello..world")); + assertTrue(urlValidator.isValid("http://www.example.org/a/hello..world")); + assertTrue(urlValidator.isValid("http://www.example.org/hello.world/")); + assertTrue(urlValidator.isValid("http://www.example.org/hello..world/")); + assertTrue(urlValidator.isValid("http://www.example.org/hello.world")); + assertTrue(urlValidator.isValid("http://www.example.org/hello..world")); + assertTrue(urlValidator.isValid("http://www.example.org/..world")); + assertTrue(urlValidator.isValid("http://www.example.org/.../world")); + assertFalse(urlValidator.isValid("http://www.example.org/../world")); + assertFalse(urlValidator.isValid("http://www.example.org/..")); + assertFalse(urlValidator.isValid("http://www.example.org/../")); + assertFalse(urlValidator.isValid("http://www.example.org/./..")); + assertFalse(urlValidator.isValid("http://www.example.org/././..")); + assertTrue(urlValidator.isValid("http://www.example.org/...")); + assertTrue(urlValidator.isValid("http://www.example.org/.../")); + assertTrue(urlValidator.isValid("http://www.example.org/.../..")); + } + + @Test + public void testValidator375() { + UrlValidator validator = new UrlValidator(); + String url = "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html"; + assertTrue("IPv6 address URL should validate: " + url, validator.isValid(url)); + url = "http://[::1]:80/index.html"; + assertTrue("IPv6 address URL should validate: " + url, validator.isValid(url)); + url = "http://FEDC:BA98:7654:3210:FEDC:BA98:7654:3210:80/index.html"; + assertFalse("IPv6 address without [] should not validate: " + url, validator.isValid(url)); + } + + + @Test + public void testValidator353() { // userinfo + UrlValidator validator = new UrlValidator(); + assertTrue(validator.isValid("http://www.apache.org:80/path")); + assertTrue(validator.isValid("http://user:pass@www.apache.org:80/path")); + assertTrue(validator.isValid("http://user:@www.apache.org:80/path")); + assertTrue(validator.isValid("http://user@www.apache.org:80/path")); + assertTrue(validator.isValid("http://us%00er:-._~!$&'()*+,;=@www.apache.org:80/path")); + assertFalse(validator.isValid("http://:pass@www.apache.org:80/path")); + assertFalse(validator.isValid("http://:@www.apache.org:80/path")); + assertFalse(validator.isValid("http://user:pa:ss@www.apache.org/path")); + assertFalse(validator.isValid("http://user:pa@ss@www.apache.org/path")); + } + + @Test + public void testValidator382() { + UrlValidator validator = new UrlValidator(); + assertTrue(validator.isValid("ftp://username:password@example.com:8042/over/there/index.dtb?type=animal&name=narwhal#nose")); + } + + @Test + public void testValidator380() { + UrlValidator validator = new UrlValidator(); + assertTrue(validator.isValid("http://www.apache.org:80/path")); + assertTrue(validator.isValid("http://www.apache.org:8/path")); + assertTrue(validator.isValid("http://www.apache.org:/path")); + } + + @Test + public void testValidator420() { + UrlValidator validator = new UrlValidator(); + assertFalse(validator.isValid("http://example.com/serach?address=Main Avenue")); + assertTrue(validator.isValid("http://example.com/serach?address=Main%20Avenue")); + assertTrue(validator.isValid("http://example.com/serach?address=Main+Avenue")); + } + + @Test + public void testValidator467() { + UrlValidator validator = new UrlValidator(UrlValidator.ALLOW_2_SLASHES); + assertTrue(validator.isValid("https://example.com/some_path/path/")); + assertTrue(validator.isValid("https://example.com//somepath/path/")); + assertTrue(validator.isValid("https://example.com//some_path/path/")); + assertTrue(validator.isValid("http://example.com//_test")); // VALIDATOR-429 + } + + @Test + public void testValidator283() { + UrlValidator validator = new UrlValidator(); + assertFalse(validator.isValid("http://finance.yahoo.com/news/Owners-54B-NY-housing-apf-2493139299.html?x=0&ap=%fr")); + assertTrue(validator.isValid("http://finance.yahoo.com/news/Owners-54B-NY-housing-apf-2493139299.html?x=0&ap=%22")); + } + + @Test + public void testFragments() { + String[] schemes = {"http","https"}; + UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.NO_FRAGMENTS); + assertFalse(urlValidator.isValid("http://apache.org/a/b/c#frag")); + urlValidator = new UrlValidator(schemes); + assertTrue(urlValidator.isValid("http://apache.org/a/b/c#frag")); +} + + //-------------------- Test data for creating a composite URL + /** + * The data given below approximates the 4 parts of a URL + * {@code ://?} except that the port number + * is broken out of authority to increase the number of permutations. + * A complete URL is composed of a scheme+authority+port+path+query, + * all of which must be individually valid for the entire URL to be considered + * valid. + */ + ResultPair[] testUrlScheme = {new ResultPair("http://", true), + new ResultPair("ftp://", true), + new ResultPair("h3t://", true), + new ResultPair("3ht://", false), + new ResultPair("http:/", false), + new ResultPair("http:", false), + new ResultPair("http/", false), + new ResultPair("://", false)}; + + ResultPair[] testUrlAuthority = {new ResultPair("www.google.com", true), + new ResultPair("www.google.com.", true), + new ResultPair("go.com", true), + new ResultPair("go.au", true), + new ResultPair("0.0.0.0", true), + new ResultPair("255.255.255.255", true), + new ResultPair("256.256.256.256", false), + new ResultPair("255.com", true), + new ResultPair("1.2.3.4.5", false), + new ResultPair("1.2.3.4.", false), + new ResultPair("1.2.3", false), + new ResultPair(".1.2.3.4", false), + new ResultPair("go.a", false), + new ResultPair("go.a1a", false), + new ResultPair("go.cc", true), + new ResultPair("go.1aa", false), + new ResultPair("aaa.", false), + new ResultPair(".aaa", false), + new ResultPair("aaa", false), + new ResultPair("", false), + }; + ResultPair[] testUrlPort = {new ResultPair(":80", true), + new ResultPair(":65535", true), // max possible + new ResultPair(":65536", false), // max possible +1 + new ResultPair(":0", true), + new ResultPair("", true), + new ResultPair(":-1", false), + new ResultPair(":65636", false), + new ResultPair(":999999999999999999", false), + new ResultPair(":65a", false), + }; + ResultPair[] testPath = {new ResultPair("/test1", true), + new ResultPair("/t123", true), + new ResultPair("/$23", true), + new ResultPair("/..", false), + new ResultPair("/../", false), + new ResultPair("/test1/", true), + new ResultPair("", true), + new ResultPair("/test1/file", true), + new ResultPair("/..//file", false), + new ResultPair("/test1//file", false), + }; + //Test allow2slash, noFragment + ResultPair[] testUrlPathOptions = {new ResultPair("/test1", true), + new ResultPair("/t123", true), + new ResultPair("/$23", true), + new ResultPair("/..", false), + new ResultPair("/../", false), + new ResultPair("/test1/", true), + new ResultPair("/#", false), + new ResultPair("", true), + new ResultPair("/test1/file", true), + new ResultPair("/t123/file", true), + new ResultPair("/$23/file", true), + new ResultPair("/../file", false), + new ResultPair("/..//file", false), + new ResultPair("/test1//file", true), + new ResultPair("/#/file", false), + }; + + ResultPair[] testUrlQuery = {new ResultPair("?action=view", true), + new ResultPair("?action=edit&mode=up", true), + new ResultPair("", true), + }; + + Object[] testUrlParts = {testUrlScheme, testUrlAuthority, testUrlPort, testPath, testUrlQuery}; + Object[] testUrlPartsOptions = {testUrlScheme, testUrlAuthority, testUrlPort, testUrlPathOptions, testUrlQuery}; + int[] testPartsIndex = {0, 0, 0, 0, 0}; + + //---------------- Test data for individual url parts ---------------- + private final String[] schemes = {"http", "gopher", "g0-To+.", + "not_valid", // TODO this will need to be dropped if the ctor validates schemes + }; + + ResultPair[] testScheme = {new ResultPair("http", true), + new ResultPair("ftp", false), + new ResultPair("httpd", false), + new ResultPair("gopher", true), + new ResultPair("g0-to+.", true), + new ResultPair("not_valid", false), // underscore not allowed + new ResultPair("HtTp", true), + new ResultPair("telnet", false)}; + + + /** + * Validator for checking URL parsing + * @param args - URLs to validate + */ + public static void main(String[] args) { + UrlValidator uv = new UrlValidator(); + for(String arg: args) { + try { + URI uri = new URI(arg); + uri = uri.normalize(); + System.out.println(uri); + System.out.printf("URI scheme: %s%n", uri.getScheme()); + System.out.printf("URI scheme specific part: %s%n", uri.getSchemeSpecificPart()); + System.out.printf("URI raw scheme specific part: %s%n", uri.getRawSchemeSpecificPart()); + System.out.printf("URI auth: %s%n", uri.getAuthority()); + System.out.printf("URI raw auth: %s%n", uri.getRawAuthority()); + System.out.printf("URI userInfo: %s%n", uri.getUserInfo()); + System.out.printf("URI raw userInfo: %s%n", uri.getRawUserInfo()); + System.out.printf("URI host: %s%n", uri.getHost()); + System.out.printf("URI port: %s%n", uri.getPort()); + System.out.printf("URI path: %s%n", uri.getPath()); + System.out.printf("URI raw path: %s%n", uri.getRawPath()); + System.out.printf("URI query: %s%n", uri.getQuery()); + System.out.printf("URI raw query: %s%n", uri.getRawQuery()); + System.out.printf("URI fragment: %s%n", uri.getFragment()); + System.out.printf("URI raw fragment: %s%n", uri.getRawFragment()); + } catch (URISyntaxException e) { + System.out.println(e.getMessage()); + } + System.out.printf("isValid: %s%n",uv.isValid(arg)); + } + } + +} From f39b68427d5f96bb74e9fefaf9de48ad40147aa3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 07:27:25 -0800 Subject: [PATCH 11/58] Bump actions/upload-artifact from 2.3.0 to 2.3.1 (#6057) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 4991a9fd5cab..e7b74ccfd717 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -28,7 +28,7 @@ jobs: env: GITHUB_AUTH: github-actions:${{ secrets.GITHUB_TOKEN }} - name: Upload Changelog YAML - uses: actions/upload-artifact@v2.3.0 + uses: actions/upload-artifact@v2.3.1 with: name: changelog.yaml path: changelog.yaml From 691ac8e6ae1d27f379cd36ee6f55278e672b43ba Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Thu, 16 Dec 2021 16:28:17 +0100 Subject: [PATCH 12/58] [JENKINS-65113] Add issue tracker metadata support (#5358) Co-authored-by: Daniel Beck --- core/src/main/java/hudson/PluginWrapper.java | 18 +++++++ .../main/java/hudson/model/UpdateSite.java | 47 +++++++++++++++++++ .../hudson/PluginManager/installed.jelly | 8 ++++ .../hudson/PluginManager/installed.properties | 1 + 4 files changed, 74 insertions(+) diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index d2ed43ec2e28..a04ad5188f65 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -1411,6 +1411,24 @@ public List getDeprecations() { return deprecations; } + @Restricted(NoExternalUse.class) + public String getIssueTrackerReportUrl() { + final UpdateCenter updateCenter = Jenkins.get().getUpdateCenter(); + if (updateCenter.isSiteDataReady()) { + for (UpdateSite site : updateCenter.getSites()) { + final UpdateSite.Plugin sitePlugin = site.getPlugin(this.shortName); + if (sitePlugin != null && sitePlugin.issueTrackers != null) { + for (UpdateSite.IssueTracker issueTracker : sitePlugin.issueTrackers) { + if (issueTracker.reportUrl != null) { + return issueTracker.reportUrl; + } + } + } + } + } + return null; + } + private static final Logger LOGGER = Logger.getLogger(PluginWrapper.class.getName()); /** diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index 63c82a96f76a..cf56d13bea44 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -1078,6 +1078,42 @@ private static String get(JSONObject o, String prop) { static final Predicate IS_DEP_PREDICATE = x -> x instanceof JSONObject && get((JSONObject) x, "name") != null; static final Predicate IS_NOT_OPTIONAL = x -> "false".equals(get((JSONObject) x, "optional")); + /** + * Metadata for one issue tracker provided by the update site. + */ + @Restricted(NoExternalUse.class) + public static final class IssueTracker { + /** + * A string specifying the type of issue tracker. + */ + public final String type; + /** + * Issue tracker URL that can be used to view previously reported issues. + */ + public final String viewUrl; + /** + * Issue tracker URL that can be used to report a new issue. + */ + @CheckForNull + public final String reportUrl; + + public IssueTracker(@NonNull String type, @NonNull String viewUrl, @CheckForNull String reportUrl) { + this.type = type; + this.viewUrl = viewUrl; + this.reportUrl = reportUrl; + } + + private static IssueTracker createFromJSONObject(Object o) { + if (o instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) o; + if (jsonObject.has("type") && jsonObject.has("viewUrl") && jsonObject.has("reportUrl")) { + return new IssueTracker(jsonObject.getString("type"), jsonObject.getString("viewUrl"), jsonObject.getString("reportUrl")); + } + } + return null; + } + } + public final class Plugin extends Entry { /** * Optional URL to the Wiki page that discusses this plugin. @@ -1162,6 +1198,15 @@ public final class Plugin extends Entry { @Restricted(NoExternalUse.class) public String latest; + /** + * Issue trackers associated with this plugin. + * This list is sorted by preference in descending order, meaning a UI + * supporting only one issue tracker should reference the first one + * supporting the desired behavior (like having a {@code reportUrl}). + */ + @Restricted(NoExternalUse.class) + public IssueTracker[] issueTrackers; + @DataBoundConstructor public Plugin(String sourceId, JSONObject o) { super(sourceId, o, UpdateSite.this.url); @@ -1193,6 +1238,8 @@ public Plugin(String sourceId, JSONObject o) { this.popularity = popularity; this.releaseTimestamp = date; this.categories = o.has("labels") ? internInPlace((String[])o.getJSONArray("labels").toArray(EMPTY_STRING_ARRAY)) : null; + this.issueTrackers = o.has("issueTrackers") ? o.getJSONArray("issueTrackers").stream().map(IssueTracker::createFromJSONObject).filter(Objects::nonNull).toArray(IssueTracker[]::new) : null; + JSONArray ja = o.getJSONArray("dependencies"); int depCount = (int)ja.stream().filter(IS_DEP_PREDICATE.and(IS_NOT_OPTIONAL)).count(); int optionalDepCount = (int)ja.stream().filter(IS_DEP_PREDICATE.and(IS_NOT_OPTIONAL.negate())).count(); diff --git a/core/src/main/resources/hudson/PluginManager/installed.jelly b/core/src/main/resources/hudson/PluginManager/installed.jelly index bb6f9bf8ec41..a2ca8a67ef02 100644 --- a/core/src/main/resources/hudson/PluginManager/installed.jelly +++ b/core/src/main/resources/hudson/PluginManager/installed.jelly @@ -128,6 +128,14 @@ THE SOFTWARE. + + + +
    ${%securityWarning}
      diff --git a/core/src/main/resources/hudson/PluginManager/installed.properties b/core/src/main/resources/hudson/PluginManager/installed.properties index d1a35a9d3b98..fa5ecc5526a9 100644 --- a/core/src/main/resources/hudson/PluginManager/installed.properties +++ b/core/src/main/resources/hudson/PluginManager/installed.properties @@ -31,3 +31,4 @@ securityWarning=\ adoptThisPlugin=\ This plugin is up for adoption! We are looking for new maintainers. \ Visit our Adopt a Plugin initiative for more information. +reportIssue=Report an issue with this plugin From b0c61ecac17177d136e0685ae23653f8734714c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 20:43:37 +0000 Subject: [PATCH 13/58] Bump mockito-inline from 4.1.0 to 4.2.0 (#6065) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 61861a7f185c..0dc0b6fccbfa 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ THE SOFTWARE. 1.25 1.22 5.8.2 - 4.1.0 + 4.2.0 2.17.6 From 09f1f51e9e4289906ea447dbdefb1ca27cf8764e Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 17 Dec 2021 07:56:53 -0800 Subject: [PATCH 14/58] Only require one approval for minor changes (#6051) Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com> --- docs/MAINTAINERS.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/MAINTAINERS.adoc b/docs/MAINTAINERS.adoc index 3218936cc6b6..f8fcf3831420 100644 --- a/docs/MAINTAINERS.adoc +++ b/docs/MAINTAINERS.adoc @@ -374,6 +374,9 @@ LTS backporting, if needed, will be handled separately by the release team. Security hardening and enhancements go through the standard process. * Release Team members are permitted to bypass the review/merge process if and only if a change is needed to unblock the security release. Common review process is used otherwise. +* Only one approval is required for low-risk small changes with the `skip-changelog` label, + as long as both author and approver have write access to the repository. + Note that the 24-hour waiting period still applies. * 24 hours waiting period after adding the `ready-for-merge` label is not required for: //TODO(oleg_nenashev): Define "trivial" better to avoid loopholes ** changes that do not result in changes to the primary functionality, such as typo fixes in documentation or localization files From cbcdb128c7c74e5491ac6037218639bf0438dd16 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Dec 2021 07:57:24 -0800 Subject: [PATCH 15/58] Bump matrix-auth from 2.6.11 to 3.0 (#6058) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pom.xml b/test/pom.xml index 28924f241558..83e6aa431aa0 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -122,7 +122,7 @@ THE SOFTWARE. org.jenkins-ci.plugins matrix-auth - 2.6.11 + 3.0 test From b55d30c69c005e41f1f7f915180ad86f9cb841b8 Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Fri, 17 Dec 2021 16:57:48 +0100 Subject: [PATCH 16/58] Clarify that `FilePath` telemetry collects component versions now (#6061) Co-authored-by: Daniel Beck --- .../impl/SlaveToMasterFileCallableUsage/description.jelly | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly b/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly index 83f02a200bf1..1e85405b0903 100644 --- a/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly +++ b/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly @@ -8,6 +8,10 @@ Jenkins developers are considering disabling this kind of usage entirely. Since it is difficult to determine via static analysis or even manual code inspection which plugins are using this system, we are collecting information on how widely it is used. - The data includes names of Java classes mainly in Jenkins core and plugins as well as method names and line numbers. - It does not include the names of files being accessed or anything else not determined by versions of software components in use. + The data includes: +
        +
      • Java stack traces (names of Java classes mainly in Jenkins core and plugins as well as method names and line numbers) of file accesses on the controller from agents
      • +
      • Version numbers of Jenkins core and all installed plugins
      • +
      + We do not collect the names of files being accessed or anything else not determined by versions of software components in use. From aa5911a548693b4438cd3a515b221875a13c9b26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Dec 2021 07:58:30 -0800 Subject: [PATCH 17/58] Bump access-modifier.version from 1.25 to 1.27 (#6064) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Basil Crow --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0dc0b6fccbfa..77bae95a471a 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ THE SOFTWARE. Medium ${project.basedir}/../src/spotbugs/spotbugs-excludes.xml - 1.25 + 1.27 1.22 5.8.2 4.2.0 From d497839b4df3b53a534e5520bc5b0c817a780636 Mon Sep 17 00:00:00 2001 From: Marat Talipov Date: Sun, 19 Dec 2021 00:04:08 +0500 Subject: [PATCH 18/58] Translate some parameters to ru (#6011) --- .../help-boolean-default_ru.html | 3 +++ .../model/RunParameterDefinition/config_ru.properties | 7 +++++++ war/src/main/webapp/help/parameter/boolean_ru.html | 4 ++++ .../main/webapp/help/parameter/choice-choices_ru.html | 3 +++ .../main/webapp/help/parameter/description_ru.html | 3 +++ war/src/main/webapp/help/parameter/name_ru.html | 6 ++++++ war/src/main/webapp/help/parameter/run-filter_ru.html | 9 +++++++++ .../main/webapp/help/parameter/run-project_ru.html | 11 +++++++++++ 8 files changed, 46 insertions(+) create mode 100644 core/src/main/resources/hudson/model/BooleanParameterDefinition/help-boolean-default_ru.html create mode 100644 war/src/main/webapp/help/parameter/boolean_ru.html create mode 100644 war/src/main/webapp/help/parameter/choice-choices_ru.html create mode 100644 war/src/main/webapp/help/parameter/description_ru.html create mode 100644 war/src/main/webapp/help/parameter/name_ru.html create mode 100644 war/src/main/webapp/help/parameter/run-filter_ru.html create mode 100644 war/src/main/webapp/help/parameter/run-project_ru.html diff --git a/core/src/main/resources/hudson/model/BooleanParameterDefinition/help-boolean-default_ru.html b/core/src/main/resources/hudson/model/BooleanParameterDefinition/help-boolean-default_ru.html new file mode 100644 index 000000000000..faf96921432f --- /dev/null +++ b/core/src/main/resources/hudson/model/BooleanParameterDefinition/help-boolean-default_ru.html @@ -0,0 +1,3 @@ +
      + Задает значение поля по умолчанию. +
      \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/RunParameterDefinition/config_ru.properties b/core/src/main/resources/hudson/model/RunParameterDefinition/config_ru.properties index f8ff0f56ce70..46cd35fdf206 100644 --- a/core/src/main/resources/hudson/model/RunParameterDefinition/config_ru.properties +++ b/core/src/main/resources/hudson/model/RunParameterDefinition/config_ru.properties @@ -21,3 +21,10 @@ # THE SOFTWARE. Project=\u041F\u0440\u043E\u0435\u043A\u0442 +Description=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 +Completed\ Builds\ Only=\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438 +All\ Builds=\u0412\u0441\u0435 \u0441\u0431\u043e\u0440\u043a\u0438 +Name=\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 +Filter=\u0424\u0438\u043b\u044c\u0442\u0440 +Stable\ Builds\ Only=\u0422\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438 +Successful\ Builds\ Only=\u0422\u043e\u043b\u044c\u043a\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438 \ No newline at end of file diff --git a/war/src/main/webapp/help/parameter/boolean_ru.html b/war/src/main/webapp/help/parameter/boolean_ru.html new file mode 100644 index 000000000000..ab41a328b8e3 --- /dev/null +++ b/war/src/main/webapp/help/parameter/boolean_ru.html @@ -0,0 +1,4 @@ +
      + Boolean-параметр, который можно использовать во время сборки, либо в качестве переменной окружения, либо + путем подстановки переменных в других частях конфигурации. Строковое значение будет представлено в виде 'true' или 'false'. +
      \ No newline at end of file diff --git a/war/src/main/webapp/help/parameter/choice-choices_ru.html b/war/src/main/webapp/help/parameter/choice-choices_ru.html new file mode 100644 index 000000000000..9e2c4ad4012f --- /dev/null +++ b/war/src/main/webapp/help/parameter/choice-choices_ru.html @@ -0,0 +1,3 @@ +
      + Возможные варианты, каждый должен начинаться с новой строки. Первое значение будет значением по умолчанию. +
      \ No newline at end of file diff --git a/war/src/main/webapp/help/parameter/description_ru.html b/war/src/main/webapp/help/parameter/description_ru.html new file mode 100644 index 000000000000..85aafe977dd0 --- /dev/null +++ b/war/src/main/webapp/help/parameter/description_ru.html @@ -0,0 +1,3 @@ +
      + Описание, которое увидит пользователь при запуске задачи. +
      \ No newline at end of file diff --git a/war/src/main/webapp/help/parameter/name_ru.html b/war/src/main/webapp/help/parameter/name_ru.html new file mode 100644 index 000000000000..088414b866bb --- /dev/null +++ b/war/src/main/webapp/help/parameter/name_ru.html @@ -0,0 +1,6 @@ +
      + Название параметра + +

      + Эти параметры доступны при сборке в качестве переменных среды. +

      \ No newline at end of file diff --git a/war/src/main/webapp/help/parameter/run-filter_ru.html b/war/src/main/webapp/help/parameter/run-filter_ru.html new file mode 100644 index 000000000000..564628f79803 --- /dev/null +++ b/war/src/main/webapp/help/parameter/run-filter_ru.html @@ -0,0 +1,9 @@ +
      +Фильтрация списка сборок, которые доступны при запуске задачи. +
      +'Все сборки' - Все сборки, включая незавершенные.
      +'Только завершенные сборки' - Все завершенные сборки.
      +'Только успешные сборки' - Все успешные сборки (стабильные и нестабильные).
      +'Только стабильные сборки' - Все стабильные сборки.
      +
      +
      \ No newline at end of file diff --git a/war/src/main/webapp/help/parameter/run-project_ru.html b/war/src/main/webapp/help/parameter/run-project_ru.html new file mode 100644 index 000000000000..3d9b28dd8fb2 --- /dev/null +++ b/war/src/main/webapp/help/parameter/run-project_ru.html @@ -0,0 +1,11 @@ +
      + Задача, из которой пользователь может выбирать определенную сборку. По умолчанию выбирается последняя сборка. + Эти параметры доступны во время сборки в виде переменных окружения: +
      +PARAMETER_NAME=<jenkins_url>/job/<название_задачи>/<номер_запуска>/
      +PARAMETER_NAME_JOBNAME=<название_задачи>
      +PARAMETER_NAME_NUMBER=<номер_запуска>
      +PARAMETER_NAME_NAME=<описание_задачи>
      +PARAMETER_NAME_RESULT=<результат_запуска>
      +
      +
      \ No newline at end of file From dea9288bce734b5c11382ab4d9b98575a287dbc3 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 18 Dec 2021 11:04:33 -0800 Subject: [PATCH 19/58] Simplify JTH assertions (#6060) --- test/src/test/java/hudson/LauncherTest.java | 4 +- .../src/test/java/hudson/ProcStarterTest.java | 3 +- .../java/hudson/cli/ConsoleCommandTest.java | 4 +- .../hudson/cli/DeleteBuildsCommandTest.java | 12 ++-- .../java/hudson/cli/RunRangeCommand2Test.java | 4 +- .../java/hudson/cli/RunRangeCommandTest.java | 3 +- .../cli/SetBuildDescriptionCommandTest.java | 2 +- .../cli/SetBuildDisplayNameCommandTest.java | 2 +- .../hudson/diagnosis/OldDataMonitorTest.java | 2 +- .../model/AbortedFreeStyleBuildTest.java | 6 +- .../java/hudson/model/AbstractBuildTest.java | 9 ++- .../java/hudson/model/AbstractItemTest.java | 2 +- .../hudson/model/AbstractProjectTest.java | 6 +- .../java/hudson/model/BuildExecutionTest.java | 2 +- .../model/DirectoryBrowserSupportTest.java | 28 ++++----- .../model/EnvironmentContributorTest.java | 2 +- .../hudson/model/FreeStyleProjectTest.java | 4 +- .../model/FreestyleJobPublisherTest.java | 9 +-- .../src/test/java/hudson/model/ItemsTest.java | 2 +- test/src/test/java/hudson/model/JobTest.java | 16 ++--- .../java/hudson/model/ParametersTest.java | 8 +-- .../src/test/java/hudson/model/QueueTest.java | 12 ++-- test/src/test/java/hudson/model/RSSTest.java | 4 +- .../test/java/hudson/model/RunMapTest.java | 4 +- .../model/RunParameterDefinitionTest.java | 58 +++++++++---------- test/src/test/java/hudson/model/RunTest.java | 2 +- .../model/WorkspaceCleanupThreadTest.java | 4 +- .../model/labels/LabelExpressionTest.java | 8 +-- .../hudson/model/queue/WideExecutionTest.java | 2 +- .../hudson/scm/AbstractScmTagActionTest.java | 4 +- test/src/test/java/hudson/scm/ScmTest.java | 7 +-- .../EnvironmentVariableNodePropertyTest.java | 6 +- .../hudson/tasks/ArtifactArchiverTest.java | 13 ++--- .../test/java/hudson/tasks/BatchFileTest.java | 10 ++-- .../java/hudson/tasks/BuildTriggerTest.java | 6 +- .../tasks/EnvVarsInConfigTasksTest.java | 16 ++--- .../java/hudson/tasks/FingerprinterTest.java | 38 ++++++------ .../src/test/java/hudson/tasks/MavenTest.java | 7 +-- .../src/test/java/hudson/tasks/ShellTest.java | 12 ++-- .../java/hudson/util/ProcessTreeTest.java | 2 +- .../hudson/widgets/HistoryWidgetTest.java | 2 +- .../jenkins/model/PeepholePermalinkTest.java | 4 +- .../jenkins/security/ClassFilterImplTest.java | 2 +- .../jenkins/tasks/SimpleBuildWrapperTest.java | 4 +- .../impl/RetainVariablesLocalRuleTest.java | 4 +- .../triggers/ReverseBuildTriggerTest.java | 2 +- .../jenkins/widgets/BuildTimeTrendTest.java | 8 +-- .../test/java/lib/form/ApplyButtonTest.java | 2 +- 48 files changed, 172 insertions(+), 201 deletions(-) diff --git a/test/src/test/java/hudson/LauncherTest.java b/test/src/test/java/hudson/LauncherTest.java index 8f67d8fdb1fb..3833b07d2168 100644 --- a/test/src/test/java/hudson/LauncherTest.java +++ b/test/src/test/java/hudson/LauncherTest.java @@ -87,7 +87,7 @@ public void correctlyExpandEnvVars() throws Exception { ; project.getBuildersList().add(script); - FreeStyleBuild build = project.scheduleBuild2(0).get(); + FreeStyleBuild build = rule.buildAndAssertSuccess(project); rule.assertLogContains("aaa aaaccc ccc", build); } @@ -108,7 +108,7 @@ public void overwriteSystemEnvVars() throws Exception { project.getBuildersList().add(script); project.setAssignedNode(slave.getComputer().getNode()); - FreeStyleBuild build = project.scheduleBuild2(0).get(); + FreeStyleBuild build = rule.buildAndAssertSuccess(project); rule.assertLogContains("original value and new value", build); } diff --git a/test/src/test/java/hudson/ProcStarterTest.java b/test/src/test/java/hudson/ProcStarterTest.java index 0ebf9a93e7f1..1dbc43a972e6 100644 --- a/test/src/test/java/hudson/ProcStarterTest.java +++ b/test/src/test/java/hudson/ProcStarterTest.java @@ -77,9 +77,8 @@ public void testNonExistingPwd() throws Exception { FreeStyleProject project = rule.createFreeStyleProject(); project.getBuildersList().add(new EchoBuilder()); - FreeStyleBuild run = project.scheduleBuild2(0).get(); + FreeStyleBuild run = rule.buildAndAssertStatus(Result.FAILURE, project); - rule.assertBuildStatus(Result.FAILURE, run); rule.assertLogContains("java.io.IOException: Process working directory", run); } diff --git a/test/src/test/java/hudson/cli/ConsoleCommandTest.java b/test/src/test/java/hudson/cli/ConsoleCommandTest.java index 19eecb1d103d..b30aaa68b5fe 100644 --- a/test/src/test/java/hudson/cli/ConsoleCommandTest.java +++ b/test/src/test/java/hudson/cli/ConsoleCommandTest.java @@ -144,7 +144,7 @@ public class ConsoleCommandTest { assertThat(result.stderr(), containsString("ERROR: Not sure what you meant by \"1a\"")); project.getBuildersList().add(new Shell("echo 1")); - project.scheduleBuild2(0).get(); + j.buildAndAssertSuccess(project); result = command .authorizedTo(Jenkins.READ, Item.READ, Item.BUILD) @@ -249,7 +249,7 @@ public class ConsoleCommandTest { } else { project.getBuildersList().add(new Shell("echo 1\necho 2\necho 3\necho 4\necho 5")); } - project.scheduleBuild2(0).get(); + j.buildAndAssertSuccess(project); assertThat(project.getBuildByNumber(1).getLog(), containsString("echo 1")); assertThat(project.getBuildByNumber(1).getLog(), containsString("echo 5")); diff --git a/test/src/test/java/hudson/cli/DeleteBuildsCommandTest.java b/test/src/test/java/hudson/cli/DeleteBuildsCommandTest.java index ef5139cdbb82..366f8e981839 100644 --- a/test/src/test/java/hudson/cli/DeleteBuildsCommandTest.java +++ b/test/src/test/java/hudson/cli/DeleteBuildsCommandTest.java @@ -60,7 +60,7 @@ public class DeleteBuildsCommandTest { } @Test public void deleteBuildsShouldFailWithoutJobReadPermission() throws Exception { - j.createFreeStyleProject("aProject").scheduleBuild2(0).get(); + j.buildAndAssertSuccess(j.createFreeStyleProject("aProject")); final CLICommandInvoker.Result result = command .authorizedTo(Jenkins.READ) @@ -71,7 +71,7 @@ public class DeleteBuildsCommandTest { } @Test public void deleteBuildsShouldFailWithoutRunDeletePermission() throws Exception { - j.createFreeStyleProject("aProject").scheduleBuild2(0).get(); + j.buildAndAssertSuccess(j.createFreeStyleProject("aProject")); final CLICommandInvoker.Result result = command .authorizedTo(Jenkins.READ, Item.READ) @@ -91,7 +91,7 @@ public class DeleteBuildsCommandTest { } @Test public void deleteBuildsShouldFailIfJobNameIsEmpty() throws Exception { - j.createFreeStyleProject("aProject").scheduleBuild2(0).get(); + j.buildAndAssertSuccess(j.createFreeStyleProject("aProject")); assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1)); final CLICommandInvoker.Result result = command @@ -103,7 +103,7 @@ public class DeleteBuildsCommandTest { } @Test public void deleteBuildsShouldSuccess() throws Exception { - j.createFreeStyleProject("aProject").scheduleBuild2(0).get(); + j.buildAndAssertSuccess(j.createFreeStyleProject("aProject")); assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1)); final CLICommandInvoker.Result result = command @@ -115,7 +115,7 @@ public class DeleteBuildsCommandTest { } @Test public void deleteBuildsShouldSuccessIfBuildDoesNotExist() throws Exception { - j.createFreeStyleProject("aProject").scheduleBuild2(0).get(); + j.buildAndAssertSuccess(j.createFreeStyleProject("aProject")); assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1)); final CLICommandInvoker.Result result = command @@ -126,7 +126,7 @@ public class DeleteBuildsCommandTest { } @Test public void deleteBuildsShouldSuccessIfBuildNumberZeroSpecified() throws Exception { - j.createFreeStyleProject("aProject").scheduleBuild2(0).get(); + j.buildAndAssertSuccess(j.createFreeStyleProject("aProject")); assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1)); final CLICommandInvoker.Result result = command diff --git a/test/src/test/java/hudson/cli/RunRangeCommand2Test.java b/test/src/test/java/hudson/cli/RunRangeCommand2Test.java index 99cfdfb70775..c9330304e2ec 100644 --- a/test/src/test/java/hudson/cli/RunRangeCommand2Test.java +++ b/test/src/test/java/hudson/cli/RunRangeCommand2Test.java @@ -58,7 +58,7 @@ public class RunRangeCommand2Test { } @Test public void dummyRangeShouldFailIfJobNameIsEmptyOnEmptyJenkins() throws Exception { - j.createFreeStyleProject("aProject").scheduleBuild2(0).get(); + j.buildAndAssertSuccess(j.createFreeStyleProject("aProject")); assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds().size(), equalTo(1)); CLICommandInvoker.Result result = command @@ -70,7 +70,7 @@ public class RunRangeCommand2Test { } @Test public void dummyRangeShouldFailIfJobNameIsSpaceOnEmptyJenkins() throws Exception { - j.createFreeStyleProject("aProject").scheduleBuild2(0).get(); + j.buildAndAssertSuccess(j.createFreeStyleProject("aProject")); assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds().size(), equalTo(1)); CLICommandInvoker.Result result = command diff --git a/test/src/test/java/hudson/cli/RunRangeCommandTest.java b/test/src/test/java/hudson/cli/RunRangeCommandTest.java index 6d60b3ea633a..fee41c163386 100644 --- a/test/src/test/java/hudson/cli/RunRangeCommandTest.java +++ b/test/src/test/java/hudson/cli/RunRangeCommandTest.java @@ -30,7 +30,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; import hudson.Extension; import hudson.model.FreeStyleProject; @@ -62,7 +61,7 @@ public class RunRangeCommandTest { command = new CLICommandInvoker(j, new DummyRangeCommand()); project = j.createFreeStyleProject(PROJECT_NAME); for (int i=0; i build, Launcher launcher, BuildListen throw new InterruptedException(); } }); - Run build = project.scheduleBuild2(0).get(); - assertEquals(Result.FAILURE, build.getResult()); + j.buildAndAssertStatus(Result.FAILURE, project); assertEquals(Result.FAILURE, wrapper.buildResultInTearDown); } diff --git a/test/src/test/java/hudson/model/AbstractBuildTest.java b/test/src/test/java/hudson/model/AbstractBuildTest.java index 43dc7c8f1a56..12744e3a7f3d 100644 --- a/test/src/test/java/hudson/model/AbstractBuildTest.java +++ b/test/src/test/java/hudson/model/AbstractBuildTest.java @@ -185,7 +185,7 @@ public void culprits() throws Exception { // 2nd build scm.addChange().withAuthor("bob"); p.getBuildersList().add(new FailureBuilder()); - b = j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + b = j.buildAndAssertStatus(Result.FAILURE, p); assertCulprits(b, "bob"); // 3rd build. bob continues to be in culprit @@ -203,12 +203,12 @@ public void culprits() throws Exception { // 4th build, unstable. culprit list should continue scm.addChange().withAuthor("dave"); p.getBuildersList().replaceBy(Collections.singleton(new UnstableBuilder())); - b = j.assertBuildStatus(Result.UNSTABLE, p.scheduleBuild2(0).get()); + b = j.buildAndAssertStatus(Result.UNSTABLE, p); assertCulprits(b, "bob", "charlie", "dave"); // 5th build, unstable. culprit list should continue scm.addChange().withAuthor("eve"); - b = j.assertBuildStatus(Result.UNSTABLE, p.scheduleBuild2(0).get()); + b = j.buildAndAssertStatus(Result.UNSTABLE, p); assertCulprits(b, "bob", "charlie", "dave", "eve"); // 6th build, success, accumulation continues up to this point @@ -241,8 +241,7 @@ public void lastBuildNextBuild() throws Exception { public void doNotInterruptBuildAbruptlyWhenExceptionThrownFromBuildStep() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.getBuildersList().add(new ThrowBuilder()); - FreeStyleBuild build = p.scheduleBuild2(0).get(); - j.assertBuildStatus(Result.FAILURE, build); + FreeStyleBuild build = j.buildAndAssertStatus(Result.FAILURE, p); j.assertLogContains("Finished: FAILURE", build); j.assertLogContains("Build step 'ThrowBuilder' marked build as failure", build); } diff --git a/test/src/test/java/hudson/model/AbstractItemTest.java b/test/src/test/java/hudson/model/AbstractItemTest.java index 96c91a719fc2..f227238e2e46 100644 --- a/test/src/test/java/hudson/model/AbstractItemTest.java +++ b/test/src/test/java/hudson/model/AbstractItemTest.java @@ -43,7 +43,7 @@ public void reload() throws Exception { FreeStyleProject p = jenkins.createProject(FreeStyleProject.class, "foo"); p.setDescription("Hello World"); - FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = j.buildAndAssertSuccess(p); b.setDescription("This is my build"); // update on disk representation diff --git a/test/src/test/java/hudson/model/AbstractProjectTest.java b/test/src/test/java/hudson/model/AbstractProjectTest.java index 5457cdfc1307..4ef7e5dd2954 100644 --- a/test/src/test/java/hudson/model/AbstractProjectTest.java +++ b/test/src/test/java/hudson/model/AbstractProjectTest.java @@ -114,7 +114,7 @@ public void wipeWorkspace() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); project.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo hello") : new Shell("echo hello")); - FreeStyleBuild b = project.scheduleBuild2(0).get(); + FreeStyleBuild b = j.buildAndAssertSuccess(project); assertTrue("Workspace should exist by now", b.getWorkspace().exists()); @@ -132,7 +132,7 @@ public void wipeWorkspaceProtected() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); project.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo hello") : new Shell("echo hello")); - FreeStyleBuild b = project.scheduleBuild2(0).get(); + FreeStyleBuild b = j.buildAndAssertSuccess(project); assertTrue("Workspace should exist by now", b.getWorkspace().exists()); @@ -283,7 +283,7 @@ public void testGetBuildAfterGC() { @Issue("JENKINS-18678") public void renameJobLostBuilds() throws Exception { FreeStyleProject p = j.createFreeStyleProject("initial"); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); assertEquals(1, p.getBuilds().stream().count()); p.renameTo("edited"); p._getRuns().purgeCache(); diff --git a/test/src/test/java/hudson/model/BuildExecutionTest.java b/test/src/test/java/hudson/model/BuildExecutionTest.java index e9326d318def..6effa4f5ee73 100644 --- a/test/src/test/java/hudson/model/BuildExecutionTest.java +++ b/test/src/test/java/hudson/model/BuildExecutionTest.java @@ -44,7 +44,7 @@ public class BuildExecutionTest { @Test public void workspaceReliablyReleased() throws Exception { FreeStyleProject p = r.createFreeStyleProject(); p.getPublishersList().add(new BrokenPublisher()); - FreeStyleBuild b = r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + FreeStyleBuild b = r.buildAndAssertStatus(Result.FAILURE, p); r.assertLogContains(Messages.Build_post_build_steps_failed(), b); FilePath ws = r.jenkins.getWorkspaceFor(p); try (WorkspaceList.Lease lease = r.jenkins.toComputer().getWorkspaceList().allocate(ws)) { diff --git a/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java b/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java index 7ecfa13bfb2e..9b27bad306bf 100644 --- a/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java +++ b/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java @@ -116,7 +116,7 @@ public void doubleDots() throws Exception { p.getBuildersList().add(new BatchFile("echo > abc..def")); else p.getBuildersList().add(new Shell("touch abc..def")); - p.scheduleBuild2(0).get(); + j.buildAndAssertSuccess(p); // can we see it? j.createWebClient().goTo("job/"+p.getName()+"/ws/abc..def","application/octet-stream"); @@ -144,7 +144,7 @@ public void doubleDots2() throws Exception { // create a problematic file name in the workspace FreeStyleProject p = j.createFreeStyleProject(); p.getBuildersList().add(new Shell("mkdir abc; touch abc/def.bin")); - p.scheduleBuild2(0).get(); + j.buildAndAssertSuccess(p); // can we see it? j.createWebClient().goTo("job/"+p.getName()+"/ws/abc%5Cdef.bin","application/octet-stream"); @@ -161,7 +161,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen return true; } }); // Kanji - p.scheduleBuild2(0).get(); + j.buildAndAssertSuccess(p); // can we see it? j.createWebClient().goTo("job/"+p.getName()+"/ws/%e6%bc%a2%e5%ad%97.bin","application/octet-stream"); @@ -183,7 +183,7 @@ public void glob() throws Exception { return true; } }); - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); String text = j.createWebClient().goTo("job/"+p.getName()+"/ws/**/*.java").asText(); assertTrue(text, text.contains("X.java")); assertTrue(text, text.contains("XTest.java")); @@ -197,7 +197,7 @@ public void zipDownload() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.setScm(new SingleFileSCM("artifact.out", "Hello world!")); p.getPublishersList().add(new ArtifactArchiver("*", "", true)); - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); HtmlPage page = j.createWebClient().goTo("job/"+p.getName()+"/lastSuccessfulBuild/artifact/"); Page download = page.getAnchorByHref("./*zip*/archive.zip").click(); @@ -223,7 +223,7 @@ public void zipDownloadFileLeakMx_hypothesis() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.setScm(new SingleFileSCM("artifact.out", content)); p.getPublishersList().add(new ArtifactArchiver("*", "", true)); - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); HtmlPage page = j.createWebClient().goTo("job/" + p.getName() + "/lastSuccessfulBuild/artifact/"); Page downloadPage = page.getAnchorByHref("artifact.out").click(); @@ -246,7 +246,7 @@ public void zipDownloadFileLeakMx() throws Exception { // add randomness just to prevent any potential caching issue p.setScm(new SingleFileSCM("artifact.out", "Hello world! " + Math.random())); p.getPublishersList().add(new ArtifactArchiver("*", "", true)); - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); HtmlPage page = j.createWebClient().goTo("job/" + p.getName() + "/lastSuccessfulBuild/artifact/"); for (int clicks = 0; clicks < numOfClicks; clicks++) { @@ -300,7 +300,7 @@ public void contentSecurityPolicy() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.setScm(new SingleFileSCM("test.html", "

      Hello world!

      ")); p.getPublishersList().add(new ArtifactArchiver("*", "", true)); - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); HtmlPage page = j.createWebClient().goTo("job/" + p.getName() + "/lastSuccessfulBuild/artifact/test.html"); for (String header : new String[]{"Content-Security-Policy", "X-WebKit-CSP", "X-Content-Security-Policy"}) { @@ -569,7 +569,7 @@ public void symlink_outsideWorkspace_areNotAllowed() throws Exception { p.getBuildersList().add(new Shell(script)); } - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); JenkinsRule.WebClient wc = j.createWebClient(); wc.getOptions().setThrowExceptionOnFailingStatusCode(false); @@ -714,7 +714,7 @@ public void symlink_avoidLeakingInformation_aboutIllegalFolder() throws Exceptio p.getBuildersList().add(new Shell(script)); } - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); JenkinsRule.WebClient wc = j.createWebClient(); wc.getOptions().setThrowExceptionOnFailingStatusCode(false); @@ -777,7 +777,7 @@ public void junctionAndSymlink_outsideWorkspace_areNotAllowed_windowsJunction() String script = loadContentFromResource("outsideWorkspaceStructureWithJunctions.bat"); p.getBuildersList().add(new BatchFile(script)); - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); JenkinsRule.WebClient wc = j.createWebClient(); wc.getOptions().setThrowExceptionOnFailingStatusCode(false); @@ -908,7 +908,7 @@ private List getListOfEntriesInDownloadedZip(UnexpectedPage zipPage) thr public void directSymlink_forTestingZip() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); FilePath ws = p.getSomeWorkspace(); /* @@ -968,7 +968,7 @@ public void symlink_insideWorkspace_areNotAllowedAnymore() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); // build once to have the workspace set up - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); File jobWorkspaceFolder = new File(new File(j.jenkins.getRootDir(), "workspace"), p.name); File folderInsideWorkspace = new File(jobWorkspaceFolder, "asset"); @@ -995,7 +995,7 @@ public void symlink_insideWorkspace_areNotAllowedAnymore() throws Exception { p.getBuildersList().add(new Shell(script)); } - assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + j.buildAndAssertSuccess(p); JenkinsRule.WebClient wc = j.createWebClient(); wc.getOptions().setThrowExceptionOnFailingStatusCode(false); diff --git a/test/src/test/java/hudson/model/EnvironmentContributorTest.java b/test/src/test/java/hudson/model/EnvironmentContributorTest.java index 897cf8690e77..31f506342f29 100644 --- a/test/src/test/java/hudson/model/EnvironmentContributorTest.java +++ b/test/src/test/java/hudson/model/EnvironmentContributorTest.java @@ -24,7 +24,7 @@ public void projectScoped() throws Exception { CaptureEnvironmentBuilder c = new CaptureEnvironmentBuilder(); p.getBuildersList().add(c); p.setDescription("Issac Newton"); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); assertEquals("Issac Newton", c.getEnvVars().get("ABC")); assertEquals("built-in", c.getEnvVars().get("NODE_NAME")); diff --git a/test/src/test/java/hudson/model/FreeStyleProjectTest.java b/test/src/test/java/hudson/model/FreeStyleProjectTest.java index 879dedf9d55d..b7acb7f1ad6e 100644 --- a/test/src/test/java/hudson/model/FreeStyleProjectTest.java +++ b/test/src/test/java/hudson/model/FreeStyleProjectTest.java @@ -156,8 +156,10 @@ public void buildStabilityReports() throws Exception { if (j <= 16) { expectedFails++; } + this.j.buildAndAssertStatus(Result.FAILURE, p); + } else { + this.j.buildAndAssertSuccess(p); } - p.scheduleBuild2(0).get(); } HealthReport health = p.getBuildHealth(); diff --git a/test/src/test/java/hudson/model/FreestyleJobPublisherTest.java b/test/src/test/java/hudson/model/FreestyleJobPublisherTest.java index 25b52f3242ae..24bfe3fcd0dc 100644 --- a/test/src/test/java/hudson/model/FreestyleJobPublisherTest.java +++ b/test/src/test/java/hudson/model/FreestyleJobPublisherTest.java @@ -40,8 +40,7 @@ public void testFreestyleWithFalsePublisher() throws Exception { artifactArchiver.setOnlyIfSuccessful(false); p.getPublishersList().add(artifactArchiver); // transfer file to build dir - FreeStyleBuild b = p.scheduleBuild2(0).get(); - assertEquals("Build must fail, because we used FalsePublisher", Result.FAILURE, b.getResult()); + FreeStyleBuild b = j.buildAndAssertStatus(Result.FAILURE, p); File file = new File(b.getArtifactsDir(), "result.txt"); assertTrue("ArtifactArchiver is executed even prior publisher fails", file.exists()); assertEquals("Publisher, after publisher with return false status, must see FAILURE status", FileUtils.readFileToString(file, StandardCharsets.UTF_8), Result.FAILURE.toString()); @@ -62,9 +61,8 @@ public void testFreestyleWithExceptionPublisher() throws Exception { artifactArchiver.setOnlyIfSuccessful(false); p.getPublishersList().add(artifactArchiver); // transfer file to build dir - FreeStyleBuild b = p.scheduleBuild2(0).get(); + FreeStyleBuild b = j.buildAndAssertStatus(Result.FAILURE, p); - assertEquals("Build must fail, because we used AbortExceptionPublisher", Result.FAILURE, b.getResult()); j.assertLogNotContains("\tat", b); // log must not contain stacktrace j.assertLogContains("Threw AbortException from publisher!", b); // log must contain exact error message File file = new File(b.getArtifactsDir(), "result.txt"); @@ -87,9 +85,8 @@ public void testFreestyleWithIOExceptionPublisher() throws Exception { artifactArchiver.setOnlyIfSuccessful(false); p.getPublishersList().add(artifactArchiver); // transfer file to build dir - FreeStyleBuild b = p.scheduleBuild2(0).get(); + FreeStyleBuild b = j.buildAndAssertStatus(Result.FAILURE, p); - assertEquals("Build must fail, because we used FalsePublisher", Result.FAILURE, b.getResult()); j.assertLogContains("\tat hudson.model.utils.IOExceptionPublisher", b); // log must contain stacktrace j.assertLogContains("Threw IOException from publisher!", b); // log must contain exact error message File file = new File(b.getArtifactsDir(), "result.txt"); diff --git a/test/src/test/java/hudson/model/ItemsTest.java b/test/src/test/java/hudson/model/ItemsTest.java index bf2d7fd4e560..2bbc7b23f5a6 100644 --- a/test/src/test/java/hudson/model/ItemsTest.java +++ b/test/src/test/java/hudson/model/ItemsTest.java @@ -166,7 +166,7 @@ public void allItems() throws Exception { MockFolder foo = r.createFolder("foo"); MockFolder bar = r.createFolder("bar"); FreeStyleProject test = foo.createProject(FreeStyleProject.class, "test"); - test.scheduleBuild2(0).get(); + r.buildAndAssertSuccess(test); Items.move(test, bar); assertFalse(new File(tmp, "foo/test/1").exists()); assertTrue(new File(tmp, "bar/test/1").exists()); diff --git a/test/src/test/java/hudson/model/JobTest.java b/test/src/test/java/hudson/model/JobTest.java index bc3f25048695..cb7ab86c9789 100644 --- a/test/src/test/java/hudson/model/JobTest.java +++ b/test/src/test/java/hudson/model/JobTest.java @@ -305,13 +305,13 @@ private static void tryConfigDotXml(JenkinsRule.WebClient wc, int status, String final FreeStyleProject p = j.createFreeStyleProject(); RunLoadCounter.prepare(p); p.getBuildersList().add(new FailureBuilder()); - j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); - j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); - j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + j.buildAndAssertStatus(Result.FAILURE, p); + j.buildAndAssertStatus(Result.FAILURE, p); + j.buildAndAssertStatus(Result.FAILURE, p); p.getBuildersList().remove(FailureBuilder.class); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); + j.buildAndAssertSuccess(p); + j.buildAndAssertSuccess(p); assertEquals(6, p.getLastSuccessfulBuild().getNumber()); assertEquals(3, RunLoadCounter.assertMaxLoads(p, 1, new Callable() { @Override public Integer call() { @@ -324,7 +324,7 @@ private static void tryConfigDotXml(JenkinsRule.WebClient wc, int status, String @Test public void testRenameWithCustomBuildsDirWithSubdir() throws Exception { j.jenkins.setRawBuildsDir("${JENKINS_HOME}/builds/${ITEM_FULL_NAME}/builds"); final FreeStyleProject p = j.createFreeStyleProject(); - p.scheduleBuild2(0).get(); + j.buildAndAssertSuccess(p); p.renameTo("different-name"); } @@ -525,7 +525,7 @@ public void interruptOnDelete() throws Exception { j.jenkins.setNumExecutors(0); FreeStyleProject p = j.createFreeStyleProject(); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); node.setVirtualName(desiredNodeName); diff --git a/test/src/test/java/hudson/model/ParametersTest.java b/test/src/test/java/hudson/model/ParametersTest.java index 96c81998384a..5b321c1e7372 100644 --- a/test/src/test/java/hudson/model/ParametersTest.java +++ b/test/src/test/java/hudson/model/ParametersTest.java @@ -46,7 +46,7 @@ public class ParametersTest { @Test public void parameterTypes() throws Exception { FreeStyleProject otherProject = j.createFreeStyleProject(); - otherProject.scheduleBuild2(0).get(); + j.buildAndAssertSuccess(otherProject); FreeStyleProject project = j.createFreeStyleProject(); ParametersDefinitionProperty pdp = new ParametersDefinitionProperty( @@ -145,7 +145,7 @@ public void sensitiveParameters() throws Exception { CaptureEnvironmentBuilder builder = new CaptureEnvironmentBuilder(); project.getBuildersList().add(builder); - FreeStyleBuild build = project.scheduleBuild2(0).get(); + FreeStyleBuild build = j.buildAndAssertSuccess(project); Set sensitiveVars = build.getSensitiveBuildVariables(); assertNotNull(sensitiveVars); @@ -162,7 +162,7 @@ public void nonSensitiveParameters() throws Exception { CaptureEnvironmentBuilder builder = new CaptureEnvironmentBuilder(); project.getBuildersList().add(builder); - FreeStyleBuild build = project.scheduleBuild2(0).get(); + FreeStyleBuild build = j.buildAndAssertSuccess(project); Set sensitiveVars = build.getSensitiveBuildVariables(); assertNotNull(sensitiveVars); @@ -182,7 +182,7 @@ public void mixedSensitivity() throws Exception { CaptureEnvironmentBuilder builder = new CaptureEnvironmentBuilder(); project.getBuildersList().add(builder); - FreeStyleBuild build = project.scheduleBuild2(0).get(); + FreeStyleBuild build = j.buildAndAssertSuccess(project); Set sensitiveVars = build.getSensitiveBuildVariables(); assertNotNull(sensitiveVars); diff --git a/test/src/test/java/hudson/model/QueueTest.java b/test/src/test/java/hudson/model/QueueTest.java index c384cb1cb99b..6d82e8abdfa8 100644 --- a/test/src/test/java/hudson/model/QueueTest.java +++ b/test/src/test/java/hudson/model/QueueTest.java @@ -254,7 +254,7 @@ private void resetQueueState() throws IOException { @Test public void queue_id_to_run_mapping() throws Exception { FreeStyleProject testProject = r.createFreeStyleProject("test"); - FreeStyleBuild build = r.assertBuildStatusSuccess(testProject.scheduleBuild2(0)); + FreeStyleBuild build = r.buildAndAssertSuccess(testProject); Assert.assertNotEquals(Run.QUEUE_ID_UNKNOWN, build.getQueueId()); } @@ -670,7 +670,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen return true; } }); - r.assertBuildStatusSuccess(p.scheduleBuild2(0)); + r.buildAndAssertSuccess(p); } private static Authentication alice2 = new UsernamePasswordAuthenticationToken("alice","alice", Collections.emptySet()); @@ -698,8 +698,8 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen } }); - final FreeStyleBuild b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0)); - final FreeStyleBuild b2 = r.assertBuildStatusSuccess(p.scheduleBuild2(0)); + final FreeStyleBuild b1 = r.buildAndAssertSuccess(p); + final FreeStyleBuild b2 = r.buildAndAssertSuccess(p); // scheduling algorithm would prefer running the same job on the same node // kutzi: 'prefer' != 'enforce', therefore disabled this assertion: assertSame(b1.getBuiltOn(),b2.getBuiltOn()); @@ -708,7 +708,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen // now that we prohibit alice to do a build on the same node, the build should run elsewhere for (int i=0; i<3; i++) { - FreeStyleBuild b3 = r.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b3 = r.buildAndAssertSuccess(p); assertNotSame(b3.getBuiltOnStr(), b1.getBuiltOnStr()); } } @@ -877,7 +877,7 @@ public void shouldRunFlyweightTaskOnProvisionedNodeWhenNodeRestricted() throws E dummyCloud.label = label; r.jenkins.clouds.add(dummyCloud); matrixProject.setAssignedLabel(label); - r.assertBuildStatusSuccess(matrixProject.scheduleBuild2(0)); + r.buildAndAssertSuccess(matrixProject); assertEquals("aws-linux-dummy", matrixProject.getBuilds().getLastBuild().getBuiltOn().getLabelString()); } diff --git a/test/src/test/java/hudson/model/RSSTest.java b/test/src/test/java/hudson/model/RSSTest.java index 35af27b6be30..88b5a7053d54 100644 --- a/test/src/test/java/hudson/model/RSSTest.java +++ b/test/src/test/java/hudson/model/RSSTest.java @@ -465,7 +465,7 @@ private XmlPage getRssLatestPage(JenkinsRule.WebClient webClient, String pathPre private FreeStyleProject runSuccessfulBuild() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); return p; } @@ -478,7 +478,7 @@ private FreeStyleProject runSuccessfulBuild(String userId) throws Exception { private void runFailingBuild() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.getBuildersList().add(new FailureBuilder()); - j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + j.buildAndAssertStatus(Result.FAILURE, p); } private void runFailingBuild(String userId) throws Exception { diff --git a/test/src/test/java/hudson/model/RunMapTest.java b/test/src/test/java/hudson/model/RunMapTest.java index 3dd69e257a40..bf8487b9456d 100644 --- a/test/src/test/java/hudson/model/RunMapTest.java +++ b/test/src/test/java/hudson/model/RunMapTest.java @@ -40,7 +40,7 @@ public class RunMapTest { FreeStyleProject p = r.createFreeStyleProject(); // want some completed build records - FreeStyleBuild b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b1 = r.buildAndAssertSuccess(p); // now create a build that hangs until we signal the OneShotEvent p.getBuildersList().add(new SleepBuilder(9999999)); @@ -103,7 +103,7 @@ public class RunMapTest { @Issue("JENKINS-15533") @Test public void runtimeExceptionInUnmarshalling() throws Exception { FreeStyleProject p = r.createFreeStyleProject(); - FreeStyleBuild b = r.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = r.buildAndAssertSuccess(p); b.addAction(new BombAction()); b.save(); diff --git a/test/src/test/java/hudson/model/RunParameterDefinitionTest.java b/test/src/test/java/hudson/model/RunParameterDefinitionTest.java index 2b8e6fd8f5e6..890d14e9381e 100644 --- a/test/src/test/java/hudson/model/RunParameterDefinitionTest.java +++ b/test/src/test/java/hudson/model/RunParameterDefinitionTest.java @@ -63,9 +63,9 @@ public class RunParameterDefinitionTest { MockFolder dir = j.createFolder("dir"); MockFolder subdir = dir.createProject(MockFolder.class, "sub dir"); FreeStyleProject p = subdir.createProject(FreeStyleProject.class, "some project"); - p.scheduleBuild2(0).get(); - FreeStyleBuild build2 = p.scheduleBuild2(0).get(); - p.scheduleBuild2(0).get(); + j.buildAndAssertSuccess(p); + FreeStyleBuild build2 = j.buildAndAssertSuccess(p); + j.buildAndAssertSuccess(p); String id = build2.getExternalizableId(); assertEquals("dir/sub dir/some project#2", id); assertEquals(build2, Run.fromExternalizableId(id)); @@ -91,19 +91,19 @@ public class RunParameterDefinitionTest { public void testNULLFilter() throws Exception { FreeStyleProject project = j.createFreeStyleProject("project"); - FreeStyleBuild successfulBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild successfulBuild = j.buildAndAssertSuccess(project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.UNSTABLE))); - FreeStyleBuild unstableBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild unstableBuild = j.buildAndAssertStatus(Result.UNSTABLE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.FAILURE))); - FreeStyleBuild failedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild failedBuild = j.buildAndAssertStatus(Result.FAILURE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.NOT_BUILT))); - FreeStyleBuild notBuiltBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild notBuiltBuild = j.buildAndAssertStatus(Result.NOT_BUILT, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.ABORTED))); - FreeStyleBuild abortedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild abortedBuild = j.buildAndAssertStatus(Result.ABORTED, project); FreeStyleProject paramProject = j.createFreeStyleProject("paramProject"); ParametersDefinitionProperty pdp = @@ -123,19 +123,19 @@ public void testNULLFilter() throws Exception { public void testALLFilter() throws Exception { FreeStyleProject project = j.createFreeStyleProject("project"); - FreeStyleBuild successfulBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild successfulBuild = j.buildAndAssertSuccess(project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.UNSTABLE))); - FreeStyleBuild unstableBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild unstableBuild = j.buildAndAssertStatus(Result.UNSTABLE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.FAILURE))); - FreeStyleBuild failedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild failedBuild = j.buildAndAssertStatus(Result.FAILURE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.NOT_BUILT))); - FreeStyleBuild notBuiltBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild notBuiltBuild = j.buildAndAssertStatus(Result.NOT_BUILT, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.ABORTED))); - FreeStyleBuild abortedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild abortedBuild = j.buildAndAssertStatus(Result.ABORTED, project); FreeStyleProject paramProject = j.createFreeStyleProject("paramProject"); ParametersDefinitionProperty pdp = @@ -154,19 +154,19 @@ public void testALLFilter() throws Exception { public void testCOMPLETEDFilter() throws Exception { FreeStyleProject project = j.createFreeStyleProject("project"); - FreeStyleBuild successfulBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild successfulBuild = j.buildAndAssertSuccess(project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.UNSTABLE))); - FreeStyleBuild unstableBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild unstableBuild = j.buildAndAssertStatus(Result.UNSTABLE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.FAILURE))); - FreeStyleBuild failedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild failedBuild = j.buildAndAssertStatus(Result.FAILURE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.NOT_BUILT))); - FreeStyleBuild notBuiltBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild notBuiltBuild = j.buildAndAssertStatus(Result.NOT_BUILT, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.ABORTED))); - FreeStyleBuild abortedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild abortedBuild = j.buildAndAssertStatus(Result.ABORTED, project); FreeStyleProject paramProject = j.createFreeStyleProject("paramProject"); ParametersDefinitionProperty pdp = @@ -185,19 +185,19 @@ public void testCOMPLETEDFilter() throws Exception { public void testSUCCESSFULFilter() throws Exception { FreeStyleProject project = j.createFreeStyleProject("project"); - FreeStyleBuild successfulBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild successfulBuild = j.buildAndAssertSuccess(project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.UNSTABLE))); - FreeStyleBuild unstableBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild unstableBuild = j.buildAndAssertStatus(Result.UNSTABLE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.FAILURE))); - FreeStyleBuild failedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild failedBuild = j.buildAndAssertStatus(Result.FAILURE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.NOT_BUILT))); - FreeStyleBuild notBuiltBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild notBuiltBuild = j.buildAndAssertStatus(Result.NOT_BUILT, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.ABORTED))); - FreeStyleBuild abortedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild abortedBuild = j.buildAndAssertStatus(Result.ABORTED, project); FreeStyleProject paramProject = j.createFreeStyleProject("paramProject"); ParametersDefinitionProperty pdp = @@ -217,19 +217,19 @@ public void testSUCCESSFULFilter() throws Exception { public void testSTABLEFilter() throws Exception { FreeStyleProject project = j.createFreeStyleProject("project"); - FreeStyleBuild successfulBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild successfulBuild = j.buildAndAssertSuccess(project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.UNSTABLE))); - FreeStyleBuild unstableBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild unstableBuild = j.buildAndAssertStatus(Result.UNSTABLE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.FAILURE))); - FreeStyleBuild failedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild failedBuild = j.buildAndAssertStatus(Result.FAILURE, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.NOT_BUILT))); - FreeStyleBuild notBuiltBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild notBuiltBuild = j.buildAndAssertStatus(Result.NOT_BUILT, project); project.getPublishersList().replaceBy(Collections.singleton(new ResultPublisher(Result.ABORTED))); - FreeStyleBuild abortedBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild abortedBuild = j.buildAndAssertStatus(Result.ABORTED, project); FreeStyleProject paramProject = j.createFreeStyleProject("paramProject"); ParametersDefinitionProperty pdp = @@ -249,7 +249,7 @@ public void testSTABLEFilter() throws Exception { public void testLoadEnvironmentVariablesWhenRunParameterJobHasBeenDeleted() throws Exception { FreeStyleProject project = j.createFreeStyleProject("project"); - FreeStyleBuild successfulBuild = project.scheduleBuild2(0).get(); + FreeStyleBuild successfulBuild = j.buildAndAssertSuccess(project); FreeStyleProject paramProject = j.createFreeStyleProject("paramProject"); ParametersDefinitionProperty pdp = diff --git a/test/src/test/java/hudson/model/RunTest.java b/test/src/test/java/hudson/model/RunTest.java index 24159b701fc3..73e7c35c2d83 100644 --- a/test/src/test/java/hudson/model/RunTest.java +++ b/test/src/test/java/hudson/model/RunTest.java @@ -84,7 +84,7 @@ public class RunTest { }); } }); - j.assertBuildStatusSuccess(j.createFreeStyleProject("stuff").scheduleBuild2(0)); + j.buildAndAssertSuccess(j.createFreeStyleProject("stuff")); j.createWebClient().assertFails("job/stuff/1/nonexistent", HttpURLConnection.HTTP_NOT_FOUND); } diff --git a/test/src/test/java/hudson/model/WorkspaceCleanupThreadTest.java b/test/src/test/java/hudson/model/WorkspaceCleanupThreadTest.java index 8e9a511ab2ac..027ce17d81d0 100644 --- a/test/src/test/java/hudson/model/WorkspaceCleanupThreadTest.java +++ b/test/src/test/java/hudson/model/WorkspaceCleanupThreadTest.java @@ -61,7 +61,7 @@ public class WorkspaceCleanupThreadTest { FilePath ws1 = createOldWorkspaceOn(r.createOnlineSlave(), p); p.setAssignedNode(r.jenkins); - FreeStyleBuild b = r.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = r.buildAndAssertSuccess(p); assertEquals(r.jenkins, b.getBuiltOn()); FilePath ws2 = b.getWorkspace(); @@ -189,7 +189,7 @@ public void deleteTemporaryDirectory() throws Exception { private FilePath createOldWorkspaceOn(Node slave, FreeStyleProject p) throws Exception { p.setAssignedNode(slave); - FreeStyleBuild b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b1 = r.buildAndAssertSuccess(p); assertEquals(slave, b1.getBuiltOn()); FilePath ws = b1.getWorkspace(); assertNotNull(ws); diff --git a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java index 812ebdd85551..047a44752b0d 100644 --- a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java +++ b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java @@ -100,7 +100,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen Thread.sleep(1000); // time window to ensure queue has tried to assign f2 build // p3 is tied to 'win', so even though p1 is busy, this should still go ahead and complete - FreeStyleBuild b3 = j.assertBuildStatusSuccess(p3.scheduleBuild2(0)); + FreeStyleBuild b3 = j.buildAndAssertSuccess(p3); assertSame(w64,b3.getBuiltOn()); seq.phase(3); // once we confirm that p3 build is over, we let p1 proceed @@ -125,15 +125,15 @@ public void queueBehavior2() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.setAssignedLabel(j.jenkins.getLabel("!win")); - FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = j.buildAndAssertSuccess(p); assertSame(j.jenkins,b.getBuiltOn()); p.setAssignedLabel(j.jenkins.getLabel("win")); - b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + b = j.buildAndAssertSuccess(p); assertSame(s,b.getBuiltOn()); p.setAssignedLabel(j.jenkins.getLabel("!win")); - b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + b = j.buildAndAssertSuccess(p); assertSame(j.jenkins,b.getBuiltOn()); } diff --git a/test/src/test/java/hudson/model/queue/WideExecutionTest.java b/test/src/test/java/hudson/model/queue/WideExecutionTest.java index 2bfd6f396233..e170575e9e75 100644 --- a/test/src/test/java/hudson/model/queue/WideExecutionTest.java +++ b/test/src/test/java/hudson/model/queue/WideExecutionTest.java @@ -97,7 +97,7 @@ public String getDisplayName() { @Test public void run() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); - FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = j.buildAndAssertSuccess(p); assertEquals("I was here", b.getDescription()); } } diff --git a/test/src/test/java/hudson/scm/AbstractScmTagActionTest.java b/test/src/test/java/hudson/scm/AbstractScmTagActionTest.java index 065bcbfdfa6f..df6a146f48c6 100644 --- a/test/src/test/java/hudson/scm/AbstractScmTagActionTest.java +++ b/test/src/test/java/hudson/scm/AbstractScmTagActionTest.java @@ -59,7 +59,7 @@ public void regularTextDisplayedCorrectly() throws Exception { String tagToKeep = "Nice tag with space"; p.setScm(new FakeSCM(tagToKeep)); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); String tooltip = buildAndExtractTooltipAttribute(p); assertEquals(tagToKeep, tooltip); @@ -71,7 +71,7 @@ public void preventXssInTagAction() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.setScm(new FakeSCM("XSS")); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); String tooltip = buildAndExtractTooltipAttribute(p); assertThat(tooltip, not(containsString("<"))); diff --git a/test/src/test/java/hudson/scm/ScmTest.java b/test/src/test/java/hudson/scm/ScmTest.java index e95034c6c373..bd683c8d5541 100644 --- a/test/src/test/java/hudson/scm/ScmTest.java +++ b/test/src/test/java/hudson/scm/ScmTest.java @@ -23,7 +23,6 @@ */ package hudson.scm; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import hudson.FilePath; @@ -31,7 +30,6 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; -import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import hudson.model.Node; import hudson.model.Result; @@ -69,7 +67,7 @@ private Object writeReplace() { // don't really care about save return new NullSCM(); } }); - p.scheduleBuild2(0).get(); + j.buildAndAssertSuccess(p); p.delete(); assertTrue(callback[0]); } @@ -92,7 +90,6 @@ private Object writeReplace() { // don't really care about save } }); - FreeStyleBuild build = p.scheduleBuild2(0).get(); - assertEquals(Result.ABORTED, build.getResult()); + j.buildAndAssertStatus(Result.ABORTED, p); } } diff --git a/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java b/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java index 6b2cc338b3e8..e13958293547 100644 --- a/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java +++ b/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java @@ -8,7 +8,6 @@ import hudson.model.FreeStyleProject; import hudson.model.Node; import hudson.model.ParametersDefinitionProperty; -import hudson.model.Result; import hudson.model.StringParameterDefinition; import java.io.IOException; import java.util.Collections; @@ -168,11 +167,8 @@ private Map executeBuild(Node node) throws Exception { project.getBuildersList().add(builder); project.setAssignedLabel(node.getSelfLabel()); - // use a timeout so we don't wait infinitely in case of failure - FreeStyleBuild build = project.scheduleBuild2(0).get(/*10, TimeUnit.SECONDS*/); + FreeStyleBuild build = j.buildAndAssertSuccess(project); - assertEquals(Result.SUCCESS, build.getResult()); - return builder.getEnvVars(); } diff --git a/test/src/test/java/hudson/tasks/ArtifactArchiverTest.java b/test/src/test/java/hudson/tasks/ArtifactArchiverTest.java index 852f3a31c8ed..4bf001c6b79d 100644 --- a/test/src/test/java/hudson/tasks/ArtifactArchiverTest.java +++ b/test/src/test/java/hudson/tasks/ArtifactArchiverTest.java @@ -151,7 +151,7 @@ public void testAllowEmptyArchive() throws Exception { ArtifactArchiver aa = new ArtifactArchiver("dir/lodge"); aa.setAllowEmptyArchive(true); p.getPublishersList().add(aa); - FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = j.buildAndAssertSuccess(p); FilePath ws = b.getWorkspace(); assertNotNull(ws); assumeTrue("May not be testable on Windows:\n" + JenkinsRule.getLog(b), ws.child("dir/lodge").exists()); @@ -186,7 +186,7 @@ public void testAllowEmptyArchive() throws Exception { aa.setFollowSymlinks(false); aa.setAllowEmptyArchive(true); p.getPublishersList().add(aa); - FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = j.buildAndAssertSuccess(p); FilePath ws = b.getWorkspace(); assertNotNull(ws); assumeTrue("May not be testable on Windows:\n" + JenkinsRule.getLog(b), ws.child("dir/lodge").exists()); @@ -199,7 +199,7 @@ public void testAllowEmptyArchive() throws Exception { FreeStyleProject p = j.jenkins.getItemByFullName(Functions.isWindows() ? "sample-windows" : "sample", FreeStyleProject.class); - FreeStyleBuild b = p.scheduleBuild2(0).get(); + FreeStyleBuild b = j.buildAndAssertSuccess(p); assumeTrue("May not be testable on Windows:\n" + JenkinsRule.getLog(b),b.getResult()==Result.SUCCESS); FilePath ws = b.getWorkspace(); assertNotNull(ws); @@ -227,7 +227,7 @@ public void testAllowEmptyArchive() throws Exception { } }); p.getPublishersList().add(new ArtifactArchiver("hack", "", false, true)); - FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = j.buildAndAssertSuccess(p); List artifacts = b.getArtifacts(); assertEquals(1, artifacts.size()); FreeStyleBuild.Artifact artifact = artifacts.get(0); @@ -387,9 +387,8 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen p.getPublishersList().add(new ArtifactArchiver(FILENAME)); p.setAssignedNode(slave); - FreeStyleBuild build = p.scheduleBuild2(0).get(); + FreeStyleBuild build = j.buildAndAssertStatus(Result.FAILURE, p); assumeFalse(FILENAME + " should not be readable by " + System.getProperty("user.name"), new File(build.getWorkspace().child(FILENAME).getRemote()).canRead()); - j.assertBuildStatus(Result.FAILURE, build); String expectedPath = build.getWorkspace().child(FILENAME).getRemote(); j.assertLogContains("ERROR: Step ‘Archive the artifacts’ failed: java.nio.file.AccessDeniedException: " + expectedPath, build); assertThat("No stacktrace shown", build.getLog(31), Matchers.iterableWithSize(lessThan(30))); @@ -416,7 +415,7 @@ public void lengthOfArtifactIsCorrect_eventForInvalidSymlink() throws Exception ArtifactArchiver aa = new ArtifactArchiver("dir/**"); aa.setAllowEmptyArchive(true); p.getPublishersList().add(aa); - FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = j.buildAndAssertSuccess(p); FilePath ws = b.getWorkspace(); assertNotNull(ws); List artifacts = b.getArtifacts(); diff --git a/test/src/test/java/hudson/tasks/BatchFileTest.java b/test/src/test/java/hudson/tasks/BatchFileTest.java index 08d5232c9799..2e1535af44ec 100644 --- a/test/src/test/java/hudson/tasks/BatchFileTest.java +++ b/test/src/test/java/hudson/tasks/BatchFileTest.java @@ -67,7 +67,7 @@ private void nonZeroErrorlevelShouldMakeBuildUnstable(int exitCode) throws Excep FreeStyleProject p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewBatchTask("", exitCode)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.UNSTABLE, p.scheduleBuild2(0).get()); + rule.buildAndAssertStatus(Result.UNSTABLE, p); } @Test @@ -87,12 +87,12 @@ private void nonZeroErrorlevelShouldBreakTheBuildByDefault(int exitCode) throws p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewBatchTask("", null)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + rule.buildAndAssertStatus(Result.FAILURE, p); p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewBatchTask("", 0)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + rule.buildAndAssertStatus(Result.FAILURE, p); } @Test @@ -112,7 +112,7 @@ private void nonZeroErrorlevelShouldBreakTheBuildIfNotMatching(int exitCode) thr FreeStyleProject p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewBatchTask("", notMatchingExitCode)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + rule.buildAndAssertStatus(Result.FAILURE, p); } @Test @@ -134,7 +134,7 @@ public void windowsErrorlevel0ShouldNeverMakeTheBuildUnstable() throws Exception FreeStyleProject p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewBatchTask("", unstableReturn)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); + rule.buildAndAssertSuccess(p); } } diff --git a/test/src/test/java/hudson/tasks/BuildTriggerTest.java b/test/src/test/java/hudson/tasks/BuildTriggerTest.java index a7efe039ae7a..b00bdabbc0a6 100644 --- a/test/src/test/java/hudson/tasks/BuildTriggerTest.java +++ b/test/src/test/java/hudson/tasks/BuildTriggerTest.java @@ -103,14 +103,12 @@ private void doTriggerTest(boolean evenWhenUnstable, Result triggerResult, j.jenkins.rebuildDependencyGraph(); // First build should not trigger downstream job - FreeStyleBuild b = p.scheduleBuild2(0).get(); + FreeStyleBuild b = j.buildAndAssertStatus(dontTriggerResult, p); assertNoDownstreamBuild(dp, b); - j.assertBuildStatus(dontTriggerResult, b); // Next build should trigger downstream job p.getBuildersList().replace(new MockBuilder(triggerResult)); - b = p.scheduleBuild2(0).get(); - j.assertBuildStatus(triggerResult, b); + b = j.buildAndAssertStatus(triggerResult, p); j.assertBuildStatusSuccess(j.waitForCompletion(assertDownstreamBuild(dp, b))); } diff --git a/test/src/test/java/hudson/tasks/EnvVarsInConfigTasksTest.java b/test/src/test/java/hudson/tasks/EnvVarsInConfigTasksTest.java index a2b94beae7d7..9b11d096dd7c 100644 --- a/test/src/test/java/hudson/tasks/EnvVarsInConfigTasksTest.java +++ b/test/src/test/java/hudson/tasks/EnvVarsInConfigTasksTest.java @@ -73,17 +73,13 @@ public void testFreeStyleShellOnAgent() throws Exception { // test the regular agent - variable not expanded project.setAssignedLabel(agentRegular.getSelfLabel()); - FreeStyleBuild build = project.scheduleBuild2(0).get(); - - j.assertBuildStatusSuccess(build); + FreeStyleBuild build = j.buildAndAssertSuccess(project); j.assertLogContains(DUMMY_LOCATION_VARNAME, build); // test the agent with prepared environment project.setAssignedLabel(agentEnv.getSelfLabel()); - build = project.scheduleBuild2(0).get(); - - j.assertBuildStatusSuccess(build); + build = j.buildAndAssertSuccess(project); // Check variable was expanded j.assertLogNotContains(DUMMY_LOCATION_VARNAME, build); @@ -103,17 +99,13 @@ public void testFreeStyleMavenOnAgent() throws Exception { // test the regular agent - variable not expanded project.setAssignedLabel(agentRegular.getSelfLabel()); - FreeStyleBuild build = project.scheduleBuild2(0).get(); - - j.assertBuildStatus(Result.FAILURE, build); + FreeStyleBuild build = j.buildAndAssertStatus(Result.FAILURE, project); j.assertLogContains(DUMMY_LOCATION_VARNAME, build); // test the agent with prepared environment project.setAssignedLabel(agentEnv.getSelfLabel()); - build = project.scheduleBuild2(0).get(); - - j.assertBuildStatusSuccess(build); + build = j.buildAndAssertSuccess(project); // Check variable was expanded j.assertLogNotContains(DUMMY_LOCATION_VARNAME, build); diff --git a/test/src/test/java/hudson/tasks/FingerprinterTest.java b/test/src/test/java/hudson/tasks/FingerprinterTest.java index ffa5a087326f..5ca37997667e 100644 --- a/test/src/test/java/hudson/tasks/FingerprinterTest.java +++ b/test/src/test/java/hudson/tasks/FingerprinterTest.java @@ -105,8 +105,8 @@ public static void setUp() { FreeStyleProject upstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles); FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles); - j.assertBuildStatusSuccess(upstream.scheduleBuild2(0).get()); - j.assertBuildStatusSuccess(downstream.scheduleBuild2(0).get()); + j.buildAndAssertSuccess(upstream); + j.buildAndAssertSuccess(downstream); j.jenkins.rebuildDependencyGraph(); @@ -144,9 +144,9 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen FreeStyleProject upstream2 = createFreeStyleProjectWithFingerprints(singleContents2, singleFiles2); FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(doubleContents, doubleFiles); - j.assertBuildStatusSuccess(upstream.scheduleBuild2(0).get()); - j.assertBuildStatusSuccess(upstream2.scheduleBuild2(0).get()); - j.assertBuildStatusSuccess(downstream.scheduleBuild2(0).get()); + j.buildAndAssertSuccess(upstream); + j.buildAndAssertSuccess(upstream2); + j.buildAndAssertSuccess(downstream); j.jenkins.rebuildDependencyGraph(); @@ -167,9 +167,9 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles); FreeStyleProject downstream2 = createFreeStyleProjectWithFingerprints(singleContents2, singleFiles2); - j.assertBuildStatusSuccess(upstream.scheduleBuild2(0).get()); - j.assertBuildStatusSuccess(downstream.scheduleBuild2(0).get()); - j.assertBuildStatusSuccess(downstream2.scheduleBuild2(0).get()); + j.buildAndAssertSuccess(upstream); + j.buildAndAssertSuccess(downstream); + j.buildAndAssertSuccess(downstream2); j.jenkins.rebuildDependencyGraph(); @@ -190,8 +190,8 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen FreeStyleProject upstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles); FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles); - FreeStyleBuild upstreamBuild = j.assertBuildStatusSuccess(upstream.scheduleBuild2(0).get()); - j.assertBuildStatusSuccess(downstream.scheduleBuild2(0).get()); + FreeStyleBuild upstreamBuild = j.buildAndAssertSuccess(upstream); + j.buildAndAssertSuccess(downstream); upstreamBuild.delete(); @@ -207,8 +207,8 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen @Test public void circularDependency() throws Exception { FreeStyleProject p = createFreeStyleProjectWithFingerprints(singleContents, singleFiles); - j.assertBuildStatusSuccess(p.scheduleBuild2(0).get()); - j.assertBuildStatusSuccess(p.scheduleBuild2(0).get()); + j.buildAndAssertSuccess(p); + j.buildAndAssertSuccess(p); Jenkins.get().rebuildDependencyGraph(); @@ -247,8 +247,8 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen FreeStyleProject upstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles); FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles); - FreeStyleBuild upstreamBuild = j.assertBuildStatusSuccess(upstream.scheduleBuild2(0).get()); - FreeStyleBuild downstreamBuild = j.assertBuildStatusSuccess(downstream.scheduleBuild2(0).get()); + FreeStyleBuild upstreamBuild = j.buildAndAssertSuccess(upstream); + FreeStyleBuild downstreamBuild = j.buildAndAssertSuccess(downstream); String oldUpstreamName = upstream.getName(); String oldDownstreamName = downstream.getName(); @@ -314,7 +314,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen } else { assertEquals("{a=2d5fac981a2e865baf0e15db655c7d63}", action.getRecords().toString()); } - j.assertBuildStatusSuccess(job.scheduleBuild2(0)); + j.buildAndAssertSuccess(job); job._getRuns().purgeCache(); // force build records to be reloaded build = job.getBuildByNumber(3); assertNotNull(build); @@ -339,9 +339,9 @@ public void fingerprintCleanup() throws Exception { FreeStyleProject p2 = createFreeStyleProjectWithFingerprints(singleContents, singleFiles2); FreeStyleProject p3 = createFreeStyleProjectWithFingerprints(singleContents, singleFiles); - j.assertBuildStatusSuccess(p1.scheduleBuild2(0)); - j.assertBuildStatusSuccess(p2.scheduleBuild2(0)); - j.assertBuildStatusSuccess(p3.scheduleBuild2(0)); + j.buildAndAssertSuccess(p1); + j.buildAndAssertSuccess(p2); + j.buildAndAssertSuccess(p3); Fingerprint f = j.jenkins._getFingerprint(Util.getDigestOf(singleContents[0]+System.lineSeparator())); assertNotNull(f); @@ -369,7 +369,7 @@ public void fingerprintCleanup() throws Exception { p2.getBuildersList().clear(); p2.getPublishersList().clear(); addFingerprinterToProject(p2,singleContents2,singleFiles2); - j.assertBuildStatusSuccess(p2.scheduleBuild2(0)); + j.buildAndAssertSuccess(p2); // another garbage collection that gets rid of p2 records from the fingerprint p2.getBuildByNumber(1).delete(); diff --git a/test/src/test/java/hudson/tasks/MavenTest.java b/test/src/test/java/hudson/tasks/MavenTest.java index c6395bf72b71..96f15a57bdef 100644 --- a/test/src/test/java/hudson/tasks/MavenTest.java +++ b/test/src/test/java/hudson/tasks/MavenTest.java @@ -34,7 +34,6 @@ import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlPage; import hudson.EnvVars; -import hudson.model.Build; import hudson.model.Cause.LegacyCodeCause; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; @@ -42,7 +41,6 @@ import hudson.model.ParametersAction; import hudson.model.ParametersDefinitionProperty; import hudson.model.PasswordParameterDefinition; -import hudson.model.Result; import hudson.model.StringParameterDefinition; import hudson.model.StringParameterValue; import hudson.slaves.EnvironmentVariablesNodeProperty; @@ -123,10 +121,7 @@ public class MavenTest { project.getBuildersList().add(new Maven("--help", varMaven.getName())); project.setJDK(varJDK); - Build build = project.scheduleBuild2(0).get(); - - assertEquals(Result.SUCCESS, build.getResult()); - + j.buildAndAssertSuccess(project); } @Test public void withParameter() throws Exception { diff --git a/test/src/test/java/hudson/tasks/ShellTest.java b/test/src/test/java/hudson/tasks/ShellTest.java index de058c61075f..251a1f031fbb 100644 --- a/test/src/test/java/hudson/tasks/ShellTest.java +++ b/test/src/test/java/hudson/tasks/ShellTest.java @@ -73,7 +73,7 @@ public Proc onLaunch(ProcStarter p) { p.getBuildersList().add(new Shell("echo abc")); p.setAssignedNode(s); - FreeStyleBuild b = rule.assertBuildStatusSuccess(p.scheduleBuild2(0).get()); + FreeStyleBuild b = rule.buildAndAssertSuccess(p); assertEquals(1,s.numLaunch); assertTrue(IOUtils.toString(b.getLogInputStream(), StandardCharsets.UTF_8).contains("Hudson was here")); @@ -106,7 +106,7 @@ private void nonZeroExitCodeShouldMakeBuildUnstable(int exitCode) throws Excepti FreeStyleProject p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewShell("", exitCode)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.UNSTABLE, p.scheduleBuild2(0).get()); + rule.buildAndAssertStatus(Result.UNSTABLE, p); } @Test @@ -126,12 +126,12 @@ private void nonZeroExitCodeShouldBreakTheBuildByDefault(int exitCode) throws Ex p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewShell("", null)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + rule.buildAndAssertStatus(Result.FAILURE, p); p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewShell("", 0)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + rule.buildAndAssertStatus(Result.FAILURE, p); } @Test @@ -152,7 +152,7 @@ private void nonZeroExitCodeShouldBreakTheBuildIfNotMatching(int exitCode) throw FreeStyleProject p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewShell("", notMatchingExitCode)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + rule.buildAndAssertStatus(Result.FAILURE, p); } @Test @@ -174,7 +174,7 @@ public void unixExitCodes0ShouldNeverMakeTheBuildUnstable() throws Exception { FreeStyleProject p = rule.createFreeStyleProject(); p.getBuildersList().add(createNewShell("", unstableReturn)); p.setAssignedNode(slave); - rule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); + rule.buildAndAssertSuccess(p); } } diff --git a/test/src/test/java/hudson/util/ProcessTreeTest.java b/test/src/test/java/hudson/util/ProcessTreeTest.java index aa187bb45181..7e0a6e668bb9 100644 --- a/test/src/test/java/hudson/util/ProcessTreeTest.java +++ b/test/src/test/java/hudson/util/ProcessTreeTest.java @@ -83,7 +83,7 @@ public void processProperlyKilledUnix() throws Exception { sleepProject.getBuildersList().add(new Shell("nohup sleep 100000 &")); - j.assertBuildStatusSuccess(sleepProject.scheduleBuild2(0).get()); + j.buildAndAssertSuccess(sleepProject); processJob.getBuildersList().add(new Shell("ps -ef | grep sleep")); diff --git a/test/src/test/java/hudson/widgets/HistoryWidgetTest.java b/test/src/test/java/hudson/widgets/HistoryWidgetTest.java index fd28e63f1930..e1658c96949a 100644 --- a/test/src/test/java/hudson/widgets/HistoryWidgetTest.java +++ b/test/src/test/java/hudson/widgets/HistoryWidgetTest.java @@ -16,7 +16,7 @@ public class HistoryWidgetTest { public void moreLink() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); for (int x = 0; x < 3; x++) { - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); } JenkinsRule.WebClient wc = j.createWebClient(); diff --git a/test/src/test/java/jenkins/model/PeepholePermalinkTest.java b/test/src/test/java/jenkins/model/PeepholePermalinkTest.java index df4b9b119154..0273e014fb24 100644 --- a/test/src/test/java/jenkins/model/PeepholePermalinkTest.java +++ b/test/src/test/java/jenkins/model/PeepholePermalinkTest.java @@ -31,7 +31,7 @@ public class PeepholePermalinkTest { @Test public void basics() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); - FreeStyleBuild b1 = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b1 = j.buildAndAssertSuccess(p); String lsb = "lastSuccessfulBuild"; String lfb = "lastFailedBuild"; @@ -53,7 +53,7 @@ public void basics() throws Exception { // one more build and this time it succeeds p.getBuildersList().clear(); - FreeStyleBuild b3 = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b3 = j.buildAndAssertSuccess(p); assertStorage(lsb, p, b3); assertStorage(lfb, p, b2); diff --git a/test/src/test/java/jenkins/security/ClassFilterImplTest.java b/test/src/test/java/jenkins/security/ClassFilterImplTest.java index cb793c0b8c4c..52d044e093ae 100644 --- a/test/src/test/java/jenkins/security/ClassFilterImplTest.java +++ b/test/src/test/java/jenkins/security/ClassFilterImplTest.java @@ -113,7 +113,7 @@ public void agentToControllerRequiresWhitelist() throws Exception { FreeStyleProject p = r.createFreeStyleProject(); p.setAssignedNode(r.createSlave()); p.getBuildersList().add(new S2MBuilder()); - r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0)); + r.buildAndAssertStatus(Result.FAILURE, p); } public static class S2MBuilder extends Builder { @Override diff --git a/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java b/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java index bb469e8bea0b..3278150953a8 100644 --- a/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java +++ b/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java @@ -189,7 +189,7 @@ private static final class DisposerImpl extends Disposer { FreeStyleProject p = r.createFreeStyleProject(); p.setScm(new FailingSCM()); p.getBuildWrappersList().add(new PreCheckoutWrapperWithDisposer()); - FreeStyleBuild b = r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0)); + FreeStyleBuild b = r.buildAndAssertStatus(Result.FAILURE, p); r.assertLogContains("ran DisposerImpl #1", b); r.assertLogNotContains("ran DisposerImpl #2", b); } @@ -221,7 +221,7 @@ public ChangeLogParser createChangeLogParser() { p.getBuildWrappersList().add(new WrapperWithDisposer()); p.getBuildWrappersList().add(new InterruptedDisposerWrapper()); // build is ABORTED because of InterruptedException during tearDown (trumps the FAILURE result) - FreeStyleBuild b = r.assertBuildStatus(Result.ABORTED, p.scheduleBuild2(0)); + FreeStyleBuild b = r.buildAndAssertStatus(Result.ABORTED, p); r.assertLogContains("tearDown InterruptedDisposerImpl", b); r.assertLogContains("ran DisposerImpl", b); // ran despite earlier InterruptedException } diff --git a/test/src/test/java/jenkins/tasks/filters/impl/RetainVariablesLocalRuleTest.java b/test/src/test/java/jenkins/tasks/filters/impl/RetainVariablesLocalRuleTest.java index 14010c6e7c7d..47c12d62cd19 100644 --- a/test/src/test/java/jenkins/tasks/filters/impl/RetainVariablesLocalRuleTest.java +++ b/test/src/test/java/jenkins/tasks/filters/impl/RetainVariablesLocalRuleTest.java @@ -250,7 +250,7 @@ public void retainVariable_removeSystemVariables_shell() throws Exception { Shell shell = new Shell("env"); p.getBuildersList().add(shell); - FreeStyleBuild build = j.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0, (Cause) null)); + FreeStyleBuild build = j.buildAndAssertSuccess(p); List unfilteredLogOutput = build.getLog(200).stream().filter(s -> s.contains("=")).map(s -> s.substring(0, s.indexOf('='))).collect(Collectors.toList()); p.getBuildersList().remove(shell); @@ -264,7 +264,7 @@ public void retainVariable_removeSystemVariables_shell() throws Exception { filteredShell.setConfiguredLocalRules(Collections.singletonList(localRule)); p.getBuildersList().add(filteredShell); - build = j.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0, (Cause) null)); + build = j.buildAndAssertSuccess(p); List filteredLogOutput = build.getLog(200).stream().filter(s -> s.contains("=")).map(s -> s.substring(0, s.indexOf('='))).collect(Collectors.toList()); assertTrue(filteredLogOutput.size() < unfilteredLogOutput.size() - 10); // 10 is a value slightly larger than the number of characteristic env vars (7) diff --git a/test/src/test/java/jenkins/triggers/ReverseBuildTriggerTest.java b/test/src/test/java/jenkins/triggers/ReverseBuildTriggerTest.java index d89b57db0112..1cbfada7afaa 100644 --- a/test/src/test/java/jenkins/triggers/ReverseBuildTriggerTest.java +++ b/test/src/test/java/jenkins/triggers/ReverseBuildTriggerTest.java @@ -206,7 +206,7 @@ public void nullJobInTriggerNotCausesNPE() throws Exception { r.configRoundtrip(downstreamJob2); r.jenkins.rebuildDependencyGraph(); - final FreeStyleBuild build = upstreamJob.scheduleBuild2(0).get(); + final FreeStyleBuild build = r.buildAndAssertSuccess(upstreamJob); r.waitUntilNoActivity(); r.assertLogNotContains("java.lang.NullPointerException", build); diff --git a/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java b/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java index c19572fae3cf..c585e3898327 100644 --- a/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java +++ b/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java @@ -63,7 +63,7 @@ public class BuildTimeTrendTest { @Test public void withAbstractJob_OnBuiltInNode() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); JenkinsRule.WebClient wc = j.createWebClient(); @@ -80,7 +80,7 @@ public void withAbstractJob_OnAgentNode() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.setAssignedNode(agent); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); JenkinsRule.WebClient wc = j.createWebClient(); @@ -99,10 +99,10 @@ public void withAbstractJob_OnBoth() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.setAssignedNode(j.jenkins); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); p.setAssignedNode(agent); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.buildAndAssertSuccess(p); JenkinsRule.WebClient wc = j.createWebClient(); diff --git a/test/src/test/java/lib/form/ApplyButtonTest.java b/test/src/test/java/lib/form/ApplyButtonTest.java index 295f172917ee..3914b68f247b 100644 --- a/test/src/test/java/lib/form/ApplyButtonTest.java +++ b/test/src/test/java/lib/form/ApplyButtonTest.java @@ -27,7 +27,7 @@ public class ApplyButtonTest { public void editDescription() throws Exception { j.jenkins.setMarkupFormatter(RawHtmlMarkupFormatter.INSTANCE); // need something using CodeMirror FreeStyleProject p = j.createFreeStyleProject(); - FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b = j.buildAndAssertSuccess(p); HtmlPage config = j.createWebClient().getPage(b, "configure"); HtmlForm form = config.getFormByName("config"); From 5f955488e458979f9335685c7886f1ea89386b52 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 18 Dec 2021 11:04:49 -0800 Subject: [PATCH 20/58] Bump Stapler from 1593.v0e838714faae to 1612.v2a13b906bf3a (#6066) --- bom/pom.xml | 2 +- cli/pom.xml | 7 +++++++ core/pom.xml | 13 ++++++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index bb8ab745f8fd..507551c89e2a 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -41,7 +41,7 @@ THE SOFTWARE. 9.2 1.7.32 - 1593.v0e838714faae + 1612.v2a13b906bf3a 2.4.21 diff --git a/cli/pom.xml b/cli/pom.xml index 0a296a810e00..59a7354a8527 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -103,6 +103,13 @@ com.github.spotbugs spotbugs-annotations provided + true + + + com.google.code.findbugs + jsr305 + +
      commons-lang diff --git a/core/pom.xml b/core/pom.xml index b85438f1c60b..dc66f3de254d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -127,12 +127,6 @@ THE SOFTWARE. org.kohsuke.stapler stapler - - - com.google.code.findbugs - jsr305 - - org.kohsuke.stapler @@ -517,7 +511,12 @@ THE SOFTWARE. com.github.spotbugs spotbugs-annotations - true + + + com.google.code.findbugs + jsr305 + + net.jcip From 9731799ffc57bac807d419a283d0da1dea98c159 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 18 Dec 2021 11:05:05 -0800 Subject: [PATCH 21/58] Ship JCIP annotations in WAR (#6067) --- core/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index dc66f3de254d..d99b67f23b95 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -521,7 +521,6 @@ THE SOFTWARE. net.jcip jcip-annotations - true From 01fa6a60a4d27ecf7a7b303b9fb39c7c321732d5 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 18 Dec 2021 11:05:16 -0800 Subject: [PATCH 22/58] Skip resource-intensive tests on Windows CI builds (#6069) --- test/src/test/java/hudson/model/FreeStyleProjectTest.java | 3 +++ test/src/test/java/hudson/model/JobTest.java | 2 ++ test/src/test/java/hudson/slaves/NodeProvisionerTest.java | 5 +++++ test/src/test/java/hudson/slaves/SlaveComputerTest.java | 3 +++ test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java | 7 +++++++ 5 files changed, 20 insertions(+) diff --git a/test/src/test/java/hudson/model/FreeStyleProjectTest.java b/test/src/test/java/hudson/model/FreeStyleProjectTest.java index b7acb7f1ad6e..2e6e46bd40ac 100644 --- a/test/src/test/java/hudson/model/FreeStyleProjectTest.java +++ b/test/src/test/java/hudson/model/FreeStyleProjectTest.java @@ -33,6 +33,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.HttpMethod; @@ -40,6 +41,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlPage; import hudson.ExtensionList; +import hudson.Functions; import hudson.diagnosis.OldDataMonitor; import hudson.tasks.Builder; import hudson.tasks.Shell; @@ -146,6 +148,7 @@ public void minimalConfigXml() throws Exception { @Test @Issue("JENKINS-36629") public void buildStabilityReports() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); for (int i = 0; i <= 32; i++) { FreeStyleProject p = j.createFreeStyleProject(String.format("Pattern-%s", Integer.toBinaryString(i))); int expectedFails = 0; diff --git a/test/src/test/java/hudson/model/JobTest.java b/test/src/test/java/hudson/model/JobTest.java index cb7ab86c9789..6cb040df07c2 100644 --- a/test/src/test/java/hudson/model/JobTest.java +++ b/test/src/test/java/hudson/model/JobTest.java @@ -493,6 +493,7 @@ public void testRenameSpaceInBetween() throws Exception { @Issue("JENKINS-35160") @Test public void interruptOnDelete() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); j.jenkins.setNumExecutors(2); Queue.getInstance().maintain(); final FreeStyleProject p = j.createFreeStyleProject(); @@ -514,6 +515,7 @@ public void interruptOnDelete() throws Exception { @Issue("SECURITY-1868") @Test public void noXssPossible() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); String desiredNodeName = "agent is a better name2 "; String initialNodeName = "agent is a better name"; diff --git a/test/src/test/java/hudson/slaves/NodeProvisionerTest.java b/test/src/test/java/hudson/slaves/NodeProvisionerTest.java index 5e3b900451fa..6d2c89daa934 100644 --- a/test/src/test/java/hudson/slaves/NodeProvisionerTest.java +++ b/test/src/test/java/hudson/slaves/NodeProvisionerTest.java @@ -25,8 +25,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeFalse; import hudson.BulkChange; +import hudson.Functions; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.BuildListener; @@ -113,6 +115,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen */ // TODO fragile @Test public void loadSpike() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); try (BulkChange bc = new BulkChange(r.jenkins)) { DummyCloudImpl cloud = initHudson(0); @@ -129,6 +132,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen */ // TODO fragile @Test public void baselineSlaveUsage() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); try (BulkChange bc = new BulkChange(r.jenkins)) { DummyCloudImpl cloud = initHudson(0); // add agents statically upfront @@ -147,6 +151,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen */ // TODO fragile @Test public void labels() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); try (BulkChange bc = new BulkChange(r.jenkins)) { DummyCloudImpl cloud = initHudson(0); Label blue = r.jenkins.getLabel("blue"); diff --git a/test/src/test/java/hudson/slaves/SlaveComputerTest.java b/test/src/test/java/hudson/slaves/SlaveComputerTest.java index 1db74056ce1e..b03965fbd8aa 100644 --- a/test/src/test/java/hudson/slaves/SlaveComputerTest.java +++ b/test/src/test/java/hudson/slaves/SlaveComputerTest.java @@ -26,8 +26,10 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; +import static org.junit.Assume.assumeFalse; import com.gargoylesoftware.htmlunit.WebResponse; +import hudson.Functions; import hudson.model.Computer; import hudson.model.Node; import hudson.model.TaskListener; @@ -145,6 +147,7 @@ public void onOnline(Computer c, TaskListener listener) { @Issue("JENKINS-57111") public void startupShouldFailOnErrorOnlineListener() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); DumbSlave nodeA = j.createSlave(); Assert.assertTrue(nodeA.getComputer() instanceof SlaveComputer); int retries = 10; diff --git a/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java b/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java index c585e3898327..ac1c3e2579b6 100644 --- a/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java +++ b/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java @@ -27,12 +27,14 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import com.gargoylesoftware.htmlunit.html.DomNode; import com.gargoylesoftware.htmlunit.html.DomNodeList; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlTable; import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.Functions; import hudson.XmlFile; import hudson.model.FreeStyleProject; import hudson.model.ItemGroup; @@ -62,6 +64,7 @@ public class BuildTimeTrendTest { @Test public void withAbstractJob_OnBuiltInNode() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); FreeStyleProject p = j.createFreeStyleProject(); j.buildAndAssertSuccess(p); @@ -76,6 +79,7 @@ public void withAbstractJob_OnBuiltInNode() throws Exception { @Test public void withAbstractJob_OnAgentNode() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); DumbSlave agent = j.createSlave(); FreeStyleProject p = j.createFreeStyleProject(); p.setAssignedNode(agent); @@ -95,6 +99,7 @@ public void withAbstractJob_OnAgentNode() throws Exception { @Test public void withAbstractJob_OnBoth() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); DumbSlave agent = j.createSlave(); FreeStyleProject p = j.createFreeStyleProject(); @@ -128,6 +133,7 @@ public void withAbstractJob_OnBoth() throws Exception { @Test @LocalData("localDataNonAbstractJob") public void withNonAbstractJob_withoutAgents() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); JenkinsRule.WebClient wc = j.createWebClient(); TopLevelItem p = j.jenkins.getItem("job0"); assertThat(p, instanceOf(NonAbstractJob.class)); @@ -147,6 +153,7 @@ public void withNonAbstractJob_withoutAgents() throws Exception { @LocalData("localDataNonAbstractJob") @Issue("JENKINS-63232") public void withNonAbstractJob_withAgents() throws Exception { + assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null); // just to trigger data-is-distributed-build-enabled = true j.createSlave(); From f37d3690884cb0aa9c7fa11c14235386570db5fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Dec 2021 11:05:30 -0800 Subject: [PATCH 23/58] Bump spotless-maven-plugin from 2.17.6 to 2.17.7 (#6071) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 77bae95a471a..9f50be37fa7a 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ THE SOFTWARE. 1.22 5.8.2 4.2.0 - 2.17.6 + 2.17.7 org.jenkins-ci.plugins script-security - 1.78 + 1118.vba21ca2e3286 From 1f6ff272274d2293224f8386018f32485c1c78d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Dec 2021 07:59:15 -0800 Subject: [PATCH 34/58] Bump bridge-method-injector.version from 1.22 to 1.23 (#6087) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9f50be37fa7a..a5b64b3b972e 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ THE SOFTWARE. ${project.basedir}/../src/spotbugs/spotbugs-excludes.xml 1.27 - 1.22 + 1.23 5.8.2 4.2.0 2.17.7 From 18009fbee77b97d286b1304d26e95cec442274d8 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Mon, 20 Dec 2021 07:59:41 -0800 Subject: [PATCH 35/58] Switch ATH from Java 8 to Java 11 (#6086) --- Jenkinsfile | 4 +--- essentials.yml | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4cd19525f6e9..4aeb3fd1487b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -109,8 +109,6 @@ for(j = 0; j < jdks.size(); j++) { } }} -// TODO: Restore ATH once https://groups.google.com/forum/#!topic/jenkinsci-dev/v9d-XosOp2s is resolved -// TODO: ATH flow now supports Java 8 only, it needs to be reworked (INFRA-1690) builds.ath = { node("docker-highmem") { // Just to be safe @@ -120,7 +118,7 @@ builds.ath = { dir("sources") { checkout scm def mvnCmd = 'mvn --batch-mode --show-version -ntp -Pquick-build -am -pl war package -Dmaven.repo.local=$WORKSPACE_TMP/m2repo' - infra.runWithMaven(mvnCmd, "8", javaOpts, true) + infra.runWithMaven(mvnCmd, "11", javaOpts, true) dir("war/target") { fileUri = "file://" + pwd() + "/jenkins.war" } diff --git a/essentials.yml b/essentials.yml index 099746b2b3f7..df1f6639ee6c 100644 --- a/essentials.yml +++ b/essentials.yml @@ -5,3 +5,5 @@ ath: athImage: "jenkins/ath:1.97-pre" categories: - org.jenkinsci.test.acceptance.junit.SmokeTest + jdks: + - 11 From cc47699ca0aa9da004011c8d5941ecf827c12bf5 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Mon, 20 Dec 2021 07:59:54 -0800 Subject: [PATCH 36/58] Miscellaneous test cleanup (#6063) --- core/src/test/java/hudson/UtilTest.java | 1 + test/src/test/java/hudson/cli/CLITest.java | 3 +- .../java/hudson/cli/ConsoleCommandTest.java | 2 +- test/src/test/java/hudson/model/JobTest.java | 6 +- .../src/test/java/hudson/model/QueueTest.java | 13 ++--- .../test/java/hudson/model/RunMapTest.java | 10 ++-- .../hudson/slaves/NodeCanTakeTaskTest.java | 8 +-- .../hudson/tasks/ArtifactArchiverTest.java | 2 +- .../jenkins/cli/StopBuildsCommandTest.java | 58 +++++++++---------- 9 files changed, 50 insertions(+), 53 deletions(-) diff --git a/core/src/test/java/hudson/UtilTest.java b/core/src/test/java/hudson/UtilTest.java index 37b1cf49247a..253b7b7a326d 100644 --- a/core/src/test/java/hudson/UtilTest.java +++ b/core/src/test/java/hudson/UtilTest.java @@ -667,6 +667,7 @@ public void createDirectories() throws Exception { @Test @Issue("JENKINS-67372") public void createDirectoriesInRoot() throws Exception { + assumeFalse(Functions.isWindows()); Path newDirInRoot = Paths.get("/new-dir-in-root"); Path newSymlinkInRoot = Paths.get("/new-symlink-in-root"); try { diff --git a/test/src/test/java/hudson/cli/CLITest.java b/test/src/test/java/hudson/cli/CLITest.java index c84e39c9a986..0cfecbda0d19 100644 --- a/test/src/test/java/hudson/cli/CLITest.java +++ b/test/src/test/java/hudson/cli/CLITest.java @@ -46,7 +46,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.TimeUnit; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -97,7 +96,7 @@ public void interrupt() throws Exception { r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin")); FreeStyleProject p = r.createFreeStyleProject("p"); - p.getBuildersList().add(new SleepBuilder(TimeUnit.MINUTES.toMillis(5))); + p.getBuildersList().add(new SleepBuilder(Long.MAX_VALUE)); doInterrupt(p, "-http", "-auth", "admin:admin"); doInterrupt(p, "-webSocket", "-auth", "admin:admin"); } diff --git a/test/src/test/java/hudson/cli/ConsoleCommandTest.java b/test/src/test/java/hudson/cli/ConsoleCommandTest.java index b30aaa68b5fe..0fe619623ebd 100644 --- a/test/src/test/java/hudson/cli/ConsoleCommandTest.java +++ b/test/src/test/java/hudson/cli/ConsoleCommandTest.java @@ -143,7 +143,7 @@ public class ConsoleCommandTest { assertThat(result, hasNoStandardOutput()); assertThat(result.stderr(), containsString("ERROR: Not sure what you meant by \"1a\"")); - project.getBuildersList().add(new Shell("echo 1")); + project.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo 1") : new Shell("echo 1")); j.buildAndAssertSuccess(project); result = command diff --git a/test/src/test/java/hudson/model/JobTest.java b/test/src/test/java/hudson/model/JobTest.java index 6cb040df07c2..926e8c85f479 100644 --- a/test/src/test/java/hudson/model/JobTest.java +++ b/test/src/test/java/hudson/model/JobTest.java @@ -53,8 +53,8 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; @@ -500,7 +500,7 @@ public void interruptOnDelete() throws Exception { p.addProperty(new ParametersDefinitionProperty( new StringParameterDefinition("dummy", "0"))); p.setConcurrentBuild(true); - p.getBuildersList().add(new SleepBuilder(30000)); // we want the uninterrupted job to run for long time + p.getBuildersList().add(new SleepBuilder(Long.MAX_VALUE)); // we want the uninterrupted job to run for long time FreeStyleBuild build1 = p.scheduleBuild2(0).getStartCondition().get(); FreeStyleBuild build2 = p.scheduleBuild2(0).getStartCondition().get(); QueueTaskFuture build3 = p.scheduleBuild2(0); @@ -552,7 +552,7 @@ static class NameChangingNode extends Slave { private String virtualName; NameChangingNode(JenkinsRule j, String name) throws Exception { - super(name, "dummy", j.createTmpDir().getPath(), "1", Node.Mode.NORMAL, "", j.createComputerLauncher(null), RetentionStrategy.NOOP, new ArrayList<>()); + super(name, "dummy", j.createTmpDir().getPath(), "1", Node.Mode.NORMAL, "", j.createComputerLauncher(null), RetentionStrategy.NOOP, Collections.emptyList()); } public void setVirtualName(String virtualName) { diff --git a/test/src/test/java/hudson/model/QueueTest.java b/test/src/test/java/hudson/model/QueueTest.java index 01ce831a6398..78a7137328ec 100644 --- a/test/src/test/java/hudson/model/QueueTest.java +++ b/test/src/test/java/hudson/model/QueueTest.java @@ -399,14 +399,13 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen // View for build should group duplicates JenkinsRule.WebClient wc = r.createWebClient(); - String nl = System.getProperty("line.separator"); - String buildPage = wc.getPage(build, "").asNormalizedText().replace(nl," "); + String buildPage = wc.getPage(build, "").asNormalizedText(); assertTrue("Build page should combine duplicates and show counts: " + buildPage, - buildPage.contains("Started by user SYSTEM (2 times) " - + "Started by an SCM change (3 times) " - + "Started by timer (2 times) " - + "Started by remote host 1.2.3.4 with note: test (2 times) " - + "Started by remote host 4.3.2.1 with note: test " + buildPage.contains("Started by user SYSTEM (2 times)\n" + + "Started by an SCM change (3 times)\n" + + "Started by timer (2 times)\n" + + "Started by remote host 1.2.3.4 with note: test (2 times)\n" + + "Started by remote host 4.3.2.1 with note: test\n" + "Started by remote host 1.2.3.4 with note: foo")); System.out.println(new XmlFile(new File(build.getRootDir(), "build.xml")).asString()); } diff --git a/test/src/test/java/hudson/model/RunMapTest.java b/test/src/test/java/hudson/model/RunMapTest.java index bf8487b9456d..979453dc110f 100644 --- a/test/src/test/java/hudson/model/RunMapTest.java +++ b/test/src/test/java/hudson/model/RunMapTest.java @@ -18,19 +18,21 @@ import java.util.SortedMap; import java.util.Spliterator; import java.util.TreeMap; +import java.util.logging.Level; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; 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.LoggerRule; import org.jvnet.hudson.test.RunLoadCounter; import org.jvnet.hudson.test.SleepBuilder; public class RunMapTest { @Rule public JenkinsRule r = new JenkinsRule(); - // TODO https://github.com/jenkinsci/jenkins/pull/2438: @Rule public LoggerRule logs = new LoggerRule(); + @Rule public LoggerRule logs = new LoggerRule(); /** * Makes sure that reloading the project while a build is in progress won't clobber that in-progress build. @@ -43,7 +45,7 @@ public class RunMapTest { FreeStyleBuild b1 = r.buildAndAssertSuccess(p); // now create a build that hangs until we signal the OneShotEvent - p.getBuildersList().add(new SleepBuilder(9999999)); + p.getBuildersList().add(new SleepBuilder(Long.MAX_VALUE)); FreeStyleBuild b2 = p.scheduleBuild2(0).waitForStart(); assertEquals(2, b2.number); @@ -65,9 +67,9 @@ public class RunMapTest { @Issue("JENKINS-27530") @Test public void reloadWhileBuildIsInQueue() throws Exception { - //logs.record(Queue.class, Level.FINE); + logs.record(Queue.class, Level.FINE); FreeStyleProject p = r.createFreeStyleProject("p"); - p.getBuildersList().add(new SleepBuilder(9999999)); + p.getBuildersList().add(new SleepBuilder(Long.MAX_VALUE)); r.jenkins.setNumExecutors(1); assertEquals(1, p.scheduleBuild2(0).waitForStart().number); p.scheduleBuild2(0); diff --git a/test/src/test/java/hudson/slaves/NodeCanTakeTaskTest.java b/test/src/test/java/hudson/slaves/NodeCanTakeTaskTest.java index c2dd0d109f2e..4435ee664a8e 100644 --- a/test/src/test/java/hudson/slaves/NodeCanTakeTaskTest.java +++ b/test/src/test/java/hudson/slaves/NodeCanTakeTaskTest.java @@ -37,7 +37,6 @@ import hudson.model.Slave; import hudson.model.queue.CauseOfBlockage; import java.util.List; -import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.Rule; @@ -60,8 +59,7 @@ public void takeBlockedByProperty() throws Exception { FreeStyleProject project = r.createFreeStyleProject(); // First, attempt to run our project before adding the property - Future build = project.scheduleBuild2(0); - r.assertBuildStatus(Result.SUCCESS, build.get(20, TimeUnit.SECONDS)); + r.buildAndAssertSuccess(project); // Add the build-blocker property and try again slave.getNodeProperties().add(new RejectAllTasksProperty()); @@ -96,13 +94,15 @@ public void becauseNodeIsBusy() throws Exception { project.setAssignedNode(slave); project.setConcurrentBuild(true); project.getBuildersList().add(new SleepBuilder(Long.MAX_VALUE)); - project.scheduleBuild2(0).waitForStart(); // consume the one executor + FreeStyleBuild build = project.scheduleBuild2(0).waitForStart(); // consume the one executor project.scheduleBuild2(0); // now try to reschedule Queue.Item item; while ((item = r.jenkins.getQueue().getItem(project)) == null || !item.isBuildable()) { Thread.sleep(100); } assertEquals(hudson.model.Messages.Queue_WaitingForNextAvailableExecutorOn(slave.getDisplayName()), item.getWhy()); + build.doStop(); + r.assertBuildStatus(Result.ABORTED, r.waitForCompletion(build)); } } diff --git a/test/src/test/java/hudson/tasks/ArtifactArchiverTest.java b/test/src/test/java/hudson/tasks/ArtifactArchiverTest.java index 4bf001c6b79d..38b92e034b83 100644 --- a/test/src/test/java/hudson/tasks/ArtifactArchiverTest.java +++ b/test/src/test/java/hudson/tasks/ArtifactArchiverTest.java @@ -199,7 +199,7 @@ public void testAllowEmptyArchive() throws Exception { FreeStyleProject p = j.jenkins.getItemByFullName(Functions.isWindows() ? "sample-windows" : "sample", FreeStyleProject.class); - FreeStyleBuild b = j.buildAndAssertSuccess(p); + FreeStyleBuild b = p.scheduleBuild2(0).get(); assumeTrue("May not be testable on Windows:\n" + JenkinsRule.getLog(b),b.getResult()==Result.SUCCESS); FilePath ws = b.getWorkspace(); assertNotNull(ws); diff --git a/test/src/test/java/jenkins/cli/StopBuildsCommandTest.java b/test/src/test/java/jenkins/cli/StopBuildsCommandTest.java index 7de846f7bd3f..7eb00dc025de 100644 --- a/test/src/test/java/jenkins/cli/StopBuildsCommandTest.java +++ b/test/src/test/java/jenkins/cli/StopBuildsCommandTest.java @@ -27,23 +27,22 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; -import hudson.Functions; import hudson.cli.CLICommand; import hudson.cli.CLICommandInvoker; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import hudson.model.Item; -import hudson.tasks.BatchFile; -import hudson.tasks.Builder; -import hudson.tasks.Shell; +import hudson.model.Result; import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; import jenkins.model.Jenkins; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.MockAuthorizationStrategy; +import org.jvnet.hudson.test.SleepBuilder; public class StopBuildsCommandTest { @@ -57,20 +56,20 @@ public class StopBuildsCommandTest { @Test public void shouldStopLastBuild() throws Exception { final FreeStyleProject project = createLongRunningProject(TEST_JOB_NAME); - project.scheduleBuild2(0).waitForStart(); + FreeStyleBuild build = project.scheduleBuild2(0).waitForStart(); + j.waitForMessage("Sleeping", build); final String stdout = runWith(Collections.singletonList(TEST_JOB_NAME)).stdout(); assertThat(stdout, equalTo("Build '#1' stopped for job 'jobName'" + LN)); - waitForLastBuildToStop(project); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(build)); } @Test public void shouldNotStopEndedBuild() throws Exception { final FreeStyleProject project = j.createFreeStyleProject(TEST_JOB_NAME); - project.getBuildersList().add(createScriptBuilder("echo 1")); - project.scheduleBuild2(0).waitForStart(); - waitForLastBuildToStop(project); + project.getBuildersList().add(new SleepBuilder(TimeUnit.SECONDS.toMillis(1))); + j.buildAndAssertSuccess(project); final String out = runWith(Collections.singletonList(TEST_JOB_NAME)).stdout(); @@ -82,14 +81,17 @@ public void shouldStopSeveralWorkingBuilds() throws Exception { final FreeStyleProject project = createLongRunningProject(TEST_JOB_NAME); project.setConcurrentBuild(true); - project.scheduleBuild2(0).waitForStart(); - project.scheduleBuild2(0).waitForStart(); + FreeStyleBuild b1 = project.scheduleBuild2(0).waitForStart(); + j.waitForMessage("Sleeping", b1); + FreeStyleBuild b2 = project.scheduleBuild2(0).waitForStart(); + j.waitForMessage("Sleeping", b2); final String stdout = runWith(Collections.singletonList(TEST_JOB_NAME)).stdout(); assertThat(stdout, equalTo("Build '#2' stopped for job 'jobName'" + LN + "Build '#1' stopped for job 'jobName'" + LN)); - waitForLastBuildToStop(project); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(b1)); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(b2)); } @Test @@ -131,6 +133,7 @@ public void shouldReportBuildStopError() throws Exception { grant(Item.READ).onItems(project).toEveryone(). grant(Item.CANCEL).onItems(project).toAuthenticated()); FreeStyleBuild build = project.scheduleBuild2(0).waitForStart(); + j.waitForMessage("Sleeping", build); final String stdout = runWith(Collections.singletonList(TEST_JOB_NAME)).stdout(); @@ -140,7 +143,7 @@ public void shouldReportBuildStopError() throws Exception { "No builds stopped" + LN)); build.doStop(); - j.waitForCompletion(build); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(build)); } @Test @@ -157,7 +160,9 @@ public void shouldStopSecondJobEvenIfFirstStopFailed() throws Exception { grant(Item.CANCEL).onItems(project).toEveryone()); FreeStyleBuild b1 = restrictedProject.scheduleBuild2(0).waitForStart(); + j.waitForMessage("Sleeping", b1); FreeStyleBuild b2 = project.scheduleBuild2(0).waitForStart(); + j.waitForMessage("Sleeping", b2); final String stdout = runWith(asList(TEST_JOB_NAME, TEST_JOB_NAME_2)).stdout(); @@ -168,8 +173,8 @@ public void shouldStopSecondJobEvenIfFirstStopFailed() throws Exception { b1.doStop(); b2.doStop(); - j.waitForCompletion(b1); - j.waitForCompletion(b2); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(b1)); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(b2)); } private CLICommandInvoker.Result runWith(final List jobNames) throws Exception { @@ -182,32 +187,23 @@ private void setupAndAssertTwoBuildsStop(final List inputNames) throws E final FreeStyleProject project = createLongRunningProject(TEST_JOB_NAME); final FreeStyleProject project2 = createLongRunningProject(TEST_JOB_NAME_2); - project.scheduleBuild2(0).waitForStart(); - project2.scheduleBuild2(0).waitForStart(); + FreeStyleBuild b1 = project.scheduleBuild2(0).waitForStart(); + j.waitForMessage("Sleeping", b1); + FreeStyleBuild b2 = project2.scheduleBuild2(0).waitForStart(); + j.waitForMessage("Sleeping", b2); final String stdout = runWith(inputNames).stdout(); assertThat(stdout, equalTo("Build '#1' stopped for job 'jobName'" + LN + "Build '#1' stopped for job 'jobName2'" + LN)); - waitForLastBuildToStop(project); - waitForLastBuildToStop(project2); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(b1)); + j.assertBuildStatus(Result.ABORTED, j.waitForCompletion(b2)); } private FreeStyleProject createLongRunningProject(final String jobName) throws IOException { final FreeStyleProject project = j.createFreeStyleProject(jobName); - project.getBuildersList().add(createScriptBuilder("sleep 50000")); + project.getBuildersList().add(new SleepBuilder(Long.MAX_VALUE)); return project; } - - private Builder createScriptBuilder(String script) { - return Functions.isWindows() ? new BatchFile(script) : new Shell(script); - } - - private void waitForLastBuildToStop(final FreeStyleProject project) throws InterruptedException { - while (project.getLastBuild().isBuilding()) { - Thread.sleep(500); - } - assertThat(project.getLastBuild().isBuilding(), equalTo(false)); - } } From b81428f41094265b5750e520ce44006b35168462 Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Mon, 20 Dec 2021 17:00:51 +0100 Subject: [PATCH 37/58] [JENKINS-67173] Remove Callable allowlist and FilePath agent-to-controller support (#5885) Co-authored-by: Daniel Beck --- core/src/main/java/hudson/FilePath.java | 325 ++----- .../src/main/java/jenkins/FilePathFilter.java | 132 --- .../jenkins/FilePathFilterAggregator.java | 142 --- .../jenkins/ReflectiveFilePathFilter.java | 53 -- .../main/java/jenkins/SoloFilePathFilter.java | 103 --- .../java/jenkins/install/SetupWizard.java | 7 +- .../security/s2m/AdminCallableMonitor.java | 71 -- .../security/s2m/AdminCallableWhitelist.java | 31 - .../security/s2m/AdminFilePathFilter.java | 50 - .../security/s2m/AdminWhitelistRule.java | 232 +---- .../s2m/CallableDirectionChecker.java | 41 +- .../security/s2m/CallableRejectionConfig.java | 78 -- .../security/s2m/CallableWhitelist.java | 47 - .../security/s2m/CallableWhitelistConfig.java | 38 - .../jenkins/security/s2m/ConfigDirectory.java | 58 -- .../java/jenkins/security/s2m/ConfigFile.java | 122 --- .../security/s2m/DefaultFilePathFilter.java | 84 -- .../jenkins/security/s2m/FilePathRule.java | 21 - .../security/s2m/FilePathRuleConfig.java | 121 --- .../s2m/MasterKillSwitchConfiguration.java | 70 -- .../security/s2m/MasterKillSwitchWarning.java | 50 - .../java/jenkins/security/s2m/OpMatcher.java | 15 - .../security/s2m/RejectedCallable.java | 21 - .../s2m/RunningBuildFilePathFilter.java | 152 --- .../jenkins/security/s2m/package-info.java | 2 +- .../impl/SlaveToMasterFileCallableUsage.java | 86 -- .../s2m/AdminCallableMonitor/message.jelly | 34 - .../AdminCallableMonitor/message.properties | 2 - .../message_bg.properties | 32 - .../message_de.properties | 26 - .../message_it.properties | 29 - .../message_pt_BR.properties | 24 - .../message_sr.properties | 6 - .../s2m/AdminWhitelistRule/index.jelly | 113 --- .../AdminWhitelistRule/index_bg.properties | 28 - .../AdminWhitelistRule/index_de.properties | 25 - .../AdminWhitelistRule/index_it.properties | 26 - .../AdminWhitelistRule/index_ja.properties | 24 - .../AdminWhitelistRule/index_pt_BR.properties | 24 - .../AdminWhitelistRule/index_sr.properties | 5 - .../config.groovy | 13 - .../config_it.properties | 29 - .../help-agentToControllerAccessControl.html | 4 - ...elp-agentToControllerAccessControl_bg.html | 4 - ...elp-agentToControllerAccessControl_it.html | 4 - .../s2m/MasterKillSwitchWarning/message.jelly | 34 - .../message.properties | 2 - .../message_bg.properties | 32 - .../message_de.properties | 26 - .../message_it.properties | 29 - .../message_pt_BR.properties | 26 - .../message_sr.properties | 6 - .../message_tr.properties | 24 - .../jenkins/security/s2m/Messages.properties | 2 - .../security/s2m/Messages_bg.properties | 28 - .../security/s2m/Messages_de.properties | 24 - .../security/s2m/Messages_it.properties | 27 - .../jenkins/security/s2m/callable.conf | 29 - .../jenkins/security/s2m/filepath-filter.conf | 49 - .../description.jelly | 17 - .../SlaveToMasterFileCallableUsageTest.java | 37 - .../AgentToControllerSecurityTest.java | 211 +++++ .../jenkins/security/FilePathSecureTest.java | 1 + .../jenkins/security/Security2455Test.java | 865 ------------------ .../security/s2m/AdminFilePathFilterTest.java | 235 ----- .../s2m/DefaultFilePathFilterTest.java | 108 --- .../s2m/RunningBuildFilePathFilterTest.java | 146 --- 67 files changed, 336 insertions(+), 4226 deletions(-) delete mode 100644 core/src/main/java/jenkins/FilePathFilter.java delete mode 100644 core/src/main/java/jenkins/FilePathFilterAggregator.java delete mode 100644 core/src/main/java/jenkins/ReflectiveFilePathFilter.java delete mode 100644 core/src/main/java/jenkins/SoloFilePathFilter.java delete mode 100644 core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java delete mode 100644 core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java delete mode 100644 core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java delete mode 100644 core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java delete mode 100644 core/src/main/java/jenkins/security/s2m/CallableWhitelist.java delete mode 100644 core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java delete mode 100644 core/src/main/java/jenkins/security/s2m/ConfigDirectory.java delete mode 100644 core/src/main/java/jenkins/security/s2m/ConfigFile.java delete mode 100644 core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java delete mode 100644 core/src/main/java/jenkins/security/s2m/FilePathRule.java delete mode 100644 core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java delete mode 100644 core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java delete mode 100644 core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java delete mode 100644 core/src/main/java/jenkins/security/s2m/OpMatcher.java delete mode 100644 core/src/main/java/jenkins/security/s2m/RejectedCallable.java delete mode 100644 core/src/main/java/jenkins/security/s2m/RunningBuildFilePathFilter.java delete mode 100644 core/src/main/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage.java delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.jelly delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_bg.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_de.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_it.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_pt_BR.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_sr.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_bg.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_de.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_it.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_ja.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_pt_BR.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_sr.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config_it.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl.html delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_bg.html delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_it.html delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.jelly delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_bg.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_de.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_it.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_pt_BR.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_sr.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_tr.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/Messages.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/Messages_bg.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/Messages_de.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/Messages_it.properties delete mode 100644 core/src/main/resources/jenkins/security/s2m/callable.conf delete mode 100644 core/src/main/resources/jenkins/security/s2m/filepath-filter.conf delete mode 100644 core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly delete mode 100644 core/src/test/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsageTest.java create mode 100644 test/src/test/java/jenkins/security/AgentToControllerSecurityTest.java delete mode 100644 test/src/test/java/jenkins/security/Security2455Test.java delete mode 100644 test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java delete mode 100644 test/src/test/java/jenkins/security/s2m/DefaultFilePathFilterTest.java delete mode 100644 test/src/test/java/jenkins/security/s2m/RunningBuildFilePathFilterTest.java diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 5b79710b25f3..16e060f9d23c 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -118,10 +118,8 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; -import jenkins.FilePathFilter; import jenkins.MasterToSlaveFileCallable; import jenkins.SlaveToMasterFileCallable; -import jenkins.SoloFilePathFilter; import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; import jenkins.util.ContextResettingExecutorService; @@ -515,7 +513,7 @@ public int archive(final ArchiverFactory factory, OutputStream os, final DirScan final OutputStream out = channel != null ? new RemoteOutputStream(os) : os; return act(new Archive(factory, out, scanner, verificationRoot, noFollowLinks)); } - private static class Archive extends SecureFileCallable { + private static class Archive extends MasterToSlaveFileCallable { private final ArchiverFactory factory; private final OutputStream out; private final DirScanner scanner; @@ -533,7 +531,7 @@ private static class Archive extends SecureFileCallable { public Integer invoke(File f, VirtualChannel channel) throws IOException { Archiver a = factory.create(out); try { - scanner.scan(f, ignoringSymlinks(reading(a), verificationRoot, noFollowLinks)); + scanner.scan(f, ignoringSymlinks(a, verificationRoot, noFollowLinks)); } finally { a.close(); } @@ -568,7 +566,7 @@ public void unzip(final FilePath target) throws IOException, InterruptedExceptio target.act(new UnzipLocal(this)); } } - private static class UnzipRemote extends SecureFileCallable { + private static class UnzipRemote extends MasterToSlaveFileCallable { private final RemoteInputStream in; UnzipRemote(RemoteInputStream in) { this.in = in; @@ -580,7 +578,7 @@ public Void invoke(File dir, VirtualChannel channel) throws IOException, Interru } private static final long serialVersionUID = 1L; } - private static class UnzipLocal extends SecureFileCallable { + private static class UnzipLocal extends MasterToSlaveFileCallable { private final FilePath filePath; @@ -593,7 +591,7 @@ public Void invoke(File dir, VirtualChannel channel) throws IOException, Interru if (this.filePath.isRemote()) { throw new IllegalStateException("Expected local path for file: " + filePath); // this.channel==target.channel above } - unzip(dir, reading(new File(this.filePath.getRemote()))); // shortcut to local file + unzip(dir, new File(this.filePath.getRemote())); // shortcut to local file return null; } private static final long serialVersionUID = 1L; @@ -619,7 +617,7 @@ public void untar(final FilePath target, final TarCompression compression) throw target.act(new UntarLocal(source, compression)); } } - private static class UntarRemote extends SecureFileCallable { + private static class UntarRemote extends MasterToSlaveFileCallable { private final TarCompression compression; private final RemoteInputStream in; private final String name; @@ -635,7 +633,7 @@ public Void invoke(File dir, VirtualChannel channel) throws IOException, Interru } private static final long serialVersionUID = 1L; } - private static class UntarLocal extends SecureFileCallable { + private static class UntarLocal extends MasterToSlaveFileCallable { private final TarCompression compression; private final FilePath filePath; @@ -663,7 +661,7 @@ public void unzipFrom(InputStream _in) throws IOException, InterruptedException final InputStream in = new RemoteInputStream(_in, Flag.GREEDY); act(new UnzipFrom(in)); } - private static class UnzipFrom extends SecureFileCallable { + private static class UnzipFrom extends MasterToSlaveFileCallable { private final InputStream in; UnzipFrom(InputStream in) { this.in = in; @@ -709,7 +707,7 @@ private static void unzip(File dir, File zipFile) throws IOException { mkdirs(p); } try (InputStream input = zip.getInputStream(e)) { - IOUtils.copy(input, writing(f)); + IOUtils.copy(input, f); } try { FilePath target = new FilePath(f); @@ -733,11 +731,11 @@ private static void unzip(File dir, File zipFile) throws IOException { public FilePath absolutize() throws IOException, InterruptedException { return new FilePath(channel, act(new Absolutize())); } - private static class Absolutize extends SecureFileCallable { + private static class Absolutize extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public String invoke(File f, VirtualChannel channel) throws IOException { - return stating(f).getAbsolutePath(); + return f.getAbsolutePath(); } } @@ -745,7 +743,7 @@ public String invoke(File f, VirtualChannel channel) throws IOException { public boolean hasSymlink(FilePath verificationRoot, boolean noFollowLinks) throws IOException, InterruptedException { return act(new HasSymlink(verificationRoot == null ? null : verificationRoot.remote, noFollowLinks)); } - private static class HasSymlink extends SecureFileCallable { + private static class HasSymlink extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; private final String verificationRoot; private final boolean noFollowLinks; @@ -757,7 +755,7 @@ private static class HasSymlink extends SecureFileCallable { @Override public Boolean invoke(File f, VirtualChannel channel) throws IOException { - return isSymlink(stating(f), verificationRoot, noFollowLinks); + return isSymlink(f, verificationRoot, noFollowLinks); } } @@ -795,7 +793,7 @@ public boolean accept(File file) { public void symlinkTo(final String target, final TaskListener listener) throws IOException, InterruptedException { act(new SymlinkTo(target, listener)); } - private static class SymlinkTo extends SecureFileCallable { + private static class SymlinkTo extends MasterToSlaveFileCallable { private final String target; private final TaskListener listener; SymlinkTo(String target, TaskListener listener) { @@ -805,7 +803,7 @@ private static class SymlinkTo extends SecureFileCallable { private static final long serialVersionUID = 1L; @Override public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - Util.createSymlink(symlinking(f).getParentFile(), target, f.getName(), listener); + Util.createSymlink(f.getParentFile(), target, f.getName(), listener); return null; } } @@ -820,11 +818,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException, Interrupt public String readLink() throws IOException, InterruptedException { return act(new ReadLink()); } - private static class ReadLink extends SecureFileCallable { + private static class ReadLink extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - return Util.resolveSymlink(reading(f)); + return Util.resolveSymlink(f); } } @@ -898,7 +896,7 @@ public void untarFrom(InputStream _in, final TarCompression compression) throws _in.close(); } } - private static class UntarFrom extends SecureFileCallable { + private static class UntarFrom extends MasterToSlaveFileCallable { private final TarCompression compression; private final InputStream in; UntarFrom(TarCompression compression, InputStream in) { @@ -907,7 +905,7 @@ private static class UntarFrom extends SecureFileCallable { } @Override public Void invoke(File dir, VirtualChannel channel) throws IOException { - readFromTar("input stream",dir, compression.extract(in)); // #writing etc. are called in #readFromTar + readFromTar("input stream",dir, compression.extract(in)); return null; } private static final long serialVersionUID = 1L; @@ -1097,7 +1095,7 @@ public void copyFrom(FilePath src) throws IOException, InterruptedException { public void copyFrom(FileItem file) throws IOException, InterruptedException { if(channel==null) { try { - file.write(writing(new File(remote))); + file.write(new File(remote)); } catch (IOException e) { throw e; } catch (Exception e) { @@ -1138,15 +1136,6 @@ public interface FileCallable extends Serializable, RoleSensitive { T invoke(File f, VirtualChannel channel) throws IOException, InterruptedException; } - /** - * {@link FileCallable}s that can be executed anywhere, including the controller. - * - * The code is the same as {@link SlaveToMasterFileCallable}, but used as a marker to - * designate those impls that use {@link FilePathFilter}. - */ - /*package*/ abstract static class SecureFileCallable extends SlaveToMasterFileCallable { - } - /** * Executes some program on the machine that this {@link FilePath} exists, * so that one can perform local file operations. @@ -1300,11 +1289,11 @@ public void checkRoles(RoleChecker checker) throws SecurityException { public URI toURI() throws IOException, InterruptedException { return act(new ToURI()); } - private static class ToURI extends SecureFileCallable { + private static class ToURI extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public URI invoke(File f, VirtualChannel channel) { - return stating(f).toURI(); + return f.toURI(); } } @@ -1342,7 +1331,7 @@ public void mkdirs() throws IOException, InterruptedException { throw new IOException("Failed to mkdirs: " + remote); } } - private static class Mkdirs extends SecureFileCallable { + private static class Mkdirs extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { @@ -1368,14 +1357,14 @@ public void deleteSuffixesRecursive() throws IOException, InterruptedException { /** * Deletes all suffixed directories that are separated by {@link WorkspaceList#COMBINATOR}, including all its contents recursively. */ - private static class DeleteSuffixesRecursive extends SecureFileCallable { + private static class DeleteSuffixesRecursive extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Void invoke(File f, VirtualChannel channel) throws IOException { for (File file : listParentFiles(f)) { if (file.getName().startsWith(f.getName() + WorkspaceList.COMBINATOR)) { - Util.deleteRecursive(file.toPath(), path -> deleting(path.toFile())); + Util.deleteRecursive(file.toPath(), path -> path.toFile()); } } @@ -1400,11 +1389,11 @@ private static File[] listParentFiles(File f) { public void deleteRecursive() throws IOException, InterruptedException { act(new DeleteRecursive()); } - private static class DeleteRecursive extends SecureFileCallable { + private static class DeleteRecursive extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Void invoke(File f, VirtualChannel channel) throws IOException { - Util.deleteRecursive(fileToPath(f), path -> deleting(path.toFile())); + Util.deleteRecursive(fileToPath(f), path -> path.toFile()); return null; } } @@ -1415,11 +1404,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException { public void deleteContents() throws IOException, InterruptedException { act(new DeleteContents()); } - private static class DeleteContents extends SecureFileCallable { + private static class DeleteContents extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Void invoke(File f, VirtualChannel channel) throws IOException { - Util.deleteContentsRecursive(fileToPath(f), path -> deleting(path.toFile())); + Util.deleteContentsRecursive(fileToPath(f), path -> path.toFile()); return null; } } @@ -1518,7 +1507,7 @@ public FilePath createTempFile(final String prefix, final String suffix) throws throw new IOException("Failed to create a temp file on "+remote,e); } } - private static class CreateTempFile extends SecureFileCallable { + private static class CreateTempFile extends MasterToSlaveFileCallable { private final String prefix; private final String suffix; CreateTempFile(String prefix, String suffix) { @@ -1528,8 +1517,7 @@ private static class CreateTempFile extends SecureFileCallable { private static final long serialVersionUID = 1L; @Override public String invoke(File dir, VirtualChannel channel) throws IOException { - creating(new File(dir, prefix + "-security-check-dummy-" + suffix)); // use fake file to check access before creation - File f = creating(File.createTempFile(prefix, suffix, dir)); + File f = File.createTempFile(prefix, suffix, dir); return f.getName(); } } @@ -1583,7 +1571,7 @@ public FilePath createTextTempFile(final String prefix, final String suffix, fin throw new IOException("Failed to create a temp file on "+remote,e); } } - private static class CreateTextTempFile extends SecureFileCallable { + private static class CreateTextTempFile extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; private final boolean inThisDirectory; private final String prefix; @@ -1604,13 +1592,12 @@ public String invoke(File dir, VirtualChannel channel) throws IOException { File f; try { - creating(new File(dir, prefix + "-security-check-dummy-" + suffix)); // use fake file to check access before creation - f = creating(File.createTempFile(prefix, suffix, dir)); + f = File.createTempFile(prefix, suffix, dir); } catch (IOException e) { throw new IOException("Failed to create a temporary directory in "+dir,e); } - try (Writer w = new FileWriter(writing(f))) { + try (Writer w = new FileWriter(f)) { w.write(contents); } @@ -1646,7 +1633,7 @@ public FilePath createTempDir(final String prefix, final String suffix) throws I throw new IOException("Failed to create a temp directory on "+remote,e); } } - private static class CreateTempDir extends SecureFileCallable { + private static class CreateTempDir extends MasterToSlaveFileCallable { private final String name; CreateTempDir(String name) { this.name = name; @@ -1654,7 +1641,6 @@ private static class CreateTempDir extends SecureFileCallable { private static final long serialVersionUID = 1L; @Override public String invoke(File dir, VirtualChannel channel) throws IOException { - mkdirsing(new File(dir, name + "-security-test")); // ensure access Path tempPath; final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); @@ -1666,7 +1652,7 @@ public String invoke(File dir, VirtualChannel channel) throws IOException { tempPath = Files.createTempDirectory(Util.fileToPath(dir), name); } - if (mkdirsing(tempPath.toFile()) == null) { + if (tempPath.toFile() == null) { throw new IOException("Failed to obtain file from path " + dir); } return tempPath.toFile().getName(); @@ -1682,11 +1668,11 @@ public boolean delete() throws IOException, InterruptedException { act(new Delete()); return true; } - private static class Delete extends SecureFileCallable { + private static class Delete extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Void invoke(File f, VirtualChannel channel) throws IOException { - Util.deleteFile(deleting(f)); + Util.deleteFile(f); return null; } } @@ -1697,11 +1683,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException { public boolean exists() throws IOException, InterruptedException { return act(new Exists()); } - private static class Exists extends SecureFileCallable { + private static class Exists extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Boolean invoke(File f, VirtualChannel channel) throws IOException { - return stating(f).exists(); + return f.exists(); } } @@ -1715,11 +1701,11 @@ public Boolean invoke(File f, VirtualChannel channel) throws IOException { public long lastModified() throws IOException, InterruptedException { return act(new LastModified()); } - private static class LastModified extends SecureFileCallable { + private static class LastModified extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Long invoke(File f, VirtualChannel channel) throws IOException { - return stating(f).lastModified(); + return f.lastModified(); } } @@ -1731,7 +1717,7 @@ public Long invoke(File f, VirtualChannel channel) throws IOException { public void touch(final long timestamp) throws IOException, InterruptedException { act(new Touch(timestamp)); } - private static class Touch extends SecureFileCallable { + private static class Touch extends MasterToSlaveFileCallable { private final long timestamp; Touch(long timestamp) { this.timestamp = timestamp; @@ -1740,9 +1726,9 @@ private static class Touch extends SecureFileCallable { @Override public Void invoke(File f, VirtualChannel channel) throws IOException { if(!f.exists()) { - Files.newOutputStream(fileToPath(creating(f))).close(); + Files.newOutputStream(fileToPath(f)).close(); } - if(!stating(f).setLastModified(timestamp)) + if(!f.setLastModified(timestamp)) throw new IOException("Failed to set the timestamp of "+f+" to "+timestamp); return null; } @@ -1755,7 +1741,7 @@ private void setLastModifiedIfPossible(final long timestamp) throws IOException, LOGGER.warning(message); } } - private static class SetLastModified extends SecureFileCallable { + private static class SetLastModified extends MasterToSlaveFileCallable { private final long timestamp; SetLastModified(long timestamp) { this.timestamp = timestamp; @@ -1763,7 +1749,7 @@ private static class SetLastModified extends SecureFileCallable { private static final long serialVersionUID = -828220335793641630L; @Override public String invoke(File f, VirtualChannel channel) throws IOException { - if(!writing(f).setLastModified(timestamp)) { + if(!f.setLastModified(timestamp)) { if (Functions.isWindows()) { // On Windows this seems to fail often. See JENKINS-11073 // Therefore don't fail, but just log a warning @@ -1782,11 +1768,11 @@ public String invoke(File f, VirtualChannel channel) throws IOException { public boolean isDirectory() throws IOException, InterruptedException { return act(new IsDirectory()); } - private static class IsDirectory extends SecureFileCallable { + private static class IsDirectory extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Boolean invoke(File f, VirtualChannel channel) throws IOException { - return stating(f).isDirectory(); + return f.isDirectory(); } } @@ -1798,11 +1784,11 @@ public Boolean invoke(File f, VirtualChannel channel) throws IOException { public long length() throws IOException, InterruptedException { return act(new Length()); } - private static class Length extends SecureFileCallable { + private static class Length extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Long invoke(File f, VirtualChannel channel) throws IOException { - return stating(f).length(); + return f.length(); } } @@ -1875,7 +1861,7 @@ public void chmod(final int mask) throws IOException, InterruptedException { if(!isUnix() || mask==-1) return; act(new Chmod(mask)); } - private static class Chmod extends SecureFileCallable { + private static class Chmod extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; private final int mask; Chmod(int mask) { @@ -1883,7 +1869,7 @@ private static class Chmod extends SecureFileCallable { } @Override public Void invoke(File f, VirtualChannel channel) throws IOException { - _chmod(writing(f), mask); + _chmod(f, mask); return null; } @@ -1914,11 +1900,11 @@ public int mode() throws IOException, InterruptedException, PosixException { if(!isUnix()) return -1; return act(new Mode()); } - private static class Mode extends SecureFileCallable { + private static class Mode extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public Integer invoke(File f, VirtualChannel channel) throws IOException { - return IOUtils.mode(stating(f)); + return IOUtils.mode(f); } } @@ -1984,7 +1970,7 @@ public List list(final FileFilter filter) throws IOException, Interrup } return act(new ListFilter(filter), (filter != null ? filter : this).getClass().getClassLoader()); } - private static class ListFilter extends SecureFileCallable> { + private static class ListFilter extends MasterToSlaveFileCallable> { private final FileFilter filter; ListFilter(FileFilter filter) { this.filter = filter; @@ -1992,7 +1978,7 @@ private static class ListFilter extends SecureFileCallable> { private static final long serialVersionUID = 1L; @Override public List invoke(File f, VirtualChannel channel) throws IOException { - File[] children = reading(f).listFiles(filter); + File[] children = f.listFiles(filter); if (children == null) { return Collections.emptyList(); } @@ -2050,7 +2036,7 @@ public FilePath[] list(final String includes, final String excludes) throws IOEx public FilePath[] list(final String includes, final String excludes, final boolean defaultExcludes) throws IOException, InterruptedException { return act(new ListGlob(includes, excludes, defaultExcludes)); } - private static class ListGlob extends SecureFileCallable { + private static class ListGlob extends MasterToSlaveFileCallable { private final String includes; private final String excludes; private final boolean defaultExcludes; @@ -2062,11 +2048,11 @@ private static class ListGlob extends SecureFileCallable { private static final long serialVersionUID = 1L; @Override public FilePath[] invoke(File f, VirtualChannel channel) throws IOException { - String[] files = glob(reading(f), includes, excludes, defaultExcludes); + String[] files = glob(f, includes, excludes, defaultExcludes); FilePath[] r = new FilePath[files.length]; for( int i=0; i { + private static class Read extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; private final Pipe p; private String verificationRoot; @@ -2202,7 +2188,7 @@ private static class Read extends SecureFileCallable { } @Override public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - try (InputStream fis = newInputStreamDenyingSymlinkAsNeeded(reading(f), verificationRoot, noFollowLinks); OutputStream out = p.getOut()) { + try (InputStream fis = newInputStreamDenyingSymlinkAsNeeded(f, verificationRoot, noFollowLinks); OutputStream out = p.getOut()) { org.apache.commons.io.IOUtils.copy(fis, out); } catch (Exception x) { p.error(x); @@ -2256,7 +2242,7 @@ public int read(byte[] b) throws IOException { return new java.util.zip.GZIPInputStream(p.getIn()); } - private static class OffsetPipeSecureFileCallable extends SecureFileCallable { + private static class OffsetPipeSecureFileCallable extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; private Pipe p; @@ -2271,7 +2257,7 @@ private OffsetPipeSecureFileCallable(Pipe p, long offset) { public Void invoke(File f, VirtualChannel channel) throws IOException { try (OutputStream os = p.getOut(); OutputStream out = new java.util.zip.GZIPOutputStream(os, 8192); - RandomAccessFile raf = new RandomAccessFile(reading(f), "r")) { + RandomAccessFile raf = new RandomAccessFile(f, "r")) { raf.seek(offset); byte[] buf = new byte[8192]; int len; @@ -2289,11 +2275,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException { public String readToString() throws IOException, InterruptedException { return act(new ReadToString()); } - private static class ReadToString extends SecureFileCallable { + private static class ReadToString extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - return new String(Files.readAllBytes(fileToPath(reading(f)))); + return new String(Files.readAllBytes(fileToPath(f))); } } @@ -2313,18 +2299,18 @@ public OutputStream write() throws IOException, InterruptedException { if(channel==null) { File f = new File(remote).getAbsoluteFile(); mkdirs(f.getParentFile()); - return Files.newOutputStream(fileToPath(writing(f))); // TODO #writing seems unnecessary on a local file + return Files.newOutputStream(fileToPath(f)); } return act(new WritePipe()); } - private static class WritePipe extends SecureFileCallable { + private static class WritePipe extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public OutputStream invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { f = f.getAbsoluteFile(); mkdirs(f.getParentFile()); - return new RemoteOutputStream(Files.newOutputStream(fileToPath(writing(f)))); + return new RemoteOutputStream(Files.newOutputStream(fileToPath(f))); } } @@ -2338,7 +2324,7 @@ public OutputStream invoke(File f, VirtualChannel channel) throws IOException, I public void write(final String content, final String encoding) throws IOException, InterruptedException { act(new Write(encoding, content)); } - private static class Write extends SecureFileCallable { + private static class Write extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; private final String encoding; private final String content; @@ -2349,7 +2335,7 @@ private static class Write extends SecureFileCallable { @Override public Void invoke(File f, VirtualChannel channel) throws IOException { mkdirs(f.getParentFile()); - try (OutputStream fos = Files.newOutputStream(fileToPath(writing(f))); + try (OutputStream fos = Files.newOutputStream(fileToPath(f)); Writer w = encoding != null ? new OutputStreamWriter(fos, encoding) : new OutputStreamWriter(fos)) { w.write(content); } @@ -2364,11 +2350,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException { public String digest() throws IOException, InterruptedException { return act(new Digest()); } - private static class Digest extends SecureFileCallable { + private static class Digest extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public String invoke(File f, VirtualChannel channel) throws IOException { - return Util.getDigestOf(reading(f)); + return Util.getDigestOf(f); } } @@ -2382,7 +2368,7 @@ public void renameTo(final FilePath target) throws IOException, InterruptedExcep } act(new RenameTo(target)); } - private static class RenameTo extends SecureFileCallable { + private static class RenameTo extends MasterToSlaveFileCallable { private final FilePath target; RenameTo(FilePath target) { this.target = target; @@ -2390,7 +2376,7 @@ private static class RenameTo extends SecureFileCallable { private static final long serialVersionUID = 1L; @Override public Void invoke(File f, VirtualChannel channel) throws IOException { - Files.move(fileToPath(deleting(reading(f))), fileToPath(writing(creating(new File(target.remote)))), LinkOption.NOFOLLOW_LINKS); + Files.move(fileToPath(f), fileToPath(new File(target.remote)), LinkOption.NOFOLLOW_LINKS); return null; } } @@ -2406,7 +2392,7 @@ public void moveAllChildrenTo(final FilePath target) throws IOException, Interru } act(new MoveAllChildrenTo(target)); } - private static class MoveAllChildrenTo extends SecureFileCallable { + private static class MoveAllChildrenTo extends MasterToSlaveFileCallable { private final FilePath target; MoveAllChildrenTo(FilePath target) { this.target = target; @@ -2417,17 +2403,17 @@ public Void invoke(File f, VirtualChannel channel) throws IOException { // JENKINS-16846: if f.getName() is the same as one of the files/directories in f, // then the rename op will fail File tmp = new File(f.getAbsolutePath()+".__rename"); - if (!deleting(f).renameTo(creating(tmp))) + if (!f.renameTo(tmp)) throw new IOException("Failed to rename "+f+" to "+tmp); File t = new File(target.getRemote()); - for(File child : reading(tmp).listFiles()) { + for(File child : tmp.listFiles()) { File target = new File(t, child.getName()); - if(!deleting(reading(child)).renameTo(writing(creating(target)))) + if(!child.renameTo(target)) throw new IOException("Failed to rename "+child+" to "+target); } - Files.deleteIfExists(Util.fileToPath(deleting(tmp))); + Files.deleteIfExists(Util.fileToPath(tmp)); return null; } } @@ -2461,7 +2447,7 @@ public void copyToWithPermission(FilePath target) throws IOException, Interrupte target.chmod(mode()); target.setLastModifiedIfPossible(lastModified()); } - private static class CopyToWithPermission extends SecureFileCallable { + private static class CopyToWithPermission extends MasterToSlaveFileCallable { private final FilePath target; CopyToWithPermission(FilePath target) { this.target = target; @@ -2470,9 +2456,8 @@ private static class CopyToWithPermission extends SecureFileCallable { public Void invoke(File f, VirtualChannel channel) throws IOException { File targetFile = new File(target.remote); File targetDir = targetFile.getParentFile(); - filterNonNull().mkdirs(targetDir); Files.createDirectories(fileToPath(targetDir)); - Files.copy(fileToPath(reading(f)), fileToPath(writing(targetFile)), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); + Files.copy(fileToPath(f), fileToPath(targetFile), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); return null; } } @@ -2489,7 +2474,7 @@ public void copyTo(OutputStream os) throws IOException, InterruptedException { // this is needed because I/O operation is asynchronous syncIO(); } - private static class CopyTo extends SecureFileCallable { + private static class CopyTo extends MasterToSlaveFileCallable { private static final long serialVersionUID = 4088559042349254141L; private final OutputStream out; CopyTo(OutputStream out) { @@ -2497,7 +2482,7 @@ private static class CopyTo extends SecureFileCallable { } @Override public Void invoke(File f, VirtualChannel channel) throws IOException { - try (InputStream fis = Files.newInputStream(fileToPath(reading(f)))) { + try (InputStream fis = Files.newInputStream(fileToPath(f))) { org.apache.commons.io.IOUtils.copy(fis, out); return null; } finally { @@ -2667,7 +2652,7 @@ private IOException ioWithCause(ExecutionException e) { ; } - private static class CopyRecursiveLocal extends SecureFileCallable { + private static class CopyRecursiveLocal extends MasterToSlaveFileCallable { private final FilePath target; private final DirScanner scanner; CopyRecursiveLocal(FilePath target, DirScanner scanner) { @@ -2685,7 +2670,7 @@ public Integer invoke(File base, VirtualChannel channel) throws IOException { } final File dest = new File(target.remote); final AtomicInteger count = new AtomicInteger(); - scanner.scan(base, reading(new FileVisitor() { + scanner.scan(base, new FileVisitor() { private boolean exceptionEncountered; private boolean logMessageShown; @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "TODO needs triage") @@ -2694,7 +2679,7 @@ public void visit(File f, String relativePath) throws IOException { if (f.isFile()) { File target = new File(dest, relativePath); mkdirsE(target.getParentFile()); - Path targetPath = fileToPath(writing(target)); + Path targetPath = fileToPath(target); exceptionEncountered = exceptionEncountered || !tryCopyWithAttributes(f, targetPath); if (exceptionEncountered) { Files.copy(fileToPath(f), targetPath, StandardCopyOption.REPLACE_EXISTING); @@ -2727,18 +2712,17 @@ public boolean understandsSymlink() { public void visitSymlink(File link, String target, String relativePath) throws IOException { try { mkdirsE(new File(dest, relativePath).getParentFile()); - writing(new File(dest, target)); Util.createSymlink(dest, target, relativePath, TaskListener.NULL); } catch (InterruptedException x) { throw new IOException(x); } count.incrementAndGet(); } - })); + }); return count.get(); } } - private static class ReadFromTar extends SecureFileCallable { + private static class ReadFromTar extends MasterToSlaveFileCallable { private final Pipe pipe; private final String description; private final TarCompression compression; @@ -2759,7 +2743,7 @@ public Void invoke(File f, VirtualChannel channel) throws IOException { } } } - private static class WriteToTar extends SecureFileCallable { + private static class WriteToTar extends MasterToSlaveFileCallable { private final DirScanner scanner; private final Pipe pipe; private final TarCompression compression; @@ -2771,10 +2755,10 @@ private static class WriteToTar extends SecureFileCallable { private static final long serialVersionUID = 1L; @Override public Integer invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - return writeToTar(reading(f), scanner, compression.compress(pipe.getOut())); + return writeToTar(f, scanner, compression.compress(pipe.getOut())); } } - private static class CopyRecursiveRemoteToLocal extends SecureFileCallable { + private static class CopyRecursiveRemoteToLocal extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; private final Pipe pipe; private final DirScanner scanner; @@ -2787,7 +2771,7 @@ private static class CopyRecursiveRemoteToLocal extends SecureFileCallable files) { @NonNull public static final LocalChannel localChannel = new LocalChannel(threadPoolForRemoting); - private static @NonNull SoloFilePathFilter filterNonNull() { - final SoloFilePathFilter filter = SoloFilePathFilter.wrap(FilePathFilter.current()); - return filter != null ? filter : UNRESTRICTED; - } - - /** - * Wraps {@link FileVisitor} to notify read access to {@link FilePathFilter}. - */ - private static FileVisitor reading(final FileVisitor v) { - final FilePathFilter filter = SoloFilePathFilter.wrap(FilePathFilter.current()); - if (filter==null) return v; - - return new FileVisitor() { - @Override - public void visit(File f, String relativePath) throws IOException { - filter.read(f); - v.visit(f,relativePath); - } - - @Override - public void visitSymlink(File link, String target, String relativePath) throws IOException { - filter.read(link); - v.visitSymlink(link, target, relativePath); - } - - @Override - public boolean understandsSymlink() { - return v.understandsSymlink(); - } - }; - } - /** * Wraps {@link FileVisitor} to ignore symlinks. */ @@ -3501,76 +3453,8 @@ public boolean understandsSymlink() { return v; } - /** - * Pass through 'f' after ensuring that we can read that file. - */ - private static File reading(File f) { - filterNonNull().read(f); - return f; - } - - /** - * Pass through 'f' after ensuring that we can access the file attributes. - */ - private static File stating(File f) { - filterNonNull().stat(f); - return f; - } - - /** - * Pass through 'f' after ensuring that we can create that file/dir. - */ - private static File creating(File f) { - filterNonNull().create(f); - return f; - } - - /** - * Pass through 'f' after ensuring that we can write to that file. - */ - private static File writing(File f) { - FilePathFilter filter = filterNonNull(); - if (!f.exists()) - filter.create(f); - filter.write(f); - return f; - } - - /** - * Pass through 'f' after ensuring that we can create that symlink. - */ - private static File symlinking(File f) { - FilePathFilter filter = filterNonNull(); - if (!f.exists()) - filter.create(f); - filter.symlink(f); - return f; - } - - /** - * Pass through 'f' after ensuring that we can delete that file. - */ - private static File deleting(File f) { - filterNonNull().delete(f); - return f; - } - - /** - * Pass through 'f' after ensuring that we can mkdirs that directory. - */ - private static File mkdirsing(File f) { - filterNonNull().mkdirs(f); - return f; - } - private static boolean mkdirs(File dir) throws IOException { if (dir.exists()) return false; - - File reference = dir; - while (reference != null && !reference.exists()) { - filterNonNull().mkdirs(reference); - reference = reference.getParentFile(); - } Files.createDirectories(fileToPath(dir)); return true; } @@ -3579,11 +3463,6 @@ private static File mkdirsE(File dir) throws IOException { if (dir.exists()) { return dir; } - File reference = dir; - while (reference != null && !reference.exists()) { - filterNonNull().mkdirs(reference); - reference = reference.getParentFile(); - } return IOUtils.mkdirs(dir); } @@ -3597,7 +3476,7 @@ public boolean isDescendant(@NonNull String potentialChildRelativePath) throws I return act(new IsDescendant(potentialChildRelativePath)); } - private static class IsDescendant extends SecureFileCallable { + private static class IsDescendant extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; private String potentialChildRelativePath; @@ -3611,7 +3490,7 @@ public Boolean invoke(@NonNull File parentFile, @NonNull VirtualChannel channel) throw new IllegalArgumentException("Only a relative path is supported, the given path is absolute: " + potentialChildRelativePath); } - Path parentAbsolutePath = Util.fileToPath(stating(parentFile).getAbsoluteFile()); + Path parentAbsolutePath = Util.fileToPath(parentFile.getAbsoluteFile()); Path parentRealPath; try { if (Functions.isWindows()) { @@ -3716,8 +3595,6 @@ private static Path getRealPath(Path path) throws IOException { return path.toRealPath(LinkOption.NOFOLLOW_LINKS); } - private static final SoloFilePathFilter UNRESTRICTED = SoloFilePathFilter.wrap(FilePathFilter.UNRESTRICTED); - private static class SymlinkDiscardingFileFilter implements FileFilter, Serializable { private final String verificationRoot; diff --git a/core/src/main/java/jenkins/FilePathFilter.java b/core/src/main/java/jenkins/FilePathFilter.java deleted file mode 100644 index 585b0d6cb468..000000000000 --- a/core/src/main/java/jenkins/FilePathFilter.java +++ /dev/null @@ -1,132 +0,0 @@ -package jenkins; - -import edu.umd.cs.findbugs.annotations.CheckForNull; -import hudson.FilePath; -import hudson.remoting.Channel; -import hudson.remoting.ChannelBuilder; -import java.io.File; -import jenkins.security.ChannelConfigurator; - -/** - * Inspects {@link FilePath} access from remote channels. - * - *

      - * Returning {@code true} indicates that the access is accepted. No other {@link FilePathFilter} - * will be consulted to reject the execution, and the access will go through. Returning {@code false} - * indicates "I don't know". Other {@link FilePathFilter}s get to inspect the access, and they might - * accept/reject access. And finally, throwing {@link SecurityException} is to reject the access. - * - *

      - * To insert a custom {@link FilePathFilter} into a connection, - * see {@link ChannelConfigurator#onChannelBuilding(ChannelBuilder, Object)} - * - * @author Kohsuke Kawaguchi - * @see FilePath - * @since 1.587 / 1.580.1 - */ -public abstract class FilePathFilter { - /** - * Checks if the given file/directory can be read. - * - * On POSIX, this corresponds to the 'r' permission of the file/directory itself. - */ - public boolean read(File f) throws SecurityException { return false; } - - /** - * Checks if the given file can be written. - * - * On POSIX, this corresponds to the 'w' permission of the file itself. - */ - public boolean write(File f) throws SecurityException { return false; } - - /** - * Checks if a symlink can be created at 'f' - * - * On POSIX, this corresponds to the 'w' permission of the file itself. - */ - public boolean symlink(File f) throws SecurityException { return false; } - - /** - * Checks if the given directory can be created. - * - * On POSIX, this corresponds to the 'w' permission of the parent directory. - */ - public boolean mkdirs(File f) throws SecurityException { return false; } - - /** - * Checks if the given file can be created. - * - * On POSIX, this corresponds to the 'w' permission of the parent directory. - */ - public boolean create(File f) throws SecurityException { return false; } - - /** - * Checks if the given file/directory can be deleted. - * - * On POSIX, this corresponds to the 'w' permission of the parent directory. - */ - public boolean delete(File f) throws SecurityException { return false; } - - /** - * Checks if the metadata of the given file/directory (as opposed to the content) can be accessed. - * - * On POSIX, this corresponds to the 'r' permission of the parent directory. - */ - public boolean stat(File f) throws SecurityException { return false; } - - - public final void installTo(ChannelBuilder cb) { - installTo(cb,FilePathFilterAggregator.DEFAULT_ORDINAL); - } - - public final void installTo(ChannelBuilder cb, double d) { - synchronized (cb) { - FilePathFilterAggregator filters = (FilePathFilterAggregator) cb.getProperties().get(FilePathFilterAggregator.KEY); - if (filters==null) { - filters = new FilePathFilterAggregator(); - cb.withProperty(FilePathFilterAggregator.KEY,filters); - } - filters.add(this,d); - } - } - - public final void uninstallFrom(Channel ch) { - synchronized (ch) { - FilePathFilterAggregator filters = ch.getProperty(FilePathFilterAggregator.KEY); - if (filters!=null) { - filters.remove(this); - } - } - } - - /** - * Returns an {@link FilePathFilter} object that represents all the in-scope filters, - * or {@code null} if none is needed. - */ - public static @CheckForNull FilePathFilter current() { - Channel ch = Channel.current(); - if (ch==null) return null; - - return ch.getProperty(FilePathFilterAggregator.KEY); - } - - /** - * Immutable instance that represents the filter that allows everything. - */ - public static final FilePathFilter UNRESTRICTED = new FilePathFilterAggregator() { - @Override - protected boolean defaultAction() throws SecurityException { - return true; - } - - @Override - public void add(FilePathFilter f, double d) { - // noop because we are immutable - } - - @Override - public String toString() { - return "Unrestricted"; - } - }; -} diff --git a/core/src/main/java/jenkins/FilePathFilterAggregator.java b/core/src/main/java/jenkins/FilePathFilterAggregator.java deleted file mode 100644 index 0702bebb8048..000000000000 --- a/core/src/main/java/jenkins/FilePathFilterAggregator.java +++ /dev/null @@ -1,142 +0,0 @@ -package jenkins; - -import hudson.Extension; -import hudson.FilePath; -import hudson.remoting.ChannelProperty; -import java.io.File; -import java.util.Collections; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Maintains a bundle of {@link FilePathFilter} and implement a hook that broadcasts to all the filters. - * - * Accessible as channel property. - * - * @author Kohsuke Kawaguchi - * @see FilePath - * @since 1.587 / 1.580.1 - */ -class FilePathFilterAggregator extends FilePathFilter { - private final CopyOnWriteArrayList all = new CopyOnWriteArrayList<>(); - - private static class Entry implements Comparable { - final FilePathFilter filter; - final double ordinal; - - private Entry(FilePathFilter filter, double ordinal) { - this.filter = filter; - this.ordinal = ordinal; - } - - @Override - public int compareTo(Entry that) { - int result = Double.compare(this.ordinal, that.ordinal); - - if (result < 0) return -1; - if (result > 0) return 1; - - // to create predictable order that doesn't depend on the insertion order, use class name - // to break a tie - return this.filter.getClass().getName().compareTo(that.filter.getClass().getName()); - } - } - - public final void add(FilePathFilter f) { - add(f, DEFAULT_ORDINAL); - } - - /** - * - * @param ordinal - * Crude ordering control among {@link FilePathFilter} ala {@link Extension#ordinal()}. - * A filter with a bigger value will get precedence. Defaults to 0. - */ - public void add(FilePathFilter f, double ordinal) { - Entry e = new Entry(f, ordinal); - int i = Collections.binarySearch(all, e, Collections.reverseOrder()); - if (i>=0) all.add(i,e); - else all.add(-i-1,e); - } - - public void remove(FilePathFilter f) { - all.removeIf(e -> e.filter == f); - } - - /** - * If no filter cares, what to do? - */ - protected boolean defaultAction() throws SecurityException { - return false; - } - - @Override - public boolean read(File f) throws SecurityException { - for (Entry e : all) { - if (e.filter.read(f)) - return true; - } - return defaultAction(); - } - - @Override - public boolean mkdirs(File f) throws SecurityException { - for (Entry e : all) { - if (e.filter.mkdirs(f)) - return true; - } - return defaultAction(); - } - - @Override - public boolean write(File f) throws SecurityException { - for (Entry e : all) { - if (e.filter.write(f)) - return true; - } - return defaultAction(); - } - - @Override - public boolean symlink(File f) throws SecurityException { - for (Entry e : all) { - if (e.filter.symlink(f)) - return true; - } - return defaultAction(); - } - - @Override - public boolean create(File f) throws SecurityException { - for (Entry e : all) { - if (e.filter.create(f)) - return true; - } - return defaultAction(); - } - - @Override - public boolean delete(File f) throws SecurityException { - for (Entry e : all) { - if (e.filter.delete(f)) - return true; - } - return defaultAction(); - } - - @Override - public boolean stat(File f) throws SecurityException { - for (Entry e : all) { - if (e.filter.stat(f)) - return true; - } - return defaultAction(); - } - - @Override public String toString() { - return "FilePathFilterAggregator" + all; - } - - static final ChannelProperty KEY = new ChannelProperty<>(FilePathFilterAggregator.class, "FilePathFilters"); - - public static final int DEFAULT_ORDINAL = 0; -} diff --git a/core/src/main/java/jenkins/ReflectiveFilePathFilter.java b/core/src/main/java/jenkins/ReflectiveFilePathFilter.java deleted file mode 100644 index f35186aebc1c..000000000000 --- a/core/src/main/java/jenkins/ReflectiveFilePathFilter.java +++ /dev/null @@ -1,53 +0,0 @@ -package jenkins; - -import java.io.File; - -/** - * Convenient adapter for {@link FilePathFilter} that allows you to handle all - * operations as a single string argument. - * - * @author Kohsuke Kawaguchi - * @since 1.587 / 1.580.1 - */ -public abstract class ReflectiveFilePathFilter extends FilePathFilter { - /** - * @param name - * Name of the operation. - */ - protected abstract boolean op(String name, File path) throws SecurityException; - - @Override - public boolean read(File f) throws SecurityException { - return op("read", f); - } - - @Override - public boolean write(File f) throws SecurityException { - return op("write", f); - } - - @Override - public boolean symlink(File f) throws SecurityException { - return op("symlink",f); - } - - @Override - public boolean mkdirs(File f) throws SecurityException { - return op("mkdirs", f); - } - - @Override - public boolean create(File f) throws SecurityException { - return op("create", f); - } - - @Override - public boolean delete(File f) throws SecurityException { - return op("delete", f); - } - - @Override - public boolean stat(File f) throws SecurityException { - return op("stat", f); - } -} diff --git a/core/src/main/java/jenkins/SoloFilePathFilter.java b/core/src/main/java/jenkins/SoloFilePathFilter.java deleted file mode 100644 index 48420c4cee5b..000000000000 --- a/core/src/main/java/jenkins/SoloFilePathFilter.java +++ /dev/null @@ -1,103 +0,0 @@ -package jenkins; - -import edu.umd.cs.findbugs.annotations.Nullable; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import hudson.FilePath; -import java.io.File; -import java.util.UUID; -import java.util.logging.Level; -import java.util.logging.Logger; -import jenkins.util.SystemProperties; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - -/** - * Variant of {@link FilePathFilter} that assumes it is the sole actor. - * - * It throws {@link SecurityException} instead of returning false. This makes it the - * convenient final wrapper for the caller. - * - * @author Kohsuke Kawaguchi - */ -public final class SoloFilePathFilter extends FilePathFilter { - - private static final Logger LOGGER = Logger.getLogger(SoloFilePathFilter.class.getName()); - - @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console") - @Restricted(NoExternalUse.class) - public static /* non-final for Groovy */ boolean REDACT_ERRORS = SystemProperties.getBoolean(SoloFilePathFilter.class.getName() + ".redactErrors", true); - - private final FilePathFilter base; - - private SoloFilePathFilter(FilePathFilter base) { - this.base = base; - } - - /** - * Null-safe constructor. - */ - public static @Nullable SoloFilePathFilter wrap(@Nullable FilePathFilter base) { - if (base==null) return null; - return new SoloFilePathFilter(base); - } - - private boolean noFalse(String op, File f, boolean b) { - if (!b) { - final String detailedMessage = "Agent may not '" + op + "' at '" + f + "'. See https://www.jenkins.io/redirect/security-144 for more information."; - if (REDACT_ERRORS) { - // We may end up trying to access file paths indirectly, e.g. FilePath#listFiles starts in an allowed dir but follows symlinks outside, so do not disclose paths in error message - UUID uuid = UUID.randomUUID(); - LOGGER.log(Level.WARNING, () -> uuid + ": " + detailedMessage); - throw new SecurityException("Agent may not access a file path. See the system log for more details about the error ID '" + uuid + "' and https://www.jenkins.io/redirect/security-144 for more information."); - } else { - throw new SecurityException(detailedMessage); - } - } - return true; - } - - private File normalize(File file){ - return new File(FilePath.normalize(file.getAbsolutePath())); - } - - @Override - public boolean read(File f) throws SecurityException { - return noFalse("read",f,base.read(normalize(f))); - } - - @Override - public boolean write(File f) throws SecurityException { - return noFalse("write",f,base.write(normalize(f))); - } - - @Override - public boolean symlink(File f) throws SecurityException { - return noFalse("symlink",f,base.symlink(normalize(f))); - } - - @Override - public boolean mkdirs(File f) throws SecurityException { - // mkdirs is special because it could operate on parents of the specified path - File reference = normalize(f); - while (reference != null && !reference.exists()) { - noFalse("mkdirs", f, base.mkdirs(reference)); // Pass f as reference into the error to be vague - reference = reference.getParentFile(); - } - return true; - } - - @Override - public boolean create(File f) throws SecurityException { - return noFalse("create",f,base.create(normalize(f))); - } - - @Override - public boolean delete(File f) throws SecurityException { - return noFalse("delete",f,base.delete(normalize(f))); - } - - @Override - public boolean stat(File f) throws SecurityException { - return noFalse("stat",f,base.stat(normalize(f))); - } -} diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index caac5b987333..35579cd02e97 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -54,7 +54,6 @@ import jenkins.model.JenkinsLocationConfiguration; import jenkins.security.ApiTokenProperty; import jenkins.security.apitoken.TokenUuidAndPlainValue; -import jenkins.security.s2m.AdminWhitelistRule; import jenkins.security.seed.UserSeedProperty; import jenkins.util.SystemProperties; import jenkins.util.UrlHelper; @@ -175,11 +174,7 @@ public String getDisplayName() { // require a crumb issuer jenkins.setCrumbIssuer(GlobalCrumbIssuerConfiguration.createDefaultCrumbIssuer()); - - // set controller -> agent security: - jenkins.getInjector().getInstance(AdminWhitelistRule.class) - .setMasterKillSwitch(false); - + jenkins.save(); // TODO could probably be removed since some of the above setters already call save bc.commit(); } diff --git a/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java b/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java deleted file mode 100644 index 4b8c07d92034..000000000000 --- a/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java +++ /dev/null @@ -1,71 +0,0 @@ -package jenkins.security.s2m; - -import hudson.Extension; -import hudson.FilePath; -import hudson.model.AdministrativeMonitor; -import hudson.remoting.Callable; -import java.io.IOException; -import javax.inject.Inject; -import jenkins.model.Jenkins; -import org.jenkinsci.Symbol; -import org.kohsuke.stapler.HttpResponse; -import org.kohsuke.stapler.HttpResponses; -import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.interceptor.RequirePOST; - -/** - * Report any rejected {@link Callable}s and {@link FilePath} executions and allow - * admins to whitelist them. - * - * @since 1.587 / 1.580.1 - * @author Kohsuke Kawaguchi - */ -@Extension @Symbol({"agentToControllerAccessControl", "slaveToMasterAccessControl"}) -public class AdminCallableMonitor extends AdministrativeMonitor { - @Inject - Jenkins jenkins; - - @Inject - AdminWhitelistRule rule; - - public AdminCallableMonitor() { - super("slaveToMasterAccessControl"); // TODO Can we change this while retaining compatibility? - } - - @Override - public boolean isSecurity() { - return true; - } - - @Override - public boolean isActivated() { - return !rule.rejected.describe().isEmpty(); - } - - @Override - public String getDisplayName() { - return Messages.AdminCallableMonitor_DisplayName(); - } - - // bind this to URL - public AdminWhitelistRule getRule() { - return rule; - } - - /** - * Depending on whether the user said "examine" or "dismiss", send him to the right place. - */ - @RequirePOST - public HttpResponse doAct(@QueryParameter String dismiss) throws IOException { - if(dismiss!=null) { - disable(true); - return HttpResponses.redirectViaContextPath("/manage"); - } else { - return HttpResponses.redirectTo("rule/"); - } - } - - public HttpResponse doIndex() { - return HttpResponses.redirectTo("rule/"); - } -} diff --git a/core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java b/core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java deleted file mode 100644 index 2dc832fa0c60..000000000000 --- a/core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java +++ /dev/null @@ -1,31 +0,0 @@ -package jenkins.security.s2m; - -import hudson.Extension; -import hudson.remoting.Callable; -import java.util.Collection; -import javax.inject.Inject; -import org.jenkinsci.Symbol; -import org.jenkinsci.remoting.Role; -import org.jenkinsci.remoting.RoleSensitive; - -/** - * Whitelists {@link Callable}s that are approved by the admins. - * - * - *

      - * Smaller ordinal value allows other programmable {@link CallableWhitelist} to accept/reject - * {@link Callable}s without bothering the admins. This impl should be used only for those - * {@link Callable}s that our program does not have any idea for. - * - * @author Kohsuke Kawaguchi - */ -@Extension(ordinal=-100) @Symbol("admin") -public class AdminCallableWhitelist extends CallableWhitelist { - @Inject - AdminWhitelistRule rule; - - @Override - public boolean isWhitelisted(RoleSensitive subject, Collection expected, Object context) { - return rule.isWhitelisted(subject,expected,context); - } -} diff --git a/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java deleted file mode 100644 index bf2ce553b420..000000000000 --- a/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -package jenkins.security.s2m; - -import edu.umd.cs.findbugs.annotations.Nullable; -import hudson.Extension; -import hudson.remoting.ChannelBuilder; -import java.io.File; -import javax.inject.Inject; -import jenkins.FilePathFilter; -import jenkins.ReflectiveFilePathFilter; -import jenkins.security.ChannelConfigurator; - -/** - * {@link FilePathFilter} that allows admins to whitelist specific file access. - * - *

      - * This class is just a glue, and the real logic happens inside {@link AdminWhitelistRule} - * - * @author Kohsuke Kawaguchi - * @since 1.587 / 1.580.1 - */ -public class AdminFilePathFilter extends ReflectiveFilePathFilter { - - private final AdminWhitelistRule rule; - - public AdminFilePathFilter(AdminWhitelistRule rule) { - this.rule = rule; - } - - @Override - protected boolean op(String op, File path) throws SecurityException { - return rule.checkFileAccess(op,path); - } - - @Extension - public static class ChannelConfiguratorImpl extends ChannelConfigurator { - @Inject - AdminWhitelistRule rule; - - @Override - public void onChannelBuilding(ChannelBuilder builder, @Nullable Object context) { - new AdminFilePathFilter(rule).installTo(builder,ORDINAL); - } - } - - /** - * Local user preference should have higher priority than random FilePathFilters that - * plugins might provide. - */ - public static final double ORDINAL = 100; -} diff --git a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java index 98bcac0f7b8b..03622c94bc4e 100644 --- a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java +++ b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java @@ -1,238 +1,40 @@ package jenkins.security.s2m; +import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -import edu.umd.cs.findbugs.annotations.CheckReturnValue; -import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Extension; -import hudson.FilePath; -import hudson.Functions; -import hudson.Util; -import hudson.util.HttpResponses; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.nio.charset.Charset; -import java.util.Collection; -import java.util.Enumeration; import java.util.logging.Logger; -import jenkins.model.Jenkins; -import jenkins.util.io.FileBoolean; -import org.apache.commons.io.FileUtils; -import org.jenkinsci.remoting.Role; -import org.jenkinsci.remoting.RoleSensitive; -import org.kohsuke.stapler.HttpResponse; -import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.StaplerProxy; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.interceptor.RequirePOST; /** - * Rules of whitelisting for {@link RoleSensitive} objects and {@link FilePath}s. + * @deprecated This class no longer has any effect. + * Support for allowlisting {@link hudson.remoting.Callable}s predating the introduction of the {@link org.jenkinsci.remoting.RoleSensitive} interface for SECURITY-144 in 2014 has been dropped. + * {@link hudson.FilePath} no longer supports being invoked from agents at all, so {@code FilePathFilter} etc. have been removed. + * + * @see Agent-to-controller security subsystem documentation * - * @author Kohsuke Kawaguchi */ +@Deprecated @Extension -public class AdminWhitelistRule implements StaplerProxy { - /** - * Ones that we rejected but want to run by admins. - */ - public final CallableRejectionConfig rejected; - - /** - * Callables that admins have whitelisted explicitly. - */ - public final CallableWhitelistConfig whitelisted; - - /** - * FilePath access pattern rules specified by the admin - */ - public final FilePathRuleConfig filePathRules; - - private boolean masterKillSwitch; - - public AdminWhitelistRule() throws IOException, InterruptedException { - final Jenkins jenkins = Jenkins.get(); - - // while this file is not a secret, write access to this file is dangerous, - // so put this in the better-protected part of $JENKINS_HOME, which is in secrets/ - - // overwrite 30-default.conf with what we think is the best from the core. - // this file shouldn't be touched by anyone. For local customization, use other files in the conf dir. - // 0-byte file is used as a signal from the admin to prevent this overwriting - try (InputStream callable = getClass().getResourceAsStream("callable.conf")) { - placeDefaultRule( - new File(jenkins.getRootDir(), "secrets/whitelisted-callables.d/default.conf"), - callable); - } - try (InputStream filepathFilter = getClass().getResourceAsStream("filepath-filter.conf")) { - placeDefaultRule( - new File(jenkins.getRootDir(), "secrets/filepath-filters.d/30-default.conf"), - transformForWindows(filepathFilter)); - } - - this.whitelisted = new CallableWhitelistConfig( - new File(jenkins.getRootDir(),"secrets/whitelisted-callables.d/gui.conf")); - this.rejected = new CallableRejectionConfig( - new File(jenkins.getRootDir(),"secrets/rejected-callables.txt"), - whitelisted); - this.filePathRules = new FilePathRuleConfig( - new File(jenkins.getRootDir(),"secrets/filepath-filters.d/50-gui.conf")); - - File f = getMasterKillSwitchFile(jenkins); - this.masterKillSwitch = loadMasterKillSwitchFile(f); - } - - /** - * Reads the master kill switch from a file. - * - * Instead of {@link FileBoolean}, we use a text file so that the admin can prevent Jenkins from - * writing this to file. - * @param f File to load - * @return {@code true} if the file was loaded, {@code false} otherwise - */ - @CheckReturnValue - private boolean loadMasterKillSwitchFile(@NonNull File f) { - try { - if (!f.exists()) return true; - return Boolean.parseBoolean(FileUtils.readFileToString(f, Charset.defaultCharset()).trim()); - } catch (IOException e) { - LOGGER.log(WARNING, "Failed to read "+f, e); - return false; - } - } - - @NonNull - private File getMasterKillSwitchFile(@NonNull Jenkins jenkins) { - return new File(jenkins.getRootDir(),"secrets/slave-to-master-security-kill-switch"); - } - - /** - * Transform path for Windows. - */ - private InputStream transformForWindows(InputStream src) throws IOException { - BufferedReader r = new BufferedReader(new InputStreamReader(src)); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (PrintStream p = new PrintStream(out)) { - String line; - while ((line = r.readLine()) != null) { - if (!line.startsWith("#") && Functions.isWindows()) - line = line.replace("/", "\\\\"); - p.println(line); - } - } - return new ByteArrayInputStream(out.toByteArray()); - } - - private void placeDefaultRule(File f, InputStream src) throws IOException, InterruptedException { - try { - new FilePath(f).copyFrom(src); - } catch (IOException e) { - // we allow admins to create a read-only file here to block overwrite, - // so this can fail legitimately - if (!f.canWrite()) return; - LOGGER.log(WARNING, "Failed to generate "+f,e); - } - } +public class AdminWhitelistRule { - public boolean isWhitelisted(RoleSensitive subject, Collection expected, Object context) { - if (masterKillSwitch) - return true; // master kill switch is on. subsystem deactivated - - String name = subject.getClass().getName(); - - if (whitelisted.contains(name)) - return true; // whitelisted by admin - - // otherwise record the problem and refuse to execute that - rejected.report(subject.getClass()); - return false; - } - - public boolean checkFileAccess(String op, File f) { - // if the master kill switch is off, we allow everything - if (masterKillSwitch) - return true; - - return filePathRules.checkFileAccess(op, f); - } - - @RequirePOST - public HttpResponse doSubmit(StaplerRequest req) throws IOException { - StringBuilder whitelist = new StringBuilder(Util.fixNull(req.getParameter("whitelist"))); - if (whitelist.length() > 0 && whitelist.charAt(whitelist.length() - 1) != '\n') - whitelist.append("\n"); - - Enumeration e = req.getParameterNames(); - while (e.hasMoreElements()) { - String name = (String) e.nextElement(); - if (name.startsWith("class:")) { - whitelist.append(name.substring(6)).append("\n"); - } - } - - whitelisted.set(whitelist.toString()); - - String newRules = Util.fixNull(req.getParameter("filePathRules")); - filePathRules.parseTest(newRules); // test first before writing a potentially broken rules - filePathRules.set(newRules); - - return HttpResponses.redirectToDot(); - } - - /** - * Approves all the currently rejected subjects - */ - @RequirePOST - public HttpResponse doApproveAll() throws IOException { - StringBuilder buf = new StringBuilder(); - for (Class c : rejected.get()) { - buf.append(c.getName()).append('\n'); - } - whitelisted.append(buf.toString()); - - return HttpResponses.ok(); - } - - /** - * Approves specific callables by their names. - */ - @RequirePOST - public HttpResponse doApprove(@QueryParameter String value) throws IOException { - whitelisted.append(value); - return HttpResponses.ok(); + public AdminWhitelistRule() { } public boolean getMasterKillSwitch() { - return masterKillSwitch; + LOGGER.log(WARNING, "AdminWhitelistRule no longer has any effect but an attempt was made to read its current configuration value. See https://www.jenkins.io/redirect/AdminWhitelistRule to learn more.", new Exception()); + return false; } public void setMasterKillSwitch(boolean state) { - final Jenkins jenkins = Jenkins.get(); - try { - jenkins.checkPermission(Jenkins.ADMINISTER); - File f = getMasterKillSwitchFile(jenkins); - FileUtils.writeStringToFile(f, Boolean.toString(state), Charset.defaultCharset()); - // treat the file as the canonical source of information in case write fails - masterKillSwitch = loadMasterKillSwitchFile(f); - } catch (IOException e) { - LOGGER.log(WARNING, "Failed to write master kill switch", e); + if (state) { + // an attempt to disable protections should warn + LOGGER.log(WARNING, "Setting AdminWhitelistRule no longer has any effect. See https://www.jenkins.io/redirect/AdminWhitelistRule to learn more.", new Exception()); + } else { + // This is basically no-op + LOGGER.log(INFO, "Setting AdminWhitelistRule no longer has any effect. See https://www.jenkins.io/redirect/AdminWhitelistRule to learn more.", new Exception()); } } - /** - * Restricts the access to administrator. - */ - @Override - public Object getTarget() { - Jenkins.get().checkPermission(Jenkins.ADMINISTER); - return this; - } - private static final Logger LOGGER = Logger.getLogger(AdminWhitelistRule.class.getName()); } diff --git a/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java index 5f05307ed059..bb3807341add 100644 --- a/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java @@ -19,17 +19,13 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; /** - * Inspects {@link Callable}s that run on the master. + * Inspects {@link Callable}s that run on the controller. * * @author Kohsuke Kawaguchi * @since 1.587 / 1.580.1 */ @Restricted(NoExternalUse.class) // used implicitly via listener public class CallableDirectionChecker extends RoleChecker { - /** - * Context parameter given to {@link ChannelConfigurator#onChannelBuilding(ChannelBuilder, Object)}. - */ - private final Object context; private static final String BYPASS_PROP = CallableDirectionChecker.class.getName()+".allow"; @@ -44,10 +40,6 @@ public class CallableDirectionChecker extends RoleChecker { @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console") public static boolean BYPASS = SystemProperties.getBoolean(BYPASS_PROP); - private CallableDirectionChecker(Object context) { - this.context = context; - } - @Override public void check(RoleSensitive subject, @NonNull Collection expected) throws SecurityException { final String name = subject.getClass().getName(); @@ -63,26 +55,14 @@ public void check(RoleSensitive subject, @NonNull Collection expected) thr return; } - if (isWhitelisted(subject,expected)) { - // this subject is dubious, but we are letting it through as per whitelisting - LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from agent to controller", name); + if (BYPASS) { + LOGGER.log(Level.FINE, "Allowing {0} to be sent from agent to controller because bypass is set", name); return; } throw new SecurityException("Sending " + name + " from agent to controller is prohibited.\nSee https://www.jenkins.io/redirect/security-144 for more details"); } - /** - * Is this subject class name whitelisted? - */ - private boolean isWhitelisted(RoleSensitive subject, Collection expected) { - for (CallableWhitelist w : CallableWhitelist.all()) { - if (w.isWhitelisted(subject, expected, context)) - return true; - } - return false; - } - /** * Installs {@link CallableDirectionChecker} to every channel. */ @@ -97,20 +77,7 @@ public void onChannelBuilding(ChannelBuilder builder, Object context) { builder.withRemoteClassLoadingAllowed(false); } // In either of the above cases, the check method will return normally, but may log things. - builder.withRoleChecker(new CallableDirectionChecker(context)); - } - } - - /** - * Whitelist rule based on system properties. - * - * For the bypass "kill" switch to be effective, it needs to have a high enough priority - */ - @Extension(ordinal=100) - public static class DefaultWhitelist extends CallableWhitelist { - @Override - public boolean isWhitelisted(RoleSensitive subject, Collection expected, Object context) { - return BYPASS; + builder.withRoleChecker(new CallableDirectionChecker()); } } diff --git a/core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java b/core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java deleted file mode 100644 index 29ae9d4d0750..000000000000 --- a/core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java +++ /dev/null @@ -1,78 +0,0 @@ -package jenkins.security.s2m; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import jenkins.model.Jenkins; - -/** - * Text file that lists whitelisted callables. - * - * @author Kohsuke Kawaguchi - */ -public class CallableRejectionConfig extends ConfigFile> { - private final CallableWhitelistConfig whitelist; - - CallableRejectionConfig(File file, CallableWhitelistConfig whitelist) { - super(file); - this.whitelist = whitelist; - } - - @Override - protected Set create() { - return new HashSet<>(); - } - - @Override - protected Set readOnly(Set base) { - return Collections.unmodifiableSet(new HashSet<>(base)); - } - - @Override - protected Class parse(String line) { - try { - line = line.trim(); - if (whitelist.contains(line)) return null; // already whitelisted - - return Jenkins.get().pluginManager.uberClassLoader.loadClass(line); - } catch (ClassNotFoundException e) { - // no longer present in the system? - return null; - } - } - - /** - * This method gets called every time we see a new type of callable that we reject, - * so that we can persist the list. - */ - void report(Class c) { - if (!get().contains(c)) { - try { - append(c.getName()); - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Failed to persist " + file, e); - } - } - } - - /** - * Return the object that helps the UI rendering by providing the details. - */ - public List describe() { - List l = new ArrayList<>(); - for (Class c : get()) { - if (!whitelist.contains(c.getName())) - l.add(new RejectedCallable(c)); - } - return l; - } - - - private static final Logger LOGGER = Logger.getLogger(CallableRejectionConfig.class.getName()); -} diff --git a/core/src/main/java/jenkins/security/s2m/CallableWhitelist.java b/core/src/main/java/jenkins/security/s2m/CallableWhitelist.java deleted file mode 100644 index 7be48452c796..000000000000 --- a/core/src/main/java/jenkins/security/s2m/CallableWhitelist.java +++ /dev/null @@ -1,47 +0,0 @@ -package jenkins.security.s2m; - -import hudson.ExtensionList; -import hudson.ExtensionPoint; -import hudson.remoting.Callable; -import hudson.remoting.ChannelBuilder; -import java.util.Collection; -import jenkins.model.Jenkins; -import jenkins.security.ChannelConfigurator; -import org.jenkinsci.remoting.Role; -import org.jenkinsci.remoting.RoleChecker; -import org.jenkinsci.remoting.RoleSensitive; - -/** - * Used on the master to selectively allow specific {@link Callable}s to execute on the master - * even when those {@link Callable}s do not have proper {@link Role} declarations from its - * {@link Callable#checkRoles(RoleChecker)} method. - * - * @author Kohsuke Kawaguchi - * @since 1.587 / 1.580.1 - */ -public abstract class CallableWhitelist implements ExtensionPoint { - /** - * Returns true if given {@code subject} should be allowed to execute on the master even though - * it came over channel from other JVMs. - * - * @param subject - * See {@link RoleChecker#check(RoleSensitive, Collection)} - * @param expected - * See {@link RoleChecker#check(RoleSensitive, Collection)} - * @param context - * Parameter given to {@link ChannelConfigurator#onChannelBuilding(ChannelBuilder, Object)} - * @return - * true to allow this subject to execute. No further {@link CallableWhitelist} is consulted - * when this method returns true. - * false to "-0" this subject. Other {@link CallableWhitelist}s will be given a chance to - * accept/reject this subject, and if no one accepts it, the subject will be rejected. - * @throws SecurityException - * to blacklist a subject, throw this exception. No further {@link CallableWhitelist} is consulted, - * and the execution will be rejected. - */ - public abstract boolean isWhitelisted(RoleSensitive subject, Collection expected, Object context); - - public static ExtensionList all() { - return Jenkins.get().getExtensionList(CallableWhitelist.class); - } -} diff --git a/core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java b/core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java deleted file mode 100644 index 0eb434554dbf..000000000000 --- a/core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -package jenkins.security.s2m; - -import hudson.Util; -import java.io.File; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import org.jenkinsci.remoting.RoleSensitive; - -/** - * Set of fully-qualified {@link RoleSensitive} (mostly Callable) class names that are whitelisted by admin. - * - * @author Kohsuke Kawaguchi - */ -class CallableWhitelistConfig extends ConfigDirectory> { - CallableWhitelistConfig(File file) { - super(file); - } - - @Override - protected Set create() { - return new HashSet<>(); - } - - @Override - protected Set readOnly(Set base) { - return Collections.unmodifiableSet(new HashSet<>(base)); - } - - @Override - protected String parse(String line) { - return Util.fixEmptyAndTrim(line); - } - - public boolean contains(String name) { - return get().contains(name); - } -} diff --git a/core/src/main/java/jenkins/security/s2m/ConfigDirectory.java b/core/src/main/java/jenkins/security/s2m/ConfigDirectory.java deleted file mode 100644 index cbfa699e3acf..000000000000 --- a/core/src/main/java/jenkins/security/s2m/ConfigDirectory.java +++ /dev/null @@ -1,58 +0,0 @@ -package jenkins.security.s2m; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Bit of a hack to expand {@link ConfigFile} to support conf.d format that assembles the fragment. - * - *

      - * {@link #file} points to the "primary" file that we programmatically write to. - * - * @author Kohsuke Kawaguchi - */ -abstract class ConfigDirectory> extends ConfigFile { - private final File dir; - - protected ConfigDirectory(File file) { - super(file); - this.dir = file.getParentFile(); - } - - @Override - public synchronized void load2() { - COL result = create(); - - if (dir.exists()) { - String[] fragments = dir.list((dir, name) -> name.endsWith(".conf")); - if (fragments!=null) { - Arrays.sort(fragments); - - for (String fragment : fragments) { - File f = new File(dir, fragment); - try (BufferedReader reader = new BufferedReader(new FileReader(f))) { - String line; - while ((line=reader.readLine())!=null) { - if (line.startsWith("#")) continue; // comment - T r = parse(line); - if (r != null) - result.add(r); - } - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Failed to parse "+f,e); - } - } - } - } - - parsed = readOnly(result); - } - - private static final Logger LOGGER = Logger.getLogger(ConfigDirectory.class.getName()); -} diff --git a/core/src/main/java/jenkins/security/s2m/ConfigFile.java b/core/src/main/java/jenkins/security/s2m/ConfigFile.java deleted file mode 100644 index 6bec4a8ab3e5..000000000000 --- a/core/src/main/java/jenkins/security/s2m/ConfigFile.java +++ /dev/null @@ -1,122 +0,0 @@ -package jenkins.security.s2m; - -import hudson.CopyOnWrite; -import hudson.util.TextFile; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.util.Collection; -import java.util.stream.Stream; -import jenkins.model.Jenkins; - -/** - * Abstraction of a line-by-line configuration text file that gets parsed into some in-memory data form. - * - * @author Kohsuke Kawaguchi - */ -abstract class ConfigFile> extends TextFile { - @CopyOnWrite - protected volatile COL parsed; - - ConfigFile(File file) { - super(file); - } - - protected abstract COL create(); - protected abstract COL readOnly(COL base); - - /** - * Loads the configuration from the configuration file. - *

      - * This method is equivalent to {@link #load2()}, except that any - * {@link java.io.IOException} that occurs is wrapped as a - * {@link java.lang.RuntimeException}. - *

      - * This method exists for source compatibility. Users should call - * {@link #load2()} instead. - * @deprecated use {@link #load2()} instead. - */ - @Deprecated - public void load() { - try { - load2(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Loads the configuration from the configuration file. - * @throws IOException if the configuration file could not be read. - * @since 2.111 - */ - public synchronized void load2() throws IOException { - COL result = create(); - - if (exists()) { - try (Stream stream = lines()) { - stream.forEach(line -> { - if (line.startsWith("#")) return; // comment - T r = parse(line); - if (r != null) - result.add(r); - }); - } - } - - parsed = readOnly(result); - } - - /** - * Goes through the parser with the given text to make sure it doesn't yield any error. - */ - public void parseTest(String candidate) { - try { - BufferedReader r = new BufferedReader(new StringReader(candidate)); - String line; - while ((line=r.readLine())!=null) { - if (line.startsWith("#")) continue; // comment - parse(line); - } - } catch (IOException e) { - throw new IllegalArgumentException(e); // can't happen but just in case - } - } - - protected abstract T parse(String line); - - public synchronized void set(String newContent) throws IOException { - Jenkins.get().checkPermission(Jenkins.ADMINISTER); - - write(newContent); - load2(); - } - - public synchronized void append(String additional) throws IOException { - if (!exists()) { - set(additional); - return; - } - String s = read(); - if (!s.endsWith("\n")) - s += "\n"; - s+= additional; - - set(s); - } - - public COL get() { - // load upon the first use - if (parsed==null) { - synchronized (this) { - if (parsed==null) { - load(); - } - } - } - return parsed; - } - - -} diff --git a/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java deleted file mode 100644 index a9ab17d2636b..000000000000 --- a/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * The MIT License - * - * Copyright 2014 Jesse Glick. - * - * 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.s2m; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import hudson.Extension; -import hudson.ExtensionList; -import hudson.remoting.ChannelBuilder; -import hudson.remoting.Command; -import hudson.remoting.Request; -import java.io.File; -import java.lang.reflect.Field; -import java.util.logging.Level; -import java.util.logging.Logger; -import jenkins.ReflectiveFilePathFilter; -import jenkins.security.ChannelConfigurator; -import jenkins.telemetry.impl.SlaveToMasterFileCallableUsage; -import jenkins.util.SystemProperties; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - -/** - * Blocks agents from writing to files on the master by default (and also provide the kill switch.) - */ -@Restricted(NoExternalUse.class) // impl -@Extension public class DefaultFilePathFilter extends ChannelConfigurator { - - /** - * Escape hatch to disable this check completely. - */ - @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console") - public static boolean BYPASS = SystemProperties.getBoolean(DefaultFilePathFilter.class.getName()+".allow"); - - private static final Logger LOGGER = Logger.getLogger(DefaultFilePathFilter.class.getName()); - - @Override - public void onChannelBuilding(ChannelBuilder builder, Object context) { - new ReflectiveFilePathFilter() { - @Override - protected boolean op(String op, File f) throws SecurityException { - if (BYPASS) { - LOGGER.log(Level.FINE, "agent allowed to {0} {1}", new Object[] {op, f}); - return true; - } else { - try { - Field current = Request.class.getDeclaredField("CURRENT"); - current.setAccessible(true); - Field createdAt = Command.class.getDeclaredField("createdAt"); - createdAt.setAccessible(true); - Throwable trace = (Throwable) createdAt.get(((ThreadLocal) current.get(null)).get()); - ExtensionList.lookupSingleton(SlaveToMasterFileCallableUsage.class).recordTrace(trace); - LOGGER.log(Level.WARNING, "Permitting agent-to-controller '" + op + "' on '" + f + "'. This is deprecated and will soon be rejected. Learn more: https://www.jenkins.io/redirect/permitted-agent-to-controller-file-access", trace); - } catch (Exception x) { - LOGGER.log(Level.WARNING, null, x); - } - return false; - } - } - }.installTo(builder, AdminFilePathFilter.ORDINAL+100); - // for the bypass switch to be effective, it should have a high priority - } -} diff --git a/core/src/main/java/jenkins/security/s2m/FilePathRule.java b/core/src/main/java/jenkins/security/s2m/FilePathRule.java deleted file mode 100644 index 010748897ade..000000000000 --- a/core/src/main/java/jenkins/security/s2m/FilePathRule.java +++ /dev/null @@ -1,21 +0,0 @@ -package jenkins.security.s2m; - -import hudson.FilePath; -import java.util.regex.Pattern; - -/** - * One entry of the {@link FilePath} access rule. - * - * @author Kohsuke Kawaguchi - */ -class FilePathRule { - final Pattern path; - final OpMatcher op; - final boolean allow; - - FilePathRule(Pattern path, OpMatcher op, boolean allow) { - this.path = path; - this.op = op; - this.allow = allow; - } -} diff --git a/core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java b/core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java deleted file mode 100644 index ab0a463d2c5e..000000000000 --- a/core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java +++ /dev/null @@ -1,121 +0,0 @@ -package jenkins.security.s2m; - -import static hudson.Functions.isWindows; - -import hudson.Functions; -import hudson.model.Failure; -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import jenkins.model.Jenkins; - -/** - * Config file that lists {@link FilePathRule} rules. - * - * @author Kohsuke Kawaguchi - */ -class FilePathRuleConfig extends ConfigDirectory> { - - private static final Logger LOGGER = Logger.getLogger(FilePathRuleConfig.class.getName()); - - FilePathRuleConfig(File file) { - super(file); - } - - @Override - protected List create() { - return new ArrayList<>(); - } - - @Override - protected List readOnly(List base) { - return Collections.unmodifiableList(new ArrayList<>(base)); - } - - @Override - protected FilePathRule parse(String line) { - line = line.trim(); - if (line.isEmpty()) return null; - - // TODO This does not support custom build dir configuration (Jenkins#getRawBuildsDir() etc.) - line = line.replace("","/builds/[0-9]+"); - - // Kept only for compatibility with custom user-provided rules: - line = line.replace("","(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]|[0-9]+)"); - line = line.replace("","/jobs/.+"); - final File jenkinsHome = Jenkins.get().getRootDir(); - try { - line = line.replace("","\\Q" + jenkinsHome.getCanonicalPath() + "\\E"); - } catch (IOException e) { - LOGGER.log(Level.WARNING, e, () -> "Failed to determine canonical path to Jenkins home directory, falling back to configured value: " + jenkinsHome.getPath()); - line = line.replace("","\\Q" + jenkinsHome.getPath() + "\\E"); - } - - // config file is always /-separated even on Windows, so bring it back to \-separation. - // This is done in the context of regex, so it has to be \\, which means in the source code it is \\\\ - if (isWindows()) line = line.replace("/","\\\\"); - - Matcher m = PARSER.matcher(line); - if (!m.matches()) - throw new Failure("Invalid filter rule line: "+line); - - try { - return new FilePathRule( - Pattern.compile(m.group(3)), - createOpMatcher(m.group(2)), - m.group(1).equals("allow")); - } catch (RuntimeException e) { - throw new Failure("Invalid filter rule line: "+line+"\n"+ Functions.printThrowable(e)); - } - } - - private OpMatcher createOpMatcher(String token) { - if (token.equals("all")) - return OpMatcher.ALL; - - final Set ops = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(token.split(",")))); - return ops::contains; - } - - public boolean checkFileAccess(String op, File path) throws SecurityException { - String pathStr = null; - - for (FilePathRule rule : get()) { - if (rule.op.matches(op)) { - if (pathStr==null) { - try { - pathStr = path.getCanonicalPath(); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - if (isWindows()) // Windows accepts '/' as separator, but for rule matching we want to normalize for consistent comparison - pathStr = pathStr.replace('/','\\'); - } - - if (rule.path.matcher(pathStr).matches()) { - // exclusion rule is only to bypass later path rules within #filePathRules, - // and we still want other FilePathFilters to whitelist/blacklist access. - // therefore I'm not throwing a SecurityException here - return rule.allow; - } - } - } - - return false; - } - - /** - * - */ - private static final Pattern PARSER = Pattern.compile("(allow|deny)\\s+([a-z,]+)\\s+(.*)"); -} diff --git a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java deleted file mode 100644 index bd906bc22335..000000000000 --- a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java +++ /dev/null @@ -1,70 +0,0 @@ -package jenkins.security.s2m; - -import edu.umd.cs.findbugs.annotations.NonNull; -import hudson.Extension; -import javax.inject.Inject; -import jenkins.model.GlobalConfiguration; -import jenkins.model.GlobalConfigurationCategory; -import jenkins.model.Jenkins; -import net.sf.json.JSONObject; -import org.kohsuke.stapler.StaplerRequest; - -/** - * Exposes {@code AdminWhitelistRule#masterKillSwitch} to the admin. - * - * @author Kohsuke Kawaguchi - * @since 1.587 / 1.580.1 - */ -@Extension -public class MasterKillSwitchConfiguration extends GlobalConfiguration { - @Inject - AdminWhitelistRule rule; - - @Inject - Jenkins jenkins; - - @Override - public @NonNull GlobalConfigurationCategory getCategory() { - return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class); - } - - /** - * @deprecated Use {@link #getAgentToControllerAccessControl()} instead - */ - @Deprecated - public boolean getMasterToSlaveAccessControl() { - return getAgentToControllerAccessControl(); - } - - /** - * @since 2.310 - */ - public boolean getAgentToControllerAccessControl() { - return !rule.getMasterKillSwitch(); - } - - @Override - public boolean configure(StaplerRequest req, JSONObject json) throws FormException { - if (isRelevant()) { - // don't record on/off unless this becomes relevant, so that we can differentiate - // those who have disabled vs those who haven't cared. - rule.setMasterKillSwitch(!json.has("agentToControllerAccessControl")); - } - return true; - } - - /** - * Returns true if the configuration of this subsystem is relevant. - * - *

      Historically, this was only shown when "security" (authn/authz) was enabled. - * That missed the use case of trusted local networks and Jenkins building public (untrusted) pull requests. - * To be sure we're not missing another case where this option is useful, just show it always.

      - */ - public boolean isRelevant() { - /* - * TODO Consider restricting this again to something like: - * return !jenkins.clouds.isEmpty() || !jenkins.getNodes().isEmpty(); - */ - return true; - } -} diff --git a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java deleted file mode 100644 index 10f142f6db3f..000000000000 --- a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java +++ /dev/null @@ -1,50 +0,0 @@ -package jenkins.security.s2m; - -import hudson.Extension; -import hudson.model.AdministrativeMonitor; -import java.io.IOException; -import javax.inject.Inject; -import org.kohsuke.stapler.HttpResponse; -import org.kohsuke.stapler.HttpResponses; -import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.interceptor.RequirePOST; - -/** - * If {@link AdminWhitelistRule#masterKillSwitch} is on, warn the user. - * - * @author Kohsuke Kawaguchi - * @since 1.587 / 1.580.1 - */ -@Extension -public class MasterKillSwitchWarning extends AdministrativeMonitor { - @Inject - AdminWhitelistRule rule; - - @Inject - MasterKillSwitchConfiguration config; - - @Override - public boolean isActivated() { - return rule.getMasterKillSwitch() && config.isRelevant(); - } - - @Override - public boolean isSecurity() { - return true; - } - - @Override - public String getDisplayName() { - return Messages.MasterKillSwitchWarning_DisplayName(); - } - - @RequirePOST - public HttpResponse doAct(@QueryParameter String dismiss) throws IOException { - if(dismiss!=null) { - disable(true); - return HttpResponses.redirectViaContextPath("/manage"); - } else { - return HttpResponses.redirectViaContextPath("configureSecurity"); - } - } -} diff --git a/core/src/main/java/jenkins/security/s2m/OpMatcher.java b/core/src/main/java/jenkins/security/s2m/OpMatcher.java deleted file mode 100644 index b78f1fc62316..000000000000 --- a/core/src/main/java/jenkins/security/s2m/OpMatcher.java +++ /dev/null @@ -1,15 +0,0 @@ -package jenkins.security.s2m; - -import java.io.File; -import jenkins.ReflectiveFilePathFilter; - -/** - * Tests a match against file operation name of {@link ReflectiveFilePathFilter#op(String, File)}. - * - * @author Kohsuke Kawaguchi - */ -interface OpMatcher { - boolean matches(String op); - - OpMatcher ALL = op -> true; -} diff --git a/core/src/main/java/jenkins/security/s2m/RejectedCallable.java b/core/src/main/java/jenkins/security/s2m/RejectedCallable.java deleted file mode 100644 index 42cb5b515b4f..000000000000 --- a/core/src/main/java/jenkins/security/s2m/RejectedCallable.java +++ /dev/null @@ -1,21 +0,0 @@ -package jenkins.security.s2m; - -import edu.umd.cs.findbugs.annotations.CheckForNull; -import hudson.PluginWrapper; -import jenkins.model.Jenkins; - -/** -* @author Kohsuke Kawaguchi -*/ -public /*for Jelly*/ class RejectedCallable { - public final Class clazz; - - /*package*/ RejectedCallable(Class clazz) { - this.clazz = clazz; - } - - public @CheckForNull - PluginWrapper getPlugin() { - return Jenkins.get().pluginManager.whichPlugin(clazz); - } -} diff --git a/core/src/main/java/jenkins/security/s2m/RunningBuildFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/RunningBuildFilePathFilter.java deleted file mode 100644 index 5e4d88757e75..000000000000 --- a/core/src/main/java/jenkins/security/s2m/RunningBuildFilePathFilter.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * The MIT License - * - * Copyright 2021 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.s2m; - -import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import hudson.Extension; -import hudson.model.Computer; -import hudson.model.Executor; -import hudson.model.Queue; -import hudson.model.Run; -import hudson.remoting.ChannelBuilder; -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import jenkins.ReflectiveFilePathFilter; -import jenkins.model.Jenkins; -import jenkins.security.ChannelConfigurator; -import jenkins.util.SystemProperties; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - -/** - * When an agent tries to access build directories on the controller, limit it to those for builds running on that agent. - * - * @since 2.319 - */ -@Restricted(NoExternalUse.class) -public class RunningBuildFilePathFilter extends ReflectiveFilePathFilter { - - /** - * By default, unauthorized accesses will result in a {@link SecurityException}. - * If this is set to {@code false}, instead just log a warning. - */ - private static final String FAIL_PROPERTY = RunningBuildFilePathFilter.class.getName() + ".FAIL"; - - /** - * Disables this filter entirely. - */ - private static final String SKIP_PROPERTY = RunningBuildFilePathFilter.class.getName() + ".SKIP"; - - private static final Logger LOGGER = Logger.getLogger(RunningBuildFilePathFilter.class.getName()); - - private final Object context; - - public RunningBuildFilePathFilter(Object context) { - this.context = context; - } - - @Override - protected boolean op(String name, File path) throws SecurityException { - if (SystemProperties.getBoolean(SKIP_PROPERTY)) { - LOGGER.log(Level.FINE, () -> "Skipping check for '" + name + "' on '" + path + "'"); - return false; - } - - final Jenkins jenkins = Jenkins.get(); - - String patternString; - try { - patternString = Jenkins.expandVariablesForDirectory(jenkins.getRawBuildsDir(), "(.+)", "\\Q" + Jenkins.get().getRootDir().getCanonicalPath().replace('\\', '/') + "\\E/jobs/(.+)") + "/[0-9]+(/.*)?"; - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Failed to obtain canonical path to Jenkins home directory", e); - throw new SecurityException("Failed to obtain canonical path"); // Minimal details - } - final Pattern pattern = Pattern.compile(patternString); - - String absolutePath; - try { - absolutePath = path.getCanonicalPath().replace('\\', '/'); - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Failed to obtain canonical path to '" + path + "'", e); - throw new SecurityException("Failed to obtain canonical path"); // Minimal details - } - if (!pattern.matcher(absolutePath).matches()) { - /* This is not a build directory, so another filter will take care of it */ - LOGGER.log(Level.FINE, "Not a build directory, so skipping: " + absolutePath); - return false; - } - - if (!(context instanceof Computer)) { - LOGGER.warning(() -> "Unrecognized context " + context + " rejected for " + name + " on " + path); - throw new SecurityException("Failed to discover context of access to build directory"); // Minimal details - } - Computer c = (Computer) context; - final Path thePath = path.getAbsoluteFile().toPath(); - for (Executor executor : c.getExecutors()) { - Run build = findRun(executor.getCurrentExecutable()); - if (build == null) { - continue; - } - final Path buildDir = build.getRootDir().getAbsoluteFile().toPath(); - // If the directory being accessed is for a build currently running on this node, allow it - if (thePath.startsWith(buildDir)) { - return false; - } - } - - final String computerName = c.getName(); - if (SystemProperties.getBoolean(FAIL_PROPERTY, true)) { - // This filter can only prohibit by throwing a SecurityException; it never allows on its own. - LOGGER.log(Level.WARNING, "Rejecting unexpected agent-to-controller file path access: Agent '" + computerName + "' is attempting to access '" + absolutePath + "' using operation '" + name + "'. Learn more: https://www.jenkins.io/redirect/security-144/"); - throw new SecurityException("Agent tried to access build directory of a build not currently running on this system. Learn more: https://www.jenkins.io/redirect/security-144/"); - } else { - LOGGER.log(Level.WARNING, "Unexpected agent-to-controller file path access: Agent '" + computerName + "' is accessing '" + absolutePath + "' using operation '" + name + "'. Learn more: https://www.jenkins.io/redirect/security-144/"); - return false; - } - } - - private static @CheckForNull Run findRun(@CheckForNull Queue.Executable exec) { - if (exec == null) { - return null; - } else if (exec instanceof Run) { - return (Run) exec; - } else { - return findRun(exec.getParentExecutable()); - } - } - - @Extension - public static class ChannelConfiguratorImpl extends ChannelConfigurator { - @Override - public void onChannelBuilding(ChannelBuilder builder, @Nullable Object context) { - new RunningBuildFilePathFilter(context).installTo(builder, 150.0); - } - } -} diff --git a/core/src/main/java/jenkins/security/s2m/package-info.java b/core/src/main/java/jenkins/security/s2m/package-info.java index 0339747c1692..03e604c5be4b 100644 --- a/core/src/main/java/jenkins/security/s2m/package-info.java +++ b/core/src/main/java/jenkins/security/s2m/package-info.java @@ -1,4 +1,4 @@ /** - * Agent → master security. + * Agent → controller security. */ package jenkins.security.s2m; diff --git a/core/src/main/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage.java b/core/src/main/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage.java deleted file mode 100644 index 2c577d64f5af..000000000000 --- a/core/src/main/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * The MIT License - * - * Copyright 2021 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.telemetry.impl; - -import hudson.Extension; -import hudson.Functions; -import java.time.LocalDate; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.regex.Matcher; -import jenkins.SlaveToMasterFileCallable; -import jenkins.security.s2m.DefaultFilePathFilter; -import jenkins.telemetry.Telemetry; -import net.sf.json.JSONObject; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - -/** - * Records when {@link DefaultFilePathFilter} found {@link SlaveToMasterFileCallable} or similar being used. - */ -@Extension -@Restricted(NoExternalUse.class) -public class SlaveToMasterFileCallableUsage extends Telemetry { - - private Set traces = new TreeSet<>(); - - @Override - public String getDisplayName() { - return "Access to files on controllers from code running on an agent"; - } - - @Override - public LocalDate getStart() { - return LocalDate.of(2021, 11, 4); // https://www.jenkins.io/security/advisory/2021-11-04/ - } - - @Override - public LocalDate getEnd() { - return LocalDate.of(2022, 3, 1); - } - - @Override - public synchronized JSONObject createContent() { - Map info = new TreeMap<>(); - info.put("traces", traces); - info.put("components", buildComponentInformation()); - JSONObject json = JSONObject.fromObject(info); - traces.clear(); - return json; - } - - public synchronized void recordTrace(Throwable trace) { - traces.add(generalize(Functions.printThrowable(trace))); - } - - protected static String generalize(String trace) { - return trace - .replaceAll("@[a-f0-9]+", "@…") - .replaceAll("]\\([0-9]+\\) created at", "](…) created at") - .replaceAll("com[.]sun[.]proxy[.][$]Proxy[0-9]+[.]", Matcher.quoteReplacement("com.sun.proxy.$Proxy….")); - } -} diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.jelly b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.jelly deleted file mode 100644 index f599db7a1b31..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.jelly +++ /dev/null @@ -1,34 +0,0 @@ - - - - -
      -
      - - - - ${%blurb} -
      -
      diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.properties deleted file mode 100644 index 89d5bfb1799b..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.properties +++ /dev/null @@ -1,2 +0,0 @@ -blurb=Jenkins has rejected some commands from agents, because we are unsure if it is open for agents to execute, \ - but this rejection has probably broken some builds. You should examine the situation to see if they should be whitelisted. diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_bg.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_bg.properties deleted file mode 100644 index 190c24f3282b..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_bg.properties +++ /dev/null @@ -1,32 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2016, Alexander Shopov -# -# 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. - -Dismiss=\ - \u041e\u0442\u043a\u0430\u0437 -# Jenkins has rejected some commands from agents, because we are unsure if it is open for agents to execute, \ -# but this rejection has probably broken some builds. You should examine the situation to see if they should be whitelisted. -blurb=\ - Jenkins \u043e\u0442\u0445\u0432\u044a\u0440\u043b\u0438 \u0447\u0430\u0441\u0442 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u0438\u0442\u0435 \u043e\u0442 \u0430\u0433\u0435\u043d\u0442\u0438\u0442\u0435, \u0437\u0430\u0449\u043e\u0442\u043e \u043d\u0435 \u0435 \u044f\u0441\u043d\u043e \u0434\u0430\u043b\u0438 \u043c\u043e\u0436\u0435 \u0434\u0430\ - \u0433\u0438 \u043f\u0440\u0438\u0435\u043c\u0430. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u043e \u0435 \u0442\u043e\u0432\u0430 \u0434\u0430 \u0435 \u0441\u0447\u0443\u043f\u0438\u043b\u043e \u0447\u0430\u0441\u0442 \u043e\u0442 \u0438\u0437\u0433\u0440\u0430\u0436\u0434\u0430\u043d\u0438\u044f\u0442\u0430. \u041f\u0440\u0435\u0433\u043b\u0435\u0434\u0430\u0439\u0442\u0435 \u0433\u0438,\ - \u0437\u0430 \u0434\u0430 \u0440\u0435\u0448\u0438\u0442\u0435 \u0434\u0430\u043b\u0438 \u0438\u0437\u0440\u0438\u0447\u043d\u043e \u0434\u0430 \u043d\u0435 \u0433\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u0435. -Examine=\ - \u041f\u0440\u0435\u0433\u043b\u0435\u0434 diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_de.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_de.properties deleted file mode 100644 index dd2a59ad1aee..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_de.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017 Daniel Beck and a number of other of contributors -# -# 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. - -Dismiss=Schlie\u00DFen -Examine=Pr\u00FCfen -blurb=Jenkins hat Befehle von Agenten aufgrund der aktuellen Einstellungen abgelehnt. Dadurch sind vermutlich Builds fehlgeschlagen. \ - Es wird empfohlen, die Situation zu pr\u00FCfen, und die abgelehnten Befehle ggf. in die Positivliste aufzunehmen. diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_it.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_it.properties deleted file mode 100644 index 1c88cf915c28..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_it.properties +++ /dev/null @@ -1,29 +0,0 @@ -# The MIT License -# -# Italian localization plugin for Jenkins -# Copyright 2020 Alessandro Menti -# -# 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. - -blurb=Jenkins ha rifiutato alcuni comandi dagli agenti perch non certo \ - che siano liberamente eseguibili da questi, ma tale rifiuto probabilmente \ - ha mandato in errore alcune compilazioni. Si dovrebbe esaminare la \ - situazione per verificare se sia possibile aggiungerli alla whitelist. -Dismiss=Nascondi -Examine=Esamina diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_pt_BR.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_pt_BR.properties deleted file mode 100644 index 25a215256fc0..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_pt_BR.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -Examine= -Dismiss= diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_sr.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_sr.properties deleted file mode 100644 index e2deed27ee89..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_sr.properties +++ /dev/null @@ -1,6 +0,0 @@ -# This file is under the MIT License by authors - -Examine=\u041F\u0440\u0435\u0433\u043B\u0435\u0434\u0430\u0458 -Dismiss=\u041E\u0442\u043A\u0430\u0436\u0438 -blurb=Jenkins \u0458\u0435 \u043E\u0434\u0431\u0438\u0458\u043E \u043D\u0435\u043A\u0435 \u0430\u0433\u0435\u043D\u0442\u043E\u0432\u0435 \u043A\u043E\u043C\u0430\u043D\u0434\u0435, \u0437\u0430\u0448\u0442\u043E \u043D\u0438\u0458\u0435 \u0458\u0430\u0441\u043D\u043E \u0430\u043A\u043E \u0458\u0435 \u0434\u043E\u0437\u0432\u043E\u0459\u0435\u043D\u043E \u0430\u0433\u0435\u043D\u0442\u0438\u043C\u0430, \ - \u043C\u0435\u0452\u0443\u0442\u0438\u043C \u043E\u0432\u0430 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0458\u0430 \u0458\u0435 \u0432\u0435\u0440\u043E\u0432\u0430\u0442\u043D\u043E \u043F\u0440\u0435\u043A\u0438\u043D\u0443\u043B\u0430 \u043D\u0435\u043A\u0430 \u0438\u0437\u0433\u0440\u0430\u0434\u045A\u0430. \u0420\u0430\u0437\u043C\u0438\u0441\u043B\u0438\u0442\u0435 \u0430\u043A\u043E \u0431\u0438 \u043C\u043E\u0433\u043E \u0434\u043E\u0437\u0432\u043E\u043B\u0438\u0442\u0438 \u0430\u0433\u0435\u043D\u0442\u0438\u043C\u0430. diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly deleted file mode 100644 index 14dd6056c2d4..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - -

      ${%Agent → Controller Access Control}

      - -

      - The Jenkins controller is now more strict about what commands its agents can send to the controller. - Unfortunately, this prevents some plugins from functioning correctly, as those plugins do not - specify which commands are open for agents to execute and which ones are not. - While plugin developers work on improving this, - as an administrator, you can mark commands as OK for agents to execute. -

      -
      - Please see the discussion of this feature to - understand the security implication of this. -
      - -
      - The agent → controller access control subsystem is currently disabled. - This is unsafe if you have agents from other less trusted people. - You can turn it back on from Global Security Configuration UI. -
      -
      - -
      -

      Currently Allowed Commands

      -

      - Agents are currently allowed to execute the following commands on the controller. -

      - - - - - -

      Currently Rejected Commands

      -

      - Agents have attempted to use the following functionalities but the plugins that implement them - did not specify whether or not they should be usable from agents. Check ones you want to allow to be executed: -

      -
        - -
      • - ${r.clazz.name} - - - - - - - (from core) - - - (from ${plugin.longName}) - - -
      • -
        -
      -
      - -

      File Access Rules

      -

      - The following rules control which part of $JENKINS_HOME can be accessed from agents: -

      - -

      - Example: -

      -
      -# deny access to anything in the SkunkWorks folder, regardless of what later rules say
      -deny read &lt;JENKINS_HOME>/jobs/SkunkWorks/.*
      -
      -# allow any access under builds
      -allow all &lt;BUILDDIR>/.*
      -
      -# allow read-only access under userContent
      -allow read,stat &lt;JENKINS_HOME>/userContent/.*
      -
      -

      - General syntax: (allow|deny) Op,Op,Op,... PathRegExp, -

      -
      - -
      - -
      -
      -
      diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_bg.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_bg.properties deleted file mode 100644 index 79e58cf1d1a8..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_bg.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2016, Alexander Shopov -# -# 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. - -Whitelist=\ - \u041e\u0434\u043e\u0431\u0440\u0435\u043d \u0441\u043f\u0438\u0441\u044a\u043a -Update=\ - \u041e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435 -Agent\ &\#8594;\ Master\ Access\ Control=\ - \u0410\u0433\u0435\u043d\u0442 \u2192 \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u0434\u043e\u0441\u0442\u044a\u043f\u0430 diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_de.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_de.properties deleted file mode 100644 index 0f7ba5afe444..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_de.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017 Daniel Beck and a number of other of contributors -# -# 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. - -Whitelist=Positivliste (Whitelist) -Agent\ &\#8594;\ Master\ Access\ Control=Zugangskontrolle Agent → Master -Update=Aktualisieren diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_it.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_it.properties deleted file mode 100644 index 0d5f667b67f0..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_it.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Italian localization plugin for Jenkins -# Copyright 2020 Alessandro Menti -# -# 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. - -Agent\ &\#8594;\ Master\ Access\ Control=Controllo accessi agente→master -Update=Aggiorna -Whitelist=Whitelist diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_ja.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_ja.properties deleted file mode 100644 index a4a88ca399f0..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_ja.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -Whitelist=\u30db\u30ef\u30a4\u30c8\u30ea\u30b9\u30c8 -Update=\u66f4\u65b0 diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_pt_BR.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_pt_BR.properties deleted file mode 100644 index 42d23f6efe16..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_pt_BR.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2015, Seiji Sogabe -# -# 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. - -Whitelist= -Update= diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_sr.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_sr.properties deleted file mode 100644 index 605e8f9eda41..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_sr.properties +++ /dev/null @@ -1,5 +0,0 @@ -# This file is under the MIT License by authors - -Whitelist= -Agent\ \u2192\ Master\ Access\ Control= -Update=\u0410\u0436\u0443\u0440\u0438\u0440\u0430\u0458 diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy deleted file mode 100644 index 40d93c8fd986..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package jenkins.security.s2m.MasterKillSwitchConfiguration - -def f=namespace(lib.FormTagLib) - -if (instance.isRelevant()) { - f.section(title: _('Agent \u2192 Controller Security')) { - f.optionalBlock(field: "agentToControllerAccessControl", title: _("Enable Agent \u2192 Controller Access Control")) { - f.nested() { - raw _("Rules can be tweaked here") - } - } - } -} diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config_it.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config_it.properties deleted file mode 100644 index 5445c9b31d4e..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config_it.properties +++ /dev/null @@ -1,29 +0,0 @@ -# The MIT License -# -# Italian localization plugin for Jenkins -# Copyright 2020 Alessandro Menti -# -# 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. - -Agent\ \\u2192\ Master\ Security=Sicurezza agente\u2192master -Enable\ Agent\ \\u2192\ Master\ Access\ Control=Abilita controllo accessi \ - agente\u2192master -Rules\ can\ be\ tweaked\ here=\ - possibile personalizzare le regole qui diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl.html b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl.html deleted file mode 100644 index ab058fba3e8e..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl.html +++ /dev/null @@ -1,4 +0,0 @@ -
      - See Jenkins project website for discussion of this feature. - We strongly recommend you enable this. -
      diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_bg.html b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_bg.html deleted file mode 100644 index 2b58243985f5..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_bg.html +++ /dev/null @@ -1,4 +0,0 @@ -
      - За повече информация вижте сайта на Jenkins. - Силно препоръчваме да включите тази настройка. -
      diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_it.html b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_it.html deleted file mode 100644 index 9e724361f467..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_it.html +++ /dev/null @@ -1,4 +0,0 @@ -
      - Si veda il sito Web del progetto Jenkins per una trattazione di questa funzionalità. - Raccomandiamo caldamente di abilitarla. -
      diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.jelly b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.jelly deleted file mode 100644 index de2ebcb9db8f..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.jelly +++ /dev/null @@ -1,34 +0,0 @@ - - - - -
      -
      - - - - ${%blurb} -
      -
      diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties deleted file mode 100644 index 04be9aaf9347..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties +++ /dev/null @@ -1,2 +0,0 @@ -blurb=Agent to controller security subsystem is currently off. \ - Please read the documentation and consider turning it on. diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_bg.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_bg.properties deleted file mode 100644 index a8091c3b1914..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_bg.properties +++ /dev/null @@ -1,32 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2016, 2017, Alexander Shopov -# -# 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. - -Dismiss=\ - \u041e\u0442\u043a\u0430\u0437 -Examine=\ - \u041f\u0440\u0435\u0433\u043b\u0435\u0434 -# Agent to master security subsystem is currently off. \ -# Please read the documentation and consider turning it on. -blurb=\ - \u0410\u0433\u0435\u043d\u0442\u044a\u0442 \u0437\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u0442\u0430 \u043f\u043e\u0434\u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0437\u0430 \u0441\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u0442\u0442\u0430 \u0435 \u0438\u0437\u043a\u043b\u044e\u0447\u0435\u043d \u0432 \u043c\u043e\u043c\u0435\u043d\u0442\u0430.\ - \u041f\u0440\u0435\u0433\u043b\u0435\u0434\u0430\u0439\u0442\u0435\ - \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u0433\u043e \u0432\u043a\u043b\u044e\u0447\u0435\u0442\u0435. diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_de.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_de.properties deleted file mode 100644 index 24f77098d88f..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_de.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017 Daniel Beck and a number of other of contributors -# -# 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. - -Examine=Pr\u00FCfen -Dismiss=Schlie\u00DFen -blurb=Das Agent-Master-Sicherheits-Subsystem ist aktuell ausgeschaltet und sollte eingeschaltet werden. \ - Bitte lesen Sie die Dokumentation. diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_it.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_it.properties deleted file mode 100644 index 37994252bcde..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_it.properties +++ /dev/null @@ -1,29 +0,0 @@ -# The MIT License -# -# Italian localization plugin for Jenkins -# Copyright 2020 Alessandro Menti -# -# 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. - -blurb=Il sottosistema di sicurezza da agente a master attualmente \ - disabilitato. Si legga la documentazione e si prenda in \ - considerazione l''ipotesi di abilitarlo. -Dismiss=Nascondi -Examine=Esamina diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_pt_BR.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_pt_BR.properties deleted file mode 100644 index 01a6d07f6f3d..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_pt_BR.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers -# -# 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. - -Examine= -Dismiss= -blurb=O agente para o master security subsystem est\u00e1 desligado. \ - Por favor leia a documenta\u00e7\u00e3o e ligue novamente. diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_sr.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_sr.properties deleted file mode 100644 index 9d3bdc19a71a..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_sr.properties +++ /dev/null @@ -1,6 +0,0 @@ -# This file is under the MIT License by authors - -Examine= -Dismiss=\u041E\u0442\u043A\u0430\u0436\u0438 -blurb=\u0421\u0438\u0441\u0442\u0435\u043C \u043E\u0431\u0435\u0437\u0431\u0435\u0436\u0438\u0432\u0430\u045A\u0430 \u0432\u0435\u0437\u043E\u043C \u0438\u0437\u043C\u0435\u0452\u0443 \u0430\u0433\u0435\u043D\u0442\u0430 \u0438 \u043C\u0430\u0441\u0442\u0435\u0440\u0430 \u0458\u0435 \u0438\u0441\u043A\u0459\u0443\u0447\u0435\u043D. \ - \u041C\u043E\u043B\u0438\u043C\u043E \u0432\u0430\u0441, \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0458\u0442\u0435 \u0434\u043E\u043A\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u0458\u0443 \u0438 \u0443\u043A\u0459\u0443\u0447\u0438\u0442\u0435 \u0430\u043A\u043E \u0432\u0430\u043C \u0458\u0435 \u043F\u043E\u0442\u0440\u0435\u0431\u043D\u043E. diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_tr.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_tr.properties deleted file mode 100644 index 4a99fc431b8e..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_tr.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2021, Mustafa Ulu -# -# 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. - -Examine=Denetle -Dismiss=Reddet diff --git a/core/src/main/resources/jenkins/security/s2m/Messages.properties b/core/src/main/resources/jenkins/security/s2m/Messages.properties deleted file mode 100644 index e2b2c28f3bd3..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/Messages.properties +++ /dev/null @@ -1,2 +0,0 @@ -AdminCallableMonitor.DisplayName=Rejected Agent \u2192 Controller Access Attempt -MasterKillSwitchWarning.DisplayName=Disabled Agent \u2192 Controller Access Control diff --git a/core/src/main/resources/jenkins/security/s2m/Messages_bg.properties b/core/src/main/resources/jenkins/security/s2m/Messages_bg.properties deleted file mode 100644 index 199b5d56ec12..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/Messages_bg.properties +++ /dev/null @@ -1,28 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2017, Alexander Shopov -# -# 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. - -# Rejected Agent \u2192 Controller Access Attempt -AdminCallableMonitor.DisplayName=\ - \u041e\u0442\u0445\u0432\u044a\u0440\u043b\u0435\u043d \u043f\u043e\u0434\u0447\u0438\u043d\u0435\u043d \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440 \u2014 \u043e\u043f\u0438\u0442 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f \u0434\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440 -# Disabled Agent \u2192 Controller Access Control -MasterKillSwitchWarning.DisplayName=\ - \u0418\u0437\u043a\u043b\u044e\u0447\u0435\u043d \u043f\u043e\u0434\u0447\u0438\u043d\u0435\u043d \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440 \u2014 \u043a\u043e\u043d\u0442\u0440\u043e\u043b \u043d\u0430 \u0434\u043e\u0441\u0442\u044a\u043f\u0430 \u0434\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440 diff --git a/core/src/main/resources/jenkins/security/s2m/Messages_de.properties b/core/src/main/resources/jenkins/security/s2m/Messages_de.properties deleted file mode 100644 index 162be628503e..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/Messages_de.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017 Daniel Beck and a number of other of contributors -# -# 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. - -AdminCallableMonitor.DisplayName=Zugriffsversuch Agent \u2192 Master abgelehnt -MasterKillSwitchWarning.DisplayName=Sicherheitsfunktionen Agent \u2192 Master deaktiviert diff --git a/core/src/main/resources/jenkins/security/s2m/Messages_it.properties b/core/src/main/resources/jenkins/security/s2m/Messages_it.properties deleted file mode 100644 index 2c259e5b976a..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/Messages_it.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Italian localization plugin for Jenkins -# Copyright 2020 Alessandro Menti -# -# 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. - -AdminCallableMonitor.DisplayName=Tentativo di accesso agente\u2192master \ - rifiutato -MasterKillSwitchWarning.DisplayName=Controllo accessi agente\u2192master \ - disabilitato diff --git a/core/src/main/resources/jenkins/security/s2m/callable.conf b/core/src/main/resources/jenkins/security/s2m/callable.conf deleted file mode 100644 index 440e02d19fc2..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/callable.conf +++ /dev/null @@ -1,29 +0,0 @@ -# GENERATED FILE. DO NOT MODIFY. -# -# This file is for Jenkins core developers to list plugin callables which we believe are safe. -# -# To whitelist other names, place *.conf files by other names into this folder. -# This file gets overwritten every time Jenkins starts. -# -# See https://www.jenkins.io/redirect/security-144 for more details. - -# maven plugin -hudson.maven.MavenBuildProxy$Filter$AsyncInvoker - -# cloudbees-deployer plugin -com.cloudbees.plugins.deployer.engines.Engine$FingerprintDecorator - -# subversion plugin -hudson.scm.SubversionWorkspaceSelector$1 - -# git-client plugin (prior to #147) -org.jenkinsci.plugins.gitclient.CliGitAPIImpl$GetPrivateKeys - -# ssh-credentials plugin -com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator$1 - -# cygwin-process-killer plugin -com.synopsys.arc.jenkinsci.plugins.cygwinprocesskiller.CygwinProcessKiller$KillerRemoteCall - -# selenium-plugin -hudson.plugins.selenium.JenkinsCapabilityMatcher$LabelMatcherCallable diff --git a/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf b/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf deleted file mode 100644 index 543c56e83f85..000000000000 --- a/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf +++ /dev/null @@ -1,49 +0,0 @@ -# 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 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 -# gets overwritten every time Jenkins starts. -# -# See https://www.jenkins.io/redirect/security-144 for more details. - -# This directory contains credentials, master encryption keys, and other sensitive information -# 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 agents to read, too. -# (The xunit plugin is known to read from here.) -# https://www.jenkins.io/redirect/user-content-directory -allow read,stat /userContent($|/.*) - -# In the next rule we grant general access under build directories, so first we protect -# the actual build record that Jenkins core reads, which nothing should be touching. -deny all /build.xml -# Similarly for Pipeline build (WorkflowRun) metadata: -deny all /program.dat -deny all /workflow($|/.*) -deny all /libs($|/.*) -deny all /checkpoints($|/.*) - -# Various plugins read/write files under build directories, so allow them all. -# - 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/ -# - dependency-check writes archive/artifacts.txt -# But not allowing deletion to prevent data loss and symlink to prevent jailbreaking. -allow create,mkdirs,read,stat,write /.+ - -# cobertura also writes out annotated sources to a dir under the Maven module: -allow create,mkdirs,read,stat,write /modules/([^/]+)/cobertura($|/.*) - -# Some maven-plugin reporters also create content outside of build directories (including one in cobertura but that is covered above): -allow create,mkdirs,read,stat,write (/jobs/([^/]+))+(|/modules/([^/]+))/(javadoc|test-javadoc)($|/.*) -allow create,mkdirs,read,stat,write (/jobs/([^/]+))+/site($|/.*) - -# all the other accesses that aren't specified here will be left up to other rules in this directory. -# if no rules in those other files matches, then the access will be rejected. diff --git a/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly b/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly deleted file mode 100644 index 1e85405b0903..000000000000 --- a/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly +++ /dev/null @@ -1,17 +0,0 @@ - - - Jenkins controllers construct a remote-procedure-call (RPC) channel to agents to instruct them to perform work. - This channel is bidirectional and in a handful of cases agents made requests of the controller. - This was always tricky to secure, - and recently - the category of usages which involved access to files was more tightly restricted than before; - Jenkins developers are considering disabling this kind of usage entirely. - Since it is difficult to determine via static analysis or even manual code inspection which plugins are using this system, - we are collecting information on how widely it is used. - The data includes: -
        -
      • Java stack traces (names of Java classes mainly in Jenkins core and plugins as well as method names and line numbers) of file accesses on the controller from agents
      • -
      • Version numbers of Jenkins core and all installed plugins
      • -
      - We do not collect the names of files being accessed or anything else not determined by versions of software components in use. -
      diff --git a/core/src/test/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsageTest.java b/core/src/test/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsageTest.java deleted file mode 100644 index ef1bdcc464df..000000000000 --- a/core/src/test/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsageTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package jenkins.telemetry.impl; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class SlaveToMasterFileCallableUsageTest { - @Test - public void ignoreObjectIdentity() { - assertGeneralization("Command UserRequest:hudson.FilePath$CopyTo@… created at", "Command UserRequest:hudson.FilePath$CopyTo@abcdef12 created at"); - - assertUnmodified("FilePath$CopyTo-abcdef12 created at"); - assertUnmodified("abcdef12 created at"); - assertUnmodified("\tat hudson.FilePath.readToString(FilePath.java:2289)"); - } - - @Test - public void ignoreRPCRequestOid() { - assertGeneralization("Command UserRequest:UserRPCRequest:hudson.maven.MavenBuildProxy2.end[](…) created at", "Command UserRequest:UserRPCRequest:hudson.maven.MavenBuildProxy2.end[](16) created at"); - assertGeneralization("Command UserRequest:UserRPCRequest:hudson.maven.MavenBuildProxy2.end[org.acme.FakeType](…) created at", "Command UserRequest:UserRPCRequest:hudson.maven.MavenBuildProxy2.end[org.acme.FakeType](16) created at"); - } - - @Test - public void ignoreProxyIndex() { - assertGeneralization("at com.sun.proxy.$Proxy….end(Unknown Source)", "at com.sun.proxy.$Proxy6.end(Unknown Source)"); - assertGeneralization("at com.sun.proxy.$Proxy….end(Unknown Source)", "at com.sun.proxy.$Proxy66.end(Unknown Source)"); - assertGeneralization("at com.sun.proxy.$Proxy….begin(Unknown Source)", "at com.sun.proxy.$Proxy66.begin(Unknown Source)"); - } - - private static void assertGeneralization(String generalized, String actual) { - assertEquals(generalized, SlaveToMasterFileCallableUsage.generalize(actual)); - } - - private static void assertUnmodified(String actual) { - assertEquals(actual, SlaveToMasterFileCallableUsage.generalize(actual)); - } -} diff --git a/test/src/test/java/jenkins/security/AgentToControllerSecurityTest.java b/test/src/test/java/jenkins/security/AgentToControllerSecurityTest.java new file mode 100644 index 000000000000..0555cec0f1fe --- /dev/null +++ b/test/src/test/java/jenkins/security/AgentToControllerSecurityTest.java @@ -0,0 +1,211 @@ +package jenkins.security; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import hudson.FilePath; +import hudson.remoting.Callable; +import hudson.remoting.VirtualChannel; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Objects; +import jenkins.MasterToSlaveFileCallable; +import jenkins.SlaveToMasterFileCallable; +import jenkins.agents.AgentComputerUtil; +import jenkins.security.s2m.CallableDirectionChecker; +import jenkins.util.JenkinsJVM; +import org.jenkinsci.remoting.RoleChecker; +import org.junit.Rule; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; + +public class AgentToControllerSecurityTest { + + @Rule + public JenkinsRule j = new JenkinsRule(); + + // ----- try to run a legacy callable + + @Test + public void testLegacyCallable() { + final SecurityException securityException = assertThrowsIOExceptionCausedBySecurityException(() -> Objects.requireNonNull(j.createOnlineSlave().getChannel()).call(new CallLegacyCallableCallable())); + assertThat(securityException.getMessage(), containsString("Sending jenkins.security.AgentToControllerSecurityTest$LegacyCallable from agent to controller is prohibited")); + } + + private static class CallLegacyCallableCallable extends MasterToSlaveCallable { + @Override + public Void call() throws Exception { + Objects.requireNonNull(AgentComputerUtil.getChannelToController()).call(new LegacyCallable()); + return null; + } + } + + private static class LegacyCallable implements Callable { + @Override + public Void call() throws Exception { + fail("LegacyCallable got called"); + return null; + } + + @Override + public void checkRoles(RoleChecker checker) throws SecurityException { + throw new AbstractMethodError("pretending to be a legacy Callable"); + } + } + + // ----- Attempt any file path access using a FilePath method + + @Test + public void testFilePaths() { + final SecurityException securityException = assertThrowsIOExceptionCausedBySecurityException(() -> Objects.requireNonNull(j.createOnlineSlave().getChannel()).call(new AccessControllerFilePathCallable())); + assertThat(securityException.getMessage(), containsString("Sending hudson.FilePath$ReadLink from agent to controller is prohibited")); + } + + private static class AccessControllerFilePathCallable extends MasterToSlaveCallable { + @Override + public Void call() throws Exception { + new FilePath(AgentComputerUtil.getChannelToController(), "foo").readLink(); + return null; + } + } + + // ----- Agent to controller access is still possible using SlaveToMaster[File]Callable + + @Test + public void testAgentToControllerFileCallable() throws Exception { + Objects.requireNonNull(j.createOnlineSlave().getChannel()).call(new InvokeAgentToControllerCallables()); + } + + private static class InvokeAgentToControllerCallables extends MasterToSlaveCallable { + @Override + public Void call() throws Exception { + assertFalse(JenkinsJVM.isJenkinsJVM()); + final VirtualChannel channelToController = AgentComputerUtil.getChannelToController(); + assertNotNull(channelToController); + channelToController.call(new A2CCallable()); + new FilePath(channelToController, "foo").act(new A2CFileCallable()); + return null; + } + } + + private static class A2CFileCallable extends SlaveToMasterFileCallable { + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + assertTrue(JenkinsJVM.isJenkinsJVM()); + return null; + } + } + + private static class A2CCallable extends SlaveToMasterCallable { + @Override + public Void call() throws Exception { + assertTrue(JenkinsJVM.isJenkinsJVM()); + return null; + } + } + + // ----- Agent to controller access control can be disabled using system property (but really shouldn't) + @Test + public void ensureBypass() throws Exception { + CallableDirectionChecker.BYPASS = true; + try { + Objects.requireNonNull(j.createOnlineSlave().getChannel()).call(new InvokeControllerToAgentCallables()); + } finally { + CallableDirectionChecker.BYPASS = false; + } + } + + private static class InvokeControllerToAgentCallables extends MasterToSlaveCallable { + @Override + public Void call() throws Exception { + assertFalse(JenkinsJVM.isJenkinsJVM()); + final VirtualChannel channelToController = AgentComputerUtil.getChannelToController(); + assertNotNull(channelToController); + channelToController.call(new NoopMasterToSlaveCallable()); + new FilePath(channelToController, "foo").act(new NoopMasterToSlaveFileCallable()); + return null; + } + } + + private static class NoopMasterToSlaveCallable extends MasterToSlaveCallable { + @Override + public Void call() throws Exception { + return null; + } + } + + private static class NoopMasterToSlaveFileCallable extends MasterToSlaveFileCallable { + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + return null; + } + } + + // --- Ensure local FilePath operations work inside a Callable + + @Test + @Issue("JENKINS-67189") + public void controllerToControllerTest() throws Exception { + // Send a callable to the agent, which sends a callable to the controller, which invokes a method of a local FilePath + Objects.requireNonNull(j.createOnlineSlave().getChannel()).call(new BackToControllerCallable()); + } + + private static class BackToControllerCallable extends MasterToSlaveCallable { + @Override + public String call() throws Exception { + assertFalse(JenkinsJVM.isJenkinsJVM()); + return Objects.requireNonNull(AgentComputerUtil.getChannelToController()).call(new LocalFileOpCallable(true)); + } + } + + // Used for both agent-to-agent and controller-to-controller, so make it S2MC + private static class LocalFileOpCallable extends SlaveToMasterCallable { + private final boolean executesOnJenkinsJVM; + + LocalFileOpCallable(boolean executesOnJenkinsJVM) { + this.executesOnJenkinsJVM = executesOnJenkinsJVM; + } + + @Override + public String call() throws Exception { + assertEquals(executesOnJenkinsJVM, JenkinsJVM.isJenkinsJVM()); + final File tempFile = Files.createTempFile("jenkins-test", null).toFile(); + return new FilePath(tempFile).readToString(); + } + } + + @Test + @Issue("JENKINS-67189") // but this test works even in 2.319 because no agent side filtering + public void agentToAgentTest() throws Exception { + Objects.requireNonNull(j.createOnlineSlave().getChannel()).call(new LocalFileOpCallable(false)); + } + + // ----- Utility methods + + public static SecurityException assertThrowsIOExceptionCausedBySecurityException(ThrowingRunnable runnable) { + return assertThrowsIOExceptionCausedBy(SecurityException.class, runnable); + } + + public static X assertThrowsIOExceptionCausedBy(Class causeClass, ThrowingRunnable runnable) { + try { + runnable.run(); + } catch (IOException ex) { + final Throwable cause = ex.getCause(); + assertTrue("IOException with message: '" + ex.getMessage() + "' wasn't caused by " + causeClass + ": " + (cause == null ? "(null)" : (cause.getClass().getName() + ": " + cause.getMessage())), + cause != null && causeClass.isAssignableFrom(cause.getClass())); + return causeClass.cast(cause); + } catch (Throwable t) { + fail("Threw other Throwable: " + t.getClass() + " with message " + t.getMessage()); + } + fail("Expected exception but passed"); + return null; + } +} diff --git a/test/src/test/java/jenkins/security/FilePathSecureTest.java b/test/src/test/java/jenkins/security/FilePathSecureTest.java index 02ca3a68cdde..936fc47fe314 100644 --- a/test/src/test/java/jenkins/security/FilePathSecureTest.java +++ b/test/src/test/java/jenkins/security/FilePathSecureTest.java @@ -35,6 +35,7 @@ import org.junit.Test; import org.jvnet.hudson.test.JenkinsRule; +// TODO What is this even testing? public class FilePathSecureTest { @Rule public JenkinsRule r = new JenkinsRule(); diff --git a/test/src/test/java/jenkins/security/Security2455Test.java b/test/src/test/java/jenkins/security/Security2455Test.java deleted file mode 100644 index 77c5035ed875..000000000000 --- a/test/src/test/java/jenkins/security/Security2455Test.java +++ /dev/null @@ -1,865 +0,0 @@ -package jenkins.security; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; -import static org.jvnet.hudson.test.LoggerRule.recorded; - -import hudson.ExtensionList; -import hudson.FilePath; -import hudson.Functions; -import hudson.Util; -import hudson.model.Cause; -import hudson.model.FreeStyleBuild; -import hudson.model.FreeStyleProject; -import hudson.model.Node; -import hudson.model.TaskListener; -import hudson.remoting.VirtualChannel; -import hudson.slaves.DumbSlave; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.net.URI; -import java.nio.file.Files; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.logging.Level; -import jenkins.SlaveToMasterFileCallable; -import jenkins.SoloFilePathFilter; -import jenkins.agents.AgentComputerUtil; -import jenkins.security.s2m.AdminWhitelistRule; -import jenkins.security.s2m.RunningBuildFilePathFilter; -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.output.NullOutputStream; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.function.ThrowingRunnable; -import org.jvnet.hudson.test.FlagRule; -import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.JenkinsRule; -import org.jvnet.hudson.test.LoggerRule; -import org.jvnet.hudson.test.MockFolder; -import org.jvnet.hudson.test.recipes.LocalData; - -@SuppressWarnings("ThrowableNotThrown") -@Issue("SECURITY-2455") -public class Security2455Test { - - @Rule - public final FlagRule flagRule = FlagRule.systemProperty(RunningBuildFilePathFilter.class.getName() + ".FAIL", "false"); - - @Rule - public JenkinsRule j = new JenkinsRule(); - - @Rule - public LoggerRule logging = new LoggerRule().record(SoloFilePathFilter.class, Level.WARNING); - - @Before - public void setup() { - ExtensionList.lookupSingleton(AdminWhitelistRule.class).setMasterKillSwitch(false); - } - - // -------- - - @Test - @Issue("SECURITY-2427") - public void mkdirsParentsTest() { - final File buildStuff = new File(j.jenkins.getRootDir(), "job/nonexistent/builds/1/foo/bar"); - logging.capture(10); - SecurityException ex = assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkdirsParentsCallable(buildStuff))); - assertThat(logging, recorded(containsString("foo" + File.separator + "bar"))); - assertThat(ex.getMessage(), not(containsString("foo" + File.separator + "bar"))); // test error redaction - - SoloFilePathFilter.REDACT_ERRORS = false; - try { - SecurityException ex2 = assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkdirsParentsCallable(buildStuff))); - assertThat(ex2.getMessage(), containsString("foo" + File.separator + "bar")); // test error redaction - } finally { - SoloFilePathFilter.REDACT_ERRORS = true; - } - } - private static class MkdirsParentsCallable extends MasterToSlaveCallable { - private final File file; - - private MkdirsParentsCallable(File file) { - this.file = file; - } - - @Override - public String call() throws Exception { - toFilePathOnController(this.file).mkdirs(); - return null; - } - } - - // -------- - - @Test - @Issue("SECURITY-2444") - public void testNonCanonicalPath() throws Exception { - assumeFalse(Functions.isWindows()); - final FreeStyleBuild build = j.createFreeStyleProject().scheduleBuild2(0, new Cause.UserIdCause()).waitForStart(); - j.waitForCompletion(build); - final File link = new File(build.getRootDir(), "link"); - final File secrets = new File(j.jenkins.getRootDir(), "secrets/master.key"); - Files.createSymbolicLink(link.toPath(), secrets.toPath()); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new ReadToStringCallable(link))); - } - @Test - @Issue("SECURITY-2444") - public void testNonCanonicalPathOnController() throws Exception { - assumeFalse(Functions.isWindows()); - final FreeStyleBuild build = j.createFreeStyleProject().scheduleBuild2(0, new Cause.UserIdCause()).waitForStart(); - j.waitForCompletion(build); - final File link = new File(build.getRootDir(), "link"); - final File secrets = new File(j.jenkins.getRootDir(), "secrets/master.key"); - Files.createSymbolicLink(link.toPath(), secrets.toPath()); - String result = FilePath.localChannel.call(new ReadToStringCallable(link)); - assertEquals(IOUtils.readLines(new FileReader(secrets)).get(0), result); - } - - private static class ReadToStringCallable extends MasterToSlaveCallable { - - final String abs; - - ReadToStringCallable(File link) { - abs = link.getPath(); - } - - @Override - public String call() throws IOException { - FilePath p = toFilePathOnController(new File(abs)); - try { - return p.readToString(); - } catch (InterruptedException e) { - throw new IOException(e); - } - } - } - - // -------- - - @Test - @Issue({"SECURITY-2446", "SECURITY-2531"}) - // $ tar tvf symlink.tar - // lrwxr-xr-x 0 501 20 0 Oct 5 09:50 foo -> ../../../../secrets - @LocalData - public void testUntaringSymlinksFails() throws Exception { - final FreeStyleBuild freeStyleBuild = j.buildAndAssertSuccess(j.createFreeStyleProject()); - final File symlinkTarFile = new File(j.jenkins.getRootDir(), "symlink.tar"); - final File untarTargetFile = new File(freeStyleBuild.getRootDir(), "foo"); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new UntarFileCallable(symlinkTarFile, untarTargetFile))); - } - private static final class UntarFileCallable extends MasterToSlaveCallable { - private final File source; - private final File destination; - - private UntarFileCallable(File source, File destination) { - this.source = source; - this.destination = destination; - } - - @Override - public Integer call() throws Exception { - final FilePath sourceFilePath = new FilePath(source); - final FilePath destinationFilePath = toFilePathOnController(destination); - sourceFilePath.untar(destinationFilePath, FilePath.TarCompression.NONE); - return 1; - } - } - - // -------- - - @Test - @Issue("SECURITY-2453") - public void testTarSymlinksThatAreSafe() throws Exception { - assumeFalse(Functions.isWindows()); - final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir(); - // We cannot touch the build dir itself - final File innerDir = new File(buildDir, "dir"); - final File innerDir2 = new File(buildDir, "dir2"); - assertTrue(innerDir.mkdirs()); - assertTrue(innerDir2.mkdirs()); - assertTrue(new File(innerDir2, "the-file").createNewFile()); - Util.createSymlink(innerDir, "../dir2", "link", TaskListener.NULL); - assertTrue(new File(innerDir, "link/the-file").exists()); - final int files = invokeOnAgent(new TarCaller(innerDir)); - assertEquals(1, files); - } - @Test - @Issue("SECURITY-2453") - public void testTarSymlinksOutsideAllowedDirs() throws Exception { - assumeFalse(Functions.isWindows()); - final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir(); - // We cannot touch the build dir itself - final File innerDir = new File(buildDir, "dir"); - assertTrue(innerDir.mkdirs()); - Util.createSymlink(innerDir, "../../../../../secrets", "secrets-link", TaskListener.NULL); - assertTrue(new File(innerDir, "secrets-link/master.key").exists()); - logging.capture(10); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new TarCaller(innerDir))); - assertThat(logging, recorded(containsString("filepath-filters.d"))); - } - - private static class TarCaller extends MasterToSlaveCallable { - private final File root; - - private TarCaller(File root) { - this.root = root; - } - - @Override - public Integer call() throws Exception { - return toFilePathOnController(root).tar(NullOutputStream.NULL_OUTPUT_STREAM, "**"); - } - } - - // -------- - - @Test - @Issue("SECURITY-2484") - public void zipTest() { - final File secrets = new File(j.jenkins.getRootDir(), "secrets"); - assertTrue(secrets.exists()); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new ZipTestCallable(secrets))); - } - @Test - @Issue("SECURITY-2484") - public void zipTestController() throws Exception { - final File secrets = new File(j.jenkins.getRootDir(), "secrets"); - assertTrue(secrets.exists()); - FilePath.localChannel.call(new ZipTestCallable(secrets)); - } - - private static class ZipTestCallable extends MasterToSlaveCallable { - private final File file; - - private ZipTestCallable(File file) { - this.file = file; - } - - @Override - public String call() throws Exception { - final File tmp = File.createTempFile("security2455_", ".zip"); - tmp.deleteOnExit(); - toFilePathOnController(file).zip(new FilePath(tmp)); - return tmp.getName(); - } - } - - // -------- - - @Test - @Issue("SECURITY-2485") - @LocalData - public void unzipRemoteTest() { - final File targetDir = j.jenkins.getRootDir(); - final File source = new File(targetDir, "file.zip"); // in this test, controller and agent are on same FS so this works -- file needs to exist but content should not be read - assertTrue(targetDir.exists()); - final List filesBefore = Arrays.asList(Objects.requireNonNull(targetDir.listFiles())); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new UnzipRemoteTestCallable(targetDir, source))); - final List filesAfter = Arrays.asList(Objects.requireNonNull(targetDir.listFiles())); - // We cannot do a direct comparison here because `logs/` appears during execution - assertEquals(filesBefore.size(), filesAfter.stream().filter(it -> !it.getName().equals("logs")).count()); - } - - private static class UnzipRemoteTestCallable extends MasterToSlaveCallable { - private final File destination; - private final File source; - - private UnzipRemoteTestCallable(File destination, File source) { - this.destination = destination; - this.source = source; - } - - @Override - public String call() throws Exception { - FilePath onAgent = new FilePath(source); - onAgent.unzip(toFilePathOnController(destination)); - return null; - } - } - - // -------- - - @Test - @Issue("SECURITY-2485") - public void testCopyRecursiveFromControllerToAgent() { - IOException ex = assertThrowsIOExceptionCausedBy(IOException.class, () -> invokeOnAgent(new CopyRecursiveToFromControllerToAgentCallable(new FilePath(new File(j.jenkins.getRootDir(), "secrets"))))); - assertThat(Objects.requireNonNull(ex).getMessage(), containsString("Unexpected end of ZLIB input stream")); // TODO this used to say "premature", why the change? - } - private static class CopyRecursiveToFromControllerToAgentCallable extends MasterToSlaveCallable { - private final FilePath controllerFilePath; - - private CopyRecursiveToFromControllerToAgentCallable(FilePath controllerFilePath) { - this.controllerFilePath = controllerFilePath; - } - - @Override - public Integer call() throws Exception { - return controllerFilePath.copyRecursiveTo(new FilePath(Files.createTempDirectory("jenkins-test").toFile())); - } - } - - // -------- - - @Test - @Issue("SECURITY-2485") - public void testCopyRecursiveFromAgentToController() { - assertThrowsIOExceptionCausedBy(SecurityException.class, () -> invokeOnAgent(new CopyRecursiveToFromAgentToControllerCallable(new FilePath(new File(j.jenkins.getRootDir(), "secrets"))))); - } - private static class CopyRecursiveToFromAgentToControllerCallable extends MasterToSlaveCallable { - private final FilePath controllerFilePath; - - private CopyRecursiveToFromAgentToControllerCallable(FilePath controllerFilePath) { - this.controllerFilePath = controllerFilePath; - } - - @Override - public Integer call() throws Exception { - final File localPath = Files.createTempDirectory("jenkins-test").toFile(); - assertTrue(new File(localPath, "tmpfile").createNewFile()); - return new FilePath(localPath).copyRecursiveTo(controllerFilePath); - } - } - - // -------- - - @Test - @Issue("SECURITY-2486") - public void testDecoyWrapper() { - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new ReadToStringBypassCallable(j.jenkins.getRootDir()))); - } - - private static class ReadToStringBypassCallable extends MasterToSlaveCallable { - private final File file; - - private ReadToStringBypassCallable(File file) { - this.file = file; - } - - @Override - public String call() throws Exception { - final Class readToStringClass = Class.forName("hudson.FilePath$ReadToString"); - final Constructor constructor = readToStringClass.getDeclaredConstructor(); // Used to have FilePath.class from non-static context - constructor.setAccessible(true); - - //FilePath agentFilePath = new FilePath(new File("on agent lol")); // only used for the core code before fix - - final SlaveToMasterFileCallable callable = (SlaveToMasterFileCallable) constructor.newInstance(); // agentFilePath - - FilePath controllerFilePath = toFilePathOnController(new File(file, "secrets/master.key")); - final Object returned = controllerFilePath.act(callable); - return (String) returned; - } - } - - // -------- - - @Test - @Issue("SECURITY-2531") - public void testSymlinkCheck() throws Exception { - final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir(); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new SymlinkCreator(buildDir))); - } - private static class SymlinkCreator extends MasterToSlaveCallable { - private final File baseDir; - private SymlinkCreator(File baseDir) { - this.baseDir = baseDir; - } - - @Override - public String call() throws Exception { - toFilePathOnController(new File(baseDir, "child")).symlinkTo(baseDir.getPath() + "child2", TaskListener.NULL); - return null; - } - } - - // -------- - - // -------- - - @Test - @Issue("SECURITY-2538") // SECURITY-2538 adjacent, confirms that reading, stating etc. is supposed to be possible for userContent - public void testReadUserContent() throws Exception { - invokeOnAgent(new UserContentReader(new File(j.jenkins.getRootDir(), "userContent"))); - } - private static class UserContentReader extends MasterToSlaveCallable { - private final File userContent; - - private UserContentReader(File userContent) { - this.userContent = userContent; - } - - @Override - public String call() throws Exception { - final FilePath userContentFilePath = toFilePathOnController(userContent); - userContentFilePath.lastModified(); - userContentFilePath.zip(NullOutputStream.NULL_OUTPUT_STREAM); - assertThat(userContentFilePath.child("readme.txt").readToString(), containsString(hudson.model.Messages.Hudson_USER_CONTENT_README())); - return null; - } - } - - @Test - @Issue("SECURITY-2538") - public void testRenameTo() throws Exception { - final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir(); - final File userContentDir = new File(j.jenkins.getRootDir(), "userContent"); - final File readme = new File(userContentDir, "readme.txt"); - final File to = new File(buildDir, "readme.txt"); - assertTrue("readme.txt is a file", readme.isFile()); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new RenameToCaller(readme, to))); - assertTrue("readme.txt is still a file", readme.isFile()); - assertFalse("to does not exist", to.exists()); - } - private static class RenameToCaller extends MasterToSlaveCallable { - private final File from; - private final File to; - - private RenameToCaller(File from, File to) { - this.from = from; - this.to = to; - } - - @Override - public String call() throws Exception { - toFilePathOnController(from).renameTo(toFilePathOnController(to)); - return null; - } - } - - @Test - @Issue("SECURITY-2538") - public void testMoveChildren() throws Exception { - final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir(); - final File userContentDir = new File(j.jenkins.getRootDir(), "userContent"); - // The implementation of MoveAllChildrenTo seems odd and ends up removing the source directory, so work only in subdir of userContent - final File userContentSubDir = new File(userContentDir, "stuff"); - assertTrue(userContentSubDir.mkdirs()); - final File userContentSubDirFileA = new File(userContentSubDir, "fileA"); - final File userContentSubDirFileB = new File(userContentSubDir, "fileB"); - assertTrue(userContentSubDirFileA.createNewFile()); - assertTrue(userContentSubDirFileB.createNewFile()); - assertTrue("userContentSubDir is a directory", userContentSubDir.isDirectory()); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MoveAllChildrenToCaller(userContentSubDir, buildDir))); - assertTrue("userContentSubDir is a directory", userContentSubDir.isDirectory()); - assertFalse("no fileA in buildDir", new File(buildDir, "fileA").exists()); - assertFalse("no fileB in buildDir", new File(buildDir, "fileB").exists()); - assertTrue("fileA is still a file", userContentSubDirFileA.isFile()); - assertTrue("fileB is still a file", userContentSubDirFileB.isFile()); - } - private static class MoveAllChildrenToCaller extends MasterToSlaveCallable { - private final File from; - private final File to; - - private MoveAllChildrenToCaller(File from, File to) { - this.from = from; - this.to = to; - } - - @Override - public String call() throws Exception { - toFilePathOnController(from).moveAllChildrenTo(toFilePathOnController(to)); - return null; - } - } - - // -------- - - @Test - @Issue("SECURITY-2539") - public void testCreateTempFile() { - final File rootDir = j.jenkins.getRootDir(); - assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix"))); - - // Extra level of catch/throw - logging.capture(10); - final IOException ioException = assertThrowsIOExceptionCausedBy(IOException.class, () -> invokeOnAgent(new CreateTempFileCaller(rootDir))); - assertNotNull(ioException); - final Throwable cause = ioException.getCause(); - assertNotNull(cause); - assertTrue(cause instanceof SecurityException); - assertThat(cause.getMessage(), not(containsString("prefix"))); // redacted - assertThat(logging, recorded(containsString("'create'"))); - assertThat(logging, recorded(containsString(File.separator + "prefix-security-check-dummy-suffix"))); - assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix"))); - } - private static class CreateTempFileCaller extends MasterToSlaveCallable { - private final File baseDir; - - private CreateTempFileCaller(File baseDir) { - this.baseDir = baseDir; - } - - @Override - public String call() throws Exception { - toFilePathOnController(baseDir).createTempFile("prefix", "suffix"); - return null; - } - } - - - @Test - @Issue("SECURITY-2539") - public void testCreateTextTempFile() { - final File rootDir = j.jenkins.getRootDir(); - assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix"))); - - // Extra level of catch/throw - logging.capture(10); - final IOException ioException = assertThrowsIOExceptionCausedBy(IOException.class, () -> invokeOnAgent(new CreateTextTempFileCaller(rootDir))); - assertNotNull(ioException); - final Throwable cause = ioException.getCause(); - assertNotNull(cause); - assertTrue(cause instanceof SecurityException); - assertThat(cause.getMessage(), not(containsString("prefix"))); // redacted - assertThat(logging, recorded(containsString("'create'"))); - assertThat(logging, recorded(containsString(File.separator + "prefix-security-check-dummy-suffix"))); - assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix"))); - } - private static class CreateTextTempFileCaller extends MasterToSlaveCallable { - private final File baseDir; - - private CreateTextTempFileCaller(File baseDir) { - this.baseDir = baseDir; - } - - @Override - public String call() throws Exception { - toFilePathOnController(baseDir).createTextTempFile("prefix", "suffix", "content"); - return null; - } - } - - @Test - @Issue("SECURITY-2539") - public void testCreateTempDir() { - final File rootDir = j.jenkins.getRootDir(); - assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix"))); - - // Extra level of catch/throw - logging.capture(10); - final IOException ioException = assertThrowsIOExceptionCausedBy(IOException.class, () -> invokeOnAgent(new CreateTempDirCaller(rootDir))); - assertNotNull(ioException); - final Throwable cause = ioException.getCause(); - assertNotNull(cause); - assertTrue(cause instanceof SecurityException); - assertThat(cause.getMessage(), not(containsString("prefix"))); // redacted - assertThat(logging, recorded(containsString("'mkdirs'"))); - assertThat(logging, recorded(containsString(File.separator + "prefix.suffix-security-test"))); // weird but that's what it looks like - assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix"))); - } - private static class CreateTempDirCaller extends MasterToSlaveCallable { - private final File baseDir; - - private CreateTempDirCaller(File baseDir) { - this.baseDir = baseDir; - } - - @Override - public String call() throws Exception { - toFilePathOnController(baseDir).createTempDir("prefix", "suffix"); - return null; - } - } - // -------- - - @Test - @Issue("SECURITY-2541") - public void testStatStuff() { - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new ToURICaller(j.jenkins.getRootDir()))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new FreeDiskSpaceCaller(j.jenkins.getRootDir()))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new TotalDiskSpaceCaller(j.jenkins.getRootDir()))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new UsableDiskSpaceCaller(j.jenkins.getRootDir()))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new AbsolutizeCaller(j.jenkins.getRootDir()))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new HasSymlinkCaller(new File (j.jenkins.getRootDir(), "secrets"), j.jenkins.getRootDir()))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new IsDescendantCaller(j.jenkins.getRootDir(), "secrets"))); - } - - private static class ToURICaller extends MasterToSlaveCallable { - private final File file; - - private ToURICaller(File file) { - this.file = file; - } - - @Override - public URI call() throws Exception { - return toFilePathOnController(file).toURI(); - } - } - private static class AbsolutizeCaller extends MasterToSlaveCallable { - private final File file; - - private AbsolutizeCaller(File file) { - this.file = file; - } - - @Override - public String call() throws Exception { - return toFilePathOnController(file).absolutize().getRemote(); - } - } - private static class HasSymlinkCaller extends MasterToSlaveCallable { - private final File file; - private final File root; - - private HasSymlinkCaller(File file, File root) { - this.file = file; - this.root = root; - } - - @Override - public Boolean call() throws Exception { - return toFilePathOnController(file).hasSymlink(toFilePathOnController(root), false); - } - } - private static class IsDescendantCaller extends MasterToSlaveCallable { - private final File file; - private final String childPath; - - private IsDescendantCaller(File file, String childPath) { - this.file = file; - this.childPath = childPath; - } - - @Override - public Boolean call() throws Exception { - return toFilePathOnController(file).isDescendant(childPath); - } - } - private static class FreeDiskSpaceCaller extends MasterToSlaveCallable { - private final File file; - - private FreeDiskSpaceCaller(File file) { - this.file = file; - } - - @Override - public Long call() throws Exception { - return toFilePathOnController(file).getFreeDiskSpace(); - } - } - private static class UsableDiskSpaceCaller extends MasterToSlaveCallable { - private final File file; - - private UsableDiskSpaceCaller(File file) { - this.file = file; - } - - @Override - public Long call() throws Exception { - return toFilePathOnController(file).getUsableDiskSpace(); - } - } - private static class TotalDiskSpaceCaller extends MasterToSlaveCallable { - private final File file; - - private TotalDiskSpaceCaller(File file) { - this.file = file; - } - - @Override - public Long call() throws Exception { - return toFilePathOnController(file).getTotalDiskSpace(); - } - } - - // -------- - - @Test - @Issue("SECURITY-2542") // adjacent, this confirms we follow symlinks when it's within allowed directories - public void testGlobFollowsSymlinks() throws Exception { - assumeFalse(Functions.isWindows()); - final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir(); - // We cannot touch the build dir itself - final File innerDir = new File(buildDir, "dir"); - final File innerDir2 = new File(buildDir, "dir2"); - assertTrue(innerDir.mkdirs()); - assertTrue(innerDir2.mkdirs()); - assertTrue(new File(innerDir2, "the-file").createNewFile()); - Util.createSymlink(innerDir, "../dir2", "link", TaskListener.NULL); - assertTrue(new File(innerDir, "link/the-file").exists()); - final int files = invokeOnAgent(new GlobCaller(innerDir)); - assertEquals(1, files); - } - @Test - @Issue("SECURITY-2542") - public void testGlobSymlinksThrowsOutsideAllowedDirectories() throws Exception { - assumeFalse(Functions.isWindows()); - final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir(); - // We cannot touch the build dir itself - final File innerDir = new File(buildDir, "dir"); - assertTrue(innerDir.mkdirs()); - Util.createSymlink(innerDir, "../../../../../secrets", "secrets-link", TaskListener.NULL); - assertTrue(new File(innerDir, "secrets-link/master.key").exists()); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new GlobCaller(innerDir))); - } - private static class GlobCaller extends MasterToSlaveCallable { - private final File root; - - private GlobCaller(File root) { - this.root = root; - } - - @Override - public Integer call() throws Exception { - return toFilePathOnController(root).list("**/*", "", false).length; - } - } - - // -------- - - @Issue("SECURITY-2455") // general issue -- Maven Projects would no longer be allowed to perform some actions - @Test - public void testMavenReportersAllowListForTopLevelJob() throws Exception { - final FreeStyleProject project = j.createFreeStyleProject(); - final File topLevelProjectDir = project.getRootDir(); - - // similar but wrong names: - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "not-site")))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "not-javadoc")))); - - // project-level archived stuff: - invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "javadoc"))); - invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "test-javadoc"))); - invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "site"))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "cobertura")))); - - // cannot mkdirs this from agent: - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "modules")))); - - final File mavenModuleDir = new File(topLevelProjectDir, "modules/pretend-maven-module"); - assertTrue(mavenModuleDir.mkdirs()); - - // module-level archived stuff: - invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "javadoc"))); - invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "test-javadoc"))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "site")))); - invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "cobertura"))); - } - - @Issue("SECURITY-2455") // general issue -- Maven Projects would no longer be allowed to perform some actions - @Test - public void testMavenReportersAllowListForJobInFolder() throws Exception { - final MockFolder theFolder = j.createFolder("theFolder"); - { - // basic child job - final FreeStyleProject childProject = theFolder.createProject(FreeStyleProject.class, "child"); - final File childProjectRootDir = childProject.getRootDir(); - - // project-level archived stuff for child project inside folder: - invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "javadoc"))); - invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "test-javadoc"))); - invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "site"))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "cobertura")))); - } - - { // misleadingly named child job (like one of the approved folders): - final FreeStyleProject siteChildProject = theFolder.createProject(FreeStyleProject.class, "site"); - final File siteChildProjectRootDir = siteChildProject.getRootDir(); - - // cannot mkdirs this from agent despite 'site' in the path (but on wrong level): - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(siteChildProjectRootDir))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "foo")))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "modules")))); - - // project-level archived stuff for another child inside folder: - invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "javadoc"))); - invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "test-javadoc"))); - invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "site"))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "cobertura")))); - - final File childProjectMavenModuleDir = new File(siteChildProjectRootDir, "modules/pretend-maven-module"); - assertTrue(childProjectMavenModuleDir.mkdirs()); - - // module-level archived stuff: - invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "javadoc"))); - invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "test-javadoc"))); - assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "site")))); - invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "cobertura"))); - } - } - - private static class MkDirsWriter extends MasterToSlaveCallable { - private final File root; - - private MkDirsWriter(File root) { - this.root = root; - } - - @Override - public Object call() throws Exception { - toFilePathOnController(root).mkdirs(); - toFilePathOnController(new File(root, "file.txt")).write("text", "UTF-8"); - return null; - } - } - - // -------- - - // Misc tests - - @LocalData - @Test - public void testRemoteLocalUnzip() throws Exception { - final DumbSlave onlineSlave = j.createOnlineSlave(); - final File zipFile = new File(j.jenkins.getRootDir(), "file.zip"); - assertTrue(zipFile.isFile()); - final FilePath agentRootPath = onlineSlave.getRootPath(); - final FilePath agentZipPath = agentRootPath.child("file.zip"); - new FilePath(zipFile).copyTo(agentZipPath); - agentZipPath.unzip(agentRootPath); - } - - // -------- - - // Utility functions - - protected static FilePath toFilePathOnController(File file) { - return toFilePathOnController(file.getPath()); - } - - protected static FilePath toFilePathOnController(String path) { - final VirtualChannel channel = AgentComputerUtil.getChannelToMaster(); - return new FilePath(channel, path); - } - - protected Node agent; - - protected T invokeOnAgent(MasterToSlaveCallable callable) throws Exception, X { - if (agent == null) { - agent = j.createOnlineSlave(); - } - return Objects.requireNonNull(agent.getChannel()).call(callable); - } - - private static SecurityException assertThrowsIOExceptionCausedBySecurityException(ThrowingRunnable runnable) { - return assertThrowsIOExceptionCausedBy(SecurityException.class, runnable); - } - - private static X assertThrowsIOExceptionCausedBy(Class causeClass, ThrowingRunnable runnable) { - try { - runnable.run(); - } catch (IOException ex) { - final Throwable cause = ex.getCause(); - assertTrue("IOException with message: '" + ex.getMessage() + "' wasn't caused by " + causeClass + ": " + (cause == null ? "(null)" : (cause.getClass().getName() + ": " + cause.getMessage())), - cause != null && causeClass.isAssignableFrom(cause.getClass())); - return causeClass.cast(cause); - } catch (Throwable t) { - fail("Threw other Throwable: " + t.getClass() + " with message " + t.getMessage()); - } - fail("Expected exception but passed"); - return null; - } -} diff --git a/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java b/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java deleted file mode 100644 index 383417a2db9e..000000000000 --- a/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * The MIT License - * - * Copyright 2016 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.s2m; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.hasSize; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.jvnet.hudson.test.LoggerRule.recorded; - -import hudson.ExtensionList; -import hudson.FilePath; -import hudson.model.Slave; -import hudson.remoting.Callable; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.reflect.Field; -import java.util.List; -import java.util.logging.Level; -import javax.inject.Inject; -import jenkins.SoloFilePathFilter; -import jenkins.telemetry.impl.SlaveToMasterFileCallableUsage; -import org.jenkinsci.remoting.RoleChecker; -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.LoggerRule; - -public class AdminFilePathFilterTest { - - @Rule - public JenkinsRule r = new JenkinsRule(); - - @Rule - public LoggerRule logging = new LoggerRule().record(SoloFilePathFilter.class, Level.WARNING); - - @Inject - AdminWhitelistRule rule; - - @Before - public void setUp() { - r.jenkins.getInjector().injectMembers(this); - rule.setMasterKillSwitch(false); - } - - @Issue({"JENKINS-27055", "SECURITY-358"}) - @Test - public void matchBuildDir() throws Exception { - File buildDir = r.buildAndAssertSuccess(r.createFreeStyleProject()).getRootDir(); - assertTrue(rule.checkFileAccess("write", new File(buildDir, "whatever"))); - assertFalse(rule.checkFileAccess("write", new File(buildDir, "build.xml"))); - // WorkflowRun: - assertFalse(rule.checkFileAccess("write", new File(buildDir, "program.dat"))); - assertFalse(rule.checkFileAccess("write", new File(buildDir, "workflow/23.xml"))); - } - - @Test - public void slaveCannotReadFileFromSecrets_butCanFromUserContent() throws Exception { - Slave s = r.createOnlineSlave(); - FilePath root = r.jenkins.getRootPath(); - - { // agent can read userContent folder - FilePath rootUserContentFolder = root.child("userContent"); - FilePath rootTargetPublic = rootUserContentFolder.child("target_public.txt"); - rootTargetPublic.write("target_public", null); - checkSlave_can_readFile(s, rootTargetPublic); - } - - { // agent cannot read files inside secrets - FilePath rootSecretFolder = root.child("secrets"); - FilePath rootTargetPrivate = rootSecretFolder.child("target_private.txt"); - rootTargetPrivate.write("target_private", null); - - checkSlave_cannot_readFile(s, rootTargetPrivate); - } - - rule.setMasterKillSwitch(true); - - { // with the master kill switch activated, agent can read files inside secrets - FilePath rootSecretFolder = root.child("secrets"); - FilePath rootTargetPrivate = rootSecretFolder.child("target_private.txt"); - - checkSlave_can_readFile(s, rootTargetPrivate); - } - - @SuppressWarnings("unchecked") - List traces = (List) ExtensionList.lookupSingleton(SlaveToMasterFileCallableUsage.class).createContent().getJSONArray("traces"); - assertThat(traces, hasSize(1)); - assertThat(traces.get(0), allOf(containsString("Command UserRequest:hudson.FilePath$ReadToString@… created at"), containsString(ReadFileS2MCallable.class.getName() + ".call"))); - @SuppressWarnings("unchecked") - List cleared = (List) ExtensionList.lookupSingleton(SlaveToMasterFileCallableUsage.class).createContent().getJSONArray("traces"); - assertThat(cleared, empty()); - } - - private static class ReadFileS2MCallable implements Callable { - private final FilePath p; - ReadFileS2MCallable(FilePath p) { - this.p = p; - } - @Override - public String call() throws Exception { - assertTrue(p.isRemote()); - return p.readToString(); - } - @Override - public void checkRoles(RoleChecker checker) throws SecurityException { - // simulate legacy Callable impls - throw new NoSuchMethodError(); - } - } - - @Test - @Issue("SECURITY-788") - public void slaveCannotUse_dotDotSlashStuff_toBypassRestriction() throws Exception { - Slave s = r.createOnlineSlave(); - FilePath root = r.jenkins.getRootPath(); - - { // use ../ to access a non-restricted folder - FilePath rootUserContentFolder = root.child("userContent"); - FilePath rootTargetPublic = rootUserContentFolder.child("target_public.txt"); - rootTargetPublic.write("target_public", null); - - FilePath dotDotSlashTargetPublic = root.child("logs/target_public.txt"); - replaceRemote(dotDotSlashTargetPublic, "logs", "logs/../userContent"); - - checkSlave_can_readFile(s, dotDotSlashTargetPublic); - } - - { // use ../ to try to bypass the rules - FilePath rootSecretFolder = root.child("secrets"); - FilePath rootTargetPrivate = rootSecretFolder.child("target_private.txt"); - rootTargetPrivate.write("target_private", null); - - FilePath dotDotSlashTargetPrivate = root.child("userContent/target_private.txt"); - replaceRemote(dotDotSlashTargetPrivate, "userContent", "userContent/../secrets"); - - checkSlave_cannot_readFile(s, dotDotSlashTargetPrivate); - } - } - - @Test - @Issue("SECURITY-788") - public void slaveCannotUse_encodedCharacters_toBypassRestriction() throws Exception { - Slave s = r.createOnlineSlave(); - FilePath root = r.jenkins.getRootPath(); - - // \u002e is the Unicode of . and is interpreted directly by Java as . - - { // use ../ to access a non-restricted folder - FilePath rootUserContentFolder = root.child("userContent"); - FilePath rootTargetPublic = rootUserContentFolder.child("target_public.txt"); - rootTargetPublic.write("target_public", null); - - FilePath dotDotSlashTargetPublic = root.child("logs/target_public.txt"); - replaceRemote(dotDotSlashTargetPublic, "logs", "logs/\u002e\u002e/userContent"); - - checkSlave_can_readFile(s, dotDotSlashTargetPublic); - } - - { // use ../ to try to bypass the rules - FilePath rootSecretFolder = root.child("secrets"); - FilePath rootTargetPrivate = rootSecretFolder.child("target_private.txt"); - rootTargetPrivate.write("target_private", null); - - FilePath dotDotSlashTargetPrivate = root.child("userContent/target_private.txt"); - replaceRemote(dotDotSlashTargetPrivate, "userContent", "userContent/\u002e\u002e/secrets"); - - checkSlave_cannot_readFile(s, dotDotSlashTargetPrivate); - } - } - - private void checkSlave_can_readFile(Slave s, FilePath target) throws Exception { - // The agent can read file from userContent - String content = s.getChannel().call(new ReadFileS2MCallable(target)); - // and the controller can directly reach it - assertEquals(target.readToString(), content); - } - - private void checkSlave_cannot_readFile(Slave s, FilePath target) throws Exception { - try { - logging.capture(10); - s.getChannel().call(new ReadFileS2MCallable(target)); - fail("Slave should not be able to read file in " + target.getRemote()); - } catch (IOException e){ - Throwable t = e.getCause(); - assertTrue(t instanceof SecurityException); - SecurityException se = (SecurityException) t; - StringWriter sw = new StringWriter(); - se.printStackTrace(new PrintWriter(sw)); - assertTrue(sw.toString().contains("Agent may not access a file path")); - - assertThat(logging, recorded(containsString("Agent may not 'read' at"))); - } - } - - // to bypass the normalization done in constructor - private void replaceRemote(FilePath p, String before, String after) throws Exception { - Field field = FilePath.class.getDeclaredField("remote"); - field.setAccessible(true); - String currentRemote = (String) field.get(p); - String newRemote = currentRemote.replace(before, after); - field.set(p, newRemote); - } -} diff --git a/test/src/test/java/jenkins/security/s2m/DefaultFilePathFilterTest.java b/test/src/test/java/jenkins/security/s2m/DefaultFilePathFilterTest.java deleted file mode 100644 index a9ff8d4fe501..000000000000 --- a/test/src/test/java/jenkins/security/s2m/DefaultFilePathFilterTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * The MIT License - * - * Copyright 2014 Jesse Glick. - * - * 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.s2m; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; - -import hudson.FilePath; -import hudson.Functions; -import hudson.model.Slave; -import hudson.remoting.Callable; -import java.io.File; -import java.io.IOException; -import javax.inject.Inject; -import org.jenkinsci.remoting.RoleChecker; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.JenkinsRule; - -public class DefaultFilePathFilterTest { - - @Rule public JenkinsRule r = new JenkinsRule(); - - @Inject - AdminWhitelistRule rule; - - @Before - public void setUp() { - r.jenkins.getInjector().injectMembers(this); - rule.setMasterKillSwitch(false); - } - - @Test public void remotePath() throws Exception { - Slave s = r.createOnlineSlave(); - FilePath forward = s.getRootPath().child("forward"); - forward.write("hello", null); - assertEquals("hello", s.getRootPath().act(new LocalCallable(forward))); - FilePath reverse = new FilePath(new File(r.jenkins.root, "reverse")); - assertFalse(reverse.exists()); - IOException x = assertThrows(IOException.class, () -> s.getChannel().call(new ReverseCallable(reverse))); - // make sure that the stack trace contains the call site info to help assist diagnosis - assertThat(Functions.printThrowable(x), containsString(DefaultFilePathFilterTest.class.getName() + ".remotePath")); - assertFalse(reverse.exists()); - DefaultFilePathFilter.BYPASS = true; - s.getChannel().call(new ReverseCallable(reverse)); - assertTrue(reverse.exists()); - assertEquals("goodbye", reverse.readToString()); - } - - private static class LocalCallable implements Callable { - private final FilePath p; - LocalCallable(FilePath p) { - this.p = p; - } - @Override public String call() throws Exception { - assertFalse(p.isRemote()); - return p.readToString(); - } - @Override - public void checkRoles(RoleChecker checker) throws SecurityException { - throw new NoSuchMethodError(); // simulate legacy Callable impls - } - } - - private static class ReverseCallable implements Callable { - private final FilePath p; - ReverseCallable(FilePath p) { - this.p = p; - } - @Override public Void call() throws Exception { - assertTrue(p.isRemote()); - p.write("goodbye", null); - return null; - } - @Override - public void checkRoles(RoleChecker checker) throws SecurityException { - throw new NoSuchMethodError(); // simulate legacy Callable impls - } - } - -} diff --git a/test/src/test/java/jenkins/security/s2m/RunningBuildFilePathFilterTest.java b/test/src/test/java/jenkins/security/s2m/RunningBuildFilePathFilterTest.java deleted file mode 100644 index b560f4df312b..000000000000 --- a/test/src/test/java/jenkins/security/s2m/RunningBuildFilePathFilterTest.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * The MIT License - * - * Copyright 2021 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.s2m; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import hudson.ExtensionList; -import hudson.FilePath; -import hudson.Functions; -import hudson.Launcher; -import hudson.model.AbstractBuild; -import hudson.model.BuildListener; -import hudson.model.FreeStyleProject; -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.function.Function; -import jenkins.security.MasterToSlaveCallable; -import org.apache.commons.io.FileUtils; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.BuildWatcher; -import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.JenkinsRule; -import org.jvnet.hudson.test.TestBuilder; - -@Issue("SECURITY-2428") -public class RunningBuildFilePathFilterTest { - - @ClassRule - public static BuildWatcher buildWatcher = new BuildWatcher(); - - @Rule - public JenkinsRule r = new JenkinsRule(); - - @Test - public void accessPermittedOnlyFromCurrentBuild() throws Exception { - ExtensionList.lookupSingleton(AdminWhitelistRule.class).setMasterKillSwitch(false); - FreeStyleProject main = r.createFreeStyleProject("main"); - main.setAssignedNode(r.createSlave()); - WriteBackPublisher wbp = new WriteBackPublisher(); - main.getBuildersList().add(wbp); - // Normal case: writing to our own build directory - wbp.controllerFile = build -> new File(build.getRootDir(), "stuff.txt"); - r.buildAndAssertSuccess(main); - // Attacks: - wbp.legal = false; - // Writing to someone else’s build directory (covered by RunningBuildFilePathFilter) - FreeStyleProject other = r.createFreeStyleProject("other"); - r.buildAndAssertSuccess(other); - wbp.controllerFile = build -> new File(other.getBuildByNumber(1).getRootDir(), "hack"); - r.buildAndAssertSuccess(main); - // Writing to some other directory (covered by AdminWhitelistRule) - wbp.controllerFile = build -> new File(r.jenkins.getRootDir(), "hack"); - r.buildAndAssertSuccess(main); - // Writing to a sensitive file even in my own build dir (covered by AdminWhitelistRule) - wbp.controllerFile = build -> new File(build.getRootDir(), "build.xml"); - r.buildAndAssertSuccess(main); - // Writing to the directory of an earlier build - wbp.controllerFile = build -> new File(main.getBuildByNumber(1).getRootDir(), "stuff.txt"); - r.buildAndAssertSuccess(main); - - System.setProperty(RunningBuildFilePathFilter.class.getName() + ".FAIL", "false"); - try { - wbp.legal = true; - wbp.controllerFile = build -> new File(main.getBuildByNumber(1).getRootDir(), "stuff.txt"); - r.buildAndAssertSuccess(main); - } finally { - System.clearProperty(RunningBuildFilePathFilter.class.getName() + ".FAIL"); - } - } - - private static final class WriteBackPublisher extends TestBuilder { - Function, File> controllerFile; - boolean legal = true; - @Override - public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { - File f = controllerFile.apply(build); - listener.getLogger().println("Will try to write to " + f + "; legal? " + legal); - String text = build.getExternalizableId(); - try { - launcher.getChannel().call(new WriteBackCallable(new FilePath(f), text)); - if (legal) { - assertEquals(text, FileUtils.readFileToString(f, StandardCharsets.UTF_8)); - listener.getLogger().println("Allowed as expected"); - } else { - fail("should not have been allowed"); - } - } catch (Exception x) { - if (!legal && x.toString().contains("SecurityException")) { - // TODO assert error message is either from RunningBuildFilePathFilter or from SoloFilePathFilter - Functions.printStackTrace(x, listener.error("Rejected as expected!")); - } else { - throw x; - } - } - return true; - } - } - - private static final class WriteBackCallable extends MasterToSlaveCallable { - private final FilePath controllerFile; - private final String text; - WriteBackCallable(FilePath controllerFile, String text) { - this.controllerFile = controllerFile; - this.text = text; - } - @Override - public Void call() throws IOException { - assertTrue(controllerFile.isRemote()); - try { - controllerFile.write(text, null); - } catch (InterruptedException x) { - throw new IOException(x); - } - return null; - } - - } - -} From 61e43d592a43d25b820db1fcd644769102b718fa Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 21 Dec 2021 12:41:46 +0000 Subject: [PATCH 38/58] [maven-release-plugin] prepare release jenkins-2.326 --- bom/pom.xml | 2 +- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index 027157f20653..457e02ad7a83 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.326 jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 59a7354a8527..d7c9c64bd7f5 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.326 cli diff --git a/core/pom.xml b/core/pom.xml index 48d4ea485f9b..321e4723217c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.326 jenkins-core diff --git a/pom.xml b/pom.xml index a5b64b3b972e..97f9a112c29b 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.326 pom Jenkins main module @@ -61,7 +61,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.326 diff --git a/test/pom.xml b/test/pom.xml index 79c731b5d412..0bb96b059814 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.326 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index d6cb8dddc820..c3fb48374f3b 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.326 jenkins-war From 1ce5adfccb051c883f7fb53bf0f41ae3eeb5ec29 Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 21 Dec 2021 12:42:09 +0000 Subject: [PATCH 39/58] [maven-release-plugin] prepare for next development iteration --- bom/pom.xml | 2 +- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 6 +++--- test/pom.xml | 2 +- war/pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index 457e02ad7a83..027157f20653 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.326 + ${revision}${changelist} jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index d7c9c64bd7f5..59a7354a8527 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.326 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 321e4723217c..48d4ea485f9b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.326 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index 97f9a112c29b..e8a823797a56 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.326 + ${revision}${changelist} pom Jenkins main module @@ -61,7 +61,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.326 + ${scmTag} @@ -70,7 +70,7 @@ THE SOFTWARE. - 2.326 + 2.327 -SNAPSHOT org.springframework.security spring-security-bom - 5.6.0 + 5.6.1 pom import
      From d963feec9b6741740aab373eb47879d3af748b2b Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 21 Dec 2021 18:07:59 -0500 Subject: [PATCH 44/58] Include `shortName` in relevant `PluginWrapper.dependencyErrors` (#6077) --- core/src/main/java/hudson/PluginWrapper.java | 14 ++++++------- .../main/resources/hudson/Messages.properties | 9 ++++----- .../test/java/hudson/PluginWrapperTest.java | 20 +++++++++++-------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index a04ad5188f65..0e417f3d6780 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -972,7 +972,7 @@ public boolean hasLicensesXml() { if (dependency == null) { PluginWrapper failedDependency = NOTICE.getPlugin(d.shortName); if (failedDependency != null) { - dependencyErrors.put(Messages.PluginWrapper_failed_to_load_dependency(failedDependency.getLongName(), failedDependency.getVersion()), true); + dependencyErrors.put(Messages.PluginWrapper_failed_to_load_dependency_2(failedDependency.getLongName(), failedDependency.getShortName(), failedDependency.getVersion()), true); break; } else { dependencyErrors.put(Messages.PluginWrapper_missing(d.shortName, d.version), false); @@ -980,13 +980,13 @@ public boolean hasLicensesXml() { } else { if (dependency.isActive()) { if (isDependencyObsolete(d, dependency)) { - versionDependencyError(Messages.PluginWrapper_obsolete(dependency.getLongName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version); + versionDependencyError(Messages.PluginWrapper_obsolete_2(dependency.getLongName(), dependency.getShortName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version); } } else { if (isDependencyObsolete(d, dependency)) { - versionDependencyError(Messages.PluginWrapper_disabledAndObsolete(dependency.getLongName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version); + versionDependencyError(Messages.PluginWrapper_obsolete_2(dependency.getLongName(), dependency.getShortName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version); } else { - dependencyErrors.put(Messages.PluginWrapper_disabled(dependency.getLongName()), false); + dependencyErrors.put(Messages.PluginWrapper_disabled_2(dependency.getLongName(), dependency.getShortName()), false); } } @@ -997,7 +997,7 @@ public boolean hasLicensesXml() { PluginWrapper dependency = parent.getPlugin(d.shortName); if (dependency != null && dependency.isActive()) { if (isDependencyObsolete(d, dependency)) { - versionDependencyError(Messages.PluginWrapper_obsolete(dependency.getLongName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version); + versionDependencyError(Messages.PluginWrapper_obsolete_2(dependency.getLongName(), dependency.getShortName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version); } else { dependencies.add(d); } @@ -1006,7 +1006,7 @@ public boolean hasLicensesXml() { if (!dependencyErrors.isEmpty()) { NOTICE.addPlugin(this); StringBuilder messageBuilder = new StringBuilder(); - messageBuilder.append(Messages.PluginWrapper_failed_to_load_plugin(getLongName(), getVersion())).append(System.lineSeparator()); + messageBuilder.append(Messages.PluginWrapper_failed_to_load_plugin_2(getLongName(), getShortName(), getVersion())).append(System.lineSeparator()); for (Iterator iterator = dependencyErrors.keySet().iterator(); iterator.hasNext(); ) { String dependencyError = iterator.next(); messageBuilder.append(" - ").append(dependencyError); @@ -1029,7 +1029,7 @@ private boolean isDependencyObsolete(Dependency d, PluginWrapper dependency) { */ private void versionDependencyError(String message, String actual, String minimum) { if (isSnapshot(actual) || isSnapshot(minimum)) { - LOGGER.log(WARNING, "Suppressing dependency error in {0} v{1}: {2}", new Object[] {getLongName(), getVersion(), message}); + LOGGER.log(WARNING, "Suppressing dependency error in {0} v{1}: {2}", new Object[] {getShortName(), getVersion(), message}); } else { dependencyErrors.put(message, false); } diff --git a/core/src/main/resources/hudson/Messages.properties b/core/src/main/resources/hudson/Messages.properties index 67d9aaf1bd2a..2f0ca5b9f88e 100644 --- a/core/src/main/resources/hudson/Messages.properties +++ b/core/src/main/resources/hudson/Messages.properties @@ -124,11 +124,10 @@ ProxyConfiguration.Success=Success Functions.NoExceptionDetails=No Exception details PluginWrapper.missing=Plugin is missing: {0} ({1}) -PluginWrapper.failed_to_load_plugin=Failed to load: {0} ({1}) -PluginWrapper.failed_to_load_dependency=Failed to load: {0} ({1}) -PluginWrapper.disabledAndObsolete=Update required: {0} ({1}) to {2} or higher -PluginWrapper.disabled=Required plugin is disabled: {0} -PluginWrapper.obsolete=Update required: {0} ({1}) to be updated to {2} or higher +PluginWrapper.failed_to_load_plugin_2=Failed to load: {0} ({1} {2}) +PluginWrapper.failed_to_load_dependency_2=Failed to load: {0} ({1} {2}) +PluginWrapper.disabled_2=Required plugin is disabled: {0} ({1}) +PluginWrapper.obsolete_2=Update required: {0} ({1} {2}) to be updated to {3} or higher PluginWrapper.obsoleteCore=Jenkins ({1}) or higher required PluginWrapper.obsoleteJava=Java ({1}) or higher required PluginWrapper.PluginWrapperAdministrativeMonitor.DisplayName=Plugins Failed To Load diff --git a/core/src/test/java/hudson/PluginWrapperTest.java b/core/src/test/java/hudson/PluginWrapperTest.java index 405473699952..0a0e51a588ff 100644 --- a/core/src/test/java/hudson/PluginWrapperTest.java +++ b/core/src/test/java/hudson/PluginWrapperTest.java @@ -26,6 +26,7 @@ import jenkins.model.Jenkins; import jenkins.util.AntClassLoader; import jenkins.util.URLClassLoader2; +import org.apache.commons.lang.StringUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -45,7 +46,9 @@ public static void before() { @AfterAll public static void after() { - Locale.setDefault(loc); + if (loc != null) { + Locale.setDefault(loc); + } } @Test @@ -71,7 +74,7 @@ public void jenkinsCoreTooOld() { PluginWrapper pw = pluginWrapper("fake").requiredCoreVersion("3.0").buildLoaded(); final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies); - assertContains(ex, "Failed to load: fake (42)", "Jenkins (3.0) or higher required"); + assertContains(ex, "Failed to load: Fake (fake 42)", "Jenkins (3.0) or higher required"); } @Test @@ -79,7 +82,7 @@ public void dependencyNotInstalled() { PluginWrapper pw = pluginWrapper("dependee").deps("dependency:42").buildLoaded(); final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies); - assertContains(ex, "Failed to load: dependee (42)", "Plugin is missing: dependency (42)"); + assertContains(ex, "Failed to load: Dependee (dependee 42)", "Plugin is missing: dependency (42)"); } @Test @@ -88,7 +91,7 @@ public void dependencyOutdated() { PluginWrapper pw = pluginWrapper("dependee").deps("dependency:5").buildLoaded(); final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies); - assertContains(ex, "Failed to load: dependee (42)", "Update required: dependency (3) to be updated to 5 or higher"); + assertContains(ex, "Failed to load: Dependee (dependee 42)", "Update required: Dependency (dependency 3) to be updated to 5 or higher"); } @Test @@ -97,7 +100,7 @@ public void dependencyFailedToLoad() { PluginWrapper pw = pluginWrapper("dependee").deps("dependency:3").buildLoaded(); final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies); - assertContains(ex, "Failed to load: dependee (42)", "Failed to load: dependency (5)"); + assertContains(ex, "Failed to load: Dependee (dependee 42)", "Failed to load: Dependency (dependency 5)"); } @Issue("JENKINS-66563") @@ -196,9 +199,10 @@ private PluginWrapper buildFailed() { private PluginWrapper build() { Manifest manifest = new Manifest(); Attributes attributes = manifest.getMainAttributes(); - attributes.put(new Attributes.Name("Short-Name"), name); - attributes.put(new Attributes.Name("Jenkins-Version"), requiredCoreVersion); - attributes.put(new Attributes.Name("Plugin-Version"), version); + attributes.putValue("Short-Name", name); + attributes.putValue("Long-Name", StringUtils.capitalize(name)); + attributes.putValue("Jenkins-Version", requiredCoreVersion); + attributes.putValue("Plugin-Version", version); return new PluginWrapper( pm, new File("/tmp/" + name + ".jpi"), From bdc72c3b3b043ac23f8d8eb35f53da345b66c162 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Dec 2021 06:14:43 -0800 Subject: [PATCH 45/58] Bump cloudbees-folder from 6.16 to 6.17 (#6094) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Basil Crow --- test/pom.xml | 2 +- test/src/test/java/lib/layout/IconTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pom.xml b/test/pom.xml index 79c731b5d412..538edf07a5d5 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -110,7 +110,7 @@ THE SOFTWARE. org.jenkins-ci.plugins cloudbees-folder - 6.16 + 6.17 test diff --git a/test/src/test/java/lib/layout/IconTest.java b/test/src/test/java/lib/layout/IconTest.java index 18c115dfe01b..e59e01e47def 100644 --- a/test/src/test/java/lib/layout/IconTest.java +++ b/test/src/test/java/lib/layout/IconTest.java @@ -142,7 +142,7 @@ public void testTasks() throws Exception { assertIconToImageOkay(taskDivs.get(0).getElementsByTagName("img").get(0), "/images/svgs/up.svg", "icon-up icon-md"); // this is loading the png from cloudbees-folder plugin // when this is swapped to an SVG and the dep updated this test will need to change - assertIconToImageOkay(taskDivs.get(1).getElementsByTagName("img").get(0), "/images/24x24/folder.png", "icon-folder icon-md"); + assertIconToImageOkay(taskDivs.get(1).getElementsByTagName("img").get(0), "/images/svgs/folder.svg", "icon-folder icon-md"); assertIconToImageOkay(taskDivs.get(2).getElementsByTagName("img").get(0), "/images/svgs/package.svg", "icon-package icon-xlg"); assertIconToImageOkay(taskDivs.get(3).getElementsByTagName("img").get(0), "/images/svgs/package.svg", "icon-package icon-xlg"); assertIconToImageOkay(taskDivs.get(4).getElementsByTagName("img").get(0), "/images/svgs/package.svg", "icon-package icon-xlg"); From a5f13e65ef8498ae6f1f3de38d26aa5e19d91919 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 22 Dec 2021 06:15:14 -0800 Subject: [PATCH 46/58] Triage some SpotBugs violations (#6049) --- core/src/main/java/hudson/ExtensionList.java | 5 +- core/src/main/java/hudson/WebAppMain.java | 19 --- .../main/java/hudson/console/ConsoleNote.java | 2 + .../main/java/hudson/model/AbstractItem.java | 1 + .../model/AutoCompletionCandidates.java | 2 + core/src/main/java/hudson/model/Cause.java | 2 + .../hudson/model/DirectoryBrowserSupport.java | 1 + .../java/hudson/model/FreeStyleProject.java | 3 + core/src/main/java/hudson/model/Queue.java | 1 + .../java/hudson/model/ResourceController.java | 2 + .../model/StringParameterDefinition.java | 2 +- .../main/java/hudson/model/UpdateCenter.java | 1 + .../hudson/node_monitors/ClockMonitor.java | 3 + .../node_monitors/SwapSpaceMonitor.java | 2 + .../node_monitors/TemporarySpaceMonitor.java | 3 + core/src/main/java/hudson/security/ACL.java | 2 +- ...trolOnceLoggedInAuthorizationStrategy.java | 2 + .../hudson/security/LegacySecurityRealm.java | 2 + .../main/java/hudson/slaves/JNLPLauncher.java | 1 + core/src/main/java/hudson/tasks/Maven.java | 1 + .../main/java/hudson/triggers/SCMTrigger.java | 5 +- .../main/java/hudson/util/ProcessTree.java | 2 +- .../java/hudson/util/RemotingDiagnostics.java | 2 + .../main/java/hudson/util/jna/Advapi32.java | 1 + .../main/java/hudson/util/jna/Options.java | 2 + .../fingerprints/FileFingerprintStorage.java | 2 + .../java/jenkins/install/SetupWizard.java | 1 + core/src/main/java/jenkins/model/Jenkins.java | 1 + .../java/jenkins/model/PeepholePermalink.java | 2 + .../jenkins/slaves/NioChannelSelector.java | 2 +- .../main/java/jenkins/util/DirectedGraph.java | 2 + .../jenkins/util/JSONSignatureValidator.java | 2 + .../jenkins/util/groovy/GroovyHookScript.java | 2 + .../java/jenkins/util/io/PathRemover.java | 2 + .../main/java/jenkins/util/xml/XMLUtils.java | 3 + src/spotbugs/spotbugs-excludes.xml | 131 +++--------------- 36 files changed, 79 insertions(+), 140 deletions(-) diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java index c453a46b3d30..89098e86ea28 100644 --- a/core/src/main/java/hudson/ExtensionList.java +++ b/core/src/main/java/hudson/ExtensionList.java @@ -38,6 +38,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -327,7 +328,7 @@ private List> ensureLoaded() { * Chooses the object that locks the loading of the extension instances. */ protected Object getLoadLock() { - return jenkins.lookup.setIfNull(Lock.class,new Lock()); + return Objects.requireNonNull(jenkins).lookup.setIfNull(Lock.class,new Lock()); } /** @@ -379,7 +380,7 @@ protected List> load() { LOGGER.log(Level.FINER, String.format("Loading ExtensionList '%s' from", extensionType.getName()), new Throwable("Only present for stacktrace information")); } - return jenkins.getPluginManager().getPluginStrategy().findComponents(extensionType, hudson); + return Objects.requireNonNull(jenkins).getPluginManager().getPluginStrategy().findComponents(extensionType, hudson); } /** diff --git a/core/src/main/java/hudson/WebAppMain.java b/core/src/main/java/hudson/WebAppMain.java index cfa828ec9c25..f3becef4932f 100644 --- a/core/src/main/java/hudson/WebAppMain.java +++ b/core/src/main/java/hudson/WebAppMain.java @@ -71,8 +71,6 @@ import javax.servlet.ServletContextListener; import javax.servlet.ServletResponse; import javax.servlet.SessionTrackingMode; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.TransformerFactoryConfigurationError; import jenkins.model.Jenkins; import jenkins.util.JenkinsJVM; import jenkins.util.SystemProperties; @@ -235,23 +233,6 @@ public Locale get() { throw new NoTempDir(e); } - // Tomcat breaks XSLT with JDK 5.0 and onward. Check if that's the case, and if so, - // try to correct it - try { - TransformerFactory.newInstance(); - // if this works we are all happy - } catch (TransformerFactoryConfigurationError x) { - // no it didn't. - LOGGER.log(WARNING, "XSLT not configured correctly. Hudson will try to fix this. See https://bz.apache.org/bugzilla/show_bug.cgi?id=40895 for more details",x); - System.setProperty(TransformerFactory.class.getName(),"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); - try { - TransformerFactory.newInstance(); - LOGGER.info("XSLT is set to the JAXP RI in JRE"); - } catch(TransformerFactoryConfigurationError y) { - LOGGER.log(SEVERE, "Failed to correct the problem."); - } - } - installExpressionFactory(event); context.setAttribute(APP,new HudsonIsLoading()); diff --git a/core/src/main/java/hudson/console/ConsoleNote.java b/core/src/main/java/hudson/console/ConsoleNote.java index 443625350cb2..ee7896895dec 100644 --- a/core/src/main/java/hudson/console/ConsoleNote.java +++ b/core/src/main/java/hudson/console/ConsoleNote.java @@ -325,10 +325,12 @@ public static void skip(DataInputStream in) throws IOException { * Preamble of the encoded form. ANSI escape sequence to stop echo back * plus a few magic characters. */ + @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "used in several plugins") public static final byte[] PREAMBLE = PREAMBLE_STR.getBytes(); /** * Post amble is the ANSI escape sequence that brings back the echo. */ + @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "used in several plugins") public static final byte[] POSTAMBLE = POSTAMBLE_STR.getBytes(); /** diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java index e16836c8a807..58a1d90bc5cb 100644 --- a/core/src/main/java/hudson/model/AbstractItem.java +++ b/core/src/main/java/hudson/model/AbstractItem.java @@ -353,6 +353,7 @@ protected void checkRename(@NonNull String newName) throws Failure { * Not all the Items need to support this operation, but if you decide to do so, * you can use this method. */ + @SuppressFBWarnings(value = "SWL_SLEEP_WITH_LOCK_HELD", justification = "no big deal") protected void renameTo(final String newName) throws IOException { if (!isNameEditable()) { diff --git a/core/src/main/java/hudson/model/AutoCompletionCandidates.java b/core/src/main/java/hudson/model/AutoCompletionCandidates.java index 824915477b97..19fff10cca29 100644 --- a/core/src/main/java/hudson/model/AutoCompletionCandidates.java +++ b/core/src/main/java/hudson/model/AutoCompletionCandidates.java @@ -25,6 +25,7 @@ package hudson.model; import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.search.Search; import hudson.search.UserSearchProperty; import java.io.IOException; @@ -108,6 +109,7 @@ public static AutoCompletionCandidates ofJobNames(final Class AutoCompletionCandidates ofJobNames(final Class type, final String value, ItemGroup container) { final AutoCompletionCandidates candidates = new AutoCompletionCandidates(); class Visitor extends ItemVisitor { diff --git a/core/src/main/java/hudson/model/Cause.java b/core/src/main/java/hudson/model/Cause.java index 256fbb24eb3c..b58129f19ae1 100644 --- a/core/src/main/java/hudson/model/Cause.java +++ b/core/src/main/java/hudson/model/Cause.java @@ -26,6 +26,7 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Util; import hudson.console.ModelHyperlinkNote; import hudson.diagnosis.OldDataMonitor; @@ -129,6 +130,7 @@ public void print(TaskListener listener) { */ @Deprecated public static class LegacyCodeCause extends Cause { + @SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "for backward compatibility") private StackTraceElement [] stackTrace; public LegacyCodeCause() { stackTrace = new Exception().getStackTrace(); diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java index 5ed6f2260905..cc4c13f14dff 100644 --- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java +++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java @@ -750,6 +750,7 @@ private static final class BuildChildPaths extends MasterToSlaveCallable> buildChildPaths(VirtualFile cur, Locale locale) throws IOException { List> r = new ArrayList<>(); diff --git a/core/src/main/java/hudson/model/FreeStyleProject.java b/core/src/main/java/hudson/model/FreeStyleProject.java index 9992ffcc80c2..73234b815683 100644 --- a/core/src/main/java/hudson/model/FreeStyleProject.java +++ b/core/src/main/java/hudson/model/FreeStyleProject.java @@ -23,6 +23,7 @@ */ package hudson.model; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import jenkins.model.Jenkins; import jenkins.model.item_category.StandaloneProjectsCategory; @@ -70,10 +71,12 @@ public DescriptorImpl getDescriptor() { */ @Deprecated @Restricted(NoExternalUse.class) + @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "for backward compatibility") public static /*almost final*/ DescriptorImpl DESCRIPTOR; @Extension(ordinal=1000) @Symbol({"freeStyle","freeStyleJob"}) public static class DescriptorImpl extends AbstractProjectDescriptor { + @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "for backward compatibility") public DescriptorImpl() { DESCRIPTOR = this; } diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java index 9d3bfb1194b4..1790fec1f21a 100644 --- a/core/src/main/java/hudson/model/Queue.java +++ b/core/src/main/java/hudson/model/Queue.java @@ -1370,6 +1370,7 @@ public static java.util.concurrent.Callable wrapWithLock(java.util.concur } @Override + @SuppressFBWarnings(value = "WA_AWAIT_NOT_IN_LOOP", justification = "the caller does indeed call this method in a loop") protected void _await() throws InterruptedException { condition.await(); } diff --git a/core/src/main/java/hudson/model/ResourceController.java b/core/src/main/java/hudson/model/ResourceController.java index 3ae1b597e6e1..7c15a52f98e0 100644 --- a/core/src/main/java/hudson/model/ResourceController.java +++ b/core/src/main/java/hudson/model/ResourceController.java @@ -24,6 +24,7 @@ package hudson.model; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.util.AdaptedIterator; import java.util.AbstractCollection; import java.util.Collection; @@ -166,6 +167,7 @@ public ResourceActivity getBlockingActivity(ResourceActivity activity) { return null; } + @SuppressFBWarnings(value = "WA_NOT_IN_LOOP", justification = "the caller does indeed call this method in a loop") protected void _await() throws InterruptedException { wait(); } diff --git a/core/src/main/java/hudson/model/StringParameterDefinition.java b/core/src/main/java/hudson/model/StringParameterDefinition.java index 23a7c2794baf..1bff7a75a241 100644 --- a/core/src/main/java/hudson/model/StringParameterDefinition.java +++ b/core/src/main/java/hudson/model/StringParameterDefinition.java @@ -147,7 +147,7 @@ public String getHelpFile() { @Override public ParameterValue createValue(StaplerRequest req, JSONObject jo) { StringParameterValue value = req.bindJSON(StringParameterValue.class, jo); - if (isTrim() && value!=null) { + if (isTrim()) { value.doTrim(); } value.setDescription(getDescription()); diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index fdcaa3ada6f4..081e4e4cc816 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -1240,6 +1240,7 @@ public void postValidate(DownloadJob job, File src) throws IOException { * @throws IOException if there were problems downloading the resource. * @see DownloadJob */ + @SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_SHA1", justification = "SHA-1 is only used as a fallback if SHA-256/SHA-512 are not available") public File download(DownloadJob job, URL src) throws IOException { MessageDigest sha1 = null; MessageDigest sha256 = null; diff --git a/core/src/main/java/hudson/node_monitors/ClockMonitor.java b/core/src/main/java/hudson/node_monitors/ClockMonitor.java index 602ba646859d..fdbca6b751d7 100644 --- a/core/src/main/java/hudson/node_monitors/ClockMonitor.java +++ b/core/src/main/java/hudson/node_monitors/ClockMonitor.java @@ -23,6 +23,7 @@ */ package hudson.node_monitors; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.model.Computer; import hudson.model.Node; @@ -53,10 +54,12 @@ public ClockDifference getDifferenceFor(Computer c) { */ @Deprecated @Restricted(NoExternalUse.class) + @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "for backward compatibility") public static /*almost final*/ AbstractNodeMonitorDescriptor DESCRIPTOR; @Extension @Symbol("clock") public static class DescriptorImpl extends AbstractAsyncNodeMonitorDescriptor { + @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "for backward compatibility") public DescriptorImpl() { DESCRIPTOR = this; } diff --git a/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java b/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java index bf0cbf774473..65b0083c754c 100644 --- a/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java +++ b/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java @@ -23,6 +23,7 @@ */ package hudson.node_monitors; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.Functions; import hudson.Util; @@ -89,6 +90,7 @@ public String getColumnCaption() { @Extension @Symbol("swapSpace") public static class DescriptorImpl extends AbstractAsyncNodeMonitorDescriptor { + @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "for backward compatibility") public DescriptorImpl() { DESCRIPTOR = this; } diff --git a/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java b/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java index b5ddce5bbfb5..db814f99bed9 100644 --- a/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java +++ b/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java @@ -23,6 +23,7 @@ */ package hudson.node_monitors; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.FilePath; import hudson.model.Computer; @@ -66,10 +67,12 @@ public String getColumnCaption() { * Use injection */ @Deprecated + @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "for backward compatibility") public static /*almost final*/ DiskSpaceMonitorDescriptor DESCRIPTOR; @Extension @Symbol("tmpSpace") public static class DescriptorImpl extends DiskSpaceMonitorDescriptor { + @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "for backward compatibility") public DescriptorImpl() { DESCRIPTOR = this; } diff --git a/core/src/main/java/hudson/security/ACL.java b/core/src/main/java/hudson/security/ACL.java index 47ff187077e7..97980fa0a578 100644 --- a/core/src/main/java/hudson/security/ACL.java +++ b/core/src/main/java/hudson/security/ACL.java @@ -355,7 +355,7 @@ public String toString() { */ public static final Sid ANONYMOUS = new PrincipalSid(ANONYMOUS_USERNAME); - protected static final Sid[] AUTOMATIC_SIDS = new Sid[]{EVERYONE,ANONYMOUS}; + static final Sid[] AUTOMATIC_SIDS = new Sid[]{EVERYONE,ANONYMOUS}; /** * The username for the system user diff --git a/core/src/main/java/hudson/security/FullControlOnceLoggedInAuthorizationStrategy.java b/core/src/main/java/hudson/security/FullControlOnceLoggedInAuthorizationStrategy.java index df0363ac69cf..b30153ce8f90 100644 --- a/core/src/main/java/hudson/security/FullControlOnceLoggedInAuthorizationStrategy.java +++ b/core/src/main/java/hudson/security/FullControlOnceLoggedInAuthorizationStrategy.java @@ -23,6 +23,7 @@ */ package hudson.security; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.model.Descriptor; import java.util.Collections; @@ -96,6 +97,7 @@ public void setAllowAnonymousRead(boolean allowAnonymousRead) { @Extension @Symbol("loggedInUsersCanDoAnything") public static class DescriptorImpl extends Descriptor { + @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "for backward compatibility") public DescriptorImpl() { DESCRIPTOR = this; } diff --git a/core/src/main/java/hudson/security/LegacySecurityRealm.java b/core/src/main/java/hudson/security/LegacySecurityRealm.java index ce88c6d15f94..d690f00df7ac 100644 --- a/core/src/main/java/hudson/security/LegacySecurityRealm.java +++ b/core/src/main/java/hudson/security/LegacySecurityRealm.java @@ -23,6 +23,7 @@ */ package hudson.security; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.model.Descriptor; import java.util.ArrayList; @@ -99,6 +100,7 @@ public Filter createFilter(FilterConfig filterConfig) { @Extension @Symbol("legacy") public static class DescriptorImpl extends Descriptor { + @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "for backward compatibility") public DescriptorImpl() { DESCRIPTOR = this; } diff --git a/core/src/main/java/hudson/slaves/JNLPLauncher.java b/core/src/main/java/hudson/slaves/JNLPLauncher.java index fce275c3d75f..50f4df223b61 100644 --- a/core/src/main/java/hudson/slaves/JNLPLauncher.java +++ b/core/src/main/java/hudson/slaves/JNLPLauncher.java @@ -202,6 +202,7 @@ public String getWorkDirOptions(@NonNull Computer computer) { @Extension @Symbol("jnlp") public static class DescriptorImpl extends Descriptor { + @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "for backward compatibility") public DescriptorImpl() { DESCRIPTOR = this; } diff --git a/core/src/main/java/hudson/tasks/Maven.java b/core/src/main/java/hudson/tasks/Maven.java index ec399bec0d77..7b770097778b 100644 --- a/core/src/main/java/hudson/tasks/Maven.java +++ b/core/src/main/java/hudson/tasks/Maven.java @@ -429,6 +429,7 @@ public static final class DescriptorImpl extends BuildStepDescriptor im @CopyOnWrite private volatile MavenInstallation[] installations = new MavenInstallation[0]; + @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "for backward compatibility") public DescriptorImpl() { DESCRIPTOR = this; } diff --git a/core/src/main/java/hudson/triggers/SCMTrigger.java b/core/src/main/java/hudson/triggers/SCMTrigger.java index 8dc4b18179b9..c6339c225140 100644 --- a/core/src/main/java/hudson/triggers/SCMTrigger.java +++ b/core/src/main/java/hudson/triggers/SCMTrigger.java @@ -62,6 +62,7 @@ import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -209,7 +210,7 @@ public Collection getProjectActions() { * Returns the file that records the last/current polling activity. */ public File getLogFile() { - return new File(job.getRootDir(),"scm-polling.log"); + return new File(Objects.requireNonNull(job).getRootDir(),"scm-polling.log"); } @Extension @Symbol("pollSCM") @@ -691,7 +692,7 @@ public boolean equals(Object that) { @Override public int hashCode() { - return job.hashCode(); + return Objects.requireNonNull(job).hashCode(); } } diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 1a5bfc5774f2..4344cc4f42f9 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -2242,6 +2242,6 @@ public T act(ProcessCallable callable) throws IOException, InterruptedExc * in case there's a fatal problem. * */ - public static boolean enabled = !SystemProperties.getBoolean("hudson.util.ProcessTreeKiller.disable") + static boolean enabled = !SystemProperties.getBoolean("hudson.util.ProcessTreeKiller.disable") && !SystemProperties.getBoolean(ProcessTree.class.getName()+".disable"); } diff --git a/core/src/main/java/hudson/util/RemotingDiagnostics.java b/core/src/main/java/hudson/util/RemotingDiagnostics.java index 9bba59bbed66..c8bcecd6f498 100644 --- a/core/src/main/java/hudson/util/RemotingDiagnostics.java +++ b/core/src/main/java/hudson/util/RemotingDiagnostics.java @@ -24,6 +24,7 @@ package hudson.util; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import groovy.lang.Binding; import groovy.lang.GroovyShell; import hudson.FilePath; @@ -127,6 +128,7 @@ public ClassLoader getClassLoader() { } @Override + @SuppressFBWarnings(value = "GROOVY_SHELL", justification = "script console is a feature, not a bug") public String call() throws RuntimeException { // if we run locally, cl!=null. Otherwise the delegating classloader will be available as context classloader. if (cl==null) cl = Thread.currentThread().getContextClassLoader(); diff --git a/core/src/main/java/hudson/util/jna/Advapi32.java b/core/src/main/java/hudson/util/jna/Advapi32.java index 3f5c70acb97c..cba0c8190e99 100644 --- a/core/src/main/java/hudson/util/jna/Advapi32.java +++ b/core/src/main/java/hudson/util/jna/Advapi32.java @@ -31,6 +31,7 @@ * * @author TB */ +@SuppressFBWarnings(value = "MS_OOI_PKGPROTECT", justification = "for backward compatibility") @SuppressWarnings("UnusedReturnValue") public interface Advapi32 extends StdCallLibrary { Advapi32 INSTANCE = (Advapi32) Native.load("Advapi32", Advapi32.class, Options.UNICODE_OPTIONS); diff --git a/core/src/main/java/hudson/util/jna/Options.java b/core/src/main/java/hudson/util/jna/Options.java index 44f1652408ba..6c1a20d2a5bc 100644 --- a/core/src/main/java/hudson/util/jna/Options.java +++ b/core/src/main/java/hudson/util/jna/Options.java @@ -20,6 +20,7 @@ import com.sun.jna.win32.W32APIFunctionMapper; import com.sun.jna.win32.W32APITypeMapper; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.HashMap; import java.util.Map; @@ -27,6 +28,7 @@ * * @author TB */ +@SuppressFBWarnings(value = "MS_OOI_PKGPROTECT", justification = "for backward compatibility") public interface Options { Map UNICODE_OPTIONS = new HashMap() { { diff --git a/core/src/main/java/jenkins/fingerprints/FileFingerprintStorage.java b/core/src/main/java/jenkins/fingerprints/FileFingerprintStorage.java index 9a10790e1a09..1daf4dd771da 100644 --- a/core/src/main/java/jenkins/fingerprints/FileFingerprintStorage.java +++ b/core/src/main/java/jenkins/fingerprints/FileFingerprintStorage.java @@ -26,6 +26,7 @@ import com.thoughtworks.xstream.converters.basic.DateConverter; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.Functions; import hudson.Util; @@ -325,6 +326,7 @@ private static String messageOfParseException(Throwable throwable) { /** * Deletes a directory if it's empty. */ + @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "https://github.com/spotbugs/spotbugs/issues/756") private void deleteIfEmpty(File dir) { try { if (Files.isDirectory(dir.toPath())) { diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index 35579cd02e97..feaa0c17e7e8 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -749,6 +749,7 @@ public void init(FilterConfig cfg) throws ServletException { } @Override + @SuppressFBWarnings(value = "UNVALIDATED_REDIRECT", justification = "TODO needs triage") public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Force root requests to the setup wizard if (request instanceof HttpServletRequest && !Jenkins.get().getInstallState().isSetupComplete()) { diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index aeaf7f1649c3..dedcba153839 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -4857,6 +4857,7 @@ public void doResources(StaplerRequest req, StaplerResponse rsp) throws IOExcept * Extension list that {@link #doResources(StaplerRequest, StaplerResponse)} can serve. * This set is mutable to allow plugins to add additional extensions. */ + @SuppressFBWarnings(value = "MS_MUTABLE_COLLECTION_PKGPROTECT", justification = "mutable to allow plugins to add additional extensions") public static final Set ALLOWED_RESOURCE_EXTENSIONS = new HashSet<>(Arrays.asList( "js|css|jpeg|jpg|png|gif|html|htm".split("\\|") )); diff --git a/core/src/main/java/jenkins/model/PeepholePermalink.java b/core/src/main/java/jenkins/model/PeepholePermalink.java index a7c129548512..f6c088b596bb 100644 --- a/core/src/main/java/jenkins/model/PeepholePermalink.java +++ b/core/src/main/java/jenkins/model/PeepholePermalink.java @@ -2,6 +2,7 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.Util; import hudson.model.Job; @@ -146,6 +147,7 @@ private Run find(Run b) { } } + @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "https://github.com/spotbugs/spotbugs/issues/756") private static @NonNull Map load(@NonNull File buildDir) { Map cache = new TreeMap<>(); File storage = storageFor(buildDir); diff --git a/core/src/main/java/jenkins/slaves/NioChannelSelector.java b/core/src/main/java/jenkins/slaves/NioChannelSelector.java index fbf2c0597421..80cda778c44c 100644 --- a/core/src/main/java/jenkins/slaves/NioChannelSelector.java +++ b/core/src/main/java/jenkins/slaves/NioChannelSelector.java @@ -46,7 +46,7 @@ public void cleanUp() throws IOException { /** * Escape hatch to disable use of NIO. */ - public static boolean DISABLED = SystemProperties.getBoolean(NioChannelSelector.class.getName()+".disabled"); + static boolean DISABLED = SystemProperties.getBoolean(NioChannelSelector.class.getName()+".disabled"); private static final Logger LOGGER = Logger.getLogger(NioChannelSelector.class.getName()); } diff --git a/core/src/main/java/jenkins/util/DirectedGraph.java b/core/src/main/java/jenkins/util/DirectedGraph.java index 916e49423413..cc781cabc7ab 100644 --- a/core/src/main/java/jenkins/util/DirectedGraph.java +++ b/core/src/main/java/jenkins/util/DirectedGraph.java @@ -1,5 +1,6 @@ package jenkins.util; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; @@ -76,6 +77,7 @@ class Node { */ int lowlink; + @SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "no big deal") SCC scc; Node(N n) { diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java index 274c0cb96cd9..1c932f0dfa22 100644 --- a/core/src/main/java/jenkins/util/JSONSignatureValidator.java +++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java @@ -1,5 +1,6 @@ package jenkins.util; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.util.FormValidation; import java.io.ByteArrayInputStream; import java.io.File; @@ -52,6 +53,7 @@ public JSONSignatureValidator(String name) { /** * Verifies the signature in the update center data file. */ + @SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_SHA1", justification = "SHA-1 is only used as a fallback if SHA-512 is not available") public FormValidation verifySignature(JSONObject o) throws IOException { try { FormValidation warning = null; diff --git a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java index cf39bffbaba9..178ae3cb9b82 100644 --- a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java +++ b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java @@ -3,6 +3,7 @@ import static java.util.logging.Level.WARNING; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import groovy.lang.Binding; import groovy.lang.GroovyCodeSource; import groovy.lang.GroovyShell; @@ -129,6 +130,7 @@ protected void execute(File f) { } } + @SuppressFBWarnings(value = "GROOVY_SHELL", justification = "Groovy hook scripts are a feature, not a bug") protected void execute(GroovyCodeSource s) { try { createShell().evaluate(s); diff --git a/core/src/main/java/jenkins/util/io/PathRemover.java b/core/src/main/java/jenkins/util/io/PathRemover.java index 8a7d8b299ffc..bf8ff997d4e4 100644 --- a/core/src/main/java/jenkins/util/io/PathRemover.java +++ b/core/src/main/java/jenkins/util/io/PathRemover.java @@ -214,6 +214,7 @@ private List tryRemoveRecursive(@NonNull Path path) { return accumulatedErrors; } + @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "https://github.com/spotbugs/spotbugs/issues/756") private List tryRemoveDirectoryContents(@NonNull Path path) { Path normalized = path.normalize(); List accumulatedErrors = new ArrayList<>(); @@ -228,6 +229,7 @@ private List tryRemoveDirectoryContents(@NonNull Path path) { return accumulatedErrors; } + @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "https://github.com/spotbugs/spotbugs/issues/756") private void removeOrMakeRemovableThenRemove(@NonNull Path path) throws IOException { pathChecker.check(path); try { diff --git a/core/src/main/java/jenkins/util/xml/XMLUtils.java b/core/src/main/java/jenkins/util/xml/XMLUtils.java index 0eac9169269c..c0d59bded42e 100644 --- a/core/src/main/java/jenkins/util/xml/XMLUtils.java +++ b/core/src/main/java/jenkins/util/xml/XMLUtils.java @@ -1,6 +1,7 @@ package jenkins.util.xml; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -109,6 +110,7 @@ public static void safeTransform(@NonNull Source source, @NonNull Result out) th * @throws IOException Error reading from the steam. * @since 2.265 */ + @SuppressFBWarnings(value = "XXE_DOCUMENT", justification = "newDocumentBuilderFactory() does what FindSecBugs recommends, yet FindSecBugs cannot see this") public static @NonNull Document parse(@NonNull InputStream stream) throws SAXException, IOException { DocumentBuilder docBuilder; @@ -133,6 +135,7 @@ public static void safeTransform(@NonNull Source source, @NonNull Result out) th * @throws IOException Error reading from the steam. * @since 2.0 */ + @SuppressFBWarnings(value = "XXE_DOCUMENT", justification = "newDocumentBuilderFactory() does what FindSecBugs recommends, yet FindSecBugs cannot see this") public static @NonNull Document parse(@NonNull Reader stream) throws SAXException, IOException { DocumentBuilder docBuilder; diff --git a/src/spotbugs/spotbugs-excludes.xml b/src/spotbugs/spotbugs-excludes.xml index b86bdf5f8f91..978042fce847 100644 --- a/src/spotbugs/spotbugs-excludes.xml +++ b/src/spotbugs/spotbugs-excludes.xml @@ -7,6 +7,8 @@ + + @@ -15,6 +17,19 @@ + + + + + + + + + + + + + @@ -93,18 +108,6 @@ - - - - - - - - - - - - @@ -148,13 +151,6 @@ - - - - - - - @@ -207,30 +203,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -252,15 +224,6 @@ - - - - - - - - - @@ -364,6 +327,7 @@ + @@ -399,6 +363,7 @@ + @@ -417,6 +382,7 @@ + @@ -434,14 +400,6 @@ - - - - - - - - @@ -496,13 +454,6 @@ - - - - - - - @@ -525,25 +476,13 @@ - - - - - - - - - - - - @@ -572,13 +511,6 @@ - - - - - - - @@ -621,21 +553,6 @@ - - - - - - - - - - - - - - - @@ -643,18 +560,6 @@ - - - - - - - - - - - - From 8a5e3092566a6a9631a5bce91e7edd02643dd156 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 22 Dec 2021 20:33:59 -0800 Subject: [PATCH 47/58] Fix `DM_DEFAULT_ENCODING` SpotBugs violations (#6050) --- .../java/hudson/cli/CLIConnectionFactory.java | 3 +- .../java/hudson/cli/PrivateKeyProvider.java | 6 +-- core/src/main/java/hudson/FilePath.java | 6 +-- core/src/main/java/hudson/Proc.java | 3 +- core/src/main/java/hudson/WebAppMain.java | 3 +- core/src/main/java/hudson/cli/CLICommand.java | 21 +++++++++-- .../hudson/console/AnnotatedLargeText.java | 2 +- .../ConsoleAnnotationOutputStream.java | 2 +- .../main/java/hudson/console/ConsoleNote.java | 5 ++- .../main/java/hudson/model/AbstractBuild.java | 3 +- .../java/hudson/model/FileParameterValue.java | 3 +- core/src/main/java/hudson/model/Run.java | 4 +- .../main/java/hudson/model/UpdateCenter.java | 5 ++- .../java/hudson/model/UsageStatistics.java | 2 +- core/src/main/java/hudson/scm/SCM.java | 6 ++- .../HudsonAuthenticationEntryPoint.java | 3 +- .../security/csrf/DefaultCrumbIssuer.java | 4 +- .../main/java/hudson/util/BootFailure.java | 8 ++-- .../main/java/hudson/util/CompressedFile.java | 5 ++- .../main/java/hudson/util/ConsistentHash.java | 3 +- core/src/main/java/hudson/util/IOUtils.java | 3 +- .../java/hudson/util/LogTaskListener.java | 9 ++++- .../main/java/hudson/util/ProcessTree.java | 37 ++++++++++++++----- core/src/main/java/hudson/util/Scrambler.java | 2 +- core/src/main/java/hudson/util/Secret.java | 2 +- .../security/DefaultConfidentialStore.java | 3 +- .../slaves/restarter/UnixSlaveRestarter.java | 3 +- .../jenkins/util/JSONSignatureValidator.java | 2 +- src/spotbugs/spotbugs-excludes.xml | 32 ---------------- 29 files changed, 108 insertions(+), 82 deletions(-) diff --git a/cli/src/main/java/hudson/cli/CLIConnectionFactory.java b/cli/src/main/java/hudson/cli/CLIConnectionFactory.java index eceff79eff3c..0553dd215de5 100644 --- a/cli/src/main/java/hudson/cli/CLIConnectionFactory.java +++ b/cli/src/main/java/hudson/cli/CLIConnectionFactory.java @@ -1,5 +1,6 @@ package hudson.cli; +import java.nio.charset.StandardCharsets; import java.util.Base64; /** @@ -33,7 +34,7 @@ public CLIConnectionFactory basicAuth(String username, String password) { * Cf. {@code BasicHeaderApiTokenAuthenticator}. */ public CLIConnectionFactory basicAuth(String userInfo) { - return authorization("Basic " + Base64.getEncoder().encodeToString(userInfo.getBytes())); + return authorization("Basic " + Base64.getEncoder().encodeToString(userInfo.getBytes(StandardCharsets.UTF_8))); } /** diff --git a/cli/src/main/java/hudson/cli/PrivateKeyProvider.java b/cli/src/main/java/hudson/cli/PrivateKeyProvider.java index 1ab1eff33049..f283053f9cb5 100644 --- a/cli/src/main/java/hudson/cli/PrivateKeyProvider.java +++ b/cli/src/main/java/hudson/cli/PrivateKeyProvider.java @@ -23,7 +23,6 @@ */ package hudson.cli; -import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.logging.Level.FINE; import java.io.ByteArrayInputStream; @@ -32,6 +31,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Paths; @@ -134,7 +134,7 @@ private static String readPemFile(File f) throws IOException{ DataInputStream dis = new DataInputStream(is)) { byte[] bytes = new byte[(int) f.length()]; dis.readFully(bytes); - return new String(bytes); + return new String(bytes, StandardCharsets.UTF_8); } catch (InvalidPathException e) { throw new IOException(e); } @@ -143,7 +143,7 @@ private static String readPemFile(File f) throws IOException{ public static KeyPair loadKey(String pemString, String passwd) throws IOException, GeneralSecurityException { Iterable itr = SecurityUtils.loadKeyPairIdentities(null, new PathResource(Paths.get("key")), - new ByteArrayInputStream(pemString.getBytes(UTF_8)), + new ByteArrayInputStream(pemString.getBytes(StandardCharsets.UTF_8)), FilePasswordProvider.of(passwd)); long numLoaded = itr == null ? 0 : StreamSupport.stream(itr.spliterator(), false).count(); if (numLoaded <= 0) { diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 16e060f9d23c..cadb8cc257e9 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -86,6 +86,7 @@ import java.net.URI; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.Charset; import java.nio.file.FileSystemException; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -2279,7 +2280,7 @@ private static class ReadToString extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; @Override public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - return new String(Files.readAllBytes(fileToPath(f))); + return new String(Files.readAllBytes(fileToPath(f)), Charset.defaultCharset()); } } @@ -2336,7 +2337,7 @@ private static class Write extends MasterToSlaveFileCallable { public Void invoke(File f, VirtualChannel channel) throws IOException { mkdirs(f.getParentFile()); try (OutputStream fos = Files.newOutputStream(fileToPath(f)); - Writer w = encoding != null ? new OutputStreamWriter(fos, encoding) : new OutputStreamWriter(fos)) { + Writer w = encoding != null ? new OutputStreamWriter(fos, encoding) : new OutputStreamWriter(fos, Charset.defaultCharset())) { w.write(content); } return null; @@ -3611,5 +3612,4 @@ public boolean accept(File file) { } private static final long serialVersionUID = 1L; } - } diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java index dd94d2097c39..24cd16552295 100644 --- a/core/src/main/java/hudson/Proc.java +++ b/core/src/main/java/hudson/Proc.java @@ -39,6 +39,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.Charset; import java.util.Locale; import java.util.Map; import java.util.concurrent.CancellationException; @@ -354,7 +355,7 @@ public int join() throws InterruptedException, IOException { // } catch (IOException x) { // LOGGER.log(Level.FINE,"stderr termination failed",x); // } - out.write(msg.getBytes()); + out.write(msg.getBytes(Charset.defaultCharset())); out.write('\n'); } return r; diff --git a/core/src/main/java/hudson/WebAppMain.java b/core/src/main/java/hudson/WebAppMain.java index f3becef4932f..b0596a66374d 100644 --- a/core/src/main/java/hudson/WebAppMain.java +++ b/core/src/main/java/hudson/WebAppMain.java @@ -50,6 +50,7 @@ import java.io.OutputStream; import java.net.URL; import java.net.URLClassLoader; +import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.StandardOpenOption; @@ -293,7 +294,7 @@ public void joinInit() throws InterruptedException { */ private void recordBootAttempt(File home) { try (OutputStream o=Files.newOutputStream(BootFailure.getBootFailureFile(home).toPath(), StandardOpenOption.CREATE, StandardOpenOption.APPEND)) { - o.write((new Date() + System.getProperty("line.separator", "\n")).getBytes()); + o.write((new Date() + System.getProperty("line.separator", "\n")).getBytes(Charset.defaultCharset())); } catch (IOException | InvalidPathException e) { LOGGER.log(WARNING, "Failed to record boot attempts",e); } diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java index 198736dc9fca..d27d0b6beff5 100644 --- a/core/src/main/java/hudson/cli/CLICommand.java +++ b/core/src/main/java/hudson/cli/CLICommand.java @@ -40,6 +40,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import java.nio.charset.Charset; @@ -404,7 +405,11 @@ protected void printUsage(PrintStream stderr, CmdLineParser p) { public final String getSingleLineSummary() { ByteArrayOutputStream out = new ByteArrayOutputStream(); getCmdLineParser().printSingleLineUsage(out); - return out.toString(); + try { + return out.toString(getClientCharset().name()); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } } /** @@ -414,7 +419,11 @@ public final String getSingleLineSummary() { public final String getUsage() { ByteArrayOutputStream out = new ByteArrayOutputStream(); getCmdLineParser().printUsage(out); - return out.toString(); + try { + return out.toString(getClientCharset().name()); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } } /** @@ -427,7 +436,11 @@ public final String getLongDescription() { printUsageSummary(ps); ps.close(); - return out.toString(); + try { + return out.toString(getClientCharset().name()); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } } /** @@ -457,7 +470,7 @@ public void setClientCharset(@NonNull Charset encoding) { this.encoding = encoding; } - protected @NonNull Charset getClientCharset() throws IOException, InterruptedException { + protected @NonNull Charset getClientCharset() { if (encoding != null) { return encoding; } diff --git a/core/src/main/java/hudson/console/AnnotatedLargeText.java b/core/src/main/java/hudson/console/AnnotatedLargeText.java index af70227ad1a5..dafff632d305 100644 --- a/core/src/main/java/hudson/console/AnnotatedLargeText.java +++ b/core/src/main/java/hudson/console/AnnotatedLargeText.java @@ -186,7 +186,7 @@ public long writeHtmlTo(long start, Writer w) throws IOException { oos.close(); StaplerResponse rsp = Stapler.getCurrentResponse(); if (rsp!=null) - rsp.setHeader("X-ConsoleAnnotator", new String(Base64.getEncoder().encode(baos.toByteArray()))); + rsp.setHeader("X-ConsoleAnnotator", Base64.getEncoder().encodeToString(baos.toByteArray())); return r; } diff --git a/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java b/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java index 00a84b6f4612..6e70a743d824 100644 --- a/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java +++ b/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java @@ -121,7 +121,7 @@ public ConsoleAnnotator annotate(T context, MarkupText text) { } } catch (IOException | ClassNotFoundException e) { // if we failed to resurrect an annotation, ignore it. - LOGGER.log(Level.FINE, "Failed to resurrect annotation from \"" + StringEscapeUtils.escapeJava(new String(in, next, rest)) + "\"", e); + LOGGER.log(Level.FINE, "Failed to resurrect annotation from \"" + StringEscapeUtils.escapeJava(new String(in, next, rest, Charset.defaultCharset())) + "\"", e); } int bytesUsed = rest - b.available(); // bytes consumed by annotations diff --git a/core/src/main/java/hudson/console/ConsoleNote.java b/core/src/main/java/hudson/console/ConsoleNote.java index ee7896895dec..62a9349c02c0 100644 --- a/core/src/main/java/hudson/console/ConsoleNote.java +++ b/core/src/main/java/hudson/console/ConsoleNote.java @@ -43,6 +43,7 @@ import java.io.OutputStream; import java.io.Serializable; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -326,12 +327,12 @@ public static void skip(DataInputStream in) throws IOException { * plus a few magic characters. */ @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "used in several plugins") - public static final byte[] PREAMBLE = PREAMBLE_STR.getBytes(); + public static final byte[] PREAMBLE = PREAMBLE_STR.getBytes(StandardCharsets.UTF_8); /** * Post amble is the ANSI escape sequence that brings back the echo. */ @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "used in several plugins") - public static final byte[] POSTAMBLE = POSTAMBLE_STR.getBytes(); + public static final byte[] POSTAMBLE = POSTAMBLE_STR.getBytes(StandardCharsets.UTF_8); /** * Locates the preamble in the given buffer. diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index 0a7f1cb68323..add476c3cea7 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -65,6 +65,7 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.lang.ref.WeakReference; +import java.nio.channels.ClosedByInterruptException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; @@ -668,7 +669,7 @@ public void defaultCheckout() throws IOException, InterruptedException { } } catch (AbortException e) { listener.error(e.getMessage()); - } catch (InterruptedIOException e) { + } catch (ClosedByInterruptException | InterruptedIOException e) { throw (InterruptedException)new InterruptedException().initCause(e); } catch (IOException e) { // checkout error not yet reported diff --git a/core/src/main/java/hudson/model/FileParameterValue.java b/core/src/main/java/hudson/model/FileParameterValue.java index b3e5c146cd9d..cc9f39faefee 100644 --- a/core/src/main/java/hudson/model/FileParameterValue.java +++ b/core/src/main/java/hudson/model/FileParameterValue.java @@ -36,6 +36,7 @@ import java.io.OutputStream; import java.io.UncheckedIOException; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.nio.file.Files; import java.util.regex.Pattern; import jenkins.util.SystemProperties; @@ -302,7 +303,7 @@ public String getString(String encoding) throws UnsupportedEncodingException { @Override public String getString() { - return new String(get()); + return new String(get(), Charset.defaultCharset()); } @Override diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index e331a9e0c172..33673abfcdc3 100644 --- a/core/src/main/java/hudson/model/Run.java +++ b/core/src/main/java/hudson/model/Run.java @@ -1509,11 +1509,11 @@ public Collection getBuildFingerprints() { } String message = "No such file: " + logFile; - return new ByteArrayInputStream(charset != null ? message.getBytes(charset) : message.getBytes()); + return new ByteArrayInputStream(charset != null ? message.getBytes(charset) : message.getBytes(Charset.defaultCharset())); } public @NonNull Reader getLogReader() throws IOException { - if (charset==null) return new InputStreamReader(getLogInputStream()); + if (charset==null) return new InputStreamReader(getLogInputStream(),Charset.defaultCharset()); else return new InputStreamReader(getLogInputStream(),charset); } diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index 081e4e4cc816..e3b0dcb40f77 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -68,6 +68,7 @@ import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.Files; import java.nio.file.InvalidPathException; @@ -2025,11 +2026,11 @@ private static VerificationResult verifyChecksums(String expectedDigest, String } if (caseSensitive) { - if (MessageDigest.isEqual(expectedDigest.getBytes(), actualDigest.getBytes())) { + if (MessageDigest.isEqual(expectedDigest.getBytes(StandardCharsets.US_ASCII), actualDigest.getBytes(StandardCharsets.US_ASCII))) { return VerificationResult.PASS; } } else { - if (MessageDigest.isEqual(expectedDigest.toLowerCase().getBytes(), actualDigest.toLowerCase().getBytes())) { + if (MessageDigest.isEqual(expectedDigest.toLowerCase().getBytes(StandardCharsets.US_ASCII), actualDigest.toLowerCase().getBytes(StandardCharsets.US_ASCII))) { return VerificationResult.PASS; } } diff --git a/core/src/main/java/hudson/model/UsageStatistics.java b/core/src/main/java/hudson/model/UsageStatistics.java index bc292db26c36..a62031bfc406 100644 --- a/core/src/main/java/hudson/model/UsageStatistics.java +++ b/core/src/main/java/hudson/model/UsageStatistics.java @@ -193,7 +193,7 @@ public String getStatData() throws IOException { o.write(w); } - return new String(Base64.getEncoder().encode(baos.toByteArray())); + return Base64.getEncoder().encodeToString(baos.toByteArray()); } catch (Throwable e) { // the exception could be GeneralSecurityException, InvalidParameterException or any other depending on the security provider you have installed LOG.log(Level.INFO, "Usage statistics could not be sent ({0})", e.getMessage()); LOG.log(Level.FINE, "Error sending usage statistics", e); diff --git a/core/src/main/java/hudson/scm/SCM.java b/core/src/main/java/hudson/scm/SCM.java index 7b94b07e7dd5..1e2fdde83ec8 100644 --- a/core/src/main/java/hudson/scm/SCM.java +++ b/core/src/main/java/hudson/scm/SCM.java @@ -52,8 +52,10 @@ import hudson.security.PermissionScope; import hudson.tasks.Builder; import java.io.File; -import java.io.FileWriter; import java.io.IOException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -739,7 +741,7 @@ protected final boolean createEmptyChangeLog(File changelogFile, BuildListener l * @since 1.568 */ protected final void createEmptyChangeLog(@NonNull File changelogFile, @NonNull TaskListener listener, @NonNull String rootTag) throws IOException { - try (FileWriter w = new FileWriter(changelogFile)) { + try (Writer w = Files.newBufferedWriter(Util.fileToPath(changelogFile), Charset.defaultCharset())) { w.write("<"+rootTag +"/>"); } } diff --git a/core/src/main/java/hudson/security/HudsonAuthenticationEntryPoint.java b/core/src/main/java/hudson/security/HudsonAuthenticationEntryPoint.java index 229c7974b521..3acab22bf065 100644 --- a/core/src/main/java/hudson/security/HudsonAuthenticationEntryPoint.java +++ b/core/src/main/java/hudson/security/HudsonAuthenticationEntryPoint.java @@ -31,6 +31,7 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.text.MessageFormat; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -100,7 +101,7 @@ public void commence(HttpServletRequest req, HttpServletResponse rsp, Authentica PrintWriter out; try { - out = new PrintWriter(new OutputStreamWriter(rsp.getOutputStream())); + out = new PrintWriter(new OutputStreamWriter(rsp.getOutputStream(), StandardCharsets.UTF_8)); } catch (IllegalStateException e) { out = rsp.getWriter(); } diff --git a/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java b/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java index e625759f37fd..a5ec4a26ad72 100644 --- a/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java +++ b/core/src/main/java/hudson/security/csrf/DefaultCrumbIssuer.java @@ -83,8 +83,8 @@ protected synchronized String issueCrumb(ServletRequest request, String salt) { buffer.append(req.getSession().getId()); } - md.update(buffer.toString().getBytes()); - return Util.toHexString(md.digest(salt.getBytes())); + md.update(buffer.toString().getBytes(StandardCharsets.UTF_8)); + return Util.toHexString(md.digest(salt.getBytes(StandardCharsets.US_ASCII))); } } return null; diff --git a/core/src/main/java/hudson/util/BootFailure.java b/core/src/main/java/hudson/util/BootFailure.java index 23dff92dcd84..0de96ea8bd1b 100644 --- a/core/src/main/java/hudson/util/BootFailure.java +++ b/core/src/main/java/hudson/util/BootFailure.java @@ -4,8 +4,10 @@ import hudson.WebAppMain; import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; @@ -60,7 +62,7 @@ protected List loadAttempts(File home) { File f = getBootFailureFile(home); try { if (f.exists()) { - try (BufferedReader failureFileReader = new BufferedReader(new FileReader(f))) { + try (BufferedReader failureFileReader = Files.newBufferedReader(f.toPath(), Charset.defaultCharset())) { String line; DateFormat df = DateFormat.getDateInstance(); while ((line=failureFileReader.readLine())!=null) { @@ -72,7 +74,7 @@ protected List loadAttempts(File home) { } } } - } catch (IOException e) { + } catch (IOException | InvalidPathException e) { LOGGER.log(Level.WARNING,"Failed to parse "+f,e); } } diff --git a/core/src/main/java/hudson/util/CompressedFile.java b/core/src/main/java/hudson/util/CompressedFile.java index 48add2d49f10..648972716144 100644 --- a/core/src/main/java/hudson/util/CompressedFile.java +++ b/core/src/main/java/hudson/util/CompressedFile.java @@ -34,6 +34,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; +import java.nio.charset.Charset; import java.nio.file.Files; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; @@ -101,7 +102,9 @@ public InputStream read() throws IOException { /** * Loads the file content as a string. + * @deprecated removed without replacement */ + @Deprecated public String loadAsString() throws IOException { long sizeGuess; if(file.exists()) @@ -115,7 +118,7 @@ public String loadAsString() throws IOException { StringBuilder str = new StringBuilder((int)sizeGuess); try (InputStream is = read(); - Reader r = new InputStreamReader(is)) { + Reader r = new InputStreamReader(is, Charset.defaultCharset())) { char[] buf = new char[8192]; int len; while((len=r.read(buf,0,buf.length))>0) diff --git a/core/src/main/java/hudson/util/ConsistentHash.java b/core/src/main/java/hudson/util/ConsistentHash.java index 76c7c8b872f3..3a2d983f3c0c 100644 --- a/core/src/main/java/hudson/util/ConsistentHash.java +++ b/core/src/main/java/hudson/util/ConsistentHash.java @@ -24,6 +24,7 @@ package hudson.util; import hudson.util.Iterators.DuplicateFilterIterator; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -304,7 +305,7 @@ private synchronized void refreshTable() { private int digest(String s) { try { MessageDigest messageDigest = createMessageDigest(); - messageDigest.update(s.getBytes()); + messageDigest.update(s.getBytes(StandardCharsets.UTF_8)); byte[] digest = messageDigest.digest(); // 16 bytes -> 4 bytes diff --git a/core/src/main/java/hudson/util/IOUtils.java b/core/src/main/java/hudson/util/IOUtils.java index c0b92536ea76..e2eab5dd4514 100644 --- a/core/src/main/java/hudson/util/IOUtils.java +++ b/core/src/main/java/hudson/util/IOUtils.java @@ -17,6 +17,7 @@ import java.io.Reader; import java.io.StringWriter; import java.io.Writer; +import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.util.Collection; @@ -148,7 +149,7 @@ public static int mode(File f) throws PosixException { */ public static String readFirstLine(InputStream is, String encoding) throws IOException { try (BufferedReader reader = new BufferedReader( - encoding == null ? new InputStreamReader(is) : new InputStreamReader(is, encoding))) { + encoding == null ? new InputStreamReader(is, Charset.defaultCharset()) : new InputStreamReader(is, encoding))) { return reader.readLine(); } } diff --git a/core/src/main/java/hudson/util/LogTaskListener.java b/core/src/main/java/hudson/util/LogTaskListener.java index 33b2ca2318ad..7d696ada4632 100644 --- a/core/src/main/java/hudson/util/LogTaskListener.java +++ b/core/src/main/java/hudson/util/LogTaskListener.java @@ -31,6 +31,8 @@ import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; @@ -86,7 +88,12 @@ public void write(int b) throws IOException { @Override public void flush() throws IOException { if (baos.size() > 0) { - LogRecord lr = new LogRecord(level, baos.toString()); + LogRecord lr; + try { + lr = new LogRecord(level, baos.toString(StandardCharsets.UTF_8.name())); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } lr.setLoggerName(logger.getName()); lr.setSourceClassName(caller.getClassName()); lr.setSourceMethodName(caller.getMethodName()); diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 4344cc4f42f9..f9d140f51ce5 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -50,15 +50,17 @@ import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.File; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.ObjectStreamException; import java.io.RandomAccessFile; import java.io.Serializable; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -983,7 +985,7 @@ class LinuxProcess extends UnixProcess { LinuxProcess(int pid) throws IOException { super(pid); - try (BufferedReader r = new BufferedReader(new FileReader(getFile("status")))) { + try (BufferedReader r = Files.newBufferedReader(Util.fileToPath(getFile("status")), StandardCharsets.UTF_8)) { String line; while((line=r.readLine())!=null) { line=line.toLowerCase(Locale.ENGLISH); @@ -1015,7 +1017,7 @@ public synchronized List getArguments() { for (int i = 0; i < cmdline.length; i++) { byte b = cmdline[i]; if(b==0) { - arguments.add(new String(cmdline,pos,i-pos)); + arguments.add(new String(cmdline,pos,i-pos, StandardCharsets.UTF_8)); pos=i+1; } } @@ -1039,7 +1041,7 @@ public synchronized EnvVars getEnvironmentVariables() { for (int i = 0; i < environ.length; i++) { byte b = environ[i]; if(b==0) { - envVars.addLine(new String(environ,pos,i-pos)); + envVars.addLine(new String(environ,pos,i-pos, StandardCharsets.UTF_8)); pos=i+1; } } @@ -1345,7 +1347,12 @@ private String readLine(int fd, long addr, String prefix) throws IOException { buf.write(ch); addr++; } - String line = buf.toString(); + String line; + try { + line = buf.toString(StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } if(LOGGER.isLoggable(FINEST)) LOGGER.finest(prefix+" was "+line); return line; @@ -1616,7 +1623,12 @@ private String readLine(int fd, long addr, String prefix) throws IOException { buf.write(ch); addr++; } - String line = buf.toString(); + String line; + try { + line = buf.toString(StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } if(LOGGER.isLoggable(FINEST)) LOGGER.finest(prefix+" was "+line); return line; @@ -1779,13 +1791,16 @@ byte peek() { return getByte(offset); } - @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "TODO needs triage") String readString() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte ch; while(offset < length && (ch = getByte(offset++))!='\0') baos.write(ch); - return baos.toString(); + try { + return baos.toString(StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } } void skip0() { @@ -2098,7 +2113,11 @@ private void parse(Memory m, NativeLong size, Consumer consumer) { while ((ch = m.getByte(offset++)) != '\0') { baos.write(ch); } - consumer.accept(baos.toString()); + try { + consumer.accept(baos.toString(StandardCharsets.UTF_8.name())); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } baos.reset(); } } diff --git a/core/src/main/java/hudson/util/Scrambler.java b/core/src/main/java/hudson/util/Scrambler.java index 374a1e9ba21d..283a80347bad 100644 --- a/core/src/main/java/hudson/util/Scrambler.java +++ b/core/src/main/java/hudson/util/Scrambler.java @@ -40,7 +40,7 @@ public class Scrambler { public static String scramble(String secret) { if(secret==null) return null; - return new String(Base64.getEncoder().encode(secret.getBytes(StandardCharsets.UTF_8))); + return Base64.getEncoder().encodeToString(secret.getBytes(StandardCharsets.UTF_8)); } public static String descramble(String scrambled) { diff --git a/core/src/main/java/hudson/util/Secret.java b/core/src/main/java/hudson/util/Secret.java index 45385af68e0d..6da075b331a5 100644 --- a/core/src/main/java/hudson/util/Secret.java +++ b/core/src/main/java/hudson/util/Secret.java @@ -148,7 +148,7 @@ public String getEncryptedValue() { System.arraycopy(iv, 0, payload, pos, iv.length); pos+=iv.length; System.arraycopy(encrypted, 0, payload, pos, encrypted.length); - return "{"+new String(Base64.getEncoder().encode(payload))+"}"; + return "{" + Base64.getEncoder().encodeToString(payload) + "}"; } catch (GeneralSecurityException e) { throw new Error(e); // impossible } diff --git a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java index d24bc837dd4a..49513a02ef60 100644 --- a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java +++ b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.security.GeneralSecurityException; @@ -151,5 +152,5 @@ public byte[] randomBytes(int size) { return random; } - private static final byte[] MAGIC = "::::MAGIC::::".getBytes(); + private static final byte[] MAGIC = "::::MAGIC::::".getBytes(StandardCharsets.US_ASCII); } diff --git a/core/src/main/java/jenkins/slaves/restarter/UnixSlaveRestarter.java b/core/src/main/java/jenkins/slaves/restarter/UnixSlaveRestarter.java index d0114c85fa8b..8b7aeeefbdf5 100644 --- a/core/src/main/java/jenkins/slaves/restarter/UnixSlaveRestarter.java +++ b/core/src/main/java/jenkins/slaves/restarter/UnixSlaveRestarter.java @@ -13,6 +13,7 @@ import hudson.Extension; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.logging.Logger; import jenkins.util.JavaVMArguments; @@ -107,7 +108,7 @@ private static String resolveSymlink(File link) throws IOException { byte[] buf = new byte[r]; m.read(0, buf, 0, r); - return new String(buf); + return new String(buf, StandardCharsets.UTF_8); } throw new IOException("Failed to readlink " + link); diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java index 1c932f0dfa22..a6bd08c91a42 100644 --- a/core/src/main/java/jenkins/util/JSONSignatureValidator.java +++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java @@ -241,7 +241,7 @@ private boolean verifySignature(Signature signature, String providedSignature) { * Utility method supporting both possible digest formats: Base64 and Hex */ private boolean digestMatches(byte[] digest, String providedDigest) { - return providedDigest.equalsIgnoreCase(Hex.encodeHexString(digest)) || providedDigest.equalsIgnoreCase(new String(Base64.getEncoder().encode(digest))); + return providedDigest.equalsIgnoreCase(Hex.encodeHexString(digest)) || providedDigest.equalsIgnoreCase(Base64.getEncoder().encodeToString(digest)); } diff --git a/src/spotbugs/spotbugs-excludes.xml b/src/spotbugs/spotbugs-excludes.xml index 978042fce847..9f5927b041b1 100644 --- a/src/spotbugs/spotbugs-excludes.xml +++ b/src/spotbugs/spotbugs-excludes.xml @@ -51,48 +51,16 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From e16c5eb2ca034f11c198fa8471a9c1b6a0c42e2e Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Thu, 23 Dec 2021 08:19:52 -0800 Subject: [PATCH 48/58] Miscellaneous test cleanup (#6097) --- .../java/hudson/cli/ConsoleCommandTest.java | 56 +++++-------------- .../hudson/cli/DeleteBuildsCommandTest.java | 3 +- .../java/hudson/cli/RunRangeCommand2Test.java | 17 ++---- .../test/java/hudson/model/ExecutorTest.java | 15 +++-- .../LabelLoadStatisticsQueueLengthTest.java | 9 +-- .../src/test/java/hudson/model/QueueTest.java | 32 ++++++----- .../src/test/java/hudson/tasks/MavenTest.java | 48 ++++++---------- .../java/hudson/triggers/SCMTriggerTest.java | 9 +-- 8 files changed, 71 insertions(+), 118 deletions(-) diff --git a/test/src/test/java/hudson/cli/ConsoleCommandTest.java b/test/src/test/java/hudson/cli/ConsoleCommandTest.java index ea99d6508f09..e3a232b4ad4b 100644 --- a/test/src/test/java/hudson/cli/ConsoleCommandTest.java +++ b/test/src/test/java/hudson/cli/ConsoleCommandTest.java @@ -31,13 +31,12 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertNotNull; import hudson.Functions; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import hudson.model.Item; -import hudson.model.Result; import hudson.model.labels.LabelAtom; import hudson.tasks.BatchFile; import hudson.tasks.Shell; @@ -204,23 +203,10 @@ public class ConsoleCommandTest { project.getBuildersList().add(new Shell("echo start - ${BUILD_NUMBER}\nsleep 10s\n" + "echo after sleep - ${BUILD_NUMBER}")); } - if (!project.scheduleBuild(0)) { - fail("Job wasn't scheduled properly"); - } - - // Wait until project is started (at least 1s) - while(!project.isBuilding()) { - System.out.println("Waiting for build to start and sleep 1s..."); - Thread.sleep(1000); - } - - // Wait for the first message - if(!project.getBuildByNumber(1).getLog().contains("start - 1")) { - Thread.sleep(1000); - } - - assertThat(project.getBuildByNumber(1).getLog(), containsString("start - 1")); - assertThat(project.getBuildByNumber(1).getLog(), not(containsString("after sleep - 1"))); + FreeStyleBuild build = project.scheduleBuild2(0).waitForStart(); + j.waitForMessage("start - 1", build); + j.assertLogContains("start - 1", build); + j.assertLogNotContains("after sleep - 1", build); CLICommandInvoker.Result result = command .authorizedTo(Jenkins.READ, Item.READ, Item.BUILD) @@ -237,9 +223,8 @@ public class ConsoleCommandTest { assertThat(result, succeeded()); assertThat(result.stdout(), containsString("after sleep - 1")); - assertThat(project.getBuildByNumber(1).isBuilding(), equalTo(false)); - assertThat(project.getBuildByNumber(1).getResult(), equalTo(Result.SUCCESS)); - assertThat(project.getBuildByNumber(1).getLog(), containsString("after sleep - 1")); + j.assertBuildStatusSuccess(j.waitForCompletion(build)); + j.assertLogContains("after sleep - 1", build); } @Test public void consoleShouldSuccessWithLastNLines() throws Exception { @@ -277,23 +262,11 @@ public class ConsoleCommandTest { + "echo 6\necho 7\necho 8\necho 9")); } - if (!project.scheduleBuild(0)) { - fail("Job wasn't scheduled properly"); - } - - // Wait until project is started (at least 1s) - while(!project.isBuilding()) { - System.out.println("Waiting for build to start and sleep 1s..."); - Thread.sleep(1000); - } + FreeStyleBuild build = project.scheduleBuild2(0).waitForStart(); - // Wait for the first sleep - if(!project.getBuildByNumber(1).getLog().contains("echo 5")) { - Thread.sleep(1000); - } - - assertThat(project.getBuildByNumber(1).getLog(), containsString("echo 5")); - assertThat(project.getBuildByNumber(1).getLog(), not(containsString("echo 6"))); + j.waitForMessage("echo 5", build); + j.assertLogContains("echo 5", build); + j.assertLogNotContains("echo 6", build); CLICommandInvoker.Result result = command .authorizedTo(Jenkins.READ, Item.READ, Item.BUILD) @@ -305,9 +278,8 @@ public class ConsoleCommandTest { assertThat(result.stdout(), containsString("echo 6")); assertThat(result.stdout(), containsString("echo 9")); - assertThat(project.getBuildByNumber(1).isBuilding(), equalTo(false)); - assertThat(project.getBuildByNumber(1).getResult(), equalTo(Result.SUCCESS)); - assertThat(project.getBuildByNumber(1).getLog(), containsString("echo 9")); + j.assertBuildStatusSuccess(j.waitForCompletion(build)); + j.assertLogContains("echo 9", build); } @Test public void consoleShouldFailIfTheBuildIsStuckInTheQueue() throws Exception { @@ -316,7 +288,7 @@ public class ConsoleCommandTest { project.getBuildersList().add(new Shell("echo 1\nsleep 10s")); project.setAssignedLabel(new LabelAtom("never_created")); - assertThat("Job wasn't scheduled properly", project.scheduleBuild(0), equalTo(true)); + assertNotNull(project.scheduleBuild2(0)); Thread.sleep(1000); assertThat("Job wasn't scheduled properly - it isn't in the queue", project.isInQueue(), equalTo(true)); assertThat("Job wasn't scheduled properly - it is running on non-exist node", project.isBuilding(), equalTo(false)); diff --git a/test/src/test/java/hudson/cli/DeleteBuildsCommandTest.java b/test/src/test/java/hudson/cli/DeleteBuildsCommandTest.java index 366f8e981839..9d8c5d7f44f3 100644 --- a/test/src/test/java/hudson/cli/DeleteBuildsCommandTest.java +++ b/test/src/test/java/hudson/cli/DeleteBuildsCommandTest.java @@ -31,6 +31,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertNotNull; import static org.junit.Assume.assumeFalse; import hudson.Functions; @@ -155,7 +156,7 @@ public class DeleteBuildsCommandTest { FreeStyleProject project = j.createFreeStyleProject("aProject"); project.getBuildersList().add(new Shell("echo 1")); project.setAssignedLabel(new LabelAtom("never_created")); - assertThat("Job wasn't scheduled properly", project.scheduleBuild(0), equalTo(true)); + assertNotNull(project.scheduleBuild2(0)); Thread.sleep(1000); assertThat("Job wasn't scheduled properly - it isn't in the queue", project.isInQueue(), equalTo(true)); assertThat("Job wasn't scheduled properly - it is running on non-exist node", project.isBuilding(), equalTo(false)); diff --git a/test/src/test/java/hudson/cli/RunRangeCommand2Test.java b/test/src/test/java/hudson/cli/RunRangeCommand2Test.java index c9330304e2ec..894e10c95148 100644 --- a/test/src/test/java/hudson/cli/RunRangeCommand2Test.java +++ b/test/src/test/java/hudson/cli/RunRangeCommand2Test.java @@ -30,6 +30,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertNotNull; import hudson.Functions; import hudson.model.FreeStyleBuild; @@ -85,31 +86,21 @@ public class RunRangeCommand2Test { FreeStyleProject project = j.createFreeStyleProject("aProject"); project.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo 1\r\nping -n 10 127.0.0.1 >nul") : new Shell("echo 1\nsleep 10s")); FreeStyleBuild build = project.scheduleBuild2(0).waitForStart(); - - // Wait until classProject is started (at least 1s) - while(!project.isBuilding()) { - System.out.println("Waiting for build to start and sleep 1s..."); - Thread.sleep(1000); - } - - // Wait for the first sleep - if(!project.getBuildByNumber(1).getLog().contains("echo 1")) { - Thread.sleep(1000); - } + j.waitForMessage("echo 1", build); final CLICommandInvoker.Result result = command .authorizedTo(Jenkins.READ, Item.READ) .invokeWithArgs("aProject", "1"); assertThat(result, succeeded()); assertThat(result.stdout(), containsString("Builds: 1" + System.lineSeparator())); - j.waitForCompletion(build); + j.assertBuildStatusSuccess(j.waitForCompletion(build)); } @Test public void dummyRangeShouldSuccessEvenTheBuildIsStuckInTheQueue() throws Exception { FreeStyleProject project = j.createFreeStyleProject("aProject"); project.getBuildersList().add(new Shell("echo 1\nsleep 10s")); project.setAssignedLabel(new LabelAtom("never_created")); - assertThat("Job wasn't scheduled properly", project.scheduleBuild(0), equalTo(true)); + assertNotNull(project.scheduleBuild2(0)); Thread.sleep(1000); assertThat("Job wasn't scheduled properly - it isn't in the queue", project.isInQueue(), equalTo(true)); diff --git a/test/src/test/java/hudson/model/ExecutorTest.java b/test/src/test/java/hudson/model/ExecutorTest.java index 3ef9cf3a4d24..b511caf16332 100644 --- a/test/src/test/java/hudson/model/ExecutorTest.java +++ b/test/src/test/java/hudson/model/ExecutorTest.java @@ -90,13 +90,13 @@ public void abortCause() throws Exception { FreeStyleBuild b = r.get(); // make sure this information is recorded - assertEquals(Result.FAILURE, b.getResult()); + j.assertBuildStatus(Result.FAILURE, j.waitForCompletion(b)); InterruptedBuildAction iba = b.getAction(InterruptedBuildAction.class); assertEquals(1,iba.getCauses().size()); assertEquals(((UserInterruption) iba.getCauses().get(0)).getUser(), johnny); // make sure it shows up in the log - assertTrue(b.getLog().contains(johnny.getId())); + j.assertLogContains(johnny.getId(), b); } @Test @@ -114,12 +114,11 @@ public void disconnectCause() throws Exception { FreeStyleBuild b = r.get(); - String log = b.getLog(); - assertEquals(Result.FAILURE, b.getResult()); - assertThat(log, containsString("Finished: FAILURE")); - assertThat(log, containsString("Build step 'BlockingBuilder' marked build as failure")); - assertThat(log, containsString("Agent went offline during the build")); - assertThat(log, containsString("Disconnected by Johnny : Taking offline to break your build")); + j.assertBuildStatus(Result.FAILURE, j.waitForCompletion(b)); + j.assertLogContains("Finished: FAILURE", b); + j.assertLogContains("Build step 'BlockingBuilder' marked build as failure", b); + j.assertLogContains("Agent went offline during the build", b); + j.assertLogContains("Disconnected by Johnny : Taking offline to break your build", b); } @Issue("SECURITY-611") diff --git a/test/src/test/java/hudson/model/LabelLoadStatisticsQueueLengthTest.java b/test/src/test/java/hudson/model/LabelLoadStatisticsQueueLengthTest.java index 0e29a791ca13..d977a8ed64a2 100644 --- a/test/src/test/java/hudson/model/LabelLoadStatisticsQueueLengthTest.java +++ b/test/src/test/java/hudson/model/LabelLoadStatisticsQueueLengthTest.java @@ -3,6 +3,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import edu.umd.cs.findbugs.annotations.NonNull; @@ -90,9 +91,9 @@ public void queueLengthReflectsBuildableItemsAssignedLabel() // Add the job to the build queue several times with an assigned label. for (int i = 0; i < 3; i++) { - project.scheduleBuild(0, CAUSE, new LabelAssignmentActionImpl(), + assertNotNull(project.scheduleBuild2(0, CAUSE, new LabelAssignmentActionImpl(), new ParametersAction(new StringParameterValue( - PARAMETER_NAME, String.valueOf(i)))); + PARAMETER_NAME, String.valueOf(i))))); } // Verify that the real queue length is 3. @@ -143,9 +144,9 @@ public void queueLengthReflectsJobsAssignedLabel() throws Exception { // Add the job to the build queue several times. for (int i = 0; i < 3; i++) { - project.scheduleBuild(0, CAUSE, + assertNotNull(project.scheduleBuild2(0, CAUSE, new ParametersAction(new StringParameterValue( - PARAMETER_NAME, String.valueOf(i)))); + PARAMETER_NAME, String.valueOf(i))))); } // Verify that the real queue length is 3. diff --git a/test/src/test/java/hudson/model/QueueTest.java b/test/src/test/java/hudson/model/QueueTest.java index 78a7137328ec..5e8d9eb2057b 100644 --- a/test/src/test/java/hudson/model/QueueTest.java +++ b/test/src/test/java/hudson/model/QueueTest.java @@ -174,7 +174,7 @@ public class QueueTest { r.jenkins.setNumExecutors(0); FreeStyleProject testProject = r.createFreeStyleProject("test"); - testProject.scheduleBuild(new UserIdCause()); + assertNotNull(testProject.scheduleBuild2(0, new UserIdCause())); q.save(); System.out.println(FileUtils.readFileToString(new File(r.jenkins.getRootDir(), "queue.xml"), StandardCharsets.UTF_8)); @@ -222,7 +222,7 @@ public void recover_from_legacy_list() { r.jenkins.setNumExecutors(0); FreeStyleProject testProject = r.createFreeStyleProject("test"); - testProject.scheduleBuild(new UserIdCause()); + assertNotNull(testProject.scheduleBuild2(0, new UserIdCause())); q.save(); System.out.println(FileUtils.readFileToString(new File(r.jenkins.getRootDir(), "queue.xml"))); @@ -277,10 +277,12 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen }); Future b1 = p.scheduleBuild2(0); + assertNotNull(b1); seq.phase(1); // and make sure we have one build under way // get another going Future b2 = p.scheduleBuild2(0); + assertNotNull(b2); q.scheduleMaintenance().get(); Queue.Item[] items = q.getItems(); @@ -361,22 +363,22 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen }); // Start one build to block others - assertTrue(project.scheduleBuild(new UserIdCause())); + project.scheduleBuild2(0, new UserIdCause()).waitForStart(); buildStarted.block(); // wait for the build to really start // Schedule a new build, and trigger it many ways while it sits in queue - Future fb = project.scheduleBuild2(0, new UserIdCause()); + final Future fb = project.scheduleBuild2(0, new UserIdCause()); assertNotNull(fb); - assertTrue(project.scheduleBuild(new SCMTriggerCause(""))); - assertTrue(project.scheduleBuild(new UserIdCause())); - assertTrue(project.scheduleBuild(new TimerTriggerCause())); - assertTrue(project.scheduleBuild(new RemoteCause("1.2.3.4", "test"))); - assertTrue(project.scheduleBuild(new RemoteCause("4.3.2.1", "test"))); - assertTrue(project.scheduleBuild(new SCMTriggerCause(""))); - assertTrue(project.scheduleBuild(new RemoteCause("1.2.3.4", "test"))); - assertTrue(project.scheduleBuild(new RemoteCause("1.2.3.4", "foo"))); - assertTrue(project.scheduleBuild(new SCMTriggerCause(""))); - assertTrue(project.scheduleBuild(new TimerTriggerCause())); + assertNotNull(project.scheduleBuild2(0, new SCMTriggerCause(""))); + assertNotNull(project.scheduleBuild2(0, new UserIdCause())); + assertNotNull(project.scheduleBuild2(0, new TimerTriggerCause())); + assertNotNull(project.scheduleBuild2(0, new RemoteCause("1.2.3.4", "test"))); + assertNotNull(project.scheduleBuild2(0, new RemoteCause("4.3.2.1", "test"))); + assertNotNull(project.scheduleBuild2(0, new SCMTriggerCause(""))); + assertNotNull(project.scheduleBuild2(0, new RemoteCause("1.2.3.4", "test"))); + assertNotNull(project.scheduleBuild2(0, new RemoteCause("1.2.3.4", "foo"))); + assertNotNull(project.scheduleBuild2(0, new SCMTriggerCause(""))); + assertNotNull(project.scheduleBuild2(0, new TimerTriggerCause())); // Wait for 2nd build to finish buildShouldComplete.signal(); @@ -1123,7 +1125,7 @@ private void checkCancelOperationUsingUrl(Function urlProvid assertThat(q.getItems().length, equalTo(0)); FreeStyleProject testProject = r.createFreeStyleProject("test"); - testProject.scheduleBuild(new UserIdCause()); + assertNotNull(testProject.scheduleBuild2(0, new UserIdCause())); Queue.Item[] items = q.getItems(); assertThat(items.length, equalTo(1)); diff --git a/test/src/test/java/hudson/tasks/MavenTest.java b/test/src/test/java/hudson/tasks/MavenTest.java index 96f15a57bdef..5276b52381f8 100644 --- a/test/src/test/java/hudson/tasks/MavenTest.java +++ b/test/src/test/java/hudson/tasks/MavenTest.java @@ -199,12 +199,8 @@ private void verify() throws Exception { project.setScm(new ExtractResourceSCM(getClass().getResource("maven-empty.zip"))); project.getBuildersList().add(new Maven("clean package",null)); - FreeStyleBuild build = project.scheduleBuild2(0).get(); - @SuppressWarnings("deprecation") - String buildLog = build.getLog(); - assertNotNull(buildLog); - System.out.println(buildLog); - assertFalse(buildLog.contains("-Dpassword=12345")); + FreeStyleBuild build = j.waitForCompletion(project.scheduleBuild2(0).waitForStart()); + j.assertLogNotContains("-Dpassword=12345", build); } @Test @@ -222,19 +218,9 @@ public void parametersReferencedFromPropertiesShouldRetainBackslashes() throws E new EnvironmentVariablesNodeProperty(envVar) )); - FreeStyleBuild build = project.scheduleBuild2(0).get(); - @SuppressWarnings("deprecation") - String buildLog = build.getLog(); - - assertNotNull(buildLog); - assertTrue( - "Parameter my.path should preserve backslashes in:\n" + buildLog, - buildLog.contains("-Dmy.path=C:\\Windows\\Dir") - ); - assertTrue( - "Parameter global.path should preserve backslashes in:\n" + buildLog, - buildLog.contains("-Dglobal.path=D:\\Jenkins") - ); + FreeStyleBuild build = j.waitForCompletion(project.scheduleBuild2(0).waitForStart()); + j.assertLogContains("-Dmy.path=C:\\Windows\\Dir", build); + j.assertLogContains("-Dglobal.path=D:\\Jenkins", build); } @Test public void defaultSettingsProvider() throws Exception { @@ -314,21 +300,21 @@ public void parametersReferencedFromPropertiesShouldRetainBackslashes() throws E FreeStyleProject p = j.createFreeStyleProject(); p.updateByXml((Source) new StreamSource(getClass().getResourceAsStream("MavenTest/doPassBuildVariablesOptionally.xml"))); - String log = j.buildAndAssertSuccess(p).getLog(); + FreeStyleBuild build = j.buildAndAssertSuccess(p); assertTrue("Build variables injection should be enabled by default when loading from XML", p.getBuildersList().get(Maven.class).isInjectBuildVariables()); - assertTrue("Build variables should be injected by default when loading from XML", log.contains("-DNAME=VALUE")); + j.assertLogContains("-DNAME=VALUE", build); p.getBuildersList().clear(); p.getBuildersList().add(new Maven("--help", maven.getName(), null, null, null, false, null, null, false/*do not inject*/)); - log = j.buildAndAssertSuccess(p).getLog(); - assertFalse("Build variables should not be injected", log.contains("-DNAME=VALUE")); + build = j.buildAndAssertSuccess(p); + j.assertLogNotContains("-DNAME=VALUE", build); p.getBuildersList().clear(); p.getBuildersList().add(new Maven("--help", maven.getName(), null, null, null, false, null, null, true/*do inject*/)); - log = j.buildAndAssertSuccess(p).getLog(); - assertTrue("Build variables should be injected", log.contains("-DNAME=VALUE")); + build = j.buildAndAssertSuccess(p); + j.assertLogContains("-DNAME=VALUE", build); assertFalse("Build variables injection should be disabled by default", new Maven("", "").isInjectBuildVariables()); } @@ -341,16 +327,16 @@ public void parametersReferencedFromPropertiesShouldRetainBackslashes() throws E p.getBuildersList().add(new Maven("--help", maven.getName(), null, properties, null, false, null, null, false/*do not inject build variables*/)); - String log = j.buildAndAssertSuccess(p).getLog(); - assertTrue("Properties should always be injected, even when build variables injection is disabled", - log.contains("-DTEST_PROP1=VAL1") && log.contains("-DTEST_PROP2=VAL2")); + FreeStyleBuild build = j.buildAndAssertSuccess(p); + j.assertLogContains("-DTEST_PROP1=VAL1", build); + j.assertLogContains("-DTEST_PROP2=VAL2", build); p.getBuildersList().clear(); p.getBuildersList().add(new Maven("--help", maven.getName(), null, properties, null, false, null, null, true/*do inject build variables*/)); - log = j.buildAndAssertSuccess(p).getLog(); - assertTrue("Properties should always be injected, even when build variables injection is enabled", - log.contains("-DTEST_PROP1=VAL1") && log.contains("-DTEST_PROP2=VAL2")); + build = j.buildAndAssertSuccess(p); + j.assertLogContains("-DTEST_PROP1=VAL1", build); + j.assertLogContains("-DTEST_PROP2=VAL2", build); } @Issue("JENKINS-34138") diff --git a/test/src/test/java/hudson/triggers/SCMTriggerTest.java b/test/src/test/java/hudson/triggers/SCMTriggerTest.java index faa14acfe940..e5f8a6f3f107 100644 --- a/test/src/test/java/hudson/triggers/SCMTriggerTest.java +++ b/test/src/test/java/hudson/triggers/SCMTriggerTest.java @@ -82,6 +82,7 @@ public void simultaneousPollAndBuild() throws Exception { p.setScm(new TestSCM(checkoutStarted)); Future build = p.scheduleBuild2(0, new Cause.UserCause()); + assertNotNull(build); checkoutStarted.block(); assertFalse("SCM-poll after build has started should wait until that build finishes SCM-update", p.pollSCMChanges(StreamTaskListener.fromStdout())); build.get(); // let mock build finish @@ -155,15 +156,15 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen p.addTrigger(t); // Start one build to block others - assertTrue(p.scheduleBuild(new Cause.UserCause())); + p.scheduleBuild2(0, new Cause.UserCause()).waitForStart(); buildStarted.block(); // wait for the build to really start // Schedule a new build, and trigger it many ways while it sits in queue Future fb = p.scheduleBuild2(0, new Cause.UserCause()); assertNotNull(fb); - assertTrue(p.scheduleBuild(new SCMTriggerCause("First poll"))); - assertTrue(p.scheduleBuild(new SCMTriggerCause("Second poll"))); - assertTrue(p.scheduleBuild(new SCMTriggerCause("Third poll"))); + assertNotNull(p.scheduleBuild2(0, new SCMTriggerCause("First poll"))); + assertNotNull(p.scheduleBuild2(0, new SCMTriggerCause("Second poll"))); + assertNotNull(p.scheduleBuild2(0, new SCMTriggerCause("Third poll"))); // Wait for 2nd build to finish buildShouldComplete.signal(); From f91066f5c683ee47d8a1afd539a399c697867285 Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Thu, 23 Dec 2021 16:54:50 +0000 Subject: [PATCH 49/58] Bump ATH --- essentials.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials.yml b/essentials.yml index df1f6639ee6c..85d1dba0eb74 100644 --- a/essentials.yml +++ b/essentials.yml @@ -1,7 +1,7 @@ --- ath: useLocalSnapshots: false - athRevision: "acceptance-test-harness-1.108" + athRevision: "acceptance-test-harness-1.109" athImage: "jenkins/ath:1.97-pre" categories: - org.jenkinsci.test.acceptance.junit.SmokeTest From efdec08f3cc718113535f513ae864eb02d377e13 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu, 23 Dec 2021 18:41:13 +0000 Subject: [PATCH 50/58] Update form-element-path.js --- .../main/resources/jenkins/formelementpath/form-element-path.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/jenkins/formelementpath/form-element-path.js b/core/src/main/resources/jenkins/formelementpath/form-element-path.js index 0bba9171438e..2232450a36bf 100644 --- a/core/src/main/resources/jenkins/formelementpath/form-element-path.js +++ b/core/src/main/resources/jenkins/formelementpath/form-element-path.js @@ -62,7 +62,7 @@ document.addEventListener("DOMContentLoaded", function(){ case "button": var element // modern buttons aren't wrapped in spans - if (e.classList.contains('jenkins-button')) { + if (e.classList.contains('jenkins-button') || e.classList.contains('repeatable-delete')) { element = e } else { p = findParent(e); From 54287e5ef317c46eda6e0251f860703a56384181 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu, 23 Dec 2021 19:00:05 +0000 Subject: [PATCH 51/58] Fix legacy attach previous labels --- war/src/main/webapp/scripts/hudson-behavior.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index 51f8073a3176..5c32d3d166cb 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -835,11 +835,14 @@ function expandButton(e) { function labelAttachPreviousOnClick() { var e = $(this).previous(); while (e!=null) { - if (e.tagName=="INPUT") { - e.click(); - break; - } - e = e.previous(); + if (e.classList.contains("jenkins-radio")) { + e = e.querySelector("input"); + } + if (e.tagName=="INPUT") { + e.click(); + break; + } + e = e.previous(); } } From 502383a0cfdce511eb2f5e650cba3d1f3837cda7 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Thu, 23 Dec 2021 19:18:36 +0000 Subject: [PATCH 52/58] Bump ATH (#6102) --- essentials.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials.yml b/essentials.yml index df1f6639ee6c..85d1dba0eb74 100644 --- a/essentials.yml +++ b/essentials.yml @@ -1,7 +1,7 @@ --- ath: useLocalSnapshots: false - athRevision: "acceptance-test-harness-1.108" + athRevision: "acceptance-test-harness-1.109" athImage: "jenkins/ath:1.97-pre" categories: - org.jenkinsci.test.acceptance.junit.SmokeTest From 86ce50e78fed3dcd96d64679459af2e8ff6547f5 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Thu, 23 Dec 2021 11:19:50 -0800 Subject: [PATCH 53/58] Display JUnit test results as they appear (#6091) Co-authored-by: Jesse Glick --- Jenkinsfile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 93d06f7155ec..c4fca9efa1fd 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -7,8 +7,6 @@ def buildNumber = BUILD_NUMBER as int; if (buildNumber > 1) milestone(buildNumber - 1); milestone(buildNumber) // JENKINS-43353 / JENKINS-58625 -// TEST FLAG - to make it easier to turn on/off unit tests for speeding up access to later stuff. -def runTests = true def failFast = false // Same memory sizing for both builds and ATH def javaOpts = ["JAVA_OPTS=-Xmx1536m -Xms512m","MAVEN_OPTS=-Xmx1536m -Xms512m"] @@ -48,21 +46,21 @@ for(j = 0; j < jdks.size(); j++) { // Now run the actual build. stage("${buildType} Build / Test") { timeout(time: 300, unit: 'MINUTES') { + realtimeJUnit(healthScaleFactor: 20.0, testResults: '*/target/surefire-reports/*.xml,war/junit.xml') { // -Dmaven.repo.local=… tells Maven to create a subdir in the temporary directory for the local Maven repository // -ntp requires Maven >= 3.6.1 - def mvnCmd = "mvn -Pdebug -Pjapicmp -U -Dset.changelist help:evaluate -Dexpression=changelist -Doutput=$changelistF clean install ${runTests ? '-Dmaven.test.failure.ignore' : '-DskipTests'} -V -B -ntp -Dmaven.repo.local=$m2repo -Dspotbugs.failOnError=false -Dcheckstyle.failOnViolation=false -e" + def mvnCmd = "mvn -Pdebug -Pjapicmp -U -Dset.changelist help:evaluate -Dexpression=changelist -Doutput=$changelistF clean install -Dmaven.test.failure.ignore -V -B -ntp -Dmaven.repo.local=$m2repo -Dspotbugs.failOnError=false -Dcheckstyle.failOnViolation=false -e" infra.runWithMaven(mvnCmd, jdk.toString(), javaOpts, true) if(isUnix()) { sh 'git add . && git diff --exit-code HEAD' } + } } } // Once we've built, archive the artifacts and the test results. stage("${buildType} Publishing") { - if (runTests) { - junit healthScaleFactor: 20.0, testResults: '*/target/surefire-reports/*.xml,war/junit.xml' archiveArtifacts allowEmptyArchive: true, artifacts: '**/target/surefire-reports/*.dumpstream' if (! fileExists('core/target/surefire-reports/TEST-jenkins.Junit4TestsRanTest.xml') ) { error 'junit 4 tests are no longer being run for the core package' @@ -73,7 +71,6 @@ for(j = 0; j < jdks.size(); j++) { if (failFast && currentBuild.result == 'UNSTABLE') { error 'There were test failures; halting early' } - } if (buildType == 'Linux' && jdk == jdks[0]) { def folders = env.JOB_NAME.split('/') if (folders.length > 1) { From e27a8c1736fc37550508eb06cc324bb7549375b5 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu, 23 Dec 2021 19:40:50 +0000 Subject: [PATCH 54/58] Fix attach-previous for radios without IDs, add styling for multi-selects, reordeer entry for entries without titles, fix title class for dropdownLists --- .../resources/lib/form/dropdownList.jelly | 2 +- core/src/main/resources/lib/form/entry.jelly | 12 ++++++----- core/src/main/resources/lib/form/radio.jelly | 2 +- war/src/main/less/base/style.less | 4 ---- war/src/main/less/modules/form.less | 21 ++++++++++++++++++- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/core/src/main/resources/lib/form/dropdownList.jelly b/core/src/main/resources/lib/form/dropdownList.jelly index b45a868906e6..159e4fc37ad9 100644 --- a/core/src/main/resources/lib/form/dropdownList.jelly +++ b/core/src/main/resources/lib/form/dropdownList.jelly @@ -39,7 +39,7 @@ THE SOFTWARE.
      -
      +
      ${attrs.title}
      diff --git a/core/src/main/resources/lib/form/entry.jelly b/core/src/main/resources/lib/form/entry.jelly index 9f601cb422a0..6e5c81e6d468 100644 --- a/core/src/main/resources/lib/form/entry.jelly +++ b/core/src/main/resources/lib/form/entry.jelly @@ -96,18 +96,20 @@ THE SOFTWARE. -
      - +
      - -
      - +
      + +
      + +
      +
      diff --git a/core/src/main/resources/lib/form/radio.jelly b/core/src/main/resources/lib/form/radio.jelly index c549bb49de65..953504a48654 100644 --- a/core/src/main/resources/lib/form/radio.jelly +++ b/core/src/main/resources/lib/form/radio.jelly @@ -47,7 +47,7 @@ THE SOFTWARE. - +
      diff --git a/war/src/main/less/base/style.less b/war/src/main/less/base/style.less index 80cba11095bd..283eb1b41e50 100644 --- a/war/src/main/less/base/style.less +++ b/war/src/main/less/base/style.less @@ -441,10 +441,6 @@ div.behavior-loading { } } -label.attach-previous { - margin-left: 0.5em; -} - .bottom-sticker, #bottom-sticker { width: 100%; /* it needs to occupy the entire width or else the underlying content will see through */ diff --git a/war/src/main/less/modules/form.less b/war/src/main/less/modules/form.less index faa21e048c24..80d578c6366a 100644 --- a/war/src/main/less/modules/form.less +++ b/war/src/main/less/modules/form.less @@ -245,7 +245,7 @@ max-width: 100% !important; // TODO remove important after https://github.com/jenkinsci/credentials-plugin/pull/255 border-radius: 6px; box-shadow: 0 0 0 10px transparent; - transition: 0.2s ease; + transition: var(--standard-transition); min-height: 38px; &:hover { @@ -265,6 +265,25 @@ } } +.jenkins-multi-select { + position: relative; + width: 100%; + border: 2px solid var(--input-border); + border-radius: var(--form-input-border-radius); + box-shadow: 0 0 0 10px transparent; + transition: var(--standard-transition); + outline: none; + + &:focus { + border-color: var(--focus-input-border); + box-shadow: 0 0 0 5px var(--focus-input-glow); + } + + &:disabled { + pointer-events: none; + } +} + .jenkins-radio-help-wrapper { display: flex; align-items: center; From d3914e67c39810a6b590f085d0d2181b256b9c4b Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu, 23 Dec 2021 19:43:02 +0000 Subject: [PATCH 55/58] Fix sticky bottom not aligning to left correctly --- war/src/main/less/base/style.less | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/war/src/main/less/base/style.less b/war/src/main/less/base/style.less index 283eb1b41e50..1968036195fa 100644 --- a/war/src/main/less/base/style.less +++ b/war/src/main/less/base/style.less @@ -447,8 +447,20 @@ div.behavior-loading { } .bottom-sticker-inner { + position: relative; + padding: 1em 0; + z-index: 0; + + &::before { + content: ""; + position: absolute; + top: 0; + left: calc(-2rem - 2px); + bottom: 0; + right: 0; background: var(--background); - padding: 1em 0; + z-index: -1; + } } .top-sticker, From a2615d8527da603bb1156caa3547ea6dffd21ef1 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri, 24 Dec 2021 14:05:41 +0000 Subject: [PATCH 56/58] Revert titleless entry change, fix gap between draggable items, fix non draggable cards having drag handle --- core/src/main/resources/lib/form/entry.jelly | 12 +++++------- core/src/main/resources/lib/form/repeatable.jelly | 2 +- war/src/main/less/modules/draggable-card.less | 3 ++- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core/src/main/resources/lib/form/entry.jelly b/core/src/main/resources/lib/form/entry.jelly index 6e5c81e6d468..9f601cb422a0 100644 --- a/core/src/main/resources/lib/form/entry.jelly +++ b/core/src/main/resources/lib/form/entry.jelly @@ -96,20 +96,18 @@ THE SOFTWARE. -
      +
      +
      + +
      + -
      - -
      - -
      -
      diff --git a/core/src/main/resources/lib/form/repeatable.jelly b/core/src/main/resources/lib/form/repeatable.jelly index dc43213ccaa5..33b24776fd1c 100644 --- a/core/src/main/resources/lib/form/repeatable.jelly +++ b/core/src/main/resources/lib/form/repeatable.jelly @@ -133,7 +133,7 @@ THE SOFTWARE.
      -
      +
      ${header}
      diff --git a/war/src/main/less/modules/draggable-card.less b/war/src/main/less/modules/draggable-card.less index 10246b3e3273..68ef14c86a1e 100644 --- a/war/src/main/less/modules/draggable-card.less +++ b/war/src/main/less/modules/draggable-card.less @@ -5,6 +5,7 @@ border: 2px dashed var(--input-border); padding: 1rem; border-radius: 10px; + margin-bottom: 1rem; } .repeated-chunk .show-if-last { visibility: hidden; } @@ -193,4 +194,4 @@ div.to-be-removed { display: none; } } } } -} \ No newline at end of file +} From 41a8e28d9ddcf584bf15686499190efe05cd0e15 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri, 24 Dec 2021 14:11:01 +0000 Subject: [PATCH 57/58] Adjust config job page to look better --- war/src/main/js/widgets/config/tabbar.less | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/war/src/main/js/widgets/config/tabbar.less b/war/src/main/js/widgets/config/tabbar.less index 1d3170f10f2d..ea7bac3cb844 100644 --- a/war/src/main/js/widgets/config/tabbar.less +++ b/war/src/main/js/widgets/config/tabbar.less @@ -12,6 +12,7 @@ min-height:2.5em; .border-radius-top(@border-radius); z-index:5; + overflow: hidden; nav:before, nav:after { display: none; @@ -50,7 +51,6 @@ .form-config.tabBarFrame { position: relative; - border-bottom: solid 1px var(--medium-grey); .config-section-activators { margin: 0; @@ -99,10 +99,11 @@ } #jenkins{ .jenkins-config { - border:1px solid var(--input-border); - padding: 2rem; - border-top:none; - background:var(--bright-bg-color); + border: 1px solid var(--input-border); + padding: 2rem 2rem 0 2rem; + border-top: none; + border-bottom: none; + background: var(--bright-bg-color); .border-radius-bottom(@border-radius); .showTabs{ From cc50203b9c3e76b6c5d5a82b4feabf020e49eb25 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri, 24 Dec 2021 14:29:13 +0000 Subject: [PATCH 58/58] Potential fix? --- .../main/resources/jenkins/formelementpath/form-element-path.js | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/resources/jenkins/formelementpath/form-element-path.js b/core/src/main/resources/jenkins/formelementpath/form-element-path.js index 2232450a36bf..6c512faeea86 100644 --- a/core/src/main/resources/jenkins/formelementpath/form-element-path.js +++ b/core/src/main/resources/jenkins/formelementpath/form-element-path.js @@ -63,6 +63,7 @@ document.addEventListener("DOMContentLoaded", function(){ var element // modern buttons aren't wrapped in spans if (e.classList.contains('jenkins-button') || e.classList.contains('repeatable-delete')) { + p = findParent(e); element = e } else { p = findParent(e);