Skip to content

Commit 03f54f7

Browse files
authored
Backport 2.541.1 (#26070)
2 parents fb686ea + 99bc87b commit 03f54f7

File tree

18 files changed

+344
-24
lines changed

18 files changed

+344
-24
lines changed

core/src/main/java/jenkins/security/csp/CspHeader.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
package jenkins.security.csp;
2626

27+
import edu.umd.cs.findbugs.annotations.CheckForNull;
2728
import org.kohsuke.accmod.Restricted;
2829
import org.kohsuke.accmod.restrictions.Beta;
2930

@@ -35,14 +36,16 @@
3536
@Restricted(Beta.class)
3637
public enum CspHeader {
3738
ContentSecurityPolicy("Content-Security-Policy"),
38-
ContentSecurityPolicyReportOnly("Content-Security-Policy-Report-Only");
39+
ContentSecurityPolicyReportOnly("Content-Security-Policy-Report-Only"),
40+
None(null);
3941

4042
private final String headerName;
4143

4244
CspHeader(String headerName) {
4345
this.headerName = headerName;
4446
}
4547

48+
@CheckForNull
4649
public String getHeaderName() {
4750
return headerName;
4851
}

core/src/main/java/jenkins/security/csp/impl/CspDecorator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,11 @@ public String getReportingEndpointsHeaderValue(HttpServletRequest req) {
105105
}
106106

107107
/**
108-
* Determines the name of the HTTP header to set.
108+
* Determines the name of the HTTP header to set, or {@code null} if none.
109109
*
110110
* @return the name of the HTTP header to set.
111111
*/
112+
@CheckForNull
112113
public String getContentSecurityPolicyHeaderName() {
113114
final Optional<CspHeaderDecider> decider = CspHeaderDecider.getCurrentDecider();
114115
if (decider.isPresent()) {

core/src/main/java/jenkins/security/csp/impl/CspFilter.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,14 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
6161

6262
CspDecorator cspDecorator = ExtensionList.lookupSingleton(CspDecorator.class);
6363
final String headerName = cspDecorator.getContentSecurityPolicyHeaderName();
64+
final boolean headerShouldBeSet = headerName != null;
6465

6566
// This is the preliminary value outside Stapler request handling (and providing a context object)
6667
final String headerValue = cspDecorator.getContentSecurityPolicyHeaderValue(req);
6768

6869
final boolean isResourceRequest = ResourceDomainConfiguration.isResourceRequest(req);
69-
if (!isResourceRequest) {
70+
71+
if (headerShouldBeSet && !isResourceRequest) {
7072
// The Filter/Decorator approach needs us to "set" headers rather than "add", so no additional endpoints are supported at the moment.
7173
final String reportingEndpoints = cspDecorator.getReportingEndpointsHeaderValue(req);
7274
if (reportingEndpoints != null) {
@@ -78,14 +80,16 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
7880
try {
7981
chain.doFilter(req, rsp);
8082
} finally {
81-
try {
82-
final String actualHeader = rsp.getHeader(headerName);
83-
if (!isResourceRequest && hasUnexpectedDifference(headerValue, actualHeader)) {
84-
LOGGER.log(Level.FINE, "CSP header has unexpected differences: Expected '" + headerValue + "' but got '" + actualHeader + "'");
83+
if (headerShouldBeSet) {
84+
try {
85+
final String actualHeader = rsp.getHeader(headerName);
86+
if (!isResourceRequest && hasUnexpectedDifference(headerValue, actualHeader)) {
87+
LOGGER.log(Level.FINE, "CSP header has unexpected differences: Expected '" + headerValue + "' but got '" + actualHeader + "'");
88+
}
89+
} catch (RuntimeException e) {
90+
// Be defensive just in case
91+
LOGGER.log(Level.FINER, "Error checking CSP header after request processing", e);
8592
}
86-
} catch (RuntimeException e) {
87-
// Be defensive just in case
88-
LOGGER.log(Level.FINER, "Error checking CSP header after request processing", e);
8993
}
9094
}
9195
}

core/src/main/java/jenkins/security/csp/impl/SystemPropertyHeaderDecider.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
package jenkins.security.csp.impl;
2626

2727
import hudson.Extension;
28+
import hudson.Util;
2829
import java.util.Arrays;
30+
import java.util.Objects;
2931
import java.util.Optional;
3032
import java.util.logging.Level;
3133
import java.util.logging.Logger;
@@ -50,7 +52,8 @@ public Optional<CspHeader> decide() {
5052
final String systemProperty = SystemProperties.getString(SYSTEM_PROPERTY_NAME);
5153
if (systemProperty != null) {
5254
LOGGER.log(Level.FINEST, "Using system property: {0}", new Object[]{ systemProperty });
53-
return Arrays.stream(CspHeader.values()).filter(h -> h.getHeaderName().equals(systemProperty)).findFirst();
55+
final String expected = Util.fixEmptyAndTrim(systemProperty);
56+
return Arrays.stream(CspHeader.values()).filter(h -> Objects.equals(expected, h.getHeaderName())).findFirst();
5457
}
5558
return Optional.empty();
5659
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2025, CloudBees, 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+
25+
package jenkins.telemetry.impl;
26+
27+
import edu.umd.cs.findbugs.annotations.NonNull;
28+
import hudson.Extension;
29+
import hudson.ExtensionList;
30+
import java.time.LocalDate;
31+
import java.util.Map;
32+
import java.util.Optional;
33+
import java.util.Set;
34+
import java.util.TreeSet;
35+
import java.util.logging.Level;
36+
import java.util.logging.Logger;
37+
import java.util.stream.Collectors;
38+
import jenkins.security.csp.AdvancedConfigurationDescriptor;
39+
import jenkins.security.csp.Contributor;
40+
import jenkins.security.csp.CspBuilder;
41+
import jenkins.security.csp.CspHeader;
42+
import jenkins.security.csp.CspHeaderDecider;
43+
import jenkins.security.csp.impl.CspConfiguration;
44+
import jenkins.telemetry.Telemetry;
45+
import net.sf.json.JSONObject;
46+
import org.kohsuke.accmod.Restricted;
47+
import org.kohsuke.accmod.restrictions.NoExternalUse;
48+
49+
/**
50+
* Collect information about Content Security Policy configuration.
51+
*
52+
*/
53+
@Restricted(NoExternalUse.class)
54+
@Extension
55+
public class ContentSecurityPolicy extends Telemetry {
56+
57+
private static final Logger LOGGER = Logger.getLogger(ContentSecurityPolicy.class.getName());
58+
59+
@NonNull
60+
@Override
61+
public String getDisplayName() {
62+
return "Content Security Policy";
63+
}
64+
65+
@NonNull
66+
@Override
67+
public LocalDate getStart() {
68+
return LocalDate.of(2025, 12, 1);
69+
}
70+
71+
@NonNull
72+
@Override
73+
public LocalDate getEnd() {
74+
return LocalDate.of(2026, 6, 1);
75+
}
76+
77+
@Override
78+
public JSONObject createContent() {
79+
final JSONObject data = new JSONObject();
80+
data.put("enforce", ExtensionList.lookupSingleton(CspConfiguration.class).isEnforce());
81+
final Optional<CspHeaderDecider> decider = CspHeaderDecider.getCurrentDecider();
82+
data.put("decider", decider.map(Object::getClass).map(Class::getName).orElse(null));
83+
data.put("header", decider.map(CspHeaderDecider::decide).filter(Optional::isPresent).map(Optional::get).map(CspHeader::getHeaderName).orElse(null));
84+
85+
Set<String> contributors = new TreeSet<>();
86+
ExtensionList.lookup(Contributor.class).stream().map(Contributor::getClass).map(Class::getName).forEach(contributors::add);
87+
data.put("contributors", contributors);
88+
89+
Set<String> configurations = new TreeSet<>();
90+
ExtensionList.lookup(AdvancedConfigurationDescriptor.class).stream().map(AdvancedConfigurationDescriptor::getClass).map(Class::getName).forEach(configurations::add);
91+
data.put("configurations", configurations);
92+
93+
try {
94+
Map<String, Map<String, Integer>> directivesSize = new CspBuilder().withDefaultContributions().getMergedDirectives().stream()
95+
.map(d -> Map.entry(d.name(), Map.of("entries", d.values().size(), "chars", String.join(" ", d.values()).length())))
96+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
97+
data.put("directivesSize", directivesSize);
98+
} catch (RuntimeException ex) {
99+
LOGGER.log(Level.FINE, "Error during directive processing", ex);
100+
}
101+
102+
data.put("components", buildComponentInformation());
103+
return data;
104+
}
105+
}

core/src/main/java/jenkins/telemetry/impl/JavaSystemProperties.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ public String getDisplayName() {
7070
@NonNull
7171
@Override
7272
public LocalDate getStart() {
73-
return LocalDate.of(2023, 12, 17);
73+
return LocalDate.of(2026, 1, 4);
7474
}
7575

7676
@NonNull
7777
@Override
7878
public LocalDate getEnd() {
79-
return LocalDate.of(2024, 4, 1);
79+
return LocalDate.of(2026, 4, 1);
8080
}
8181

8282
@Override

core/src/main/resources/hudson/model/Run/console-log.jelly

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
</j:when>
3030
<!-- output is completed now. -->
3131
<j:otherwise>
32-
<pre class="console-output">
32+
<pre id="out" class="console-output">
3333
<st:getOutput var="output" />
3434
<j:whitespace>${it.writeLogTo(offset,output)}</j:whitespace>
3535
</pre>

core/src/main/resources/hudson/slaves/Cloud/sidepanel.jelly

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ THE SOFTWARE.
2727
<l:header />
2828
<l:side-panel>
2929
<l:tasks>
30-
<l:task contextMenu="false" href="." icon="symbol-computer" title="${%Status}"/>
31-
<l:task href="configure" icon="symbol-settings"
30+
<j:set var="url" value="${h.getNearestAncestorUrl(request2,it)}"/>
31+
<l:task contextMenu="false" href="${url}/" icon="symbol-computer" title="${%Status}"/>
32+
<l:task href="${url}/configure" icon="symbol-settings"
3233
title="${app.hasPermission(app.ADMINISTER) ? '%Configure' : '%View Configuration'}"/>
33-
<l:delete permission="${app.ADMINISTER}" title="${%Delete Cloud}" message="${%delete.cloud(it.displayName)}"/>
34+
<l:delete permission="${app.ADMINISTER}" title="${%Delete Cloud}" message="${%delete.cloud(it.displayName)}" urlPrefix="${url}"/>
3435
<t:actions />
3536
</l:tasks>
3637
<j:forEach var="action" items="${it.allActions}">

core/src/main/resources/jenkins/security/csp/impl/CspDecorator/httpHeaders.jelly

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ THE SOFTWARE.
2323
-->
2424
<?jelly escape-by-default='true'?>
2525
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
26-
<st:setHeader name="${it.getContentSecurityPolicyHeaderName()}" value="${it.getContentSecurityPolicyHeaderValue(request2)}" />
27-
<st:setHeader name="Reporting-Endpoints" value="${it.getReportingEndpointsHeaderValue(request2)}" />
26+
<j:set var="cspHeaderName" value="${it.getContentSecurityPolicyHeaderName()}"/>
27+
<j:if test="${cspHeaderName != null}">
28+
<st:setHeader name="${cspHeaderName}" value="${it.getContentSecurityPolicyHeaderValue(request2)}" />
29+
<st:setHeader name="Reporting-Endpoints" value="${it.getReportingEndpointsHeaderValue(request2)}" />
30+
</j:if>
2831
</j:jelly>

core/src/main/resources/jenkins/security/csp/impl/SystemPropertyHeaderDecider/message.jelly

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,14 @@ THE SOFTWARE.
2323
-->
2424
<?jelly escape-by-default='true'?>
2525
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
26-
<f:description>${%blurb(it.headerName)}</f:description>
26+
<f:description>
27+
<j:choose>
28+
<j:when test="${it.headerName != null}">
29+
${%blurb(it.headerName)}
30+
</j:when>
31+
<j:otherwise>
32+
${%blurbUnset}
33+
</j:otherwise>
34+
</j:choose>
35+
</f:description>
2736
</j:jelly>

0 commit comments

Comments
 (0)