2424
2525package org .jenkinsci .plugins .scriptsecurity .sandbox .groovy ;
2626
27+ import org .htmlunit .CollectingAlertHandler ;
2728import org .htmlunit .html .HtmlCheckBoxInput ;
2829import org .htmlunit .html .HtmlInput ;
2930import groovy .lang .Binding ;
6162import org .jenkinsci .plugins .scriptsecurity .scripts .UnapprovedUsageException ;
6263
6364import static org .hamcrest .MatcherAssert .assertThat ;
65+ import static org .hamcrest .Matchers .arrayWithSize ;
66+ import static org .hamcrest .Matchers .contains ;
6467import static org .hamcrest .Matchers .containsString ;
68+ import static org .hamcrest .Matchers .empty ;
69+ import static org .hamcrest .Matchers .emptyArray ;
70+ import static org .hamcrest .Matchers .is ;
6571import org .jenkinsci .plugins .scriptsecurity .sandbox .whitelists .Whitelisted ;
6672import static org .junit .Assert .assertEquals ;
6773import static org .junit .Assert .assertFalse ;
@@ -163,7 +169,7 @@ public class SecureGroovyScriptTest {
163169
164170
165171 /**
166- * Test where the user has ADMINISTER privs, default to non sandbox mode.
172+ * Test where the user has ADMINISTER privs, default to non sandbox mode, but require approval
167173 */
168174 @ Test public void testSandboxDefault_with_ADMINISTER_privs () throws Exception {
169175 r .jenkins .setSecurityRealm (r .createDummySecurityRealm ());
@@ -198,12 +204,12 @@ public class SecureGroovyScriptTest {
198204 // The user has ADMINISTER privs => should default to non sandboxed
199205 assertFalse (publisher .getScript ().isSandbox ());
200206
201- // Because it has ADMINISTER privs, the script should not have ended up pending approval
207+ // even though it has ADMINISTER privs, the script should still require approval
202208 Set <ScriptApproval .PendingScript > pendingScripts = ScriptApproval .get ().getPendingScripts ();
203- assertEquals (0 , pendingScripts .size ());
209+ assertEquals (1 , pendingScripts .size ());
204210
205211 // Test that the script is executable. If it's not, we will get an UnapprovedUsageException
206- assertEquals ( groovy , ScriptApproval .get ().using (groovy , GroovyLanguage .get ()));
212+ assertThrows ( UnapprovedUsageException . class , () -> ScriptApproval .get ().using (groovy , GroovyLanguage .get ()));
207213 }
208214
209215 /**
@@ -892,7 +898,7 @@ public void testScriptApproval() throws Exception {
892898
893899 JenkinsRule .WebClient wc = r .createWebClient ();
894900
895- // If configured by a user with ADMINISTER script is approved if edited by that user
901+ // If configured by a user with ADMINISTER script is not approved and approval is requested
896902 {
897903 wc .login ("admin" );
898904 HtmlForm config = wc .getPage (p , "configure" ).getFormByName ("config" );
@@ -903,8 +909,9 @@ public void testScriptApproval() throws Exception {
903909 script .setText (groovy );
904910 r .submit (config );
905911
906- assertTrue (ScriptApproval .get ().isScriptApproved (groovy , GroovyLanguage .get ()));
907-
912+ assertFalse (ScriptApproval .get ().isScriptApproved (groovy , GroovyLanguage .get ()));
913+ assertEquals (1 , ScriptApproval .get ().getPendingScripts ().size ());
914+
908915 // clean up for next tests
909916 ScriptApproval .get ().preapproveAll ();
910917 ScriptApproval .get ().clearApprovedScripts ();
@@ -929,11 +936,14 @@ public void testScriptApproval() throws Exception {
929936 ScriptApproval .get ().clearApprovedScripts ();
930937 }
931938
932- // If configured by a user with ADMINISTER while escape hatch is on script is approved upon save
939+ // If configured by a user with ADMINISTER while escape hatches are on script is approved upon save
933940 {
934941 wc .login ("admin" );
935- boolean original = ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED ;
942+ boolean originalAdminAutoApprove = ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED ;
936943 ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = true ;
944+ boolean originalAllowAdminApproval = ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED ;
945+ ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED = true ;
946+
937947 try {
938948 HtmlForm config = wc .getPage (p , "configure" ).getFormByName ("config" );
939949 List <HtmlTextArea > scripts = config .getTextAreasByName ("_.script" );
@@ -948,15 +958,18 @@ public void testScriptApproval() throws Exception {
948958 ScriptApproval .get ().preapproveAll ();
949959 ScriptApproval .get ().clearApprovedScripts ();
950960 } finally {
951- ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = original ;
961+ ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = originalAdminAutoApprove ;
962+ ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED = originalAllowAdminApproval ;
952963 }
953964 }
954965
955966 // If configured by a user without ADMINISTER while escape hatch is on script is not approved
956967 {
957968 wc .login ("devel" );
958- boolean original = ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED ;
969+ boolean originalAdminAutoApprove = ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED ;
959970 ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = true ;
971+ boolean originalAllowAdminApproval = ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED ;
972+ ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED = true ;
960973 try {
961974 r .submit (wc .getPage (p , "configure" ).getFormByName ("config" ));
962975
@@ -966,7 +979,8 @@ public void testScriptApproval() throws Exception {
966979 ScriptApproval .get ().preapproveAll ();
967980 ScriptApproval .get ().clearApprovedScripts ();
968981 } finally {
969- ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = original ;
982+ ScriptApproval .ADMIN_AUTO_APPROVAL_ENABLED = originalAdminAutoApprove ;
983+ ScriptApproval .ALLOW_ADMIN_APPROVAL_ENABLED = originalAllowAdminApproval ;
970984 }
971985 }
972986 }
@@ -1271,6 +1285,52 @@ public void testScriptAtFieldInitializers() throws Exception {
12711285 r .assertLogContains ("new java.io.File java.lang.String" , b );
12721286 }
12731287
1288+ @ Test public void testApprovalFromFormValidation () throws Exception {
1289+ r .jenkins .setSecurityRealm (r .createDummySecurityRealm ());
1290+ MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy ();
1291+ mockStrategy .grant (Jenkins .ADMINISTER ).everywhere ().to ("admin" );
1292+ for (Permission p : Item .PERMISSIONS .getPermissions ()) {
1293+ mockStrategy .grant (p ).everywhere ().to ("admin" );
1294+ }
1295+ r .jenkins .setAuthorizationStrategy (mockStrategy );
1296+
1297+ FreeStyleProject p = r .createFreeStyleProject ("p" );
1298+ try (JenkinsRule .WebClient wc = r .createWebClient ()) {
1299+ CollectingAlertHandler altertHandler = new CollectingAlertHandler ();
1300+ wc .setAlertHandler (altertHandler );
1301+
1302+ wc .login ("admin" );
1303+ HtmlPage page = wc .getPage (p , "configure" );
1304+ HtmlForm config = page .getFormByName ("config" );
1305+ HtmlFormUtil .getButtonByCaption (config , "Add post-build action" ).click (); // lib/hudson/project/config-publishers2.jelly
1306+ page .getAnchorByText (r .jenkins .getExtensionList (BuildStepDescriptor .class ).get (TestGroovyRecorder .DescriptorImpl .class ).getDisplayName ()).click ();
1307+ wc .waitForBackgroundJavaScript (10000 );
1308+ List <HtmlTextArea > scripts = config .getTextAreasByName ("_.script" );
1309+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
1310+ HtmlTextArea script = scripts .get (scripts .size () - 1 );
1311+ String groovy = "build.externalizableId" ;
1312+ script .setText (groovy );
1313+ // nothing is approved or pending (no save)
1314+ assertThat (ScriptApproval .get ().getPendingScripts (), is (empty ()));
1315+ assertThat (ScriptApproval .get ().getApprovedScriptHashes (), is (emptyArray ()));
1316+
1317+ wc .waitForBackgroundJavaScript (10_000 ); // FormValidation to display
1318+
1319+ page .getAnchorByText ("Approve script" ).click ();
1320+
1321+ wc .waitForBackgroundJavaScript (10_000 ); // wait for the ajax approval to complete
1322+
1323+ assertThat (altertHandler .getCollectedAlerts (), contains ("Script approved" ));
1324+ // script is approved
1325+ assertThat (ScriptApproval .get ().getPendingScripts (), is (empty ()));
1326+ assertThat (ScriptApproval .get ().getApprovedScriptHashes (), is (arrayWithSize (1 )));
1327+
1328+ r .submit (config );
1329+
1330+ FreeStyleBuild b = r .assertBuildStatus (Result .SUCCESS , p .scheduleBuild2 (0 ));
1331+ }
1332+ }
1333+
12741334 public static class HasMainMethod {
12751335 @ Whitelisted
12761336 public HasMainMethod () { }
0 commit comments