From 05367a0c10deadfc0a3d2eee204a4be16725ba18 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Wed, 21 Feb 2018 08:31:07 -0800 Subject: [PATCH] Allowing config flag to turn off javascript controls (#4400) * Allowing config flag to turn off javascript controls * lint * one line, avoiding mutation * Setting JS fields as readOnly (cherry picked from commit a373db24f044d19fda904331265fae56ee5c28e0) --- .../explore/components/ControlHeader.jsx | 14 ++++++++++++++ .../components/controls/TextAreaControl.jsx | 4 ++++ .../assets/javascripts/explore/stores/controls.jsx | 5 +++++ superset/config.py | 6 ++++++ superset/views/base.py | 5 ++++- superset/views/core.py | 13 ++++++++++++- 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/superset/assets/javascripts/explore/components/ControlHeader.jsx b/superset/assets/javascripts/explore/components/ControlHeader.jsx index bc474a68852..ce00a9d7690 100644 --- a/superset/assets/javascripts/explore/components/ControlHeader.jsx +++ b/superset/assets/javascripts/explore/components/ControlHeader.jsx @@ -14,6 +14,7 @@ const propTypes = { onClick: PropTypes.func, hovered: PropTypes.bool, tooltipOnClick: PropTypes.func, + warning: PropTypes.string, }; const defaultProps = { @@ -75,6 +76,19 @@ export default class ControlHeader extends React.Component { {this.props.label} {' '} + {(this.props.warning) && + + {this.props.warning} + } + > + + + {' '} + + } {(this.props.validationErrors.length > 0) && ); } @@ -67,6 +70,7 @@ export default class TextAreaControl extends React.Component { placeholder={t('textarea')} onChange={this.onControlChange.bind(this)} value={this.props.value} + disabled={this.props.readOnly} style={{ height: this.props.height }} /> ); diff --git a/superset/assets/javascripts/explore/stores/controls.jsx b/superset/assets/javascripts/explore/stores/controls.jsx index ea0feaa3785..561ab65d017 100644 --- a/superset/assets/javascripts/explore/stores/controls.jsx +++ b/superset/assets/javascripts/explore/stores/controls.jsx @@ -97,6 +97,11 @@ function jsFunctionControl(label, description, extraDescr = null, height = 100, {extraDescr} ), + mapStateToProps: state => ({ + warning: !state.common.conf.ENABLE_JAVASCRIPT_CONTROLS ? + t('This functionality is disabled in your environment for security reasons.') : null, + readOnly: !state.common.conf.ENABLE_JAVASCRIPT_CONTROLS, + }), }; } diff --git a/superset/config.py b/superset/config.py index 1ada471c6aa..9e84c466b1d 100644 --- a/superset/config.py +++ b/superset/config.py @@ -370,6 +370,12 @@ TRACKING_URL_TRANSFORMER = lambda x: x # noqa: E731 # Interval between consecutive polls when using Hive Engine HIVE_POLL_INTERVAL = 5 +# Allow for javascript controls components +# this enables programmers to customize certain charts (like the +# geospatial ones) by inputing javascript in controls. This exposes +# an XSS security vulnerability +ENABLE_JAVASCRIPT_CONTROLS = False + try: if CONFIG_PATH_ENV_VAR in os.environ: # Explicitly import config module that is not in pythonpath; useful diff --git a/superset/views/base.py b/superset/views/base.py index a909ed078a4..7e0edc476d4 100644 --- a/superset/views/base.py +++ b/superset/views/base.py @@ -19,7 +19,10 @@ from superset.connectors.connector_registry import ConnectorRegistry from superset.connectors.sqla.models import SqlaTable from superset.translations.utils import get_language_pack -FRONTEND_CONF_KEYS = ('SUPERSET_WEBSERVER_TIMEOUT',) +FRONTEND_CONF_KEYS = ( + 'SUPERSET_WEBSERVER_TIMEOUT', + 'ENABLE_JAVASCRIPT_CONTROLS', +) def get_error_msg(): diff --git a/superset/views/core.py b/superset/views/core.py index 0cb52fd5ddb..619b018965e 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -73,6 +73,14 @@ if perms_instruction_link: else: DATASOURCE_ACCESS_ERR = __("You don't have access to this datasource") +FORM_DATA_KEY_BLACKLIST = [] +if not config.get('ENABLE_JAVASCRIPT_CONTROLS'): + FORM_DATA_KEY_BLACKLIST = [ + 'js_tooltip', + 'js_onclick_href', + 'js_data_mutator', + ] + def get_database_access_error_msg(database_name): return __('This view requires the database %(name)s or ' @@ -948,7 +956,10 @@ class Superset(BaseSupersetView): if request.args.get('viz_type'): # Converting old URLs - d = cast_form_data(request.args) + d = cast_form_data(d) + + d = {k: v for k, v in d.items() if k not in FORM_DATA_KEY_BLACKLIST} + return d def get_viz(