fix: upgrade sequelize to latest version to fix CVE

Signed-off-by: BoHong Li <a60814billy@gmail.com>
This commit is contained in:
BoHong Li 2019-04-12 12:05:32 +08:00 committed by Sheogorath
parent 02929cd4bf
commit 63c96e7359
No known key found for this signature in database
GPG key ID: 1F05CC3635CDDFFD
6 changed files with 796 additions and 789 deletions

View file

@ -18,9 +18,10 @@ module.exports = function (sequelize, DataTypes) {
unique: true, unique: true,
fields: ['noteId', 'userId'] fields: ['noteId', 'userId']
} }
], ]
classMethods: { })
associate: function (models) {
Author.associate = function (models) {
Author.belongsTo(models.Note, { Author.belongsTo(models.Note, {
foreignKey: 'noteId', foreignKey: 'noteId',
as: 'note', as: 'note',
@ -36,7 +37,6 @@ module.exports = function (sequelize, DataTypes) {
hooks: true hooks: true
}) })
} }
}
})
return Author return Author
} }

View file

@ -10,7 +10,9 @@ var config = require('../config')
var logger = require('../logger') var logger = require('../logger')
var dbconfig = cloneDeep(config.db) var dbconfig = cloneDeep(config.db)
dbconfig.logging = config.debug ? logger.info : false dbconfig.logging = config.debug ? (data) => {
logger.info(data)
} : false
var sequelize = null var sequelize = null

View file

