Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/server/src/controllers/marketplaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NextFunction, Request, Response } from 'express'
import { StatusCodes } from 'http-status-codes'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import marketplacesService from '../../services/marketplaces'
import { stripProtectedFields } from '../../utils/stripProtectedFields'

// Get all templates for marketplaces
const getAllTemplates = async (req: Request, res: Response, next: NextFunction) => {
Expand Down Expand Up @@ -52,15 +53,14 @@ const saveCustomTemplate = async (req: Request, res: Response, next: NextFunctio
`Error: marketplacesService.saveCustomTemplate - body not provided!`
)
}
const body = req.body
body.workspaceId = req.user?.activeWorkspaceId
if (!body.workspaceId) {
const workspaceId = req.user?.activeWorkspaceId
if (!workspaceId) {
throw new InternalFlowiseError(
StatusCodes.NOT_FOUND,
`Error: marketplacesController.saveCustomTemplate - workspace ${body.workspaceId} not found!`
`Error: marketplacesController.saveCustomTemplate - workspace ${workspaceId} not found!`
)
}
const apiResponse = await marketplacesService.saveCustomTemplate(body)
const apiResponse = await marketplacesService.saveCustomTemplate({ ...stripProtectedFields(req.body), workspaceId })
return res.json(apiResponse)
} catch (error) {
next(error)
Expand Down
4 changes: 3 additions & 1 deletion packages/server/src/services/marketplaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { getErrorMessage } from '../../errors/utils'
import { IReactFlowEdge, IReactFlowNode } from '../../Interface'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { stripProtectedFields } from '../../utils/stripProtectedFields'
import chatflowsService from '../chatflows'

type ITemplate = {
Expand Down Expand Up @@ -208,7 +209,8 @@ const saveCustomTemplate = async (body: any): Promise<any> => {
let flowDataStr = ''
let derivedFramework = ''
const customTemplate = new CustomTemplate()
Object.assign(customTemplate, body)
Object.assign(customTemplate, stripProtectedFields(body))
customTemplate.workspaceId = body.workspaceId // re-apply: set by controller from req.user
Comment on lines +212 to +213
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

While using stripProtectedFields fixes the mass assignment vulnerability, a more robust and secure pattern is to use a whitelist by explicitly mapping only the allowed properties. This aligns with the repository rule to 'explicitly map allowed properties' and prevents accidental exposure of any new sensitive fields. Sensitive fields like workspaceId must be set from a trusted source (e.g., user session) rather than being mapped from the request body.

Note: framework is omitted from this mapping because its value is overwritten by logic later in the function.

customTemplate.name = body.name
customTemplate.description = body.description
customTemplate.badge = body.badge
customTemplate.usecases = body.usecases
customTemplate.type = body.type
References
  1. Avoid mass assignment from request bodies to entities. Instead of using a generic assignment like Object.assign(entity, body), explicitly map allowed properties. Sensitive fields like workspaceId must be set on the server from a trusted source (e.g., user session), not from the client request body, to prevent IDOR vulnerabilities.


if (body.chatflowId) {
const chatflow = await chatflowsService.getChatflowById(body.chatflowId, body.workspaceId)
Expand Down
Loading