This is an automated email from the ASF dual-hosted git repository.

brfrn169 pushed a commit to branch branch-2.2
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/branch-2.2 by this push:
     new 5e8d71c  HBASE-23303 Add security headers to REST server/info page 
(#843)
5e8d71c is described below

commit 5e8d71ca1e2915cacf4951833fb8c5f15dd16fbe
Author: Andor Molnár <an...@cloudera.com>
AuthorDate: Sun Dec 8 14:06:40 2019 +0100

    HBASE-23303 Add security headers to REST server/info page (#843)
    
    Signed-off-by: Toshihiro Suzuki <brfrn...@gmail.com>
    Signed-off-by: Sean Busbey <bus...@apache.org>
---
 .../hbase/http/ClickjackingPreventionFilter.java   |  47 +++++----
 .../org/apache/hadoop/hbase/http/HttpServer.java   |  11 ++-
 .../hadoop/hbase/http/SecurityHeadersFilter.java   |  81 +++++++++++++++
 .../hbase/http/TestSecurityHeadersFilter.java      | 106 ++++++++++++++++++++
 .../org/apache/hadoop/hbase/rest/RESTServer.java   |  62 +++++++-----
 .../hbase/rest/TestSecurityHeadersFilter.java      | 110 +++++++++++++++++++++
 6 files changed, 372 insertions(+), 45 deletions(-)

diff --git 
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/ClickjackingPreventionFilter.java
 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/ClickjackingPreventionFilter.java
index 9944d29..d2764f6 100644
--- 
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/ClickjackingPreventionFilter.java
+++ 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/ClickjackingPreventionFilter.java
@@ -17,6 +17,10 @@
  */
 package org.apache.hadoop.hbase.http;
 
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
@@ -24,32 +28,37 @@ import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
 
-import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.yetus.audience.InterfaceAudience;
 
 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
 public class ClickjackingPreventionFilter implements Filter {
+  private FilterConfig filterConfig;
+  private static final String DEFAULT_XFRAMEOPTIONS = "DENY";
 
-    private FilterConfig filterConfig;
-
-    @Override
-    public void init(FilterConfig filterConfig) throws ServletException {
-        this.filterConfig = filterConfig;
-    }
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+    this.filterConfig = filterConfig;
+  }
 
-    @Override
-    public void doFilter(ServletRequest req, ServletResponse res,
-                         FilterChain chain)
-            throws IOException, ServletException {
-        HttpServletResponse httpRes = (HttpServletResponse) res;
-        httpRes.addHeader("X-Frame-Options", 
filterConfig.getInitParameter("xframeoptions"));
-        chain.doFilter(req, res);
-    }
+  @Override
+  public void doFilter(ServletRequest req, ServletResponse res, FilterChain 
chain)
+        throws IOException, ServletException {
+    HttpServletResponse httpRes = (HttpServletResponse) res;
+    httpRes.addHeader("X-Frame-Options", 
filterConfig.getInitParameter("xframeoptions"));
+    chain.doFilter(req, res);
+  }
 
-    @Override
-    public void destroy() {
-    }
+  @Override
+  public void destroy() {
+  }
 
+  public static Map<String, String> getDefaultParameters(Configuration conf) {
+    Map<String, String> params = new HashMap<>();
+    params.put("xframeoptions", 
conf.get("hbase.http.filter.xframeoptions.mode",
+        DEFAULT_XFRAMEOPTIONS));
+    return params;
+  }
 }
diff --git 
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
index a34b26c..7fba6a6 100644
--- a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
+++ b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
@@ -560,10 +560,15 @@ public class HttpServer implements FilterContainer {
     addDefaultApps(contexts, appDir, conf);
 
     addGlobalFilter("safety", QuotingInputFilter.class.getName(), null);
-    Map<String, String> params = new HashMap<>();
-    params.put("xframeoptions", 
conf.get("hbase.http.filter.xframeoptions.mode", "DENY"));
+
     addGlobalFilter("clickjackingprevention",
-            ClickjackingPreventionFilter.class.getName(), params);
+        ClickjackingPreventionFilter.class.getName(),
+        ClickjackingPreventionFilter.getDefaultParameters(conf));
+
+    addGlobalFilter("securityheaders",
+        SecurityHeadersFilter.class.getName(),
+        SecurityHeadersFilter.getDefaultParameters(conf));
+
     final FilterInitializer[] initializers = getFilterInitializers(conf);
     if (initializers != null) {
       conf = new Configuration(conf);
diff --git 
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
new file mode 100644
index 0000000..b83fef1
--- /dev/null
+++ 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
@@ -0,0 +1,81 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.http;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
+public class SecurityHeadersFilter implements Filter {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(SecurityHeadersFilter.class);
+  private static final String DEFAULT_HSTS = "";
+  private static final String DEFAULT_CSP = "";
+  private FilterConfig filterConfig;
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+    this.filterConfig = filterConfig;
+    LOG.info("Added security headers filter");
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain)
+      throws IOException, ServletException {
+    HttpServletResponse httpResponse = (HttpServletResponse) response;
+    httpResponse.addHeader("X-Content-Type-Options", "nosniff");
+    httpResponse.addHeader("X-XSS-Protection", "1; mode=block");
+    String hsts = filterConfig.getInitParameter("hsts");
+    if (StringUtils.isNotBlank(hsts)) {
+      httpResponse.addHeader("Strict-Transport-Security", hsts);
+    }
+    String csp = filterConfig.getInitParameter("csp");
+    if (StringUtils.isNotBlank(csp)) {
+      httpResponse.addHeader("Content-Security-Policy", csp);
+    }
+    chain.doFilter(request, response);
+  }
+
+  @Override
+  public void destroy() {
+  }
+
+  public static Map<String, String> getDefaultParameters(Configuration conf) {
+    Map<String, String> params = new HashMap<>();
+    params.put("hsts", conf.get("hbase.http.filter.hsts.value",
+        DEFAULT_HSTS));
+    params.put("csp", conf.get("hbase.http.filter.csp.value",
+        DEFAULT_CSP));
+    return params;
+  }
+}
diff --git 
a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSecurityHeadersFilter.java
 
b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSecurityHeadersFilter.java
new file mode 100644
index 0000000..41a1235
--- /dev/null
+++ 
b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSecurityHeadersFilter.java
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.http;
+
+import static 
org.apache.hadoop.hbase.http.HttpServerFunctionalTest.createTestServer;
+import static 
org.apache.hadoop.hbase.http.HttpServerFunctionalTest.getServerURL;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.hamcrest.core.Is;
+import org.hamcrest.core.IsEqual;
+import org.junit.After;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category({HttpServerFunctionalTest.class, MediumTests.class})
+public class TestSecurityHeadersFilter {
+  private static URL baseUrl;
+  private HttpServer http;
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+      HBaseClassTestRule.forClass(TestSecurityHeadersFilter.class);
+
+  @After
+  public void tearDown() throws Exception {
+    http.stop();
+  }
+
+  @Test
+  public void testDefaultValues() throws Exception {
+    http = createTestServer();
+    http.start();
+    baseUrl = getServerURL(http);
+
+    URL url = new URL(baseUrl, "/");
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    assertThat(conn.getResponseCode(), equalTo(HttpURLConnection.HTTP_OK));
+
+    assertThat("Header 'X-Content-Type-Options' is missing",
+        conn.getHeaderField("X-Content-Type-Options"), is(not((String)null)));
+    assertThat(conn.getHeaderField("X-Content-Type-Options"), 
equalTo("nosniff"));
+    assertThat("Header 'X-XSS-Protection' is missing",
+        conn.getHeaderField("X-XSS-Protection"), is(not((String)null)));
+    assertThat("Header 'X-XSS-Protection' has invalid value",
+        conn.getHeaderField("X-XSS-Protection"), equalTo("1; mode=block"));
+
+    assertThat("Header 'Strict-Transport-Security' should be missing from 
response," +
+            "but it's present",
+        conn.getHeaderField("Strict-Transport-Security"), is((String)null));
+    assertThat("Header 'Content-Security-Policy' should be missing from 
response," +
+            "but it's present",
+        conn.getHeaderField("Content-Security-Policy"), is((String)null));
+  }
+
+  @Test
+  public void testHstsAndCspSettings() throws IOException {
+    Configuration conf = new Configuration();
+    conf.set("hbase.http.filter.hsts.value",
+        "max-age=63072000;includeSubDomains;preload");
+    conf.set("hbase.http.filter.csp.value",
+        "default-src https: data: 'unsafe-inline' 'unsafe-eval'");
+    http = createTestServer(conf);
+    http.start();
+    baseUrl = getServerURL(http);
+
+    URL url = new URL(baseUrl, "/");
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    assertThat(conn.getResponseCode(), equalTo(HttpURLConnection.HTTP_OK));
+
+    assertThat("Header 'Strict-Transport-Security' is missing from Rest 
response",
+        conn.getHeaderField("Strict-Transport-Security"), 
Is.is(not((String)null)));
+    assertThat("Header 'Strict-Transport-Security' has invalid value",
+        conn.getHeaderField("Strict-Transport-Security"),
+        IsEqual.equalTo("max-age=63072000;includeSubDomains;preload"));
+
+    assertThat("Header 'Content-Security-Policy' is missing from Rest 
response",
+        conn.getHeaderField("Content-Security-Policy"), 
Is.is(not((String)null)));
+    assertThat("Header 'Content-Security-Policy' has invalid value",
+        conn.getHeaderField("Content-Security-Policy"),
+        IsEqual.equalTo("default-src https: data: 'unsafe-inline' 
'unsafe-eval'"));
+  }
+}
diff --git 
a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java 
b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
index a73f280..c3d16ee 100644
--- a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
+++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
@@ -18,59 +18,56 @@
 
 package org.apache.hadoop.hbase.rest;
 
+import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
 import java.lang.management.ManagementFactory;
 import java.util.ArrayList;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
-import java.util.EnumSet;
 import java.util.concurrent.ArrayBlockingQueue;
-
-import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+import javax.servlet.DispatcherType;
 import org.apache.commons.lang3.ArrayUtils;
-import org.apache.yetus.audience.InterfaceAudience;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HBaseConfiguration;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.hadoop.hbase.http.ClickjackingPreventionFilter;
+import org.apache.hadoop.hbase.http.HttpServerUtil;
 import org.apache.hadoop.hbase.http.InfoServer;
+import org.apache.hadoop.hbase.http.SecurityHeadersFilter;
 import org.apache.hadoop.hbase.log.HBaseMarkers;
 import org.apache.hadoop.hbase.rest.filter.AuthFilter;
 import org.apache.hadoop.hbase.rest.filter.GzipFilter;
 import org.apache.hadoop.hbase.rest.filter.RestCsrfPreventionFilter;
 import org.apache.hadoop.hbase.security.UserProvider;
 import org.apache.hadoop.hbase.util.DNS;
-import org.apache.hadoop.hbase.http.HttpServerUtil;
 import org.apache.hadoop.hbase.util.Pair;
 import org.apache.hadoop.hbase.util.ReflectionUtils;
 import org.apache.hadoop.hbase.util.Strings;
 import org.apache.hadoop.hbase.util.VersionInfo;
-
-import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
-import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
-import org.apache.hbase.thirdparty.org.apache.commons.cli.HelpFormatter;
-import org.apache.hbase.thirdparty.org.apache.commons.cli.Options;
-import org.apache.hbase.thirdparty.org.apache.commons.cli.ParseException;
-import org.apache.hbase.thirdparty.org.apache.commons.cli.PosixParser;
-
+import org.apache.yetus.audience.InterfaceAudience;
 import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.jmx.MBeanContainer;
 import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.SecureRequestCustomizer;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.eclipse.jetty.jmx.MBeanContainer;
-import org.eclipse.jetty.servlet.FilterHolder;
-
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.servlet.ServletContainer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import javax.servlet.DispatcherType;
+import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
+import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
+import org.apache.hbase.thirdparty.org.apache.commons.cli.HelpFormatter;
+import org.apache.hbase.thirdparty.org.apache.commons.cli.Options;
+import org.apache.hbase.thirdparty.org.apache.commons.cli.ParseException;
+import org.apache.hbase.thirdparty.org.apache.commons.cli.PosixParser;
 
 /**
  * Main class for launching REST gateway as a servlet hosted by Jetty.
@@ -137,6 +134,23 @@ public class RESTServer implements Constants {
     }
   }
 
+  private void addClickjackingPreventionFilter(ServletContextHandler 
ctxHandler,
+      Configuration conf) {
+    FilterHolder holder = new FilterHolder();
+    holder.setName("clickjackingprevention");
+    holder.setClassName(ClickjackingPreventionFilter.class.getName());
+    
holder.setInitParameters(ClickjackingPreventionFilter.getDefaultParameters(conf));
+    ctxHandler.addFilter(holder, PATH_SPEC_ANY, 
EnumSet.allOf(DispatcherType.class));
+  }
+
+  private void addSecurityHeadersFilter(ServletContextHandler ctxHandler, 
Configuration conf) {
+    FilterHolder holder = new FilterHolder();
+    holder.setName("securityheaders");
+    holder.setClassName(SecurityHeadersFilter.class.getName());
+    holder.setInitParameters(SecurityHeadersFilter.getDefaultParameters(conf));
+    ctxHandler.addFilter(holder, PATH_SPEC_ANY, 
EnumSet.allOf(DispatcherType.class));
+  }
+
   // login the server principal (if using secure Hadoop)
   private static Pair<FilterHolder, Class<? extends ServletContainer>> 
loginServerPrincipal(
     UserProvider userProvider, Configuration conf) throws Exception {
@@ -352,6 +366,8 @@ public class RESTServer implements Constants {
       ctxHandler.addFilter(filter, PATH_SPEC_ANY, 
EnumSet.of(DispatcherType.REQUEST));
     }
     addCSRFFilter(ctxHandler, conf);
+    addClickjackingPreventionFilter(ctxHandler, conf);
+    addSecurityHeadersFilter(ctxHandler, conf);
     HttpServerUtil.constrainHttpMethods(ctxHandler, servlet.getConfiguration()
         .getBoolean(REST_HTTP_ALLOW_OPTIONS_METHOD, 
REST_HTTP_ALLOW_OPTIONS_METHOD_DEFAULT));
 
diff --git 
a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestSecurityHeadersFilter.java
 
b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestSecurityHeadersFilter.java
new file mode 100644
index 0000000..bf0c695
--- /dev/null
+++ 
b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestSecurityHeadersFilter.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.rest;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.rest.client.Client;
+import org.apache.hadoop.hbase.rest.client.Cluster;
+import org.apache.hadoop.hbase.rest.client.Response;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RestTests;
+import org.junit.After;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category({RestTests.class, MediumTests.class})
+public class TestSecurityHeadersFilter {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+      HBaseClassTestRule.forClass(TestSecurityHeadersFilter.class);
+
+  private static final HBaseTestingUtility TEST_UTIL = new 
HBaseTestingUtility();
+  private static final HBaseRESTTestingUtility REST_TEST_UTIL =
+      new HBaseRESTTestingUtility();
+  private static Client client;
+
+  @After
+  public void tearDown() throws Exception {
+    REST_TEST_UTIL.shutdownServletContainer();
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+  @Test
+  public void testDefaultValues() throws Exception {
+    TEST_UTIL.startMiniCluster();
+    REST_TEST_UTIL.startServletContainer(TEST_UTIL.getConfiguration());
+    client = new Client(new Cluster().add("localhost",
+        REST_TEST_UTIL.getServletPort()));
+
+    String path = "/version/cluster";
+    Response response = client.get(path);
+    assertThat(response.getCode(), equalTo(200));
+
+    assertThat("Header 'X-Content-Type-Options' is missing from Rest response",
+        response.getHeader("X-Content-Type-Options"), is(not((String)null)));
+    assertThat("Header 'X-Content-Type-Options' has invalid default value",
+        response.getHeader("X-Content-Type-Options"), equalTo("nosniff"));
+
+    assertThat("Header 'X-XSS-Protection' is missing from Rest response",
+        response.getHeader("X-XSS-Protection"), is(not((String)null)));
+    assertThat("Header 'X-XSS-Protection' has invalid default value",
+        response.getHeader("X-XSS-Protection"), equalTo("1; mode=block"));
+
+    assertThat("Header 'Strict-Transport-Security' should be missing from Rest 
response," +
+            "but it's present",
+        response.getHeader("Strict-Transport-Security"), is((String)null));
+    assertThat("Header 'Content-Security-Policy' should be missing from Rest 
response," +
+            "but it's present",
+        response.getHeader("Content-Security-Policy"), is((String)null));
+  }
+
+  @Test
+  public void testHstsAndCspSettings() throws Exception {
+    TEST_UTIL.getConfiguration().set("hbase.http.filter.hsts.value",
+        "max-age=63072000;includeSubDomains;preload");
+    TEST_UTIL.getConfiguration().set("hbase.http.filter.csp.value",
+        "default-src https: data: 'unsafe-inline' 'unsafe-eval'");
+    TEST_UTIL.startMiniCluster();
+    REST_TEST_UTIL.startServletContainer(TEST_UTIL.getConfiguration());
+    client = new Client(new Cluster().add("localhost",
+        REST_TEST_UTIL.getServletPort()));
+
+    String path = "/version/cluster";
+    Response response = client.get(path);
+    assertThat(response.getCode(), equalTo(200));
+
+    assertThat("Header 'Strict-Transport-Security' is missing from Rest 
response",
+        response.getHeader("Strict-Transport-Security"), 
is(not((String)null)));
+    assertThat("Header 'Strict-Transport-Security' has invalid value",
+        response.getHeader("Strict-Transport-Security"),
+        equalTo("max-age=63072000;includeSubDomains;preload"));
+
+    assertThat("Header 'Content-Security-Policy' is missing from Rest 
response",
+        response.getHeader("Content-Security-Policy"), is(not((String)null)));
+    assertThat("Header 'Content-Security-Policy' has invalid value",
+        response.getHeader("Content-Security-Policy"),
+        equalTo("default-src https: data: 'unsafe-inline' 'unsafe-eval'"));
+  }
+}

Reply via email to