CedarCopilot wants to merge 5 commits into staging from fix/transcript-key-cross-org-auth
Live on prod, no production signal yet
Strict CloudWatch fallback found 1 recent prod failure log lines for [processBufferedSlackWebhookEvents] Failed to process row (Failed query: delete from \), but the same failure had 48 log lines in the comparable pre-deploy baseline. Libra is not blaming this PR intent without.
Strict CloudWatch fallback found 1 recent prod failure log lines for [processBufferedSlackWebhookEvents] Failed to process row (Failed query: delete from \), but the same failure had 48 log lines in the comparable pre-deploy baseline. Libra is not blaming this PR intent without.
Unable to verify the intended behavior: no production OTEL spans matched replay, snapshot, or event-execution surfaces since the 2026-09-10T02:22:47Z deploy. Positive runtime evidence is absent, so the result is inconclusive.
Since 2026-09-10T02:22:47Z, CloudWatch returned 0 logs for `[updateConversationTypeInFileStore]`, including no meeting-not-found warnings or direct update successes. OTEL returned 0 matching update/transcript/file-store update spans; the only related result was one unrelated.
Since 2026-09-10T02:22:47Z, queries for handleReexecuteEvent/reexecute-related OTEL spans and concrete CloudWatch messages returned 0 rows. The handler source confirms the meeting success message and storage-failure message, but there is no positive runtime execution evidence.
Verification reached its 120s wall-clock budget before a tied verdict. Libra is keeping this intent verifying until the next check.
internal_only
internal_only
internal_only
internal_only
internal_only
internal_only
internal_only
internal_only
internal_only
internal_only
internal_only
internal_only
internal_only
Libra has verdicts on 0 of 9 tracked behaviors on prod; 9 are still being checked. Libra checks hourly for 3 days after each deploy.
Follow-up to #2740 (merged), fixing a cross-org security gap Greptile flagged on that PR's review.
#2740 fixed cross-user meeting-transcript resolution by trusting crm_meeting_events.transcription_key directly instead of guessing an S3 key from the querying user's own connections. But transcription_key is client-writable via crm.createEvent with no ownership check at write time , so trusting it unconditionally let any authenticated caller read any organization's meeting transcript, just by guessing or leaking a key. This gap has been live in production since #2740 merged.
isTranscriptionKeyAuthorizedForOrg (services/crm/events.ts) parses the connection id out of a transcriptionKey, resolves that connection's owning user's organizationId, and checks it against the reading event's org. Fails closed (false) when db or organizationId is missing.resolveMeetingS3Object now calls this before trusting a direct key; a rejected key falls through to the existing guessed-connection loop exactly as if no key had been recorded , which can only ever find a meeting under a connection the querying user already owns, so it cannot leak another org's transcript.getEventRawContentByIds and hydrateExecutionEvents now resolve and thread the acting organizationId through. Both callers of hydrateExecutionEvents (pre-execution-setup.ts, initial-sync/handlers.ts) pass the db/userId they already had in scope.getMeetingTranscription (trpc/routes/crm.ts) had the same underlying gap via a raw client-supplied transcriptionKey , this one predates #2740. It's a small, self-contained reuse of isTranscriptionKeyAuthorizedForOrg with the same fall-through-to-connection-lookup behavior.cedarMeetingDataSchema (services/integrations/meetings/types.ts) replaces the as CedarMeetingData casts at both S3-JSON read boundaries with real zod validation (per root CLAUDE.md → "NEVER cast"). meetingTime accepts either a Date or an ISO string since S3-stored data round-trips as JSON, and normalizes to Date.pnpm --filter @zero/server run types , cleanpnpm --filter @zero/server exec vitest run src/services/crm/__tests__/meeting-s3-cross-user-resolution.test.ts , updated for resolveMeetingS3Object's new db/organizationId params; adds cross-org rejection, fail-closed (no db/no orgId), and isTranscriptionKeyAuthorizedForOrg unit coverage (19 tests)pnpm --filter @zero/server exec vitest run src/services/crm/__tests__/transcript-key-cross-org-auth.test.ts , new end-to-end coverage for getEventRawContentByIds and hydrateExecutionEvents: cross-org rejection (no disclosure), legitimate same-org cross-user resolution (the original #2740 case), and malformed S3 JSON now caught by cedarMeetingDataSchema.parse and handled the same way as any other S3-read failure at each site (7 tests)src/services/crm/__tests__/ suite (70 files, 805 tests) , all passeslint on every touched file , clean (one pre-existing unrelated no-explicit-any finding elsewhere in crm.ts, untouched by this diff)🤖 Generated with Claude Code
https://claude.ai/code/session_01YZB5RgfxHLmuKtWof2wP4g
This PR adds organization authorization before direct meeting-transcript object reads, threads database and acting-user context through hydration callers, and validates stored meeting JSON with Zod.
The PR is not ready to merge because its new authorization
Libra has not measured any production surfaces for this change yet.