Use Dict everywhere we have keys based on potentially dangerous, user-supplied data

There are also one or two places we don't need to use it for security
purposes, but we do so for consistencey.

(imported from commit aa111f5a22a0e8597ec3cf8504adae66d5fb6768)
This commit is contained in:
Zev Benjamin 2013-08-07 17:56:51 -04:00
parent 0cfc8fae9f
commit ec9322fc87
17 changed files with 148 additions and 133 deletions

View File

@ -41,11 +41,11 @@ function sort_users(users, user_info) {
// Sort equivalent PM names alphabetically
var full_name_a = a;
var full_name_b = b;
if (people_dict[a] !== undefined) {
full_name_a = people_dict[a].full_name;
if (people_dict.has(a)) {
full_name_a = people_dict.get(a).full_name;
}
if (people_dict[b] !== undefined) {
full_name_b = people_dict[b].full_name;
if (people_dict.has(b)) {
full_name_b = people_dict.get(b).full_name;
}
return util.strcmp(full_name_a, full_name_b);
});

View File

@ -203,8 +203,8 @@ exports.initialize = function () {
$( "#subject" ).typeahead({
source: function (query, process) {
var stream_name = $("#stream").val(), i;
if (subject_dict.hasOwnProperty(stream_name)) {
return subject_dict[stream_name];
if (subject_dict.has(stream_name)) {
return subject_dict.get(stream_name);
}
return [];
},

View File

@ -33,7 +33,7 @@ function unmunge(k) {
return k.substr(1);
}
Dict.prototype = {
Dict.prototype = _.object(_.map({
get: function Dict_get(key) {
return this._items[munge(key)];
},
@ -63,12 +63,9 @@ Dict.prototype = {
return [unmunge(pair[0]), pair[1]];
});
}
};
Dict.prototype = _.object(_.map(Dict.prototype,
function (value, key) {
return [key, util.enforce_arity(value)];
}));
}, function (value, key) {
return [key, util.enforce_arity(value)];
}));
}());

View File

@ -291,15 +291,15 @@ exports.search_string = function () {
// Collect operators which appear only once into an object,
// and discard those which appear more than once.
function collect_single(operators) {
var seen = {};
var result = {};
var seen = new Dict();
var result = new Dict();
_.each(operators, function (elem) {
var key = elem[0];
if (seen.hasOwnProperty(key)) {
delete result[key];
if (seen.has(key)) {
result.del(key);
} else {
result[key] = elem[1];
seen [key] = true;
result.set(key, elem[1]);
seen.set(key, true);
}
});
return result;
@ -317,16 +317,16 @@ exports.set_compose_defaults = function (opts) {
// Set the stream, subject, and/or PM recipient if they are
// uniquely specified in the narrow view.
if (single.stream) {
opts.stream = single.stream;
if (single.has('stream')) {
opts.stream = single.get('stream');
}
if (single.topic) {
opts.subject = single.topic;
if (single.has('topic')) {
opts.subject = single.get('topic');
}
if (single['pm-with'] !== undefined) {
opts.private_message_recipient = single['pm-with'];
if (single.has('pm-with')) {
opts.private_message_recipient = single.get('pm-with');
}
};

View File

@ -281,7 +281,7 @@ function get_topic_suggestions(query_operators) {
stream = subs.canonicalized_name(stream);
var topics = recent_subjects[stream];
var topics = recent_subjects.get(stream);
if (!topics) {
return [];

View File

@ -16,11 +16,9 @@ exports.sort_narrow_list = function () {
streams.sort(function (a, b) {
if (sort_recent) {
if (recent_subjects[b] !== undefined &&
recent_subjects[a] === undefined) {
if (recent_subjects.has(b) && ! recent_subjects.has(a)) {
return 1;
} else if (recent_subjects[b] === undefined &&
recent_subjects[a] !== undefined) {
} else if (! recent_subjects.has(b) && recent_subjects.has(a)) {
return -1;
}
}
@ -41,7 +39,7 @@ exports.sort_narrow_list = function () {
_.each(streams, function (stream) {
var li = $(subs.get(stream).sidebar_li);
if (sort_recent) {
if (recent_subjects[stream] === undefined) {
if (! recent_subjects.has(stream)) {
li.addClass('inactive_stream');
} else {
li.removeClass('inactive_stream');
@ -152,7 +150,7 @@ function rebuild_recent_subjects(stream, subject) {
$('.expanded_subjects').remove();
var max_subjects = 5;
var stream_li = get_filter_li('stream', stream);
var subjects = recent_subjects[stream] || [];
var subjects = recent_subjects.get(stream) || [];
var active_orig_subject = subject;
var display_subjects = _.filter(subjects, function (subject_obj, idx) {
var num_unread = unread.num_unread_for_subject(stream, subject_obj.canon_subject);
@ -230,19 +228,27 @@ exports.update_dom_with_unread_counts = function (counts) {
// Our job is to update some DOM elements.
// counts.stream_count maps streams to counts
_.each(counts.stream_count, function (count, stream) {
_.each(counts.stream_count.items(), function (item) {
var stream = item[0];
var count = item[1];
exports.set_count("stream", stream, count);
});
// counts.subject_count maps streams to hashes of subjects to counts
_.each(counts.subject_count, function (subject_hash, stream) {
_.each(subject_hash, function (count, subject) {
_.each(counts.subject_count.items(), function (item) {
var stream = item[0];
var subject_hash = item[1];
_.each(subject_hash.items(), function (item) {
var subject = item[0];
var count = item[1];
exports.set_subject_count(stream, subject, count);
});
});
// counts.pm_count maps people to counts
_.each(counts.pm_count, function (count, person) {
_.each(counts.pm_count.items(), function (item) {
var person = item[0];
var count = item[1];
exports.set_count("private", person, count);
});

View File

@ -2,16 +2,16 @@ var subs = (function () {
var exports = {};
var stream_info = {}; // Maps lowercase stream name to stream properties object
var stream_info = new Dict(); // Maps lowercase stream name to stream properties object
var next_sub_id = 0;
function add_sub(stream_name, sub) {
stream_info[stream_name.toLowerCase()] = sub;
stream_info.set(stream_name.toLowerCase(), sub);
}
function get_sub(stream_name) {
return stream_info[stream_name.toLowerCase()];
return stream_info.get(stream_name.toLowerCase());
}
exports.stream_info = function (new_stream_info) {
@ -23,8 +23,7 @@ exports.stream_info = function (new_stream_info) {
};
exports.subscribed_streams = function () {
return _.chain(stream_info)
.values()
return _.chain(stream_info.values())
.where({subscribed: true})
.pluck('name')
.value();
@ -43,7 +42,7 @@ exports.update_all_messages_link = function () {
// the user has any subscriptions hidden from home view.
var all_messages = $("#global_filters [data-name='all']")[0];
if (_.every(stream_info, function (sub) { return sub.in_home_view; })) {
if (_.every(stream_info.values(), function (sub) { return sub.in_home_view; })) {
$(all_messages).addClass('hidden-filter');
} else {
$(all_messages).removeClass('hidden-filter');
@ -385,7 +384,7 @@ exports.reload_subscriptions = function (opts) {
}
if (opts.clear_first) {
stream_info = {};
stream_info = new Dict();
stream_list.remove_all_narrow_filters();
}
@ -739,7 +738,7 @@ $(function () {
// mark_subscribed adds the user to the member list
mark_subscribed(stream);
} else {
add_to_member_list(list, people_dict[principal].full_name, principal);
add_to_member_list(list, people_dict.get(principal).full_name, principal);
}
} else {
error_elem.addClass("hide");
@ -782,11 +781,11 @@ $(function () {
success: function (data) {
util.destroy_loading_indicator(indicator_elem);
var subscribers = _.map(data.subscribers, function (elem) {
var person = people_dict[elem];
var person = people_dict.get(elem);
if (person === undefined) {
return elem;
}
return format_member_list_elem(people_dict[elem].full_name, elem);
return format_member_list_elem(people_dict.get(elem).full_name, elem);
});
_.each(subscribers.sort().reverse(), function (elem) {
// add_to_member_list *prepends* the element,

View File

@ -41,10 +41,10 @@ function make_tab_data() {
if (filter.has_operator("pm-with")) {
var emails = filter.operands("pm-with")[0].split(',');
var names = _.map(emails, function (email) {
if (! people_dict[email]) {
if (! people_dict.has(email)) {
return email;
}
return people_dict[email].full_name;
return people_dict.get(email).full_name;
});
tabs.push(make_tab(names.join(', '), hashed));
@ -60,8 +60,8 @@ function make_tab_data() {
tabs.push(make_tab("Mentions", hashed));
} else if (filter.has_operator("sender")) {
var sender = filter.operands("sender")[0];
if (people_dict[sender]) {
sender = people_dict[sender].full_name;
if (people_dict.has(sender)) {
sender = people_dict.get(sender).full_name;
}
tabs.push(make_tab("Sent by " + sender, hashed));
} else if (filter.has_operator("search")) {

View File

@ -7,9 +7,9 @@ var event_handlers = {};
// We'll temporarily set stream colors for the streams we use in the demo
// tutorial messages.
var real_stream_info;
var tutorial_stream_info = {"design": {"color": "#76ce90"},
"social": {"color": "#fae589"},
"devel": {"color": "#a6c7e5"}};
var tutorial_stream_info = new Dict({"design": {"color": "#76ce90"},
"social": {"color": "#fae589"},
"devel": {"color": "#a6c7e5"}});
// Each message object contains the minimal information necessary for it to be
// processed by our system for adding messages to your feed.

View File

@ -1434,7 +1434,7 @@ exports.set_presence_list = function (users, presence_info) {
function info_for(email) {
var presence = presence_info[email];
return {
name: people_dict[email].full_name,
name: people_dict.get(email).full_name,
email: email,
type: presence,
type_desc: presence_descriptions[presence]
@ -1442,7 +1442,7 @@ exports.set_presence_list = function (users, presence_info) {
}
var user_emails = _.filter(users, function (email) {
return people_dict[email] !== undefined;
return people_dict.has(email);
});
var user_info = [my_info].concat(_.map(user_emails, info_for));

View File

@ -3,11 +3,11 @@ var unread = (function () {
var exports = {};
var unread_counts = {
'stream': {},
'private': {}
'stream': new Dict(),
'private': new Dict()
};
var unread_mentioned = {};
var unread_subjects = {};
var unread_subjects = new Dict();
function unread_hashkey(message) {
var hashkey;
@ -17,17 +17,17 @@ function unread_hashkey(message) {
hashkey = message.reply_to;
}
if (unread_counts[message.type][hashkey] === undefined) {
unread_counts[message.type][hashkey] = {};
if (! unread_counts[message.type].has(hashkey)) {
unread_counts[message.type].set(hashkey, {});
}
if (message.type === 'stream') {
var canon_subject = subs.canonicalized_name(message.subject);
if (unread_subjects[hashkey] === undefined) {
unread_subjects[hashkey] = {};
if (! unread_subjects.has(hashkey)) {
unread_subjects.set(hashkey, new Dict());
}
if (unread_subjects[hashkey][canon_subject] === undefined) {
unread_subjects[hashkey][canon_subject] = {};
if (! unread_subjects.get(hashkey).has(canon_subject)) {
unread_subjects.get(hashkey).set(canon_subject, {});
}
}
@ -47,19 +47,19 @@ exports.update_unread_subjects = function (msg, event) {
var canon_subject = subs.canonicalized_name(msg.subject);
if (event.subject !== undefined &&
unread_subjects[canon_stream] !== undefined &&
unread_subjects[canon_stream][canon_subject] !== undefined &&
unread_subjects[canon_stream][canon_subject][msg.id]) {
unread_subjects.has(canon_stream) &&
unread_subjects.get(canon_stream).has(canon_subject) &&
unread_subjects.get(canon_stream).get(canon_subject)[msg.id]) {
var new_canon_subject = subs.canonicalized_name(event.subject);
// Move the unread subject count to the new subject
delete unread_subjects[canon_stream][canon_subject][msg.id];
if (unread_subjects[canon_stream][canon_subject].length === 0) {
delete unread_subjects[canon_stream][canon_subject];
delete unread_subjects.get(canon_stream).get(canon_subject)[msg.id];
if (unread_subjects.get(canon_stream).get(canon_subject).length === 0) {
unread_subjects.get(canon_stream).del(canon_subject);
}
if (unread_subjects[canon_stream][new_canon_subject] === undefined) {
unread_subjects[canon_stream][new_canon_subject] = {};
if (! unread_subjects.get(canon_stream).has(new_canon_subject)) {
unread_subjects.get(canon_stream).set(new_canon_subject, {});
}
unread_subjects[canon_stream][new_canon_subject][msg.id] = true;
unread_subjects.get(canon_stream).get(new_canon_subject)[msg.id] = true;
}
};
@ -71,11 +71,11 @@ exports.process_loaded_messages = function (messages) {
}
var hashkey = unread_hashkey(message);
unread_counts[message.type][hashkey][message.id] = true;
unread_counts[message.type].get(hashkey)[message.id] = true;
if (message.type === 'stream') {
var canon_subject = subs.canonicalized_name(message.subject);
unread_subjects[hashkey][canon_subject][message.id] = true;
unread_subjects.get(hashkey).get(canon_subject)[message.id] = true;
}
if (message.mentioned) {
@ -86,17 +86,17 @@ exports.process_loaded_messages = function (messages) {
exports.process_read_message = function (message) {
var hashkey = unread_hashkey(message);
delete unread_counts[message.type][hashkey][message.id];
delete unread_counts[message.type].get(hashkey)[message.id];
if (message.type === 'stream') {
var canon_stream = subs.canonicalized_name(message.stream);
var canon_subject = subs.canonicalized_name(message.subject);
delete unread_subjects[canon_stream][canon_subject][message.id];
delete unread_subjects.get(canon_stream).get(canon_subject)[message.id];
}
delete unread_mentioned[message.id];
};
exports.declare_bankruptcy = function () {
unread_counts = {'stream': {}, 'private': {}};
unread_counts = {'stream': new Dict(), 'private': new Dict()};
};
exports.num_unread_current_messages = function () {
@ -120,9 +120,9 @@ exports.get_counts = function () {
res.private_message_count = 0;
res.home_unread_messages = 0;
res.mentioned_message_count = Object.keys(unread_mentioned).length;
res.stream_count = {}; // hash by stream -> count
res.subject_count = {}; // hash of hashes (stream, then subject -> count)
res.pm_count = {}; // Hash by email -> count
res.stream_count = new Dict(); // hash by stream -> count
res.subject_count = new Dict(); // hash of hashes (stream, then subject -> count)
res.pm_count = new Dict(); // Hash by email -> count
function only_in_home_view(msgids) {
return _.filter(msgids, function (msgid) {
@ -130,31 +130,37 @@ exports.get_counts = function () {
});
}
_.each(unread_counts.stream, function (msgs, stream) {
_.each(unread_counts.stream.items(), function (item) {
var stream = item[0];
var msgs = item[1];
if (! subs.is_subscribed(stream)) {
return true;
}
var count = Object.keys(msgs).length;
res.stream_count[stream] = count;
res.stream_count.set(stream, count);
if (subs.in_home_view(stream)) {
res.home_unread_messages += only_in_home_view(Object.keys(msgs)).length;
}
if (unread_subjects[stream] !== undefined) {
res.subject_count[stream] = {};
_.each(unread_subjects[stream], function (msgs, subject) {
res.subject_count[stream][subject] = Object.keys(msgs).length;
if (unread_subjects.has(stream)) {
res.subject_count.set(stream, new Dict());
_.each(unread_subjects.get(stream).items(), function (item) {
var subject = item[0];
var msgs = item[1];
res.subject_count.get(stream).set(subject, Object.keys(msgs).length);
});
}
});
var pm_count = 0;
_.each(unread_counts["private"], function (obj, index) {
_.each(unread_counts["private"].items(), function (item) {
var index = item[0];
var obj = item[1];
var count = Object.keys(obj).length;
res.pm_count[index] = count;
res.pm_count.set(index, count);
pm_count += count;
});
res.private_message_count = pm_count;
@ -172,9 +178,9 @@ exports.get_counts = function () {
exports.num_unread_for_subject = function (stream, subject) {
var num_unread = 0;
if (unread_subjects[stream] !== undefined &&
unread_subjects[stream][subject] !== undefined) {
num_unread = Object.keys(unread_subjects[stream][subject]).length;
if (unread_subjects.has(stream) &&
unread_subjects.get(stream).has(subject)) {
num_unread = Object.keys(unread_subjects.get(stream).get(subject)).length;
}
return num_unread;
};

View File

@ -5,9 +5,9 @@ var home_msg_list = new MessageList('zhome',
);
var narrowed_msg_list;
var current_msg_list = home_msg_list;
var subject_dict = {};
var people_dict = {};
var recent_subjects = {};
var subject_dict = new Dict();
var people_dict = new Dict();
var recent_subjects = new Dict();
var queued_mark_as_read = [];
var queued_flag_timer;
@ -43,7 +43,7 @@ var waiting_on_browser_scroll = true;
function add_person(person) {
page_params.people_list.push(person);
people_dict[person.email] = person;
people_dict.set(person.email, person);
person.pm_recipient_count = 0;
}
@ -55,7 +55,7 @@ function remove_person(person) {
break;
}
}
delete people_dict[person.email];
people_dict.del(person.email);
}
function update_person(person) {
@ -64,12 +64,12 @@ function update_person(person) {
// that can change, this will need to either get complicated or be
// replaced by MVC
var i;
if (people_dict[person.email] === undefined) {
if (! people_dict.has(person.email)) {
blueslip.error("Got update_person event for unexpected user",
{email: person.email});
return;
}
people_dict[person.email].full_name = person.full_name;
people_dict.get(person.email).full_name = person.full_name;
for (i = 0; i < page_params.people_list.length; i++) {
if (page_params.people_list[i].email === person.email) {
page_params.people_list[i].full_name = person.full_name;
@ -503,22 +503,22 @@ function process_message_for_recent_subjects(message, remove_message) {
var canon_stream = subs.canonicalized_name(message.stream);
var canon_subject = subs.canonicalized_name(message.subject);
if (! recent_subjects.hasOwnProperty(canon_stream)) {
recent_subjects[canon_stream] = [];
if (! recent_subjects.has(canon_stream)) {
recent_subjects.set(canon_stream, []);
} else {
recent_subjects[canon_stream] =
_.filter(recent_subjects[canon_stream], function (item) {
var is_duplicate = (item.canon_subject.toLowerCase() === canon_subject.toLowerCase());
if (is_duplicate) {
current_timestamp = item.timestamp;
count = item.count;
}
recent_subjects.set(canon_stream,
_.filter(recent_subjects.get(canon_stream), function (item) {
var is_duplicate = (item.canon_subject.toLowerCase() === canon_subject.toLowerCase());
if (is_duplicate) {
current_timestamp = item.timestamp;
count = item.count;
}
return !is_duplicate;
});
return !is_duplicate;
}));
}
var recents = recent_subjects[canon_stream];
var recents = recent_subjects.get(canon_stream);
if (remove_message !== undefined) {
count = count - 1;
@ -537,7 +537,7 @@ function process_message_for_recent_subjects(message, remove_message) {
return b.timestamp - a.timestamp;
});
recent_subjects[canon_stream] = recents;
recent_subjects.set(canon_stream, recents);
}
var msg_metadata_cache = {};
@ -570,12 +570,12 @@ function add_message_metadata(message) {
case 'stream':
message.is_stream = true;
message.stream = message.display_recipient;
if (! _.has(subject_dict, message.stream)) {
subject_dict[message.stream] = [];
if (! subject_dict.has(message.stream)) {
subject_dict.set(message.stream, []);
}
if (! case_insensitive_find(message.subject, subject_dict[message.stream])) {
subject_dict[message.stream].push(message.subject);
subject_dict[message.stream].sort();
if (! case_insensitive_find(message.subject, subject_dict.get(message.stream))) {
subject_dict.get(message.stream).push(message.subject);
subject_dict.get(message.stream).sort();
}
message.reply_to = message.sender_email;
@ -608,13 +608,13 @@ function add_message_metadata(message) {
_.each(involved_people, function (person) {
// Do the hasOwnProperty() call via the prototype to avoid problems
// with keys like "hasOwnProperty"
if (people_dict[person.email] === undefined) {
if (! people_dict.has(person.email)) {
add_person(person);
}
if (message.type === 'private' && message.sent_by_me) {
// Track the number of PMs we've sent to this person to improve autocomplete
people_dict[person.email].pm_recipient_count += 1;
people_dict.get(person.email).pm_recipient_count += 1;
}
});
@ -1199,13 +1199,13 @@ function fast_forward_pointer() {
function main() {
_.each(page_params.people_list, function (person) {
people_dict[person.email] = person;
people_dict.set(person.email, person);
person.pm_recipient_count = 0;
});
// The special account feedback@zulip.com is used for in-app
// feedback and should always show up as an autocomplete option.
if (people_dict['feedback@zulip.com'] === undefined){
if (! people_dict.has('feedback@zulip.com')){
add_person({"email": "feedback@zulip.com",
"full_name": "Zulip Feedback Bot"});
}

View File

@ -4,6 +4,7 @@ var assert = require('assert');
global._ = require('third/underscore/underscore.js');
global.activity = require('js/activity.js');
global.util = require('js/util.js');
global.Dict = require('js/dict.js');
}());
var activity = global.activity;
@ -18,11 +19,11 @@ var activity = global.activity;
};
global.people_dict = {
global.people_dict = new global.Dict({
'alice@zulip.com': 'Alice Smith',
'fred@zulip.com': 'Fred Flintstone',
'jill@zulip.com': 'Jill Hill'
};
});
activity._sort_users(users, user_info);

View File

@ -3,6 +3,8 @@ var assert = require('assert');
(function set_up_dependencies () {
global._ = require('third/underscore/underscore.js');
global.util = require('js/util.js');
global.Dict = require('js/dict.js');
// An upcoming change is to put Filter in its own module, but
// for now it still lives in narrow.js. (I'm waiting for a big
// commit from Zev to hit master first. Once that happens,

View File

@ -3,6 +3,7 @@ var assert = require('assert');
(function set_up_dependencies () {
global._ = require('third/underscore/underscore.js');
global.util = require('js/util.js');
global.Dict = require('js/dict.js');
global.narrow = require('js/narrow.js');
global.$ = function () {}; // for subs.js
global.subs = require('js/subs.js');

View File

@ -31,9 +31,9 @@ function set_up_dependencies() {
global.typeahead_helper = require('js/typeahead_helper.js');
global.recent_subjects = {};
global.util = require('js/util.js');
global.Dict = require('js/dict.js');
global.recent_subjects = new global.Dict();
return search;
}
@ -107,13 +107,13 @@ var search = set_up_dependencies();
return 'office';
};
global.recent_subjects = {
global.recent_subjects = new global.Dict({
office: [
{subject: 'team'},
{subject: 'ignore'},
{subject: 'test'}
]
};
});
var suggestions = search.get_suggestions(query);
@ -144,7 +144,7 @@ var search = set_up_dependencies();
return;
};
global.recent_subjects = {};
global.recent_subjects = new global.Dict();
var suggestions = search.get_suggestions(query);
@ -177,13 +177,13 @@ var search = set_up_dependencies();
}
];
global.recent_subjects = {
global.recent_subjects = new global.Dict({
office: [
{subject: 'team'},
{subject: 'ignore'},
{subject: 'test'}
]
};
});
var suggestions = search.get_suggestions(query);

View File

@ -8,6 +8,9 @@
// dependencies (except _).
global._ = require('third/underscore/underscore.js');
global.util = require('js/util.js');
global.Dict = require('js/dict.js');
var Dict = global.Dict;
var unread = require('js/unread.js');
var assert = require('assert');
@ -31,9 +34,9 @@ var zero_counts = {
private_message_count: 0,
home_unread_messages: 0,
mentioned_message_count: 0,
stream_count: {},
subject_count: {},
pm_count: {},
stream_count: new Dict(),
subject_count: new Dict(),
pm_count: new Dict(),
unread_in_current_view: 0
};