Skip to content

[SYSADMIN ACTION][Bugfix:System] Filter websocket cookies - #13393

Merged
bmcutler merged 3 commits into
Submitty:mainfrom
RichardJSun:websocket-proxy-cookie-filter
Oct 7, 2026
Merged

bmcutler merged 3 commits into
Submitty:mainfrom
RichardJSun:websocket-proxy-cookie-filter

Conversation

@RichardJSun

@RichardJSun RichardJSun commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Why is this Change Important & Necessary?

Related to #13391

Ratchet, the library the websocket server uses, rejects handshakes over 4096 bytes with a 413. This PR doesn't add that limit. The browser sends every cookie for the host with the handshake, including cookies that other RPI sites set on .rpi.edu. Submitty cannot remove those cookies. The socket server reads only submitty_websocket_token.

What is the New Behavior?

nginx forwards only submitty_websocket_token to the socket server, so other cookies cannot make the handshake too large. The PHP websocket client is unaffected, because the server identifies it by headers, not cookies.

Existing servers keep their own nginx config, so a sysadmin adds the line by hand. See https://submitty.org/sysadmin/installation/version_notes/v26.10.01 (Submitty/submitty.github.io#797).

Screenshots and video

On submitty.cs.rpi.edu, the cookie list includes _snow_id.1d43 from another site on .rpi.edu:

Office hours queue with the websocket warning; the cookie list includes _snow_id.1d43 on .rpi.edu

On a local dev server with the 3.4 KB cookie from step 2, the main nginx config fails with 413, and a live queue announcement does not arrive:

13393-before.mp4
main nginx: HTTP 413 and no live update

With this config, the websocket connects and the announcement arrives live:

13393-after.mp4
This nginx config: HTTP 101 and the announcement arrived live

What steps should a reviewer take to reproduce or test the bug or new feature?

  1. Add the line from .setup/nginx/submitty.conf to the location /ws block of /etc/nginx/sites-available/submitty.conf, then run sudo systemctl reload nginx.
  2. Run Cookies.set('big_test_cookie', 'x'.repeat(3500)) in the browser console.
  3. Open the office hours queue. It connects. On main it fails with 413.
  4. From a second browser, post a queue announcement as the instructor. It appears without a reload.

Automated Testing & Documentation

No new tests.

Other information

SYSADMIN ACTION: see https://submitty.org/sysadmin/installation/version_notes/v26.10.01.

This doesn't help once the whole Cookie header passes 8 KB. nginx and Apache both reject such requests with a 400 by default, so every page fails at that point, not just the websocket.

Forward only submitty_websocket_token to the socket server. Ratchet
rejects handshakes over 4096 bytes with a 413, and the browser sends
every cookie for the host, so unrelated cookies could block websocket
connections.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RichardJSun
RichardJSun force-pushed the websocket-proxy-cookie-filter branch from 4933a85 to 05ff77a Compare September 30, 2026 16:29
@RichardJSun
RichardJSun marked this pull request as ready for review October 1, 2026 02:28
@RichardJSun RichardJSun changed the title [Bugfix:System] Filter websocket cookies [SYSADMIN ACTION][Bugfix:System] Filter websocket cookies Oct 1, 2026
@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 22.87%. Comparing base (0b0f225) to head (c35478e).
⚠️ Report is 16 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main   #13393      +/-   ##
============================================
- Coverage     22.88%   22.87%   -0.01%     
- Complexity    10299    10306       +7     
============================================
  Files           307      307              
  Lines         39868    39878      +10     
  Branches        672      672              
============================================
  Hits           9123     9123              
- Misses        30176    30186      +10     
  Partials        569      569              
Flag Coverage Δ
autograder 21.16% <ø> (ø)
js 0.30% <ø> (ø)
migrator 100.00% <ø> (ø)
php 20.95% <ø> (-0.01%) ⬇️
python_submitty_utils 79.83% <ø> (ø)
submitty_daemon_jobs 56.25% <ø> (ø)
vue 96.31% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bmcutler

bmcutler commented Oct 1, 2026

Copy link
Copy Markdown
Member

@RichardJSun
Please include screenshots and video in the original message to demonstrate that you actually tested this PR before asking for a review.

@RichardJSun

Copy link
Copy Markdown
Member Author

Professor, I uploaded screenshots from submitty.cs.rpi.edu to #13391 and added them to the description above. They show that the websocket failure comes from cookie size, because removing the 2.78 KB cm_folder_data cookie restores the connection. #13392 removes that cookie.

This PR handles cookies Submitty doesn't control, such as _snow_id.1d43, which another RPI site sets on .rpi.edu and the browser sends with every websocket handshake. I've added recordings from my local dev environment that show a large cookie causing a 413 before the nginx change and the websocket connecting after it.

…figs

