diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 75173359aa..9fb5800383 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -1469,7 +1469,11 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas jsonObject.put("sourceId", plugin.sourceId); jsonObject.put("title", plugin.title); jsonObject.put("displayName", plugin.getDisplayName()); - jsonObject.put("wiki", plugin.wiki); + if (plugin.wiki == null || !(plugin.wiki.startsWith("https://") || plugin.wiki.startsWith("http://"))) { + jsonObject.put("wiki", StringUtils.EMPTY); + } else { + jsonObject.put("wiki", plugin.wiki); + } jsonObject.put("categories", plugin.getCategoriesStream() .filter(PluginManager::isNonMetaLabel) .map(UpdateCenter::getCategoryDisplayName) @@ -1486,7 +1490,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas jsonObject.put("version", plugin.version); jsonObject.put("popularity", plugin.popularity); if (plugin.isForNewerHudson()) { - jsonObject.put("newerCoreRequired", Messages.PluginManager_coreWarning(plugin.requiredCore)); + jsonObject.put("newerCoreRequired", Messages.PluginManager_coreWarning(Util.xmlEscape(plugin.requiredCore))); } if (plugin.hasWarnings()) { JSONObject unresolvedSecurityWarnings = new JSONObject(); diff --git a/test/src/test/java/hudson/PluginManagerSecurity3037Test.java b/test/src/test/java/hudson/PluginManagerSecurity3037Test.java new file mode 100644 index 0000000000..cdc420d86e --- /dev/null +++ b/test/src/test/java/hudson/PluginManagerSecurity3037Test.java @@ -0,0 +1,101 @@ +package hudson; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.gargoylesoftware.htmlunit.AlertHandler; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlElementUtil; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import hudson.model.DownloadService; +import hudson.model.RootAction; +import hudson.model.UpdateSite; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import javax.servlet.ServletException; +import jenkins.model.Jenkins; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.FlagRule; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; + +public class PluginManagerSecurity3037Test { + @Rule + public JenkinsRule r = new JenkinsRule(); + + @Rule + public FlagRule signatureCheck = new FlagRule<>(() -> DownloadService.signatureCheck, x -> DownloadService.signatureCheck = x); + + @Test + public void noInjectionOnAvailablePluginsPage() throws Exception { + DownloadService.signatureCheck = false; + Jenkins.get().getUpdateCenter().getSites().clear(); + UpdateSite us = new UpdateSite("Security3037", Jenkins.get().getRootUrl() + "security3037UpdateCenter/update-center.json"); + Jenkins.get().getUpdateCenter().getSites().add(us); + + try (JenkinsRule.WebClient wc = r.createWebClient()) { + HtmlPage p = wc.goTo("pluginManager"); + List elements = p.getElementById("bottom-sticker") + .getElementsByTagName("a") + .stream() + .filter(link -> link.getAttribute("href").equals("checkUpdatesServer")) + .collect(Collectors.toList()); + assertEquals(1, elements.size()); + AlertHandlerImpl alertHandler = new AlertHandlerImpl(); + wc.setAlertHandler(alertHandler); + + HtmlElementUtil.click(elements.get(0)); + HtmlPage available = wc.goTo("pluginManager/available"); + assertTrue(available.querySelector(".alert-danger") + .getTextContent().contains("This plugin is built for Jenkins 2.999")); + wc.waitForBackgroundJavaScript(100); + + HtmlAnchor anchor = available.querySelector(".jenkins-table__link"); + anchor.click(true, false, false); + wc.waitForBackgroundJavaScript(100); + assertTrue(alertHandler.messages.isEmpty()); + } + } + + static class AlertHandlerImpl implements AlertHandler { + List messages = Collections.synchronizedList(new ArrayList<>()); + + @Override + public void handleAlert(final Page page, final String message) { + messages.add(message); + } + } + + @TestExtension("noInjectionOnAvailablePluginsPage") + public static final class Security3037UpdateCenter implements RootAction { + + @Override + public String getIconFileName() { + return "gear2.png"; + } + + @Override + public String getDisplayName() { + return "security-3037-update-center"; + } + + @Override + public String getUrlName() { + return "security3037UpdateCenter"; + } + + public void doDynamic(StaplerRequest staplerRequest, StaplerResponse staplerResponse) throws ServletException, IOException { + staplerResponse.setContentType("application/json"); + staplerResponse.setStatus(200); + staplerResponse.serveFile(staplerRequest, PluginManagerSecurity3037Test.class.getResource("PluginManagerSecurity3037Test/update-center.json")); + } + } +} diff --git a/test/src/test/resources/hudson/PluginManagerSecurity3037Test/update-center.json b/test/src/test/resources/hudson/PluginManagerSecurity3037Test/update-center.json new file mode 100644 index 0000000000..c956749c05 --- /dev/null +++ b/test/src/test/resources/hudson/PluginManagerSecurity3037Test/update-center.json @@ -0,0 +1,65 @@ +updateCenter.post( +{ + "connectionCheckUrl": "https://www.google.com/", + "core": { + "buildDate": "Jan 31, 2023", + "name": "core", + "sha1": "TO8l3+wqx2hWtLWMHjmIOj+ERsk=", + "sha256": "BrXTCwJse5e6b/shXjv5edRJQRjxfJ/GbNrhgYcrZoU=", + "size": 98362341, + "url": "https://updates.jenkins.io/download/war/2.389/jenkins.war", + "version": "2.389" + }, + "generationTimestamp": "2023-02-03T11:04:30Z", + "id": "Security3037", + "deprecations": [], + "plugins": { + "workflow-aggregator": { + "buildDate": "Jun 29, 2022", + "defaultBranch": "master", + "dependencies": [ + { + "name": "pipeline-groovy-lib", + "optional": false, + "version": "591.v3a_7f422b_d058" + } + ], + "developers": [ + { + "developerId": "johndoe", + "name": "john doe" + } + ], + "excerpt": "A suite of plugins that lets you orchestrate automation, simple or complex. See Pipeline as Code with Jenkins for more details.", + "gav": "org.jenkins-ci.plugins.workflow:workflow-aggregator:590.v6a_d052e5a_a_b_5", + "issueTrackers": [ + { + "reportUrl": "https://www.jenkins.io/participate/report-issue/redirect/#21710", + "type": "jira", + "viewUrl": "https://issues.jenkins.io/issues/?jql=component=21710" + } + ], + "labels": [ + "cli", + "misc", + "slaves", + "trigger" + ], + "name": "workflow-aggregator", + "popularity": 256809, + "previousTimestamp": "2022-05-24T12:27:03.00Z", + "previousVersion": "581.v0c46fa_697ffd", + "releaseTimestamp": "2022-06-29T21:02:16.00Z", + "requiredCore": "2.999", + "scm": "https://github.com/jenkinsci/workflow-aggregator-plugin", + "sha1": "6iNlKTnXvr4W+9Svwy1+ctyqgvo=", + "sha256": "P0SFgnqOBFZjxkdKORiQUbrhdlfW5YU7tOpe85bfGWE=", + "size": 8282, + "title": "Pipeline", + "url": "https://updates.jenkins.io/download/plugins/workflow-aggregator/590.v6a_d052e5a_a_b_5/workflow-aggregator.hpi", + "version": "590.v6a_d052e5a_a_b_5", + "wiki": "javascript:alert(1)" + } + }, + "updateCenterVersion": "1" +});