Adjust SHA-512 supported format on plugin install
This commit adjusts the format of the SHA-512 checksum files supported by the plugin installer. In particular, we now require that the SHA-512 format be a single-line file containing the checksum followed by two spaces followed by the filename. We continue to support the legacy format for SHA-1. Relates #27093
This commit is contained in:
parent
5818ff6b56
commit
cfa4646161
|
@ -43,6 +43,7 @@ import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
|
@ -385,11 +386,41 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
||||||
}
|
}
|
||||||
final String expectedChecksum;
|
final String expectedChecksum;
|
||||||
try (InputStream in = checksumUrl.openStream()) {
|
try (InputStream in = checksumUrl.openStream()) {
|
||||||
BufferedReader checksumReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
|
/*
|
||||||
|
* The supported format of the SHA-1 files is a single-line file containing the SHA-1. The supported format of the SHA-512 files
|
||||||
|
* is a single-line file containing the SHA-512 and the filename, separated by two spaces. For SHA-1, we verify that the hash
|
||||||
|
* matches, and that the file contains a single line. For SHA-512, we verify that the hash and the filename match, and that the
|
||||||
|
* file contains a single line.
|
||||||
|
*/
|
||||||
|
if (digestAlgo.equals("SHA-1")) {
|
||||||
|
final BufferedReader checksumReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||||
expectedChecksum = checksumReader.readLine();
|
expectedChecksum = checksumReader.readLine();
|
||||||
if (checksumReader.readLine() != null) {
|
if (checksumReader.readLine() != null) {
|
||||||
throw new UserException(ExitCodes.IO_ERROR, "Invalid checksum file at " + checksumUrl);
|
throw new UserException(ExitCodes.IO_ERROR, "Invalid checksum file at " + checksumUrl);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
final BufferedReader checksumReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||||
|
final String checksumLine = checksumReader.readLine();
|
||||||
|
final String[] fields = checksumLine.split(" {2}");
|
||||||
|
if (fields.length != 2) {
|
||||||
|
throw new UserException(ExitCodes.IO_ERROR, "Invalid checksum file at " + checksumUrl);
|
||||||
|
}
|
||||||
|
expectedChecksum = fields[0];
|
||||||
|
final String[] segments = URI.create(urlString).getPath().split("/");
|
||||||
|
final String expectedFile = segments[segments.length - 1];
|
||||||
|
if (fields[1].equals(expectedFile) == false) {
|
||||||
|
final String message = String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"checksum file at [%s] is not for this plugin, expected [%s] but was [%s]",
|
||||||
|
checksumUrl,
|
||||||
|
expectedFile,
|
||||||
|
fields[1]);
|
||||||
|
throw new UserException(ExitCodes.IO_ERROR, message);
|
||||||
|
}
|
||||||
|
if (checksumReader.readLine() != null) {
|
||||||
|
throw new UserException(ExitCodes.IO_ERROR, "Invalid checksum file at " + checksumUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] zipbytes = Files.readAllBytes(zip);
|
byte[] zipbytes = Files.readAllBytes(zip);
|
||||||
|
|
|
@ -47,6 +47,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.DirectoryStream;
|
import java.nio.file.DirectoryStream;
|
||||||
|
@ -77,6 +78,7 @@ import java.util.stream.Stream;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import static org.elasticsearch.test.hamcrest.RegexMatcher.matches;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
@ -799,8 +801,7 @@ public class InstallPluginCommandTests extends ESTestCase {
|
||||||
|
|
||||||
public void assertInstallPluginFromUrl(String pluginId, String name, String url, String stagingHash) throws Exception {
|
public void assertInstallPluginFromUrl(String pluginId, String name, String url, String stagingHash) throws Exception {
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
||||||
assertInstallPluginFromUrl(pluginId, name, url, stagingHash, ".sha512",
|
assertInstallPluginFromUrl(pluginId, name, url, stagingHash, ".sha512", checksumAndFilename(digest, url));
|
||||||
bytes -> MessageDigests.toHexString(digest.digest(bytes)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOfficalPlugin() throws Exception {
|
public void testOfficalPlugin() throws Exception {
|
||||||
|
@ -839,8 +840,7 @@ public class InstallPluginCommandTests extends ESTestCase {
|
||||||
public void testMavenSha1Backcompat() throws Exception {
|
public void testMavenSha1Backcompat() throws Exception {
|
||||||
String url = "https://repo1.maven.org/maven2/mygroup/myplugin/1.0.0/myplugin-1.0.0.zip";
|
String url = "https://repo1.maven.org/maven2/mygroup/myplugin/1.0.0/myplugin-1.0.0.zip";
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||||
MockTerminal terminal = assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null,
|
MockTerminal terminal = assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null, ".sha1", checksum(digest));
|
||||||
".sha1", bytes -> MessageDigests.toHexString(digest.digest(bytes)));
|
|
||||||
assertTrue(terminal.getOutput(), terminal.getOutput().contains("sha512 not found, falling back to sha1"));
|
assertTrue(terminal.getOutput(), terminal.getOutput().contains("sha512 not found, falling back to sha1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,8 +848,7 @@ public class InstallPluginCommandTests extends ESTestCase {
|
||||||
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||||
UserException e = expectThrows(UserException.class, () ->
|
UserException e = expectThrows(UserException.class, () ->
|
||||||
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, ".sha1",
|
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, ".sha1", checksum(digest)));
|
||||||
bytes -> MessageDigests.toHexString(digest.digest(bytes))));
|
|
||||||
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
||||||
assertEquals("Plugin checksum missing: " + url + ".sha512", e.getMessage());
|
assertEquals("Plugin checksum missing: " + url + ".sha512", e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -862,12 +861,41 @@ public class InstallPluginCommandTests extends ESTestCase {
|
||||||
assertEquals("Plugin checksum missing: " + url + ".sha1", e.getMessage());
|
assertEquals("Plugin checksum missing: " + url + ".sha1", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testInvalidShaFile() throws Exception {
|
public void testInvalidShaFileMissingFilename() throws Exception {
|
||||||
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
||||||
UserException e = expectThrows(UserException.class, () ->
|
UserException e = expectThrows(UserException.class, () ->
|
||||||
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, ".sha512",
|
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, ".sha512", checksum(digest)));
|
||||||
bytes -> MessageDigests.toHexString(digest.digest(bytes)) + "\nfoobar"));
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
||||||
|
assertTrue(e.getMessage(), e.getMessage().startsWith("Invalid checksum file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInvalidShaFileMismatchFilename() throws Exception {
|
||||||
|
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
||||||
|
UserException e = expectThrows(UserException.class, () ->
|
||||||
|
assertInstallPluginFromUrl(
|
||||||
|
"analysis-icu",
|
||||||
|
"analysis-icu",
|
||||||
|
url,
|
||||||
|
null,
|
||||||
|
".sha512",
|
||||||
|
checksumAndString(digest, " repository-s3-" + Version.CURRENT + ".zip")));
|
||||||
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
||||||
|
assertThat(e, hasToString(matches("checksum file at \\[.*\\] is not for this plugin")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInvalidShaFileContainingExtraLine() throws Exception {
|
||||||
|
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
||||||
|
UserException e = expectThrows(UserException.class, () ->
|
||||||
|
assertInstallPluginFromUrl(
|
||||||
|
"analysis-icu",
|
||||||
|
"analysis-icu",
|
||||||
|
url,
|
||||||
|
null,
|
||||||
|
".sha512",
|
||||||
|
checksumAndString(digest, " analysis-icu-" + Version.CURRENT + ".zip\nfoobar")));
|
||||||
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
||||||
assertTrue(e.getMessage(), e.getMessage().startsWith("Invalid checksum file"));
|
assertTrue(e.getMessage(), e.getMessage().startsWith("Invalid checksum file"));
|
||||||
}
|
}
|
||||||
|
@ -875,8 +903,13 @@ public class InstallPluginCommandTests extends ESTestCase {
|
||||||
public void testSha512Mismatch() throws Exception {
|
public void testSha512Mismatch() throws Exception {
|
||||||
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
||||||
UserException e = expectThrows(UserException.class, () ->
|
UserException e = expectThrows(UserException.class, () ->
|
||||||
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, ".sha512",
|
assertInstallPluginFromUrl(
|
||||||
bytes -> "foobar"));
|
"analysis-icu",
|
||||||
|
"analysis-icu",
|
||||||
|
url,
|
||||||
|
null,
|
||||||
|
".sha512",
|
||||||
|
bytes -> "foobar analysis-icu-" + Version.CURRENT + ".zip"));
|
||||||
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
||||||
assertTrue(e.getMessage(), e.getMessage().contains("SHA-512 mismatch, expected foobar"));
|
assertTrue(e.getMessage(), e.getMessage().contains("SHA-512 mismatch, expected foobar"));
|
||||||
}
|
}
|
||||||
|
@ -884,8 +917,7 @@ public class InstallPluginCommandTests extends ESTestCase {
|
||||||
public void testSha1Mismatch() throws Exception {
|
public void testSha1Mismatch() throws Exception {
|
||||||
String url = "https://repo1.maven.org/maven2/mygroup/myplugin/1.0.0/myplugin-1.0.0.zip";
|
String url = "https://repo1.maven.org/maven2/mygroup/myplugin/1.0.0/myplugin-1.0.0.zip";
|
||||||
UserException e = expectThrows(UserException.class, () ->
|
UserException e = expectThrows(UserException.class, () ->
|
||||||
assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null,
|
assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null, ".sha1", bytes -> "foobar"));
|
||||||
".sha1", bytes -> "foobar"));
|
|
||||||
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
||||||
assertTrue(e.getMessage(), e.getMessage().contains("SHA-1 mismatch, expected foobar"));
|
assertTrue(e.getMessage(), e.getMessage().contains("SHA-1 mismatch, expected foobar"));
|
||||||
}
|
}
|
||||||
|
@ -917,4 +949,18 @@ public class InstallPluginCommandTests extends ESTestCase {
|
||||||
MockTerminal terminal = installPlugin(pluginZip, env.v1());
|
MockTerminal terminal = installPlugin(pluginZip, env.v1());
|
||||||
assertTrue(Files.exists(KeyStoreWrapper.keystorePath(env.v2().configFile())));
|
assertTrue(Files.exists(KeyStoreWrapper.keystorePath(env.v2().configFile())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Function<byte[], String> checksum(final MessageDigest digest) {
|
||||||
|
return checksumAndString(digest, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<byte[], String> checksumAndFilename(final MessageDigest digest, final String url) throws MalformedURLException {
|
||||||
|
final String[] segments = URI.create(url).getPath().split("/");
|
||||||
|
return checksumAndString(digest, " " + segments[segments.length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<byte[], String> checksumAndString(final MessageDigest digest, final String s) {
|
||||||
|
return bytes -> MessageDigests.toHexString(digest.digest(bytes)) + s;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue