codeant-ai-for-open-source[bot] commented on code in PR #41606:
URL: https://github.com/apache/superset/pull/41606#discussion_r3556163069


##########
superset/mcp_service/dashboard/schemas.py:
##########
@@ -1028,6 +1028,291 @@ class UpdateDashboardResponse(BaseModel):
     )
 
 
+class ManageDashboardOwnersRequest(BaseModel):
+    """Request schema for explicit add/remove dashboard owner management.
+
+    Unlike ``update_dashboard``'s dropped ``owners`` field (a full-replacement
+    list with no safety guard, so an empty or partial list could silently
+    orphan a dashboard), this tool takes explicit add/remove operations and
+    rejects any change that would leave the dashboard with zero owners.
+
+    "Owners" here means USER-type entries in the dashboard's Subject-based
+    ``editors`` list (the ownership model apache/superset#38831 introduced,
+    replacing the legacy ``owners`` relationship). Any ROLE- or GROUP-type
+    editors already on the dashboard are left untouched by this tool.
+    """
+
+    identifier: int | str = Field(
+        ...,
+        description=(
+            "Dashboard ID (integer), UUID, or slug. Same identifier shape "
+            "accepted by ``get_dashboard_info``."
+        ),
+    )
+    add_owner_ids: List[int] = Field(
+        default_factory=list,
+        description=(
+            "User IDs to add as dashboard owners. Discover IDs with 
``find_users``."
+        ),
+    )
+    remove_owner_ids: List[int] = Field(
+        default_factory=list,
+        description=(
+            "User IDs to remove from dashboard owners. Rejected if it would "
+            "leave the dashboard with zero owners, or if an ID is not "
+            "currently an owner."
+        ),
+    )
+
+    @model_validator(mode="after")
+    def _validate_operations(self) -> "ManageDashboardOwnersRequest":
+        if not self.add_owner_ids and not self.remove_owner_ids:
+            raise ValueError(
+                "At least one of add_owner_ids or remove_owner_ids is 
required."
+            )
+        overlap = sorted(set(self.add_owner_ids) & set(self.remove_owner_ids))
+        if overlap:
+            raise ValueError(
+                "User IDs cannot appear in both add_owner_ids and "
+                f"remove_owner_ids: {overlap}."
+            )
+        return self
+
+
+class ManageDashboardOwnersResponse(BaseModel):
+    """Response schema for ``manage_dashboard_owners``."""
+
+    owners: List[SubjectInfo] = Field(
+        default_factory=list,
+        description=(
+            "Full list of USER-type editor subjects (dashboard owners) "
+            "after the operation. Any ROLE/GROUP-type editors on the "
+            "dashboard are not included here."
+        ),
+    )
+    dashboard_url: str | None = Field(None, description="URL to view the 
dashboard")
+    added_owner_ids: List[int] = Field(
+        default_factory=list,
+        description="User IDs actually added as owners by this call.",
+    )
+    removed_owner_ids: List[int] = Field(
+        default_factory=list,
+        description="User IDs actually removed from owners by this call.",
+    )
+    warnings: List[str] = Field(
+        default_factory=list,
+        description=(
+            "Non-fatal advisory messages, e.g. that a non-admin caller was "
+            "automatically re-added as owner after trying to remove "
+            "themselves."
+        ),
+    )
+    error: str | None = Field(None, description="Error message, if operation 
failed")
+    permission_denied: bool = Field(
+        default=False,
+        description=("True when the user lacks edit rights on the target 
dashboard."),
+    )
+
+    @field_validator("error")
+    @classmethod
+    def sanitize_error_for_llm_context(cls, value: str | None) -> str | None:
+        """Wrap error text before it is exposed to LLM context."""
+        if value is None:
+            return value
+        return sanitize_for_llm_context(value, field_path=("error",))
+
+
+class ManageDashboardRolesRequest(BaseModel):
+    """Request schema for explicit add/remove dashboard RBAC role management.
+
+    Unlike ``update_dashboard``'s dropped ``roles`` field (a full-replacement
+    access-control list), this tool takes explicit add/remove operations.
+
+    "Roles" here means ROLE-type entries in the dashboard's Subject-based
+    ``viewers`` list (the access model apache/superset#38831 introduced,
+    replacing the legacy ``roles``/``DASHBOARD_RBAC`` relationship). Any
+    USER- or GROUP-type viewers already on the dashboard are left untouched
+    by this tool.
+    """
+
+    identifier: int | str = Field(
+        ...,
+        description=(
+            "Dashboard ID (integer), UUID, or slug. Same identifier shape "
+            "accepted by ``get_dashboard_info``."
+        ),
+    )
+    add_role_ids: List[int] = Field(
+        default_factory=list,
+        description=(
+            "Role IDs to grant dashboard access to. Discover IDs with 
``list_roles``."
+        ),
+    )
+    remove_role_ids: List[int] = Field(
+        default_factory=list,
+        description=(
+            "Role IDs to revoke dashboard access from. Rejected if an ID is "
+            "not currently assigned to the dashboard."
+        ),
+    )
+
+    @model_validator(mode="after")
+    def _validate_operations(self) -> "ManageDashboardRolesRequest":
+        if not self.add_role_ids and not self.remove_role_ids:
+            raise ValueError(
+                "At least one of add_role_ids or remove_role_ids is required."
+            )
+        overlap = sorted(set(self.add_role_ids) & set(self.remove_role_ids))
+        if overlap:
+            raise ValueError(
+                "Role IDs cannot appear in both add_role_ids and "
+                f"remove_role_ids: {overlap}."
+            )
+        return self
+
+
+class ManageDashboardRolesResponse(BaseModel):
+    """Response schema for ``manage_dashboard_roles``."""
+
+    roles: List[SubjectInfo] = Field(
+        default_factory=list,
+        description=(
+            "Full list of ROLE-type viewer subjects (dashboard access "
+            "roles) after the operation. Any USER/GROUP-type viewers on "
+            "the dashboard are not included here."
+        ),
+    )
+    dashboard_url: str | None = Field(None, description="URL to view the 
dashboard")
+    added_role_ids: List[int] = Field(
+        default_factory=list,
+        description="Role IDs actually added by this call.",
+    )
+    removed_role_ids: List[int] = Field(
+        default_factory=list,
+        description="Role IDs actually removed by this call.",
+    )
+    viewers_enabled: bool = Field(
+        default=False,
+        description=(
+            "Whether the ENABLE_VIEWERS feature flag is enabled on this "
+            "instance. When False, dashboard viewers are stored but have no "
+            "effect on access control — access still follows normal "
+            "Superset permissions/editorship."
+        ),
+    )
+    warnings: List[str] = Field(
+        default_factory=list, description="Non-fatal advisory messages."
+    )
+    error: str | None = Field(None, description="Error message, if operation 
failed")
+    permission_denied: bool = Field(
+        default=False,
+        description=("True when the user lacks edit rights on the target 
dashboard."),
+    )
+
+    @field_validator("error")
+    @classmethod
+    def sanitize_error_for_llm_context(cls, value: str | None) -> str | None:
+        """Wrap error text before it is exposed to LLM context."""
+        if value is None:
+            return value
+        return sanitize_for_llm_context(value, field_path=("error",))
+
+
+class ManageDashboardCertificationRequest(BaseModel):
+    """Request schema for setting or clearing dashboard certification.
+
+    ``certified_by`` and ``certification_details`` are independent optional
+    fields: omit (None) to leave a field unchanged, pass an empty string to
+    clear it, or pass a value to set it — mirrors the ``slug``/``css``
+    clear-with-empty-string convention on ``update_dashboard``.
+    """
+
+    identifier: int | str = Field(
+        ...,
+        description=(
+            "Dashboard ID (integer), UUID, or slug. Same identifier shape "
+            "accepted by ``get_dashboard_info``."
+        ),
+    )
+    certified_by: str | None = Field(
+        None,
+        max_length=500,
+        description=(
+            "Person or team certifying this dashboard. Pass an empty string "
+            "to clear. Omit (None) to leave unchanged."
+        ),
+    )
+    certification_details: str | None = Field(
+        None,
+        max_length=5000,
+        description=(
+            "Details of the certification (why/how it was certified). Pass "
+            "an empty string to clear. Omit (None) to leave unchanged."
+        ),
+    )
+
+    @field_validator("certified_by")
+    @classmethod
+    def sanitize_certified_by(cls, v: str | None) -> str | None:
+        """Sanitize certified_by to prevent XSS; it renders as a UI badge."""
+        if v is None or v == "":
+            return v
+        return sanitize_user_input(v, "certified_by", max_length=500, 
allow_empty=True)
+
+    @field_validator("certification_details")
+    @classmethod
+    def sanitize_certification_details(cls, v: str | None) -> str | None:
+        """Sanitize certification_details to prevent XSS; it renders in the
+        same CertifiedBadge tooltip as certified_by."""
+        if v is None or v == "":
+            return v
+        return sanitize_user_input(
+            v, "certification_details", max_length=5000, allow_empty=True
+        )

Review Comment:
   **Suggestion:** `certification_details` has the same destructive edge case: 
non-empty payloads that sanitize down to empty are treated as explicit clear 
requests. This can wipe existing certification details on invalid/malicious 
input instead of returning a validation error, so reject empty-after-sanitize 
unless the original input was explicitly `""`. [logic error]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ⚠️ MCP certification tool can silently erase certification details.
   - ⚠️ Governance documentation attached to dashboards may be lost 
unexpectedly.
   - ⚠️ Malicious or invalid HTML-only inputs cause unintended data deletion.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Start the MCP server and use the `manage_dashboard_certification` FastMCP 
tool
   implemented in
   
`superset/mcp_service/dashboard/tool/manage_dashboard_certification.py:102-120`.
   
   2. Ensure there is a dashboard with an existing non-empty 
`certification_details` value
   (e.g., `_mock_dashboard(certification_details="Existing details")` as in
   `test_manage_dashboard_certification.py:48-61`).
   
   3. Call `manage_dashboard_certification` via FastMCP client (pattern from
   `test_set_certification` at `test_manage_dashboard_certification.py:74-83`), 
but send an
   HTML-only payload for `certification_details`, for example:
   
      `{"request": {"identifier": 42, "certification_details": "<b></b>"}}`.
   
   4. Pydantic instantiates `ManageDashboardCertificationRequest` 
(`schemas.py:42-73`); the
   `sanitize_certification_details` validator at `schemas.py:63-71` calls
   `sanitize_user_input(..., allow_empty=True)` from 
`utils/sanitization.py:18-82`, which
   strips HTML via `_strip_html_tags` and can return the empty string `""` when 
the original
   content is HTML-only.
   
   5. Since `sanitize_user_input` returns `""` and the validator simply returns 
that value,
   `request.certification_details` becomes the empty string even though the 
caller never
   explicitly sent `""` as a clear request.
   
   6. In `manage_dashboard_certification` at 
`manage_dashboard_certification.py:147-149`, the
   tool checks `if request.certification_details is not None:` and then executes
   `dashboard.certification_details = request.certification_details or None`, 
treating the
   sanitized `""` as `None` and clearing the field. The response reports a 
successful change
   with `changed_fields` containing `"certification_details"`, so existing 
details are
   silently wiped instead of rejecting the invalid input.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=d767cc3d67704d47bd418b5a5a8454b7&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=d767cc3d67704d47bd418b5a5a8454b7&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
   
   *(Use Cmd/Ctrl + Click for best experience)*
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset/mcp_service/dashboard/schemas.py
   **Line:** 1262:1271
   **Comment:**
        *Logic Error: `certification_details` has the same destructive edge 
case: non-empty payloads that sanitize down to empty are treated as explicit 
clear requests. This can wipe existing certification details on 
invalid/malicious input instead of returning a validation error, so reject 
empty-after-sanitize unless the original input was explicitly `""`.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   Once fix is implemented, also check other comments on the same PR, and ask 
user if the user wants to fix the rest of the comments as well. if said yes, 
then fetch all the comments validate the correctness and implement a minimal fix
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41606&comment_hash=559da9a7b669f1358afe27e6b98fdb791a9b609d1b95c9a1bc678d101c66169a&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41606&comment_hash=559da9a7b669f1358afe27e6b98fdb791a9b609d1b95c9a1bc678d101c66169a&reaction=dislike'>👎</a>



##########
superset/mcp_service/dashboard/schemas.py:
##########
@@ -1028,6 +1028,291 @@ class UpdateDashboardResponse(BaseModel):
     )
 
 
+class ManageDashboardOwnersRequest(BaseModel):
+    """Request schema for explicit add/remove dashboard owner management.
+
+    Unlike ``update_dashboard``'s dropped ``owners`` field (a full-replacement
+    list with no safety guard, so an empty or partial list could silently
+    orphan a dashboard), this tool takes explicit add/remove operations and
+    rejects any change that would leave the dashboard with zero owners.
+
+    "Owners" here means USER-type entries in the dashboard's Subject-based
+    ``editors`` list (the ownership model apache/superset#38831 introduced,
+    replacing the legacy ``owners`` relationship). Any ROLE- or GROUP-type
+    editors already on the dashboard are left untouched by this tool.
+    """
+
+    identifier: int | str = Field(
+        ...,
+        description=(
+            "Dashboard ID (integer), UUID, or slug. Same identifier shape "
+            "accepted by ``get_dashboard_info``."
+        ),
+    )
+    add_owner_ids: List[int] = Field(
+        default_factory=list,
+        description=(
+            "User IDs to add as dashboard owners. Discover IDs with 
``find_users``."
+        ),
+    )
+    remove_owner_ids: List[int] = Field(
+        default_factory=list,
+        description=(
+            "User IDs to remove from dashboard owners. Rejected if it would "
+            "leave the dashboard with zero owners, or if an ID is not "
+            "currently an owner."
+        ),
+    )
+
+    @model_validator(mode="after")
+    def _validate_operations(self) -> "ManageDashboardOwnersRequest":
+        if not self.add_owner_ids and not self.remove_owner_ids:
+            raise ValueError(
+                "At least one of add_owner_ids or remove_owner_ids is 
required."
+            )
+        overlap = sorted(set(self.add_owner_ids) & set(self.remove_owner_ids))
+        if overlap:
+            raise ValueError(
+                "User IDs cannot appear in both add_owner_ids and "
+                f"remove_owner_ids: {overlap}."
+            )
+        return self
+
+
+class ManageDashboardOwnersResponse(BaseModel):
+    """Response schema for ``manage_dashboard_owners``."""
+
+    owners: List[SubjectInfo] = Field(
+        default_factory=list,
+        description=(
+            "Full list of USER-type editor subjects (dashboard owners) "
+            "after the operation. Any ROLE/GROUP-type editors on the "
+            "dashboard are not included here."
+        ),
+    )
+    dashboard_url: str | None = Field(None, description="URL to view the 
dashboard")
+    added_owner_ids: List[int] = Field(
+        default_factory=list,
+        description="User IDs actually added as owners by this call.",
+    )
+    removed_owner_ids: List[int] = Field(
+        default_factory=list,
+        description="User IDs actually removed from owners by this call.",
+    )
+    warnings: List[str] = Field(
+        default_factory=list,
+        description=(
+            "Non-fatal advisory messages, e.g. that a non-admin caller was "
+            "automatically re-added as owner after trying to remove "
+            "themselves."
+        ),
+    )
+    error: str | None = Field(None, description="Error message, if operation 
failed")
+    permission_denied: bool = Field(
+        default=False,
+        description=("True when the user lacks edit rights on the target 
dashboard."),
+    )
+
+    @field_validator("error")
+    @classmethod
+    def sanitize_error_for_llm_context(cls, value: str | None) -> str | None:
+        """Wrap error text before it is exposed to LLM context."""
+        if value is None:
+            return value
+        return sanitize_for_llm_context(value, field_path=("error",))
+
+
+class ManageDashboardRolesRequest(BaseModel):
+    """Request schema for explicit add/remove dashboard RBAC role management.
+
+    Unlike ``update_dashboard``'s dropped ``roles`` field (a full-replacement
+    access-control list), this tool takes explicit add/remove operations.
+
+    "Roles" here means ROLE-type entries in the dashboard's Subject-based
+    ``viewers`` list (the access model apache/superset#38831 introduced,
+    replacing the legacy ``roles``/``DASHBOARD_RBAC`` relationship). Any
+    USER- or GROUP-type viewers already on the dashboard are left untouched
+    by this tool.
+    """
+
+    identifier: int | str = Field(
+        ...,
+        description=(
+            "Dashboard ID (integer), UUID, or slug. Same identifier shape "
+            "accepted by ``get_dashboard_info``."
+        ),
+    )
+    add_role_ids: List[int] = Field(
+        default_factory=list,
+        description=(
+            "Role IDs to grant dashboard access to. Discover IDs with 
``list_roles``."
+        ),
+    )
+    remove_role_ids: List[int] = Field(
+        default_factory=list,
+        description=(
+            "Role IDs to revoke dashboard access from. Rejected if an ID is "
+            "not currently assigned to the dashboard."
+        ),
+    )
+
+    @model_validator(mode="after")
+    def _validate_operations(self) -> "ManageDashboardRolesRequest":
+        if not self.add_role_ids and not self.remove_role_ids:
+            raise ValueError(
+                "At least one of add_role_ids or remove_role_ids is required."
+            )
+        overlap = sorted(set(self.add_role_ids) & set(self.remove_role_ids))
+        if overlap:
+            raise ValueError(
+                "Role IDs cannot appear in both add_role_ids and "
+                f"remove_role_ids: {overlap}."
+            )
+        return self
+
+
+class ManageDashboardRolesResponse(BaseModel):
+    """Response schema for ``manage_dashboard_roles``."""
+
+    roles: List[SubjectInfo] = Field(
+        default_factory=list,
+        description=(
+            "Full list of ROLE-type viewer subjects (dashboard access "
+            "roles) after the operation. Any USER/GROUP-type viewers on "
+            "the dashboard are not included here."
+        ),
+    )
+    dashboard_url: str | None = Field(None, description="URL to view the 
dashboard")
+    added_role_ids: List[int] = Field(
+        default_factory=list,
+        description="Role IDs actually added by this call.",
+    )
+    removed_role_ids: List[int] = Field(
+        default_factory=list,
+        description="Role IDs actually removed by this call.",
+    )
+    viewers_enabled: bool = Field(
+        default=False,
+        description=(
+            "Whether the ENABLE_VIEWERS feature flag is enabled on this "
+            "instance. When False, dashboard viewers are stored but have no "
+            "effect on access control — access still follows normal "
+            "Superset permissions/editorship."
+        ),
+    )
+    warnings: List[str] = Field(
+        default_factory=list, description="Non-fatal advisory messages."
+    )
+    error: str | None = Field(None, description="Error message, if operation 
failed")
+    permission_denied: bool = Field(
+        default=False,
+        description=("True when the user lacks edit rights on the target 
dashboard."),
+    )
+
+    @field_validator("error")
+    @classmethod
+    def sanitize_error_for_llm_context(cls, value: str | None) -> str | None:
+        """Wrap error text before it is exposed to LLM context."""
+        if value is None:
+            return value
+        return sanitize_for_llm_context(value, field_path=("error",))
+
+
+class ManageDashboardCertificationRequest(BaseModel):
+    """Request schema for setting or clearing dashboard certification.
+
+    ``certified_by`` and ``certification_details`` are independent optional
+    fields: omit (None) to leave a field unchanged, pass an empty string to
+    clear it, or pass a value to set it — mirrors the ``slug``/``css``
+    clear-with-empty-string convention on ``update_dashboard``.
+    """
+
+    identifier: int | str = Field(
+        ...,
+        description=(
+            "Dashboard ID (integer), UUID, or slug. Same identifier shape "
+            "accepted by ``get_dashboard_info``."
+        ),
+    )
+    certified_by: str | None = Field(
+        None,
+        max_length=500,
+        description=(
+            "Person or team certifying this dashboard. Pass an empty string "
+            "to clear. Omit (None) to leave unchanged."
+        ),
+    )
+    certification_details: str | None = Field(
+        None,
+        max_length=5000,
+        description=(
+            "Details of the certification (why/how it was certified). Pass "
+            "an empty string to clear. Omit (None) to leave unchanged."
+        ),
+    )
+
+    @field_validator("certified_by")
+    @classmethod
+    def sanitize_certified_by(cls, v: str | None) -> str | None:
+        """Sanitize certified_by to prevent XSS; it renders as a UI badge."""
+        if v is None or v == "":
+            return v
+        return sanitize_user_input(v, "certified_by", max_length=500, 
allow_empty=True)

Review Comment:
   **Suggestion:** `certified_by` sanitization can silently turn non-empty 
attacker/user input into an empty string, and the tool interprets empty string 
as “clear field”. That means malformed HTML-only input can unintentionally 
erase an existing certification instead of failing validation. Reject inputs 
that sanitize to empty unless the caller explicitly sent `""`. [logic error]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ⚠️ MCP tool `manage_dashboard_certification` can silently drop 
certifications.
   - ⚠️ Governance badge metadata on dashboards can be unintentionally cleared.
   - ⚠️ XSS-only or malformed inputs cause destructive, non-obvious data loss.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Start the MCP server and use the `manage_dashboard_certification` tool 
defined in
   
`superset/mcp_service/dashboard/tool/manage_dashboard_certification.py:102-120`.
   
   2. Ensure there is a dashboard with an existing non-empty `certified_by` 
value (e.g., by
   mocking `_mock_dashboard(certified_by="Existing Team")` as in
   
`tests/unit_tests/mcp_service/dashboard/tool/test_manage_dashboard_certification.py:48-61`).
   
   3. Call the tool via FastMCP client (pattern from `test_set_certification` at
   `test_manage_dashboard_certification.py:74-83`), but send an XSS-only 
payload for
   `certified_by`, for example:
   
      `{"request": {"identifier": 42, "certified_by": 
"<script>alert(1)</script>"}}`.
   
   4. Pydantic builds `ManageDashboardCertificationRequest` 
(`schemas.py:42-73`); its
   validator `sanitize_certified_by` at `schemas.py:75-81` calls 
`sanitize_user_input(...,
   allow_empty=True)` in `utils/sanitization.py:18-82`, which strips all HTML 
tags via
   `_strip_html_tags`, returning the empty string `""` for HTML-only input.
   
   5. Because `sanitize_user_input` does not re-check emptiness after stripping 
HTML, it
   returns `""` to the validator, which passes that value through unchanged. 
The tool then
   sees `request.certified_by == ""` and executes `dashboard.certified_by =
   request.certified_by or None` at 
`manage_dashboard_certification.py:143-145`, clearing the
   certification to `None`.
   
   6. There is no error raised and `changed_fields` includes `"certified_by"`, 
so the tool
   reports a successful update while the existing certification was silently 
erased due to
   input that sanitized to empty, even though the caller never explicitly sent 
an empty
   string as a clear request.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=0f0bdb99f5154194b492ed46efbafec0&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=0f0bdb99f5154194b492ed46efbafec0&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
   
   *(Use Cmd/Ctrl + Click for best experience)*
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset/mcp_service/dashboard/schemas.py
   **Line:** 1254:1260
   **Comment:**
        *Logic Error: `certified_by` sanitization can silently turn non-empty 
attacker/user input into an empty string, and the tool interprets empty string 
as “clear field”. That means malformed HTML-only input can unintentionally 
erase an existing certification instead of failing validation. Reject inputs 
that sanitize to empty unless the caller explicitly sent `""`.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   Once fix is implemented, also check other comments on the same PR, and ask 
user if the user wants to fix the rest of the comments as well. if said yes, 
then fetch all the comments validate the correctness and implement a minimal fix
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41606&comment_hash=62b8e5238449f48653d9afc28389340cc08b4cf8855af9d8cee18c12c9f57bf3&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41606&comment_hash=62b8e5238449f48653d9afc28389340cc08b4cf8855af9d8cee18c12c9f57bf3&reaction=dislike'>👎</a>



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