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


##########
superset/mcp_service/utils/sanitization.py:
##########
@@ -70,11 +70,29 @@ def _strip_html_tags(value: str) -> str:
     # url_schemes=set() blocks all URL schemes in any remaining attributes
     cleaned = nh3.clean(decoded, tags=set(), url_schemes=set())
 
-    # Only restore & → & to preserve ampersands in display text (e.g. "A & 
B").
-    # Do NOT use html.unescape() here: nh3 may pass through HTML entities from
-    # the input (e.g. <script>), and a full unescape would re-introduce
-    # raw angle brackets, creating an XSS vector.
-    return cleaned.replace("&", "&")
+    # HTML tag-open states require an ASCII letter, slash, bang or question

Review Comment:
   Fixed in 6143776c9a761201f4633bcf804243f5901094ce. I adopted the 
comparison-character allowlist. 
`tests/unit_tests/mcp_service/utils/test_sanitization.py::test_strip_html_tags_keeps_casefolding_tag_openers_escaped`
 fails for both U+017F and U+212A with reviewed source and passes after the 
change; 
`tests/unit_tests/mcp_service/utils/test_sanitization.py::test_strip_html_tags_preserves_non_tag_less_than`
 retains the literal-comparison contract. 
`tests/unit_tests/mcp_service/test_sanitization_warning_inputs.py::test_escaped_title_still_fits_model_column`
 additionally caught generate/duplicate dashboard overflow from intentionally 
retained escaping (2 failed before the storage-cap guard, all pass after). 
Docker: 1,232 touched Python tests passed. The tooltip sibling fix escapes the 
corresponding display fragments.
   
   Siblings checked:
   - superset/mcp_service/chart/schemas.py:6369 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (generate/save=false/update chart 
names).
   - superset/mcp_service/dashboard/schemas.py:884 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (generate titles).
   - superset/mcp_service/dashboard/schemas.py:1050 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (update titles).
   - superset/mcp_service/dashboard/schemas.py:1531 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (duplicate titles).
   - superset/mcp_service/dashboard/schemas.py:1393 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (certified_by).
   - superset/mcp_service/dashboard/schemas.py:1414 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (certification_details).
   - superset/mcp_service/chart/schemas.py:879 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (temporal columns).
   - superset/mcp_service/chart/schemas.py:1003 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (column names).
   - superset/mcp_service/chart/schemas.py:1011 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (column labels).
   - superset/mcp_service/chart/schemas.py:1132 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (filter columns).
   - superset/mcp_service/chart/schemas.py:1143 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (scalar/list filter values).
   - superset/mcp_service/chart/schemas.py:1211 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (Gantt columns).
   - superset/mcp_service/chart/schemas.py:2761 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (Sunburst ranges).
   - superset/mcp_service/chart/schemas.py:3155 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (pivot comparison periods).
   - superset/mcp_service/chart/schemas.py:4628 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (Bullet range/marker/marker-line 
labels).
   - superset/mcp_service/chart/schemas.py:4637 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (Bullet ranges).
   - superset/mcp_service/chart/tool/generate_chart.py:255 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (storage/readback regression; schema 
revalidation carries the sanitized title).
   - superset/mcp_service/chart/tool/update_chart.py — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (storage/readback regression).
   - superset/mcp_service/dashboard/tool/generate_dashboard.py — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (storage/readback regression).
   - superset/mcp_service/dashboard/tool/update_dashboard.py — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (storage/readback regression).
   - superset/mcp_service/chart/schemas.py:6435 — not affected: Explore links 
and update_chart_preview have no title field; their config uses the shared 
field validators above.
   - superset/mcp_service/dataset/schemas.py — not affected: object 
names/descriptions do not use this HTML sanitizer; literal-text controls remain 
covered.
   - superset/mcp_service/theme/schemas.py — not affected: blank/length 
validation without HTML stripping.
   - superset/mcp_service/sql_lab/schemas.py — not affected: label/tab trimming 
without HTML stripping.
   - superset/mcp_service/utils/sanitization.py:sanitize_sql_expression — not 
affected: separate SQL canonicalization, with existing comparison-operator 
tests.
   - superset/mcp_service/utils/error_builder.py:54 — not affected: differently 
named diagnostic scrubber, not a caller of the shared sanitizer.
   - superset/charts/schemas.py — not affected: REST does not call the MCP 
sanitizer; the tooltip rendering changes also cover raw text originating in 
REST/Explore.
   
   Siblings checked:
   - 
superset-frontend/plugins/plugin-chart-echarts/src/Bullet/transformProps.ts:229 
— fixed in 6143776c9a761201f4633bcf804243f5901094ce (group prefix, metric, 
containing-range label and formatted value).
   - 
superset-frontend/plugins/plugin-chart-echarts/src/Bullet/transformProps.ts:261 
— fixed in 6143776c9a761201f4633bcf804243f5901094ce (range threshold tooltip).
   - 
superset-frontend/plugins/plugin-chart-echarts/src/Bullet/transformProps.ts:278 
— fixed in 6143776c9a761201f4633bcf804243f5901094ce (marker tooltip).
   - 
superset-frontend/plugins/plugin-chart-echarts/src/Rose/transformProps.ts:136 — 
fixed in 6143776c9a761201f4633bcf804243f5901094ce (rich series tooltip, period 
text and formatted value).
   - 
superset-frontend/plugins/plugin-chart-echarts/src/Rose/transformProps.ts:183 — 
fixed in 6143776c9a761201f4633bcf804243f5901094ce (plain series tooltip).
   - superset-frontend/plugins/plugin-chart-echarts/src/Rose/EchartsRose.tsx:81 
— fixed in 6143776c9a761201f4633bcf804243f5901094ce (drill-down pie tooltip).
   - superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts:1043 — 
