2323 */
2424package org .jenkinsci .plugins .docker .workflow ;
2525
26+ import com .cloudbees .jenkins .plugins .sshcredentials .impl .BasicSSHUserPrivateKey ;
2627import com .cloudbees .plugins .credentials .CredentialsProvider ;
2728import com .cloudbees .plugins .credentials .CredentialsScope ;
2829import com .cloudbees .plugins .credentials .common .IdCredentials ;
3738import hudson .model .Node ;
3839import hudson .model .Result ;
3940import hudson .model .User ;
41+ import hudson .plugins .sshslaves .SSHLauncher ;
42+ import hudson .slaves .DumbSlave ;
43+ import java .io .ByteArrayOutputStream ;
4044import jenkins .model .Jenkins ;
4145import jenkins .security .QueueItemAuthenticatorConfiguration ;
4246import org .jenkinsci .plugins .docker .commons .credentials .DockerRegistryEndpoint ;
5963import org .junit .Rule ;
6064import org .junit .Test ;
6165import org .jvnet .hudson .test .Issue ;
62- import org .jvnet .hudson .test .JenkinsRule ;
6366import org .jvnet .hudson .test .MockAuthorizationStrategy ;
6467import org .jvnet .hudson .test .MockQueueItemAuthenticator ;
6568import org .jvnet .hudson .test .TestExtension ;
6669import org .kohsuke .stapler .DataBoundConstructor ;
6770
6871import java .io .Serializable ;
72+ import java .nio .charset .StandardCharsets ;
73+ import java .nio .file .Files ;
6974import java .util .Set ;
7075import java .util .logging .Level ;
76+ import org .apache .commons .text .StringEscapeUtils ;
77+ import org .apache .sshd .common .config .keys .KeyUtils ;
78+ import org .apache .sshd .common .config .keys .writer .openssh .OpenSSHKeyPairResourceWriter ;
79+ import org .apache .sshd .common .keyprovider .KeyPairProvider ;
80+ import static org .hamcrest .MatcherAssert .assertThat ;
81+ import static org .hamcrest .Matchers .containsString ;
82+ import static org .hamcrest .Matchers .not ;
83+ import static org .jenkinsci .plugins .docker .workflow .DockerTestUtil .assumeDocker ;
7184import org .jenkinsci .plugins .structs .describable .DescribableModel ;
85+ import org .jenkinsci .plugins .workflow .support .pickles .FilePathPickle ;
86+ import org .jenkinsci .plugins .workflow .test .steps .SemaphoreStep ;
87+ import org .junit .ClassRule ;
7288import org .jvnet .hudson .test .BuildWatcher ;
89+ import org .jvnet .hudson .test .JenkinsSessionRule ;
7390import org .jvnet .hudson .test .LoggerRule ;
91+ import org .testcontainers .containers .GenericContainer ;
7492
7593public class RegistryEndpointStepTest {
7694
77- @ Rule public JenkinsRule r = new JenkinsRule ();
95+ @ Rule public final JenkinsSessionRule rr = new JenkinsSessionRule ();
7896 @ Rule public LoggerRule logging = new LoggerRule ();
79- @ Rule public BuildWatcher bw = new BuildWatcher ();
97+ @ ClassRule public static BuildWatcher bw = new BuildWatcher ();
8098
8199 @ Issue ("JENKINS-51395" )
82- @ Test public void configRoundTrip () throws Exception {
100+ @ Test public void configRoundTrip () throws Throwable {
83101 logging .record (DescribableModel .class , Level .FINE );
102+ rr .then (r -> {
84103 { // Recommended syntax.
85104 SnippetizerTester st = new SnippetizerTester (r );
86105 RegistryEndpointStep step = new RegistryEndpointStep (new DockerRegistryEndpoint ("https://myreg/" , null ));
@@ -116,12 +135,14 @@ public class RegistryEndpointStepTest {
116135 assertEquals ("registryCreds" , registry .getCredentialsId ());
117136 // TODO check toolName
118137 }
138+ });
119139 }
120140
121141 @ Test
122- public void stepExecutionWithCredentials () throws Exception {
142+ public void stepExecutionWithCredentials () throws Throwable {
123143 assumeNotWindows ();
124144
145+ rr .then (r -> {
125146 IdCredentials registryCredentials = new UsernamePasswordCredentialsImpl (CredentialsScope .GLOBAL , "registryCreds" , null , "me" , "s3cr3t" );
126147 CredentialsProvider .lookupStores (r .jenkins ).iterator ().next ().addCredentials (Domain .global (), registryCredentials );
127148
@@ -137,12 +158,14 @@ public void stepExecutionWithCredentials() throws Exception {
137158 r .assertBuildStatusSuccess (r .waitForCompletion (b ));
138159 r .assertLogContains ("docker login -u me -p ******** https://my-reg:1234" , b );
139160 r .assertLogNotContains ("s3cr3t" , b );
161+ });
140162 }
141163
142164 @ Test
143- public void stepExecutionWithCredentialsAndQueueItemAuthenticator () throws Exception {
165+ public void stepExecutionWithCredentialsAndQueueItemAuthenticator () throws Throwable {
144166 assumeNotWindows ();
145167
168+ rr .then (r -> {
146169 r .getInstance ().setSecurityRealm (r .createDummySecurityRealm ());
147170 MockAuthorizationStrategy auth = new MockAuthorizationStrategy ()
148171 .grant (Jenkins .READ ).everywhere ().to ("alice" , "bob" )
@@ -181,6 +204,70 @@ public void stepExecutionWithCredentialsAndQueueItemAuthenticator() throws Excep
181204
182205 // Bob does not have Credentials.USE_ITEM permission and should not be able to use the credential.
183206 r .assertBuildStatus (Result .FAILURE , p2 .scheduleBuild2 (0 ));
207+ });
208+ }
209+
210+ @ Issue ("JENKINS-75679" )
211+ @ Test public void noFilePathPickle () throws Throwable {
212+ assumeDocker ();
213+ try (var agent = new SSHAgentContainer ()) {
214+ agent .start ();
215+ rr .then (r -> {
216+ agent .register ("remote" );
217+ var registryCredentials = new UsernamePasswordCredentialsImpl (CredentialsScope .GLOBAL , "registryCreds" , null , "me" , "s3cr3t" );
218+ CredentialsProvider .lookupStores (r .jenkins ).iterator ().next ().addCredentials (Domain .global (), registryCredentials );
219+ var p = r .createProject (WorkflowJob .class , "p" );
220+ p .setDefinition (new CpsFlowDefinition (
221+ """
222+ node('remote') {
223+ mockDockerLogin {
224+ withDockerRegistry(url: 'https://my-reg:1234', credentialsId: 'registryCreds') {
225+ semaphore 'wait'
226+ }
227+ }
228+ }
229+ """ , true ));
230+ var b = p .scheduleBuild2 (0 ).waitForStart ();
231+ SemaphoreStep .waitForStart ("wait/1" , b );
232+ });
233+ @ SuppressWarnings ("deprecation" )
234+ var verboten = FilePathPickle .class .getName ();
235+ assertThat (StringEscapeUtils .escapeJava (Files .readString (rr .getHome ().toPath ().resolve ("jobs/p/builds/1/program.dat" ), StandardCharsets .ISO_8859_1 )), not (containsString (verboten )));
236+ rr .then (r -> {
237+ var b = r .jenkins .getItemByFullName ("p" , WorkflowJob .class ).getBuildByNumber (1 );
238+ SemaphoreStep .success ("wait/1" , null );
239+ r .assertBuildStatusSuccess (r .waitForCompletion (b ));
240+ r .assertLogContains ("docker login -u me -p ******** https://my-reg:1234" , b );
241+ r .assertLogNotContains ("s3cr3t" , b );
242+ });
243+ }
244+ }
245+
246+ private static final class SSHAgentContainer extends GenericContainer <SSHAgentContainer > {
247+ private final String priv ;
248+ SSHAgentContainer () {
249+ super ("jenkins/ssh-agent:6.17.0" );
250+ try {
251+ var kp = KeyUtils .generateKeyPair (KeyPairProvider .SSH_RSA , 2048 );
252+ var kprw = new OpenSSHKeyPairResourceWriter ();
253+ var baos = new ByteArrayOutputStream ();
254+ kprw .writePublicKey (kp , null , baos );
255+ var pub = baos .toString (StandardCharsets .US_ASCII );
256+ baos .reset ();
257+ kprw .writePrivateKey (kp , null , null , baos );
258+ priv = baos .toString (StandardCharsets .US_ASCII );
259+ withEnv ("JENKINS_AGENT_SSH_PUBKEY" , pub );
260+ withExposedPorts (22 );
261+ } catch (Exception x ) {
262+ throw new AssertionError (x );
263+ }
264+ }
265+ void register (String name ) throws Exception {
266+ var creds = new BasicSSHUserPrivateKey (CredentialsScope .GLOBAL , null , "jenkins" , new BasicSSHUserPrivateKey .DirectEntryPrivateKeySource (priv ), null , null );
267+ CredentialsProvider .lookupStores (Jenkins .get ()).iterator ().next ().addCredentials (Domain .global (), creds );
268+ var port = getMappedPort (22 );
269+ Jenkins .get ().addNode (new DumbSlave (name , "/home/jenkins/agent" , new SSHLauncher (getHost (), port , creds .getId ())));
270+ }
184271 }
185272
186273 public static class MockLauncherStep extends Step {
0 commit comments