2828import hudson .model .Result ;
2929
3030import static org .jenkinsci .plugins .pipeline .utility .steps .FilenameTestsUtils .separatorsToSystemEscaped ;
31+ import static org .jenkinsci .plugins .pipeline .utility .steps .conf .ReadPropertiesStepExecution .CUSTOM_PREFIX_INTERPOLATOR_LOOKUPS ;
32+ import static org .junit .Assert .assertEquals ;
33+ import static org .junit .Assert .assertNotEquals ;
3134
3235import org .jenkinsci .plugins .pipeline .utility .steps .Messages ;
3336import org .jenkinsci .plugins .workflow .cps .CpsFlowDefinition ;
3740import org .junit .Rule ;
3841import org .junit .Test ;
3942import org .junit .rules .TemporaryFolder ;
43+ import org .jvnet .hudson .test .FlagRule ;
44+ import org .jvnet .hudson .test .Issue ;
4045import org .jvnet .hudson .test .JenkinsRule ;
4146
4247import java .io .File ;
@@ -54,6 +59,8 @@ public class ReadPropertiesStepTest {
5459 public JenkinsRule j = new JenkinsRule ();
5560 @ Rule
5661 public TemporaryFolder temp = new TemporaryFolder ();
62+ @ Rule
63+ public FlagRule <String > customLookups = new FlagRule <>(() -> CUSTOM_PREFIX_INTERPOLATOR_LOOKUPS , x -> CUSTOM_PREFIX_INTERPOLATOR_LOOKUPS = x );
5764
5865 @ Before
5966 public void setup () throws Exception {
@@ -341,4 +348,75 @@ public void readFileAndTextInterpolatedWithCyclicValues() throws Exception {
341348 "}" , true ));
342349 j .assertBuildStatusSuccess (p .scheduleBuild2 (0 ));
343350 }
344- }
351+
352+ @ Issue ("SECURITY-2949" )
353+ @ Test
354+ public void unsafeInterpolatorsDoNotInterpolate () throws Exception {
355+ Properties props = new Properties ();
356+ props .setProperty ("file" , "${file:utf8:/etc/passwd}" );
357+ props .setProperty ("hax" , "${script:Groovy:jenkins.model.Jenkins.get().systemMessage = 'pwn3d'}" );
358+ File textFile = temp .newFile ();
359+ try (FileWriter f = new FileWriter (textFile )) {
360+ props .store (f , "Pipeline test" );
361+ }
362+
363+ WorkflowJob p = j .jenkins .createProject (WorkflowJob .class , "p" );
364+ p .setDefinition (new CpsFlowDefinition (
365+ "node('slaves') {\n " +
366+ " def props = readProperties interpolate: true, file: '" + separatorsToSystemEscaped (textFile .getAbsolutePath ()) + "'\n " +
367+ " assert props['file'] == '${file:utf8:/etc/passwd}'\n " +
368+ " assert props['hax'] == '${script:Groovy:jenkins.model.Jenkins.get().systemMessage = \\ 'pwn3d\\ '}'\n " +
369+ "}" , true ));
370+ j .assertBuildStatusSuccess (p .scheduleBuild2 (0 ));
371+ assertNotEquals ("pwn3d" , j .jenkins .getSystemMessage ());
372+ }
373+
374+ @ Issue ("SECURITY-2949" )
375+ @ Test
376+ public void safeInterpolatorsDoInterpolate () throws Exception {
377+ Properties props = new Properties ();
378+ props .setProperty ("urld" , "${urlDecoder:Hello+World%21}" );
379+ props .setProperty ("urle" , "${urlEncoder:Hello World!}" );
380+ props .setProperty ("base64d" , "${base64Decoder:SGVsbG9Xb3JsZCE=}" );
381+ props .setProperty ("base64e" , "${base64Encoder:HelloWorld!}" );
382+ File textFile = temp .newFile ();
383+ try (FileWriter f = new FileWriter (textFile )) {
384+ props .store (f , "Pipeline test" );
385+ }
386+ WorkflowJob p = j .jenkins .createProject (WorkflowJob .class , "p" );
387+ p .setDefinition (new CpsFlowDefinition (
388+ "node('slaves') {\n " +
389+ " def props = readProperties interpolate: true, file: '" + separatorsToSystemEscaped (textFile .getAbsolutePath ()) + "'\n " +
390+ " assert props['base64d'] == 'HelloWorld!'\n " +
391+ " assert props['base64e'] == 'SGVsbG9Xb3JsZCE='\n " +
392+ " assert props['urld'] == 'Hello World!'\n " +
393+ " assert props['urle'] == 'Hello+World%21'\n " +
394+ "}" , true ));
395+ j .assertBuildStatusSuccess (p .scheduleBuild2 (0 ));
396+ }
397+
398+ @ Issue ("SECURITY-2949" )
399+ @ Test
400+ public void customLookupsOverrideDefaultInterpolators () throws Exception {
401+ CUSTOM_PREFIX_INTERPOLATOR_LOOKUPS = "script,base64_encoder" ;
402+
403+ Properties props = new Properties ();
404+ props .setProperty ("hax" , "${script:Groovy:jenkins.model.Jenkins.get().systemMessage = 'pwn3d'}" );
405+ props .setProperty ("base64d" , "${base64Decoder:SGVsbG9Xb3JsZCE=}" );
406+ props .setProperty ("base64e" , "${base64Encoder:HelloWorld!}" );
407+ File textFile = temp .newFile ();
408+ try (FileWriter f = new FileWriter (textFile )) {
409+ props .store (f , "Pipeline test" );
410+ }
411+
412+ WorkflowJob p = j .jenkins .createProject (WorkflowJob .class , "p" );
413+ p .setDefinition (new CpsFlowDefinition (
414+ "node('slaves') {\n " +
415+ " def props = readProperties interpolate: true, file: '" + separatorsToSystemEscaped (textFile .getAbsolutePath ()) + "'\n " +
416+ " assert props['base64d'] == '${base64Decoder:SGVsbG9Xb3JsZCE=}'\n " +
417+ " assert props['base64e'] == 'SGVsbG9Xb3JsZCE='\n " +
418+ "}" , true ));
419+ j .assertBuildStatusSuccess (p .scheduleBuild2 (0 ));
420+ assertEquals ("pwn3d" , j .jenkins .getSystemMessage ());
421+ }
422+ }
0 commit comments