# HG changeset patch # User Søren Løvborg # Date 1488298740 -3600 # Node ID 33b71a130b16340262420a57ab99dfadff6d72cb # Parent 482c531e62ef0ce329295126dce16e7e3b254d0f templates: properly escape inline JavaScript values TLDR: Kallithea has issues with escaping values for use in inline JS. Despite judicious poking of the code, no actual security vulnerabilities have been found, just lots of corner-case bugs. This patch fixes those, and hardens the code against actual security issues. The long version: To embed a Python value (typically a 'unicode' plain-text value) in a larger file, it must be escaped in a context specific manner. Example: >>> s = u'' 1) Escaped for insertion into HTML element context >>> print cgi.escape(s) <script>alert("It's a trap!");</script> 2) Escaped for insertion into HTML element or attribute context >>> print h.escape(s) <script>alert("It's a trap!");</script> This is the default Mako escaping, as usually used by Kallithea. 3) Encoded as JSON >>> print json.dumps(s) "" 4) Escaped for insertion into a JavaScript file >>> print '(' + json.dumps(s) + ')' ("") The parentheses are not actually required for strings, but may be needed to avoid syntax errors if the value is a number or dict (object). 5) Escaped for insertion into a HTML inline ' must be escaped, but not using HTML escaping, which is not available in HTML "" + is forbidden), the function ensures that the result never contains + '&', '<' and '>', thus making it safe in both those contexts (but + not in attributes). + """ + return literal( + ('(' + json.dumps(value) + ')') + # In JSON, the following can only appear in string literals. + .replace('&', r'\x26') + .replace('<', r'\x3c') + .replace('>', r'\x3e') + ) + +def jshtml(val): + """HTML escapes a string value, then converts the resulting string + to its corresponding JavaScript representation (see `js`). + + This is used when a plain-text string (possibly containing special + HTML characters) will be used by a script in an HTML context (e.g. + element.innerHTML or jQuery's 'html' method). + + If in doubt, err on the side of using `jshtml` over `js`, since it's + better to escape too much than too little. + """ + return js(escape(val)) + + def shorter(s, size=20, firstline=False, postfix='...'): """Truncate s to size, including the postfix string if truncating. If firstline, truncate at newline. diff -r 482c531e62ef -r 33b71a130b16 kallithea/model/repo.py --- a/kallithea/model/repo.py Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/model/repo.py Tue Feb 28 17:19:00 2017 +0100 @@ -35,7 +35,6 @@ from kallithea.lib.utils import make_ui from kallithea.lib.vcs.backends import get_backend -from kallithea.lib.compat import json from kallithea.lib.utils2 import LazyProperty, safe_str, safe_unicode, \ remove_prefix, obfuscate_url_pw, get_current_authuser from kallithea.lib.caching_query import FromCache @@ -127,7 +126,7 @@ .filter(User.active == True) \ .order_by(User.name, User.lastname) \ .all() - return json.dumps([ + return [ { 'id': u.user_id, 'fname': h.escape(u.name), @@ -136,7 +135,6 @@ 'gravatar_lnk': h.gravatar_url(u.email, size=28, default='default'), 'gravatar_size': 14, } for u in users] - ) def get_user_groups_js(self): user_groups = UserGroup.query() \ @@ -145,13 +143,12 @@ .options(subqueryload(UserGroup.members)) \ .all() user_groups = UserGroupList(user_groups, perm_level='read') - return json.dumps([ + return [ { 'id': gr.users_group_id, 'grname': gr.users_group_name, 'grmembers': len(gr.members), } for gr in user_groups] - ) @classmethod def _render_datatable(cls, tmpl, *args, **kwargs): diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/admin/admin.html --- a/kallithea/templates/admin/admin.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/admin/admin.html Tue Feb 28 17:19:00 2017 +0100 @@ -46,7 +46,7 @@ $('#filter_form').submit(function (e) { e.preventDefault(); var val = $('#j_filter').val(); - window.location = "${url.current(filter='__FILTER__')}".replace('__FILTER__',val); + window.location = ${h.js(url.current(filter='__FILTER__'))}.replace('__FILTER__',val); }); fix_j_filter_width($('#j_filter').val().length); }); diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/admin/gists/edit.html --- a/kallithea/templates/admin/gists/edit.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/admin/gists/edit.html Tue Feb 28 17:19:00 2017 +0100 @@ -81,10 +81,10 @@ ## dynamic edit box. diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/admin/notifications/show_notification.html --- a/kallithea/templates/admin/notifications/show_notification.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/admin/notifications/show_notification.html Tue Feb 28 17:19:00 2017 +0100 @@ -43,8 +43,8 @@ diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/admin/repo_groups/repo_groups.html --- a/kallithea/templates/admin/repo_groups/repo_groups.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/admin/repo_groups/repo_groups.html Tue Feb 28 17:19:00 2017 +0100 @@ -31,16 +31,16 @@ diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/admin/repos/repo_edit_settings.html --- a/kallithea/templates/admin/repos/repo_edit_settings.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/admin/repos/repo_edit_settings.html Tue Feb 28 17:19:00 2017 +0100 @@ -125,7 +125,7 @@ }); // autocomplete - var _USERS_AC_DATA = ${c.users_array|n}; + var _USERS_AC_DATA = ${h.js(c.users_array)}; SimpleUserAutoComplete($('#owner'), $('#owner_container'), _USERS_AC_DATA); }); diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/admin/repos/repos.html --- a/kallithea/templates/admin/repos/repos.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/admin/repos/repos.html Tue Feb 28 17:19:00 2017 +0100 @@ -30,18 +30,18 @@ diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/admin/user_groups/user_group_edit_perms.html --- a/kallithea/templates/admin/user_groups/user_group_edit_perms.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/admin/user_groups/user_group_edit_perms.html Tue Feb 28 17:19:00 2017 +0100 @@ -75,7 +75,7 @@ %endfor <% - _tmpl = h.literal("""'\ + _tmpl = """\ \ \ \ @@ -87,7 +87,7 @@
\ \ \ - '""") + """ %> ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl' @@ -110,7 +110,7 @@ diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/admin/user_groups/user_groups.html --- a/kallithea/templates/admin/user_groups/user_groups.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/admin/user_groups/user_groups.html Tue Feb 28 17:19:00 2017 +0100 @@ -30,17 +30,17 @@ @@ -82,22 +82,22 @@ tooltip_activate(); show_more_event(); // routes registration - pyroutes.register('home', "${h.url('home')}", []); - pyroutes.register('new_gist', "${h.url('new_gist')}", []); - pyroutes.register('gists', "${h.url('gists')}", []); - pyroutes.register('new_repo', "${h.url('new_repo')}", []); + pyroutes.register('home', ${h.js(h.url('home'))}, []); + pyroutes.register('new_gist', ${h.js(h.url('new_gist'))}, []); + pyroutes.register('gists', ${h.js(h.url('gists'))}, []); + pyroutes.register('new_repo', ${h.js(h.url('new_repo'))}, []); - pyroutes.register('summary_home', "${h.url('summary_home', repo_name='%(repo_name)s')}", ['repo_name']); - pyroutes.register('changelog_home', "${h.url('changelog_home', repo_name='%(repo_name)s')}", ['repo_name']); - pyroutes.register('files_home', "${h.url('files_home', repo_name='%(repo_name)s',revision='%(revision)s',f_path='%(f_path)s')}", ['repo_name', 'revision', 'f_path']); - pyroutes.register('edit_repo', "${h.url('edit_repo', repo_name='%(repo_name)s')}", ['repo_name']); - pyroutes.register('edit_repo_perms', "${h.url('edit_repo_perms', repo_name='%(repo_name)s')}", ['repo_name']); - pyroutes.register('pullrequest_home', "${h.url('pullrequest_home', repo_name='%(repo_name)s')}", ['repo_name']); + pyroutes.register('summary_home', ${h.js(h.url('summary_home', repo_name='%(repo_name)s'))}, ['repo_name']); + pyroutes.register('changelog_home', ${h.js(h.url('changelog_home', repo_name='%(repo_name)s'))}, ['repo_name']); + pyroutes.register('files_home', ${h.js(h.url('files_home', repo_name='%(repo_name)s',revision='%(revision)s',f_path='%(f_path)s'))}, ['repo_name', 'revision', 'f_path']); + pyroutes.register('edit_repo', ${h.js(h.url('edit_repo', repo_name='%(repo_name)s'))}, ['repo_name']); + pyroutes.register('edit_repo_perms', ${h.js(h.url('edit_repo_perms', repo_name='%(repo_name)s'))}, ['repo_name']); + pyroutes.register('pullrequest_home', ${h.js(h.url('pullrequest_home', repo_name='%(repo_name)s'))}, ['repo_name']); - pyroutes.register('toggle_following', "${h.url('toggle_following')}"); - pyroutes.register('changeset_info', "${h.url('changeset_info', repo_name='%(repo_name)s', revision='%(revision)s')}", ['repo_name', 'revision']); - pyroutes.register('repo_size', "${h.url('repo_size', repo_name='%(repo_name)s')}", ['repo_name']); - pyroutes.register('repo_refs_data', "${h.url('repo_refs_data', repo_name='%(repo_name)s')}", ['repo_name']); + pyroutes.register('toggle_following', ${h.js(h.url('toggle_following'))}); + pyroutes.register('changeset_info', ${h.js(h.url('changeset_info', repo_name='%(repo_name)s', revision='%(revision)s'))}, ['repo_name', 'revision']); + pyroutes.register('repo_size', ${h.js(h.url('repo_size', repo_name='%(repo_name)s'))}, ['repo_name']); + pyroutes.register('repo_refs_data', ${h.js(h.url('repo_refs_data', repo_name='%(repo_name)s'))}, ['repo_name']); }); diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/changelog/changelog.html --- a/kallithea/templates/changelog/changelog.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/changelog/changelog.html Tue Feb 28 17:19:00 2017 +0100 @@ -189,7 +189,7 @@ $(document).ready(function(){ var $checkboxes = $('.changeset_range'); - pyroutes.register('changeset_home', "${h.url('changeset_home', repo_name='%(repo_name)s', revision='%(revision)s')}", ['repo_name', 'revision']); + pyroutes.register('changeset_home', ${h.js(h.url('changeset_home', repo_name='%(repo_name)s', revision='%(revision)s'))}, ['repo_name', 'revision']); var checkbox_checker = function(e) { var $checked_checkboxes = $checkboxes.filter(':checked'); @@ -206,19 +206,19 @@ if ($checked_checkboxes.length > 1 || singlerange) { var rev_start = $checked_checkboxes.last().prop('name'); $('#rev_range_container').prop('href', - pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}', + pyroutes.url('changeset_home', {'repo_name': ${h.js(c.repo_name)}, 'revision': rev_start + '...' + rev_end})); $('#rev_range_container').html( _TM['Show Selected Changesets {0} → {1}'].format(rev_start.substr(0, 12), rev_end.substr(0, 12))); $('#rev_range_container').show(); $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home', - {'repo_name': '${c.repo_name}', + {'repo_name': ${h.js(c.repo_name)}, 'rev_start': rev_start, 'rev_end': rev_end})); $('#open_new_pr').html(_TM['Open New Pull Request for {0} → {1}'].format(rev_start.substr(0, 12), rev_end.substr(0, 12))); } else { $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home', - {'repo_name': '${c.repo_name}', + {'repo_name': ${h.js(c.repo_name)}, 'rev_end': rev_end})); $('#open_new_pr').html(_TM['Open New Pull Request from {0}'].format(rev_end.substr(0, 12))); } @@ -252,14 +252,14 @@ $('#rev_range_clear').hide(); %if c.revision: $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home', - {'repo_name': '${c.repo_name}', - 'rev_end':'${c.first_revision.raw_id}'})); - $('#open_new_pr').html(_TM['Open New Pull Request from {0}'].format('${c.revision}')); + {'repo_name': ${h.js(c.repo_name)}, + 'rev_end':${h.js(c.first_revision.raw_id)}})); + $('#open_new_pr').html(_TM['Open New Pull Request from {0}'].format(${h.jshtml(c.revision)})); %else: $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home', - {'repo_name': '${c.repo_name}', - 'branch':'${c.first_revision.branch}'})); - $('#open_new_pr').html(_TM['Open New Pull Request from {0}'].format('${c.first_revision.branch}')); + {'repo_name': ${h.js(c.repo_name)}, + 'branch':${h.js(c.first_revision.branch)}})); + $('#open_new_pr').html(_TM['Open New Pull Request from {0}'].format(${h.jshtml(c.first_revision.branch)})); %endif $('#compare_fork').show(); $checkboxes.closest('tr').removeClass('out-of-range'); @@ -311,15 +311,15 @@ $("#branch_filter").change(function(e){ var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value; if(selected_branch != ''){ - window.location = pyroutes.url('changelog_home', {'repo_name': '${c.repo_name}', + window.location = pyroutes.url('changelog_home', {'repo_name': ${h.js(c.repo_name)}, 'branch': selected_branch}); }else{ - window.location = pyroutes.url('changelog_home', {'repo_name': '${c.repo_name}'}); + window.location = pyroutes.url('changelog_home', {'repo_name': ${h.js(c.repo_name)}}); } $("#changelog").hide(); }); - var jsdata = ${c.jsdata|n}; + var jsdata = ${h.js(c.jsdata)}; var r = new BranchRenderer('graph_canvas', 'graph_content', 'chg_'); r.render(jsdata); }); diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/changeset/changeset.html --- a/kallithea/templates/changeset/changeset.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/changeset/changeset.html Tue Feb 28 17:19:00 2017 +0100 @@ -23,10 +23,10 @@ ${self.breadcrumbs()}
@@ -217,7 +217,7 @@ move_comments($(".comments .comments-list-chunk")); pyroutes.register('changeset_home', - "${h.url('changeset_home', repo_name='%(repo_name)s', revision='%(revision)s')}", + ${h.js(h.url('changeset_home', repo_name='%(repo_name)s', revision='%(revision)s'))}, ['repo_name', 'revision']); //next links @@ -226,15 +226,15 @@ //>1 links show them to user to choose if(!$('#child_link').hasClass('disabled')){ $.ajax({ - url: '${h.url('changeset_children',repo_name=c.repo_name, revision=c.changeset.raw_id)}', + url: ${h.js(h.url('changeset_children',repo_name=c.repo_name, revision=c.changeset.raw_id))}, success: function(data) { if(data.results.length === 0){ $('#child_link').addClass('disabled'); - $('#child_link').html('${_('No revisions')}'); + $('#child_link').html(${h.jshtml(_('No revisions'))}); } if(data.results.length === 1){ var commit = data.results[0]; - window.location = pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': commit.raw_id}); + window.location = pyroutes.url('changeset_home', {'repo_name': ${h.js(c.repo_name)},'revision': commit.raw_id}); } else if(data.results.length === 2){ $('#child_link').addClass('disabled'); @@ -243,12 +243,12 @@ _html +='__rev__ ' .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6))) .replace('__title__', data.results[0].message) - .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[0].raw_id})); + .replace('__url__', pyroutes.url('changeset_home', {'repo_name': ${h.js(c.repo_name)},'revision': data.results[0].raw_id})); _html +='
' _html +='__rev__ ' .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6))) .replace('__title__', data.results[1].message) - .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[1].raw_id})); + .replace('__url__', pyroutes.url('changeset_home', {'repo_name': ${h.js(c.repo_name)},'revision': data.results[1].raw_id})); $('#child_link').html(_html); } } @@ -263,15 +263,15 @@ //>1 links show them to user to choose if(!$('#parent_link').hasClass('disabled')){ $.ajax({ - url: '${h.url('changeset_parents',repo_name=c.repo_name, revision=c.changeset.raw_id)}', + url: ${h.js(h.url('changeset_parents',repo_name=c.repo_name, revision=c.changeset.raw_id))}, success: function(data) { if(data.results.length === 0){ $('#parent_link').addClass('disabled'); - $('#parent_link').html('${_('No revisions')}'); + $('#parent_link').html(${h.jshtml(_('No revisions'))}); } if(data.results.length === 1){ var commit = data.results[0]; - window.location = pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': commit.raw_id}); + window.location = pyroutes.url('changeset_home', {'repo_name': ${h.js(c.repo_name)},'revision': commit.raw_id}); } else if(data.results.length === 2){ $('#parent_link').addClass('disabled'); @@ -280,12 +280,12 @@ _html +=' __rev__' .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6))) .replace('__title__', data.results[0].message) - .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[0].raw_id})); + .replace('__url__', pyroutes.url('changeset_home', {'repo_name': ${h.js(c.repo_name)},'revision': data.results[0].raw_id})); _html +='
' _html +=' __rev__' .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6))) .replace('__title__', data.results[1].message) - .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[1].raw_id})); + .replace('__url__', pyroutes.url('changeset_home', {'repo_name': ${h.js(c.repo_name)},'revision': data.results[1].raw_id})); $('#parent_link').html(_html); } } diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/compare/compare_cs.html --- a/kallithea/templates/compare/compare_cs.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/compare/compare_cs.html Tue Feb 28 17:19:00 2017 +0100 @@ -121,7 +121,7 @@
%if c.as_form: - + %else: %endif @@ -130,7 +130,7 @@ $(document).ready(function(){ %if not c.as_form: - var jsdata = ${c.jsdata|n}; + var jsdata = ${h.js(c.jsdata)}; var r = new BranchRenderer('graph_canvas', 'graph_content_pr', 'chg_'); r.render(jsdata); %endif diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/compare/compare_diff.html --- a/kallithea/templates/compare/compare_diff.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/compare/compare_diff.html Tue Feb 28 17:19:00 2017 +0100 @@ -149,8 +149,8 @@ }); } - make_revision_dropdown("#compare_org", "${'%s@%s' % (c.a_repo.repo_name, c.a_ref_name)}", "${c.a_repo.repo_name}", 'cache'); - make_revision_dropdown("#compare_other", "${'%s@%s' % (c.cs_repo.repo_name, c.cs_ref_name)}", "${c.cs_repo.repo_name}", 'cache2'); + make_revision_dropdown("#compare_org", ${h.jshtml('%s@%s' % (c.a_repo.repo_name, c.a_ref_name))}, ${h.jshtml(c.a_repo.repo_name)}, 'cache'); + make_revision_dropdown("#compare_other", ${h.jshtml('%s@%s' % (c.cs_repo.repo_name, c.cs_ref_name))}, ${h.jshtml(c.cs_repo.repo_name)}, 'cache2'); var values_changed = function() { var values = $('#compare_org').select2('data') && $('#compare_other').select2('data'); @@ -172,7 +172,7 @@ return; } - var compare_url = "${h.url('compare_url',repo_name=c.repo_name,org_ref_type='__other_ref_type__',org_ref_name='__org__',other_ref_type='__org_ref_type__',other_ref_name='__other__', other_repo=c.cs_repo.repo_name)}"; + var compare_url = ${h.js(h.url('compare_url',repo_name=c.repo_name,org_ref_type='__other_ref_type__',org_ref_name='__org__',other_ref_type='__org_ref_type__',other_ref_name='__other__', other_repo=c.cs_repo.repo_name))}; var u = compare_url.replace('__other_ref_type__',org.type) .replace('__org__',org.text) .replace('__org_ref_type__',other.type) diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/files/diff_2way.html --- a/kallithea/templates/files/diff_2way.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/files/diff_2way.html Tue Feb 28 17:19:00 2017 +0100 @@ -61,8 +61,8 @@
@@ -372,7 +372,7 @@ show_comment_form($(this)); }); - var avail_jsdata = ${c.avail_jsdata|n}; + var avail_jsdata = ${h.js(c.avail_jsdata)}; var avail_r = new BranchRenderer('avail_graph_canvas', 'updaterevs-table', 'chg_available_'); avail_r.render(avail_jsdata); diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/register.html --- a/kallithea/templates/register.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/register.html Tue Feb 28 17:19:00 2017 +0100 @@ -92,7 +92,7 @@ $('#username').focus(); %if c.captcha_active: - Recaptcha.create("${c.captcha_public_key}", "recaptcha", + Recaptcha.create(${h.js(c.captcha_public_key)}, "recaptcha", { theme: "white" } diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/summary/statistics.html --- a/kallithea/templates/summary/statistics.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/summary/statistics.html Tue Feb 28 17:19:00 2017 +0100 @@ -53,7 +53,7 @@ diff -r 482c531e62ef -r 33b71a130b16 kallithea/templates/summary/summary.html --- a/kallithea/templates/summary/summary.html Mon Mar 06 02:23:26 2017 +0100 +++ b/kallithea/templates/summary/summary.html Tue Feb 28 17:19:00 2017 +0100 @@ -43,7 +43,7 @@ redirect_hash_branch = function(){ var branch = window.location.hash.replace(/^#(.*)/, '$1'); if (branch){ - window.location = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}" + window.location = ${h.js(h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__'))} .replace('__BRANCH__',branch); } } @@ -252,7 +252,7 @@ query.callback(data); }else{ $.ajax({ - url: pyroutes.url('repo_refs_data', {'repo_name': '${c.repo_name}'}), + url: pyroutes.url('repo_refs_data', {'repo_name': ${h.js(c.repo_name)}}), data: {}, dataType: 'json', type: 'GET', @@ -271,7 +271,7 @@ for(k in tmpl_links){ var s = $('#'+k+'_link'); if(s){ - var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}"; + var title_tmpl = ${h.jshtml(_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__'))}; title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text); title_tmpl = title_tmpl.replace('__CS_EXT__',k); title_tmpl = ' '+ title_tmpl; @@ -287,7 +287,7 @@ var tmpl_links = {}; %for cnt,archive in enumerate(c.db_repo_scm_instance._get_archives()): - tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.db_repo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='btn btn-default btn-sm')}'; + tmpl_links[${h.jshtml(archive['type'])}] = ${h.js(h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.db_repo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='btn btn-default btn-sm'))}; %endfor }); @@ -295,7 +295,7 @@ %if c.show_stats: