Skip to content

[Tracking] Security review 2026-10-06 fixes #416

Description

@HMarzban

Goal

Fix the 34 findings from the 2026-10-06 security review. The first target was Friday 2026-10-09. Every issue below is on the milestone Security review 2026-10-06 and is a sub-issue of this one.

Status (2026-10-09)

All code is deployed (bbdaae66c, production run 37976785981, green). All migrations are applied, including the late ones for #434 and #402, and verified read-only on production. 22 sub-issues in the list below are closed with evidence.

Still open, and why:

The chatroom Cypress suite passes except attachments.cy.ts "lists the hover menu rows". That test has failed since it was added (4f2c460d1). It hovers inside the HoverMenu scroll lock, and it needs a ruling. The #427 follow-up is #471.

Status (2026-10-06)

14 issues are fixed on branch claude/youthful-lovelace-3nvsc6. The branch is not merged or deployed yet. Each issue closes when the branch merges into main.

After the merge, run the #417 runbook. Its recovery queries are in the #417 comment.

Order of work for the open issues

Highest risk first. Inside one severity, the smaller fix goes first.

Critical

  1. Require approval before fork pull requests run workflows #440 Require approval before fork pull requests run workflows. It is one repo setting and takes minutes. Do it first.
  2. Confirm the password-token hook is on, and correct the connected-app docs #441 Confirm the password-token hook is on, and correct the connected-app docs. A dashboard check plus a docs fix.

High

  1. Make a Private document's chat readable and writable only by its owner #396 Make a Private document's chat readable and writable only by its owner. Size L.
  2. Serve editor media of a Private document only to its owner #439 Serve editor media of a Private document only to its owner. It is Low, but it follows the Make a Private document's chat readable and writable only by its owner #396 design. Needs a decision first.
  3. Stop clients from writing server-owned columns on chat messages #400 Stop clients from writing server-owned columns on chat messages. Run its read-only production pre-check first.

Medium

  1. Scope heading chat channels to their own document #402 Scope heading chat channels to their own document. Size M. Do it with Make a Private document's chat readable and writable only by its owner #396 or right after it.
  2. A pasted node or a channel row can crash a document page for every reader #436 A pasted node or a channel row can crash a document page for every reader.
  3. Clear the text of a chat message when it is deleted #435 Clear the text of a chat message when it is deleted.
  4. Stop exposing every user's online status and last-active time #434 Stop exposing every user's online status and last-active time. Ship the webapp step before the migration.
  5. Require a password on production Redis #438 Require a password on production Redis. Two deploys, staging first.
  6. Stop REST /health from leaking error text or taking every /api route down #405 Stop REST /health from leaking error text or taking every /api route down. Needs sign-off on the compose healthcheck change.
  7. Clear the public profile when an account is soft-deleted #427 Clear the public profile when an account is soft-deleted. Needs the deletion-path decision below.
  8. Check that a document id is one path segment before local media is deleted #426 Check that a document id is one path segment before local media is deleted. It affects self-hosters on local storage.
  9. Run only reviewed main-branch workflows on the production runner #437 Run only reviewed main-branch workflows on the production runner. It needs org settings and a quiet deploy window.

Low

  1. Let non-members read only chat media that a live message uses #432 Let non-members read only chat media that a live message uses. It changes the same policies as Make a Private document's chat readable and writable only by its owner #396, so do it after Make a Private document's chat readable and writable only by its owner #396.
  2. Re-check ghost status before an admin delete, and lock the last-admin guard #412 Re-check ghost status before an admin delete, and lock the last-admin guard. Needs the deletion-path decision below.
  3. Stop serving editor media after a document moves to Trash #408 Stop serving editor media after a document moves to Trash. It can ship now.
  4. Stop the public avatar buckets from accepting SVG files #431 Stop the public avatar buckets from accepting SVG files.
  5. Move the email and push gateway reports behind admin auth #433 Move the email and push gateway reports behind admin auth. Ship the server and the dashboard in one release.
  6. Keep the access token out of document page props #429 Keep the access token out of document page props.
  7. Re-check a WebSocket's identity when its access token expires #430 Re-check a WebSocket's identity when its access token expires. Needs sign-off on the reconnect cost.
  8. A forged presence payload can change the avatar on live chat messages #428 A forged presence payload can change the avatar on live chat messages.
  9. Resolve chat mentions by user id, not by username text #415 Resolve chat mentions by user id, not by username text. The review lists it as "Info only". It may close as accepted risk.
  10. Use a cryptographic random source for new document slugs #442 Use a cryptographic random source for new document slugs. Needs approval for one line in proxy.ts.
  11. Give the observability containers least privilege #443 Give the observability containers least privilege.
  12. Clear chat drafts and stale document keys on sign-out #444 Clear chat drafts and stale document keys on sign-out.
  13. Unsubscribe only when the reader confirms, not when a link scanner opens the email link #445 Unsubscribe only when the reader confirms, not when a link scanner opens the email link.
  14. Remove dead code left after the security fixes #446 Remove dead code left after the security fixes.

