diff --git a/superset/assets/javascripts/explore/components/ControlHeader.jsx b/superset/assets/javascripts/explore/components/ControlHeader.jsx
index bc474a68852f..ce00a9d76901 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 ea0feaa3785e..561ab65d0173 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 1ada471c6aaf..9e84c466b1da 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -370,6 +370,12 @@ class CeleryConfig(object):
# 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 a909ed078a4b..7e0edc476d47 100644
--- a/superset/views/base.py
+++ b/superset/views/base.py
@@ -19,7 +19,10 @@
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 46e16506823b..e5a6bf052f1d 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -73,6 +73,14 @@
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 @@ def get_form_data(self):
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(