2424
2525package org .jenkinsci .plugins .scriptsecurity .sandbox .groovy ;
2626
27+
28+ import com .gargoylesoftware .htmlunit .CollectingAlertHandler ;
2729import com .gargoylesoftware .htmlunit .html .HtmlCheckBoxInput ;
2830import com .gargoylesoftware .htmlunit .html .HtmlInput ;
2931import groovy .lang .Binding ;
6163import org .jenkinsci .plugins .scriptsecurity .scripts .UnapprovedUsageException ;
6264
6365import static org .hamcrest .MatcherAssert .assertThat ;
66+ import static org .hamcrest .Matchers .arrayWithSize ;
67+ import static org .hamcrest .Matchers .contains ;
6468import static org .hamcrest .Matchers .containsString ;
69+ import static org .hamcrest .Matchers .empty ;
70+ import static org .hamcrest .Matchers .emptyArray ;
71+ import static org .hamcrest .Matchers .is ;
6572import org .jenkinsci .plugins .scriptsecurity .sandbox .whitelists .Whitelisted ;
6673import static org .junit .Assert .assertEquals ;
6774import static org .junit .Assert .assertFalse ;
@@ -163,7 +170,7 @@ public class SecureGroovyScriptTest {
163170
164171
165172 /**
166- * Test where the user has ADMINISTER privs, default to non sandbox mode.
173+ * Test where the user has ADMINISTER privs, default to non sandbox mode, but require approval
167174 */
168175 @ Test public void testSandboxDefault_with_ADMINISTER_privs () throws Exception {
169176 r .jenkins .setSecurityRealm (r .createDummySecurityRealm ());
@@ -198,12 +205,12 @@ public class SecureGroovyScriptTest {
198205 // The user has ADMINISTER privs => should default to non sandboxed
199206 assertFalse (publisher .getScript ().isSandbox ());
200207
201- // Because it has ADMINISTER privs, the script should not have ended up pending approval
208+ // even though it has ADMINISTER privs, the script should still require approval
202209 Set <ScriptApproval .PendingScript > pendingScripts = ScriptApproval .get ().getPendingScripts ();
203- assertEquals (0 , pendingScripts .size ());
210+ assertEquals (1 , pendingScripts .size ());
204211
205212 // Test that the script is executable. If it's not, we will get an UnapprovedUsageException
206- assertEquals ( groovy , ScriptApproval .get ().using (groovy , GroovyLanguage .get ()));
213+ assertThrows ( UnapprovedUsageException . class , () -> ScriptApproval .get ().using (groovy , GroovyLanguage .get ()));
207214 }
208215
209216 /**
@@ -892,7 +899,7 @@ public void testScriptApproval() throws Exception {
892899
893900 JenkinsRule .WebClient wc = r .createWebClient ();
894901
895- // If configured by a user with ADMINISTER script is approved if edited by that user
902+ // If configured by a user with ADMINISTER script is not approved and approval is requested
896903 {
897904 wc .login ("admin" );
898905 HtmlForm config = wc .getPage (p , "configure" ).getFormByName ("config" );
@@ -903,8 +910,9 @@ public void testScriptApproval() throws Exception {
903910 script .setText (groovy );
904911 r .submit (config );
905912
906- assertTrue (ScriptApproval .get ().isScriptApproved (groovy , GroovyLanguage .get ()));
907-
913+ assertFalse (ScriptApproval .get ().isScriptApproved (groovy , GroovyLanguage .get ()));
914+ assertEquals (1 , ScriptApproval .get ().getPendingScripts ().size ());
915+
908916 // clean up for next tests
909917 ScriptApproval .get ().preapproveAll ();
910918 ScriptApproval .get ().clearApprovedScripts ();
@@ -929,11 +937,14 @@ public void testScriptApproval() throws Exception {
929937 ScriptApproval .get ().clearApprovedScripts ();
930938 }
931939
932- // If configured by a user with ADMINISTER while escape hatch is on script is approved upon save
940+ // If configured by a user with ADMINISTER while escape hatches are on script is approved upon save
933941 {
934942 wc .login ("admin" );
935- boolean original = ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED ;
943+ boolean originalAdminAutoApprove = ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED ;
936944 ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = true ;
945+ boolean originalAllowAdminApproval = ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED ;
946+ ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED = true ;
947+
937948 try {
938949 HtmlForm config = wc .getPage (p , "configure" ).getFormByName ("config" );
939950 List <HtmlTextArea > scripts = config .getTextAreasByName ("_.script" );
@@ -948,15 +959,18 @@ public void testScriptApproval() throws Exception {
948959 ScriptApproval .get ().preapproveAll ();
949960 ScriptApproval .get ().clearApprovedScripts ();
950961 } finally {
951- ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = original ;
962+ ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = originalAdminAutoApprove ;
963+ ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED = originalAllowAdminApproval ;
952964 }
953965 }
954966
955967 // If configured by a user without ADMINISTER while escape hatch is on script is not approved
956968 {
957969 wc .login ("devel" );
958- boolean original = ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED ;
970+ boolean originalAdminAutoApprove = ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED ;
959971 ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = true ;
972+ boolean originalAllowAdminApproval = ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED ;
973+ ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED = true ;
960974 try {
961975 r .submit (wc .getPage (p , "configure" ).getFormByName ("config" ));
962976
@@ -966,7 +980,8 @@ public void testScriptApproval() throws Exception {
966980 ScriptApproval .get ().preapproveAll ();
967981 ScriptApproval .get ().clearApprovedScripts ();
968982 } finally {
969- ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = original ;
983+ ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = originalAdminAutoApprove ;
984+ ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED = originalAllowAdminApproval ;
970985 }
971986 }
972987 }
@@ -1271,6 +1286,52 @@ public void testScriptAtFieldInitializers() throws Exception {
12711286 r .assertLogContains ("new java.io.File java.lang.String" , b );
12721287 }
12731288
1289+ @ Test public void testApprovalFromFormValidation () throws Exception {
1290+ r .jenkins .setSecurityRealm (r .createDummySecurityRealm ());
1291+ MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy ();
1292+ mockStrategy .grant (Jenkins .ADMINISTER ).everywhere ().to ("admin" );
1293+ for (Permission p : Item .PERMISSIONS .getPermissions ()) {
1294+ mockStrategy .grant (p ).everywhere ().to ("admin" );
1295+ }
1296+ r .jenkins .setAuthorizationStrategy (mockStrategy );
1297+
1298+ FreeStyleProject p = r .createFreeStyleProject ("p" );
1299+ try (JenkinsRule .WebClient wc = r .createWebClient ()) {
1300+ CollectingAlertHandler altertHandler = new CollectingAlertHandler ();
1301+ wc .setAlertHandler (altertHandler );
1302+
1303+ wc .login ("admin" );
1304+ HtmlPage page = wc .getPage (p , "configure" );
1305+ HtmlForm config = page .getFormByName ("config" );
1306+ HtmlFormUtil .getButtonByCaption (config , "Add post-build action" ).click (); // lib/hudson/project/config-publishers2.jelly
1307+ page .getAnchorByText (r .jenkins .getExtensionList (BuildStepDescriptor .class ).get (TestGroovyRecorder .DescriptorImpl .class ).getDisplayName ()).click ();
1308+ wc .waitForBackgroundJavaScript (10000 );
1309+ List <HtmlTextArea > scripts = config .getTextAreasByName ("_.script" );
1310+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
1311+ HtmlTextArea script = scripts .get (scripts .size () - 1 );
1312+ String groovy = "build.externalizableId" ;
1313+ script .setText (groovy );
1314+ // nothing is approved or pending (no save)
1315+ assertThat (ScriptApproval .get ().getPendingScripts (), is (empty ()));
1316+ assertThat (ScriptApproval .get ().getApprovedScriptHashes (), is (emptyArray ()));
1317+
1318+ wc .waitForBackgroundJavaScript (10_000 ); // FormValidation to display
1319+
1320+ page .getAnchorByText ("Approve script" ).click ();
1321+
1322+ wc .waitForBackgroundJavaScript (10_000 ); // wait for the ajax approval to complete
1323+
1324+ assertThat (altertHandler .getCollectedAlerts (), contains ("Script approved" ));
1325+ // script is approved
1326+ assertThat (ScriptApproval .get ().getPendingScripts (), is (empty ()));
1327+ assertThat (ScriptApproval .get ().getApprovedScriptHashes (), is (arrayWithSize (1 )));
1328+
1329+ r .submit (config );
1330+
1331+ FreeStyleBuild b = r .assertBuildStatus (Result .SUCCESS , p .scheduleBuild2 (0 ));
1332+ }
1333+ }
1334+
12741335 public static class HasMainMethod {
12751336 @ Whitelisted
12761337 public HasMainMethod () { }
0 commit comments