未验证 提交 0ddfd46d 编写于 作者: J James Nord 提交者: GitHub

[JENKINS-70240] Support non-HTTP URLs in `PluginManager#doCheckUpdateSiteUrl` (#7524)

Co-authored-by: NJulie Heard <55280278+julieheard@users.noreply.github.com>
Co-authored-by: NBasil Crow <me@basilcrow.com>
上级 fd5237cb
......@@ -1960,44 +1960,71 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
@Restricted(NoExternalUse.class)
@RequirePOST public FormValidation doCheckUpdateSiteUrl(StaplerRequest request, @QueryParameter String value) throws InterruptedException {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
return checkUpdateSiteURL(value);
}
@Restricted(DoNotUse.class) // visible for testing only
FormValidation checkUpdateSiteURL(@CheckForNull String value) throws InterruptedException {
value = Util.fixEmptyAndTrim(value);
if (value == null) {
return FormValidation.error(Messages.PluginManager_emptyUpdateSiteUrl());
}
value += ((value.contains("?")) ? "&" : "?") + "version=" + Jenkins.VERSION + "&uctest";
URI uri;
try {
uri = new URI(value);
} catch (URISyntaxException e) {
return FormValidation.error(e, Messages.PluginManager_invalidUrl());
}
HttpClient httpClient = ProxyConfiguration.newHttpClientBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
HttpRequest httpRequest;
final URI baseUri;
try {
httpRequest = ProxyConfiguration.newHttpRequestBuilder(uri)
.method("HEAD", HttpRequest.BodyPublishers.noBody())
.build();
} catch (IllegalArgumentException e) {
return FormValidation.error(e, Messages.PluginManager_invalidUrl());
baseUri = new URI(value);
} catch (URISyntaxException ex) {
return FormValidation.error(ex, Messages.PluginManager_invalidUrl());
}
try {
java.net.http.HttpResponse<Void> httpResponse = httpClient.send(
httpRequest, java.net.http.HttpResponse.BodyHandlers.discarding());
if (100 <= httpResponse.statusCode() && httpResponse.statusCode() <= 399) {
if ("file".equalsIgnoreCase(baseUri.getScheme())) {
File f = new File(baseUri);
if (f.isFile()) {
return FormValidation.ok();
}
LOGGER.log(Level.FINE, "Obtained a non OK ({0}) response from the update center",
new Object[] {httpResponse.statusCode(), uri});
return FormValidation.error(Messages.PluginManager_connectionFailed());
} catch (IOException e) {
LOGGER.log(Level.FINE, "Failed to check update site", e);
return FormValidation.error(e, Messages.PluginManager_connectionFailed());
}
if ("https".equalsIgnoreCase(baseUri.getScheme()) || "http".equalsIgnoreCase(baseUri.getScheme())) {
final URI uriWithQuery;
try {
if (baseUri.getRawQuery() == null) {
uriWithQuery = new URI(value + "?version=" + Jenkins.VERSION + "&uctest");
} else {
uriWithQuery = new URI(value + "&version=" + Jenkins.VERSION + "&uctest");
}
} catch (URISyntaxException e) {
return FormValidation.error(e, Messages.PluginManager_invalidUrl());
}
HttpClient httpClient = ProxyConfiguration.newHttpClientBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
HttpRequest httpRequest;
try {
httpRequest = ProxyConfiguration.newHttpRequestBuilder(uriWithQuery)
.method("HEAD", HttpRequest.BodyPublishers.noBody())
.build();
} catch (IllegalArgumentException e) {
return FormValidation.error(e, Messages.PluginManager_invalidUrl());
}
try {
java.net.http.HttpResponse<Void> httpResponse = httpClient.send(
httpRequest, java.net.http.HttpResponse.BodyHandlers.discarding());
if (100 <= httpResponse.statusCode() && httpResponse.statusCode() <= 399) {
return FormValidation.ok();
}
LOGGER.log(Level.FINE, "Obtained a non OK ({0}) response from the update center",
new Object[] {httpResponse.statusCode(), baseUri});
return FormValidation.error(Messages.PluginManager_connectionFailed());
} catch (IOException e) {
LOGGER.log(Level.FINE, "Failed to check update site", e);
return FormValidation.error(e, Messages.PluginManager_connectionFailed());
}
}
// not a file or http(s) scheme
return FormValidation.error(Messages.PluginManager_invalidUrl());
}
@Restricted(NoExternalUse.class)
......
......@@ -100,7 +100,7 @@ PluginManager.deprecationWarning=<strong>This plugin is deprecated.</strong> In
PluginManager.insecureUrl=\
You are using an insecure URL to download the plugin, use at your own risk!
PluginManager.invalidUrl=\
You are using an invalid URL to download the plugin, only https and http (not recommended) are supported.
You are using an invalid URL to download the plugin. file, https and http (not recommended) URLs are supported.
PluginManager.emptyUpdateSiteUrl=\
The update site cannot be empty. Please enter a valid url.
......
......@@ -71,7 +71,7 @@ FilePath.validateAntFileMask.doesntMatchAnythingAndSuggest="{0}" não retornou n
PluginWrapper.NoSuchPlugin=Nenhuma extensão encontrada com o nome ''{0}''
PluginWrapper.failed_to_load_plugin_2=Falhou para carregar: {0} ({1} {2})
PluginManager.invalidUrl= \
Você está usando uma URL inválida para baixar a extensão, somente https e http (não recomendado) são suportados.
Você está usando uma URL inválida para baixar a extensão, somente file, https e http (não recomendado) são suportados.
PluginWrapper.Already.Disabled=A extensão ''{0}'' já foi desabilitada
PluginWrapper.disabled_2=A extensão requerida está desabilitada: {0} {1}
PluginManager.parentDepCompatWarning=As seguintes extensões são incompatíveis:
......
......@@ -74,7 +74,7 @@ PluginManager.newerVersionEntry=已有此版本的外掛但不作為更新提供
PluginManager.unavailable=無法使用
PluginManager.deprecationWarning=<strong>此外掛已棄用。</strong>這通常表示它可能已過時、不再繼續開發、不再正常運作等。<a href\="{0}" rel\="noopener noreferrer" target\="_blank">了解更多。</a>
PluginManager.insecureUrl=您正在使用不安全的 URL 下載該外掛,風險請自負!
PluginManager.invalidUrl=您正在使用無效的 URL 下載該外掛,只支援 https 和 http (不建議)
PluginManager.invalidUrl=您正在使用無效的 URL 下載該外掛,只支援 https、http (不建議) 和 file
AboutJenkins.DisplayName=關於 Jenkins
......
......@@ -27,11 +27,15 @@ package hudson;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import hudson.util.FormValidation;
import hudson.util.FormValidation.Kind;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
......@@ -44,6 +48,8 @@ import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.jvnet.hudson.test.Issue;
......@@ -114,6 +120,40 @@ public class PluginManagerTest {
equalTo(jar.lastModified()));
}
@Test
@Issue("JENKINS-70420")
public void updateSiteURLCheckValidation() throws Exception {
LocalPluginManager pm = new LocalPluginManager(tmp.toFile());
assertThat("ftp urls are not acceptable", pm.checkUpdateSiteURL("ftp://foo/bar"),
allOf(FormValidationMatcher.validationWithMessage(Kind.ERROR), hasProperty("message", containsString("invalid URL"))));
assertThat("file urls to non files are not acceptable", pm.checkUpdateSiteURL(tmp.toUri().toURL().toString()),
allOf(FormValidationMatcher.validationWithMessage(Kind.ERROR), hasProperty("message", containsString("Unable to connect to the URL"))));
assertThat("invalid URLs do not cause a stack tracek", pm.checkUpdateSiteURL("sufslef3,r3;r99 3 l4i34"),
allOf(FormValidationMatcher.validationWithMessage(Kind.ERROR), hasProperty("message", containsString("invalid URL"))));
assertThat("empty url message", pm.checkUpdateSiteURL(""),
allOf(FormValidationMatcher.validationWithMessage(Kind.ERROR), hasProperty("message", containsString("cannot be empty"))));
assertThat("null url message", pm.checkUpdateSiteURL(""),
allOf(FormValidationMatcher.validationWithMessage(Kind.ERROR), hasProperty("message", containsString("cannot be empty"))));
// create a tempoary local file
Path p = tmp.resolve("some.json");
Files.writeString(tmp.resolve("some.json"), "{}");
assertThat("file urls pointing to existing files work", pm.checkUpdateSiteURL(p.toUri().toURL().toString()),
FormValidationMatcher.validationWithMessage(Kind.OK));
assertThat("http urls with non existing servers", pm.checkUpdateSiteURL("https://bogus.example.com"),
allOf(FormValidationMatcher.validationWithMessage(Kind.ERROR), hasProperty("message", containsString("Unable to connect to the URL"))));
// starting a http server here is likely to be overkill and given this is the predominant use case is not so likely to regress.
assertThat("main UC validates correctly", pm.checkUpdateSiteURL("https://updates.jenkins.io/update-center.json"),
FormValidationMatcher.validationWithMessage(Kind.OK));
}
private static void assertAttribute(Manifest manifest, String attributeName, String value) {
Attributes attributes = manifest.getMainAttributes();
assertThat("Main attributes must not be empty", attributes, notNullValue());
......@@ -168,4 +208,29 @@ public class PluginManagerTest {
final String manifestPath = "META-INF/MANIFEST.MF";
return new URL("jar:" + jarFile.toURI().toURL() + "!/" + manifestPath);
}
private static class FormValidationMatcher extends TypeSafeDiagnosingMatcher<FormValidation> {
private final Kind kind;
private FormValidationMatcher(Kind kind) {
this.kind = kind;
}
@Override
public void describeTo(Description description) {
description.appendText("FormValidation of type ").appendValue(kind);
}
@Override
protected boolean matchesSafely(FormValidation item, Description mismatchDescription) {
mismatchDescription.appendText("FormValidation of type ").appendValue(item.kind);
return item.kind == kind;
}
static FormValidationMatcher validationWithMessage(Kind kind) {
return new FormValidationMatcher(kind);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册