Skip to content

Commit

Permalink
[Snyk] Security upgrade mongoose from 5.11.10 to 5.12.3 (#1538)
Browse files Browse the repository at this point in the history
* fix: package.json & package-lock.json to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-MQUERY-1089718

* refactor(mongoose): rework type defs

* refactor(models): rework form.server.model for mongoose

- recast FormSchema as FormDocumentSchema so that we can
  register methods specific to the handling of IFormDocument
- declare Schema.Types.DocumentArrayWithLooseDiscriminator
  so that we can more easily register our sub-schema

TODO - submit a patch to mongoose to loosen the type def for
`DocumentArray.discriminator()`

Co-authored-by: LoneRifle <LoneRifle@users.noreply.github.com>
  • Loading branch information
2 people authored and tshuli committed Apr 6, 2021
1 parent 6a9144e commit 80ee557
Show file tree
Hide file tree
Showing 15 changed files with 95 additions and 47 deletions.
28 changes: 14 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
"lodash": "^4.17.21",
"moment-timezone": "0.5.33",
"mongodb-uri": "^0.9.7",
"mongoose": "^5.11.10",
"mongoose": "^5.12.3",
"multiparty": ">=4.2.2",
"neverthrow": "^4.2.1",
"ng-infinite-scroll": "^1.3.0",
Expand Down
5 changes: 4 additions & 1 deletion src/app/models/admin_verification.server.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import { USER_SCHEMA_ID } from './user.server.model'

export const ADMIN_VERIFICATION_SCHEMA_ID = 'AdminVerification'

const AdminVerificationSchema = new Schema<IAdminVerificationSchema>(
const AdminVerificationSchema = new Schema<
IAdminVerificationSchema,
IAdminVerificationModel
>(
{
admin: {
type: Schema.Types.ObjectId,
Expand Down
28 changes: 15 additions & 13 deletions src/app/models/form.server.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const EncryptedFormSchema = new Schema<IEncryptedFormSchema>({
},
})

const EmailFormSchema = new Schema<IEmailFormSchema>({
const EmailFormSchema = new Schema<IEmailFormSchema, IEmailFormModel>({
emails: {
type: [
{
Expand Down Expand Up @@ -159,7 +159,7 @@ const compileFormModel = (db: Mongoose): IFormModel => {
const User = getUserModel(db)

// Schema
const FormSchema = new Schema<IFormSchema>(
const FormSchema = new Schema<IFormSchema, IFormModel>(
{
title: {
type: String,
Expand Down Expand Up @@ -342,7 +342,7 @@ const compileFormModel = (db: Mongoose): IFormModel => {
// Add discriminators for the various field types.
const FormFieldPath = FormSchema.path(
'form_fields',
) as Schema.Types.DocumentArray
) as Schema.Types.DocumentArrayWithLooseDiscriminator

const TableFieldSchema = createTableFieldSchema()

Expand Down Expand Up @@ -376,7 +376,7 @@ const compileFormModel = (db: Mongoose): IFormModel => {
FormFieldPath.discriminator(BasicField.Table, TableFieldSchema)
const TableColumnPath = TableFieldSchema.path(
'columns',
) as Schema.Types.DocumentArray
) as Schema.Types.DocumentArrayWithLooseDiscriminator
TableColumnPath.discriminator(
BasicField.ShortText,
createShortTextFieldSchema(),
Expand All @@ -389,13 +389,13 @@ const compileFormModel = (db: Mongoose): IFormModel => {
// Discriminator defines all possible values of startPage.logo
const StartPageLogoPath = FormSchema.path(
'startPage.logo',
) as Schema.Types.DocumentArray
) as Schema.Types.DocumentArrayWithLooseDiscriminator
StartPageLogoPath.discriminator(FormLogoState.Custom, CustomFormLogoSchema)

// Discriminator defines different logic types
const FormLogicPath = FormSchema.path(
'form_logics',
) as Schema.Types.DocumentArray
) as Schema.Types.DocumentArrayWithLooseDiscriminator

FormLogicPath.discriminator(LogicType.ShowFields, ShowFieldsLogicSchema)
FormLogicPath.discriminator(LogicType.PreventSubmit, PreventSubmitLogicSchema)
Expand Down Expand Up @@ -458,12 +458,6 @@ const compileFormModel = (db: Mongoose): IFormModel => {
}
}

FormSchema.methods.getSettings = function (
this: IFormDocument,
): FormSettings {
return pick(this, FORM_SETTING_FIELDS)
}

// Archives form.
FormSchema.methods.archive = function (this: IFormSchema) {
// Return instantly when form is already archived.
Expand All @@ -475,8 +469,16 @@ const compileFormModel = (db: Mongoose): IFormModel => {
return this.save()
}

const FormDocumentSchema = (FormSchema as unknown) as Schema<IFormDocument>

FormDocumentSchema.methods.getSettings = function (
this: IFormDocument,
): FormSettings {
return pick(this, FORM_SETTING_FIELDS)
}

// Transfer ownership of the form to another user
FormSchema.methods.transferOwner = async function (
FormDocumentSchema.methods.transferOwner = async function (
this: IFormDocument,
currentOwner: IUserSchema,
newOwner: IUserSchema,
Expand Down
5 changes: 4 additions & 1 deletion src/app/models/form_statistics_total.server.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ const FORM_STATS_TOTAL_SCHEMA_ID = 'FormStatisticsTotal'
const FORM_STATS_COLLECTION_NAME = 'formStatisticsTotal'

const compileFormStatisticsTotalModel = (db: Mongoose) => {
const FormStatisticsTotalSchema = new Schema<IFormStatisticsTotalSchema>(
const FormStatisticsTotalSchema = new Schema<
IFormStatisticsTotalSchema,
IFormStatisticsTotalModel
>(
{
formId: {
type: Schema.Types.ObjectId,
Expand Down
6 changes: 3 additions & 3 deletions src/app/models/login.server.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { USER_SCHEMA_ID } from './user.server.model'

export const LOGIN_SCHEMA_ID = 'Login'

const LoginSchema = new Schema<ILoginSchema>(
const LoginSchema = new Schema<ILoginSchema, ILoginModel>(
{
admin: {
type: Schema.Types.ObjectId,
Expand Down Expand Up @@ -133,7 +133,7 @@ LoginSchema.statics.aggregateLoginStats = function (
}

const compileLoginModel = (db: Mongoose) =>
db.model<ILoginSchema>(LOGIN_SCHEMA_ID, LoginSchema) as ILoginModel
db.model<ILoginSchema, ILoginModel>(LOGIN_SCHEMA_ID, LoginSchema)

/**
* Retrieves the Login model on the given Mongoose instance. If the model is
Expand All @@ -143,7 +143,7 @@ const compileLoginModel = (db: Mongoose) =>
*/
const getLoginModel = (db: Mongoose): ILoginModel => {
try {
return db.model(LOGIN_SCHEMA_ID) as ILoginModel
return db.model<ILoginSchema, ILoginModel>(LOGIN_SCHEMA_ID)
} catch {
return compileLoginModel(db)
}
Expand Down
13 changes: 8 additions & 5 deletions src/app/models/submission.server.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { FORM_SCHEMA_ID } from './form.server.model'

export const SUBMISSION_SCHEMA_ID = 'Submission'

const SubmissionSchema = new Schema<ISubmissionSchema>(
const SubmissionSchema = new Schema<ISubmissionSchema, ISubmissionModel>(
{
form: {
type: Schema.Types.ObjectId,
Expand Down Expand Up @@ -143,7 +143,10 @@ const webhookResponseSchema = new Schema<IWebhookResponseSchema>(
},
)

const EncryptSubmissionSchema = new Schema<IEncryptedSubmissionSchema>({
const EncryptSubmissionSchema = new Schema<
IEncryptedSubmissionSchema,
IEncryptSubmissionModel
>({
encryptedContent: {
type: String,
trim: true,
Expand Down Expand Up @@ -347,15 +350,15 @@ const compileSubmissionModel = (db: Mongoose): ISubmissionModel => {
const Submission = db.model('Submission', SubmissionSchema)
Submission.discriminator(SubmissionType.Email, EmailSubmissionSchema)
Submission.discriminator(SubmissionType.Encrypt, EncryptSubmissionSchema)
return db.model<ISubmissionSchema>(
return db.model<ISubmissionSchema, ISubmissionModel>(
SUBMISSION_SCHEMA_ID,
SubmissionSchema,
) as ISubmissionModel
)
}

const getSubmissionModel = (db: Mongoose): ISubmissionModel => {
try {
return db.model(SUBMISSION_SCHEMA_ID) as ISubmissionModel
return db.model<ISubmissionSchema, ISubmissionModel>(SUBMISSION_SCHEMA_ID)
} catch {
return compileSubmissionModel(db)
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/models/token.server.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IToken, ITokenModel, ITokenSchema } from '../../types'

export const TOKEN_SCHEMA_ID = 'Token'

const TokenSchema = new Schema<ITokenSchema>({
const TokenSchema = new Schema<ITokenSchema, ITokenModel>({
email: {
type: String,
required: true,
Expand Down
5 changes: 3 additions & 2 deletions src/app/models/user.server.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const USER_SCHEMA_ID = 'User'
const compileUserModel = (db: Mongoose) => {
const Agency = getAgencyModel(db)

const UserSchema: Schema<IUserSchema> = new Schema(
const UserSchema: Schema<IUserSchema, IUserModel> = new Schema(
{
email: {
type: String,
Expand Down Expand Up @@ -59,7 +59,8 @@ const compileUserModel = (db: Mongoose) => {
if (!phoneNumber) return false
return phoneNumber.isValid()
},
message: (props) => `${props.value} is not a valid mobile number`,
message: (props: { value: string }) =>
`${props.value} is not a valid mobile number`,
},
},
lastAccessed: Date,
Expand Down
2 changes: 1 addition & 1 deletion src/app/modules/bounce/bounce.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface IBounceModel extends Model<IBounceSchema> {
) => IBounceSchema
}

const BounceSchema = new Schema<IBounceSchema>({
const BounceSchema = new Schema<IBounceSchema, IBounceModel>({
formId: {
type: Schema.Types.ObjectId,
ref: FORM_SCHEMA_ID,
Expand Down
2 changes: 1 addition & 1 deletion src/app/modules/myinfo/myinfo_hash.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { FORM_SCHEMA_ID } from '../../models/form.server.model'

export const MYINFO_HASH_SCHEMA_ID = 'MyInfoHash'

const MyInfoHashSchema = new Schema<IMyInfoHashSchema>(
const MyInfoHashSchema = new Schema<IMyInfoHashSchema, IMyInfoHashModel>(
{
// We stored a hashed uinFin using a salt stored as a env var
// Note: key name not updated to reflect this for backward compatibility purposes
Expand Down
4 changes: 2 additions & 2 deletions src/app/modules/submission/submission.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,15 +227,15 @@ export const getFormSubmissionsCount = (
* @returns ok(true) if all emails were sent successfully
* @returns err(SendEmailConfirmationError) if any email failed to be sent
*/
export const sendEmailConfirmations = ({
export const sendEmailConfirmations = <S extends ISubmissionSchema>({
form,
submission,
parsedResponses,
autoReplyData,
attachments,
}: {
form: IPopulatedForm
submission: ISubmissionSchema
submission: S
parsedResponses: ProcessedFieldResponse[]
autoReplyData?: EmailRespondentConfirmationField[]
attachments?: IAttachmentInfo[]
Expand Down
5 changes: 4 additions & 1 deletion src/app/modules/verification/verification.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ const VerificationFieldSchema = new Schema<IVerificationFieldSchema>({
})

const compileVerificationModel = (db: Mongoose): IVerificationModel => {
const VerificationSchema = new Schema<IVerificationSchema>({
const VerificationSchema = new Schema<
IVerificationSchema,
IVerificationModel
>({
formId: {
type: Schema.Types.ObjectId,
ref: FORM_SCHEMA_ID,
Expand Down
2 changes: 1 addition & 1 deletion src/app/services/sms/sms_count.server.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const BouncedSubmissionSmsCountSchema = new Schema<IBouncedSubmissionSmsCountSch
)

const compileSmsCountModel = (db: Mongoose) => {
const SmsCountSchema = new Schema<ISmsCountSchema>(
const SmsCountSchema = new Schema<ISmsCountSchema, ISmsCountModel>(
{
msgSrvcSid: {
type: String,
Expand Down
33 changes: 33 additions & 0 deletions src/types/vendor/mongoose.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Additional type declarations for mongoose to fit our use case,
* to accommodate the non-standard but compatible use of types
* in schema registration
*/
declare module 'mongoose' {
namespace Schema {
namespace Types {
/**
* A DocumentArray with a discriminator function that takes in a
* type-generic Schema.
*/
class DocumentArrayWithLooseDiscriminator extends DocumentArray {
/**
* In the built-in type declarations in
* version 5.12 of mongoose, discriminator() expects a Schema
* (and hence Schema<Document>). This does not work; a Schema
* for a subtype of Document is not a Schema for a Document, as
* Schema.methods expect to operate on the Schema's document type,
* which is not necessarily a Document.
*
* Address this by overriding the definition and provide a type-generic
* Schema argument.
*/
discriminator<T extends Document>(
name: string,
schema: Schema<T>,
tag?: string,
): unknown
}
}
}
}

0 comments on commit 80ee557

Please sign in to comment.