Skip to content

Commit 578c028

Browse files
daniel-beckjenkinsci-cert-ci
authored andcommitted
1 parent f92eadb commit 578c028

File tree

3 files changed

+162
-1
lines changed

3 files changed

+162
-1
lines changed

core/src/main/java/hudson/slaves/OfflineCause.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import edu.umd.cs.findbugs.annotations.CheckForNull;
2828
import edu.umd.cs.findbugs.annotations.NonNull;
29+
import hudson.Util;
2930
import hudson.model.Computer;
3031
import hudson.model.User;
3132
import java.io.ObjectStreamException;
@@ -166,7 +167,7 @@ public User getUser() {
166167
* @return the message that was provided when the computer was taken offline
167168
*/
168169
public String getMessage() {
169-
return message;
170+
return Util.escape(message);
170171
}
171172

172173
// Storing the User in a filed was a mistake, switch to userId
@@ -202,6 +203,11 @@ public String getComputerIconAltText() {
202203
public String getIcon() {
203204
return "symbol-person";
204205
}
206+
207+
@Override
208+
public String toString() {
209+
return Util.escape(super.toString());
210+
}
205211
}
206212

207213
public static class ByCLI extends UserCause {
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package jenkins.security;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.allOf;
5+
import static org.hamcrest.Matchers.containsString;
6+
import static org.hamcrest.Matchers.not;
7+
import static org.hamcrest.Matchers.nullValue;
8+
9+
import hudson.model.Computer;
10+
import hudson.model.User;
11+
import hudson.slaves.DumbSlave;
12+
import hudson.slaves.OfflineCause;
13+
import java.io.ByteArrayInputStream;
14+
import jenkins.model.Jenkins;
15+
import org.htmlunit.html.HtmlFormUtil;
16+
import org.htmlunit.html.HtmlPage;
17+
import org.junit.jupiter.api.Test;
18+
import org.jvnet.hudson.test.JenkinsRule;
19+
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
20+
import org.jvnet.hudson.test.junit.jupiter.WithLocalData;
21+
22+
@WithJenkins
23+
public class Security3669Test {
24+
@Test
25+
public void newOfflineCause(JenkinsRule jenkinsRule) throws Exception {
26+
try (JenkinsRule.WebClient webClient = jenkinsRule.createWebClient()) {
27+
HtmlPage formPage = webClient.getPage(Jenkins.get(), "markOffline");
28+
formPage.getElementByName("offlineMessage").setTextContent("<img src=x onerror=alert(1)>");
29+
HtmlFormUtil.submit(formPage.getForms().stream().filter(f -> f.getActionAttribute().equals("toggleOffline")).findFirst().orElseThrow());
30+
31+
final HtmlPage nodePage = webClient.getPage(Jenkins.get());
32+
assertThat(
33+
nodePage.getWebResponse().getContentAsString(),
34+
allOf(
35+
not(containsString("<img src=x onerror=alert(1)>")),
36+
containsString("Disconnected by anonymous : &lt;img src=x onerror=alert(1)&gt;")));
37+
}
38+
}
39+
40+
@Test
41+
public void editOfflineCause(JenkinsRule jenkinsRule) throws Exception {
42+
Jenkins.get().getComputer("").setTemporaryOfflineCause(new OfflineCause.UserCause(User.current(), "initial reason"));
43+
try (JenkinsRule.WebClient webClient = jenkinsRule.createWebClient()) {
44+
HtmlPage formPage = webClient.getPage(Jenkins.get(), "setOfflineCause");
45+
formPage.getElementByName("offlineMessage").setTextContent("<img src=x onerror=alert(1)>");
46+
HtmlFormUtil.submit(formPage.getForms().stream().filter(f -> f.getActionAttribute().equals("changeOfflineCause")).findFirst().orElseThrow());
47+
48+
final HtmlPage nodePage = webClient.getPage(Jenkins.get());
49+
assertThat(
50+
nodePage.getWebResponse().getContentAsString(),
51+
allOf(
52+
not(containsString("<img src=x onerror=alert(1)>")),
53+
containsString("Disconnected by anonymous : &lt;img src=x onerror=alert(1)&gt;")));
54+
}
55+
}
56+
57+
@Test
58+
void postConfigXmlWithLocalizable(JenkinsRule jenkinsRule) throws Exception {
59+
final DumbSlave agent = jenkinsRule.createOnlineSlave();
60+
61+
String xml = "<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n" +
62+
"<slave>\n" +
63+
" <temporaryOfflineCause class=\"hudson.slaves.OfflineCause$UserCause\">\n" +
64+
" <timestamp>1770000000000</timestamp>\n" +
65+
" <description>\n" +
66+
" <holder>\n" +
67+
" <owner>hudson.slaves.Messages</owner>\n" +
68+
" </holder>\n" +
69+
" <key>SlaveComputer.DisconnectedBy</key>\n" +
70+
" <args>\n" +
71+
" <string>admin</string>\n" +
72+
" <string> : &lt;img src=x onerror=alert(1)&gt;</string>\n" +
73+
" </args>\n" +
74+
" </description>\n" +
75+
" <userId>admin</userId>\n" +
76+
" <message>&lt;img src=x onerror=alert(1)&gt;</message>\n" +
77+
" </temporaryOfflineCause>\n" +
78+
" <name>" + agent.getNodeName() + "</name>\n" +
79+
" <description></description>\n" +
80+
" <remoteFS>/tmp/foo</remoteFS>\n" +
81+
" <numExecutors>1</numExecutors>\n" +
82+
" <mode>NORMAL</mode>\n" +
83+
" <retentionStrategy class=\"hudson.slaves.RetentionStrategy$Always\"/>\n" +
84+
" <launcher class=\"hudson.slaves.JNLPLauncher\">\n" +
85+
" <workDirSettings>\n" +
86+
" <disabled>false</disabled>\n" +
87+
" <internalDir>remoting</internalDir>\n" +
88+
" <failIfWorkDirIsMissing>false</failIfWorkDirIsMissing>\n" +
89+
" </workDirSettings>\n" +
90+
" <webSocket>false</webSocket>\n" +
91+
" </launcher>\n" +
92+
" <label></label>\n" +
93+
" <nodeProperties/>\n" +
94+
"</slave>";
95+
agent.toComputer().updateByXml(new ByteArrayInputStream(xml.getBytes()));
96+
97+
try (JenkinsRule.WebClient webClient = jenkinsRule.createWebClient()) {
98+
final HtmlPage nodePage = webClient.getPage(agent);
99+
assertThat(
100+
nodePage.getWebResponse().getContentAsString(),
101+
allOf(
102+
not(containsString("<img src=x onerror=alert(1)>")),
103+
containsString("Disconnected by admin : &lt;img src=x onerror=alert(1)&gt;")));
104+
}
105+
}
106+
107+
@Test
108+
@WithLocalData
109+
void dataFromDisk(JenkinsRule jenkinsRule) throws Exception {
110+
final Computer agent = jenkinsRule.jenkins.getComputer("a1");
111+
assertThat(agent, not(nullValue()));
112+
113+
try (JenkinsRule.WebClient webClient = jenkinsRule.createWebClient()) {
114+
final HtmlPage nodePage = webClient.getPage(agent.getNode());
115+
assertThat(
116+
nodePage.getWebResponse().getContentAsString(),
117+
allOf(
118+
not(containsString("<img src=x onerror=alert(1)>")),
119+
containsString("Disconnected by anonymous : &lt;img src=x onerror=alert(1)&gt;")));
120+
}
121+
}
122+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version='1.1' encoding='UTF-8'?>
2+
<slave>
3+
<temporaryOfflineCause class="hudson.slaves.OfflineCause$UserCause">
4+
<timestamp>1770000000000</timestamp>
5+
<description>
6+
<holder>
7+
<owner>hudson.slaves.Messages</owner>
8+
</holder>
9+
<key>SlaveComputer.DisconnectedBy</key>
10+
<args>
11+
<string>anonymous</string>
12+
<string> : &lt;img src=x onerror=alert(1)&gt;</string>
13+
</args>
14+
</description>
15+
<message>&lt;img src=x onerror=alert(1)&gt;</message>
16+
</temporaryOfflineCause>
17+
<name>a1</name>
18+
<description></description>
19+
<remoteFS>/tmp/a1</remoteFS>
20+
<numExecutors>1</numExecutors>
21+
<mode>NORMAL</mode>
22+
<retentionStrategy class="hudson.slaves.RetentionStrategy$Always"/>
23+
<launcher class="hudson.slaves.JNLPLauncher">
24+
<workDirSettings>
25+
<disabled>false</disabled>
26+
<internalDir>remoting</internalDir>
27+
<failIfWorkDirIsMissing>false</failIfWorkDirIsMissing>
28+
</workDirSettings>
29+
<webSocket>false</webSocket>
30+
</launcher>
31+
<label></label>
32+
<nodeProperties/>
33+
</slave>

0 commit comments

Comments
 (0)