mirror of
https://github.com/apache/superset.git
synced 2026-07-03 05:15:35 +00:00
Compare commits
3 Commits
chore/ci/s
...
fix/versio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0052909f10 | ||
|
|
898b3c498f | ||
|
|
81a4665813 |
19
UPDATING.md
19
UPDATING.md
@@ -34,6 +34,25 @@ The embedded dashboard page now validates the origin of incoming `postMessage` e
|
|||||||
|
|
||||||
Enforcement only applies when the Allowed Domains list is non-empty. If the list is empty (the default), any origin is accepted, so there is no behavior change for embeds that did not configure Allowed Domains.
|
Enforcement only applies when the Allowed Domains list is non-empty. If the list is empty (the default), any origin is accepted, so there is no behavior change for embeds that did not configure Allowed Domains.
|
||||||
|
|
||||||
|
### New `EXPOSE_VERSION_INFO` config to control `/version` detail
|
||||||
|
|
||||||
|
A new `EXPOSE_VERSION_INFO` config option controls how much detail the unauthenticated `/version` endpoint returns. It defaults to `True`, which preserves the existing behavior: the endpoint returns the full version metadata, including the Git SHA, full SHA, build number, and branch name when available.
|
||||||
|
|
||||||
|
Operators who prefer not to expose build-specific details to unauthenticated callers can set the following in `superset_config.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
EXPOSE_VERSION_INFO = False
|
||||||
|
```
|
||||||
|
|
||||||
|
When disabled, `/version` returns only the human-readable `version_string` and omits the Git SHA, full SHA, build number, and branch name. Because the default is `True`, this change is non-breaking for existing deployments.
|
||||||
|
|
||||||
|
As an additional defense-in-depth hardening, Superset now sends a `Cross-Origin-Resource-Policy: same-site` response header by default via `DEFAULT_HTTP_HEADERS`. `same-site` is deliberately chosen over the stricter `same-origin` so that documented same-site embedding flows (e.g. the Embedded SDK, where a Superset subdomain is framed by a sibling application subdomain) continue to work unchanged. Deployments that serve Superset responses or static assets as subresources to a _cross-site_ origin may need to relax or remove this header. Because it is applied through `DEFAULT_HTTP_HEADERS`, the header is only set when a response does not already carry one, so it can be overridden per-response or by replacing the config value:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Relax to permit cross-site consumers, or set to "same-origin" to harden further.
|
||||||
|
DEFAULT_HTTP_HEADERS = {"Cross-Origin-Resource-Policy": "cross-origin"}
|
||||||
|
```
|
||||||
|
|
||||||
### Dataset import validates catalog against the target connection
|
### Dataset import validates catalog against the target connection
|
||||||
|
|
||||||
Importing a dataset now validates the `catalog` field against the target database connection. When the connection has multi-catalog disabled (`allow_multi_catalog` off) and the dataset's catalog is not the connection's default catalog, the import fails instead of silently persisting the non-default catalog. This matches the validation already enforced on the dataset update path and prevents imported datasets from querying an unintended database.
|
Importing a dataset now validates the `catalog` field against the target database connection. When the connection has multi-catalog disabled (`allow_multi_catalog` off) and the dataset's catalog is not the connection's default catalog, the import fails instead of silently persisting the non-default catalog. This matches the validation already enforced on the dataset update path and prevents imported datasets from querying an unintended database.
|
||||||
|
|||||||
@@ -156,6 +156,14 @@ VERSION_SHA = _try_json_readsha(VERSION_INFO_FILE, VERSION_SHA_LENGTH)
|
|||||||
# can be replaced at build time to expose build information.
|
# can be replaced at build time to expose build information.
|
||||||
BUILD_NUMBER = None
|
BUILD_NUMBER = None
|
||||||
|
|
||||||
|
# Controls how much detail the unauthenticated ``/version`` endpoint returns.
|
||||||
|
# When True (default, preserves existing behavior) the endpoint returns the full
|
||||||
|
# version metadata, including the Git SHA and branch name when available. Set to
|
||||||
|
# False to return only the human-readable version string and omit the Git SHA,
|
||||||
|
# full SHA, build number, and branch name, so deployment-specific build details
|
||||||
|
# are not exposed to unauthenticated callers.
|
||||||
|
EXPOSE_VERSION_INFO = True
|
||||||
|
|
||||||
# default viz used in chart explorer & SQL Lab explore
|
# default viz used in chart explorer & SQL Lab explore
|
||||||
DEFAULT_VIZ_TYPE = "table"
|
DEFAULT_VIZ_TYPE = "table"
|
||||||
|
|
||||||
@@ -1449,7 +1457,18 @@ CELERY_CONFIG: type[CeleryConfig] | None = CeleryConfig
|
|||||||
# within the app
|
# within the app
|
||||||
# OVERRIDE_HTTP_HEADERS: sets override values for HTTP headers. These values will
|
# OVERRIDE_HTTP_HEADERS: sets override values for HTTP headers. These values will
|
||||||
# override anything set within the app
|
# override anything set within the app
|
||||||
DEFAULT_HTTP_HEADERS: dict[str, Any] = {}
|
#
|
||||||
|
# As a defense-in-depth default, Superset sends a conservative
|
||||||
|
# `Cross-Origin-Resource-Policy` header on its responses. `same-site` is used
|
||||||
|
# (rather than the stricter `same-origin`) so that same-site embedding patterns
|
||||||
|
# such as the Embedded SDK, where a Superset subdomain is framed by a sibling
|
||||||
|
# application subdomain, keep working out of the box. Because this is set through
|
||||||
|
# DEFAULT_HTTP_HEADERS, the value is only applied when the response does not
|
||||||
|
# already carry the header, so operators can override it (per-response or by
|
||||||
|
# replacing this config value) to suit their cross-origin requirements.
|
||||||
|
DEFAULT_HTTP_HEADERS: dict[str, Any] = {
|
||||||
|
"Cross-Origin-Resource-Policy": "same-site",
|
||||||
|
}
|
||||||
OVERRIDE_HTTP_HEADERS: dict[str, Any] = {}
|
OVERRIDE_HTTP_HEADERS: dict[str, Any] = {}
|
||||||
HTTP_HEADERS: dict[str, Any] = {}
|
HTTP_HEADERS: dict[str, Any] = {}
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,17 @@ def health() -> FlaskResponse:
|
|||||||
@talisman(force_https=False)
|
@talisman(force_https=False)
|
||||||
def version() -> FlaskResponse:
|
def version() -> FlaskResponse:
|
||||||
"""
|
"""
|
||||||
Return comprehensive version information including Git SHA
|
Return version information for the running Superset instance.
|
||||||
and branch when available.
|
|
||||||
|
When ``EXPOSE_VERSION_INFO`` is True (default) this returns the full
|
||||||
|
version metadata, including the Git SHA and branch name when available.
|
||||||
|
When it is False, only the human-readable version string is returned and
|
||||||
|
build-specific details (Git SHA, full SHA, build number, branch name) are
|
||||||
|
omitted so they are not exposed to unauthenticated callers.
|
||||||
"""
|
"""
|
||||||
|
if not app.config.get("EXPOSE_VERSION_INFO", True):
|
||||||
|
return jsonify({"version_string": app.config.get("VERSION_STRING", "unknown")})
|
||||||
|
|
||||||
from superset.utils.version import get_version_metadata
|
from superset.utils.version import get_version_metadata
|
||||||
|
|
||||||
return jsonify(get_version_metadata())
|
return jsonify(get_version_metadata())
|
||||||
|
|||||||
@@ -312,3 +312,28 @@ def test_full_setting(
|
|||||||
assert dttm_col.is_dttm
|
assert dttm_col.is_dttm
|
||||||
assert dttm_col.python_date_format == "epoch_s"
|
assert dttm_col.python_date_format == "epoch_s"
|
||||||
assert dttm_col.expression == "CAST(dttm as INTEGER)"
|
assert dttm_col.expression == "CAST(dttm as INTEGER)"
|
||||||
|
|
||||||
|
|
||||||
|
def test_expose_version_info_defaults_to_true() -> None:
|
||||||
|
"""
|
||||||
|
The /version endpoint preserves its existing behavior by default. Operators
|
||||||
|
can set EXPOSE_VERSION_INFO = False to omit build-specific details.
|
||||||
|
"""
|
||||||
|
from superset import config
|
||||||
|
|
||||||
|
assert config.EXPOSE_VERSION_INFO is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_cross_origin_resource_policy_header() -> None:
|
||||||
|
"""
|
||||||
|
Superset ships a conservative `Cross-Origin-Resource-Policy: same-site`
|
||||||
|
default through DEFAULT_HTTP_HEADERS. `same-site` (rather than `same-origin`)
|
||||||
|
is chosen so documented same-site embedding flows, such as the Embedded SDK,
|
||||||
|
keep working while still providing a defense-in-depth default that operators
|
||||||
|
can override.
|
||||||
|
"""
|
||||||
|
from superset import config
|
||||||
|
|
||||||
|
assert (
|
||||||
|
config.DEFAULT_HTTP_HEADERS.get("Cross-Origin-Resource-Policy") == "same-site"
|
||||||
|
)
|
||||||
|
|||||||
70
tests/unit_tests/views/health_version_test.py
Normal file
70
tests/unit_tests/views/health_version_test.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# 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.
|
||||||
|
"""Unit tests for the ``/version`` endpoint and ``EXPOSE_VERSION_INFO`` gating."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
|
FULL_METADATA = {
|
||||||
|
"version_string": "1.2.3",
|
||||||
|
"version_sha": "abcd1234",
|
||||||
|
"full_sha": "abcd1234ef567890",
|
||||||
|
"build_number": "42",
|
||||||
|
"branch_name": "master",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_version_exposes_full_metadata_by_default(
|
||||||
|
client: Any,
|
||||||
|
mocker: MockerFixture,
|
||||||
|
) -> None:
|
||||||
|
"""With EXPOSE_VERSION_INFO True (default) the full metadata is returned."""
|
||||||
|
mocker.patch(
|
||||||
|
"superset.utils.version.get_version_metadata",
|
||||||
|
return_value=dict(FULL_METADATA),
|
||||||
|
)
|
||||||
|
client.application.config["EXPOSE_VERSION_INFO"] = True
|
||||||
|
|
||||||
|
response = client.get("/version")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
body = response.get_json()
|
||||||
|
assert body["version_string"] == "1.2.3"
|
||||||
|
assert body["version_sha"] == "abcd1234"
|
||||||
|
assert body["full_sha"] == "abcd1234ef567890"
|
||||||
|
assert body["branch_name"] == "master"
|
||||||
|
assert body["build_number"] == "42"
|
||||||
|
|
||||||
|
|
||||||
|
def test_version_redacts_details_when_flag_disabled(
|
||||||
|
client: Any,
|
||||||
|
mocker: MockerFixture,
|
||||||
|
) -> None:
|
||||||
|
"""With EXPOSE_VERSION_INFO False only VERSION_STRING from config is returned."""
|
||||||
|
client.application.config["EXPOSE_VERSION_INFO"] = False
|
||||||
|
client.application.config["VERSION_STRING"] = "1.2.3"
|
||||||
|
|
||||||
|
response = client.get("/version")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
body = response.get_json()
|
||||||
|
assert body == {"version_string": "1.2.3"}
|
||||||
|
assert "version_sha" not in body
|
||||||
|
assert "full_sha" not in body
|
||||||
|
assert "branch_name" not in body
|
||||||
|
assert "build_number" not in body
|
||||||
Reference in New Issue
Block a user