install_system.sh copies the nginx config only when none exists, so
existing servers never get the cookie filter. A system migration adds
the line to the /ws block of the installed config, and the install's
nginx restart applies it. Also say in the config comment that the
4096-byte limit comes from Ratchet, not from this config.

Assisted-By: Claude Opus 5.5

@bmcutler bmcutler left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert/remove the migration. We leave editing of these configuration files to the system administrator because they often have other customizations. This is why the PR is labeled SYSADMIN ACTION.

Thank you for updating the comment.

Sysadmins edit their installed nginx config by hand, because it often
has local customizations. The version notes describe the change.

Assisted-By: Claude Opus 5.5
@RichardJSun

Copy link
Copy Markdown
Member Author

Removed the migration. The steps are now in the v26.10.01 version notes (Submitty/submitty.github.io#797).

bmcutler added a commit to Submitty/submitty.github.io that referenced this pull request Oct 7, 2026
Update NGINX config file to limit size of cookies
See also Submitty/Submitty#13393

Co-authored-by: Barb Cutler <Barb Cutler>
@bmcutler
bmcutler marked this pull request as ready for review October 7, 2026 04:38
@bmcutler
bmcutler merged commit 63a5ba1 into Submitty:main Oct 7, 2026
30 checks passed
bmcutler added a commit that referenced this pull request Oct 7, 2026
### Why is this Change Important & Necessary?
Fixes #13391
Partially addresses #10376

The websocket server rejects handshakes over 4 KB. Three cookies that
only JavaScript reads (`cm_folder_data`, `open_files` and
`<gradeable_id>_history`) grow with use. They can push the handshake
past the limit, and then every websocket feature breaks for that user.

### What is the New Behavior?
The three values move to web storage, so the browser no longer sends
them to the server. `cm_folder_data` and `open_files` go to
`localStorage`. Checkpoint history goes to `sessionStorage`, because it
holds student scores and logout does not clear `localStorage`.

Course Materials deletes the old cookie on load, so affected users
recover on their next visit. Folder state in the old cookie is not
carried over.

#### Screenshots and video
On submitty.cs.rpi.edu, the office hours queue fails with a 2.78 KB
`cm_folder_data` cookie and works after I delete only that cookie:

<img width="1452" height="475" alt="Office hours queue with the
websocket warning and a 2.78 KB cm_folder_data cookie"
src="https://github.com/user-attachments/assets/6d26fcbb-9729-43bd-84a7-d179ab8104e3"
/>
<img width="1470" height="486" alt="Office hours queue without the
warning after deleting cm_folder_data"
src="https://github.com/user-attachments/assets/c5a1d5ae-f9ab-45c9-98d2-5558d95f097f"
/>

On a local dev server with the large cookie from step 1, `main` fails
with 413:


https://github.com/user-attachments/assets/95cc0ba0-a483-4c9e-bef7-e3cb5c79923c

<img width="800" alt="main: websocket banner and HTTP 413 with the
oversized cookie"
src="https://github.com/user-attachments/assets/fb098576-7978-484c-bf03-a981f099183c"
/>

On this branch, Course Materials deletes the cookie and the websocket
connects:


https://github.com/user-attachments/assets/bde8224f-8a21-4718-b722-af02012e43fa

<img width="800" alt="This branch: cookie deleted and HTTP 101"
src="https://github.com/user-attachments/assets/fe449561-2de1-42c1-a797-a112764affe4"
/>

TA grading state on this branch:

<img width="800" alt="open_files in localStorage and the file reopened
for another student"
src="https://github.com/user-attachments/assets/0b495375-312d-469e-b1dc-9e7383002c65"
/>

<img width="800" alt="grading_lab_history in sessionStorage"
src="https://github.com/user-attachments/assets/95b66cec-5370-42f6-9fee-7f1cae5b7167"
/>

### What steps should a reviewer take to reproduce or test the bug or
new feature?
1. On `main`, open Course Materials and run this in the console to make
a large cookie: `Cookies.set('cm_folder_data',
JSON.stringify(Object.fromEntries([...Array(24).keys()].map(i =>
['/var/local/submitty/courses/f26/sample/uploads/course_materials/folder'
+ i, { id: 'f' + i, open: true }]))))`
2. Open the office hours queue. The websocket fails with 413.
3. Switch to this branch and reload Course Materials. The cookie is
gone.
4. Open the office hours queue again. It connects.
5. Open and close some folders, then reload. They keep their state in
Local Storage.
6. In TA grading with Auto open checked, open some files and go to the
next student. The files reopen, and `open_files` is in Local Storage.
7. Change some checkpoint scores. `<gradeable_id>_history` is in Session
Storage.

### Automated Testing & Documentation
No new tests. The existing Cypress tests do not use these cookies.

### Other information
#13393 handles the cookies that other sites set.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Barb Cutler <bmcutler@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants