Summary
GET /api/core/ai/record/getRecord authenticates any logged-in user but loads an LLM request/response trace purely by requestId with no team scoping — the llm_request_records collection has no teamId field at all. Any authenticated user reads another team's prompts, retrieved RAG chunks, and completions by supplying a requestId (which is surfaced to chat clients).
Details
projects/app/src/pages/api/core/ai/record/getRecord.ts:25-46: authCert yields tmbId used only for rate-limit; getLLMRequestRecord(requestId) called with no caller identity.
packages/service/core/ai/record/controller.ts:96-100: MongoLLMRequestRecord.findOne({ requestId }) — single-field, no team predicate.
packages/service/core/ai/record/schema.ts: fields are requestId/body/response/createdAt — no teamId/tmbId (grep confirms zero matches).
- twin:
authDatasetByTmbId rejects dataset.teamId !== teamId.
- requestId is client-surfaced via workflow node responses (
runtime/type.ts:171).
Verification
Real local mongo + verbatim schema/controller: a team-A caller resolved a team-B requestId and received team B's body + response; the schema has no team-binding field.
Remediation
Add teamId to the record at save time and constrain the lookup to { requestId, teamId }.
Summary
GET /api/core/ai/record/getRecordauthenticates any logged-in user but loads an LLM request/response trace purely byrequestIdwith no team scoping — thellm_request_recordscollection has noteamIdfield at all. Any authenticated user reads another team's prompts, retrieved RAG chunks, and completions by supplying arequestId(which is surfaced to chat clients).Details
projects/app/src/pages/api/core/ai/record/getRecord.ts:25-46:authCertyieldstmbIdused only for rate-limit;getLLMRequestRecord(requestId)called with no caller identity.packages/service/core/ai/record/controller.ts:96-100:MongoLLMRequestRecord.findOne({ requestId })— single-field, no team predicate.packages/service/core/ai/record/schema.ts: fields arerequestId/body/response/createdAt— noteamId/tmbId(grep confirms zero matches).authDatasetByTmbIdrejectsdataset.teamId !== teamId.runtime/type.ts:171).Verification
Real local mongo + verbatim schema/controller: a team-A caller resolved a team-B
requestIdand received team B's body + response; the schema has no team-binding field.Remediation
Add
teamIdto the record at save time and constrain the lookup to{ requestId, teamId }.