Bug 869615 - Sanitize Contact properties everywhere, not just in init. r=bent

--HG--
extra : rebase_source : 25fd18299c15cee78dff008460c89e78ba60d842
This commit is contained in:
Reuben Morais 2013-06-03 18:50:31 -07:00
parent 5be6cc599a
commit 029a94a7af
2 changed files with 314 additions and 173 deletions

View File

@ -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 () {

View File

@ -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();