Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Bug 808946 - Escape HTML on Contacts app

Fixing tests

Fixing printing social icons

Ensuring bday rendering

Fixing tests

Usin 'utils' variable
  • Loading branch information...
commit 652e1ab597deefe4cdf5c6b9622d603d8eb68b16 1 parent 2b436ac
@albertopq albertopq authored
View
42 apps/communications/contacts/js/contacts_details.js
@@ -241,7 +241,13 @@ contacts.Details = (function() {
var f = new navigator.mozL10n.DateTimeFormat();
var birthdayFormat = _('birthdayDateFormat') || '%e %B';
- var birthdayString = f.localeFormat(contact.bday, birthdayFormat);
+ var birthdayString = '';
+ try {
+ birthdayString = f.localeFormat(contact.bday, birthdayFormat);
+ } catch (err) {
+ console.error('Error parsing birthday');
+ return;
+ }
var element = utils.templates.render(birthdayTemplate, {
i: contact.id,
@@ -331,10 +337,11 @@ contacts.Details = (function() {
var telLength = Contacts.getLength(contact.tel);
for (var tel = 0; tel < telLength; tel++) {
var currentTel = contact.tel[tel];
+ var escapedType = utils.text.escapeHTML(currentTel.type, true);
var telField = {
- value: currentTel.value || '',
- type: currentTel.type || TAG_OPTIONS['phone-type'][0].value,
- carrier: currentTel.carrier || '',
+ value: utils.text.escapeHTML(currentTel.value, true) || '',
+ type: escapedType || TAG_OPTIONS['phone-type'][0].value,
+ carrier: utils.text.escapeHTML(currentTel.carrier, true) || '',
i: tel
};
var template = utils.templates.render(phonesTemplate, telField);
@@ -369,9 +376,10 @@ contacts.Details = (function() {
var emailLength = Contacts.getLength(contact.email);
for (var email = 0; email < emailLength; email++) {
var currentEmail = contact.email[email];
+ var escapedType = utils.text.escapeHTML(currentEmail['type'], true);
var emailField = {
- value: currentEmail['value'] || '',
- type: currentEmail['type'] || TAG_OPTIONS['email-type'][0].value,
+ value: utils.text.escapeHTML(currentEmail['value'], true) || '',
+ type: escapedType || TAG_OPTIONS['email-type'][0].value,
i: email
};
var template = utils.templates.render(emailsTemplate, emailField);
@@ -403,12 +411,22 @@ contacts.Details = (function() {
'locality', 'countryName'])) {
continue;
}
+ var address = currentAddress['streetAddress'] || '';
+ var escapedStreet = utils.text.escapeHTML(address, true);
+ var locality = currentAddress['locality'];
+ var escapedLocality = utils.text.escapeHTML(locality, true);
+ var escapedType = utils.text.escapeHTML(currentAddress['type'], true);
+ var country = currentAddress['countryName'] || '';
+ var escapedCountry = utils.text.escapeHTML(country, true);
+ var postalCode = currentAddress['postalCode'] || '';
+ var escapedPostalCode = utils.text.escapeHTML(postalCode, true);
+
var addressField = {
- streetAddress: currentAddress['streetAddress'] || '',
- postalCode: currentAddress['postalCode'] || '',
- locality: currentAddress['locality'] || '',
- countryName: currentAddress['countryName'] || '',
- type: currentAddress['type'] || TAG_OPTIONS['address-type'][0].value,
+ streetAddress: escapedStreet,
+ postalCode: escapedPostalCode,
+ locality: escapedLocality || '',
+ countryName: escapedCountry,
+ type: escapedType || TAG_OPTIONS['address-type'][0].value,
i: i
};
var template = utils.templates.render(addressesTemplate, addressField);
@@ -427,7 +445,7 @@ contacts.Details = (function() {
for (var i = 0; i < contact.note.length; i++) {
var currentNote = contact.note[i];
var noteField = {
- note: currentNote || '',
+ note: utils.text.escapeHTML(currentNote, true) || '',
i: i
};
var template = utils.templates.render(notesTemplate, noteField);
View
3  apps/communications/contacts/js/contacts_form.js
@@ -48,7 +48,7 @@ contacts.Form = (function() {
var fields = form.querySelectorAll('input.textfield');
var removedFields =
Array.slice(form.querySelectorAll('.removed input.textfield'));
- this._textFields = Array.filter(fields, function (field) {
+ this._textFields = Array.filter(fields, function(field) {
return removedFields.indexOf(field) == -1;
});
}
@@ -270,6 +270,7 @@ contacts.Form = (function() {
var def = (currentElem === 'type') ? default_type : '';
var defObj = (typeof(obj) === 'string') ? obj : obj[currentElem];
var value = currField[currentElem] = defObj || def;
+ currField[currentElem] = utils.text.escapeHTML(value, true);
if (!infoFromFB && value && nonEditableValues[value]) {
infoFromFB = true;
}
View
21 apps/communications/contacts/js/contacts_list.js
@@ -105,7 +105,7 @@ contacts.List = (function() {
contact.org = contact.org || '';
var contactContainer = document.createElement('li');
contactContainer.className = 'contact-item';
- contactContainer.dataset.uuid = contact.id;
+ contactContainer.dataset.uuid = utils.text.escapeHTML(contact.id, true);
var timestampDate = contact.updated || contact.published || new Date();
contactContainer.dataset.updated = timestampDate.getTime();
var link = document.createElement('a');
@@ -116,7 +116,11 @@ contacts.List = (function() {
var figure = document.createElement('aside');
figure.className = 'pack-end';
var img = document.createElement('img');
- img.dataset.src = window.URL.createObjectURL(contact.photo[0]);
+ try {
+ img.dataset.src = window.URL.createObjectURL(contact.photo[0]);
+ } catch(err) {
+ img.dataset.src = '';
+ }
figure.appendChild(img);
link.appendChild(figure);
}
@@ -134,7 +138,8 @@ contacts.List = (function() {
}
}
});
- name.dataset['search'] = normalizeText(searchInfo.join(' '));
+ var escapedValue = utils.text.escapeHTML(searchInfo.join(' '), true);
+ name.dataset['search'] = utils.text.normalize(escapedValue);
// Label the contact concerning social networks
var meta = document.createElement('p');
@@ -151,7 +156,7 @@ contacts.List = (function() {
}
}
//Add organization name
- meta.innerHTML += contact.org;
+ meta.innerHTML += utils.text.escapeHTML(contact.org, true);
//Final item structure
link.appendChild(name);
@@ -162,10 +167,12 @@ contacts.List = (function() {
}
var getHighlightedName = function getHighlightedName(contact) {
+ var givenName = utils.text.escapeHTML(contact.givenName);
+ var familyName = utils.text.escapeHTML(contact.familyName);
if (orderByLastName) {
- return contact.givenName + ' <strong>' + contact.familyName + '</strong>';
+ return givenName + ' <strong>' + familyName + '</strong>';
} else {
- return '<strong>' + contact.givenName + '</strong> ' + contact.familyName;
+ return '<strong>' + givenName + '</strong> ' + familyName;
}
};
@@ -575,7 +582,7 @@ contacts.List = (function() {
var getGroupName = function getGroupName(contact) {
var ret = getStringToBeOrdered(contact);
- ret = normalizeText(ret.charAt(0).toUpperCase());
+ ret = utils.text.normalize(ret.charAt(0).toUpperCase());
var code = ret.charCodeAt(0);
if (code < 65 || code > 90) {
View
2  apps/communications/contacts/js/fb/friends_list.js
@@ -37,7 +37,7 @@ fbFriends.List = (function() {
searchInfo.push(friend[field][0]);
}
});
- friend.search = normalizeText(searchInfo.join(' '));
+ friend.search = utils.text.normalize(searchInfo.join(' '));
// New friend appended
utils.templates.append(ele, friend);
// We check wether this friend was in the AB or not before
View
2  apps/communications/contacts/js/search.js
@@ -49,7 +49,7 @@ contacts.Search = (function() {
var search = function performSearch() {
- var pattern = new RegExp(normalizeText(searchBox.value.trim()), 'i');
+ var pattern = new RegExp(utils.text.normalize(searchBox.value.trim()), 'i');
var count = 0;
var allContacts = getContactsDom();
View
76 apps/communications/contacts/js/utilities/normalizer.js
@@ -1,33 +1,51 @@
'use strict';
-// This should be fixed at a plaftorm level using
-// an utf8 normalized form.
-// Platform bug: https://bugzilla.mozilla.org/show_bug.cgi?id=779068
-// Please remove when this bug is fixed.
-
-function normalizeText(value) {
- var map = [
- ['[àáâãäå]', 'a'],
- ['æ', 'ae'],
- ['ç', 'c'],
- ['[èéêë]', 'e'],
- ['[ìíîï]', 'i'],
- ['ñ', 'n'],
- ['[òóôõö]', 'o'],
- ['œ', 'oe'],
- ['[ùúûü]', 'u'],
- ['[ýÿ]', 'y']
- ];
-
- for (var i = 0; i < map.length; i++) {
- value = value.replace(new RegExp(map[i][0], 'gi'), function(match) {
- if (match.toUpperCase() === match) {
- return map[i][1].toUpperCase();
- } else {
- return map[i][1];
+var utils = window.utils || {};
+
+if (!utils.text) {
+ (function() {
+ var Text = utils.text = {};
+
+ // This should be fixed at a plaftorm level using
+ // an utf8 normalized form.
+ // Platform bug: https://bugzilla.mozilla.org/show_bug.cgi?id=779068
+ // Please remove when this bug is fixed.
+
+ Text.normalize = function normalizeText(value) {
+ var map = [
+ ['[àáâãäå]', 'a'],
+ ['æ', 'ae'],
+ ['ç', 'c'],
+ ['[èéêë]', 'e'],
+ ['[ìíîï]', 'i'],
+ ['ñ', 'n'],
+ ['[òóôõö]', 'o'],
+ ['œ', 'oe'],
+ ['[ùúûü]', 'u'],
+ ['[ýÿ]', 'y']
+ ];
+
+ for (var i = 0; i < map.length; i++) {
+ value = value.replace(new RegExp(map[i][0], 'gi'), function(match) {
+ if (match.toUpperCase() === match) {
+ return map[i][1].toUpperCase();
+ } else {
+ return map[i][1];
+ }
+ });
}
- });
- }
- return value;
-}
+ return value;
+ };
+
+ // Taken from /apps/browser/js/browser.js
+ Text.escapeHTML = function ut_escapeHTML(str, escapeQuotes) {
+ var span = document.createElement('span');
+ span.textContent = str;
+
+ if (escapeQuotes)
+ return span.innerHTML.replace(/"/g, '&quot;').replace(/'/g, '&#x27;'); //"
+ return span.innerHTML;
+ }
+ })();
+}
View
31 apps/communications/contacts/test/unit/contact_details_test.js
@@ -2,6 +2,7 @@
requireApp('communications/contacts/test/unit/mock_details_dom.js.html');
requireApp('communications/contacts/js/contacts_details.js');
+requireApp('communications/contacts/js/utilities/normalizer.js');
requireApp('communications/contacts/js/utilities/templates.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
requireApp('communications/contacts/test/unit/mock_contact_all_fields.js');
@@ -391,10 +392,10 @@ suite('Render contact', function() {
subject.render(null, TAG_OPTIONS);
assert.include(container.innerHTML, 'address-details-template-0');
var address0 = mockContact.adr[0];
- assert.include(container.innerHTML, address0.countryName);
- assert.include(container.innerHTML, address0.locality);
- assert.include(container.innerHTML, address0.postalCode);
- assert.include(container.innerHTML, address0.streetAddress);
+ assert.include(container.innerHTML, utils.text.escapeHTML(address0.countryName, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address0.locality, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address0.postalCode, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address0.streetAddress, true));
});
test('with no addresses', function() {
@@ -426,14 +427,14 @@ suite('Render contact', function() {
assert.include(container.innerHTML, 'address-details-template-1');
var address0 = contactMultAddress.adr[0];
var address1 = contactMultAddress.adr[1];
- assert.include(container.innerHTML, address0.countryName);
- assert.include(container.innerHTML, address0.locality);
- assert.include(container.innerHTML, address0.postalCode);
- assert.include(container.innerHTML, address0.streetAddress);
- assert.include(container.innerHTML, address1.countryName);
- assert.include(container.innerHTML, address1.locality);
- assert.include(container.innerHTML, address1.postalCode);
- assert.include(container.innerHTML, address1.streetAddress);
+ assert.include(container.innerHTML, utils.text.escapeHTML(address0.countryName, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address0.locality, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address0.postalCode, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address0.streetAddress, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address1.countryName, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address1.locality, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address1.postalCode, true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(address1.streetAddress, true));
var toCheck = container.innerHTML;
assert.equal(-1, toCheck.indexOf('address-details-template-2'));
});
@@ -442,7 +443,7 @@ suite('Render contact', function() {
test('with 1 note', function() {
subject.render(null, TAG_OPTIONS);
assert.include(container.innerHTML, 'note-details-template-0');
- assert.include(container.innerHTML, mockContact.note[0]);
+ assert.include(container.innerHTML, utils.text.escapeHTML(mockContact.note[0], true));
});
test('with no notes', function() {
@@ -472,8 +473,8 @@ suite('Render contact', function() {
subject.render(null, TAG_OPTIONS);
assert.include(container.innerHTML, 'note-details-template-0');
assert.include(container.innerHTML, 'note-details-template-1');
- assert.include(container.innerHTML, contactMultNote.note[0]);
- assert.include(container.innerHTML, contactMultNote.note[1]);
+ assert.include(container.innerHTML, utils.text.escapeHTML(contactMultNote.note[0], true));
+ assert.include(container.innerHTML, utils.text.escapeHTML(contactMultNote.note[1], true));
assert.equal(-1, container.innerHTML.indexOf('note-details-template-2'));
});
});
View
1  apps/communications/contacts/test/unit/contacts_form_test.js
@@ -2,6 +2,7 @@
requireApp('communications/contacts/test/unit/mock_form_dom.js.html');
requireApp('communications/contacts/js/contacts_form.js');
+requireApp('communications/contacts/js/utilities/normalizer.js');
requireApp('communications/contacts/js/utilities/templates.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
requireApp('communications/contacts/test/unit/mock_contact_all_fields.js');
View
39 apps/communications/contacts/test/unit/contacts_list_test.js
@@ -11,6 +11,7 @@ requireApp('communications/contacts/test/unit/mock_fixed_header.js');
requireApp('communications/contacts/test/unit/mock_fb.js');
requireApp('communications/contacts/test/unit/mock_extfb.js');
requireApp('communications/contacts/test/unit/mock_activities.js');
+requireApp('communications/contacts/test/unit/mock_utils.js');
// We're going to swap those with mock objects
// so we need to make sure they are defined.
@@ -37,12 +38,19 @@ if (!this.ActivityHandler) {
this.ActivityHandler = null;
}
+if (!this.ImageLoader) {
+ this.ImageLoader = null;
+}
+
+var URL = null;
+
suite('Render contacts list', function() {
var subject,
container,
realL10n,
realContacts,
realFb,
+ realImageLoader,
Contacts,
fb,
FixedHeader,
@@ -51,7 +59,10 @@ suite('Render contacts list', function() {
mockContacts,
mozL10n,
mockActivities,
+ mockImageLoader,
+ mockURL,
realActivities,
+ realURL,
groupA,
groupB,
groupC,
@@ -196,6 +207,10 @@ suite('Render contacts list', function() {
window.FixedHeader = MockFixedHeader;
realActivities = window.ActivityHandler;
window.ActivityHandler = MockActivities;
+ realImageLoader = window.ImageLoader;
+ window.ImageLoader = MockImageLoader;
+ window.URL = window.URL || {};
+ window.URL = MockURL;
window.utils = window.utils || {};
window.utils.alphaScroll = MockAlphaScroll;
subject = contacts.List;
@@ -211,6 +226,8 @@ suite('Render contacts list', function() {
window.fb = realFb;
window.mozL10n = realL10n;
window.ActivityHandler = realActivities;
+ window.ImageLoader = realActivities;
+ window.URL = MockURL;
});
suite('Render list', function() {
@@ -509,7 +526,7 @@ suite('Render contacts list', function() {
var contact = container.querySelector(selectorContact1);
var img = contact.querySelector('img');
- assert.isTrue(img.getAttribute('backgroundImage') === 'test.png',
+ assert.equal(img.dataset.src, 'test.png',
'At the begining contact 1 img === "test.png"');
var prevUpdated = contact.dataset.updated;
@@ -520,7 +537,7 @@ suite('Render contacts list', function() {
contact = container.querySelector(selectorContact1);
img = contact.querySelector('img');
- assert.isTrue(img.getAttribute('backgroundImage') === 'one.png',
+ assert.equal(img.dataset.src, 'one.png',
'After updating contact 1 img === "one.png"');
assert.isTrue(prevUpdated < contact.dataset.updated,
@@ -536,7 +553,7 @@ suite('Render contacts list', function() {
var contact = container.querySelector(selectorContact1);
var img = contact.querySelector('img');
- assert.isTrue(img.getAttribute('backgroundImage') === 'test.png',
+ assert.equal(img.dataset.src, 'test.png',
'At the begining contact 1 img === "test.png"');
subject.load(mockContacts);
@@ -544,7 +561,7 @@ suite('Render contacts list', function() {
contact = container.querySelector(selectorContact1);
img = contact.querySelector('img');
- assert.isTrue(img.getAttribute('backgroundImage') === 'test.png',
+ assert.equal(img.dataset.src, 'test.png',
'At the begining contact 1 img === "test.png"');
});
}); // suite ends
@@ -645,12 +662,12 @@ suite('Render contacts list', function() {
var printed = names[i];
var mockContact = mockContacts[i];
var expected = getSearchStringFromContact(mockContact);
- assert.equal(printed.dataset['search'], expected);
+ assert.equal(printed.dataset['search'], window.utils.text.escapeHTML(expected, true));
// Check as well the correct highlight
// familyName to be in bold
- var highlight = mockContact.givenName[0] + ' <strong>' +
- mockContact.familyName[0] + '</strong>';
+ var highlight = window.utils.text.escapeHTML(mockContact.givenName[0], true) + ' <strong>' +
+ window.utils.text.escapeHTML(mockContact.familyName[0], true) + '</strong>';
assert.isTrue(printed.innerHTML.indexOf(highlight) == 0);
}
});
@@ -663,14 +680,14 @@ suite('Render contacts list', function() {
var mockContact = mockContacts[mockContacts.length - 1];
var expected = getSearchStringFromContact(mockContact);
- assert.equal(name.dataset['search'], expected);
+ assert.equal(name.dataset['search'], window.utils.text.escapeHTML(expected, true));
// Check highlight
// Given name to be in bold
var highlight = '<strong>' +
- mockContact.givenName[0] + '</strong> ' +
- mockContact.familyName[0];
- assert.isTrue(name.innerHTML.indexOf(highlight) == 0);
+ window.utils.text.escapeHTML(mockContact.givenName[0], true) + '</strong> ' +
+ window.utils.text.escapeHTML(mockContact.familyName[0], true);
+ assert.equal(name.innerHTML.indexOf(highlight), 0);
});
});
});
View
12 apps/communications/contacts/test/unit/mock_utils.js
@@ -0,0 +1,12 @@
+'use strict';
+
+var MockURL = {
+ createObjectURL: function(url) {
+ return url;
+ }
+};
+
+var MockImageLoader = {
+ init: function() {},
+ reload: function() {}
+};
Please sign in to comment.
Something went wrong with that request. Please try again.