Fix improper mass assignment in account registration (#5689)

* Sanitize account information during registration

* Fix Improper Mass Assignment in Account Registration

* Update account.service.ts

* Fix improper mass assignment in account registration

* Fix improper mass assignment in account registration

* Update packages/server/src/enterprise/controllers/account.controller.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Update packages/server/src/enterprise/controllers/account.controller.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* fix(account.controller.ts): remove additional }

* chore(organization/index.jsx): remove hardcoded user type pro from organization setup

---------

Co-authored-by: yau-wd <[email protected]>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
christopherholland-workday
2026-02-12 22:20:59 +08:00
committed by GitHub
co-authored by gemini-code-assist[bot] yau-wd
parent 4b1b8ba376
commit 1e4b0d8788
4 changed files with 54 additions and 10 deletions
@@ -1,13 +1,16 @@
import { NextFunction, Request, Response } from 'express'
import { StatusCodes } from 'http-status-codes'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { AccountService } from '../services/account.service'
import { Organization } from '../database/entities/organization.entity'
import { User } from '../database/entities/user.entity'
import { AccountDTO, AccountService } from '../services/account.service'
export class AccountController {
public async register(req: Request, res: Response, next: NextFunction) {
try {
const accountService = new AccountService()
const data = await accountService.register(req.body)
const sanitizedBody = sanitizeRegistrationDTO(req.body)
const data = await accountService.register(sanitizedBody)
return res.status(StatusCodes.CREATED).json(data)
} catch (error) {
next(error)
@@ -134,3 +137,42 @@ export class AccountController {
}
}
}
function sanitizeRegistrationDTO(data: AccountDTO): AccountDTO {
const sanitized: AccountDTO = {
user: {},
organization: {},
organizationUser: {},
workspace: {},
workspaceUser: {},
role: {}
}
// Strict allowlist: only fields a client may supply during registration.
// Never accept server-managed fields: id, createdBy, updatedBy, createdDate, updatedDate, status, tokenExpiry.
const allowedUserFields: (keyof User)[] = ['name', 'email', 'credential', 'tempToken']
if (data.user && typeof data.user === 'object' && !Array.isArray(data.user)) {
for (const field of allowedUserFields) {
const value = data.user[field]
if (value != null) {
sanitized.user[field] = value as any
}
}
if (data.user.referral != null) {
sanitized.user.referral = data.user.referral
}
}
// Allow organization.name for Enterprise owner registration (the only path that doesn't hardcode it).
const allowedOrgFields: (keyof Organization)[] = ['name']
if (data.organization && typeof data.organization === 'object' && !Array.isArray(data.organization)) {
for (const field of allowedOrgFields) {
const value = data.organization[field]
if (value != null) {
sanitized.organization[field] = value as any
}
}
}
return sanitized
}
@@ -29,8 +29,11 @@ import { UserErrorMessage, UserService } from './user.service'
import { WorkspaceUserErrorMessage, WorkspaceUserService } from './workspace-user.service'
import { WorkspaceErrorMessage, WorkspaceService } from './workspace.service'
type AccountDTO = {
user: Partial<User>
/** Optional referral field for Stripe referral tracking in CLOUD; not a User entity column. */
type RegistrationUser = Partial<User> & { referral?: string }
export type AccountDTO = {
user: RegistrationUser
organization: Partial<Organization>
organizationUser: Partial<OrganizationUser>
workspace: Partial<Workspace>
@@ -138,7 +141,6 @@ export class AccountService {
const { customerId, subscriptionId } = await this.identityManager.createStripeUserAndSubscribe({
email: data.user.email,
userPlan: UserPlan.FREE,
// @ts-ignore
referral: data.user.referral || ''
})
data.organization.customerId = customerId
@@ -88,10 +88,11 @@ export class UserService {
if (data.status) this.validateUserStatus(data.status)
data.id = generateId()
if (data.createdBy) {
const createdBy = await this.readUserById(data.createdBy, queryRunner)
if (!createdBy) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
data.createdBy = createdBy.id
const createdById = data.createdBy
if (createdById) {
const createdByUser = await this.readUserById(createdById, queryRunner)
if (!createdByUser) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)
data.createdBy = createdByUser.id
data.updatedBy = data.createdBy
} else {
data.createdBy = data.id
@@ -145,7 +145,6 @@ const OrganizationSetupPage = () => {
user: {
name: username,
email: email,
type: 'pro',
credential: password
}
}