This is an automated email from the ASF dual-hosted git repository.
dongjoon-hyun pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.2 by this push:
new 484cd7148a01 [SPARK-57589][CORE] Support
`spark.ui.contentSecurityPolicy.enabled`
484cd7148a01 is described below
commit 484cd7148a01b93505bb43e6b619a77b54cdc67f
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Sun Jun 21 18:26:09 2026 -0700
[SPARK-57589][CORE] Support `spark.ui.contentSecurityPolicy.enabled`
### What changes were proposed in this pull request?
This PR adds a new config `spark.ui.contentSecurityPolicy.enabled` (default
`false`) that gates the new feature, `Content-Security-Policy` response header
introduced in SPARK-55252 and SPARK-55522. The header is now only set when the
config is enabled.
### Why are the changes needed?
To make the Spark UI `Content-Security-Policy` header opt-in, disabling it
by default in Apache Spark 4.2.0.
After testing this feature in Apache Spark 4.2.0, we can enable it at 4.3.0.
### Does this PR introduce _any_ user-facing change?
No behavior change because Spark UI no longer sends the
`Content-Security-Policy` header at Apache Spark 4.2.0. Previously, it was a
breaking change.
### How was this patch tested?
Pass the CIs.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 4.8)
Closes #56642 from dongjoon-hyun/dongjoon/eloquent-yonath-4682a1.
Authored-by: Dongjoon Hyun <[email protected]>
Signed-off-by: Dongjoon Hyun <[email protected]>
(cherry picked from commit 85fdee9f12ac93903e4a4bda01ea930d9ab3fd51)
Signed-off-by: Dongjoon Hyun <[email protected]>
(cherry picked from commit adaaa9d01b2dbd2afdbbb2a1be855a14511d056a)
Signed-off-by: Dongjoon Hyun <[email protected]>
---
.../main/scala/org/apache/spark/internal/config/UI.scala | 10 ++++++++++
.../scala/org/apache/spark/ui/HttpSecurityFilter.scala | 10 ++++++----
.../org/apache/spark/ui/HttpSecurityFilterSuite.scala | 16 +++++++++++++++-
docs/security.md | 10 ++++++++++
4 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/core/src/main/scala/org/apache/spark/internal/config/UI.scala
b/core/src/main/scala/org/apache/spark/internal/config/UI.scala
index 20c99794cd21..2e83c1f64f2e 100644
--- a/core/src/main/scala/org/apache/spark/internal/config/UI.scala
+++ b/core/src/main/scala/org/apache/spark/internal/config/UI.scala
@@ -136,6 +136,16 @@ private[spark] object UI {
.stringConf
.createOptional
+ val UI_CONTENT_SECURITY_POLICY_ENABLED =
+ ConfigBuilder("spark.ui.contentSecurityPolicy.enabled")
+ .doc("Whether to set the HTTP Content-Security-Policy (CSP) response
header for the " +
+ "Spark UI. When enabled, CSP restricts the sources from which the
browser is allowed " +
+ "to load resources, providing defense-in-depth against cross-site
scripting (XSS).")
+ .version("4.2.0")
+ .withBindingPolicy(ConfigBindingPolicy.NOT_APPLICABLE)
+ .booleanConf
+ .createWithDefault(false)
+
val UI_REQUEST_HEADER_SIZE = ConfigBuilder("spark.ui.requestHeaderSize")
.doc("Value for HTTP request header size in bytes.")
.version("2.2.3")
diff --git a/core/src/main/scala/org/apache/spark/ui/HttpSecurityFilter.scala
b/core/src/main/scala/org/apache/spark/ui/HttpSecurityFilter.scala
index a7e0bb683dd0..d06f2162d2f6 100644
--- a/core/src/main/scala/org/apache/spark/ui/HttpSecurityFilter.scala
+++ b/core/src/main/scala/org/apache/spark/ui/HttpSecurityFilter.scala
@@ -51,10 +51,12 @@ private class HttpSecurityFilter(
val cspNonce = CspNonce.generate()
try {
- hres.setHeader("Content-Security-Policy",
- s"default-src 'self'; script-src 'self' 'nonce-$cspNonce'; " +
- s"style-src 'self' 'unsafe-inline'; img-src 'self' data:; " +
- s"object-src 'none'; base-uri 'self';")
+ if (conf.get(UI_CONTENT_SECURITY_POLICY_ENABLED)) {
+ hres.setHeader("Content-Security-Policy",
+ s"default-src 'self'; script-src 'self' 'nonce-$cspNonce'; " +
+ s"style-src 'self' 'unsafe-inline'; img-src 'self' data:; " +
+ s"object-src 'none'; base-uri 'self';")
+ }
val requestUser = hreq.getRemoteUser()
diff --git
a/core/src/test/scala/org/apache/spark/ui/HttpSecurityFilterSuite.scala
b/core/src/test/scala/org/apache/spark/ui/HttpSecurityFilterSuite.scala
index 9965751c42cc..776f2e4496cc 100644
--- a/core/src/test/scala/org/apache/spark/ui/HttpSecurityFilterSuite.scala
+++ b/core/src/test/scala/org/apache/spark/ui/HttpSecurityFilterSuite.scala
@@ -119,6 +119,7 @@ class HttpSecurityFilterSuite extends SparkFunSuite {
.set(UI_X_XSS_PROTECTION, "xssProtection")
.set(UI_X_CONTENT_TYPE_OPTIONS, true)
.set(UI_STRICT_TRANSPORT_SECURITY, "tsec")
+ .set(UI_CONTENT_SECURITY_POLICY_ENABLED, true)
val secMgr = new SecurityManager(conf)
val req = mockRequest()
val res = mock(classOf[HttpServletResponse])
@@ -149,6 +150,19 @@ class HttpSecurityFilterSuite extends SparkFunSuite {
}
}
+ test("Content-Security-Policy header is not set by default") {
+ val conf = new SparkConf(false)
+ val secMgr = new SecurityManager(conf)
+ val req = mockRequest()
+ val res = mock(classOf[HttpServletResponse])
+ val chain = mock(classOf[FilterChain])
+
+ val filter = new HttpSecurityFilter(conf, secMgr)
+ filter.doFilter(req, res, chain)
+
+ verify(res, times(0)).setHeader(meq("Content-Security-Policy"), any())
+ }
+
test("doAs impersonation") {
val conf = new SparkConf(false)
.set(ACLS_ENABLE, true)
@@ -186,7 +200,7 @@ class HttpSecurityFilterSuite extends SparkFunSuite {
}
test("CSP nonce is available during chain.doFilter and cleared after") {
- val conf = new SparkConf(false)
+ val conf = new SparkConf(false).set(UI_CONTENT_SECURITY_POLICY_ENABLED,
true)
val secMgr = new SecurityManager(conf)
val req = mockRequest()
val res = mock(classOf[HttpServletResponse])
diff --git a/docs/security.md b/docs/security.md
index 484017df84fb..b3743d9a1b0a 100644
--- a/docs/security.md
+++ b/docs/security.md
@@ -803,6 +803,16 @@ Security.
</td>
<td>2.3.0</td>
</tr>
+<tr>
+ <td><code>spark.ui.contentSecurityPolicy.enabled</code></td>
+ <td><code>false</code></td>
+ <td>
+ When enabled, the Content-Security-Policy (CSP) HTTP response header is
set for the Spark UI,
+ restricting the sources from which the browser is allowed to load
resources as a
+ defense-in-depth measure against cross-site scripting (XSS).
+ </td>
+ <td>4.2.0</td>
+</tr>
</table>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]