Optional push follow-ups after #417 are listed in #447.

Decisions the maintainer must make

Risk to the deadline

#396 is L and #402 is M. Together they may not fit in two days. If they slip, ship the Data API read gate of #396 first (anon and non-owner reads refused). That closes the High exposure. Then move the notification and media parts to a follow-up.

Done means

  • Every acceptance box in each sub-issue is checked.
  • SQL changes ship as migrations and mirror packages/supabase/scripts/. seed.sql is regenerated with bun run --filter @docs.plus/supabase_back seed.
  • apps/webapp/src/types/supabase.ts is regenerated when a function signature or a column changes.
  • The chatroom Cypress suite passes.
  • The production check from Carry the production function revokes in the Supabase migrations #397 shows no unexpected true rows after deploy.

Source

Local report: Notes/local-docs/security-review-2026-10-06.md and its raw reports. They are not in the repo; the maintainer holds a copy sent on 2026-10-06. Every issue is self-contained, so the report is not needed to execute them.


Generated by Claude Code

Activity

  1. added theissue type on Oct 6, 2026
  2. 18 remaining items

  3. changed the title [-]Tracking: security review 2026-10-06 fixes (due Friday 2026-10-09)[/-] [+][Tracking] Security review 2026-10-06 fixes[/+] on Oct 6, 2026
  4. added
    SecuritySecurity, access control, and data exposure
    on Oct 6, 2026
  5. HMarzban commented on Oct 6, 2026

    @HMarzban
    CollaboratorAuthor

    Handoff, 2026-10-06

    The session that produced this review is closing. A new session starts here.

    Branch

    • claude/youthful-lovelace-3nvsc6 is 15 commits ahead of main and 2 behind; last commit d5117e7.
    • A test merge with main shows no conflict.
    • No pull request is open yet.

    Fixed on the branch (each closes when the branch merges into main): #397, #398, #399, #401, #403, #404, #406, #407, #409, #410, #411, #413, #414, #417. Changelogs and agent docs are updated in 7560222 and d5117e7.

    Next steps, in order

    1. Maintainer settings: Require approval before fork pull requests run workflows #440 (fork approval) and Confirm the password-token hook is on, and correct the connected-app docs #441 (password hook). Both take minutes.
    2. Maintainer decisions: see "Decisions the maintainer must make" above, including the proxy.ts edits in Stop the sign-in error page from showing text from the URL #411 and Use a cryptographic random source for new document slugs #442.
    3. Merge main into the branch, open the pull request, and let CI run extension Cypress. Cloud sessions cannot download the Cypress binary, so that gate did not run locally (see AGENTS.md §Learned Workspace Facts).
    4. After the merge, deploy in this order:
      1. migrations 20261006120000, then 20261006130000;
      2. the apps;
      3. the recovery SQL in Bring back push on Chrome and Android devices the server switched off #417, soon after deploy;
      4. the staging checks in Keep server secrets out of the webapp and admin-dashboard containers #403;
      5. push on real devices;
      6. the read-only checks in Stop signed-in users from squatting a workspace slug to block chat #409 and Carry the production function revokes in the Supabase migrations #397.
    5. Continue the order of work above, starting with Make a Private document's chat readable and writable only by its owner #396 and Stop clients from writing server-owned columns on chat messages #400.

    Environment notes for a cloud session

    • Install with CYPRESS_INSTALL_BINARY=0 bun install --frozen-lockfile.
    • Run checks with LC_ALL=C.UTF-8.
    • To run Cypress there, add download.cypress.io to the environment's allowed network domains.

    Generated by Claude Code

  6. HMarzban commented on Oct 9, 2026

    @HMarzban
    CollaboratorAuthor

    Status, 2026-10-09

    Deployed to prod today (run 37906965365, deploy job finished 09:28:41Z, after a migration push of 20261006120000–20261008140000):

    Decisions taken today:

    Now: every other open sub-issue is being re-validated against today's main, then built in file-disjoint waves. Maintainer-only steps (#440, #437, and the dashboard half of #441) get their own checklist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    SecuritySecurity, access control, and data exposure

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions