aminghadersohi commented on code in PR #45350:
URL: https://github.com/apache/superset/pull/45350#discussion_r4332802163


##########
superset/commands/database/utils.py:
##########
@@ -182,6 +183,55 @@ def oauth2_endpoint_rebind_unsafe(
     return secret_reused and endpoint_changed
 
 
+def encrypted_extra_secrets_reused(
+    sensitive_fields: set[str],
+    existing_encrypted_extra: str | None,
+    submitted_masked_encrypted_extra: str | None,
+    *,
+    submitted: bool,
+) -> bool:
+    """
+    Whether an update would carry a stored ``encrypted_extra`` secret over.
+
+    A stored secret (a non-empty value at one of ``sensitive_fields``) is 
reused
+    when the update leaves ``masked_encrypted_extra`` out -- the stored blob 
stays
+    attached as is -- or sends the mask at that secret's path, which
+    ``unmask_encrypted_extra`` restores verbatim. Judged per secret, not by
+    comparing whole blobs: any other field changing in the same save must not 
hide
+    a still-masked secret, and a secret re-entered with its stored value is a 
fresh
+    one the caller is entitled to attach. A dropped path drops the secret.
+    """
+    try:
+        existing = json.loads(existing_encrypted_extra or "{}")
+    except (TypeError, ValueError):
+        return False
+    if not isinstance(existing, dict):
+        return False
+
+    def is_stored(field: str) -> bool:
+        return any(match.value for match in parse(field).find(existing))
+
+    if not any(
+        match.value
+        for field in sensitive_fields
+        for match in parse(field).find(existing)
+    ):
+        return False  # nothing stored to carry over

Review Comment:
   Druid accepts JSON `0`/`false` passwords and Requests serializes them to 
nonempty text. Both truthiness checks label these as absent, so a masked 
password survives a host change; checking for null/empty values keeps the guard 
aligned with the driver.
   
   ```suggestion
       def is_stored(field: str) -> bool:
           return any(
               match.value not in (None, "") for match in 
parse(field).find(existing)
           )
   
       if not any(is_stored(field) for field in sensitive_fields):
           return False  # nothing stored to carry over
   ```



##########
tests/unit_tests/commands/databases/update_test.py:
##########
@@ -1285,3 +1291,108 @@ def 
test_update_oauth2_unresolvable_endpoints_purges_tokens(
     command._handle_oauth2()
 
     purge_oauth2_tokens.assert_called_once()
+
+
+def _snowflake_like_database(
+    mocker: MockerFixture, encrypted_extra: str, password: str | None
+) -> MagicMock:
+    """An existing connection whose engine masks the real sensitive paths."""
+    from superset.db_engine_specs.base import BaseEngineSpec
+
+    class Spec(BaseEngineSpec):
+        encrypted_extra_sensitive_fields = {
+            "$.oauth2_client_info.secret": "OAuth2 Client Secret",
+            "$.auth_params.privatekey_body": "Private Key Body",
+        }
+
+    model = mocker.MagicMock(allow_multi_catalog=False)
+    model.sqlalchemy_uri = "snowflake://svc:XXXXXXXXXX@old-account/DB"
+    model.password = password
+    model.extra = "{}"
+    model.encrypted_extra = encrypted_extra
+    model.ssh_tunnel = None
+    model.db_engine_spec = Spec
+    model.get_default_catalog.return_value = "DB"
+    model.id = 1
+    return model
+
+
[email protected](
+    "stored,submitted,password",
+    [
+        # A scope change alongside the move used to hide the masked secret.
+        (
+            json.dumps({"oauth2_client_info": {"id": "c", "secret": "real"}}),
+            {"oauth2_client_info": {"id": "c", "secret": PASSWORD_MASK, 
"scope": "x"}},
+            "real-pw",
+        ),
+        # Compact JSON stored on create never equalled the re-serialized form.
+        (
+            '{"oauth2_client_info":{"id":"c","secret":"real"}}',
+            {"oauth2_client_info": {"id": "c", "secret": PASSWORD_MASK}},
+            "real-pw",
+        ),
+        # Key pair, no URI password: only the encrypted_extra check guards it.
+        (
+            json.dumps({"auth_params": {"privatekey_body": "PEM"}}),
+            {"auth_params": {"privatekey_body": PASSWORD_MASK}, "auth_method": 
"kp"},
+            None,
+        ),
+    ],
+)
+def test_update_move_refuses_a_masked_secret_whatever_else_changes(
+    mocker: MockerFixture,
+    stored: str,
+    submitted: dict[str, Any],
+    password: str | None,
+) -> None:
+    """
+    A move keeps no stored secret the submission leaves masked (sc-126869).

Review Comment:
   Nit: Please drop the private tracker ids from the comments/docstrings in 
both test files; these identifiers land in the public source.



-- 
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