Fix shell-in behind host-rewriting proxy + tab-overflow alignment#7
Merged
Merged
Conversation
Shell-in (regression from the term_ws Origin check): cloudflared rewrites the Host header (`httpHostHeader: 127.0.0.1`), so the browser's Origin (code.falkinator.org) never matched the Host the app saw → the WebSocket was rejected and the terminal showed "disconnected". - blocked_origin() now also accepts X-Forwarded-Host, and FAILS OPEN when it genuinely can't determine the public origin (Host is loopback AND no DASHBOARD_ALLOWED_ORIGINS set) instead of breaking the terminal. Setting DASHBOARD_ALLOWED_ORIGINS to the public hostname restores strict cross-site-WS-hijacking protection. Tab alignment: with the flex-wrap bar the tabs are narrower, and the work-title brief (`.session-tab-summary` with overflow:visible + a max-content marquee track) spilled out and overlapped neighbouring tabs. Contain it: overflow:hidden + a 2-line clamp on the static (desktop) variant (display:contents on the inner track), and reduce the per-tab word count so the brief reads cleanly. The full title still shows on the card and in the tab tooltip. Verified: WS handshake with rewritten Host=127.0.0.1 + Origin=code.falkinator.org (allowlisted) -> 101; Origin=evil.com -> 403. Live headless screenshot confirms the tabs fill the bar without overlap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Loosens the WebSocket Origin check to support reverse proxies that rewrite the Host header (e.g. cloudflared with httpHostHeader: 127.0.0.1), and tightens shell tab summary rendering so briefs stay within their tab.
Changes:
- Accept
X-Forwarded-Hostas a valid Origin match and fail-open when Host is loopback and no allowlist is configured. - Clamp tab summary to 2 lines via CSS and reduce per-tab word counts.
- Update
.env.exampledocumentation forDASHBOARD_ALLOWED_ORIGINS.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/term.rs | Adds X-Forwarded-Host matching, loopback detection, and fail-open behavior when public origin is undeterminable. |
| public/render.js | Reduces word counts in tab summary breakpoints. |
| frontend/render.ts | Mirrors render.js changes in TypeScript source. |
| public/app.css | Clamps static tab summary to 2 lines via line-clamp. |
| .env.example | Documents proxy/loopback behavior for DASHBOARD_ALLOWED_ORIGINS. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+117
to
+123
| let host_is_public = !host.is_empty() && !is_loopback_host(&host); | ||
| let has_basis = host_is_public || !forwarded_host.is_empty() || !config.allowed_origins.is_empty(); | ||
| if has_basis { | ||
| Some("Cross-origin WebSocket blocked".to_string()) | ||
| } else { | ||
| None | ||
| } |
Comment on lines
+126
to
132
| fn is_loopback_host(host: &str) -> bool { | ||
| let bare = host | ||
| .strip_prefix('[') | ||
| .and_then(|h| h.split(']').next()) | ||
| .unwrap_or_else(|| host.split(':').next().unwrap_or(host)); | ||
| bare.eq_ignore_ascii_case("localhost") || bare == "::1" || bare.starts_with("127.") | ||
| } |
Comment on lines
+99
to
103
| for candidate in [&host, &forwarded_host] { | ||
| if !candidate.is_empty() && origin_host.eq_ignore_ascii_case(candidate) { | ||
| return None; | ||
| } | ||
| } |
| .session-tab-summary .marquee-track{display:inline-flex;gap:18px;min-width:max-content} | ||
| /* Static (desktop) tabs: clamp the brief to 2 lines INSIDE the tab so it can't spill into | ||
| neighbours when tabs are narrow. The marquee variant (mobile) keeps its max-content track. */ | ||
| .session-tab-summary:not(.moving){display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Shell-in regression: the new
term_wsOrigin check broke the browser terminal behind Cloudflare, because cloudflared rewrites the Host header (httpHostHeader: 127.0.0.1) so the browser Origin never matched.blocked_origin()now also checksX-Forwarded-Hostand fails open when the public origin is undeterminable (loopback Host + noDASHBOARD_ALLOWED_ORIGINS); setDASHBOARD_ALLOWED_ORIGINSto enforce strictly.Tab overflow: the flex-wrap tabs are narrower, and the work-title brief spilled out (overflow:visible + max-content track) and overlapped neighbours. Now contained with overflow:hidden + a 2-line clamp, plus a smaller per-tab word count.
Verified: WS handshake (rewritten Host + allowlisted Origin) → 101; evil Origin → 403. Live headless screenshot confirms tabs fill the bar without overlap.
🤖 Generated with Claude Code