diff --git a/caravel/models.py b/caravel/models.py
index e61468f5a4..1ca7483a3f 100644
--- a/caravel/models.py
+++ b/caravel/models.py
@@ -22,7 +22,7 @@ from sqlalchemy.engine.url import make_url
import sqlparse
from dateutil.parser import parse
-from flask import request, g
+from flask import escape, g, Markup, request
from flask_appbuilder import Model
from flask_appbuilder.models.mixins import AuditMixin
from flask_appbuilder.models.decorators import renders
@@ -102,12 +102,13 @@ class AuditMixinNullable(AuditMixin):
@renders('changed_on')
def changed_on_(self):
- return '{}'.format(self.changed_on)
+ return Markup(
+ '{}'.format(self.changed_on))
@renders('changed_on')
def modified(self):
s = humanize.naturaltime(datetime.now() - self.changed_on)
- return '{}'.format(s)
+ return Markup('{}'.format(s))
@property
def icons(self):
@@ -252,8 +253,8 @@ class Slice(Model, AuditMixinNullable):
@property
def slice_link(self):
url = self.slice_url
- return '{obj.slice_name}'.format(
- url=url, obj=self)
+ name = escape(self.slice_name)
+ return Markup('{name}'.format(**locals()))
def get_viz(self, url_params_multidict=None):
"""Creates :py:class:viz.BaseViz object from the url_params_multidict.
@@ -349,7 +350,9 @@ class Dashboard(Model, AuditMixinNullable):
return metadata.reflect()
def dashboard_link(self):
- return '{obj.dashboard_title}'.format(obj=self)
+ title = escape(self.dashboard_title)
+ return Markup(
+ '{title}'.format(**locals()))
@property
def json_data(self):
@@ -684,7 +687,9 @@ class SqlaTable(Model, Queryable, AuditMixinNullable):
@property
def link(self):
- return '{self.table_name}'.format(**locals())
+ table_name = escape(self.table_name)
+ return Markup(
+ '{table_name}'.format(**locals()))
@property
def perm(self):
@@ -728,10 +733,6 @@ class SqlaTable(Model, Queryable, AuditMixinNullable):
def name(self):
return self.table_name
- @renders('table_name')
- def table_link(self):
- return '{obj.table_name}'.format(obj=self)
-
@property
def metrics_combo(self):
return sorted(
@@ -1231,9 +1232,8 @@ class DruidDatasource(Model, AuditMixinNullable, Queryable):
@property
def link(self):
- return (
- ''
- '{self.datasource_name}').format(**locals())
+ name = escape(self.datasource_name)
+ return Markup('{name}').format(**locals())
@property
def full_name(self):
@@ -1247,8 +1247,8 @@ class DruidDatasource(Model, AuditMixinNullable, Queryable):
@renders('datasource_name')
def datasource_link(self):
url = "/caravel/explore/{obj.type}/{obj.id}/".format(obj=self)
- return '{obj.datasource_name}'.format(
- url=url, obj=self)
+ name = escape(self.datasource_name)
+ return Markup('{name}'.format(**locals()))
def get_metric_obj(self, metric_name):
return [
diff --git a/caravel/templates/appbuilder/general/widgets/list.html b/caravel/templates/appbuilder/general/widgets/list.html
index 294aa5b4c7..797787cdca 100644
--- a/caravel/templates/appbuilder/general/widgets/list.html
+++ b/caravel/templates/appbuilder/general/widgets/list.html
@@ -66,7 +66,7 @@
id="{{ '{}__{}'.format(pk, value) }}"
data-checkbox-api-prefix="/caravel/checkbox/{{ modelview_name }}/{{ pk }}/{{ value }}/">
{% else %}
- {{ item[value]|safe }}
+ {{ item[value] }}
{% endif %}
{% endfor %}
diff --git a/caravel/views.py b/caravel/views.py
index a3f510580d..0f0e69070a 100755
--- a/caravel/views.py
+++ b/caravel/views.py
@@ -535,10 +535,10 @@ appbuilder.add_view_no_menu(DatabaseTablesAsync)
class TableModelView(CaravelModelView, DeleteMixin): # noqa
datamodel = SQLAInterface(models.SqlaTable)
list_columns = [
- 'table_link', 'database', 'is_featured',
+ 'link', 'database', 'is_featured',
'changed_by_', 'changed_on_']
order_columns = [
- 'table_link', 'database', 'is_featured', 'changed_on_']
+ 'link', 'database', 'is_featured', 'changed_on_']
add_columns = ['table_name', 'database', 'schema']
edit_columns = [
'table_name', 'sql', 'is_featured', 'database', 'schema',
@@ -563,7 +563,7 @@ class TableModelView(CaravelModelView, DeleteMixin): # noqa
}
base_filters = [['id', TableSlice, lambda: []]]
label_columns = {
- 'table_link': _("Table"),
+ 'link': _("Table"),
'changed_by_': _("Changed By"),
'database': _("Database"),
'changed_on_': _("Last Changed"),