fix(desktop): refuse a foreign backend's session token after readiness
The served-token fallback adopts whatever token the dashboard HTML injects. That is correct when our own child regenerated the token (env pin lost across a shell-wrapped spawn), but wrong when the readiness probe answered from a process we did not spawn: /api/status is public, so an orphaned dashboard squatting the port passes waitForHermes while our child dies on the bind conflict. Silently adopting that process's token would authenticate the renderer against a foreign backend, possibly on the wrong profile. Discriminate on child liveness: the desktop pins HERMES_DASHBOARD_SESSION_TOKEN on every spawn, so a live child always serves our token. Served-token mismatch + dead child = foreign backend; fail the boot loudly instead of connecting. Mismatch + live child keeps the adopt-served-token salvage from #43720.
This commit is contained in:
@@ -78,10 +78,32 @@ async function resolveServedDashboardToken(baseUrl, fallbackToken, options = {})
|
||||
return servedToken || fallbackToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether a served-token mismatch means we are talking to a backend we
|
||||
* did NOT spawn.
|
||||
*
|
||||
* The desktop pins HERMES_DASHBOARD_SESSION_TOKEN on every backend it spawns,
|
||||
* and the dashboard honors that env at import — so a LIVE child of ours always
|
||||
* serves our token. The only way the served token differs while our child is
|
||||
* dead is that the readiness probe (public /api/status) answered from a
|
||||
* different process: an orphaned dashboard or port squatter that won the bind
|
||||
* race while our child exited. Adopting that process's token would silently
|
||||
* authenticate the renderer against a foreign backend (possibly the wrong
|
||||
* profile), so callers must fail loudly instead.
|
||||
*
|
||||
* A mismatch with a live child is the benign case the served-token fallback
|
||||
* exists for: our own child served a regenerated token because the env pin
|
||||
* did not survive the spawn (e.g. shell-wrapped CLI shims).
|
||||
*/
|
||||
function isForeignBackendToken({ servedToken, spawnToken, childAlive }) {
|
||||
return Boolean(servedToken) && servedToken !== spawnToken && !childAlive
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DEFAULT_TOKEN_FETCH_TIMEOUT_MS,
|
||||
dashboardIndexUrl,
|
||||
extractInjectedDashboardToken,
|
||||
fetchPublicText,
|
||||
isForeignBackendToken,
|
||||
resolveServedDashboardToken
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user