From 029a94a7af950c73f56649916b7bd179d7970f30 Mon Sep 17 00:00:00 2001 From: Reuben Morais Date: Mon, 3 Jun 2013 18:50:31 -0700 Subject: [PATCH] Bug 869615 - Sanitize Contact properties everywhere, not just in init. r=bent --HG-- extra : rebase_source : 25fd18299c15cee78dff008460c89e78ba60d842 --- dom/contacts/ContactManager.js | 359 +++++++++++++------ dom/contacts/tests/test_contacts_basics.html | 128 +++---- 2 files changed, 314 insertions(+), 173 deletions(-) diff --git a/dom/contacts/ContactManager.js b/dom/contacts/ContactManager.js index c87302cc874..297853b3455 100644 --- a/dom/contacts/ContactManager.js +++ b/dom/contacts/ContactManager.js @@ -205,6 +205,39 @@ const CONTACT_CONTRACTID = "@mozilla.org/contact;1"; const CONTACT_CID = Components.ID("{72a5ee28-81d8-4af8-90b3-ae935396cc66}"); const nsIDOMContact = Components.interfaces.nsIDOMContact; +function checkBlobArray(aBlob) { + if (Array.isArray(aBlob)) { + for (let i = 0; i < aBlob.length; i++) { + if (typeof aBlob != 'object') { + return null; + } + if (!(aBlob[i] instanceof Components.interfaces.nsIDOMBlob)) { + return null; + } + } + return aBlob; + } + return null; +} + +function isVanillaObj(aObj) { + return Object.prototype.toString.call(aObj) == "[object Object]"; +} + +function validateArrayField(data, createCb) { + if (data) { + data = Array.isArray(data) ? data : [data]; + let filtered = []; + for (let obj of data) { + if (obj && isVanillaObj(obj)) { + filtered.push(createCb(obj)); + } + } + return filtered; + } + return undefined; +} + function Contact() { }; Contact.prototype = { @@ -235,120 +268,220 @@ Contact.prototype = { genderIdentity: 'rw' }, + set name(aName) { + this._name = sanitizeStringArray(aName); + }, + + get name() { + return this._name; + }, + + set honorificPrefix(aHonorificPrefix) { + this._honorificPrefix = sanitizeStringArray(aHonorificPrefix); + }, + + get honorificPrefix() { + return this._honorificPrefix; + }, + + set givenName(aGivenName) { + this._givenName = sanitizeStringArray(aGivenName); + }, + + get givenName() { + return this._givenName; + }, + + set additionalName(aAdditionalName) { + this._additionalName = sanitizeStringArray(aAdditionalName); + }, + + get additionalName() { + return this._additionalName; + }, + + set familyName(aFamilyName) { + this._familyName = sanitizeStringArray(aFamilyName); + }, + + get familyName() { + return this._familyName; + }, + + set honorificSuffix(aHonorificSuffix) { + this._honorificSuffix = sanitizeStringArray(aHonorificSuffix); + }, + + get honorificSuffix() { + return this._honorificSuffix; + }, + + set nickname(aNickname) { + this._nickname = sanitizeStringArray(aNickname); + }, + + get nickname() { + return this._nickname; + }, + + set photo(aPhoto) { + this._photo = checkBlobArray(aPhoto); + }, + + get photo() { + return this._photo; + }, + + set category(aCategory) { + this._category = sanitizeStringArray(aCategory); + }, + + get category() { + return this._category; + }, + + set email(aEmail) { + this._email = validateArrayField(aEmail, function(email) { + return new ContactField(email.type, email.value, email.pref); + }); + }, + + get email() { + return this._email; + }, + + set adr(aAdr) { + this._adr = validateArrayField(aAdr, function(adr) { + return new ContactAddress(adr.type, adr.streetAddress, adr.locality, + adr.region, adr.postalCode, adr.countryName, + adr.pref); + }); + }, + + get adr() { + return this._adr; + }, + + set tel(aTel) { + this._tel = validateArrayField(aTel, function(tel) { + return new ContactTelField(tel.type, tel.value, tel.carrier, tel.pref); + }); + }, + + get tel() { + return this._tel; + }, + + set impp(aImpp) { + this._impp = validateArrayField(aImpp, function(impp) { + return new ContactField(impp.type, impp.value, impp.pref); + }); + }, + + get impp() { + return this._impp; + }, + + set url(aUrl) { + this._url = validateArrayField(aUrl, function(url) { + return new ContactField(url.type, url.value, url.pref); + }); + }, + + get url() { + return this._url; + }, + + set org(aOrg) { + this._org = sanitizeStringArray(aOrg); + }, + + get org() { + return this._org; + }, + + set jobTitle(aJobTitle) { + this._jobTitle = sanitizeStringArray(aJobTitle); + }, + + get jobTitle() { + return this._jobTitle; + }, + + set note(aNote) { + this._note = sanitizeStringArray(aNote); + }, + + get note() { + return this._note; + }, + + set bday(aBday) { + if (aBday !== undefined && aBday !== null) { + this._bday = new Date(aBday); + } + }, + + get bday() { + return this._bday; + }, + + set anniversary(aAnniversary) { + if (aAnniversary !== undefined && aAnniversary !== null) { + this._anniversary = new Date(aAnniversary); + } + }, + + get anniversary() { + return this._anniversary; + }, + + set sex(aSex) { + if (aSex !== "undefined") { + this._sex = aSex; + } else { + this._sex = null; + } + }, + + get sex() { + return this._sex; + }, + + set genderIdentity(aGenderIdentity) { + if (aGenderIdentity !== "undefined") { + this._genderIdentity = aGenderIdentity; + } else { + this._genderIdentity = null; + } + }, + + get genderIdentity() { + return this._genderIdentity; + }, + init: function init(aProp) { - function _checkBlobArray(aBlob) { - if (Array.isArray(aBlob)) { - for (let i = 0; i < aBlob.length; i++) { - if (typeof aBlob != 'object') { - return null; - } - if (!(aBlob[i] instanceof Components.interfaces.nsIDOMBlob)) { - return null; - } - } - return aBlob; - } - return null; - } - - function _isVanillaObj(aObj) { - return Object.prototype.toString.call(aObj) == "[object Object]"; - } - - let _create = sanitizeStringArray; - - this.name = _create(aProp.name); - this.honorificPrefix = _create(aProp.honorificPrefix); - this.givenName = _create(aProp.givenName); - this.additionalName = _create(aProp.additionalName); - this.familyName = _create(aProp.familyName); - this.honorificSuffix = _create(aProp.honorificSuffix); - this.nickname = _create(aProp.nickname); - - if (aProp.email) { - aProp.email = Array.isArray(aProp.email) ? aProp.email : [aProp.email]; - this.email = new Array(); - for (let email of aProp.email) { - if (_isVanillaObj(email)) { - this.email.push(new ContactField(email.type, email.value, email.pref)); - } else if (DEBUG) { - debug("email field is not a ContactField and was ignored."); - } - } - } else if (DEBUG) { - this.email = null; - } - - this.photo = _checkBlobArray(aProp.photo); - this.category = _create(aProp.category); - - if (aProp.adr) { - aProp.adr = Array.isArray(aProp.adr) ? aProp.adr : [aProp.adr]; - this.adr = new Array(); - for (let adr of aProp.adr) { - if (_isVanillaObj(adr)) { - this.adr.push(new ContactAddress(adr.type, adr.streetAddress, adr.locality, - adr.region, adr.postalCode, adr.countryName, - adr.pref)); - } else if (DEBUG) { - debug("adr field is not a ContactAddress and was ignored."); - } - } - } else { - this.adr = null; - } - - if (aProp.tel) { - aProp.tel = Array.isArray(aProp.tel) ? aProp.tel : [aProp.tel]; - this.tel = new Array(); - for (let tel of aProp.tel) { - if (_isVanillaObj(tel)) { - this.tel.push(new ContactTelField(tel.type, tel.value, tel.carrier, - tel.pref)); - } else if (DEBUG) { - debug("tel field is not a ContactTelField and was ignored."); - } - } - } else { - this.tel = null; - } - - this.org = _create(aProp.org); - this.jobTitle = _create(aProp.jobTitle); - this.bday = (aProp.bday == undefined || aProp.bday == null) ? null : new Date(aProp.bday); - this.note = _create(aProp.note); - - if (aProp.impp) { - aProp.impp = Array.isArray(aProp.impp) ? aProp.impp : [aProp.impp]; - this.impp = new Array(); - for (let impp of aProp.impp) { - if (_isVanillaObj(impp)) { - this.impp.push(new ContactField(impp.type, impp.value, impp.pref)); - } else if (DEBUG) { - debug("impp field is not a ContactField and was ignored."); - } - } - } else { - this.impp = null; - } - - if (aProp.url) { - aProp.url = Array.isArray(aProp.url) ? aProp.url : [aProp.url]; - this.url = new Array(); - for (let url of aProp.url) { - if (_isVanillaObj(url)) { - this.url.push(new ContactField(url.type, url.value, url.pref)); - } else if (DEBUG) { - debug("url field is not a ContactField and was ignored."); - } - } - } else { - this.url = null; - } - - this.anniversary = (aProp.anniversary == undefined || aProp.anniversary == null) ? null : new Date(aProp.anniversary); - this.sex = (aProp.sex != "undefined") ? aProp.sex : null; - this.genderIdentity = (aProp.genderIdentity != "undefined") ? aProp.genderIdentity : null; + this.name = aProp.name; + this.honorificPrefix = aProp.honorificPrefix; + this.givenName = aProp.givenName; + this.additionalName = aProp.additionalName; + this.familyName = aProp.familyName; + this.honorificSuffix = aProp.honorificSuffix; + this.nickname = aProp.nickname; + this.email = aProp.email; + this.photo = aProp.photo; + this.url = aProp.url; + this.category = aProp.category; + this.adr = aProp.adr; + this.tel = aProp.tel; + this.org = aProp.org; + this.jobTitle = aProp.jobTitle; + this.bday = aProp.bday; + this.note = aProp.note; + this.impp = aProp.impp; + this.anniversary = aProp.anniversary; + this.sex = aProp.sex; + this.genderIdentity = aProp.genderIdentity; }, get published () { diff --git a/dom/contacts/tests/test_contacts_basics.html b/dom/contacts/tests/test_contacts_basics.html index a8e18559427..fe0bd1a2959 100644 --- a/dom/contacts/tests/test_contacts_basics.html +++ b/dom/contacts/tests/test_contacts_basics.html @@ -137,6 +137,14 @@ function onFailure() { } function checkStr(str1, str2, msg) { + if (str1 ^ str2) { + ok(false, "Expected both strings to be either present or absent"); + return; + } + is(str1, str2, msg); +} + +function checkStrArray(str1, str2, msg) { // comparing /[null(,null)+]/ and undefined should pass function nonNull(e) { return e != null; @@ -150,88 +158,71 @@ function checkStr(str1, str2, msg) { } function checkAddress(adr1, adr2) { - checkStr(adr1.type, adr2.type, "Same type"); - checkStr(adr1.streetAddress, adr2.streetAddress, "Same streetAddress"); - checkStr(adr1.locality, adr2.locality, "Same locality"); - checkStr(adr1.region, adr2.region, "Same region"); - checkStr(adr1.postalCode, adr2.postalCode, "Same postalCode"); - checkStr(adr1.countryName, adr2.countryName, "Same countryName"); + if (adr1 ^ adr2) { + ok(false, "Expected both adrs to be either present or absent"); + return; + } + dump("adr1: " + adr1 + ", adr2: " + adr2 + "\n"); + checkStrArray(adr1.type, adr2.type, "Same type"); + checkStrArray(adr1.streetAddress, adr2.streetAddress, "Same streetAddress"); + checkStrArray(adr1.locality, adr2.locality, "Same locality"); + checkStrArray(adr1.region, adr2.region, "Same region"); + checkStrArray(adr1.postalCode, adr2.postalCode, "Same postalCode"); + checkStrArray(adr1.countryName, adr2.countryName, "Same countryName"); is(adr1.pref, adr2.pref, "Same pref"); } function checkTel(tel1, tel2) { - checkStr(tel1.type, tel2.type, "Same type"); - checkStr(tel1.value, tel2.value, "Same value"); - checkStr(tel1.carrier, tel2.carrier, "Same carrier"); + if (tel1 ^ tel2) { + ok(false, "Expected both tels to be either present or absent"); + return; + } + checkStrArray(tel1.type, tel2.type, "Same type"); + checkStrArray(tel1.value, tel2.value, "Same value"); + checkStrArray(tel1.carrier, tel2.carrier, "Same carrier"); is(tel1.pref, tel2.pref, "Same pref"); } function checkField(field1, field2) { - checkStr(field1.type, field2.type, "Same type"); - checkStr(field1.value, field2.value, "Same value"); + if (field1 ^ field2) { + ok(false, "Expected both fields to be either present or absent"); + return; + } + checkStrArray(field1.type, field2.type, "Same type"); + checkStrArray(field1.value, field2.value, "Same value"); is(field1.pref, field2.pref, "Same pref"); } function checkContacts(contact1, contact2) { - checkStr(contact1.name, contact2.name, "Same name"); - checkStr(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix"); - checkStr(contact1.givenName, contact2.givenName, "Same givenName"); - checkStr(contact1.additionalName, contact2.additionalName, "Same additionalName"); - checkStr(contact1.familyName, contact2.familyName, "Same familyName"); - checkStr(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix"); - checkStr(contact1.nickname, contact2.nickname, "Same nickname"); - checkStr(contact1.category, contact2.category, "Same category"); - checkStr(contact1.org, contact2.org, "Same org"); - checkStr(contact1.jobTitle, contact2.jobTitle, "Same jobTitle"); + checkStrArray(contact1.name, contact2.name, "Same name"); + checkStrArray(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix"); + checkStrArray(contact1.givenName, contact2.givenName, "Same givenName"); + checkStrArray(contact1.additionalName, contact2.additionalName, "Same additionalName"); + checkStrArray(contact1.familyName, contact2.familyName, "Same familyName"); + checkStrArray(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix"); + checkStrArray(contact1.nickname, contact2.nickname, "Same nickname"); + checkStrArray(contact1.category, contact2.category, "Same category"); + checkStrArray(contact1.org, contact2.org, "Same org"); + checkStrArray(contact1.jobTitle, contact2.jobTitle, "Same jobTitle"); is(contact1.bday ? contact1.bday.valueOf() : null, contact2.bday ? contact2.bday.valueOf() : null, "Same birthday"); - checkStr(contact1.note, contact2.note, "Same note"); + checkStrArray(contact1.note, contact2.note, "Same note"); is(contact1.anniversary ? contact1.anniversary.valueOf() : null , contact2.anniversary ? contact2.anniversary.valueOf() : null, "Same anniversary"); - is(contact1.sex, contact2.sex, "Same sex"); - is(contact1.genderIdentity, contact2.genderIdentity, "Same genderIdentity"); + checkStr(contact1.sex, contact2.sex, "Same sex"); + checkStr(contact1.genderIdentity, contact2.genderIdentity, "Same genderIdentity"); for (var i in contact1.email) { - if (contact1.email) { - ok(contact2.email != null, "conatct2.email exists"); - } - if (contact2.email) { - ok(contact1.email != null, "conatct1.email exists"); - } checkField(contact1.email[i], contact2.email[i]); } for (var i in contact1.adr) { - if (contact1.adr) { - ok(contact2.adr != null, "conatct2.adr exists"); - } - if (contact2.adr) { - ok(contact1.adr != null, "conatct1.adr exists"); - } checkAddress(contact1.adr[i], contact2.adr[i]); } for (var i in contact1.tel) { - if (contact1.tel) { - ok(contact2.tel != null, "conatct2.tel exists"); - } - if (contact2.tel) { - ok(contact1.tel != null, "conatct1.tel exists"); - } checkTel(contact1.tel[i], contact2.tel[i]); } for (var i in contact1.url) { - if (contact1.url) { - ok(contact2.url != null, "conatct2.url exists"); - } - if (contact2.url) { - ok(contact1.url != null, "conatct1.url exists"); - } checkField(contact1.url[i], contact2.url[i]); } for (var i in contact1.impp) { - if (contact1.impp) { - ok(contact2.impp != null, "conatct2.impp exists"); - } - if (contact2.impp) { - ok(contact1.impp != null, "conatct1.impp exists"); - } checkField(contact1.impp[i], contact2.impp[i]); } } @@ -331,7 +322,7 @@ var steps = [ req.onsuccess = function () { ok(createResult1.id, "The contact now has an ID."); sample_id1 = createResult1.id; - checkContacts(properties1, createResult1); + checkContacts(createResult1, properties1); }; req.onerror = onFailure; }, @@ -498,7 +489,7 @@ var steps = [ req.onsuccess = function () { ok(createResult1.id, "The contact now has an ID."); sample_id1 = createResult1.id; - checkContacts(properties1, createResult1); + checkContacts(createResult1, properties1); next(); }; req.onerror = onFailure; @@ -1091,7 +1082,7 @@ var steps = [ req = mozContacts.save(cloned); req.onsuccess = function () { ok(cloned.id, "The contact now has an ID."); - ok(cloned.email.value == "new email!", "Same Email"); + ok(cloned.email[0].value == "new email!", "Same Email"); ok(createResult1.email != cloned.email, "Clone has different email"); ok(cloned.givenName == "Tom", "New Name"); next(); @@ -1255,7 +1246,7 @@ var steps = [ var req2 = mozContacts.find(options); req2.onsuccess = function() { is(req2.result.length, 1, "1 Entry"); - checkStr(req2.result.givenName, "customTest", "same name"); + checkStrArray(req2.result.givenName, "customTest", "same name"); ok(req2.result.yyy === undefined, "custom property undefined"); next(); } @@ -1430,6 +1421,23 @@ var steps = [ } req.onerror = onFailure; }, + function() { + ok(true, "Test setting array properties to scalar values") + const DOMStrings = ["name","honorificPrefix","givenName","additionalName", + "familyName", "honorificSuffix","nickname","category", + "org","jobTitle","note"]; + const FIELDS = ["email","url","adr","tel","impp"]; + createResult1 = new mozContact(); + for (var prop of DOMStrings) { + createResult1[prop] = "foo"; + ok(Array.isArray(createResult1[prop]), prop + " is array"); + } + for (var prop of FIELDS) { + createResult1[prop] = {type: "foo"}; + ok(Array.isArray(createResult1[prop]), prop + " is array"); + } + next(); + }, function () { ok(true, "all done!\n"); clearTemps(); @@ -1445,11 +1453,11 @@ function next() { return; } try { - steps[index](); + var i = index++; + steps[i](); } catch(ex) { ok(false, "Caught exception", ex); } - index += 1; } SimpleTest.waitForExplicitFinish();