Repository navigation
fix(server): let a web terminal token open only one terminal - #7370
Conversation
|
Claude encountered an error after 0s —— View job I'll analyze this and get back to you. |
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
|
Claude finished @geovannewashington's task in 4m 26s —— View job Code Review CompleteReviewed 5 files and posted 2 inline issues: no Standards violations, 1 Standards judgement call, and 1 Correctness issue. The Spec axis did not run. StandardsThere are no hard violations. The one judgement call is on Correctness
SpecThe Spec axis did not run. The PR's only issue reference is Additional notes
If you've addressed the feedback and want a new review, tag |
5344a28 to
4df6cab
Compare
|
/review |
|
Claude finished @gustavosbarreto's task in 3m 28s —— View job Code Review CompleteReviewed 6 files and posted 1 inline issue. Standards: no violations and 1 judgement call. Spec: did not run. Correctness: no findings. StandardsThe one judgement call is in SpecThe Spec axis did not run. The only issue reference is Additional notes
If you've addressed the feedback and want a new review, tag |
4df6cab to
83a8739
Compare
|
/review |
|
Claude finished @gustavosbarreto's task in 4m 5s —— View job Code Review CompleteReviewed 6 files and posted 2 inline issues. Standards: no violations and 1 judgement call. Spec: did not run. Correctness: 1 finding. StandardsThe one judgement call is at CorrectnessMoving SpecThe Spec axis did not run. The only issue reference is Additional notes
If you've addressed the feedback and want a new review, tag |
The token store read a token without removing it, so the token kept opening terminals until its 30 second timer deleted it. Reading and deleting in one atomic step leaves exactly one caller with it. TestManagerSave now waits past the TTL and reads once. Polling with get spent the token on the first poll, so the test passed even if tokens never expired.
The bridge wrote its error frame straight to the WebSocket while the keep-alive goroutine pinged through Conn's lock, so the two frames could interleave. Writing the error through Conn serializes them.
The bridge ignored the decrypt error and logged in with whatever the password field held. Key and identity logins carry no password, so encrypting or decrypting an empty password now does nothing. The ErrCreditialsNoPassword sentinel existed only for callers to filter out, so it is gone. The session route also ignored the encrypt error, so a password over the 190 bytes RSA-OAEP fits was parked as plaintext and then failed to decrypt. The route now refuses it with 400, and answers any other encrypt failure, a server fault, with 500. Fixes: shellhub-io/team#292
…rypt The log could not tell a hex decode failure from an RSA one, because the bridge reported only its own sentinel.
The first redemption only asserted it was not "credentials not found", which any other error also satisfied. Expecting ErrGetAuth proves the bridge reached authentication. The not-found test now shares the dial helper. The second GetDevice stub is not for the second dial. getAuth looks the device up again for a key login, and that miss is what the first dial reports as ErrGetAuth. The second dial never reaches GetDevice.
83a8739 to
e889541
Compare
|
/review |
Code Review CompleteThe automated review ran but did not post an updated summary — this usually means no new issues were found since the previous review. If you've pushed changes and want a fresh pass, comment |
Summary
A web terminal token now opens one terminal. Before, the bridge read the token without removing it, so it kept working until its 30 second timer deleted it.
Key and identity logins carry no password. Encrypting or decrypting an empty password now does nothing, so they keep working. The
ErrCreditialsNoPasswordsentinel only existed for callers to filter out, so it is gone.The encrypt check was not in the spec. The magic key is RSA-2048 with OAEP and SHA-256, which fits at most 190 bytes. A longer password was parked as plaintext, and the new decrypt check would end the bridge on it. Refusing it when the token is created gives the browser a clear error up front.
The bridge test exposed a race that was already there.
exitwrote the error frame straight to the WebSocket, whileKeepAlivepinged throughConn's lock, so the two frames could interleave.exitnow writes throughConn. That fix has its own commit.TestManagerSavepolled withget, which now spends the token on the first poll, so it passed even if tokens never expired. It now waits past the TTL and reads once.Closes shellhub-io/team#292
Evidence
TestManagerGetRedeemsTheTokenOncefails because the second get still finds the token.TestManagerConcurrentGetsYieldOneWinnergets 50 winners out of 50.TestNewSSHServerBridge_TokenOpensOneBridgelets the second dial reachGetDeviceagain, and-racereports theexit/KeepAliverace.TestWebSessionRouteRefusesAPasswordItCannotEncryptgets 200 for a 191-byte password.After: all four pass.
go test -race ./ssh/web/passes on every commit, and the fullserversuite passes exceptstore/pg, a testcontainers suite that cannot run underexec server. This branch does not touch it. With the TTL delete removed fromsave,TestManagerSavenow fails.Merge Danger
Door: two-way
There is no wire format or database change. Reverting restores the old behaviour.
Blast Radius: web-terminal
A browser that reused a token now gets "failed to find the credentials". The console mints a new token on every connect, including reconnects, so normal use does not change.
A device password over 190 bytes logged in on
master, by accident, through the plaintext fallback.POST /ws/ssh/sessionnow refuses it with 400. Those users cannot open a web terminal with password login until the password is shorter or the encryption changes.