Skip to content

fix(realtime): validate cursor and selection presence payloads - #8041

Merged
waleedlatif1 merged 2 commits into
stagingfrom
audit/presence-validation
Sep 19, 2026
Merged

waleedlatif1 merged 2 commits into
stagingfrom
audit/presence-validation

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • cursor-update and selection-update stored whatever object a client sent into the shared room presence hash and rebroadcast it to every peer, with no shape or size check. An authenticated user with read access to any workflow could park a multi-megabyte blob (bounded only by the 8 MiB Socket.IO frame ceiling) in shared Redis on every socket they opened, and have the server fan it out on every later presence broadcast.
  • Both payloads are now rebuilt from a fixed field set before they reach room state or any broadcast, so unexpected keys can't ride along. This mirrors normalizeCellSelection in the table presence handler, which already guards the sibling path — the workflow path was the unguarded one.
  • Added a defensive per-field length cap in updateUserActivity so future presence-bearing events inherit the bound. At 4096 it sits ~8x above the largest legitimate payload, so it never trims real presence.
  • Marked UserPresence.cursor nullable. The client already sends cursor: null to clear, and that null is genuinely stored — the type just didn't say so, which only type-checked before because the handler destructured an untyped payload.

Type of Change

  • Bug fix

Testing

  • New presence.test.ts covers both handlers: well-formed payloads, cleared cursor, unexpected-key stripping, and malformed/oversized rejection. Verified the tests can fail — reverting the handler turns 15 of the 20 red, while the 5 behavior-preservation cases stay green either way.
  • Full realtime suite passes (408 tests, 18 files).
  • bunx turbo run type-check green across all 26 tasks; bun run lint and all 47 audits pass.
  • No wire-format change, so old and new pods interoperate during a rolling deploy — normalized payloads are a subset of what the previous handler accepted.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

The workflow cursor-update and selection-update handlers stored whatever
object a client sent into the shared room presence hash and rebroadcast it
to every peer, with no shape or size check. An authenticated user with read
access to any workflow could park a multi-megabyte blob in shared Redis on
every socket they opened and have the server fan it out on each presence
broadcast.

Both payloads are now rebuilt from a fixed field set before they reach room
state or any broadcast, so unexpected keys cannot ride along - mirroring
normalizeCellSelection in the table presence handler. Adds a defensive
per-field length cap in updateUserActivity so future presence-bearing events
inherit the bound, and marks UserPresence.cursor nullable to match the
cleared-cursor value the client already sends.
@vercel

vercel Bot commented Sep 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
docs Skipped Skipped Sep 19, 2026 11:16pm UTC

Request Review

@greptile-apps

greptile-apps Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge; no actionable new issue or outstanding previous finding remains.

Summary

This PR hardens workflow presence handling against oversized or malformed client payloads.

  • Rebuilds cursor and selection updates from validated, allowlisted fields before storage and broadcast.
  • Adds a UTF-8 byte-based 16 KiB defensive limit for each Redis-backed presence field.
  • Updates the presence type to represent cursor clearing with null.
  • Adds handler tests covering valid values, clearing, malformed input, oversized input, and unexpected-key removal.
  • The change since the previous review corrects the field-size calculation and fully addresses the resolved review concern.

Reviews (2) · Last reviewed commit: "fix(realtime): measure presence field ca..."

Comment thread apps/realtime/src/rooms/redis-manager.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/realtime/src/rooms/redis-manager.ts Outdated
The cap compared UTF-16 code units against a byte budget, so a multi-byte
payload could pass the check and still land several times larger in the
room hash. It now measures the UTF-8 bytes Redis actually stores.

Raises the ceiling to 16384. A table cell selection carries four ids capped
at 200 characters each, and multi-byte characters plus JSON escaping can
expand a legitimate worst case to roughly 5 KB - above the previous 4096,
so the old bound could have dropped real presence.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

@waleedlatif1
waleedlatif1 merged commit c45e8b5 into staging Sep 19, 2026
34 checks passed
@waleedlatif1
waleedlatif1 deleted the audit/presence-validation branch September 19, 2026 23:25

This branch was previously deployed

1 inactive deployment
Preview e2c57dba Deployed Sep 19, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant