templates: Fix missing quoting of attributes in HTML templates.

This fixes a bundle of issues where we were missing "" around
attributes coming from variables.  In most cases, the variables were
integers or fixed constants from the Zulip codebase (E.g. the name of
an installed integration), but in at least one case it was
user-provided data that could potentially have security impact.
This commit is contained in:
Tim Abbott
2020-05-12 22:26:30 -07:00
parent 87f7874a79
commit 4fff858aa2
11 changed files with 20 additions and 14 deletions

View File

@@ -1,6 +1,6 @@
<div class="emoji-popover">
<div class="emoji-popover-top input-append">
<input class="emoji-popover-filter" type="text" autofocus placeholder={{t 'Search' }} />
<input class="emoji-popover-filter" type="text" autofocus placeholder="{{t 'Search' }}" />
<i class="fa fa-search" aria-hidden="true"></i>
</div>
<div class="emoji-popover-category-tabs">

View File

@@ -1,5 +1,5 @@
{{#with emoji_dict}}
<div class="emoji-popover-emoji {{#if ../message_id }}{{#if has_reacted}} reacted {{/if}} reaction {{else}} composition {{/if}}" data-emoji-name={{name}} tabindex="0" data-emoji-id="{{../type}},{{../section}},{{../index}}">
<div class="emoji-popover-emoji {{#if ../message_id }}{{#if has_reacted}} reacted {{/if}} reaction {{else}} composition {{/if}}" data-emoji-name="{{name}}" tabindex="0" data-emoji-id="{{../type}},{{../section}},{{../index}}">
{{#if is_realm_emoji}}
<img src="{{url}}" class="emoji"/>
{{else}}

View File

@@ -1,6 +1,6 @@
<div class='pill' data-id='{{ id }}' tabindex=0>
{{#if has_image}}
<img class="pill-image" src={{img_src}} />
<img class="pill-image" src="{{img_src}}" />
{{/if}}
<span class="pill-value">{{ display_value }}</span>
<div class="exit">

View File

@@ -1,4 +1,4 @@
<div class="{{this.class}}" aria-label="{{this.label}}" data-reaction-id={{this.local_id}}>
<div class="{{this.class}}" aria-label="{{this.label}}" data-reaction-id="{{this.local_id}}">
{{#if this.emoji_alt_code}}
<div class="emoji_alt_code">&nbsp:{{this.emoji_name}}:</div>
{{else}}

View File

@@ -1,5 +1,5 @@
{{#with muted_topics}}
<tr data-stream-id={{stream_id}} data-stream="{{stream}}" data-topic="{{topic}}" data-date-muted="{{date_muted_str}}">
<tr data-stream-id="{{stream_id}}" data-stream="{{stream}}" data-topic="{{topic}}" data-date-muted="{{date_muted_str}}">
<td>{{stream}}</td>
<td>{{topic}}</td>
<td>{{date_muted_str}}</td>

View File

@@ -76,7 +76,7 @@
{{/each}}
</select>
<span id="play_notification_sound">
<i class="fa fa-play-circle" aria-label={{t "Play sound" }}></i>
<i class="fa fa-play-circle" aria-label="{{t 'Play sound' }}"></i>
</span>
</div>

View File

@@ -50,9 +50,9 @@
<div class="input" contenteditable="false" style="display: none;"></div>
</div>
{{else if this.is_link}}
<a href={{this.value}} target="_blank" class="value">{{this.value}}</a>
<a href="{{this.value}}" target="_blank" class="value">{{this.value}}</a>
{{else if this.is_external_account}}
<a href={{this.link}} target="_blank" class="value">{{this.value}}</a>
<a href="{{this.link}}" target="_blank" class="value">{{this.value}}</a>
{{else}}
{{#if this.rendered_value}}
<div class="value rendered_markdown">{{rendered_markdown this.rendered_value}}</div>

View File

@@ -5,7 +5,7 @@
<span class="message_sender{% if status_message %} sender_info_hover{% endif %} no-select">
{% if include_sender %}
<div class="inline_profile_picture">
<img src={{ avatar_url }} alt="" class="no-drag"/>
<img src="{{ avatar_url }}" alt="" class="no-drag"/>
</div>
{% if status_message %}
<span class="sender-status">

View File

@@ -144,7 +144,7 @@
{% for integration in integrations_dict.values() %}
{% if integration.is_enabled() %}
<div id={{ integration.name }} class="integration-instructions">
<div id="{{ integration.name }}" class="integration-instructions">
<div class="help-content"></div>
<p style="font-size:11px; font-style:italic;">
Logos are trademarks of their respective owners.

View File

@@ -28,7 +28,7 @@ Form is validated both client-side using jquery-validate (see signup.js) and ser
<input id="id_team_name" class="required" type="text"
placeholder="Acme or Ακμή"
value="{% if form.realm_name.value() %}{{ form.realm_name.value() }}{% endif %}"
name="realm_name" maxlength={{ MAX_REALM_NAME_LENGTH }} required />
name="realm_name" maxlength="{{ MAX_REALM_NAME_LENGTH }}" required />
</div>
<label for="id_team_name" class="inline-block label-title">{{ _('Organization name') }}</label>
{% if form.realm_name.errors %}
@@ -63,7 +63,7 @@ Form is validated both client-side using jquery-validate (see signup.js) and ser
class="{% if root_domain_landing_page %}required{% endif %} subdomain" type="text"
placeholder="acme"
value="{% if form.realm_subdomain.value() %}{{ form.realm_subdomain.value() }}{% endif %}"
name="realm_subdomain" maxlength={{ MAX_REALM_SUBDOMAIN_LENGTH }}
name="realm_subdomain" maxlength="{{ MAX_REALM_SUBDOMAIN_LENGTH }}"
{% if root_domain_landing_page %}required{% endif %} />
<label for="id_team_subdomain" class="realm_subdomain_label">.{{ external_host }}</label>
<p id="id_team_subdomain_error_client" class="error help-inline text-error"></p>
@@ -124,7 +124,7 @@ Form is validated both client-side using jquery-validate (see signup.js) and ser
{% else %}
<input id="id_full_name" class="required" type="text" name="full_name"
value="{% if full_name %}{{ full_name }}{% elif form.full_name.value() %}{{ form.full_name.value() }}{% endif %}"
maxlength={{ MAX_NAME_LENGTH }} placeholder="{% trans %}Full name or 名前{% endtrans %}" required />
maxlength="{{ MAX_NAME_LENGTH }}" placeholder="{% trans %}Full name or 名前{% endtrans %}" required />
<label for="id_full_name" class="inline-block label-title">{{ _('Full name') }}</label>
{% if form.full_name.errors %}
{% for error in form.full_name.errors %}
@@ -151,7 +151,7 @@ Form is validated both client-side using jquery-validate (see signup.js) and ser
<div class="input-box">
<input id="id_password" class="required" type="password" name="password"
value="{% if form.password.value() %}{{ form.password.value() }}{% endif %}"
maxlength={{ MAX_PASSWORD_LENGTH }}
maxlength="{{ MAX_PASSWORD_LENGTH }}"
data-min-length="{{password_min_length}}"
data-min-guesses="{{password_min_guesses}}" required />
<label for="id_password" class="inline-block">{{ _('Password') }}</label>

View File

@@ -498,6 +498,12 @@ html_rules: List["Rule"] = whitespace_rules + prose_style_rules + [
'exclude': {"templates/analytics/support.html"},
'good_lines': ['<input class="stream-list-filter" type="text" placeholder="{{ _(\'Search streams\') }}" />'],
'bad_lines': ['<input placeholder="foo">']},
{'pattern': '={',
# TODO: Improve the Apple auth patterns so we can remove this.
'exclude_pattern': 'appleid.cdn-apple.com/appleid/button',
'description': "Likely missing quoting in HTML attribute",
'good_lines': ['<a href="{{variable}}">'],
'bad_lines': ['<a href={{variable}}>']},
{'pattern': "placeholder='[^{]",
'description': "`placeholder` value should be translatable.",
'good_lines': ['<input class="stream-list-filter" type="text" placeholder="{{ _(\'Search streams\') }}" />'],