Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* Deduplicated HTTP security headers between nginx and Flask-Talisman: nginx now hides Talisman's upstream
copies via `proxy_hide_header` and is the sole source of `Strict-Transport-Security`, `X-Frame-Options`,
`X-XSS-Protection`, `X-Content-Type-Options`, and `Referrer-Policy`, applied with `always` so they persist
on error responses too. Corrected `Referrer-Policy` to `strict-origin-when-cross-origin`.
* Redacted MySQL password from `superset` entrypoint's `test_db` output and disabled `-x` tracing around it,
preventing plaintext password exposure in `docker logs`/`docker service logs`.
* Upload `.py` source instead of `.pyc` bytecode to decouple host Python version. (#38, #41)
Expand Down
14 changes: 10 additions & 4 deletions services/superset/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ http {
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

proxy_hide_header Strict-Transport-Security;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header X-Frame-Options;
proxy_hide_header X-XSS-Protection;
proxy_hide_header Referrer-Policy;

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "no-referrer-when-downgrade";
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

server_tokens off;
tcp_nopush on;
Expand Down
36 changes: 36 additions & 0 deletions tests/testsuite/roles/testing/files/functional_superset.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,42 @@ def status_swarm(self) -> None:
swarm_info["ControlAvailable"] is True, \
"The testing localhost is supposed to be a Swarm manager, but it is not"

@decorators.Overlay.run_selected_methods_once
def status_headers(self) -> None:
expected_headers = {
"strict-transport-security": "max-age=31536000; includeSubDomains; preload",
"x-content-type-options": "nosniff",
"x-frame-options": "DENY",
"x-xss-protection": "1; mode=block",
"referrer-policy": "strict-origin-when-cross-origin",
}
for path in ("/", "/this-path-does-not-exist-xyz"):
command = f"""
curl \
--cacert /app/server_certificate.pem \
--silent \
--head \
https://{self.virtual_ip_address}{path}
"""
response = self.run_command_on_the_container(command).decode("utf-8")
response_lines = [line.strip() for line in response.splitlines() if ":" in line]
for header_name, expected_value in expected_headers.items():
matches = [
line.split(":", 1)[1].strip()
for line in response_lines
if line.split(":", 1)[0].strip().lower() == header_name
]
assert \
len(matches) == 1, \
f"""Expected exactly one {header_name} header on {path}, found {len(matches)}: {matches}
\nCommand: {command!r}\nReturned: {response!r}
"""
assert \
matches[0] == expected_value, \
f"""Expected {header_name} to be {expected_value!r} on {path}, got {matches[0]!r}
\nCommand: {command!r}\nReturned: {response!r}
"""

def run_query(self) -> float:
payload = '{"database_id": 1, "runAsync": true, "sql": "SELECT * FROM superset.logs;"}'
command = f"""
Expand Down
Loading