fix(embedded): compare guest chart payloads against every stored control - #42864
fix(embedded): compare guest chart payloads against every stored control#42864luizotavio32 wants to merge 1 commit into
Conversation
`query_context_modified` compared a request's `metrics`, `columns`, `groupby` and `orderby` against identically-named keys in the chart's `params`. Charts save those values under whichever control names their viz type uses, so a guest merely loading an embedded dashboard was rejected as a tamperer: big number and pie store `metric`, bubble stores `x`/`y`/`size`, sankey stores `source`/`target`, time series stores `x_axis`. Each request key is now compared against every equivalent stored control, and `orderby` against any metric or column the chart already reads, since charts sort by their own metric on initial load without user interaction. Values are normalized before comparison so the same underlying column compares equal however it is spelled — adhoc references that only point back at a physical column collapse to its name, synthesized `BASE_AXIS` markers are shed, sort pairs reduce to the expression they sort on, and scalar-valued controls are no longer iterated character by character. Matching itself stays exact: only the shape of a value is normalized, never its identity, so a collapsed reference still has to match something stored on the chart. Pointing at an unrelated column or wrapping free-form SQL grants no extra access. The comparison helpers move to `superset/security/guest_payload.py`; `freeze_value` is re-exported from `superset.security.manager` for compatibility. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Bito Automatic Review Skipped - Branch Excluded |
|
/review |
Code Review Agent Run #4752b5Actionable Suggestions - 0Additional Suggestions - 1
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
SUMMARY
query_context_modifiedcompared a request'smetrics,columns,groupbyandorderbyagainst identically-named keys in the chart'sparams. Charts save those values under whichever control names their viz type uses, so a guest merely loading an embedded dashboard was rejected as a tamperer: big number and pie storemetric, bubble storesx/y/size, sankey storessource/target, time series storesx_axis.Each request key is now compared against every equivalent stored control, and
orderbyagainst any metric or column the chart already reads — charts sort by their own metric on initial load, with no user interaction. Values are normalized before comparison so the same underlying column compares equal however it is spelled:BASE_AXISmarkers (added bynormalizeTimeColumn) are shedMatching itself stays exact: only the shape of a value is normalized, never its identity, so a collapsed reference still has to match something stored on the chart. Pointing at an unrelated column or wrapping free-form SQL grants no extra access.
series_limit_metricandtimeseries_limit_metricare included as stored metric controls, so a guest cannot rank top-N groups by arbitrary adhoc SQL.The comparison helpers move to
superset/security/guest_payload.py;freeze_valueis re-exported fromsuperset.security.managerfor compatibility.TESTING INSTRUCTIONS
pytest tests/unit_tests/security/manager_test.py— 52 tests, covering per-viz-type control names, each normalization rule, and tamper vectors that must still be rejected.Manual verification against a real embedded dashboard, which is what this fix is about — synthetic
POST /api/v1/chart/datapayloads exercise the comparator but not what the frontend actually sends:EMBEDDED_SUPERSETand a guest role, and an embedded dashboard with a spread of viz types.series_limit_metricwith adhoc SQL) and assert a 403 carrying the guard's own message,Guest user cannot modify chart payload. A genericForbiddenmeans an earlier gate objected and the guard never ran, so a 200 on the legitimate load would prove nothing.Note that
query_context_modifiedreturns early whenquery_context.slice_ is None. On 6.2user_view_menu_names()returns nothing for guests, soChartFiltercan hide the slice and the guard no-ops, accepting every payload — step 3 is what distinguishes "allowed" from "never ran".ADDITIONAL INFORMATION