提交 8c451b08 编写于 作者: M Matt Sicker 提交者: Jenkins CERT CI

[SECURITY-2035][SECURITY-2171]

上级 b1ca2849
...@@ -26,6 +26,7 @@ THE SOFTWARE. ...@@ -26,6 +26,7 @@ THE SOFTWARE.
<f:section title="${descriptor.displayName}"> <f:section title="${descriptor.displayName}">
<d:taglib uri="local"> <d:taglib uri="local">
<d:tag name="section"> <d:tag name="section">
<j:set var="escapeEntryTitleAndDescription" value="true"/>
<f:entry title="${%title(descriptor.displayName)}" description="${%description(descriptor.displayName)}"> <f:entry title="${%title(descriptor.displayName)}" description="${%description(descriptor.displayName)}">
<f:repeatable name="tool" var="instance" items="${descriptor.installations}" <f:repeatable name="tool" var="instance" items="${descriptor.installations}"
add="${%label.add(descriptor.displayName)}" header="${descriptor.displayName}" enableTopButton="true" add="${%label.add(descriptor.displayName)}" header="${descriptor.displayName}" enableTopButton="true"
......
...@@ -26,7 +26,12 @@ Behaviour.specify("DIV.hetero-list-container", 'hetero-list', -100, function(e) ...@@ -26,7 +26,12 @@ Behaviour.specify("DIV.hetero-list-container", 'hetero-list', -100, function(e)
var name = n.getAttribute("name"); var name = n.getAttribute("name");
var tooltip = n.getAttribute("tooltip"); var tooltip = n.getAttribute("tooltip");
var descriptorId = n.getAttribute("descriptorId"); var descriptorId = n.getAttribute("descriptorId");
menu.options[i] = new Option(n.getAttribute("title"),""+i); // YUI Menu interprets this <option> text node as HTML, so let's escape it again!
var title = n.getAttribute("title");
if (title) {
title = title.escapeHTML();
}
menu.options[i] = new Option(title,""+i);
templates.push({html:n.innerHTML, name:name, tooltip:tooltip,descriptorId:descriptorId}); templates.push({html:n.innerHTML, name:name, tooltip:tooltip,descriptorId:descriptorId});
i++; i++;
}); });
......
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Tom Huybrechts
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.model;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLElement;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import javax.annotation.CheckForNull;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**
* @author Kohsuke Kawaguchi
*/
//TODO to be merged back into ViewTest after the security release
public class ViewSEC2171Test {
@Rule public JenkinsRule j = new JenkinsRule();
@Test
@Issue("SECURITY-2171")
public void newJob_xssPreventedInId() throws Exception {
CustomizableTLID customizableTLID = j.jenkins.getExtensionList(TopLevelItemDescriptor.class).get(CustomizableTLID.class);
customizableTLID.customId = "regularclass\" onclick=alert(123) other=\"";
customizableTLID.customDisplayName = "DN-xss-id";
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("view/all/newJob");
Object result = page.executeJavaScript("Array.from(document.querySelectorAll('.label')).filter(el => el.innerText.indexOf('" + customizableTLID.customDisplayName + "') !== -1)[0].parentElement.parentElement").getJavaScriptResult();
assertThat(result, instanceOf(HTMLElement.class));
HTMLElement resultElement = (HTMLElement) result;
assertThat(resultElement.getAttribute("onclick", null), nullValue());
}
@Test
@Issue("SECURITY-2171")
public void newJob_xssPreventedInDisplayName() throws Exception {
CustomizableTLID customizableTLID = j.jenkins.getExtensionList(TopLevelItemDescriptor.class).get(CustomizableTLID.class);
customizableTLID.customId = "xss-dn";
customizableTLID.customDisplayName = "DN <img src=x onerror=console.warn(123)>";
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("view/all/newJob");
Object result = page.executeJavaScript("document.querySelector('.xss-dn .label').innerHTML").getJavaScriptResult();
assertThat(result, instanceOf(String.class));
String resultString = (String) result;
assertThat(resultString, not(containsString("<")));
}
@Test
public void newJob_descriptionSupportsHtml() throws Exception {
CustomizableTLID customizableTLID = j.jenkins.getExtensionList(TopLevelItemDescriptor.class).get(CustomizableTLID.class);
customizableTLID.customId = "html-desc";
customizableTLID.customDescription = "Super <strong>looong</strong> description";
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("view/all/newJob");
Object result = page.executeJavaScript("document.querySelector('.html-desc .desc strong')").getJavaScriptResult();
assertThat(result, instanceOf(HTMLElement.class));
assertThat(((HTMLElement) result).getTagName(), is("STRONG"));
}
@Test
@Issue("SECURITY-2171")
public void newJob_xssPreventedInGetIconFilePathPattern() throws Exception {
CustomizableTLID customizableTLID = j.jenkins.getExtensionList(TopLevelItemDescriptor.class).get(CustomizableTLID.class);
customizableTLID.customId = "xss-ifpp";
customizableTLID.customIconClassName = null;
customizableTLID.customIconFilePathPattern = "\"><img src=x onerror=\"alert(123)";
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("view/all/newJob");
Object resultIconChildrenCount = page.executeJavaScript("document.querySelector('." + customizableTLID.customId + " .icon').children.length").getJavaScriptResult();
assertThat(resultIconChildrenCount, instanceOf(Integer.class));
int resultIconChildrenCountInt = (int) resultIconChildrenCount;
assertEquals(1, resultIconChildrenCountInt);
Object resultImgAttributesCount = page.executeJavaScript("document.querySelector('." + customizableTLID.customId + " .icon img').attributes.length").getJavaScriptResult();
assertThat(resultImgAttributesCount, instanceOf(Integer.class));
int resultImgAttributesCountInt = (int) resultImgAttributesCount;
assertEquals(1, resultImgAttributesCountInt);
}
@Test
public void newJob_iconClassName() throws Exception {
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("view/all/newJob");
Object resultClassNames = page.executeJavaScript("document.querySelector('.hudson_model_FreeStyleProject .icon img').className").getJavaScriptResult();
assertThat(resultClassNames, instanceOf(String.class));
String resultClassNamesString = (String) resultClassNames;
List<String> resultClassNamesList = Arrays.asList(resultClassNamesString.split(" "));
assertThat(resultClassNamesList, hasItem("icon-xlg"));
assertThat(resultClassNamesList, hasItem("icon-freestyle-project"));
Object resultSrc = page.executeJavaScript("document.querySelector('.hudson_model_FreeStyleProject .icon img').src").getJavaScriptResult();
assertThat(resultSrc, instanceOf(String.class));
String resultSrcString = (String) resultSrc;
assertThat(resultSrcString, containsString("48x48"));
assertThat(resultSrcString, containsString("freestyleproject.png"));
}
@Test
public void newJob_twoLetterIcon() throws Exception {
CustomizableTLID customizableTLID = j.jenkins.getExtensionList(TopLevelItemDescriptor.class).get(CustomizableTLID.class);
customizableTLID.customId = "two-letters-desc";
customizableTLID.customDisplayName = "Two words";
customizableTLID.customIconClassName = null;
customizableTLID.customIconFilePathPattern = null;
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("view/all/newJob");
Object result = page.executeJavaScript("document.querySelector('." + customizableTLID.customId + " .default-icon')").getJavaScriptResult();
assertThat(result, instanceOf(HTMLElement.class));
HTMLElement resultHtml = (HTMLElement) result;
HTMLElement spanA = (HTMLElement) resultHtml.getFirstElementChild();
HTMLElement spanB = (HTMLElement) resultHtml.getLastElementChild();
assertThat(spanA.getClassName_js(), is("a"));
assertThat(spanA.getInnerText(), is("T"));
assertThat(spanB.getClassName_js(), is("b"));
assertThat(spanB.getInnerText(), is("w"));
}
@TestExtension
public static class CustomizableTLID extends TopLevelItemDescriptor {
public String customId = "ID-not-yet-defined";
public String customDisplayName = "DisplayName-not-yet-defined";
public String customDescription = "Description-not-yet-defined";
public String customIconFilePathPattern = "IconFilePathPattern-not-yet-defined";
public String customIconClassName = "IconClassName-not-yet-defined";
public CustomizableTLID() {
super(FreeStyleProject.class);
}
@Override
public String getId() {
return customId;
}
@Override
public String getDisplayName() {
return customDisplayName;
}
@Override
public String getDescription() {
return customDescription;
}
@Override
public @CheckForNull String getIconFilePathPattern() {
return customIconFilePathPattern;
}
@Override
public String getIconClassName() {
return customIconClassName;
}
@Override
public TopLevelItem newInstance(ItemGroup parent, String name) {
throw new UnsupportedOperationException();
}
}
}
/*
* The MIT License
*
* Copyright (c) 2020, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lib.form;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlElementUtil;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLAnchorElement;
import hudson.ExtensionList;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.UnprotectedRootAction;
import hudson.tools.ToolDescriptor;
import hudson.tools.ToolInstallation;
import hudson.tools.ToolInstaller;
import hudson.tools.ToolProperty;
import hudson.util.FormValidation;
import net.sourceforge.htmlunit.corejs.javascript.NativeArray;
import org.jenkinsci.Symbol;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import javax.annotation.CheckForNull;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
//TODO rename to HeteroListTest after security release
public class HeteroListSEC2035Test {
@Rule
public JenkinsRule j = new JenkinsRule();
@Test
@Issue("SECURITY-2035")
public void xssPrevented_heteroList_usingDescriptorDisplayName() throws Exception {
JenkinsRule.WebClient wc = j.createWebClient();
RootActionImpl rootAction = ExtensionList.lookupSingleton(RootActionImpl.class);
TestItemDescribable.DynamicDisplayNameDescriptor dynamic = ExtensionList.lookupSingleton(TestItemDescribable.DynamicDisplayNameDescriptor.class);
rootAction.descriptorList = Arrays.asList(dynamic);
dynamic.displayName = "Display<strong>Name</strong>";
HtmlPage page = wc.goTo("root");
page.executeJavaScript("document.querySelector('.hetero-list-add').click();");
Object result = page.executeJavaScript("document.querySelector('.yuimenuitem a')").getJavaScriptResult();
assertThat(result, instanceOf(HTMLAnchorElement.class));
HTMLAnchorElement menuItem = (HTMLAnchorElement) result;
String menuItemContent = menuItem.getInnerHTML();
assertThat(menuItemContent, not(containsString("<")));
}
// correspond to the hardening of escapeEntryTitleAndDescription
@Test
@Issue("SECURITY-2035")
public void xssPrevented_usingToolInstallation_withJustDisplayName() throws Exception {
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("configureTools/");
// check the displayName
Object resultDN = page.executeJavaScript(
"var settingFields = document.querySelectorAll('.setting-name');" +
"var children = Array.from(settingFields).filter(b => b.textContent.indexOf('XSS:') !== -1)[0].children;" +
"Array.from(children).filter(c => c.tagName === 'IMG')"
).getJavaScriptResult();
assertThat(resultDN, instanceOf(NativeArray.class));
NativeArray resultDNNA = (NativeArray) resultDN;
assertEquals(0, resultDNNA.size());
// check the description
Object resultDesc = page.executeJavaScript(
"var settingFields = document.querySelectorAll('.setting-description');" +
"var children = Array.from(settingFields).filter(b => b.textContent.indexOf('XSS:') !== -1)[0].children;" +
"Array.from(children).filter(c => c.tagName === 'IMG')"
).getJavaScriptResult();
assertThat(resultDesc, instanceOf(NativeArray.class));
NativeArray resultDescNA = (NativeArray) resultDesc;
assertEquals(0, resultDescNA.size());
}
@Test
@Issue("SECURITY-2035")
public void xssPrevented_usingToolInstallation_repeatableAddExisting() throws Exception {
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("configureTools/");
// the existing add button can already trigger an XSS
Object result = page.executeJavaScript("Array.from(document.querySelectorAll('button')).filter(b => b.textContent.indexOf('Add XSS') !== -1)[0].innerHTML").getJavaScriptResult();
assertThat(result, instanceOf(String.class));
String resultString = (String) result;
assertThat(resultString, not(containsString("<")));
}
// only possible after a partial fix
@Test
public void xssPrevented_usingToolInstallation_repeatableAddAfterClick() throws Exception {
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("configureTools/");
Optional<DomElement> addXssButtonRawOptional = page.getElementsByTagName("button").stream().filter(e -> e.getTextContent().contains("Add XSS")).findFirst();
assertTrue(addXssButtonRawOptional.isPresent());
assertThat(addXssButtonRawOptional.get(), instanceOf(HtmlButton.class));
HtmlButton addXssButton = (HtmlButton) addXssButtonRawOptional.get();
HtmlElementUtil.click(addXssButton);
// checking only the newly created button (at the top of the panel), hence the [0]
Object result = page.executeJavaScript("Array.from(document.querySelectorAll('button')).filter(b => b.textContent.indexOf('Add XSS') !== -1)[0].innerHTML").getJavaScriptResult();
assertThat(result, instanceOf(String.class));
String resultString = (String) result;
assertThat(resultString, not(containsString("<")));
}
@Test
@Issue("SECURITY-2035")
public void xssPrevented_usingToolInstallation_repeatableAddWithExistingUsingInstallationsButton() throws Exception {
Xss.DescriptorImpl xssDescriptor = ExtensionList.lookupSingleton(Xss.DescriptorImpl.class);
xssDescriptor.installations = new Xss[]{ new Xss("name1", "home1", null) };
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("configureTools/");
// XSS: [img] installations...
Object result = page.executeJavaScript("Array.from(document.querySelectorAll('button')).filter(b => b.textContent.indexOf('XSS:') !== -1)[0].innerHTML").getJavaScriptResult();
assertThat(result, instanceOf(String.class));
String resultString = (String) result;
assertThat(resultString, not(containsString("<")));
}
@Test
@Issue("SECURITY-2035")
public void xssPrevented_usingToolInstallation_repeatableAddWithExistingAfterOpening() throws Exception {
Xss.DescriptorImpl xssDescriptor = ExtensionList.lookupSingleton(Xss.DescriptorImpl.class);
xssDescriptor.installations = new Xss[]{ new Xss("name1", "home1", null) };
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("configureTools/");
// Passing the installation button
page.executeJavaScript("Array.from(document.querySelectorAll('button')).filter(b => b.textContent.indexOf('XSS:') !== -1)[0].click()");
// Looking for all the buttons displayed, at this point there is one Add, one Delete and the second Add.
// Both add are generated through different code.
// While keeping away the installations... advanced button as it's covered in its own test
Object result = page.executeJavaScript("Array.from(document.querySelectorAll('button')).filter(b => b.textContent.indexOf('XSS') !== -1 && b.textContent.indexOf('...') === -1).map(b => b.innerHTML)").getJavaScriptResult();
assertThat(result, instanceOf(List.class));
List resultArray = (List) result;
for (int i = 0; i < resultArray.size(); i++) {
assertThat((String) resultArray.get(i), not(containsString("<")));
}
// "delete" then "add" makes us coming back in scenario covered by xssUsingToolInstallationRepeatableAdd
}
@Test
@Issue("SECURITY-2035")
public void xssPrevented_usingToolInstallation_repeatableDelete() throws Exception {
JenkinsRule.WebClient wc = j.createWebClient();
HtmlPage page = wc.goTo("configureTools/");
// we could also re-use the same method as used in xssUsingToolInstallationRepeatableAdd
page.executeJavaScript("Array.from(document.querySelectorAll('button')).filter(b => b.textContent.indexOf('Add XSS') !== -1)[0].click()");
Object result = page.executeJavaScript("Array.from(document.querySelectorAll('button')).filter(b => b.textContent.indexOf('Delete XSS') !== -1)[0].innerHTML").getJavaScriptResult();
assertThat(result, instanceOf(String.class));
String resultString = (String) result;
assertThat(resultString, not(containsString("<")));
}
public static class TestItemDescribable implements Describable<TestItemDescribable> {
public Descriptor<TestItemDescribable> getDescriptor() {
return ExtensionList.lookupSingleton(DynamicDisplayNameDescriptor.class);
}
@TestExtension
public static class DynamicDisplayNameDescriptor extends Descriptor<TestItemDescribable> {
public String displayName = "NotYetDefined";
@Override
public String getDisplayName() {
return displayName;
}
}
}
@TestExtension
public static class RootActionImpl implements UnprotectedRootAction {
public List<Descriptor<?>> descriptorList;
@CheckForNull
public String getIconFileName() {
return null;
}
@CheckForNull
public String getDisplayName() {
return null;
}
@CheckForNull
public String getUrlName() {
return "root";
}
}
public static final class Xss extends ToolInstallation {
public Xss(String name, String home, List<? extends ToolProperty<?>> properties) {
super(name, home, properties);
}
@TestExtension
@Symbol("tool-xss")
public static class DescriptorImpl extends ToolDescriptor<Xss> {
private Xss[] installations = new Xss[0];
public String getDisplayName() {
return "XSS: <img src=x onerror=console.warn('" + getClass().getName() +"') />";
}
public @Override Xss[] getInstallations() {
return installations;
}
public @Override void setInstallations(Xss... xsses) {
this.installations = xsses;
}
@Override
public List<? extends ToolInstaller> getDefaultInstallers() {
return Collections.emptyList();
}
/**
* Checks if the JAVA_HOME is a valid JAVA_HOME path.
*/
@Override protected FormValidation checkHomeDirectory(File value) {
return FormValidation.ok();
}
}
}
}
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:f="/lib/form">
<l:layout title="Testing the effect of validateButton">
<l:main-panel>
<f:hetero-list name="no-care" descriptors="${it.descriptorList}"/>
</l:main-panel>
</l:layout>
</j:jelly>
...@@ -160,9 +160,30 @@ $.when(getItems()).done(function(data) { ...@@ -160,9 +160,30 @@ $.when(getItems()).done(function(data) {
} }
function drawItem(elem) { function drawItem(elem) {
var desc = checkForLink(elem.description); var item = document.createElement('li');
var $item = $(['<li tabindex="0" role="radio" aria-checked="false" class="', cleanClassName(elem.class), '"><label><input type="radio" name="mode" value="', item.tabIndex = 0;
elem.class ,'"/> <span class="label">', elem.displayName, '</span></label></li>'].join('')).append(['<div class="desc">', desc, '</div>'].join('')).append(drawIcon(elem)); item.className = cleanClassName(elem.class);
item.setAttribute('role', 'radio');
item.setAttribute('aria-checked', 'false');
var label = item.appendChild(document.createElement('label'));
var radio = label.appendChild(document.createElement('input'));
radio.type = 'radio';
radio.name = 'mode';
radio.value = elem.class;
var displayName = label.appendChild(document.createElement('span'));
displayName.className = 'label';
displayName.appendChild(document.createTextNode(elem.displayName));
var desc = item.appendChild(document.createElement('div'));
desc.className = 'desc';
desc.innerHTML = checkForLink(elem.description);
var iconDiv = drawIcon(elem);
item.appendChild(iconDiv);
function select(e) { function select(e) {
e.preventDefault(); e.preventDefault();
...@@ -183,32 +204,41 @@ $.when(getItems()).done(function(data) { ...@@ -183,32 +204,41 @@ $.when(getItems()).done(function(data) {
} }
} }
$item.click(select); item.addEventListener('click', select);
item.addEventListener('keydown', function (evt) {
$item.keypress(function(e) { if (evt.code === 'Space' || evt.code === 'Enter') {
switch (e.which) { this.click();
case 13: evt.stopPropagation();
case 32:
$(this).trigger('click');
e.stopPropagation();
break;
} }
}); });
return $item; return item;
} }
function drawIcon(elem) { function drawIcon(elem) {
var $icn; var iconDiv = document.createElement('div');
if (elem.iconClassName && elem.iconQualifiedUrl) { if (elem.iconClassName && elem.iconQualifiedUrl) {
$icn = $('<div class="icon">'); iconDiv.className = 'icon';
$(['<img class="', elem.iconClassName, ' icon-xlg" src="', elem.iconQualifiedUrl, '">'].join('')).appendTo($icn);
var img1 = document.createElement('img');
img1.className = elem.iconClassName + ' icon-xlg';
img1.src = elem.iconQualifiedUrl;
iconDiv.appendChild(img1);
// Example for Freestyle project
// <div class="icon"><img class="icon-freestyle-project icon-xlg" src="/jenkins/static/108b2346/images/48x48/freestyleproject.png"></div>
} else if (elem.iconFilePathPattern) { } else if (elem.iconFilePathPattern) {
$icn = $('<div class="icon">'); iconDiv.className = 'icon';
var iconFilePath = jRoot + '/' + elem.iconFilePathPattern.replace(":size", "48x48"); var iconFilePath = jRoot + '/' + elem.iconFilePathPattern.replace(":size", "48x48");
$(['<img src="', iconFilePath, '">'].join('')).appendTo($icn);
var img2 = document.createElement('img');
img2.src = iconFilePath;
iconDiv.appendChild(img2);
// Example for Maven project
// <div class="icon"><img src="/jenkins/plugin/maven-plugin/images/48x48/mavenmoduleset.png"></div>
} else { } else {
$icn = $('<div class="default-icon">');
var colors = ['c-49728B','c-335061','c-D33833','c-6D6B6D', 'c-6699CC']; var colors = ['c-49728B','c-335061','c-D33833','c-6D6B6D', 'c-6699CC'];
var desc = elem.description || ''; var desc = elem.description || '';
var name = elem.displayName; var name = elem.displayName;
...@@ -216,10 +246,21 @@ $.when(getItems()).done(function(data) { ...@@ -216,10 +246,21 @@ $.when(getItems()).done(function(data) {
var aName = name.split(' '); var aName = name.split(' ');
var a = name.substring(0,1); var a = name.substring(0,1);
var b = ((aName.length === 1) ? name.substring(1,2) : aName[1].substring(0,1)); var b = ((aName.length === 1) ? name.substring(1,2) : aName[1].substring(0,1));
$(['<span class="a">',a,'</span><span class="b">',b,'</span>'].join('')).appendTo($icn);
$icn.addClass(colorClass); var spanFakeImgA = document.createElement('span');
spanFakeImgA.className = "a";
spanFakeImgA.innerText = a;
iconDiv.appendChild(spanFakeImgA);
var spanFakeImgB = document.createElement('span');
spanFakeImgB.className = "b";
spanFakeImgB.innerText = b;
iconDiv.appendChild(spanFakeImgB);
iconDiv.className = colorClass + ' default-icon';
// Example for MockFolder
// <div class="default-icon c-49728B"><span class="a">M</span><span class="b">o</span></div>
} }
return $icn; return iconDiv;
} }
// The main panel content is hidden by default via an inline style. We're ready to remove that now. // The main panel content is hidden by default via an inline style. We're ready to remove that now.
......
...@@ -611,7 +611,15 @@ function makeButton(e,onclick) { ...@@ -611,7 +611,15 @@ function makeButton(e,onclick) {
var h = e.onclick; var h = e.onclick;
var clsName = e.className; var clsName = e.className;
var n = e.name; var n = e.name;
var btn = new YAHOO.widget.Button(e,{});
var attributes = {};
// YUI Button class interprets value attribute of <input> as HTML
// similar to how the child nodes of a <button> are treated as HTML.
// in standard HTML, we wouldn't expect the former case, yet here we are!
if (e.tagName === 'INPUT') {
attributes.label = e.value.escapeHTML();
}
var btn = new YAHOO.widget.Button(e, attributes);
if(onclick!=null) if(onclick!=null)
btn.addListener("click",onclick); btn.addListener("click",onclick);
if(h!=null) if(h!=null)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册