Skip to content

fix: ensure websocket close messages are truncated to 123 bytes #779

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 1, 2022

Conversation

coadler
Copy link
Contributor

@coadler coadler commented Mar 31, 2022

It's possible for websocket close messages to be too long, which cause
them to silently fail without a proper close message. See error below:

2022-03-31 17:08:34.862 [INFO]	(stdlib)	<close_notjs.go:72>	"2022/03/31 17:08:34 websocket: failed to marshal close frame: reason string max is 123 but got \"insert provisioner daemon:Cannot encode []database.ProvisionerType into oid 19098 - []database.ProvisionerType must implement Encoder or be converted to a string\" with length 161"

@coadler coadler self-assigned this Mar 31, 2022
@codecov
Copy link

codecov bot commented Mar 31, 2022

Codecov Report

Merging #779 (c70e408) into main (50f2fca) will decrease coverage by 0.09%.
The diff coverage is 81.25%.

@@            Coverage Diff             @@
##             main     #779      +/-   ##
==========================================
- Coverage   64.18%   64.09%   -0.10%     
==========================================
  Files         199      196       -3     
  Lines       11830    11657     -173     
  Branches       87       87              
==========================================
- Hits         7593     7471     -122     
+ Misses       3419     3382      -37     
+ Partials      818      804      -14     
Flag Coverage Δ
unittest-go- 63.27% <50.00%> (+0.04%) ⬆️
unittest-go-macos-latest 59.10% <81.25%> (+0.07%) ⬆️
unittest-go-ubuntu-latest 61.97% <50.00%> (-0.04%) ⬇️
unittest-go-windows-2022 ?
unittest-js 62.63% <ø> (ø)
Impacted Files Coverage Δ
cli/ssh.go 38.88% <ø> (ø)
provisioner/terraform/serve.go 52.77% <ø> (ø)
coderd/provisionerdaemons.go 63.06% <25.00%> (+3.03%) ⬆️
coderd/httpapi/httpapi.go 72.50% <100.00%> (+4.38%) ⬆️
coderd/workspaceresources.go 57.72% <100.00%> (-3.26%) ⬇️
cli/configssh.go 50.00% <0.00%> (-8.04%) ⬇️
pty/ptytest/ptytest.go 89.65% <0.00%> (-5.18%) ⬇️
cli/projectinit.go 56.36% <0.00%> (-3.64%) ⬇️
peer/conn.go 78.17% <0.00%> (-2.54%) ⬇️
... and 7 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 50f2fca...c70e408. Read the comment docs.

@coadler coadler force-pushed the colin/ws-max-close-frame branch from ca133c8 to 0835ecc Compare March 31, 2022 18:13
@coadler coadler requested a review from kylecarbs March 31, 2022 18:19
Copy link
Member

@kylecarbs kylecarbs left a comment

Choose a reason for hiding this comment

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

Few minor nits. Good find Mr. Adler!

coderd/ws.go Outdated
Comment on lines 28 to 39
builder := strings.Builder{}
builder.Grow(byteLen)

for _, char := range str {
if builder.Len()+len(string(char)) > byteLen {
break
}

_, _ = builder.WriteRune(char)
}

return builder.String()
Copy link
Member

Choose a reason for hiding this comment

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

Could we do []byte(str)[:websocketCloseMaxLen] instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can't safely slice strings, because characters can be more than 1 byte. This could cause us to slice in the middle of a single character.

Copy link
Member

Choose a reason for hiding this comment

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

Could we do []rune(str)[:websocketCloseMaxLen] in that case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would limit the rune count instead of the byte count

Copy link
Member

Choose a reason for hiding this comment

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

I left a brainded comment

Copy link
Member

Choose a reason for hiding this comment

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

You are correct sir

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i made it simpler tho and got rid of the string builder

coadler added 2 commits March 31, 2022 13:44
It's possible for websocket close messages to be too long, which cause
them to silently fail without a proper close message. See error below:

```
2022-03-31 17:08:34.862 [INFO]	(stdlib)	<close_notjs.go:72>	"2022/03/31 17:08:34 websocket: failed to marshal close frame: reason string max is 123 but got \"insert provisioner daemon:Cannot encode []database.ProvisionerType into oid 19098 - []database.ProvisionerType must implement Encoder or be converted to a string\" with length 161"
```
@coadler coadler force-pushed the colin/ws-max-close-frame branch from 51f2225 to c3bf516 Compare March 31, 2022 18:44
@coadler coadler requested a review from kylecarbs March 31, 2022 18:50
Copy link
Member

@kylecarbs kylecarbs left a comment

Choose a reason for hiding this comment

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

Approving for now, but I'd rather this doesn't become a top-level export in coderd. Not sure where to put it though :( I can noodle!

coderd/coderd.go Outdated

// fmtWebsocketCloseMsg formats a websocket close message and ensures it is
// truncated to the maximum allowed length.
func FmtWebsocketCloseMsg(format string, vars ...any) string {
Copy link
Member

Choose a reason for hiding this comment

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

It's unfortunate we'll have to export this to test it - hmph. Any ideas on where we could place this structurally? It's both a little unfortunate to export this from coderd, and to create a package just for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we could do the coderd_internal_test.go thing? I read about that after I wrote this!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But yea I agree having this exported is weird

Copy link
Member

Choose a reason for hiding this comment

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

I think httpapi would be more appropriate.

httpapi.WebsocketCloseMessage seems clean 😎

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like that! I'll fix it up tomorrow

Copy link
Member

Choose a reason for hiding this comment

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

Woooonderful

Copy link
Member

@kylecarbs kylecarbs left a comment

Choose a reason for hiding this comment

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

Minor nit: we could do WebsocketCloseSprintf instead, but chefs choice!

@coadler coadler enabled auto-merge (squash) April 1, 2022 18:12
@coadler coadler merged commit dc46ff4 into main Apr 1, 2022
@coadler coadler deleted the colin/ws-max-close-frame branch April 1, 2022 18:17
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.

2 participants