Skip to content

Commit

Permalink
Parameterize hash function; continue to default to sha256 (#300)
Browse files Browse the repository at this point in the history
* Parameterize hash function; continue to default to sha256

* Update plugin-management-library/src/main/java/io/jenkins/tools/pluginmanager/impl/PluginManager.java

Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>

* Update README for hash function

Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
  • Loading branch information
kyounger and timja committed Mar 6, 2021
1 parent 7a45181 commit e71682a
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 20 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ if the user doesn't have a home directory when it will go to: `$(pwd)/.cache/jen
* `JENKINS_UC_DOWNLOAD`: used to configure the URL from where plugins will be downloaded from. Often used to cache or to proxy the Jenkins plugin download site.
If set then all plugins will be downloaded through that URL.

* `JENKINS_UC_HASH_FUNCTION`: used to configure the hash function which checks content from UCs. Currently `SHA1` (deprecated), `SHA256` (default), and `SHA512` can be specified.

#### Plugin Input Format
The expected format for plugins in the .txt file or entered through the `--plugins` CLI option is `artifact ID:version` or `artifact ID:url` or `artifact:version:url`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import hudson.util.VersionNumber;
import io.jenkins.tools.pluginmanager.config.Config;
import io.jenkins.tools.pluginmanager.config.Credentials;
import io.jenkins.tools.pluginmanager.config.HashFunction;
import io.jenkins.tools.pluginmanager.config.OutputFormat;
import io.jenkins.tools.pluginmanager.config.PluginInputException;
import io.jenkins.tools.pluginmanager.config.Settings;
Expand Down Expand Up @@ -172,6 +173,7 @@ Config setup() {
.withUseLatestAll(isUseLatestAll())
.withSkipFailedPlugins(isSkipFailedPlugins())
.withCredentials(credentials)
.withHashFunction(getHashFunction())
.build();
}

Expand Down Expand Up @@ -560,4 +562,22 @@ public InputStream getPropertiesInputStream(String path) {
public boolean isShowVersion() {
return showVersion;
}


/**
* Determines the hash function used with the Update Center
* set via environment variable only
*
* @return the string value for the hash function. Currently allows sha1, sha256(default), sha512
*/
private HashFunction getHashFunction() {

String fromEnv = System.getenv("JENKINS_UC_HASH_FUNCTION");
if (StringUtils.isNotBlank(fromEnv)) {
return HashFunction.valueOf(fromEnv.toUpperCase());
} else {
return Settings.DEFAULT_HASH_FUNCTION;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public void setupDefaultsTest() throws Exception {
.and("JENKINS_UC_EXPERIMENTAL", "")
.and("JENKINS_INCREMENTALS_REPO_MIRROR", "")
.and("JENKINS_PLUGIN_INFO", "")
.and("JENKINS_UC_HASH_FUNCTION", "")
.execute(options::setup);

assertThat(cfg.getPluginDir()).hasToString(Settings.DEFAULT_PLUGIN_DIR_LOCATION);
Expand All @@ -83,6 +84,7 @@ public void setupDefaultsTest() throws Exception {
assertThat(cfg.getJenkinsUcExperimental()).hasToString(Settings.DEFAULT_EXPERIMENTAL_UPDATE_CENTER_LOCATION);
assertThat(cfg.getJenkinsIncrementalsRepoMirror()).hasToString(Settings.DEFAULT_INCREMENTALS_REPO_MIRROR_LOCATION);
assertThat(cfg.getJenkinsPluginInfo()).hasToString(Settings.DEFAULT_PLUGIN_INFO_LOCATION);
assertThat(cfg.getHashFunction()).isEqualTo(Settings.DEFAULT_HASH_FUNCTION);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class Config {
private String jenkinsWar;
private List<Plugin> plugins;
private boolean verbose;
private HashFunction hashFunction;
private URL jenkinsUc;
private URL jenkinsUcExperimental;
private URL jenkinsIncrementalsRepoMirror;
Expand Down Expand Up @@ -70,6 +71,7 @@ private Config(
boolean useLatestAll,
boolean skipFailedPlugins,
OutputFormat outputFormat,
HashFunction hashFunction,
List<Credentials> credentials) {
this.pluginDir = pluginDir;
this.cleanPluginDir = cleanPluginDir;
Expand All @@ -91,6 +93,7 @@ private Config(
this.skipFailedPlugins = skipFailedPlugins;
this.outputFormat = outputFormat;
this.credentials = credentials;
this.hashFunction = hashFunction;
}

public File getPluginDir() {
Expand Down Expand Up @@ -178,6 +181,10 @@ public static Builder builder() {
return new Builder();
}

public HashFunction getHashFunction() {
return hashFunction;
}

public static class Builder {
private File pluginDir;
private boolean cleanPluginDir;
Expand All @@ -199,6 +206,7 @@ public static class Builder {
private boolean skipFailedPlugins;
private OutputFormat outputFormat = OutputFormat.STDOUT;
private List<Credentials> credentials = Collections.emptyList();
private HashFunction hashFunction = Settings.DEFAULT_HASH_FUNCTION;

private Builder() {
}
Expand Down Expand Up @@ -314,6 +322,11 @@ public Builder withCredentials(List<Credentials> credentials) {
return this;
}

public Builder withHashFunction(HashFunction hashFunction) {
this.hashFunction = hashFunction;
return this;
}

public Config build() {
return new Config(
pluginDir,
Expand All @@ -335,6 +348,7 @@ public Config build() {
useLatestAll,
skipFailedPlugins,
outputFormat,
hashFunction,
credentials
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.jenkins.tools.pluginmanager.config;

public enum HashFunction {
SHA1("sha1"),
SHA256("sha256"),
SHA512("sha512");

private String hashFunctionName;

HashFunction(String hashFunctionName) {
this.hashFunctionName = hashFunctionName;
}

@Override
public String toString(){
return hashFunctionName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class Settings {
public static final URL DEFAULT_PLUGIN_INFO;
public static final String DEFAULT_PLUGIN_INFO_LOCATION = "https://updates.jenkins.io/plugin-versions.json";
public static final Path DEFAULT_CACHE_PATH;
public static final HashFunction DEFAULT_HASH_FUNCTION = HashFunction.SHA256;

static {
String cacheBaseDir = System.getProperty("user.home");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class Plugin {
private boolean latest;
private boolean experimental;
private boolean optional;
private String sha256Checksum;
private String checksum;
private VersionNumber jenkinsVersion;

public Plugin(String name, String version, String url, String groupId) {
Expand Down Expand Up @@ -72,12 +72,12 @@ public void setLatest(boolean latest) {
this.latest = latest;
}

public String getSha256Checksum() {
return sha256Checksum;
public String getChecksum() {
return checksum;
}

public void setSha256Checksum(String sha256Checksum) {
this.sha256Checksum = sha256Checksum;
public void setChecksum(String checksum) {
this.checksum = checksum;
}

public String getName() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import hudson.util.VersionNumber;
import io.jenkins.tools.pluginmanager.config.Config;
import io.jenkins.tools.pluginmanager.config.Credentials;
import io.jenkins.tools.pluginmanager.config.HashFunction;
import io.jenkins.tools.pluginmanager.config.Settings;
import io.jenkins.tools.pluginmanager.util.FileDownloadResponseHandler;
import io.jenkins.tools.pluginmanager.util.ManifestTools;
Expand Down Expand Up @@ -82,6 +83,7 @@ public class PluginManager implements Closeable {
*/
private File pluginDir;
private String jenkinsUcLatest;
private HashFunction hashFunction;
private @CheckForNull VersionNumber jenkinsVersion;
private @CheckForNull File jenkinsWarFile;
private Map<String, Plugin> installedPluginVersions;
Expand Down Expand Up @@ -123,6 +125,7 @@ public PluginManager(Config cfg) {
useLatestSpecified = cfg.isUseLatestSpecified();
useLatestAll = cfg.isUseLatestAll();
skipFailedPlugins = cfg.isSkipFailedPlugins();
hashFunction = cfg.getHashFunction();
httpClient = null;
}

Expand Down Expand Up @@ -580,7 +583,7 @@ public void downloadPlugins(List<Plugin> plugins) {
if (skipFailedPlugins) {
System.out.println("SKIP: Unable to move " + plugin.getName() + " to the plugin directory");
} else {
throw new DownloadPluginException("Unable to move " + plugin.getName() + " to the plugin directory", ex);
throw new DownloadPluginException("Unable to move " + plugin.getName() + " to the plugin directory", ex);
}
}
}
Expand Down Expand Up @@ -677,18 +680,18 @@ private void calculateChecksum(Plugin requestedPlugin) {
String versionInUpdateCenter = pluginFromUpdateCenter.getString("version");
if (versionInUpdateCenter.equals(requestedPlugin.getVersion().toString())) {

String sha256 = pluginFromUpdateCenter.getString("sha256");
String checksum = pluginFromUpdateCenter.getString(getHashFunction().toString());
if (verbose) {
System.out.println("Setting checksum for: " + requestedPlugin.getName() + " to " + sha256);
System.out.println("Setting checksum for: " + requestedPlugin.getName() + " to " + checksum);
}
requestedPlugin.setSha256Checksum(sha256);
requestedPlugin.setChecksum(checksum);
} else {
if (verbose && requestedPlugin.getSha256Checksum() == null) {
if (verbose && requestedPlugin.getChecksum() == null) {
System.out.println("Couldn't find checksum for " + requestedPlugin.getName() + " at version: " + requestedPlugin.getVersion().toString());
}
}
} else {
if (verbose && requestedPlugin.getSha256Checksum() == null) {
if (verbose && requestedPlugin.getChecksum() == null) {
System.out.println("Couldn't find checksum for: " + requestedPlugin.getName());
}
}
Expand Down Expand Up @@ -718,7 +721,6 @@ public void outputPluginReplacementInfo(Plugin lowerVersion, Plugin higherVersio
*
* @param urlString string representing the url from which to get the json object
* @deprecated see {@link #getJson(URL, String)}
* @return json object
*/
@Deprecated
public JSONObject getJson(String urlString) {
Expand Down Expand Up @@ -811,11 +813,11 @@ public JSONArray getPluginDependencyJsonArray(Plugin plugin, JSONObject ucJson)
//plugin-versions.json has a slightly different structure than other update center json
if (pluginInfo.has(plugin.getVersion().toString())) {
JSONObject specificVersionInfo = pluginInfo.getJSONObject(plugin.getVersion().toString());
String checksum = specificVersionInfo.getString("sha256");
String checksum = specificVersionInfo.getString(getHashFunction().toString());
if (verbose) {
System.out.println("Setting checksum for: " + plugin.getName() + " to " + checksum);
}
plugin.setSha256Checksum(checksum);
plugin.setChecksum(checksum);
plugin.setJenkinsVersion(specificVersionInfo.getString("requiredCore"));
return (JSONArray) specificVersionInfo.get("dependencies");
}
Expand Down Expand Up @@ -1264,7 +1266,7 @@ public boolean downloadToFile(String urlString, Plugin plugin, @CheckForNull Fil
}

// both the download and zip validation passed
if(success) {
if (success) {
logVerbose("Downloaded plugin " + plugin.getName());
break;
}
Expand Down Expand Up @@ -1307,7 +1309,7 @@ private CredentialsProvider getCredentialsProvider() {
}

void verifyChecksum(Plugin plugin, File pluginFile) {
String expectedChecksum = plugin.getSha256Checksum();
String expectedChecksum = plugin.getChecksum();
if (expectedChecksum == null) {
if (verbose) {
System.out.println("No checksum found for " + plugin.getName() + " (probably custom built plugin)");
Expand All @@ -1334,9 +1336,20 @@ void verifyChecksum(Plugin plugin, File pluginFile) {
}
}

@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_SHA1", justification = "CloudBees update center only uses sha1, remove sha1 once this has been updated.")
private byte[] calculateChecksum(File pluginFile) {
try (FileInputStream fin = new FileInputStream(pluginFile)) {
return DigestUtils.sha256(fin);
HashFunction hashFunction = getHashFunction();
switch (hashFunction) {
case SHA1:
return DigestUtils.sha1(fin);
case SHA512:
return DigestUtils.sha512(fin);
case SHA256:
return DigestUtils.sha256(fin);
default:
throw new UnsupportedChecksumException(hashFunction.toString() + "is an unsupported hash function.");
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
Expand Down Expand Up @@ -1482,6 +1495,20 @@ public Map<String, Plugin> bundledPlugins() {
return bundledPlugins;
}


/**
* Gets the hash function used for the update center
*
* @return Jenkins update center hash function string
*/
public HashFunction getHashFunction() {
return hashFunction;
}

public void setHashFunction(HashFunction hashFunction) {
this.hashFunction = hashFunction;
}

/**
* Gets the update center url string
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.jenkins.tools.pluginmanager.impl;

public class UnsupportedChecksumException extends RuntimeException {

public UnsupportedChecksumException(String message) {
super(message);
}

public UnsupportedChecksumException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.jenkins.tools.pluginmanager.impl;

import io.jenkins.tools.pluginmanager.config.Config;
import io.jenkins.tools.pluginmanager.config.HashFunction;
import java.io.File;
import java.net.URL;
import org.junit.Before;
Expand All @@ -19,6 +20,7 @@ public void setUp() {
Config cfg = Config.builder()
.withJenkinsWar("")
.withIsVerbose(true)
.withHashFunction(HashFunction.SHA256)
.build();

pm = new PluginManager(cfg);
Expand All @@ -27,7 +29,7 @@ public void setUp() {
@Test
public void mailerPluginChecksumsMatch() {
Plugin mailer = new Plugin("mailer", "1.32", null, null);
mailer.setSha256Checksum("BChiuBjHIiPxWZrBuVqB+QwxKWFknoim5jnCr4I55Lc=");
mailer.setChecksum("BChiuBjHIiPxWZrBuVqB+QwxKWFknoim5jnCr4I55Lc=");

URL mailerHpi = this.getClass().getResource("mailer.hpi");
File mailerFile = new File(mailerHpi.getFile());
Expand All @@ -38,7 +40,7 @@ public void mailerPluginChecksumsMatch() {
@Test
public void mailerPluginInvalidChecksums() {
Plugin mailer = new Plugin("mailer", "1.32", null, null);
mailer.setSha256Checksum("jBChiuBjHIiPxWZrBuVqB+QwxKWFknoim5jnCr4I55Lc=");
mailer.setChecksum("jBChiuBjHIiPxWZrBuVqB+QwxKWFknoim5jnCr4I55Lc=");

URL mailerHpi = this.getClass().getResource("mailer.hpi");
File mailerFile = new File(mailerHpi.getFile());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ public void getPluginDependencyJsonArrayTest2() {
pm.setPluginInfoJson(pluginVersionJson);

Plugin browserStackPlugin1 = new Plugin("browserstack-integration", "1.0.0", null, null);
browserStackPlugin1.setSha256Checksum("1234");
browserStackPlugin1.setChecksum("1234");
JSONArray browserStackPluginJson1 = pm.getPluginDependencyJsonArray(browserStackPlugin1, pluginVersionJson);
assertThat(browserStackPluginJson1)
.hasToString(dependencies1.toString());
Expand Down

0 comments on commit e71682a

Please sign in to comment.