juice-shop
144 строки · 4.1 Кб
1/*
2* Copyright (c) 2014-2024 Bjoern Kimminich & the OWASP Juice Shop contributors.
3* SPDX-License-Identifier: MIT
4*/
5
6/* jslint node: true */
7import config from 'config'8import {9type InferAttributes,10type InferCreationAttributes,11Model,12DataTypes,13type CreationOptional,14type Sequelize15} from 'sequelize'16import * as challengeUtils from '../lib/challengeUtils'17import * as utils from '../lib/utils'18import { challenges } from '../data/datacache'19import * as security from '../lib/insecurity'20
21class User extends Model<22InferAttributes<User>,23InferCreationAttributes<User>24> {25declare id: CreationOptional<number>26declare username: string | undefined27declare email: CreationOptional<string>28declare password: CreationOptional<string>29declare role: CreationOptional<string>30declare deluxeToken: CreationOptional<string>31declare lastLoginIp: CreationOptional<string>32declare profileImage: CreationOptional<string>33declare totpSecret: CreationOptional<string>34declare isActive: CreationOptional<boolean>35}
36
37const UserModelInit = (sequelize: Sequelize) => { // vuln-code-snippet start weakPasswordChallenge38User.init(39{ // vuln-code-snippet hide-start40id: {41type: DataTypes.INTEGER,42primaryKey: true,43autoIncrement: true44},45username: {46type: DataTypes.STRING,47defaultValue: '',48set (username: string) {49if (utils.isChallengeEnabled(challenges.persistedXssUserChallenge)) {50username = security.sanitizeLegacy(username)51} else {52username = security.sanitizeSecure(username)53}54this.setDataValue('username', username)55}56},57email: {58type: DataTypes.STRING,59unique: true,60set (email: string) {61if (utils.isChallengeEnabled(challenges.persistedXssUserChallenge)) {62challengeUtils.solveIf(challenges.persistedXssUserChallenge, () => {63return utils.contains(64email,65'<iframe src="javascript:alert(`xss`)">'66)67})68} else {69email = security.sanitizeSecure(email)70}71this.setDataValue('email', email)72}73}, // vuln-code-snippet hide-end74password: {75type: DataTypes.STRING,76set (clearTextPassword: string) {77this.setDataValue('password', security.hash(clearTextPassword)) // vuln-code-snippet vuln-line weakPasswordChallenge78}79}, // vuln-code-snippet end weakPasswordChallenge80role: {81type: DataTypes.STRING,82defaultValue: 'customer',83validate: {84isIn: [['customer', 'deluxe', 'accounting', 'admin']]85},86set (role: string) {87const profileImage = this.getDataValue('profileImage')88if (89role === security.roles.admin &&90(!profileImage ||91profileImage === '/assets/public/images/uploads/default.svg')92) {93this.setDataValue(94'profileImage',95'/assets/public/images/uploads/defaultAdmin.png'96)97}98this.setDataValue('role', role)99}100},101deluxeToken: {102type: DataTypes.STRING,103defaultValue: ''104},105lastLoginIp: {106type: DataTypes.STRING,107defaultValue: '0.0.0.0'108},109profileImage: {110type: DataTypes.STRING,111defaultValue: '/assets/public/images/uploads/default.svg'112},113totpSecret: {114type: DataTypes.STRING,115defaultValue: ''116},117isActive: {118type: DataTypes.BOOLEAN,119defaultValue: true120}121},122{123tableName: 'Users',124paranoid: true,125sequelize
126}127)128
129User.addHook('afterValidate', async (user: User) => {130if (131user.email &&132user.email.toLowerCase() ===133`acc0unt4nt@${config.get<string>('application.domain')}`.toLowerCase()134) {135await Promise.reject(136new Error(137'Nice try, but this is not how the "Ephemeral Accountant" challenge works!'138)139)140}141})142}
143
144export { User as UserModel, UserModelInit }145