mirror of
https://github.com/KeygraphHQ/juice-shop.git
synced 2026-11-08 17:19:57 -10:00
145 lines
4.1 KiB
TypeScript
145 lines
4.1 KiB
TypeScript
/*
|
|
* Copyright (c) 2014-2025 Bjoern Kimminich & the OWASP Juice Shop contributors.
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
/* jslint node: true */
|
|
import config from 'config'
|
|
import {
|
|
type InferAttributes,
|
|
type InferCreationAttributes,
|
|
Model,
|
|
DataTypes,
|
|
type CreationOptional,
|
|
type Sequelize
|
|
} from 'sequelize'
|
|
import * as challengeUtils from '../lib/challengeUtils'
|
|
import * as utils from '../lib/utils'
|
|
import { challenges } from '../data/datacache'
|
|
import * as security from '../lib/insecurity'
|
|
|
|
class User extends Model<
|
|
InferAttributes<User>,
|
|
InferCreationAttributes<User>
|
|
> {
|
|
declare id: CreationOptional<number>
|
|
declare username: string | undefined
|
|
declare email: CreationOptional<string>
|
|
declare password: CreationOptional<string>
|
|
declare role: CreationOptional<string>
|
|
declare deluxeToken: CreationOptional<string>
|
|
declare lastLoginIp: CreationOptional<string>
|
|
declare profileImage: CreationOptional<string>
|
|
declare totpSecret: CreationOptional<string>
|
|
declare isActive: CreationOptional<boolean>
|
|
}
|
|
|
|
const UserModelInit = (sequelize: Sequelize) => { // vuln-code-snippet start weakPasswordChallenge
|
|
User.init(
|
|
{ // vuln-code-snippet hide-start
|
|
id: {
|
|
type: DataTypes.INTEGER,
|
|
primaryKey: true,
|
|
autoIncrement: true
|
|
},
|
|
username: {
|
|
type: DataTypes.STRING,
|
|
defaultValue: '',
|
|
set (username: string) {
|
|
if (utils.isChallengeEnabled(challenges.persistedXssUserChallenge)) {
|
|
username = security.sanitizeLegacy(username)
|
|
} else {
|
|
username = security.sanitizeSecure(username)
|
|
}
|
|
this.setDataValue('username', username)
|
|
}
|
|
},
|
|
email: {
|
|
type: DataTypes.STRING,
|
|
unique: true,
|
|
set (email: string) {
|
|
if (utils.isChallengeEnabled(challenges.persistedXssUserChallenge)) {
|
|
challengeUtils.solveIf(challenges.persistedXssUserChallenge, () => {
|
|
return utils.contains(
|
|
email,
|
|
'<iframe src="javascript:alert(`xss`)">'
|
|
)
|
|
})
|
|
} else {
|
|
email = security.sanitizeSecure(email)
|
|
}
|
|
this.setDataValue('email', email)
|
|
}
|
|
}, // vuln-code-snippet hide-end
|
|
password: {
|
|
type: DataTypes.STRING,
|
|
set (clearTextPassword: string) {
|
|
this.setDataValue('password', security.hash(clearTextPassword)) // vuln-code-snippet vuln-line weakPasswordChallenge
|
|
}
|
|
}, // vuln-code-snippet end weakPasswordChallenge
|
|
role: {
|
|
type: DataTypes.STRING,
|
|
defaultValue: 'customer',
|
|
validate: {
|
|
isIn: [['customer', 'deluxe', 'accounting', 'admin']]
|
|
},
|
|
set (role: string) {
|
|
const profileImage = this.getDataValue('profileImage')
|
|
if (
|
|
role === security.roles.admin &&
|
|
(!profileImage ||
|
|
profileImage === '/assets/public/images/uploads/default.svg')
|
|
) {
|
|
this.setDataValue(
|
|
'profileImage',
|
|
'/assets/public/images/uploads/defaultAdmin.png'
|
|
)
|
|
}
|
|
this.setDataValue('role', role)
|
|
}
|
|
},
|
|
deluxeToken: {
|
|
type: DataTypes.STRING,
|
|
defaultValue: ''
|
|
},
|
|
lastLoginIp: {
|
|
type: DataTypes.STRING,
|
|
defaultValue: '0.0.0.0'
|
|
},
|
|
profileImage: {
|
|
type: DataTypes.STRING,
|
|
defaultValue: '/assets/public/images/uploads/default.svg'
|
|
},
|
|
totpSecret: {
|
|
type: DataTypes.STRING,
|
|
defaultValue: ''
|
|
},
|
|
isActive: {
|
|
type: DataTypes.BOOLEAN,
|
|
defaultValue: true
|
|
}
|
|
},
|
|
{
|
|
tableName: 'Users',
|
|
paranoid: true,
|
|
sequelize
|
|
}
|
|
)
|
|
|
|
User.addHook('afterValidate', async (user: User) => {
|
|
if (
|
|
user.email &&
|
|
user.email.toLowerCase() ===
|
|
`acc0unt4nt@${config.get<string>('application.domain')}`.toLowerCase()
|
|
) {
|
|
await Promise.reject(
|
|
new Error(
|
|
'Nice try, but this is not how the "Ephemeral Accountant" challenge works!'
|
|
)
|
|
)
|
|
}
|
|
})
|
|
}
|
|
|
|
export { User as UserModel, UserModelInit }
|