@ -86,8 +86,53 @@ module.exports = function (sequelize, DataTypes) {
} }
}, { }, {
paranoid: false, paranoid: false,
classMethods: { hooks: {
associate: function (models) { beforeCreate: function (note, options) {
return new Promise(function (resolve, reject) {
// if no content specified then use default note
if (!note.content) {
var body = null
let filePath = null
if (!note.alias) {
filePath = config.defaultNotePath
} else {
filePath = path.join(config.docsPath, note.alias + '.md')
}
if (Note.checkFileExist(filePath)) {
var fsCreatedTime = moment(fs.statSync(filePath).ctime)
body = fs.readFileSync(filePath, 'utf8')
note.title = Note.parseNoteTitle(body)
note.content = body
if (filePath !== config.defaultNotePath) {
note.createdAt = fsCreatedTime
}
}
}
// if no permission specified and have owner then give default permission in config, else default permission is freely
if (!note.permission) {
if (note.ownerId) {
note.permission = config.defaultPermission
} else {
note.permission = 'freely'
}
}
return resolve(note)
})
},
afterCreate: function (note, options, callback) {
return new Promise(function (resolve, reject) {
sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
if (err) {
return reject(err)
}
return resolve(note)
})
})
}
}
})
Note.associate = function (models) {
Note.belongsTo(models.User, { Note.belongsTo(models.User, {
foreignKey: 'ownerId', foreignKey: 'ownerId',
as: 'owner', as: 'owner',
@ -109,21 +154,21 @@ module.exports = function (sequelize, DataTypes) {
as: 'authors', as: 'authors',
constraints: false constraints: false
}) })
}, }
checkFileExist: function (filePath) { Note.checkFileExist = function (filePath) {
try { try {
return fs.statSync(filePath).isFile() return fs.statSync(filePath).isFile()
} catch (err) { } catch (err) {
return false return false
} }
}, }
encodeNoteId: function (id) { Note.encodeNoteId = function (id) {
// remove dashes in UUID and encode in url-safe base64 // remove dashes in UUID and encode in url-safe base64
let str = id.replace(/-/g, '') let str = id.replace(/-/g, '')
let hexStr = Buffer.from(str, 'hex') let hexStr = Buffer.from(str, 'hex')
return base64url.encode(hexStr) return base64url.encode(hexStr)
}, }
decodeNoteId: function (encodedId) { Note.decodeNoteId = function (encodedId) {
// decode from url-safe base64 // decode from url-safe base64
let id = base64url.toBuffer(encodedId).toString('hex') let id = base64url.toBuffer(encodedId).toString('hex')
// add dashes between the UUID string parts // add dashes between the UUID string parts
@ -134,13 +179,13 @@ module.exports = function (sequelize, DataTypes) {
idParts.push(id.substr(16, 4)) idParts.push(id.substr(16, 4))
idParts.push(id.substr(20, 12)) idParts.push(id.substr(20, 12))
return idParts.join('-') return idParts.join('-')
}, }
checkNoteIdValid: function (id) { Note.checkNoteIdValid = function (id) {
var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
var result = id.match(uuidRegex) var result = id.match(uuidRegex)
if (result && result.length === 1) { return true } else { return false } if (result && result.length === 1) { return true } else { return false }
}, }
parseNoteId: function (noteId, callback) { Note.parseNoteId = function (noteId, callback) {
async.series({ async.series({
parseNoteIdByAlias: function (_callback) { parseNoteIdByAlias: function (_callback) {
// try to parse note id by alias (e.g. doc) // try to parse note id by alias (e.g. doc)
@ -273,21 +318,21 @@ module.exports = function (sequelize, DataTypes) {
} }
return callback(null, null) return callback(null, null)
}) })
}, }
parseNoteInfo: function (body) { Note.parseNoteInfo = function (body) {
var parsed = Note.extractMeta(body) var parsed = Note.extractMeta(body)
var $ = cheerio.load(md.render(parsed.markdown)) var $ = cheerio.load(md.render(parsed.markdown))
return { return {
title: Note.extractNoteTitle(parsed.meta, $), title: Note.extractNoteTitle(parsed.meta, $),
tags: Note.extractNoteTags(parsed.meta, $) tags: Note.extractNoteTags(parsed.meta, $)
} }
}, }
parseNoteTitle: function (body) { Note.parseNoteTitle = function (body) {
var parsed = Note.extractMeta(body) var parsed = Note.extractMeta(body)
var $ = cheerio.load(md.render(parsed.markdown)) var $ = cheerio.load(md.render(parsed.markdown))
return Note.extractNoteTitle(parsed.meta, $) return Note.extractNoteTitle(parsed.meta, $)
}, }
extractNoteTitle: function (meta, $) { Note.extractNoteTitle = function (meta, $) {
var title = '' var title = ''
if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) {
title = meta.title title = meta.title
@ -297,18 +342,18 @@ module.exports = function (sequelize, DataTypes) {
} }
if (!title) title = 'Untitled' if (!title) title = 'Untitled'
return title return title
}, }
generateDescription: function (markdown) { Note.generateDescription = function (markdown) {
return markdown.substr(0, 100).replace(/(?:\r\n|\r|\n)/g, ' ') return markdown.substr(0, 100).replace(/(?:\r\n|\r|\n)/g, ' ')
}, }
decodeTitle: function (title) { Note.decodeTitle = function (title) {
return title || 'Untitled' return title || 'Untitled'
}, }
generateWebTitle: function (title) { Note.generateWebTitle = function (title) {
title = !title || title === 'Untitled' ? 'CodiMD - Collaborative markdown notes' : title + ' - CodiMD' title = !title || title === 'Untitled' ? 'CodiMD - Collaborative markdown notes' : title + ' - CodiMD'
return title return title
}, }
extractNoteTags: function (meta, $) { Note.extractNoteTags = function (meta, $) {
var tags = [] var tags = []
var rawtags = [] var rawtags = []
if (meta.tags && (typeof meta.tags === 'string' || typeof meta.tags === 'number')) { if (meta.tags && (typeof meta.tags === 'string' || typeof meta.tags === 'number')) {
@ -340,8 +385,8 @@ module.exports = function (sequelize, DataTypes) {
if (!found) { tags.push(rawtags[i]) } if (!found) { tags.push(rawtags[i]) }
} }
return tags return tags
}, }
extractMeta: function (content) { Note.extractMeta = function (content) {
var obj = null var obj = null
try { try {
obj = metaMarked(content) obj = metaMarked(content)
@ -354,8 +399,8 @@ module.exports = function (sequelize, DataTypes) {
} }
} }
return obj return obj
}, }
parseMeta: function (meta) { Note.parseMeta = function (meta) {
var _meta = {} var _meta = {}
if (meta) { if (meta) {
if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { _meta.title = meta.title } if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { _meta.title = meta.title }
@ -366,8 +411,8 @@ module.exports = function (sequelize, DataTypes) {
if (meta.slideOptions && (typeof meta.slideOptions === 'object')) { _meta.slideOptions = meta.slideOptions } if (meta.slideOptions && (typeof meta.slideOptions === 'object')) { _meta.slideOptions = meta.slideOptions }
} }
return _meta return _meta
}, }
updateAuthorshipByOperation: function (operation, userId, authorships) { Note.updateAuthorshipByOperation = function (operation, userId, authorships) {
var index = 0 var index = 0
var timestamp = Date.now() var timestamp = Date.now()
for (let i = 0; i < operation.length; i++) { for (let i = 0; i < operation.length; i++) {
@ -467,8 +512,8 @@ module.exports = function (sequelize, DataTypes) {
} }
} }
return authorships return authorships
}, }
transformPatchToOperations: function (patch, contentLength) { Note.transformPatchToOperations = function (patch, contentLength) {
var operations = [] var operations = []
if (patch.length > 0) { if (patch.length > 0) {
// calculate original content length // calculate original content length
@ -527,45 +572,6 @@ module.exports = function (sequelize, DataTypes) {
} }
return operations return operations
} }
},
hooks: {
beforeCreate: function (note, options, callback) {
// if no content specified then use default note
if (!note.content) {
var body = null
let filePath = null
if (!note.alias) {
filePath = config.defaultNotePath
} else {
filePath = path.join(config.docsPath, note.alias + '.md')
}
if (Note.checkFileExist(filePath)) {
var fsCreatedTime = moment(fs.statSync(filePath).ctime)
body = fs.readFileSync(filePath, 'utf8')
note.title = Note.parseNoteTitle(body)
note.content = body
if (filePath !== config.defaultNotePath) {
note.createdAt = fsCreatedTime
}
}
}
// if no permission specified and have owner then give default permission in config, else default permission is freely
if (!note.permission) {
if (note.ownerId) {
note.permission = config.defaultPermission
} else {
note.permission = 'freely'
}
}
return callback(null, note)
},
afterCreate: function (note, options, callback) {
sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
callback(err, note)
})
}
}
})
return Note return Note
} }

