diff --git a/pom.xml b/pom.xml
index b46b0836..bab53b4c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,7 @@
999999-SNAPSHOT
2.414.3
jenkinsci/${project.artifactId}-plugin
+ true
@@ -135,6 +136,13 @@
+
+
+ io.jenkins.plugins
+ manage-permission
+ 1.0.1
+ test
+
org.jenkins-ci.plugins.workflow
workflow-support
diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/AbstractGlobalLibraries.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/AbstractGlobalLibraries.java
new file mode 100644
index 00000000..1aa9619e
--- /dev/null
+++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/AbstractGlobalLibraries.java
@@ -0,0 +1,100 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2024 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 org.jenkinsci.plugins.workflow.libs;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.model.ItemGroup;
+import hudson.model.Job;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import jenkins.model.GlobalConfiguration;
+import jenkins.model.Jenkins;
+import net.sf.json.JSONObject;
+import org.kohsuke.stapler.StaplerRequest;
+
+/**
+ * Common code between {@link GlobalLibraries} and {@link GlobalUntrustedLibraries}.
+ */
+public abstract class AbstractGlobalLibraries extends GlobalConfiguration {
+ private List libraries = new ArrayList<>();
+
+ protected AbstractGlobalLibraries() {
+ load();
+ }
+
+ public abstract String getDescription();
+
+ public List getLibraries() {
+ return libraries;
+ }
+
+ public void setLibraries(List libraries) {
+ this.libraries = libraries;
+ save();
+ }
+
+ @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
+ if (Jenkins.get().hasPermission(getRequiredGlobalConfigPagePermission())) {
+ setLibraries(Collections.emptyList()); // allow last library to be deleted
+ return super.configure(req, json);
+ } else {
+ return true;
+ }
+ }
+
+ abstract static class AbstractForJob extends LibraryResolver {
+ @NonNull
+ protected abstract AbstractGlobalLibraries getConfiguration();
+
+ @NonNull @Override public final Collection forJob(@NonNull Job,?> job, @NonNull Map libraryVersions) {
+ return getLibraries();
+ }
+
+ @NonNull @Override public final Collection fromConfiguration(@NonNull StaplerRequest request) {
+ if (Jenkins.get().hasPermission(getConfiguration().getRequiredGlobalConfigPagePermission())) {
+ return getLibraries();
+ }
+ return Collections.emptySet();
+ }
+
+ @NonNull @Override public final Collection suggestedConfigurations(@NonNull ItemGroup> group) {
+ return getLibraries();
+ }
+
+ private List getLibraries() {
+ return getConfiguration()
+ .getLibraries()
+ .stream()
+ .map(this::mayWrapLibrary)
+ .collect(Collectors.toList());
+ }
+
+ @NonNull
+ protected abstract LibraryConfiguration mayWrapLibrary(@NonNull LibraryConfiguration library);
+ }
+}
diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/GlobalLibraries.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/GlobalLibraries.java
index 48b1b025..7d04261c 100644
--- a/src/main/java/org/jenkinsci/plugins/workflow/libs/GlobalLibraries.java
+++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/GlobalLibraries.java
@@ -24,78 +24,49 @@
package org.jenkinsci.plugins.workflow.libs;
-import hudson.Extension;
-import hudson.model.ItemGroup;
-import hudson.model.Job;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
import edu.umd.cs.findbugs.annotations.NonNull;
-import jenkins.model.GlobalConfiguration;
-import jenkins.model.Jenkins;
-import net.sf.json.JSONObject;
-import org.kohsuke.stapler.StaplerRequest;
+import hudson.Extension;
+import hudson.ExtensionList;
/**
* Manages libraries available to any job in the system.
*/
-@Extension public class GlobalLibraries extends GlobalConfiguration {
-
- public static @NonNull GlobalLibraries get() {
- GlobalLibraries instance = GlobalConfiguration.all().get(GlobalLibraries.class);
- if (instance == null) { // TODO would be useful to have an ExtensionList.getOrFail
- throw new IllegalStateException();
- }
- return instance;
- }
-
- private List libraries = new ArrayList<>();
+@Extension public class GlobalLibraries extends AbstractGlobalLibraries {
public GlobalLibraries() {
- load();
+ super();
}
- public List getLibraries() {
- return libraries;
+ @Override
+ public String getDescription() {
+ return Messages.GlobalLibraries_Description();
}
- public void setLibraries(List libraries) {
- this.libraries = libraries;
- save();
+ @NonNull
+ @Override
+ public String getDisplayName() {
+ return Messages.GlobalLibraries_DisplayName();
}
- @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
- if (Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
- setLibraries(Collections.emptyList()); // allow last library to be deleted
- return super.configure(req, json);
- } else {
- return true;
- }
+ public static @NonNull GlobalLibraries get() {
+ return ExtensionList.lookupSingleton(GlobalLibraries.class);
}
- @Extension(ordinal=0) public static class ForJob extends LibraryResolver {
-
- @Override public boolean isTrusted() {
- return true;
- }
-
- @NonNull @Override public Collection forJob(@NonNull Job,?> job, @NonNull Map libraryVersions) {
- return GlobalLibraries.get().getLibraries();
+ @Extension(ordinal=0) public static class ForJob extends AbstractForJob {
+ @NonNull
+ protected GlobalLibraries getConfiguration() {
+ return get();
}
- @NonNull @Override public Collection fromConfiguration(@NonNull StaplerRequest request) {
- if (Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
- return GlobalLibraries.get().getLibraries();
- }
- return Collections.emptySet();
+ @Override
+ public boolean isTrusted() {
+ return true;
}
- @NonNull @Override public Collection suggestedConfigurations(@NonNull ItemGroup> group) {
- return GlobalLibraries.get().getLibraries();
+ @NonNull
+ @Override
+ protected LibraryConfiguration mayWrapLibrary(@NonNull LibraryConfiguration library) {
+ return library;
}
-
}
-
}
diff --git a/src/main/java/org/jenkinsci/plugins/workflow/libs/GlobalUntrustedLibraries.java b/src/main/java/org/jenkinsci/plugins/workflow/libs/GlobalUntrustedLibraries.java
new file mode 100644
index 00000000..12229489
--- /dev/null
+++ b/src/main/java/org/jenkinsci/plugins/workflow/libs/GlobalUntrustedLibraries.java
@@ -0,0 +1,80 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2024 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 org.jenkinsci.plugins.workflow.libs;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.Extension;
+import hudson.ExtensionList;
+import hudson.security.Permission;
+import jenkins.model.Jenkins;
+
+/**
+ * Manages untrusted libraries available to any job in the system.
+ */
+@Extension public class GlobalUntrustedLibraries extends AbstractGlobalLibraries {
+
+ public GlobalUntrustedLibraries() {
+ super();
+ }
+
+ @Override
+ public String getDescription() {
+ return Messages.GlobalUntrustedLibraries_Description();
+ }
+
+ @NonNull
+ @Override
+ public String getDisplayName() {
+ return Messages.GlobalUntrustedLibraries_DisplayName();
+ }
+
+ public static @NonNull GlobalUntrustedLibraries get() {
+ return ExtensionList.lookupSingleton(GlobalUntrustedLibraries.class);
+ }
+
+ @NonNull
+ @Override
+ public Permission getRequiredGlobalConfigPagePermission() {
+ return Jenkins.MANAGE;
+ }
+
+ @Extension(ordinal=1) public static class ForJob extends AbstractForJob {
+ @NonNull
+ protected GlobalUntrustedLibraries getConfiguration() {
+ return get();
+ }
+
+ @Override
+ public boolean isTrusted() {
+ return false;
+ }
+
+ @NonNull
+ @Override
+ protected LibraryConfiguration mayWrapLibrary(@NonNull LibraryConfiguration library) {
+ return new ResolvedLibraryConfiguration(library, getClass().getName());
+ }
+ }
+}
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/GlobalLibraries/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/libs/AbstractGlobalLibraries/config.jelly
similarity index 89%
rename from src/main/resources/org/jenkinsci/plugins/workflow/libs/GlobalLibraries/config.jelly
rename to src/main/resources/org/jenkinsci/plugins/workflow/libs/AbstractGlobalLibraries/config.jelly
index 72532152..918f429d 100644
--- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/GlobalLibraries/config.jelly
+++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/AbstractGlobalLibraries/config.jelly
@@ -25,10 +25,10 @@ THE SOFTWARE.
-
-
+
+
-
+
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/GlobalLibraries/config.properties b/src/main/resources/org/jenkinsci/plugins/workflow/libs/GlobalLibraries/config.properties
deleted file mode 100644
index 6235426b..00000000
--- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/GlobalLibraries/config.properties
+++ /dev/null
@@ -1,25 +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.
-
-blurb=\
- Sharable libraries available to any Pipeline jobs running on this system. \
- These libraries will be trusted, meaning they run without \u201csandbox\u201d restrictions and may use @Grab
.
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/libs/Messages.properties b/src/main/resources/org/jenkinsci/plugins/workflow/libs/Messages.properties
index c5ad5a4b..67921dcb 100644
--- a/src/main/resources/org/jenkinsci/plugins/workflow/libs/Messages.properties
+++ b/src/main/resources/org/jenkinsci/plugins/workflow/libs/Messages.properties
@@ -24,3 +24,9 @@ LibraryDecorator.could_not_find_any_definition_of_librari=Could not find any def
ResourceStep.library_resource_ambiguous_among_librari=Library resource {0} ambiguous among libraries {1}
ResourceStep.no_such_library_resource_could_be_found_=No such library resource {0} could be found.
SCMSourceRetriever.library_path_no_double_dot=Library path may not contain ".."
+GlobalLibraries.DisplayName=Global Trusted Pipeline Libraries
+GlobalLibraries.Description=Sharable libraries available to any Pipeline jobs running on this system. \
+ These libraries will be trusted, meaning they run without \u201csandbox\u201d restrictions and may use @Grab
.
+GlobalUntrustedLibraries.Description=Sharable libraries available to any Pipeline jobs running on this system. \
+ These libraries will be untrusted, meaning they run with \u201csandbox\u201d restrictions and cannot use @Grab
.
+GlobalUntrustedLibraries.DisplayName=Global Untrusted Pipeline Libraries
diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/FolderLibrariesTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/FolderLibrariesTest.java
index f8f537a1..449e2633 100644
--- a/src/test/java/org/jenkinsci/plugins/workflow/libs/FolderLibrariesTest.java
+++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/FolderLibrariesTest.java
@@ -172,19 +172,10 @@ public class FolderLibrariesTest {
/** @see GrapeTest#outsideLibrarySandbox */
@Test public void noGrape() throws Exception {
- sampleRepo1.init();
- sampleRepo1.write("src/pkg/Wrapper.groovy",
- "package pkg\n" +
- "@Grab('commons-primitives:commons-primitives:1.0')\n" +
- "import org.apache.commons.collections.primitives.ArrayIntList\n" +
- "class Wrapper {static def list() {new ArrayIntList()}}");
- sampleRepo1.git("add", "src");
- sampleRepo1.git("commit", "--message=init");
Folder d = r.jenkins.createProject(Folder.class, "d");
- d.getProperties().add(new FolderLibraries(Collections.singletonList(new LibraryConfiguration("grape", new SCMSourceRetriever(new GitSCMSource(null, sampleRepo1.toString(), "", "*", "", true))))));
+ d.getProperties().add(new FolderLibraries(List.of(LibraryTestUtils.defineLibraryUsingGrab("grape", sampleRepo1))));
WorkflowJob p = d.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition("@Library('grape@master') import pkg.Wrapper; echo(/should not have been able to run ${pkg.Wrapper.list()}/)", true));
- ScriptApproval.get().approveSignature("new org.apache.commons.collections.primitives.ArrayIntList");
r.assertLogContains("Annotation Grab cannot be used in the sandbox", r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0)));
}
diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/GlobalLibrariesTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/GlobalLibrariesTest.java
index efadbe5d..2c6f4adb 100644
--- a/src/test/java/org/jenkinsci/plugins/workflow/libs/GlobalLibrariesTest.java
+++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/GlobalLibrariesTest.java
@@ -24,12 +24,12 @@
package org.jenkinsci.plugins.workflow.libs;
-import org.htmlunit.HttpMethod;
-import org.htmlunit.WebRequest;
-import org.htmlunit.html.HtmlPage;
-import org.htmlunit.util.NameValuePair;
-import hudson.model.Item;
-import hudson.model.View;
+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 hudson.security.Permission;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
@@ -38,10 +38,16 @@
import jenkins.plugins.git.GitSCMSource;
import jenkins.plugins.git.GitSampleRepoRule;
import jenkins.scm.impl.subversion.SubversionSCMSource;
-import static org.hamcrest.Matchers.*;
-import org.junit.Test;
-import static org.junit.Assert.*;
+import org.htmlunit.HttpMethod;
+import org.htmlunit.WebRequest;
+import org.htmlunit.html.HtmlPage;
+import org.htmlunit.util.NameValuePair;
+import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
+import org.jenkinsci.plugins.workflow.job.WorkflowJob;
+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.MockAuthorizationStrategy;
@@ -50,42 +56,26 @@ public class GlobalLibrariesTest {
@Rule public JenkinsRule r = new JenkinsRule();
@Rule public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
+ @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
@Test public void configRoundtrip() throws Exception {
r.configRoundtrip();
- GlobalLibraries gl = GlobalLibraries.get();
- assertEquals(Collections.emptyList(), gl.getLibraries());
- LibraryConfiguration foo = new LibraryConfiguration("foo", new SCMSourceRetriever(new SubversionSCMSource("foo", "https://phony.jenkins.io/foo/")));
- LibraryConfiguration bar = new LibraryConfiguration("bar", new SCMSourceRetriever(new GitSCMSource(null, "https://phony.jenkins.io/bar.git", "", "origin", "+refs/heads/*:refs/remotes/origin/*", "*", "", true)));
- LibraryCachingConfiguration cachingConfiguration = new LibraryCachingConfiguration(120, "develop", "master stable");
- foo.setCachingConfiguration(cachingConfiguration);
- bar.setDefaultVersion("master");
- bar.setImplicit(true);
- bar.setAllowVersionOverride(false);
- gl.setLibraries(Arrays.asList(foo, bar));
- r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
- r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().
- grant(Jenkins.ADMINISTER).everywhere().to("alice")
- );
- HtmlPage configurePage = r.createWebClient().login("alice").goTo("configure");
- assertThat(configurePage.getWebResponse().getContentAsString(), containsString("https://phony.jenkins.io/bar.git"));
- r.submit(configurePage.getFormByName("config")); // JenkinsRule.configRoundtrip expanded to include login
- List libs = gl.getLibraries();
- r.assertEqualDataBoundBeans(Arrays.asList(foo, bar), libs);
- libs = gl.getLibraries();
- r.assertEqualDataBoundBeans(Arrays.asList(foo, bar), libs);
- boolean noFoo = true;
- for (LibraryConfiguration lib : libs) {
- if ("foo".equals(lib.getName())) {
- noFoo = false;
- r.assertEqualDataBoundBeans(lib.getCachingConfiguration(), cachingConfiguration);
- }
- }
- assertFalse("Missing a library called foo (should not happen)", noFoo);
+ configRoundtrip(r, GlobalLibraries.get(), Jenkins.ADMINISTER);
}
@Issue("SECURITY-1422")
@Test public void checkDefaultVersionRestricted() throws Exception {
+ checkDefaultVersionRestricted(r, sampleRepo, GlobalLibraries.get());
+ }
+
+ @Test public void allowedGrape() throws Exception {
+ GlobalLibraries.get().setLibraries(List.of(LibraryTestUtils.defineLibraryUsingGrab("grape", sampleRepo)));
+ WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
+ p.setDefinition(new CpsFlowDefinition("@Library('grape@master') import pkg.Wrapper; echo(/should be able to run ${pkg.Wrapper.list()}/)", true));
+ r.assertBuildStatusSuccess(p.scheduleBuild2(0));
+ }
+
+ static void checkDefaultVersionRestricted(JenkinsRule r, GitSampleRepoRule sampleRepo, AbstractGlobalLibraries gl) throws Exception {
sampleRepo.init();
sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}");
sampleRepo.git("add", "vars");
@@ -96,7 +86,7 @@ public class GlobalLibrariesTest {
.grant(Jenkins.ADMINISTER).everywhere().to("admin");
r.jenkins.setAuthorizationStrategy(s);
LibraryConfiguration foo = new LibraryConfiguration("foo", new SCMSourceRetriever(new GitSCMSource(sampleRepo.toString())));
- GlobalLibraries.get().setLibraries(Arrays.asList(foo));
+ gl.setLibraries(Arrays.asList(foo));
JenkinsRule.WebClient wc = r.createWebClient();
wc.setThrowExceptionOnFailingStatusCode(false);
WebRequest req = new WebRequest(new URL(wc.getContextPath() + "/descriptorByName/" +
@@ -116,4 +106,34 @@ public class GlobalLibrariesTest {
containsString("Currently maps to revision"));
}
+ static void configRoundtrip(JenkinsRule r, AbstractGlobalLibraries gl, Permission... alicePrivileges) throws Exception {
+ assertEquals(Collections.emptyList(), gl.getLibraries());
+ LibraryConfiguration foo = new LibraryConfiguration("foo", new SCMSourceRetriever(new SubversionSCMSource("foo", "https://phony.jenkins.io/foo/")));
+ LibraryConfiguration bar = new LibraryConfiguration("bar", new SCMSourceRetriever(new GitSCMSource(null, "https://phony.jenkins.io/bar.git", "", "origin", "+refs/heads/*:refs/remotes/origin/*", "*", "", true)));
+ LibraryCachingConfiguration cachingConfiguration = new LibraryCachingConfiguration(120, "develop", "master stable");
+ foo.setCachingConfiguration(cachingConfiguration);
+ bar.setDefaultVersion("master");
+ bar.setImplicit(true);
+ bar.setAllowVersionOverride(false);
+ gl.setLibraries(Arrays.asList(foo, bar));
+ r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
+ r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().
+ grant(alicePrivileges).everywhere().to("alice")
+ );
+ HtmlPage configurePage = r.createWebClient().login("alice").goTo("configure");
+ assertThat(configurePage.getWebResponse().getContentAsString(), containsString("https://phony.jenkins.io/bar.git"));
+ r.submit(configurePage.getFormByName("config")); // JenkinsRule.configRoundtrip expanded to include login
+ List libs = gl.getLibraries();
+ r.assertEqualDataBoundBeans(Arrays.asList(foo, bar), libs);
+ libs = gl.getLibraries();
+ r.assertEqualDataBoundBeans(Arrays.asList(foo, bar), libs);
+ boolean noFoo = true;
+ for (LibraryConfiguration lib : libs) {
+ if ("foo".equals(lib.getName())) {
+ noFoo = false;
+ r.assertEqualDataBoundBeans(lib.getCachingConfiguration(), cachingConfiguration);
+ }
+ }
+ assertFalse("Missing a library called foo (should not happen)", noFoo);
+ }
}
diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/GlobalUntrustedLibrariesTest.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/GlobalUntrustedLibrariesTest.java
new file mode 100644
index 00000000..5ea5c832
--- /dev/null
+++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/GlobalUntrustedLibrariesTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.jenkinsci.plugins.workflow.libs;
+
+import hudson.model.Result;
+import java.util.List;
+import jenkins.model.Jenkins;
+import jenkins.plugins.git.GitSampleRepoRule;
+import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
+import org.jenkinsci.plugins.workflow.job.WorkflowJob;
+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;
+
+public class GlobalUntrustedLibrariesTest {
+
+ @Rule public JenkinsRule r = new JenkinsRule();
+ @Rule public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
+ @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
+
+ @Test public void configRoundtrip() throws Exception {
+ r.configRoundtrip();
+ GlobalLibrariesTest.configRoundtrip(r, GlobalUntrustedLibraries.get(), Jenkins.READ, Jenkins.MANAGE);
+ }
+
+ @Issue("SECURITY-1422")
+ @Test public void checkDefaultVersionRestricted() throws Exception {
+ GlobalLibrariesTest.checkDefaultVersionRestricted(r, sampleRepo, GlobalUntrustedLibraries.get());
+ }
+
+ @Test public void noGrape() throws Exception {
+ GlobalUntrustedLibraries.get().setLibraries(List.of(LibraryTestUtils.defineLibraryUsingGrab("grape", sampleRepo)));
+ WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
+ p.setDefinition(new CpsFlowDefinition("@Library('grape@master') import pkg.Wrapper; echo(/should not have been able to run ${pkg.Wrapper.list()}/)", true));
+ r.assertLogContains("Annotation Grab cannot be used in the sandbox", r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0)));
+ }
+
+}
diff --git a/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryTestUtils.java b/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryTestUtils.java
new file mode 100644
index 00000000..aac1c676
--- /dev/null
+++ b/src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryTestUtils.java
@@ -0,0 +1,20 @@
+package org.jenkinsci.plugins.workflow.libs;
+
+import jenkins.plugins.git.GitSCMSource;
+import jenkins.plugins.git.GitSampleRepoRule;
+
+final class LibraryTestUtils {
+ private LibraryTestUtils(){}
+
+ static LibraryConfiguration defineLibraryUsingGrab(String libraryName, GitSampleRepoRule sampleRepo) throws Exception {
+ sampleRepo.init();
+ sampleRepo.write("src/pkg/Wrapper.groovy",
+ "package pkg\n" +
+ "@Grab('commons-primitives:commons-primitives:1.0')\n" +
+ "import org.apache.commons.collections.primitives.ArrayIntList\n" +
+ "class Wrapper {static def list() {new ArrayIntList()}}");
+ sampleRepo.git("add", "src");
+ sampleRepo.git("commit", "--message=init");
+ return new LibraryConfiguration(libraryName, new SCMSourceRetriever(new GitSCMSource(sampleRepo.toString())));
+ }
+}