Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
LinuxSuRen
jenkins
提交
8c451b08
J
jenkins
项目概览
LinuxSuRen
/
jenkins
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
jenkins
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
8c451b08
编写于
1月 11, 2021
作者:
M
Matt Sicker
提交者:
Jenkins CERT CI
1月 11, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[SECURITY-2035][SECURITY-2171]
上级
b1ca2849
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
585 addition
and
24 deletion
+585
-24
core/src/main/resources/hudson/tools/ToolInstallation/global.jelly
...main/resources/hudson/tools/ToolInstallation/global.jelly
+1
-0
core/src/main/resources/lib/form/hetero-list/hetero-list.js
core/src/main/resources/lib/form/hetero-list/hetero-list.js
+6
-1
test/src/test/java/hudson/model/ViewSEC2171Test.java
test/src/test/java/hudson/model/ViewSEC2171Test.java
+215
-0
test/src/test/java/lib/form/HeteroListSEC2035Test.java
test/src/test/java/lib/form/HeteroListSEC2035Test.java
+283
-0
test/src/test/resources/lib/form/HeteroListSEC2035Test/RootActionImpl/index.jelly
...lib/form/HeteroListSEC2035Test/RootActionImpl/index.jelly
+8
-0
war/src/main/js/add-item.js
war/src/main/js/add-item.js
+63
-22
war/src/main/webapp/scripts/hudson-behavior.js
war/src/main/webapp/scripts/hudson-behavior.js
+9
-1
未找到文件。
core/src/main/resources/hudson/tools/ToolInstallation/global.jelly
浏览文件 @
8c451b08
...
@@ -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"
...
...
core/src/main/resources/lib/form/hetero-list/hetero-list.js
浏览文件 @
8c451b08
...
@@ -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
++
;
});
});
...
...
test/src/test/java/hudson/model/ViewSEC2171Test.java
0 → 100644
浏览文件 @
8c451b08
/*
* 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
();
}
}
}
test/src/test/java/lib/form/HeteroListSEC2035Test.java
0 → 100644
浏览文件 @
8c451b08
/*
* 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
();
}
}
}
}
test/src/test/resources/lib/form/HeteroListSEC2035Test/RootActionImpl/index.jelly
0 → 100644
浏览文件 @
8c451b08
<?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>
war/src/main/js/add-item.js
浏览文件 @
8c451b08
...
@@ -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.
...
...
war/src/main/webapp/scripts/hudson-behavior.js
浏览文件 @
8c451b08
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录