fix(go): close all 17 high-divergence parity findings (H1-H17) - #235
Merged
Merged
Conversation
…rsisted on session close (H16) (#210)
…n) in VerifyAssertion — H14 (#210)
…idle-timeout; input now bumps lastActivityAt (H6, #210)
…d replay gets uniform 404 (H4), session close destroys the AgentSession so owned terminals close with agent-disconnect (H5); capture clientInfo on initialized (M4) (#210)
…eted), drop MCP transports (DropAccount, M17), revoke Hydra login+consent sessions; 90-day cleanup routes through the same path (H1, #210)
…on, deny falls through as skip) and file keys are admin-only + DB-enabled + on-disk for session auth and forwarding; file keys offered before passkeys (H2/H3, M12) (#210)
…icated surfaces w/ fastify-shaped 429s (H9), CORS reflect+preflight before the bearer gate (H10), server.trustProxy client-IP resolution consumed by allowlist/audit/limits (H11), disable go-sdk localhost DNS-rebinding check on /mcp for reverse-proxy parity (H12) (#210)
…subscription pruning (H7) + push-endpoint SSRF allowlist wired as AllowedEndpoint, routes mounted only with VAPID configured (H8, L17) (#210)
…mping goose's version table (with half-migrated guard), stamp drizzle's journal on Go-created DBs so a Node rollback applies nothing (H15) (#210)
… on both signer paths (closes M11), MCP teardown armed on first message not notifications/initialized, stale Mcp-Session-Id gets uniform JSON-RPC 404, rate-limit the webauthn register routes + bound limiter buckets (5000, throttled prune), single file-key selection per connect, injectable MCP session-id generator, drop dead clientIP indirections (#210)
… 30m, 0=off) — vanished clients unwind fully via the Wait teardown incl. their SSH sessions; maxOwned cap prunes janitor-closed session ids (Node starves); owners entry registered only for bound sessions (#210)
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.
Implements every high-severity item from the Node → Go parity audit of 2026-07-04 (
data/go-parity-findings-2026-07-04.md, audited at 8cce700). H17 (key-directory watcher) had already landed in a1fa3ac; the remaining 16 land here as one commit per logical group. Also closes M2 (via a1fa3ac), M4, M12, M17, and the L17 mount-only-with-VAPID half along the way.Security
approvedFileKeySigner→ key-approve pending action; deny falls through as a skip signature), and file-key selection matches Node exactly — admin-only +ssh_keys.enabled+ present on disk — for both session auth and the forwarding agent. File keys are offered before passkeys (M12).ServerOptions.GetSessionIDand ownership-registered before the client ever sees them; a foreign account replaying anMcp-Session-Idgets Node's uniform JSON-RPC 404.VerifyAssertionenforces strict counter increase (go-webauthn'sVerifynever sees the stored counter); covers login, consent, and step-up.rest.Push.AllowedEndpoint.internal/ratelimit, enforcing the exact per-route limits Node configures (self-register, invites, hydra login/consent/logout options+verify, step-up, DCR 10/15min) with fastify-shaped 429s andX-RateLimit-*/Retry-Afterheaders.internal/realip(Fastify/proxy-addr semantics: bool / hop count / CIDRs / presets); one context value consumed by the /mcp IP allowlist, auditsourceIp, agent-proxy, and the rate limiter — the allowlist no longer fails open behind a reverse proxy.Lifecycle
account-deleted), drops the account's MCP transports (mcp.Deps.DropAccount, closes M17), revokes Hydra login+consent sessions; both the admin DELETE route and the cleanup job go through the same path.InitializedHandler+ServerSession.Wait()destroy the AgentSession when the MCP session closes, so owned terminals close withagent-disconnect. ClientInfo is captured in the same hook (M4).idle-timeout;SendInputnow bumpslastActivityAt(Node parity — input counts as activity).Wire / infra parity
@fastify/cors {origin: true}).all=truewhen unscoped (Hydra v2 400s otherwise) — un-breaksPOST /api/auth/sessions/revoke-alland passkey-revoke session invalidation.duration_mspersisted on session close (StatusEventnow carriesCreatedAtsince terminal transitions remove the session from the registry before subscribers run).internal/pushchannel (webpush-go + VAPID) on the approval broker, Node-shaped payloads, 404/410 subscription pruning; push routes now mount only when VAPID is configured (L17).store.Migrateadopts a Node/drizzle DB by stamping goose's version table (guarding against half-migrated schemas with an actionable error) and stamps drizzle's journal on Go-created DBs so a Node rollback applies nothing.Tests
New coverage: MCP hijack + disconnect integration tests, file-key selection/approve/deny unit tests, realip table tests, rate-limit + CORS middleware tests, push payload/prune + validator tests, and migration-adoption tests that replay the real drizzle migrations.
go build ./... && go vet ./... && go test ./...clean;pnpm spdx:checkclean.New dependency:
github.com/SherClockHolmes/webpush-gov1.4.0 (H7).Refs #210
Post-review lifecycle hardening (4c5fa9e)
Two deliberate divergences from Node, both fixing inherited flaws:
mcp.sessionTimeoutMinutes(default 30,0disables; Go-only section, Node's non-strict zod schema ignores it). Sessions with no in-flight HTTP activity for that long are closed by the go-sdk timer and unwind through the existingWait()teardown: AgentSession destroyed, owned SSH sessions closed withagent-disconnect,Mcp-Session-Idinvalidated (uniform 404; spec-conforming clients re-initialize). Node keeps transports until DELETE — an unbounded leak and a forever-valid session credential. A live client holding its SSE stream never expires (the timer only runs while no request is in flight).Also: ownership-registry entries are now created only for sessions that actually bound (registration moved fully into the first-message hook), so a failed
server.Connectcan't strand an entry.