not affected: this similarly named sanitizeHtml already HTML-escapes with 
ECharts encodeHTML; reused for these fixes.
   - 
superset-frontend/plugins/plugin-chart-echarts/src/BoxPlot/transformProps.ts:149
 — not affected: both tooltip paths import the escaping helper from 
utils/series, not the core HTML filter.
   - 
superset-frontend/plugins/plugin-chart-echarts/src/Graph/transformProps.ts:390 
— not affected: source/target labels already use the escaping helper.
   - 
superset-frontend/plugins/plugin-chart-echarts/src/Pie/transformProps.ts:75 — 
not affected: raw names already use the escaping helper before tooltipHtml.
   - 
superset-frontend/plugins/plugin-chart-echarts/src/Funnel/transformProps.ts:73 
— not affected: raw names already use the escaping helper before tooltipHtml.
   - superset-frontend/plugins/plugin-chart-echarts/src/Radar/utils.ts:67 — not 
affected: label/value fragments import the escaping helper.
   - superset-frontend/plugins/plugin-chart-echarts/src/utils/forecast.ts:124 — 
not affected: series labels import the escaping helper.
   - 
superset-frontend/plugins/plugin-chart-echarts/src/utils/eChartOptionsSchema.ts:71
 — not affected: sanitizes explicitly HTML-valued option strings, not the 
plain-text label fragments changed here.
   - superset-frontend/packages/superset-ui-core/src/utils/tooltip.ts:78 — not 
affected: intentionally accepts rich HTML; the Pie/Funnel/Graph callers above 
escape plain text before calling it.
   - 
superset-frontend/plugins/plugin-chart-echarts/src/Bullet/transformProps.ts — 
not affected: canvas axis/legend/mark-line labels are not HTML sinks; the 
common transformer covers MCP, REST and Explore inputs.
   



##########
superset/mcp_service/dashboard/schemas.py:
##########
@@ -1065,17 +1069,17 @@ def _detect_dashboard_title_sanitization(cls, data: 
Any) -> Any:
 
     @field_validator("dashboard_title")
     @classmethod
-    def sanitize_dashboard_title(cls, v: str | None) -> str | None:
-        """Sanitize dashboard title to prevent XSS."""
-        if v is None or v == "":
+    def normalize_blank_dashboard_title(cls, v: str | None) -> str | None:
+        """Reject blank updates; nonblank titles are sanitized above."""
+        if v is None:
             return v
-        return sanitize_user_input(
-            v, "Dashboard title", max_length=500, allow_empty=True
-        )
+        if not v.strip():
+            raise PydanticCustomError("empty_text", "dashboard_title cannot be 
empty")

Review Comment:
   Fixed in 6143776c9a761201f4633bcf804243f5901094ce. I added the `## Next` 
migration note covering explicit blank dashboard renames, omission to retain 
the stored title, chart names dropping from the published 255 limit to 250, and 
the new 250-character theme-name cap.
   
   Regression: 
`tests/unit_tests/mcp_service/test_sanitization_warning_inputs.py::test_name_contract_changes_have_upgrade_guidance`
 fails with the reviewed UPDATING.md and passes after this change (the two 
documentation tests failed before, both pass in the 1,232-test Docker run). 
Runtime contracts remain pinned by 
`test_update_dashboard_blank_title_wire_reason`, 
`test_chart_name_model_column_boundary`, and 
`test_theme_name_model_column_boundary` in the same PR test file.
   
   Siblings checked:
   - UPDATING.md:28 — fixed in 6143776c9a761201f4633bcf804243f5901094ce.
   - docs/admin_docs/configuration/mcp-server.mdx:2428 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (blank rename and all name/length 
guidance together).
   - docs/developer_docs/extensions/mcp.md — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (built-in validation text moved to 
administration).
   - superset/mcp_service/chart/schemas.py:6330 — not affected: existing 
runtime 250 cap is documented, not changed again.
   - superset/mcp_service/dashboard/schemas.py:1072 — not affected: existing 
explicit-blank rejection is documented.
   - superset/mcp_service/theme/schemas.py:143 — not affected: existing 250 cap 
is documented.
   



##########
docs/developer_docs/extensions/mcp.md:
##########
@@ -587,3 +598,17 @@ governance concern from layout/theme/metadata edits.
 
 - **[Development](./development.md)** - Project structure, APIs, and dev 
workflow
 - **[Security](./security.md)** - Security best practices for extensions
+
+## Object-name validation

Review Comment:
   Fixed in 6143776c9a761201f4633bcf804243f5901094ce. I moved both “Chart and 
dashboard titles” and “Object-name validation” to the admin MCP server 
documentation, retaining theme/saved-query/SQL Lab/slug caps, 
trim-before-length and the theme whitespace exception. Neither section remains 
after “Next Steps” on the extension page.
   
   Regression: 
`tests/unit_tests/mcp_service/test_sanitization_warning_inputs.py::test_builtin_name_validation_documented_for_administrators`
 fails with reviewed documentation and passes after the move (both 
documentation regressions pass in the 1,232-test Docker run).
   
   Siblings checked:
   - docs/developer_docs/extensions/mcp.md — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (both built-in validation sections 
removed).
   - docs/admin_docs/configuration/mcp-server.mdx:2428 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (title-error guidance retained).
   - docs/admin_docs/configuration/mcp-server.mdx:2447 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (literal title handling moved here).
   - docs/admin_docs/configuration/mcp-server.mdx:2458 — fixed in 
6143776c9a761201f4633bcf804243f5901094ce (object caps and trimming moved here).
   - docs/developer_docs/extensions/mcp.md:31 — not affected: pre-existing 
chart-dataset rebinding guidance is outside these two validation sections and 
unchanged.
   



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