sadpandajoe commented on code in PR #45186:
URL: https://github.com/apache/superset/pull/45186#discussion_r4401139777


##########
superset-core/pyproject.toml:
##########
@@ -43,7 +43,7 @@ classifiers = [
 dependencies = [
     "flask-appbuilder>=5.2.0,<6",
     "isodate>=0.7.0",
-    "pyarrow>=16.0.0",
+    "pyarrow>=25.0.1, <26",

Review Comment:
   The `<26` ceiling conflicts with the application's `pyarrow>=26.0.0, <27` 
(root `pyproject.toml`) and the `pyarrow==26.0.0` pin in 
`requirements/base.txt`. Superset installs `apache-superset-core` (editable in 
the Docker build), so the resolver has no pyarrow version that satisfies both, 
and a published core release with this range cannot be installed beside the 
app. Could this drop the ceiling (and keep a floor that matches the advisory 
fix), as discussed earlier in this PR, so only the application pins the narrow 
window?



##########
UPDATING.md:
##########
@@ -24,6 +24,16 @@ assists people when migrating to a new version.
 
 ## Next
 
+- [45186](https://github.com/apache/superset/pull/45186): Sets pyarrow>=23.0.1

Review Comment:
   This entry no longer matches the diff: it says core gets `pyarrow>=23.0.1` 
with no upper ceiling, but `superset-core/pyproject.toml` now sets 
`pyarrow>=25.0.1, <26`. It also has stray line breaks that split 
`requirements/base.in` / `base.txt` into `equirements/...` on their own lines, 
so the rendered note is garbled for anyone reading upgrade notes. Could the 
entry be rewritten to state the actual core pyarrow range and the `msgpack` 
line in `base.in`, with the paths intact?



##########
docs/admin_docs/security/dependency-vulnerability-remediation.mdx:
##########
@@ -0,0 +1,97 @@
+---
+title: Dependency Vulnerability Remediation & Scanner Guidance
+sidebar_position: 3
+---
+
+# Dependency Vulnerability Remediation & Container Scanner Guidance
+
+This guide addresses third-party dependency vulnerabilities reported by 
container security scanners (such as Trivy, Grype, and Snyk) in Apache Superset 
environments, specifically regarding dependency constraint handling for 
cryptography, msgpack, and pyarrow (referencing issue #45087).
+
+## 1. Vulnerability Applicability & Risk Assessment
+
+When security scanners evaluate Apache Superset container images, findings 
frequently arise in transitive or pinned direct dependencies. The table below 
details the applicability and actual exploitability surface of recently 
reported findings:
+
+| Package | Finding ID | Severity | Upstream Fixed Version | Superset Target 
Floor | Superset Code Path & Exploitability Analysis |
+| :--- | :--- | :--- | :--- | :--- | :--- |
+| **cryptography** | CVE-2026-69247 | HIGH | `50.0.0` | `>=50.0.2` | 
Bleichenbacher timing and error decryption oracle in PKCS#7 EnvelopedData. 
Superset does not consume, parse, or decrypt PKCS#7 or S/MIME enveloped data. |
+| **cryptography** | CVE-2026-69249 | HIGH | `49.0.0` | `>=50.0.2` | X.509 
path-building algorithm denial of service with duplicate intermediate 
certificates. Superset parses leaf database SSL certificates using 
`load_pem_x509_certificate` and does not build untrusted X.509 certification 
paths during web request processing. |
+| **cryptography** | CVE-2026-69248 | MEDIUM | `49.0.0` | `>=50.0.2` | 
Wildcard-DNS name-constraint bypass in the cryptography X.509 certificate 
verifier (`cryptography.x509.verification.PolicyBuilder`). Superset parses leaf 
database SSL certificates using `load_pem_x509_certificate` and does not invoke 
cryptography's certificate verification or path validation APIs. TLS transport 
verification is performed by underlying network and database drivers via 
OpenSSL/Python `ssl`. |
+| **cryptography** | GHSA-537c-gmf6-5ccf (CVE-2026-34180) | HIGH | `48.0.1` | 
`>=50.0.2` | Vulnerabilities in OpenSSL bundled in binary wheels (remediated in 
upstream wheels from 48.0.1 and pinned to 50.0.2 in Superset locked 
requirements). In production container deployments following Option A, the 
runtime libraries are updated; raw cryptographic primitives are not exposed to 
untrusted external callers. |
+| **msgpack** | GHSA-6v7p-g79w-8964 (CVE-2026-57585) | HIGH | `1.2.1` | 
`>=1.2.3` | Out-of-bounds read and crash when reusing an Unpacker instance 
after a caught error. Superset uses msgpack exclusively for SQL Lab 
asynchronous query result serialization (`RESULTS_BACKEND_USE_MSGPACK`); the 
Unpacker is never reused across error boundaries in production code paths. |
+| **msgpack** | GHSA-j586-36cw-2gc2 | HIGH | `1.2.3` | `>=1.2.3` | Memory leak 
in the C extension when map key validation raises an exception during unpacking 
(triggers with default `strict_map_key=True` when a map key is not 
`str`/`bytes`). In Superset, msgpack deserialization via 
`msgpack.loads(payload, raw=False)` in `superset/views/utils.py` uses this 
default on cached query results from the results backend, whose provenance is 
internal Celery worker execution rather than direct user input. Operators 
should upgrade to `msgpack>=1.2.3` (Option A) to eliminate the leak path 
directly rather than relying on payload non-reachability. |
+| **pyarrow** | CVE-2026-25087 | HIGH | `23.0.1` | `>=25.0.1` | Use-after-free 
in the Arrow C++ IPC file reader with pre-buffering (GHSA-rgxp-2hwp-jwgg). 
Superset invokes the Parquet reader for user data uploads and the IPC stream 
reader (`RecordBatchStreamReader`) for SQL Lab asynchronous query result 
serialization; it does not invoke the IPC file reader (`RecordBatchFileReader` 
with pre-buffering). Upstream fixes start at `23.0.1`, with `>=25.0.1` enforced 
as Superset target floor in this PR. |
+
+## 2. Dependency Constraint Architecture
+
+Apache Superset manages dependencies across two layers:
+
+1. **Package Declarations (`pyproject.toml` and 
`superset-core/pyproject.toml`)**:
+   Specifies flexible semantic version ranges to avoid dependency resolver 
conflicts in downstream custom deployments.
+   - `cryptography` (`pyproject.toml`): `>=50.0.2, <51.0.0` (enforces 
remediated releases covering PKCS#7, path-building, certificate verification 
name constraints, and wheel OpenSSL advisories).
+   - `msgpack` (`pyproject.toml`): `>=1.2.3, <1.3` (enforces remediated 
releases covering both GHSA-6v7p-g79w-8964, fixed in 1.2.1, and 
GHSA-j586-36cw-2gc2, fixed in 1.2.3).
+   - `pyarrow` (`pyproject.toml` and `superset-core/pyproject.toml`): 
`>=25.0.1, <26` (enforces modern non-vulnerable engines in both core and main 
packages).

Review Comment:
   This says the root `pyproject.toml` declares `pyarrow>=25.0.1, <26`, but it 
declares `pyarrow>=26.0.0, <27` and `requirements/base.txt` pins 26.0.0. An 
operator following this guide would pin an older Arrow than the application 
actually requires and hit a conflict with the shipped constraints. Could the 
line state the real root range and the real `superset-core` range separately?



##########
tests/unit_tests/db_engine_specs/test_snowflake.py:
##########
@@ -879,3 +879,239 @@ def 
test_extended_aggregation_func_median_uses_native_snowflake_syntax() -> None
 
     for aggregate in ("STDDEV_SAMP", "VAR_SAMP"):
         assert SnowflakeEngineSpec.get_extended_aggregation_func(aggregate) is 
not None
+
+
+def test_snowflake_parameters_json_schema() -> None:
+    """
+    Verify parameters_json_schema constructs OpenAPI schema via
+    APISpec and MarshmallowPlugin.
+    """
+    from superset.db_engine_specs.snowflake import SnowflakeEngineSpec
+
+    schema = SnowflakeEngineSpec.parameters_json_schema()
+    assert isinstance(schema, dict)
+    assert "properties" in schema
+    assert "account" in schema["properties"]
+
+
+def test_snowflake_update_params_from_encrypted_extra_keypair() -> None:
+    """
+    Verify update_params_from_encrypted_extra parses PEM private key and sets 
DER bytes.
+    Protects against missing cryptography.hazmat imports.
+    """
+    from cryptography.hazmat.primitives import serialization
+    from cryptography.hazmat.primitives.asymmetric import rsa
+
+    from superset.db_engine_specs.snowflake import SnowflakeEngineSpec
+
+    private_key = rsa.generate_private_key(
+        public_exponent=65537,
+        key_size=2048,
+    )
+    pem_bytes = private_key.private_bytes(
+        encoding=serialization.Encoding.PEM,
+        format=serialization.PrivateFormat.PKCS8,
+        encryption_algorithm=serialization.NoEncryption(),
+    )
+
+    mock_db = mock.MagicMock()
+    mock_db.encrypted_extra = json.dumps(
+        {
+            "auth_method": "keypair",
+            "auth_params": {
+                "privatekey_body": pem_bytes.decode("utf-8"),
+            },
+        }
+    )
+
+    params: dict[str, Any] = {}
+    SnowflakeEngineSpec.update_params_from_encrypted_extra(mock_db, params)
+
+    expected_der = private_key.private_bytes(
+        encoding=serialization.Encoding.DER,
+        format=serialization.PrivateFormat.PKCS8,
+        encryption_algorithm=serialization.NoEncryption(),
+    )
+
+    assert "connect_args" in params
+    assert "private_key" in params["connect_args"]
+    assert params["connect_args"]["private_key"] == expected_der
+    assert not params["connect_args"]["private_key"].startswith(b"-----BEGIN")
+
+    # Verify that the emitted bytes are valid DER-deserializable PKCS8 bytes
+    deserialized_key = serialization.load_der_private_key(
+        params["connect_args"]["private_key"],
+        password=None,
+    )
+    assert deserialized_key.private_numbers().d == 
private_key.private_numbers().d
+
+
+def test_snowflake_update_params_from_encrypted_extra_keypair_passphrase() -> 
None:
+    """
+    Verify update_params_from_encrypted_extra decrypts password-protected PEM
+    private key and produces unencrypted DER-encoded PKCS8 bytes.
+    """
+    from cryptography.hazmat.primitives import serialization
+    from cryptography.hazmat.primitives.asymmetric import rsa
+
+    from superset.db_engine_specs.snowflake import SnowflakeEngineSpec
+
+    private_key = rsa.generate_private_key(
+        public_exponent=65537,
+        key_size=2048,
+    )
+    passphrase = b"test_secret_passphrase"
+    encrypted_pem = private_key.private_bytes(
+        encoding=serialization.Encoding.PEM,
+        format=serialization.PrivateFormat.PKCS8,
+        encryption_algorithm=serialization.BestAvailableEncryption(passphrase),
+    )
+
+    mock_db = mock.MagicMock()
+    mock_db.encrypted_extra = json.dumps(
+        {
+            "auth_method": "keypair",
+            "auth_params": {
+                "privatekey_body": encrypted_pem.decode("utf-8"),
+                "privatekey_pass": passphrase.decode("utf-8"),
+            },
+        }
+    )
+
+    params: dict[str, Any] = {}
+    SnowflakeEngineSpec.update_params_from_encrypted_extra(mock_db, params)
+
+    expected_der = private_key.private_bytes(
+        encoding=serialization.Encoding.DER,
+        format=serialization.PrivateFormat.PKCS8,
+        encryption_algorithm=serialization.NoEncryption(),
+    )
+
+    assert "connect_args" in params
+    assert "private_key" in params["connect_args"]
+    assert params["connect_args"]["private_key"] == expected_der
+    assert not params["connect_args"]["private_key"].startswith(b"-----BEGIN")
+
+    deserialized_key = serialization.load_der_private_key(
+        params["connect_args"]["private_key"],
+        password=None,
+    )
+    assert deserialized_key.private_numbers().d == 
private_key.private_numbers().d
+
+
[email protected]("auth_params", [None, {}])

Review Comment:
   The `None` case fails: with `"auth_params": null`, 
`encrypted_extra.get("auth_params", {})` returns `None` (the default only 
applies when the key is absent), so `auth_params.get("privatekey_body")` raises 
`AttributeError` before reaching the new `ValueError`, and 
`pytest.raises(ValueError, ...)` does not match. Should the spec normalize null 
`auth_params` (e.g. `... or {}`) so it raises the intended validation error, or 
should this case be dropped?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to