@@ -188,20 +188,33 @@ export class CustomBlockValidationError extends Error {
188188}
189189
190190/**
191- * Publish a deployed workflow as an org-wide custom block. Validates that the
192- * source workflow belongs to `organizationId` and is deployed, then inserts the
193- * row. Caller must have already authorized admin + enterprise.
191+ * Publish a deployed workflow as an org-wide custom block. The source workflow
192+ * must live in `workspaceId` — the workspace the caller was verified to admin —
193+ * so a caller cannot publish another workspace's workflow (which then runs under
194+ * that workspace owner's credentials and returns caller-chosen outputs). Also
195+ * validates the workspace belongs to `organizationId` and the workflow is
196+ * deployed, then inserts the row.
194197 */
195198export async function publishCustomBlock ( params : {
196199 organizationId : string
200+ workspaceId : string
197201 workflowId : string
198202 userId : string
199203 name : string
200204 description : string
201205 iconUrl ?: string
202206 exposedOutputs ?: CustomBlockOutput [ ]
203207} ) : Promise < CustomBlockWithInputs > {
204- const { organizationId, workflowId, userId, name, description, iconUrl, exposedOutputs } = params
208+ const {
209+ organizationId,
210+ workspaceId,
211+ workflowId,
212+ userId,
213+ name,
214+ description,
215+ iconUrl,
216+ exposedOutputs,
217+ } = params
205218
206219 const [ wf ] = await db
207220 . select ( { id : workflow . id , workspaceId : workflow . workspaceId , isDeployed : workflow . isDeployed } )
@@ -214,6 +227,13 @@ export async function publishCustomBlock(params: {
214227 throw new CustomBlockValidationError ( 'Workflow must be deployed before publishing as a block' )
215228 }
216229
230+ // Authorization boundary: the caller proved admin on `workspaceId` (route), so
231+ // the source workflow must actually live there. Without this a workspace admin
232+ // could publish a different workspace's workflow in the same org.
233+ if ( wf . workspaceId !== workspaceId ) {
234+ throw new CustomBlockValidationError ( 'You can only publish a workflow from its own workspace' )
235+ }
236+
217237 const ws = wf . workspaceId ? await getWorkspaceWithOwner ( wf . workspaceId ) : null
218238 if ( ! ws ?. organizationId || ws . organizationId !== organizationId ) {
219239 throw new CustomBlockValidationError ( 'Workflow does not belong to this organization' )
0 commit comments