Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>ssh-credentials</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>git-client</artifactId>
</dependency>
<!-- jenkins dependencies -->
<!-- test dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,26 @@

package com.cloudbees.jenkins.plugins.sshagent.exec;

import hudson.AbortException;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.TaskListener;
import hudson.slaves.WorkspaceList;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import hudson.AbortException;
import hudson.FilePath;
import hudson.Functions;
import hudson.Launcher;
import hudson.model.TaskListener;
import hudson.plugins.git.GitTool;
import hudson.slaves.WorkspaceList;

/**
* Runs a native SSH agent installed on a system.
*/
Expand All @@ -46,15 +53,46 @@

/** Agent environment used for {@code ssh-add} and {@code ssh-agent -k}. */
private final Map<String, String> agentEnv;
private final String sshAgentExe;

public ExecRemoteAgent(Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
sshAgentExe = !Functions.isWindows() ? "ssh-agent" : searchSSHAgentExeForWindows(launcher, listener);

Check warning on line 59 in src/main/java/com/cloudbees/jenkins/plugins/sshagent/exec/ExecRemoteAgent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 59 is only partially covered, one branch is missing
String agentOutput = executeCommand(launcher, listener, sshAgentExe);
agentEnv = parseAgentEnv(agentOutput, listener); // TODO could include local filenames, better to look up remote charset

Check warning on line 61 in src/main/java/com/cloudbees/jenkins/plugins/sshagent/exec/ExecRemoteAgent.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: could include local filenames, better to look up remote charset
}

private static String executeCommand(Launcher launcher, TaskListener listener, String... cmd)
throws IOException, InterruptedException, AbortException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (launcher.launch().cmds("ssh-agent").stdout(baos).start()
.joinWithTimeout(1, TimeUnit.MINUTES, listener) != 0) {
if (launcher.launch().cmds(cmd).stdout(baos).start().joinWithTimeout(1, TimeUnit.MINUTES, listener) != 0) {

Check warning on line 67 in src/main/java/com/cloudbees/jenkins/plugins/sshagent/exec/ExecRemoteAgent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 67 is only partially covered, one branch is missing
String reason = new String(baos.toByteArray(), StandardCharsets.US_ASCII);
throw new AbortException("Failed to run ssh-agent: " + reason);
}
agentEnv = parseAgentEnv(new String(baos.toByteArray(), StandardCharsets.US_ASCII), listener); // TODO could include local filenames, better to look up remote charset
return new String(baos.toByteArray(), StandardCharsets.US_ASCII);
}

private static final Path GIT_SSH_AGENT_PATH_WINDOWS = Path.of("usr", "bin", "ssh-agent.exe");

private static String searchSSHAgentExeForWindows(Launcher launcher, TaskListener listener)
throws IOException, InterruptedException {
Optional<Path> defaultGitHome = Optional.ofNullable(GitTool.getDefaultInstallation()) //
.map(GitTool::getHome).map(Path::of);
if (defaultGitHome.isPresent()) {
Path sshAgentExe = defaultGitHome.get().resolve(GIT_SSH_AGENT_PATH_WINDOWS);
if (Files.isRegularFile(sshAgentExe)) {
return sshAgentExe.toString();
}
}
String gitPaths = executeCommand(launcher, listener, "where", "git");
List<Path> paths = gitPaths.lines().map(Path::of).toList();
for (Path gitExe : paths) {
Path sshAgentExe = gitExe.getParent().getParent().resolve(GIT_SSH_AGENT_PATH_WINDOWS);

Check warning on line 89 in src/main/java/com/cloudbees/jenkins/plugins/sshagent/exec/ExecRemoteAgent.java

View check run for this annotation

ci.jenkins.io / SpotBugs

NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE

NORMAL: Possible null pointer dereference in com.cloudbees.jenkins.plugins.sshagent.exec.ExecRemoteAgent.searchSSHAgentExeForWindows(Launcher, TaskListener) due to return value of called method
Raw output
<p> The return value from a method is dereferenced without a null check, and the return value of that method is one that should generally be checked for null. This may lead to a <code>NullPointerException</code> when the code is executed. </p>
if (Files.exists(sshAgentExe)) {
return sshAgentExe.toString();
}
}
throw new IllegalStateException(

Check warning on line 94 in src/main/java/com/cloudbees/jenkins/plugins/sshagent/exec/ExecRemoteAgent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 78-94 are not covered by tests
"Executing with default ssh-agent on Windows is not supported and an alternative implementation from a git installation is not available.");
}

/**
Expand Down Expand Up @@ -84,10 +122,10 @@
env.put("DISPLAY", "bogus"); // just to force using SSH_ASKPASS
env.put("SSH_ASKPASS", askpass.getRemote());
}

// as the next command is in quiet mode, we just add a message to the log
listener.getLogger().println("Running ssh-add (command line suppressed)");

if (launcher.launch().quiet(true).cmds("ssh-add", keyFile.getRemote()).envs(env)
.stdout(listener).start().joinWithTimeout(1, TimeUnit.MINUTES, listener) != 0) {
throw new AbortException("Failed to run ssh-add");
Expand Down Expand Up @@ -117,7 +155,7 @@
throw new AbortException("Failed to run ssh-agent -k");
}
}

/**
* Parses ssh-agent output.
*/
Expand All @@ -133,10 +171,10 @@
// get SSH_AGENT_PID
env.put(AgentPidVar, getAgentValue(agentOutput, AgentPidVar));
listener.getLogger().println(AgentPidVar + "=" + env.get(AgentPidVar));

return env;
}

/**
* Parses a value from ssh-agent output.
*/
Expand All @@ -145,16 +183,16 @@
int end = agentOutput.indexOf(';', pos);
return agentOutput.substring(pos, end);
}

/**
* Creates a self-deleting script for SSH_ASKPASS. Self-deleting to be able to detect a wrong passphrase.
* Creates a self-deleting script for SSH_ASKPASS. Self-deleting to be able to detect a wrong passphrase.
*/
private FilePath createAskpassScript(FilePath temp) throws IOException, InterruptedException {
// TODO: assuming that ssh-add runs the script in shell even on Windows, not cmd

Check warning on line 191 in src/main/java/com/cloudbees/jenkins/plugins/sshagent/exec/ExecRemoteAgent.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: assuming that ssh-add runs the script in shell even on Windows, not cmd
// for cmd following could work
// suffix = ".bat";
// script = "@ECHO %SSH_PASSPHRASE%\nDEL \"" + askpass.getAbsolutePath() + "\"\n";

FilePath askpass = temp.createTextTempFile("askpass_", ".sh", "#!/bin/sh\necho \"$SSH_PASSPHRASE\"\nrm \"$0\"\n");

// executable only for a current user
Expand Down
Loading