{"record":{"id":"44ce27d6d443d388","repo":"paperclipai/paperclip","slug":"cross-company-workspace","errorCode":"cross_company_workspace","errorMessage":"Project workspace belongs to another company","messagePattern":"Project workspace belongs to another company","errorType":"http","errorClass":"HttpError","httpStatus":403,"severity":"error","filePath":"server/src/services/workspace-file-resources.ts","lineNumber":1056,"sourceCode":"    return issue;\n  }\n\n  async function targetProjectWorkspaceCandidate(\n    issue: IssueRow,\n    target: WorkspaceTargetInput,\n  ): Promise<WorkspaceCandidate | null> {\n    const projectId = target.projectId ?? null;\n    const workspaceId = target.workspaceId ?? null;\n    if (!projectId && !workspaceId) return null;\n    if (!projectId || !workspaceId) {\n      throw unprocessable(\"Workspace file target requires both projectId and workspaceId\", { code: \"invalid_target\" });\n    }\n\n    const [project] = await db.select().from(projects).where(eq(projects.id, projectId)).limit(1);\n    const [workspace] = await db.select().from(projectWorkspaces).where(eq(projectWorkspaces.id, workspaceId)).limit(1);\n    if (!project || !workspace) throw notFound(\"Project workspace not found\");\n    if (project.companyId !== issue.companyId || workspace.companyId !== issue.companyId) {\n      throw new HttpError(403, \"Project workspace belongs to another company\", { code: \"cross_company_workspace\" });\n    }\n    if (workspace.projectId !== project.id) {\n      throw unprocessable(\"Workspace does not belong to the selected project\", { code: \"workspace_project_mismatch\" });\n    }\n\n    return candidateFromProjectWorkspace(workspace, { id: project.id, name: project.name });\n  }\n\n  async function listCandidates(\n    issue: IssueRow,\n    selector: WorkspaceFileSelector,\n    target: WorkspaceTargetInput = {},\n  ): Promise<WorkspaceCandidate[]> {\n    const explicitTarget = await targetProjectWorkspaceCandidate(issue, target);\n    if (explicitTarget) return [explicitTarget];\n\n    const candidates: WorkspaceCandidate[] = [];\n    if ((selector === \"auto\" || selector === \"execution\") && issue.projectId) {","sourceCodeStart":1038,"sourceCodeEnd":1074,"githubUrl":"https://github.com/paperclipai/paperclip/blob/120ae5428fa29bee300bcf806491cd4d965fbb7c/server/src/services/workspace-file-resources.ts#L1038-L1074","documentation":"Cross-tenant guard in targetProjectWorkspaceCandidate: the project/workspace ids supplied for an explicit workspace target resolve to rows owned by a different company than the issue's. The mismatch is rejected before any path resolution so one tenant can never address another tenant's workspace files.","triggerScenarios":"Thrown at server/src/services/workspace-file-resources.ts:1056 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Access the workspace through a project that belongs to your company; cross-company workspace access is forbidden."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"120ae5428fa29bee300bcf806491cd4d965fbb7c","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}