View file

@ -7,6 +7,8 @@ var childProcess = require('child_process')
var shortId = require('shortid') var shortId = require('shortid')
var path = require('path') var path = require('path')
var Op = Sequelize.Op
// core // core
var logger = require('../logger') var logger = require('../logger')
@ -96,9 +98,9 @@ module.exports = function (sequelize, DataTypes) {
this.setDataValue('authorship', value ? JSON.stringify(value) : value) this.setDataValue('authorship', value ? JSON.stringify(value) : value)
} }
} }
}, { })
classMethods: {
associate: function (models) { Revision.associate = function (models) {
Revision.belongsTo(models.Note, { Revision.belongsTo(models.Note, {
foreignKey: 'noteId', foreignKey: 'noteId',
as: 'note', as: 'note',
@ -106,8 +108,8 @@ module.exports = function (sequelize, DataTypes) {
onDelete: 'CASCADE', onDelete: 'CASCADE',
hooks: true hooks: true
}) })
}, }
getNoteRevisions: function (note, callback) { Revision.getNoteRevisions = function (note, callback) {
Revision.findAll({ Revision.findAll({
where: { where: {
noteId: note.id noteId: note.id
@ -126,8 +128,8 @@ module.exports = function (sequelize, DataTypes) {
}).catch(function (err) { }).catch(function (err) {
callback(err, null) callback(err, null)
}) })
}, }
getPatchedNoteRevisionByTime: function (note, time, callback) { Revision.getPatchedNoteRevisionByTime = function (note, time, callback) {
// find all revisions to prepare for all possible calculation // find all revisions to prepare for all possible calculation
Revision.findAll({ Revision.findAll({
where: { where: {
@ -141,7 +143,7 @@ module.exports = function (sequelize, DataTypes) {
where: { where: {
noteId: note.id, noteId: note.id,
createdAt: { createdAt: {
$gte: time [Op.gte]: time
} }
}, },
order: [['createdAt', 'DESC']] order: [['createdAt', 'DESC']]
@ -158,8 +160,8 @@ module.exports = function (sequelize, DataTypes) {
}).catch(function (err) { }).catch(function (err) {
return callback(err, null) return callback(err, null)
}) })
}, }
checkAllNotesRevision: function (callback) { Revision.checkAllNotesRevision = function (callback) {
Revision.saveAllNotesRevision(function (err, notes) { Revision.saveAllNotesRevision(function (err, notes) {
if (err) return callback(err, null) if (err) return callback(err, null)
if (!notes || notes.length <= 0) { if (!notes || notes.length <= 0) {
@ -168,28 +170,28 @@ module.exports = function (sequelize, DataTypes) {
Revision.checkAllNotesRevision(callback) Revision.checkAllNotesRevision(callback)
} }
}) })
}, }
saveAllNotesRevision: function (callback) { Revision.saveAllNotesRevision = function (callback) {
sequelize.models.Note.findAll({ sequelize.models.Note.findAll({
// query all notes that need to save for revision // query all notes that need to save for revision
where: { where: {
$and: [ [Op.and]: [
{ {
lastchangeAt: { lastchangeAt: {
$or: { [Op.or]: {
$eq: null, [Op.eq]: null,
$and: { [Op.and]: {
$ne: null, [Op.ne]: null,
$gt: sequelize.col('createdAt') [Op.gt]: sequelize.col('createdAt')
} }
} }
} }
}, },
{ {
savedAt: { savedAt: {
$or: { [Op.or]: {
$eq: null, [Op.eq]: null,
$lt: sequelize.col('lastchangeAt') [Op.lt]: sequelize.col('lastchangeAt')
} }
} }
} }
@ -227,8 +229,8 @@ module.exports = function (sequelize, DataTypes) {
}).catch(function (err) { }).catch(function (err) {
return callback(err, null) return callback(err, null)
}) })
}, }
saveNoteRevision: function (note, callback) { Revision.saveNoteRevision = function (note, callback) {
Revision.findAll({ Revision.findAll({
where: { where: {
noteId: note.id noteId: note.id
@ -292,8 +294,8 @@ module.exports = function (sequelize, DataTypes) {
}).catch(function (err) { }).catch(function (err) {
return callback(err, null) return callback(err, null)
}) })
}, }
finishSaveNoteRevision: function (note, revision, callback) { Revision.finishSaveNoteRevision = function (note, revision, callback) {
note.update({ note.update({
savedAt: revision.updatedAt savedAt: revision.updatedAt
}).then(function () { }).then(function () {
@ -302,8 +304,6 @@ module.exports = function (sequelize, DataTypes) {
return callback(err, null) return callback(err, null)
}) })
} }
}
})
return Revision return Revision
} }

View file

@ -52,14 +52,13 @@ module.exports = function (sequelize, DataTypes) {
password: { password: {
type: Sequelize.TEXT type: Sequelize.TEXT
} }
}, { })
instanceMethods: {
verifyPassword: function (attempt) { User.prototype.verifyPassword = function (attempt) {
return scrypt.verify(Buffer.from(this.password, 'hex'), attempt) return scrypt.verify(Buffer.from(this.password, 'hex'), attempt)
} }
},
classMethods: { User.associate = function (models) {
associate: function (models) {
User.hasMany(models.Note, { User.hasMany(models.Note, {
foreignKey: 'ownerId', foreignKey: 'ownerId',
constraints: false constraints: false
@ -68,14 +67,14 @@ module.exports = function (sequelize, DataTypes) {
foreignKey: 'lastchangeuserId', foreignKey: 'lastchangeuserId',
constraints: false constraints: false
}) })
}, }
getProfile: function (user) { User.getProfile = function (user) {
if (!user) { if (!user) {
return null return null
} }
return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null) return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null)
}, }
parseProfile: function (profile) { User.parseProfile = function (profile) {
try { try {
profile = JSON.parse(profile) profile = JSON.parse(profile)
} catch (err) { } catch (err) {
@ -90,8 +89,8 @@ module.exports = function (sequelize, DataTypes) {
} }
} }
return profile return profile
}, }
parsePhotoByProfile: function (profile, bigger) { User.parsePhotoByProfile = function (profile, bigger) {
var photo = null var photo = null
switch (profile.provider) { switch (profile.provider) {
case 'facebook': case 'facebook':
@ -146,25 +145,25 @@ module.exports = function (sequelize, DataTypes) {
break break
} }
return photo return photo
}, }
parseProfileByEmail: function (email) { User.parseProfileByEmail = function (email) {
return { return {
name: email.substring(0, email.lastIndexOf('@')), name: email.substring(0, email.lastIndexOf('@')),
photo: generateAvatarURL('', email, false), photo: generateAvatarURL('', email, false),
biggerphoto: generateAvatarURL('', email, true) biggerphoto: generateAvatarURL('', email, true)
} }
} }
}
})
function updatePasswordHashHook (user, options, done) { function updatePasswordHashHook (user, options) {
// suggested way to hash passwords to be able to do this asynchronously: // suggested way to hash passwords to be able to do this asynchronously:
// @see https://github.com/sequelize/sequelize/issues/1821#issuecomment-44265819 // @see https://github.com/sequelize/sequelize/issues/1821#issuecomment-44265819
if (!user.changed('password')) { return done() }
scrypt.kdf(user.getDataValue('password'), { logN: 15 }).then(keyBuf => { if (!user.changed('password')) {
return Promise.resolve()
}
return scrypt.kdf(user.getDataValue('password'), { logN: 15 }).then(keyBuf => {
user.setDataValue('password', keyBuf.toString('hex')) user.setDataValue('password', keyBuf.toString('hex'))
done()
}) })
} }

View file

@ -31,7 +31,7 @@
"codemirror": "git+https://github.com/hackmdio/CodeMirror.git", "codemirror": "git+https://github.com/hackmdio/CodeMirror.git",
"compression": "^1.6.2", "compression": "^1.6.2",
"connect-flash": "^0.1.1", "connect-flash": "^0.1.1",
"connect-session-sequelize": "^4.1.0", "connect-session-sequelize": "^6.0.0",
"cookie": "0.3.1", "cookie": "0.3.1",
"cookie-parser": "1.4.3", "cookie-parser": "1.4.3",
"deep-freeze": "^0.0.1", "deep-freeze": "^0.0.1",
@ -113,8 +113,7 @@
"scrypt-async": "^2.0.1", "scrypt-async": "^2.0.1",
"scrypt-kdf": "^2.0.1", "scrypt-kdf": "^2.0.1",
"select2": "^3.5.2-browserify", "select2": "^3.5.2-browserify",
"sequelize": "^3.28.0", "sequelize": "5.3.2",
"sequelize-cli": "^2.5.1",
"shortid": "2.2.8", "shortid": "2.2.8",
"socket.io": "~2.1.1", "socket.io": "~2.1.1",
"socket.io-client": "~2.1.1", "socket.io-client": "~2.1.1",
@ -194,6 +193,7 @@
"mocha": "^5.2.0", "mocha": "^5.2.0",
"mock-require": "^3.0.3", "mock-require": "^3.0.3",
"optimize-css-assets-webpack-plugin": "^5.0.0", "optimize-css-assets-webpack-plugin": "^5.0.0",
"sequelize-cli": "^5.4.0",
"script-loader": "^0.7.2", "script-loader": "^0.7.2",
"string-loader": "^0.0.1", "string-loader": "^0.0.1",
"style-loader": "^0.21.0", "style-loader": "^0.21.0",