Skip to content

Commit 639ade5

Browse files
committed
1 parent cd28a6d commit 639ade5

File tree

3 files changed

+176
-0
lines changed

3 files changed

+176
-0
lines changed

core/src/main/java/hudson/model/Api.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ private boolean permit(StaplerRequest req) {
246246
protected void setHeaders(StaplerResponse rsp) {
247247
rsp.setHeader("X-Jenkins", Jenkins.VERSION);
248248
rsp.setHeader("X-Jenkins-Session", Jenkins.SESSION_HASH);
249+
// to be really defensive against dumb browsers not taking into consideration the content-type being set
250+
rsp.setHeader("X-Content-Type-Options", "nosniff");
251+
// recommended by OWASP: https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers
252+
rsp.setHeader("X-Frame-Options", "deny");
249253
}
250254

251255
private static final Logger LOGGER = Logger.getLogger(Api.class.getName());
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo!, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
package hudson.model;
25+
26+
import com.gargoylesoftware.htmlunit.WebResponse;
27+
import org.junit.Rule;
28+
import org.junit.Test;
29+
import org.jvnet.hudson.test.Issue;
30+
import org.jvnet.hudson.test.JenkinsRule;
31+
import org.jvnet.hudson.test.TestExtension;
32+
import org.kohsuke.stapler.export.ExportedBean;
33+
34+
import javax.annotation.CheckForNull;
35+
36+
import static org.hamcrest.Matchers.equalTo;
37+
import static org.junit.Assert.assertThat;
38+
39+
//TODO to be merged back to ApiTest after security release
40+
/**
41+
* @author Kohsuke Kawaguchi
42+
*/
43+
public class ApiSEC1704Test {
44+
45+
@Rule
46+
public JenkinsRule j = new JenkinsRule();
47+
48+
@Test
49+
@Issue("SECURITY-1704")
50+
public void project_notExposedToIFrame() throws Exception {
51+
FreeStyleProject p = j.createFreeStyleProject("p");
52+
ensureXmlIsNotExposedToIFrame(p.getUrl());
53+
ensureJsonIsNotExposedToIFrame(p.getUrl());
54+
ensurePythonIsNotExposedToIFrame(p.getUrl());
55+
}
56+
57+
@Test
58+
@Issue("SECURITY-1704")
59+
public void custom_notExposedToIFrame() throws Exception {
60+
ensureXmlIsNotExposedToIFrame("custom/");
61+
ensureJsonIsNotExposedToIFrame("custom/");
62+
ensurePythonIsNotExposedToIFrame("custom/");
63+
}
64+
65+
private void ensureXmlIsNotExposedToIFrame(String itemUrl) throws Exception {
66+
WebResponse response = j.createWebClient().goTo(itemUrl + "api/xml", "application/xml").getWebResponse();
67+
assertThat(response.getResponseHeaderValue("X-Frame-Options"), equalTo("deny"));
68+
}
69+
70+
private void ensureJsonIsNotExposedToIFrame(String itemUrl) throws Exception {
71+
WebResponse response = j.createWebClient().goTo(itemUrl + "api/json", "application/json").getWebResponse();
72+
assertThat(response.getResponseHeaderValue("X-Frame-Options"), equalTo("deny"));
73+
}
74+
75+
private void ensurePythonIsNotExposedToIFrame(String itemUrl) throws Exception {
76+
WebResponse response = j.createWebClient().goTo(itemUrl + "api/python", "text/x-python").getWebResponse();
77+
assertThat(response.getResponseHeaderValue("X-Frame-Options"), equalTo("deny"));
78+
}
79+
80+
@TestExtension("custom_notExposedToIFrame")
81+
public static class CustomObject implements RootAction {
82+
@Override
83+
public @CheckForNull String getIconFileName() {
84+
return null;
85+
}
86+
87+
@Override
88+
public @CheckForNull String getDisplayName() {
89+
return null;
90+
}
91+
92+
@Override
93+
public @CheckForNull String getUrlName() {
94+
return "custom";
95+
}
96+
97+
public Api getApi() {
98+
return new Api(new CustomData("s3cr3t"));
99+
}
100+
101+
@ExportedBean
102+
class CustomData {
103+
private String secret;
104+
CustomData(String secret){
105+
this.secret = secret;
106+
}
107+
}
108+
}
109+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright (c) 2008-2010 Yahoo! Inc.
3+
* All rights reserved.
4+
* The copyrights to the contents of this file are licensed under the MIT License (http://www.opensource.org/licenses/mit-license.php)
5+
*/
6+
7+
package hudson.security.csrf;
8+
9+
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
10+
import com.gargoylesoftware.htmlunit.WebResponse;
11+
import com.gargoylesoftware.htmlunit.html.DomElement;
12+
import com.gargoylesoftware.htmlunit.html.HtmlPage;
13+
import hudson.model.FreeStyleProject;
14+
import hudson.model.User;
15+
import org.junit.Before;
16+
import org.junit.Rule;
17+
import org.junit.Test;
18+
import org.jvnet.hudson.test.Issue;
19+
import org.jvnet.hudson.test.JenkinsRule;
20+
import org.jvnet.hudson.test.JenkinsRule.WebClient;
21+
22+
import static org.hamcrest.Matchers.equalTo;
23+
import static org.junit.Assert.assertEquals;
24+
import static org.junit.Assert.assertThat;
25+
import static org.junit.Assert.assertTrue;
26+
import static org.junit.Assert.fail;
27+
28+
/**
29+
*
30+
* @author dty
31+
*/
32+
//TODO merge back to DefaultCrumbIssuerTest
33+
public class DefaultCrumbIssuerSEC1704Test {
34+
35+
@Rule public JenkinsRule r = new JenkinsRule();
36+
37+
@Before public void setIssuer() {
38+
r.jenkins.setCrumbIssuer(new DefaultCrumbIssuer(false));
39+
}
40+
41+
@Test
42+
@Issue("SECURITY-1704")
43+
public void custom_notExposedToIFrame() throws Exception {
44+
ensureXmlIsNotExposedToIFrame("crumbIssuer/");
45+
ensureJsonIsNotExposedToIFrame("crumbIssuer/");
46+
ensurePythonIsNotExposedToIFrame("crumbIssuer/");
47+
}
48+
49+
private void ensureXmlIsNotExposedToIFrame(String itemUrl) throws Exception {
50+
WebResponse response = r.createWebClient().goTo(itemUrl + "api/xml", "application/xml").getWebResponse();
51+
assertThat(response.getResponseHeaderValue("X-Frame-Options"), equalTo("deny"));
52+
}
53+
54+
private void ensureJsonIsNotExposedToIFrame(String itemUrl) throws Exception {
55+
WebResponse response = r.createWebClient().goTo(itemUrl + "api/json", "application/json").getWebResponse();
56+
assertThat(response.getResponseHeaderValue("X-Frame-Options"), equalTo("deny"));
57+
}
58+
59+
private void ensurePythonIsNotExposedToIFrame(String itemUrl) throws Exception {
60+
WebResponse response = r.createWebClient().goTo(itemUrl + "api/python", "text/x-python").getWebResponse();
61+
assertThat(response.getResponseHeaderValue("X-Frame-Options"), equalTo("deny"));
62+
}
63+
}

0 commit comments

Comments
 (0)