Reapply footer control ordering on current upstream/master while preserving the required situational chip renderer. Persist composer_control_order with backend validation, keep the settings descriptions reorder-aware, and make primary/situational chip renderers participate in same-group drag ordering.
Verified with: node --check static/boot.js; node --check static/panels.js; git diff --check; ./scripts/test.sh tests/test_issue4598_composer_control_visibility.py
The embedded workspace terminal spawns a PTY shell that runs arbitrary
commands as the server-process user. check_auth() returns True
unconditionally when no password/passkey is configured (the default
out-of-the-box state), so without a network-scope gate the terminal
endpoints were reachable by any unauthenticated caller able to hit the
port — which on a passwordless public bind is remote code execution.
Apply the same local-origin gate the onboarding/bootstrap endpoints use
(_onboarding_gate_allows) to /api/terminal/{start,input,resize,close} and
/api/terminal/output: with auth disabled, accept only loopback/private
origins, ignore spoofable X-Forwarded-For/X-Real-IP unless
HERMES_WEBUI_TRUST_FORWARDED_FOR=1, and honor HERMES_WEBUI_ONBOARDING_OPEN=1
as the explicit opt-out for a deliberately-exposed server. Auth-enabled
servers (cookie already verified upstream) and genuine same-host clients
are unaffected.
Also fixes a latent test-isolation leak in
test_extension_route_remains_behind_webui_auth: it set HERMES_WEBUI_PASSWORD
but never invalidated the process-wide password-hash cache, so its result
depended on suite execution order (exposed when the new test file shifted
ordering). Invalidate before+after so it reads the env var deterministically.
12 new gate tests in tests/test_cvd3_terminal_local_origin_gate.py.
A public TTS host could 301/302/303-redirect POST /audio/speech to an internal
target (e.g. http://169.254.169.254), and urllib's default redirect handler
would follow it carrying the Authorization bearer — both an SSRF bounce past the
base-url guard and a credential leak. Now uses a no-redirect opener
(_NoRedirectTtsHandler raises on any redirect) via the _tts_open seam. +redirect
rejection regression test. Residual DNS-rebinding TOCTOU (re-resolve at connect)
is a narrower low-severity window noted for follow-up.
The base_url validator accepted any https host; an https URL pointing at an
internal/link-local/loopback/reserved IP (e.g. https://169.254.169.254 cloud
metadata, https://10.x internal) passed the scheme-only check. Now resolves the
host and rejects blocked-target addresses (private/loopback/link-local/reserved/
multicast/unspecified), while still allowing public OpenAI-compatible hosts and
the explicit localhost-over-http dev case. DNS-resolution failure is allowed
(unreachable host can't be an SSRF vector + avoids false-rejecting public hosts
that don't resolve in sandboxed envs). +5 regression vectors.
Add a default-OFF 'project_quick_create_buttons' setting that gates the
per-project '+' quick-create buttons on sidebar project chips. The chip
render site only calls _attachProjectQuickCreateButton when the new
window._projectQuickCreate runtime flag is enabled, so the buttons do
not appear unless the user opts in via Settings.
Mirrors the large_text_paste_as_attachment opt-in plumbing (defaults,
allowlist, settings collect/restore/save, window hydration at both sites,
checkbox row, i18n across all locales).
A visible tab can permanently miss a server-initiated turn (self-wake /
cron / restart hook) when its per-session SSE is momentarily down at the
emit instant. server_turn_started is a fire-and-forget broadcast with no
replay buffer; if the turn also finishes before the tab reconnects, the
on-subscribe self-heal finds no live run in ACTIVE_RUNS and replays
nothing, so the transcript stays stale until a hard refresh.
Extend the self-heal: the (re)subscribing tab reports its last-known
message_count (?known_count); when there is no live run to replay, the
handler compares the persisted count and, if the server is ahead, emits a
lightweight session-updated frame. The frontend syncs incrementally via
the #5189 keepStaleUntilLoaded swap-in-place loadSession path (no
clear+refetch, so the #5177/#5189 blank-gap jump is not reintroduced).
Idle-only and pane-scoped; emits only when the server is strictly ahead.
Extract _turn_transcript_lacks_final_assistant_answer() so settlement
logic can inspect an already-merged transcript. The merge wrapper now
delegates to the pure evaluator without changing behavior.
Fixes#5141
Gate hardening for #5198: the new stream-owner map leaked entries on three
paths that bypass the teardown finally — worker early-return when the stream
was cancelled before startup (streaming.py, gateway_chat.py) and the direct
ACTIVE_RUNS stale-zombie prune (routes.py). Each now calls unregister_stream_owner.
Adds a regression test proving the early-return path no longer leaks.
Co-authored-by: starship-s <starship-s@users.noreply.github.com>
Add a user setting 'large_text_paste_as_attachment' (default: on) that lets
users paste large text inline instead of having it auto-converted to .md file
attachments. The toggle lives in Control Center → Chat tab and persists via
settings.json, following the established upstream pattern for boolean settings.
Mirror the #5128 fast-path suffix repair when profile context is present
but requested_provider is omitted, which async delegation completion can
trigger via process_wakeup.
Co-authored-by: b3nw <b3nw@users.noreply.github.com>