From de6d39cedeb4af0e54ca04583ad3fe33e2ef094a Mon Sep 17 00:00:00 2001 From: cel Date: Sat, 14 Dec 2019 09:16:31 -1000 Subject: Sanitize strings Calling String on an object with a toString property would throw an error --- lib/render-msg.js | 152 +++++++++++++++++++++++++++--------------------------- lib/render.js | 2 +- lib/util.js | 10 ++++ 3 files changed, 87 insertions(+), 77 deletions(-) (limited to 'lib') diff --git a/lib/render-msg.js b/lib/render-msg.js index 277b419..4b8c8c0 100644 --- a/lib/render-msg.js +++ b/lib/render-msg.js @@ -124,7 +124,7 @@ RenderMsg.prototype.wrap = function (content, cb) { var ts = this.msg.value.timestamp var date = ts ? new Date(ts) : null var self = this - var channel = this.c.channel ? '#' + this.c.channel : '' + var channel = this.c.channel ? '#' + u.toString(this.c.channel) : '' var done = multicb({pluck: 1, spread: true}) done()(null, [h('tr.msg-row', h('td.msg-left', @@ -399,7 +399,7 @@ RenderMsg.prototype.markdown = function (cb) { RenderMsg.prototype.markdownSource = function (text, mentions) { return h('div', - h('pre', String(text)), + h('pre', u.toString(text)), mentions ? [ h('div', h('em', 'mentions:')), this.valueTable(mentions, 2, function () {}) @@ -478,7 +478,7 @@ RenderMsg.prototype.getName = function (id, cb) { case '%': return this.getMsgName(id, cb) case '@': // fallthrough case '&': return this.getAboutName(id, cb) - default: return cb(null, String(id)) + default: return cb(null, u.toString(id)) } } @@ -557,7 +557,7 @@ RenderMsg.prototype.title1 = function (cb) { RenderMsg.prototype.getAboutName = function (id, cb) { this.app.getAbout(id, function (err, about) { - cb(err, about && about.name || (String(id).substr(0, 8) + '…')) + cb(err, about && about.name || (u.toString(id).substr(0, 8) + '…')) }) } @@ -643,7 +643,7 @@ RenderMsg.prototype.about = function (cb) { isSelf ? 'self-identifies as ' : ['identifies ', h('a', {href: this.toUrl(this.c.about)}, u.truncate(this.c.about, 10)), ' as '], - h('ins', String(this.c.name)) + h('ins', u.toString(this.c.name)) ], cb) } @@ -705,20 +705,20 @@ RenderMsg.prototype.about = function (cb) { : h('a', {href: this.toUrl(this.c.about)}, u.truncate(this.c.about, 10)), ': ' ], - this.c.name ? [h('ins', String(this.c.name)), ' '] : '', - this.c.title ? h('h3', String(this.c.title)) : '', + this.c.name ? [h('ins', u.toString(this.c.name)), ' '] : '', + this.c.title ? h('h3', u.toString(this.c.title)) : '', this.c.series || this.c.seriesNo ? this.renderSeries(this.c) : '', - this.c.authors ? h('div', h('em', {title: 'authors'}, String(this.c.authors))) : '', + this.c.authors ? h('div', h('em', {title: 'authors'}, u.toString(this.c.authors))) : '', this.c.rating || this.c.ratingMax || this.c.ratingType ? h('div', [ 'rating: ', - String(this.c.rating), + u.toString(this.c.rating), this.c.ratingMax ? '/' + this.c.ratingMax : '', this.c.ratingType ? [' ', h('span', {innerHTML: u.unwrapP(this.render.markdown(this.c.ratingType))}) ] : '' ]) : '', - this.c.genre ? h('div', ['genre: ', h('u', String(this.c.genre))]) : '', - this.c.shelve ? h('div', ['shelf: ', h('u', String(this.c.shelve))]) : '', + this.c.genre ? h('div', ['genre: ', h('u', u.toString(this.c.genre))]) : '', + this.c.shelve ? h('div', ['shelf: ', h('u', u.toString(this.c.shelve))]) : '', this.c.description ? h('div', {innerHTML: this.render.markdown(this.c.description)}) : '', this.c.review ? h('blockquote', @@ -786,7 +786,7 @@ RenderMsg.prototype.contact = function (cb) { ' ', a, self.c.note ? [ ' from ', - h('code', String(self.c.note)) + h('code', u.toString(self.c.note)) ] : '', self.c.reason ? [' because ', h('q', {innerHTML: u.unwrapP(self.render.markdown(self.c.reason))}) @@ -808,7 +808,7 @@ RenderMsg.prototype.pub = function (cb) { RenderMsg.prototype.address = function (cb) { var availability = this.c.availability * 100 - var addr = u.extractHostPort(this.value.author, String(this.c.address)) + var addr = u.extractHostPort(this.value.author, u.toString(this.c.address)) addr = this.app.removeDefaultPort(addr) this.wrapMini([ 'has address', @@ -832,7 +832,7 @@ RenderMsg.prototype.pubOwnerAnnounce = function (cb) { RenderMsg.prototype.pubOwnerConfirm = function (cb) { var self = this var announcement = this.c.announcement - var addr = u.extractHostPort(this.value.author, String(this.c.address)) + var addr = u.extractHostPort(this.value.author, u.toString(this.c.address)) addr = this.app.removeDefaultPort(addr) this.wrap([ 'confirms pub ownership announcement ', @@ -906,18 +906,18 @@ RenderMsg.prototype.gitRepo = function (cb) { var name = self.c.name var upstream = self.c.upstream self.link(upstream, function (err, upstreamA) { - if (err) upstreamA = ('a', {href: self.toUrl(upstream)}, String(name)) + if (err) upstreamA = ('a', {href: self.toUrl(upstream)}, u.toString(name)) self.wrapMini([ upstream ? ['forked ', upstreamA, ': '] : '', 'git clone ', h('code', h('small', 'ssb://' + id)), - name ? [' ', h('a', {href: self.toUrl(id)}, String(name))] : '' + name ? [' ', h('a', {href: self.toUrl(id)}, u.toString(name))] : '' ], cb) }) } function asString(str) { - return str ? String(str) : str + return str ? u.toString(str) : str } RenderMsg.prototype.gitUpdate = function (cb) { @@ -953,8 +953,8 @@ RenderMsg.prototype.gitUpdate = function (cb) { var path = '/git/commit/' + encodeURIComponent(commit.sha1) + '?msg=' + encodeURIComponent(self.msg.key) return h('li', commit.sha1 ? [h('a', {href: self.render.toUrl(path)}, - h('code', String(commit.sha1).substr(0, 8))), ' '] : '', - commit.title ? self.linkify(String(commit.title)) : '', + h('code', u.toString(commit.sha1).substr(0, 8))), ' '] : '', + commit.title ? self.linkify(u.toString(commit.title)) : '', self.render.gitCommitBody(commit.body) ) })) : '', @@ -964,11 +964,11 @@ RenderMsg.prototype.gitUpdate = function (cb) { + '?msg=' + encodeURIComponent(self.msg.key) return h('li', h('a', {href: self.render.toUrl(path)}, - h('code', String(tag.sha1).substr(0, 8))), ' ', - 'tagged ', String(tag.type), ' ', - h('code', String(tag.object).substr(0, 8)), ' ', - String(tag.tag), - tag.title ? [': ', self.linkify(String(tag.title).trim()), ' '] : '', + h('code', u.toString(tag.sha1).substr(0, 8))), ' ', + 'tagged ', u.toString(tag.type), ' ', + h('code', u.toString(tag.object).substr(0, 8)), ' ', + u.toString(tag.tag), + tag.title ? [': ', self.linkify(u.toString(tag.title).trim()), ' '] : '', tag.body ? self.render.gitCommitBody(tag.body) : '' ) })) : '', @@ -991,9 +991,9 @@ RenderMsg.prototype.gitPullRequest = function (cb) { if (err) return cb(err) self.wrap(h('div.ssb-pull-request', 'pull request ', - 'to ', baseRepoLink, ':', String(self.c.branch), ' ', - 'from ', headRepoLink, ':', String(self.c.head_branch), - self.c.title ? h('h4', String(self.c.title)) : '', + 'to ', baseRepoLink, ':', u.toString(self.c.branch), ' ', + 'from ', headRepoLink, ':', u.toString(self.c.head_branch), + self.c.title ? h('h4', u.toString(self.c.title)) : '', h('div', {innerHTML: self.markdown()})), cb) }) } @@ -1004,7 +1004,7 @@ RenderMsg.prototype.issue = function (cb) { if (err) return cb(err) self.wrap(h('div.ssb-issue', 'issue on ', projectLink, - self.c.title ? h('h4', String(self.c.title)) : '', + self.c.title ? h('h4', u.toString(self.c.title)) : '', h('div', {innerHTML: self.markdown()})), cb) }) } @@ -1082,7 +1082,7 @@ RenderMsg.prototype.valueTable = function (val, depth, cb) { ) } else if (isContent && key === 'type') { // TODO: also link to images by type, using links2 - var type = String(val.type) + var type = u.toString(val.type) return h('tr', h('td', h('strong', 'type')), h('td', h('a', {href: self.toUrl('/type/' + type)}, type)) @@ -1105,7 +1105,7 @@ RenderMsg.prototype.valueTable = function (val, depth, cb) { type: 'checkbox', disabled: 'disabled', checked: val ? 'checked' : undefined }) default: - return cb(), String(val) + return cb(), u.toString(val) } } @@ -1129,8 +1129,8 @@ RenderMsg.prototype.issues = function (cb) { } var els = issues.map(function (issue) { var commit = issue.object || issue.label ? [ - issue.object ? h('code', String(issue.object)) : '', ' ', - issue.label ? h('q', String(issue.label)) : ''] : '' + issue.object ? h('code', u.toString(issue.object)) : '', ' ', + issue.label ? h('q', u.toString(issue.label)) : ''] : '' if (issue.merged === true) return h('div', 'merged ', self.link1(issue, done())) @@ -1142,7 +1142,7 @@ RenderMsg.prototype.issues = function (cb) { 'reopened ', self.link1(issue, done())) if (typeof issue.title === 'string') return h('div', - 'renamed ', self.link1(issue, done()), ' to ', h('ins', String(issue.title))) + 'renamed ', self.link1(issue, done()), ' to ', h('ins', u.toString(issue.title))) }) done(cb) return els.length > 0 ? [els, h('br')] : '' @@ -1172,11 +1172,11 @@ RenderMsg.prototype.repost = function (cb) { } RenderMsg.prototype.update = function (cb) { - var id = String(this.c.update) + var id = u.toString(this.c.update) this.wrapMini([ h('div', 'updated ', h('code.ssb-id', h('a', {href: this.render.toUrl(id)}, id))), - this.c.title ? h('h4.msg-title', String(this.c.title)) : '', + this.c.title ? h('h4.msg-title', u.toString(this.c.title)) : '', this.c.description ? h('div', {innerHTML: this.render.markdown(this.c.description)}) : '' ], cb) @@ -1209,7 +1209,7 @@ RenderMsg.prototype.fermentAudio = function (cb) { : ''), h('td', h('a', {href: this.render.toUrl(this.c.audioSrc)}, - String(this.c.title)), + u.toString(this.c.title)), isFinite(this.c.duration) ? ' (' + formatDuration(this.c.duration) + ')' : '', @@ -1233,7 +1233,7 @@ RenderMsg.prototype.musicRelease = function (cb) { })) : ''), h('td', - h('h4.msg-title', String(this.c.title)), + h('h4.msg-title', u.toString(this.c.title)), this.c.text ? h('div', {innerHTML: this.render.markdown(this.c.text)}) : '' @@ -1242,7 +1242,7 @@ RenderMsg.prototype.musicRelease = function (cb) { h('ul', u.toArray(this.c.tracks).filter(Boolean).map(function (track) { return h('li', h('a', {href: self.render.toUrl(track.link)}, - String(track.fname))) + u.toString(track.fname))) })) ], cb) } @@ -1255,10 +1255,10 @@ RenderMsg.prototype.dns = function (cb) { self.wrap([ h('div', h('p', - h('ins', {title: 'name'}, String(record.name)), ' ', - h('span', {title: 'ttl'}, String(record.ttl)), ' ', - h('span', {title: 'class'}, String(record.class)), ' ', - h('span', {title: 'type'}, String(record.type)) + h('ins', {title: 'name'}, u.toString(record.name)), ' ', + h('span', {title: 'ttl'}, u.toString(record.ttl)), ' ', + h('span', {title: 'class'}, u.toString(record.class)), ' ', + h('span', {title: 'type'}, u.toString(record.type)) ), h('pre', {title: 'data'}, JSON.stringify(record.data || record.value, null, 2)), @@ -1288,12 +1288,12 @@ RenderMsg.prototype.wifiNetwork = function (cb) { RenderMsg.prototype.mutualCredit = function (cb) { var self = this - var currency = String(self.c.currency) + var currency = u.toString(self.c.currency) self.link(self.c.account, function (err, a) { if (err) return cb(err) self.wrapMini([ 'credits ', a || '?', ' ', - h('code', String(self.c.amount)), ' ', + h('code', u.toString(self.c.amount)), ' ', currency[0] === '#' ? h('a', {href: self.toUrl(currency)}, currency) : h('ins', currency), @@ -1361,7 +1361,7 @@ RenderMsg.prototype.npmPublish = function (cb) { self.wrap([ h('div', 'published ', - h('u', String(pkg.name)), ' ', + h('u', u.toString(pkg.name)), ' ', hJoin(versions.map(function (version) { var distTag = distTagged[version] return [h('b', version), distTag ? [' (', h('i', distTag), ')'] : ''] @@ -1473,7 +1473,7 @@ function parseFenRank (line) { } function parseChess(fen) { - var fields = String(fen).split(/\s+/) + var fields = u.toString(fen).split(/\s+/) var ranks = fields[0].split('/') var f2 = fields[2] || '' return { @@ -1644,7 +1644,7 @@ RenderMsg.prototype.chessChat = function (cb) { if (err) return cb(err) self.wrap([ h('div', h('small', '> ', rootLink)), - h('p', String(self.c.msg)) + h('p', u.toString(self.c.msg)) ], cb) }) } @@ -1705,7 +1705,7 @@ RenderMsg.prototype.acmeChallengesHttp01 = function (cb) { href: 'http://' + challenge.domain + '/.well-known/acme-challenge/' + challenge.token, title: challenge.keyAuthorization, - }, String(challenge.domain)) + }, u.toString(challenge.domain)) }), ', ', ', and ') ), cb) } @@ -1732,10 +1732,10 @@ RenderMsg.prototype.bookclub = function (cb) { })) })), h('td', - h('h4', String(props.title)), + h('h4', u.toString(props.title)), props.series || props.seriesNo ? self.renderSeries(props) : '', props.authors ? - h('p', h('em', String(props.authors))) + h('p', h('em', u.toString(props.authors))) : '', props.description ? h('div', {innerHTML: self.render.markdown(props.description)}) @@ -1773,7 +1773,7 @@ RenderMsg.prototype.sombrioScore = function (cb) { var self = this self.wrapMini(h('span', 'scored ', - h('ins', String(self.c.score)) + h('ins', u.toString(self.c.score)) ), cb) } @@ -1794,8 +1794,8 @@ RenderMsg.prototype.blog = function (cb) { }) : 'blog'), h('td', blogId ? h('h3', h('a', {href: self.render.toUrl('/markdown/' + blogId)}, - String(self.c.title || self.msg.key))) : '', - String(self.c.summary || '')) + u.toString(self.c.title || self.msg.key))) : '', + u.toString(self.c.summary || '')) )), cb) } @@ -1809,8 +1809,8 @@ RenderMsg.prototype.imageMap = function (cb) { u.toArray(self.c.areas).map(function (areaLink) { var href = areaLink && self.toUrl(areaLink.link) return href ? h('area', { - shape: String(areaLink.shape), - coords: String(areaLink.coords), + shape: u.toString(areaLink.shape), + coords: u.toString(areaLink.coords), href: href, }) : '' }) @@ -1819,7 +1819,7 @@ RenderMsg.prototype.imageMap = function (cb) { src: self.render.imageUrl(imgRef), width: Number(imgLink.width) || undefined, height: Number(imgLink.height) || undefined, - alt: String(imgLink.name || ''), + alt: u.toString(imgLink.name || ''), usemap: '#' + mapName, }) : '' ]), cb) @@ -1829,7 +1829,7 @@ RenderMsg.prototype.skillCreate = function (cb) { var self = this self.wrapMini(h('span', ' created skill ', - h('ins', String(self.c.name)) + h('ins', u.toString(self.c.name)) ), cb) } @@ -1861,7 +1861,7 @@ RenderMsg.prototype.identitySkillAssign = function (cb) { self.wrapMini(h('span', self.c.action === 'assign' ? 'assigns ' : self.c.action === 'unassign' ? 'unassigns ' - : h('code', String(self.c.action)), ' ', + : h('code', u.toString(self.c.action)), ' ', 'skill ', a ), cb) }) @@ -1876,7 +1876,7 @@ RenderMsg.prototype.ideaSkillAssign = function (cb) { self.wrapMini(h('span', self.c.action === 'assign' ? 'assigns ' : self.c.action === 'unassign' ? 'unassigns ' - : h('code', String(self.c.action)), ' ', + : h('code', u.toString(self.c.action)), ' ', 'skill ', skillA, ' to idea ', ideaA @@ -1890,7 +1890,7 @@ RenderMsg.prototype.ideaAssocate = function (cb) { self.wrapMini(h('span', self.c.action === 'associate' ? 'associates with ' : self.c.action === 'disassociate' ? 'disassociates with ' - : h('code', String(self.c.action)), ' ', + : h('code', u.toString(self.c.action)), ' ', 'idea ', a ), cb) }) @@ -1902,7 +1902,7 @@ RenderMsg.prototype.ideaHat = function (cb) { self.wrapMini(h('span', self.c.action === 'take' ? 'takes ' : self.c.action === 'discard' ? 'discards ' - : h('code', String(self.c.action)), ' ', + : h('code', u.toString(self.c.action)), ' ', 'idea ', a ), cb) }) @@ -1922,7 +1922,7 @@ RenderMsg.prototype.ideaUpdate = function (cb) { if (keys === 'title') { return self.wrapMini(h('span', 'titles idea ', - h('a', {href: self.toUrl(self.c.ideaKey)}, String(props.title)) + h('a', {href: self.toUrl(self.c.ideaKey)}, u.toString(props.title)) ), cb) } @@ -1938,7 +1938,7 @@ RenderMsg.prototype.ideaUpdate = function (cb) { if (keys === 'description,title') { return self.wrap(h('div', 'describes idea ', - h('a', {href: self.toUrl(self.c.ideaKey)}, String(props.title)), + h('a', {href: self.toUrl(self.c.ideaKey)}, u.toString(props.title)), ':', h('blockquote', {innerHTML: self.render.markdown(props.description)}) ), cb) @@ -1974,7 +1974,7 @@ RenderMsg.prototype.aboutResource = function (cb) { var self = this return self.wrap(h('div', 'describes resource ', - h('a', {href: self.toUrl(self.c.about)}, String(self.c.name)), + h('a', {href: self.toUrl(self.c.about)}, u.toString(self.c.name)), ':', h('blockquote', {innerHTML: self.render.markdown(self.c.description)}) ), cb) @@ -1995,11 +1995,11 @@ RenderMsg.prototype.lineComment = function (cb) { }, updateMsg && updateMsg.value.timestamp ? htime(new Date(updateMsg.value.timestamp)) - : String(self.c.updateId) + : u.toString(self.c.updateId) ), ' ', h('a', { href: self.toUrl('/git/commit/' + self.c.commitId + '?msg=' + encodeURIComponent(self.c.updateId)) - }, String(self.c.commitId).substr(0, 8)), ' ', + }, u.toString(self.c.commitId).substr(0, 8)), ' ', h('a', { href: self.toUrl('/git/line-comment/' + encodeURIComponent(self.msg.key || JSON.stringify(self.msg))) @@ -2061,16 +2061,16 @@ RenderMsg.prototype.poll = function (cb) { return h('div', pollType === 'chooseOne' ? [ h('input', {type: 'radio', name: 'poll_choice', value: i}), ' ', - String(choice) + u.toString(choice) ] : pollType === 'meetingTime' /*|| pollType === 'dot'*/ ? [ h('input', {type: 'checkbox', name: 'poll_choices', value: i}), ' ', dateSpan(new Date(choice)) /* ] : pollType === 'range' ? [ h('input', {type: 'number', name: 'poll_choice_' + i, min: 0, max: max}), ' ', - String(choice) + u.toString(choice) */ - ] : String(choice) + ] : u.toString(choice) ) }), pollType === 'meetingTime' ? '' : h('p', 'reason: ', @@ -2119,7 +2119,7 @@ RenderMsg.prototype.pollPosition = function (cb) { h('ul', pickedChoices.map(function (choice) { return h('li', positionType === 'meetingTime' ? dateSpan(new Date(choice)) - : String(choice) + : u.toString(choice) ) })), reason ? h('div', {innerHTML: self.render.markdown(reason, self.c.mentions)}) : '' @@ -2153,7 +2153,7 @@ RenderMsg.prototype.pollResolution = function (cb) { h('ul', pickedChoices.map(function (choice) { return h('li', pollType === 'meetingTime' ? dateSpan(new Date(choice)) - : String(choice) + : u.toString(choice) ) })) ), cb) @@ -2163,7 +2163,7 @@ RenderMsg.prototype.pollResolution = function (cb) { RenderMsg.prototype.scat = function (cb) { this.wrapMini([ this.c.action ? '' : 'chats ', - h('q', String(this.c.text)) + h('q', u.toString(this.c.text)) ], cb) } @@ -2174,16 +2174,16 @@ RenderMsg.prototype.share = function (cb) { self.wrapMini([ 'shares ', share.content === 'blog' ? 'blog ' - : share.content ? [h('code', String(share.content)), ' '] + : share.content ? [h('code', u.toString(share.content)), ' '] : '', - link || String(share.link), + link || u.toString(share.link), share.url ? [ ' at ', h('small', h('a', {href: self.toUrl(share.url)}, share.url)) ] : '', share.text ? [ ': ', - h('q', String(share.text)) + h('q', u.toString(share.text)) ] : '' ], cb) }) @@ -2216,7 +2216,7 @@ RenderMsg.prototype.label = function (cb) { 'labeled ', link, ' as ', - h('ins', String(self.c.label)) + h('ins', u.toString(self.c.label)) ], cb) }) } diff --git a/lib/render.js b/lib/render.js index 1aec728..e694396 100644 --- a/lib/render.js +++ b/lib/render.js @@ -190,7 +190,7 @@ Render.prototype.markdown = function (text, mentions, opts) { var out = ssbcMd ? md.block(String(text), { toUrl: function (ref) { return self.toUrl(ref) }, imageLink: function (ref) { return self.imageUrl(ref) } - }) : marked(String(text), this.markedOpts) + }) : marked(u.toString(text), this.markedOpts) delete this._mentions delete this._mentionsByLink return out //fixSymbols(out) diff --git a/lib/util.js b/lib/util.js index 731cc82..c5584ee 100644 --- a/lib/util.js +++ b/lib/util.js @@ -93,6 +93,16 @@ u.toLinkArray = function (x) { return u.toArray(x).map(u.toLink).filter(u.linkDest) } +u.toString = function (str) { + switch (typeof str) { + case 'string': return str + case 'object': + if (str !== null) return JSON.stringify(str) + default: + return String(str) + } +} + u.renderError = function(err) { return h('div.error', h('h3', err.name), -- cgit v1.2.3