diff --git a/hawtio-system/src/main/java/io/hawt/web/filters/ContentSecurityPolicyFilter.java b/hawtio-system/src/main/java/io/hawt/web/filters/ContentSecurityPolicyFilter.java index 3d53b16f82..057dfa3f70 100644 --- a/hawtio-system/src/main/java/io/hawt/web/filters/ContentSecurityPolicyFilter.java +++ b/hawtio-system/src/main/java/io/hawt/web/filters/ContentSecurityPolicyFilter.java @@ -47,6 +47,8 @@ public void init(FilterConfig filterConfig) throws ServletException { List scriptSrc = new ArrayList<>(List.of("'self'")); List styleSrc = new ArrayList<>(List.of("'self'")); List workerSrc = new ArrayList<>(List.of("'self'")); + List scriptSrcElem = new ArrayList<>(List.of("'self'")); + List styleSrcElem = new ArrayList<>(List.of("'self'")); List frameAncestors = new ArrayList<>(); if (isXFrameSameOriginAllowed()) { @@ -57,6 +59,11 @@ public void init(FilterConfig filterConfig) throws ServletException { //necessary for monaco-editor to load properly: styleSrc.add("'unsafe-inline'"); + styleSrcElem.add("'unsafe-inline'"); + workerSrc.add("blob:"); + styleSrcElem.add("https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/"); + fontSrc.add("https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/"); + scriptSrcElem.add("https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/"); // add keycloak server as safe source for connect, script, frame and prefetch String keycloakConfigFile = getConfigParameter(KeycloakServlet.KEYCLOAK_CLIENT_CONFIG); if (System.getProperty(KeycloakServlet.HAWTIO_KEYCLOAK_CLIENT_CONFIG) != null) { @@ -86,14 +93,14 @@ public void init(FilterConfig filterConfig) throws ServletException { // add OIDC provider as safe source for connect, script, frame and prefetch AuthenticationConfiguration authConfig - = AuthenticationConfiguration.getConfiguration(filterConfig.getServletContext()); + = AuthenticationConfiguration.getConfiguration(filterConfig.getServletContext()); if (authConfig.isEnabled() && authConfig.getOidcConfiguration() != null - && authConfig.getOidcConfiguration().isEnabled()) { + && authConfig.getOidcConfiguration().isEnabled()) { OidcConfiguration oidcConfiguration = authConfig.getOidcConfiguration(); URL url = oidcConfiguration.getProviderURL(); if (url != null) { String oidcSrc = String.format("%s://%s%s", url.getProtocol(), - url.getHost(), (url.getPort() > 0 ? ":" + url.getPort() : "")); + url.getHost(), (url.getPort() > 0 ? ":" + url.getPort() : "")); connectSrc.add(oidcSrc); frameSrc.add(oidcSrc); scriptSrc.add(oidcSrc); @@ -113,6 +120,8 @@ public void init(FilterConfig filterConfig) throws ServletException { addPolicy(builder, "object-src", objectSrc); addPolicy(builder, "worker-src", workerSrc); addPolicy(builder, "frame-ancestors", frameAncestors); + addPolicy(builder, "script-src-elem", scriptSrcElem); + addPolicy(builder, "style-src-elem", styleSrcElem); policy = builder.toString().trim(); policy = policy.substring(0, policy.length() - 1); diff --git a/hawtio-system/src/test/java/io/hawt/web/filters/ContentSecurityPolicyFilterTest.java b/hawtio-system/src/test/java/io/hawt/web/filters/ContentSecurityPolicyFilterTest.java index 38ec2e1990..d2cbcc00ba 100644 --- a/hawtio-system/src/test/java/io/hawt/web/filters/ContentSecurityPolicyFilterTest.java +++ b/hawtio-system/src/test/java/io/hawt/web/filters/ContentSecurityPolicyFilterTest.java @@ -57,10 +57,12 @@ public void shouldSetHeader() throws Exception { // then verify(response).addHeader("Content-Security-Policy", "default-src 'self'; script-src 'self'; " - + "style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; " + + "style-src 'self' 'unsafe-inline'; font-src 'self' data: https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; img-src 'self' data:; " + "connect-src 'self'; frame-src 'self'; " - + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self'; " - + "frame-ancestors 'none'"); + + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self' blob:; " + + "frame-ancestors 'none'; " + + "script-src-elem 'self' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; " + + "style-src-elem 'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/"); } @Test @@ -73,10 +75,12 @@ public void shouldSetHeaderWithKeycloakServerWhenConfigParameterIsSet() throws E // then verify(response).addHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' http://localhost:8180; " - + "style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; " + + "style-src 'self' 'unsafe-inline'; font-src 'self' data: https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; img-src 'self' data:; " + "connect-src 'self' http://localhost:8180; frame-src 'self' http://localhost:8180; " - + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self'; " - + "frame-ancestors 'none'"); + + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self' blob:; " + + "frame-ancestors 'none'; " + + "script-src-elem 'self' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; " + + "style-src-elem 'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/"); } @Test @@ -89,10 +93,12 @@ public void shouldSetHeaderWithKeycloakServerWhenSystemPropertyIsSet() throws Ex // then verify(response).addHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' http://localhost:8180; " - + "style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; " + + "style-src 'self' 'unsafe-inline'; font-src 'self' data: https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; img-src 'self' data:; " + "connect-src 'self' http://localhost:8180; frame-src 'self' http://localhost:8180; " - + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self'; " - + "frame-ancestors 'none'"); + + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self' blob:; " + + "frame-ancestors 'none'; " + + "script-src-elem 'self' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; " + + "style-src-elem 'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/"); } @Test @@ -101,7 +107,7 @@ public void shouldSetHeaderWithOidcProvider() throws Exception { AuthenticationConfiguration authConfig = AuthenticationConfiguration.getConfiguration(servletContext); when(servletContext.getAttribute(AuthenticationConfiguration.AUTHENTICATION_CONFIGURATION)).thenReturn(authConfig); when(configManager.get(AuthenticationConfiguration.OIDC_CLIENT_CONFIG)) - .thenReturn(Optional.ofNullable(oidcConfigFile)); + .thenReturn(Optional.ofNullable(oidcConfigFile)); authConfig.configureOidc(); contentSecurityPolicyFilter.init(filterConfig); // when @@ -109,10 +115,12 @@ public void shouldSetHeaderWithOidcProvider() throws Exception { // then verify(response).addHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' https://login.microsoftonline.com; " - + "style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; " + + "style-src 'self' 'unsafe-inline'; font-src 'self' data: https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; img-src 'self' data:; " + "connect-src 'self' https://login.microsoftonline.com; frame-src 'self' https://login.microsoftonline.com; " - + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self'; " - + "frame-ancestors 'none'"); + + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self' blob:; " + + "frame-ancestors 'none'; " + + "script-src-elem 'self' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; " + + "style-src-elem 'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/"); } @Test @@ -124,10 +132,13 @@ public void shouldNotNPEWithBlankStringAsKeycloakConfigFile() throws Exception { contentSecurityPolicyFilter.addHeaders(request, response); // then verify(response).addHeader(eq("Content-Security-Policy"), eq( - "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; " - + "img-src 'self' data:; connect-src 'self'; frame-src 'self'; " - + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self'; " - + "frame-ancestors 'none'")); + "default-src 'self'; script-src 'self'; " + + "style-src 'self' 'unsafe-inline'; font-src 'self' data: https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; " + + "img-src 'self' data:; connect-src 'self'; frame-src 'self'; " + + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self' blob:; " + + "frame-ancestors 'none'; " + + "script-src-elem 'self' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; " + + "style-src-elem 'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/")); } @Test @@ -140,9 +151,11 @@ public void shouldSetHeaderWithFrameAncestorsSelfWhenConfigParameterIsSet() thro // then verify(response).addHeader("Content-Security-Policy", "default-src 'self'; script-src 'self'; " - + "style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; " + + "style-src 'self' 'unsafe-inline'; font-src 'self' data: https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; img-src 'self' data:; " + "connect-src 'self'; frame-src 'self'; " - + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self'; " - + "frame-ancestors 'self'"); + + "manifest-src 'self'; media-src 'self'; object-src 'self'; worker-src 'self' blob:; " + + "frame-ancestors 'self'; " + + "script-src-elem 'self' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/; " + + "style-src-elem 'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/"); } }