2525package org .jenkinsci .plugins .workflow .cps ;
2626
2727import com .cloudbees .groovy .cps .CpsTransformer ;
28+ import com .gargoylesoftware .htmlunit .html .HtmlCheckBoxInput ;
29+ import com .gargoylesoftware .htmlunit .html .HtmlForm ;
30+ import com .gargoylesoftware .htmlunit .html .HtmlInput ;
31+ import com .gargoylesoftware .htmlunit .html .HtmlTextArea ;
2832import hudson .Functions ;
2933import hudson .model .Computer ;
3034import hudson .model .Describable ;
3135import hudson .model .Executor ;
36+ import hudson .model .Item ;
3237import hudson .model .Result ;
3338import java .io .Serializable ;
3439import java .util .Collections ;
40+ import java .util .List ;
3541import java .util .Set ;
3642
3743import java .util .logging .Level ;
44+
45+ import hudson .security .Permission ;
3846import jenkins .model .Jenkins ;
3947
4048import org .jenkinsci .plugins .scriptsecurity .sandbox .RejectedAccessException ;
49+ import org .jenkinsci .plugins .scriptsecurity .scripts .ScriptApproval ;
50+ import org .jenkinsci .plugins .scriptsecurity .scripts .languages .GroovyLanguage ;
4151import org .jenkinsci .plugins .workflow .job .WorkflowJob ;
4252import org .jenkinsci .plugins .workflow .job .WorkflowRun ;
4353import org .jenkinsci .plugins .workflow .steps .Step ;
5868import org .jvnet .hudson .test .Issue ;
5969import org .jvnet .hudson .test .JenkinsRule ;
6070import org .jvnet .hudson .test .LoggerRule ;
71+ import org .jvnet .hudson .test .MockAuthorizationStrategy ;
6172import org .jvnet .hudson .test .TestExtension ;
6273import org .kohsuke .stapler .DataBoundConstructor ;
6374
6475import static org .hamcrest .MatcherAssert .assertThat ;
6576import static org .hamcrest .Matchers .instanceOf ;
6677import static org .junit .Assert .assertEquals ;
78+ import static org .junit .Assert .assertFalse ;
6779import static org .junit .Assert .assertNull ;
80+ import static org .junit .Assert .assertTrue ;
6881import static org .junit .Assert .fail ;
6982
7083public class CpsFlowDefinition2Test {
@@ -89,7 +102,7 @@ public void endlessRecursion() throws Exception {
89102 WorkflowRun r = jenkins .assertBuildStatus (Result .FAILURE , job .scheduleBuild2 (0 ).get ());
90103 jenkins .assertLogContains ("look for unbounded recursion" , r );
91104
92- Assert . assertTrue ("No queued FlyWeightTask for job should remain after failure" , jenkins .jenkins .getQueue ().isEmpty ());
105+ assertTrue ("No queued FlyWeightTask for job should remain after failure" , jenkins .jenkins .getQueue ().isEmpty ());
93106
94107 for (Computer c : jenkins .jenkins .getComputers ()) {
95108 for (Executor ex : c .getExecutors ()) {
@@ -117,7 +130,7 @@ public void endlessRecursionNonCPS() throws Exception {
117130 // Should have failed with error about excessive recursion depth
118131 WorkflowRun r = jenkins .assertBuildStatus (Result .FAILURE , job .scheduleBuild2 (0 ).get ());
119132
120- Assert . assertTrue ("No queued FlyWeightTask for job should remain after failure" , jenkins .jenkins .getQueue ().isEmpty ());
133+ assertTrue ("No queued FlyWeightTask for job should remain after failure" , jenkins .jenkins .getQueue ().isEmpty ());
121134
122135 for (Computer c : jenkins .jenkins .getComputers ()) {
123136 for (Executor ex : c .getExecutors ()) {
@@ -852,6 +865,141 @@ public void scriptInitializerCallsCpsTransformedMethod() throws Exception {
852865 assertNull (Jenkins .get ().getDescription ());
853866 }
854867
868+ @ Issue ("SECURITY-2450" )
869+ @ Test
870+ public void cpsScriptNonAdminConfiguration () throws Exception {
871+ jenkins .jenkins .setSecurityRealm (jenkins .createDummySecurityRealm ());
872+
873+ MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy ();
874+ mockStrategy .grant (Jenkins .READ ).everywhere ().to ("devel" );
875+ for (Permission p : Item .PERMISSIONS .getPermissions ()) {
876+ mockStrategy .grant (p ).everywhere ().to ("devel" );
877+ }
878+ jenkins .jenkins .setAuthorizationStrategy (mockStrategy );
879+
880+ JenkinsRule .WebClient wcDevel = jenkins .createWebClient ();
881+ wcDevel .login ("devel" );
882+
883+ WorkflowJob p = jenkins .createProject (WorkflowJob .class );
884+
885+ HtmlForm config = wcDevel .getPage (p , "configure" ).getFormByName ("config" );
886+ List <HtmlTextArea > scripts = config .getTextAreasByName ("_.script" );
887+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
888+ HtmlTextArea script = scripts .get (scripts .size () - 1 );
889+ String groovy = "echo 'hi from cpsScriptNonAdminConfiguration'" ;
890+ script .setText (groovy );
891+
892+ List <HtmlInput > sandboxes = config .getInputsByName ("_.sandbox" );
893+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
894+ HtmlCheckBoxInput sandbox = (HtmlCheckBoxInput ) sandboxes .get (sandboxes .size () - 1 );
895+ assertTrue (sandbox .isChecked ());
896+ sandbox .setChecked (false );
897+
898+ jenkins .submit (config );
899+
900+ assertEquals (1 , ScriptApproval .get ().getPendingScripts ().size ());
901+ assertFalse (ScriptApproval .get ().isScriptApproved (groovy , GroovyLanguage .get ()));
902+ }
903+
904+ @ Issue ("SECURITY-2450" )
905+ @ Test
906+ public void cpsScriptAdminConfiguration () throws Exception {
907+ jenkins .jenkins .setSecurityRealm (jenkins .createDummySecurityRealm ());
908+
909+ MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy ();
910+ mockStrategy .grant (Jenkins .ADMINISTER ).everywhere ().to ("admin" );
911+ for (Permission p : Item .PERMISSIONS .getPermissions ()) {
912+ mockStrategy .grant (p ).everywhere ().to ("admin" );
913+ }
914+ jenkins .jenkins .setAuthorizationStrategy (mockStrategy );
915+
916+ JenkinsRule .WebClient admin = jenkins .createWebClient ();
917+ admin .login ("admin" );
918+
919+ WorkflowJob p = jenkins .createProject (WorkflowJob .class );
920+
921+ HtmlForm config = admin .getPage (p , "configure" ).getFormByName ("config" );
922+ List <HtmlTextArea > scripts = config .getTextAreasByName ("_.script" );
923+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
924+ HtmlTextArea script = scripts .get (scripts .size () - 1 );
925+ String groovy = "echo 'hi from cpsScriptAdminConfiguration'" ;
926+ script .setText (groovy );
927+
928+ List <HtmlInput > sandboxes = config .getInputsByName ("_.sandbox" );
929+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
930+ HtmlCheckBoxInput sandbox = (HtmlCheckBoxInput ) sandboxes .get (sandboxes .size () - 1 );
931+ assertTrue (sandbox .isChecked ());
932+ sandbox .setChecked (false );
933+
934+ jenkins .submit (config );
935+
936+ assertTrue (ScriptApproval .get ().isScriptApproved (groovy , GroovyLanguage .get ()));
937+ }
938+
939+ @ Issue ("SECURITY-2450" )
940+ @ Test
941+ public void cpsScriptAdminModification () throws Exception {
942+ jenkins .jenkins .setSecurityRealm (jenkins .createDummySecurityRealm ());
943+
944+ MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy ();
945+ mockStrategy .grant (Jenkins .READ ).everywhere ().to ("devel" );
946+ mockStrategy .grant (Jenkins .ADMINISTER ).everywhere ().to ("admin" );
947+ for (Permission p : Item .PERMISSIONS .getPermissions ()) {
948+ mockStrategy .grant (p ).everywhere ().to ("devel" );
949+ mockStrategy .grant (p ).everywhere ().to ("admin" );
950+ }
951+ jenkins .jenkins .setAuthorizationStrategy (mockStrategy );
952+
953+ JenkinsRule .WebClient wc = jenkins .createWebClient ();
954+ wc .login ("devel" );
955+
956+ WorkflowJob p = jenkins .createProject (WorkflowJob .class );
957+ String userGroovy = "echo 'hi from devel'" ;
958+ String adminGroovy = "echo 'hi from admin'" ;
959+
960+ // initial configuration by user, script ends up in pending
961+ {
962+ HtmlForm config = wc .getPage (p , "configure" ).getFormByName ("config" );
963+ List <HtmlTextArea > scripts = config .getTextAreasByName ("_.script" );
964+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
965+ HtmlTextArea script = scripts .get (scripts .size () - 1 );
966+ script .setText (userGroovy );
967+
968+ List <HtmlInput > sandboxes = config .getInputsByName ("_.sandbox" );
969+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
970+ HtmlCheckBoxInput sandbox = (HtmlCheckBoxInput ) sandboxes .get (sandboxes .size () - 1 );
971+ assertTrue (sandbox .isChecked ());
972+ sandbox .setChecked (false );
973+
974+ jenkins .submit (config );
975+
976+ assertFalse (ScriptApproval .get ().isScriptApproved (userGroovy , GroovyLanguage .get ()));
977+ }
978+
979+ wc .login ("admin" );
980+
981+ // modification by admin, script gets approved automatically
982+ {
983+ HtmlForm config = wc .getPage (p , "configure" ).getFormByName ("config" );
984+ List <HtmlTextArea > scripts = config .getTextAreasByName ("_.script" );
985+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
986+ HtmlTextArea script = scripts .get (scripts .size () - 1 );
987+ script .setText (adminGroovy );
988+
989+ List <HtmlInput > sandboxes = config .getInputsByName ("_.sandbox" );
990+ // Get the last one, because previous ones might be from Lockable Resources during PCT.
991+ HtmlCheckBoxInput sandbox = (HtmlCheckBoxInput ) sandboxes .get (sandboxes .size () - 1 );
992+ assertFalse (sandbox .isChecked ());
993+
994+ jenkins .submit (config );
995+
996+ // script content was modified by admin, so it should be approved upon save
997+ // the one that had been submitted by the user previously stays in pending
998+ assertTrue (ScriptApproval .get ().isScriptApproved (adminGroovy , GroovyLanguage .get ()));
999+ assertFalse (ScriptApproval .get ().isScriptApproved (userGroovy , GroovyLanguage .get ()));
1000+ }
1001+ }
1002+
8551003 public static class UnsafeParameterStep extends Step implements Serializable {
8561004 private final UnsafeDescribable val ;
8571005 @ DataBoundConstructor
0 commit comments