зеркало из https://github.com/mozilla/bedrock.git
Fix data string escape leakage by moving error strings to the DOM (Fixes #12789)
This commit is contained in:
Родитель
3d23083de6
Коммит
02f3447b06
|
@ -20,29 +20,6 @@
|
||||||
|
|
||||||
{% block body_class %}newsletter-management{% endblock %}
|
{% block body_class %}newsletter-management{% endblock %}
|
||||||
|
|
||||||
{% set recovery_href = 'href="' + url('newsletter.recovery') + '"'|escape %}
|
|
||||||
|
|
||||||
{% block string_data %}
|
|
||||||
{#
|
|
||||||
Note the outer single quotes wrapping data-error-token-not-found, not
|
|
||||||
doubles - this is critical to avoiding some over-escaping of quotes
|
|
||||||
in `recovery_href` when we bleach during the ftl() call
|
|
||||||
See: https://github.com/mozilla/bedrock/issues/12789
|
|
||||||
#}
|
|
||||||
|
|
||||||
#}
|
|
||||||
{% if ftl_has_messages('newsletters-the-supplied-link-has-expired-v2') %}
|
|
||||||
data-error-token-not-found='{{ ftl("newsletters-the-supplied-link-has-expired-v2", recovery_href=recovery_href) }}'
|
|
||||||
{% else %}
|
|
||||||
data-error-token-not-found='{{ ftl("newsletters-the-supplied-link-has-expired") }}'
|
|
||||||
{% endif %}
|
|
||||||
data-error-invalid-email="{{ ftl('newsletters-this-is-not-a-valid-email') }}"
|
|
||||||
data-error-invalid-newsletter="{{ ftl('newsletters-is-not-a-valid-newsletter', newsletter='%newsletter%') }}"
|
|
||||||
data-error-select-country="{{ ftl('newsletters-please-select-country') }}"
|
|
||||||
data-error-select-lang="{{ ftl('newsletters-please-select-language') }}"
|
|
||||||
data-error-try-again-later="{{ ftl('newsletters-something-is-amiss-with') }}"
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% set maintenance_mode = switch('newsletter-maintenance-mode') %}
|
{% set maintenance_mode = switch('newsletter-maintenance-mode') %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
@ -168,6 +145,35 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
{# Error strings exist in the DOM instead of JSON as they can still be needed when fetching data fails! #}
|
||||||
|
{% set recovery_href = 'href="' + url('newsletter.recovery') + '"' %}
|
||||||
|
<div class="template-error-strings" hidden>
|
||||||
|
<ul>
|
||||||
|
<li class="error-token-not-found">
|
||||||
|
{% if ftl_has_messages('newsletters-the-supplied-link-has-expired-v2') %}
|
||||||
|
{{ ftl("newsletters-the-supplied-link-has-expired-v2", recovery_href=recovery_href) }}
|
||||||
|
{% else %}
|
||||||
|
{{ ftl("newsletters-the-supplied-link-has-expired") }}
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
<li class="error-invalid-email">
|
||||||
|
{{ ftl('newsletters-this-is-not-a-valid-email') }}
|
||||||
|
</li>
|
||||||
|
<li class="error-invalid-newsletter">
|
||||||
|
{{ ftl('newsletters-is-not-a-valid-newsletter', newsletter='%newsletter%') }}
|
||||||
|
</li>
|
||||||
|
<li class="error-select-country">
|
||||||
|
{{ ftl('newsletters-please-select-country') }}
|
||||||
|
</li>
|
||||||
|
<li class="error-select-lang">
|
||||||
|
{{ ftl('newsletters-please-select-language') }}
|
||||||
|
</li>
|
||||||
|
<li class="error-try-again-later">
|
||||||
|
{{ ftl('newsletters-something-is-amiss-with') }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{# Don't display the footer if there is a token present. bug 1247446 #}
|
{# Don't display the footer if there is a token present. bug 1247446 #}
|
||||||
|
|
|
@ -479,8 +479,7 @@ const NewsletterManagementForm = {
|
||||||
FormUtils.clearFormErrors(_form);
|
FormUtils.clearFormErrors(_form);
|
||||||
|
|
||||||
errors.forEach((error) => {
|
errors.forEach((error) => {
|
||||||
const item = `<li>${error}</li>`;
|
list.insertAdjacentElement('afterbegin', error);
|
||||||
list.insertAdjacentHTML('afterbegin', item);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
errorContainer.classList.remove('hidden');
|
errorContainer.classList.remove('hidden');
|
||||||
|
@ -538,32 +537,40 @@ const NewsletterManagementForm = {
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
handleFormError: (msg, newsletterId) => {
|
handleFormError: (msg, newsletterId) => {
|
||||||
const strings = document.getElementById('strings');
|
const strings = document.querySelector('.template-error-strings');
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case FormUtils.errorList.NOT_FOUND:
|
case FormUtils.errorList.NOT_FOUND:
|
||||||
error = strings.getAttribute('data-error-token-not-found');
|
error = strings.querySelector('.error-token-not-found');
|
||||||
break;
|
break;
|
||||||
case FormUtils.errorList.EMAIL_INVALID_ERROR:
|
case FormUtils.errorList.EMAIL_INVALID_ERROR:
|
||||||
error = strings.getAttribute('data-error-invalid-email');
|
error = strings.querySelector('.error-invalid-email');
|
||||||
break;
|
break;
|
||||||
case FormUtils.errorList.NEWSLETTER_ERROR:
|
case FormUtils.errorList.NEWSLETTER_ERROR:
|
||||||
error = strings.getAttribute('data-error-invalid-newsletter');
|
error = strings.querySelector('.error-invalid-newsletter');
|
||||||
|
|
||||||
// replace '%newsletter%' placeholder with actual newsletter ID.
|
// replace '%newsletter%' placeholder with actual newsletter ID.
|
||||||
if (typeof newsletterId === 'string') {
|
if (typeof newsletterId === 'string') {
|
||||||
error = error.replace('%newsletter%', newsletterId);
|
const temp = error.textContent.replace(
|
||||||
|
'%newsletter%',
|
||||||
|
newsletterId
|
||||||
|
);
|
||||||
|
error.textContent = temp;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FormUtils.errorList.COUNTRY_ERROR:
|
case FormUtils.errorList.COUNTRY_ERROR:
|
||||||
error = strings.getAttribute('data-error-select-country');
|
error = strings.querySelector('.error-select-country');
|
||||||
break;
|
break;
|
||||||
case FormUtils.errorList.LANGUAGE_ERROR:
|
case FormUtils.errorList.LANGUAGE_ERROR:
|
||||||
error = strings.getAttribute('data-error-select-lang');
|
error = strings.querySelector('.error-select-lang');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error = strings.getAttribute('data-error-try-again-later');
|
error = strings.querySelector('.error-try-again-later');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error.cloneNode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
|
@ -797,7 +804,11 @@ const NewsletterManagementForm = {
|
||||||
NewsletterManagementForm.onDataError(e);
|
NewsletterManagementForm.onDataError(e);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(NewsletterManagementForm.redirectToRecoveryPage);
|
.catch(() => {
|
||||||
|
if (!FormUtils.getUserToken()) {
|
||||||
|
NewsletterManagementForm.redirectToRecoveryPage();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ const TOKEN_MOCK = 'a1a2a3a4-abc1-12ab-a123-12345a12345b';
|
||||||
describe('management.es6.js', function () {
|
describe('management.es6.js', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
const form = `<div id="newsletter-management-test-form">
|
const form = `<div id="newsletter-management-test-form">
|
||||||
<div id="strings" data-error-token-not-found="The supplied link has expired. You will receive a new one in the next newsletter." data-error-invalid-email="This is not a valid email address. Please check the spelling." data-error-invalid-newsletter="%newsletter% is not a valid newsletter" data-error-select-country="Please select a country or region" data-error-select-lang="Please select a language" data-error-try-again-later="Something is amiss with our system, sorry! Please try again later."></div>
|
|
||||||
<header class="mzp-l-content mzp-t-content-lg">
|
<header class="mzp-l-content mzp-t-content-lg">
|
||||||
<h1>Manage your Email Preferences</h1>
|
<h1>Manage your Email Preferences</h1>
|
||||||
<div class="js-intro-msg">
|
<div class="js-intro-msg">
|
||||||
|
@ -96,6 +95,28 @@ describe('management.es6.js', function () {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<div class="template-error-strings" hidden="">
|
||||||
|
<ul>
|
||||||
|
<li class="error-token-not-found">
|
||||||
|
The supplied link has expired. Please <a href="/en-US/newsletter/recovery/">request a new link here</a>.
|
||||||
|
</li>
|
||||||
|
<li class="error-invalid-email">
|
||||||
|
This is not a valid email address. Please check the spelling.
|
||||||
|
</li>
|
||||||
|
<li class="error-invalid-newsletter">
|
||||||
|
%newsletter% is not a valid newsletter
|
||||||
|
</li>
|
||||||
|
<li class="error-select-country">
|
||||||
|
Please select a country or region
|
||||||
|
</li>
|
||||||
|
<li class="error-select-lang">
|
||||||
|
Please select a language
|
||||||
|
</li>
|
||||||
|
<li class="error-try-again-later">
|
||||||
|
Something is amiss with our system, sorry! Please try again later.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
document.body.insertAdjacentHTML('beforeend', form);
|
document.body.insertAdjacentHTML('beforeend', form);
|
||||||
|
@ -579,11 +600,16 @@ describe('management.es6.js', function () {
|
||||||
spyOn(NewsletterManagementForm, 'getFormLang').and.returnValue(
|
spyOn(NewsletterManagementForm, 'getFormLang').and.returnValue(
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
expect(NewsletterManagementForm.validateFields()).toEqual([
|
const errors = NewsletterManagementForm.validateFields();
|
||||||
'bargain-hunters-weekly is not a valid newsletter',
|
expect(errors[0].textContent.trim()).toEqual(
|
||||||
'Please select a country or region',
|
'bargain-hunters-weekly is not a valid newsletter'
|
||||||
|
);
|
||||||
|
expect(errors[1].textContent.trim()).toEqual(
|
||||||
|
'Please select a country or region'
|
||||||
|
);
|
||||||
|
expect(errors[2].textContent.trim()).toEqual(
|
||||||
'Please select a language'
|
'Please select a language'
|
||||||
]);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -873,11 +899,11 @@ describe('management.es6.js', function () {
|
||||||
).and.returnValue(window.Promise.resolve(stringData));
|
).and.returnValue(window.Promise.resolve(stringData));
|
||||||
|
|
||||||
return NewsletterManagementForm.init().then(() => {
|
return NewsletterManagementForm.init().then(() => {
|
||||||
expect(
|
const error = document
|
||||||
document.querySelector('.mzp-c-form-errors li:nth-child(1)')
|
.querySelector('.mzp-c-form-errors li:nth-child(1)')
|
||||||
.innerText
|
.innerHTML.trim();
|
||||||
).toEqual(
|
expect(error).toEqual(
|
||||||
'The supplied link has expired. You will receive a new one in the next newsletter.'
|
'The supplied link has expired. Please <a href="/en-US/newsletter/recovery/">request a new link here</a>.'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -899,10 +925,10 @@ describe('management.es6.js', function () {
|
||||||
).and.returnValue(window.Promise.resolve(stringData));
|
).and.returnValue(window.Promise.resolve(stringData));
|
||||||
|
|
||||||
return NewsletterManagementForm.init().then(() => {
|
return NewsletterManagementForm.init().then(() => {
|
||||||
expect(
|
const error = document
|
||||||
document.querySelector('.mzp-c-form-errors li:nth-child(1)')
|
.querySelector('.mzp-c-form-errors li:nth-child(1)')
|
||||||
.innerText
|
.innerHTML.trim();
|
||||||
).toEqual(
|
expect(error).toEqual(
|
||||||
'Something is amiss with our system, sorry! Please try again later.'
|
'Something is amiss with our system, sorry! Please try again later.'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
Загрузка…
Ссылка в новой задаче