Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions superset/assets/javascripts/explore/components/ControlHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const propTypes = {
onClick: PropTypes.func,
hovered: PropTypes.bool,
tooltipOnClick: PropTypes.func,
warning: PropTypes.string,
};

const defaultProps = {
Expand Down Expand Up @@ -75,6 +76,19 @@ export default class ControlHeader extends React.Component {
{this.props.label}
</span>
{' '}
{(this.props.warning) &&
<span>
<OverlayTrigger
placement="top"
overlay={
<Tooltip id={'error-tooltip'}>{this.props.warning}</Tooltip>
}
>
<i className="fa fa-exclamation-circle text-danger" />
</OverlayTrigger>
{' '}
</span>
}
{(this.props.validationErrors.length > 0) &&
<span>
<OverlayTrigger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const propTypes = {
offerEditInModal: PropTypes.bool,
language: PropTypes.oneOf([null, 'json', 'html', 'sql', 'markdown', 'javascript']),
aboveEditorSection: PropTypes.node,
readOnly: PropTypes.bool,
};

const defaultProps = {
Expand All @@ -34,6 +35,7 @@ const defaultProps = {
minLines: 3,
maxLines: 10,
offerEditInModal: true,
readOnly: false,
};

export default class TextAreaControl extends React.Component {
Expand All @@ -57,6 +59,7 @@ export default class TextAreaControl extends React.Component {
editorProps={{ $blockScrolling: true }}
enableLiveAutocompletion
value={this.props.value}
readOnly={this.props.readOnly}
/>
);
}
Expand All @@ -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 }}
/>
</FormGroup>);
Expand Down
5 changes: 5 additions & 0 deletions superset/assets/javascripts/explore/stores/controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ function jsFunctionControl(label, description, extraDescr = null, height = 100,
{extraDescr}
</div>
),
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,
}),
};
}

Expand Down
6 changes: 6 additions & 0 deletions superset/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion superset/views/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
13 changes: 12 additions & 1 deletion superset/views/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 '
Expand Down Expand Up @@ -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(
Expand Down