Skip to content

Latest commit

 

History

History
272 lines (253 loc) · 65.8 KB

File metadata and controls

272 lines (253 loc) · 65.8 KB

AgentHub Security Risk Register

Last reviewed: 2026-06-04 (merged findings from root docs/security-risk-register.md reviewed 2026-06-03; AH-SR-028 through AH-SR-044 added)

This register tracks security, privacy, reliability, network, disk, and logic risks for AgentHub. It is a living queue for audit loops; update status and evidence when a finding is fixed or intentionally accepted.

Scope

  • Hub Server: hub-server/
  • Edge Server: edge-server/
  • Desktop (Tauri): app/desktop/ — system browser PKCE, Edge lifecycle, OS secure store, tray
  • Web (Hub-only): app/web/ — Hub boundary, sessionStorage token, WS auth
  • Mobile: app/mobile/ — native token layer, OIDC deep-link callback
  • Shared client: app/shared/
  • API and deployment docs: api/, docs/, hub-server/deployments/
  • CI/governance: scripts/, .github/workflows/
  • Cross-repo identity docs: ../docs/identity/identity-auth.md, ../docs/identity/authorization-model.md, ../docs/identity/relying-party.md

P0 / High

ID Severity Status Risk Evidence Next action
AH-SR-001 High Mitigated in repo; config/deploy verification required TokenDance ID bearer validation previously did not visibly enforce iss and aud before accepting RS256 tokens. Hub now requires configured issuer and AgentHub client audience for the TokenDance bearer compatibility path. hub-server/internal/jwtutil/tokendance.go:135, hub-server/internal/jwtutil/tokendance.go:181, hub-server/internal/middleware/auth.go:30, hub-server/internal/jwtutil/tokendance_test.go:16, hub-server/internal/middleware/auth_test.go:89 Ensure AGENTHUB_TOKENDANCE_ID_CLIENT_ID is configured for every environment that enables TokenDance bearer compatibility; then verify wrong-audience tokens are rejected in deployment.
AH-SR-002 High Mitigated in repo; deploy/client verification required TokenDance ID bearer auth previously mapped accepted users to hardcoded device_type=desktop, so normal OIDC identity tokens could satisfy desktop-only Edge route checks. The compatibility path now tags accepted users as device_type=tokendance_bearer, and Hub product APIs now require auth_source=hub_local after authentication, so TokenDance bearer tokens cannot authorize /client/*, /web/*, /edge/*, device routing, or Web task dispatch directly. The Hub OIDC authorize/callback path issues Hub-local sessions with explicit UUID device proof. hub-server/internal/middleware/auth.go:31, hub-server/internal/middleware/auth.go:100, hub-server/internal/router/router.go:61, hub-server/internal/router/router.go:75, hub-server/internal/router/router.go:141, hub-server/internal/router/router.go:153, hub-server/internal/middleware/auth_test.go:181, hub-server/internal/middleware/auth_test.go:513, hub-server/internal/service/oidc.go, hub-server/internal/service/oidc_test.go, hub-server/internal/handler/oidc_test.go Verify deployed Hub OIDC config and Desktop/Web clients using Hub-issued desktop/web sessions; keep TokenDance bearer auth as identity-only compatibility and do not use it as a Hub product session.
AH-SR-003 High Mitigated in repo; deploy verification required Login previously wrote the device row, access-token device_id, and refresh-token device_id with divergent values, so logout could miss the refresh token. Login now uses the same stable UUID device ID across all three surfaces; Hub allows the same user to own multiple same-type devices, while rejecting a device_id already owned by another user or device type as BAD_REQUEST. hub-server/internal/service/auth.go:89, hub-server/internal/service/auth.go:92, hub-server/internal/repository/device.go:12, hub-server/migrations/0021_devices_allow_multiple_same_type.up.sql:1, hub-server/tests/multi_device_auth_test.go:8, hub-server/tests/multi_device_auth_test.go:36, hub-server/internal/service/device_test.go:13, app/desktop/src/api/deviceId.ts:1, app/desktop/src/api/hubAuth.ts:171 Verify the deployed Hub/Desktop login, logout, refresh, and multi-device registration cycle; decide whether pre-fix refresh tokens should be revoked during rollout.
AH-SR-004 High Mitigated in repo; remote-mode auth design still open Edge Server can start local agent CLI processes, so it must not bind beyond loopback while unauthenticated. agenthub-edge config parsing and httpserver.Run now reject wildcard, LAN, and non-loopback listen addresses; empty Origin remains allowed only for local no-browser tools on loopback. edge-server/internal/security/origin.go:36, edge-server/cmd/agenthub-edge/main.go:111, edge-server/internal/httpserver/server.go:34, edge-server/internal/security/origin_test.go:34, edge-server/cmd/agenthub-edge/main_test.go:464, edge-server/internal/httpserver/server_test.go:509 Design explicit authenticated remote Edge mode before allowing non-loopback binds; add local bearer/session proof for sensitive state-changing routes if remote control becomes a product requirement.
AH-SR-005 High Mitigated in repo; remote-mode auth/blocking approval still open Edge permission decisions previously accepted arbitrary requestId and decision. The endpoint now requires runId + requestId, consumes a one-shot pending permission registry populated from run.agent.permission_requested, rejects unknown/wrong-run/replayed decisions, and documents runId as required. edge-server/internal/api/permission_registry.go:30, edge-server/internal/api/handlers.go:40, edge-server/internal/api/handlers.go:790, edge-server/internal/events/bus.go:114, edge-server/internal/adapters/event_emitter.go:24, edge-server/internal/lifecycle/process_executor.go:516, api/openapi.yaml:831 Add authenticated/signed Desktop decision proof before non-loopback remote Edge mode, and separately design true blocking approval before claiming human-in-the-loop permission enforcement.
AH-SR-006 Medium Mitigated in repo; client/deploy verification required Hub WebSocket auth is intentionally Hub-session-only: the first frame must carry a Hub-local HS256 access token, and valid TokenDance RS256 bearer tokens are rejected instead of becoming WebSocket sessions. Hub OIDC callback now mints those Hub-local access/refresh sessions after TokenDance ID validation, preserving Hub session/device routing. hub-server/internal/handler/ws.go:93, hub-server/internal/handler/ws.go:101, hub-server/internal/handler/ws_test.go:22, hub-server/internal/handler/ws_test.go:43, hub-server/internal/service/oidc.go, hub-server/internal/service/oidc_test.go, api/events.md:7 Verify Desktop/Web login stores Hub-issued access tokens for REST/WebSocket auth and clears/reconnects them correctly; do not wire raw TokenDance bearer tokens directly into Hub WebSocket routing.
AH-SR-007 Medium Partially mitigated in repo; browser release storage model open Desktop packaged mode stores Hub access/refresh tokens through Tauri commands backed by the OS credential store, and the non-Tauri refresh-token fallback is process-memory only. Web now stores Hub access/refresh tokens and the token-source hint in sessionStorage and clears legacy localStorage token keys on load/save. Residual browser-release risk remains because same-tab XSS can still read sessionStorage; public Web should move to a BFF/HttpOnly-cookie or equivalent server-owned session model before broad release. app/desktop/src/api/hubTokenStorage.ts:1, app/desktop/src-tauri/src/secure_store.rs:1, app/web/src/api/hubTokenStorage.ts:1, app/web/src/api/hubAuth.ts:122, app/web/src/stores/hubStore.ts:1, app/web/src/api/hubTokenStorage.test.ts:1 Run packaged Desktop OS credential-store smoke and Web auth/storage smoke; design the Web BFF/HttpOnly-cookie session before public browser release.
AH-SR-020 High Mitigated in repo; deployment/client verification required Edge task callback routes now bind ack/stream/done/fail to authenticated user_id, device_id, and edge_run_id where the task has recorded them. Online WebSocket dispatch and offline pending-task replay both record the concrete routed desktop edge_device_id; callbacks from the wrong user, wrong device, or wrong run ID are rejected before state changes or agent messages are accepted. Desktop now forwards the Edge run_id on stream/done/fail callbacks, matching the Hub run binding. hub-server/internal/handler/agent.go:20, hub-server/internal/handler/agent.go:117, hub-server/internal/handler/agent.go:157, hub-server/internal/handler/agent.go:184, hub-server/internal/handler/agent.go:211, hub-server/internal/service/agent.go:252, hub-server/internal/service/agent.go:261, hub-server/internal/app/app.go:567, hub-server/internal/app/app.go:579, hub-server/internal/repository/agent.go:78, hub-server/internal/model/pending_agent_task.go:28, app/desktop/src/api/hubClient.ts:515, app/desktop/src/hooks/useHubIntegration.ts:107, hub-server/tests/edge_callback_security_test.go:19, hub-server/tests/edge_callback_security_test.go:65, api/openapi.yaml:1753 Verify the deployed Hub/Desktop pair against a real Desktop reconnect and callback chain; consider replay/idempotency hardening for repeated terminal callbacks as a separate reliability task.
AH-SR-021 High Mitigated in repo; deployment/client verification required Attachment probe remains uploader-scoped for privacy, while downloads now allow either the uploader or an active user member of a session that has an explicit message_attachments reference for the file. Sending a file message extracts UUID attachment references, rejects malformed IDs as BAD_REQUEST, rejects inaccessible or nonexistent IDs as ATTACH_NOT_FOUND, and persists the references in the same transaction as the message insert. Outsiders keep not-found semantics. Uploads still stage into a temp file and never overwrite an existing content-addressed blob on hash mismatch. hub-server/migrations/0024_message_attachments.up.sql:1, hub-server/internal/model/message_attachment.go:5, hub-server/internal/repository/message_attachment.go:9, hub-server/internal/service/message.go:111, hub-server/internal/service/message.go:164, hub-server/internal/service/message.go:211, hub-server/internal/service/attachment.go:54, hub-server/internal/service/attachment_test.go:91, hub-server/internal/service/message_test.go:243, hub-server/internal/service/message_test.go:276, hub-server/internal/service/message_test.go:295, hub-server/tests/attachment_sharing_test.go:15 Verify the deployed Hub/Desktop file-message flow and decide whether forwarded file messages should create new attachment references in the target session.
AH-SR-022 High Mitigated in repo; deployed DB constraint verified Message pinning verifies that the target message belongs to the supplied session before inserting a pin, pinned-list response construction fetches messages through a (session_id, id IN ...) query, and migration 0039_message_pins_session_fk deletes historical cross-session pin rows before replacing the message-only FK with message_pins(session_id,message_id) -> messages(session_id,id). hk2 is on migration `39 f, exposes fk_message_pins_message_session`, and has zero cross-session bad pins. hub-server/internal/service/message.go:508, hub-server/internal/service/message.go:562, hub-server/internal/repository/message.go:22, hub-server/internal/repository/message.go:99, hub-server/migrations/0039_message_pins_session_fk.up.sql:1, hub-server/tests/message_pin_security_test.go:13, hub-server/tests/message_pin_security_test.go:33, hub-server/internal/service/message_test.go:632, hub-server/internal/service/message_test.go:909
AH-SR-023 High Mitigated in repo; remote target design still open Execution Target detail and ping routes previously fetched by target UUID without checking the target owner, and Web task dispatch could only persist/echo target_id while still using the inviter desktop fallback route. Get and Ping now require current Hub user ownership, Hub targets carry workspace/trust/health policy fields, Local Edge rejects /v1/runs.workDir outside its allowlist, and Hub target-bound dispatch now resolves the target's owner desktop device_id, records edge_device_id, routes only to desktop:<device_id>, and queues offline target work by target/device without falling back to another online desktop. hub-server/internal/service/execution_target.go:55, hub-server/internal/service/execution_target.go:174, hub-server/internal/service/agent.go:319, hub-server/internal/service/agent.go:427, hub-server/internal/app/app.go:638, hub-server/internal/cache/client.go:130, hub-server/internal/ws/manager.go:126, hub-server/internal/service/agent_test.go:154, hub-server/internal/app/app_test.go:232, edge-server/internal/api/handlers.go:561, edge-server/cmd/agenthub-edge/main.go:134, edge-server/internal/api/handlers_test.go:440 Continue remote/Cloud target work with signed target health, Hub relay routing authorization, allowlist synchronization, remote approval proof, and live Web→remote/cloud smoke before claiming scenarios 3/4/5/6/8.
AH-SR-028 Critical Mitigated in repo; rotate required JWT secret was a hard-coded default in earlier Hub config. Hub Server now reads HUB_JWT_SECRET from the environment and the app.env template uses a placeholder. Any environment where the old default was used must be rotated. hub-server/internal/config/config.go, hub-server/app.env Rotate JWT secret in all deployed Hub instances; verify old secret no longer validates Hub-issued tokens.
AH-SR-029 High Mitigated in repo; deploy verification required Hub session boundary: REST and WebSocket endpoints now require a Hub-issued access token or session token. TokenDance ID bearer tokens are no longer accepted as Hub API authorization, and the WebSocket upgrade rejects bearer tokens before the Hub-local session check. hub-server/internal/middleware/auth.go, hub-server/internal/handler/ws.go, hub-server/internal/handler/client.go Deploy and smoke-test: TokenDance ID bearer token rejected on Hub REST/WS, Hub-issued session token accepted, guest/anonymous paths (health, OIDC callback) remain open.
AH-SR-030 High Mitigated and deployed CORS middleware is locked to explicit production origins. Wildcard origins and arbitrary reflection have been removed. Hub production config must use exact https:// origins. hub-server/internal/middleware/cors.go, hub-server/internal/config/config.go Keep allowed origins explicit; verify after any new client origin is added.
AH-SR-031 High Mitigated and deployed Rate-limit middleware is deployed on Hub auth/token/API routes with per-IP bucket tracking and configurable limits. Proxy header parsing trusts only loopback reverse-proxy addresses. hub-server/internal/middleware/ratelimit.go, hub-server/internal/config/config.go Keep rate-limit bucket sizes in sync with actual usage patterns; add per-route differentiation if needed.
AH-SR-032 High Mitigated in repo; deploy verification required Edge workspace allowlist restricts which local filesystem paths Edge can access. Unlisted paths are rejected before any file operation. edge-server/internal/workspace/allowlist.go, edge-server/internal/config/config.go Verify production Edge workspace config restricts to intended project roots; add regression that allowlist is never empty or /.
AH-SR-035 High Open Hub OIDC callback, JWKS validation, code exchange, and Hub session issuance have code and test coverage. 2026-06-02 production non-interactive smoke verified TokenDance ID health/discovery/JWKS, Hub health, Hub OIDC authorize URL generation, invalid callback rejection, and Desktop dev CORS with scripts/verify-oidc-flow.ps1 (32/32). This still lacks a browser-completed authorization code flow proving live callback registration, code exchange, Hub session issuance, and user-visible login completion. hub-server/internal/handler/oidc.go, hub-server/internal/service/oidc.go, hub-server/internal/service/oidc_test.go, scripts/verify-oidc-flow.ps1 Complete an end-to-end browser OIDC login against production or staging, confirm Hub session issuance and /client/auth/me, then record private evidence without copying callback codes, tokens, client secrets, or session material into this public repo.
AH-SR-036 High Open No Desktop login/logout/reconnect deployment evidence. Desktop system-browser PKCE, Hub session acquisition, WebSocket auth, logout, and reconnect recovery have code but no production or staging deployment evidence with real TokenDance ID. This is release-blocking. app/desktop/src/lib/oidc.ts, app/desktop/src/lib/auth.ts, app/desktop/src-tauri/src/oidc.rs Deploy Desktop against a live Hub with OIDC enabled, complete full login/logout/reconnect cycle, capture evidence in private server docs. Do NOT copy tokens, callback parameters, or session secrets.
AH-SR-037 High Open Web server-owned session posture not proven. Web app currently uses sessionStorage for Hub session tokens. A release-quality Web deployment should demonstrate server-owned session posture (BFF/HttpOnly cookie or accepted documented alternative) before public exposure. app/web/src/lib/auth.ts, app/web/src/stores/session.ts Implement or document the Web session posture decision; if BFF/HttpOnly, add backend proxy with cookie-based session; if documented alternative, record accepted risk with owner, date, reason, and compensating controls.
AH-SR-042 High Open Mobile labels the native token layer as secure storage, but the current Tauri command writes the Hub access token as plaintext JSON under the app data directory. Mobile OIDC deep-link callback is also explicitly incomplete, so this path must not be treated as release-ready authentication. app/mobile/src-tauri/src/secure_store.rs, app/mobile/src-tauri/src/oidc.rs, app/mobile/src/native/mobileCommands.ts Before Mobile auth release, either return a clear not-implemented state for token persistence or integrate Android Keystore / iOS Keychain and route the full flow through Hub /client/auth/oidc/*; add native storage tests or platform QA evidence.

P1 / Medium

ID Severity Status Risk Evidence Next action
AH-SR-008 Medium Mitigated in repo; remote dev opt-in remains operator-owned Dev Docker compose now binds PostgreSQL, Redis, Hub API, and Hub admin/metrics ports to 127.0.0.1 by default through AGENTHUB_BIND_HOST, so default dev credentials and admin surfaces are not published to the LAN. Operators can still explicitly set AGENTHUB_BIND_HOST=0.0.0.0 for remote debugging. docker-compose.yml:21, docker-compose.yml:36, docker-compose.yml:62, docker-compose.yml:111, docker-compose.yml:112, .env.example:8, .env.example:11, hub-server/deployments/docker-compose.prod.yml:117 If remote dev debugging is needed, require explicit firewall/operator acknowledgement; keep production compose loopback/internal-only.
AH-SR-009 Medium Mitigated in repo; MIME allowlist policy open Attachment hash shape is validated before probe/upload/download path derivation, upload MIME metadata is sniffed from staged file bytes instead of trusting multipart headers, and downloads now normalize Content-Type, set X-Content-Type-Options: nosniff, and format Content-Disposition from a sanitized basename. The remaining question is product policy: whether AgentHub should restrict uploads to an explicit MIME allowlist. hub-server/internal/handler/attachment.go:44, hub-server/internal/handler/attachment.go:69, hub-server/internal/handler/attachment.go:133, hub-server/internal/handler/attachment.go:179, hub-server/internal/handler/attachment.go:224, hub-server/internal/handler/attachment.go:242, hub-server/internal/handler/attachment.go:250, hub-server/internal/service/attachment.go:76, hub-server/internal/service/attachment_test.go:16, hub-server/internal/handler/attachment_test.go:57, hub-server/internal/handler/attachment_test.go:190, hub-server/internal/handler/attachment_test.go:239 Decide whether product requirements need a MIME allowlist or per-session file-type policy; otherwise keep byte-sniffed metadata plus forced attachment download as the default.
B9 Medium Mitigated in repo; deployed on hk2; production S3 credentials/config remain operator-owned Attachment blobs can now use S3-compatible object storage to reduce future hk2 root-disk pressure from large attachments. Incomplete S3 config fails startup validation instead of silently writing to local disk, S3 init failure fails startup instead of falling back, and content-addressed S3 writes use If-None-Match: * so an existing hash object is not overwritten. When S3 is not configured, local upload behavior is preserved. Production currently has AGENTHUB_S3_* empty, so the live Hub keeps local upload behavior until operator S3 credentials are configured. hub-server/internal/config/config.go:228, hub-server/internal/app/app.go:155, hub-server/internal/service/attachment.go:109, hub-server/internal/service/s3_client.go:46, hub-server/internal/config/config_test.go:1, hub-server/internal/service/attachment_test.go:1, hub-server/deployments/docker-compose.prod.yml:119, hub-server/deployments/.env.production.example:61 Configure real S3-compatible credentials in the operator secret store when moving attachments off hk2 local disk; then verify upload/download against that backend and monitor root-disk growth.
AH-SR-010 Medium Mitigated in repo; production Redis fail-fast retained Service-layer nil and typed-nil cache injection now resolves to a no-op/fallback cache instead of panicking. Auth/contact/session invalidation becomes no-op, contact online state defaults offline, and message/agent seq allocation falls back to the DB row lock. Production App.Run still pings Redis and fails fast before routes start. hub-server/internal/service/cache_fallback.go:11, hub-server/internal/service/auth.go:30, hub-server/internal/service/contact.go:28, hub-server/internal/service/session.go:29, hub-server/internal/service/message.go:33, hub-server/internal/service/agent.go:35, hub-server/internal/service/cache_fallback_test.go:12, hub-server/internal/service/message_test.go:242, hub-server/internal/service/agent_test.go:241 Verify deployed Hub keeps Redis health checks enabled; no live deployment change required for the service-layer fallback.
AH-SR-011 Low Mitigated in repo; public website route retained Public stats remains unauthenticated for the official website, but exact user/agent/message counts and precise uptime are no longer exposed. Counts are returned as lower-bound buckets and uptime is returned as coarse <1h/hour/day/30d+ buckets while preserving the existing response shape. hub-server/internal/handler/public.go:54, hub-server/internal/handler/public.go:66, hub-server/internal/handler/public.go:81, hub-server/internal/handler/public_test.go:21 If marketing needs exact internal stats, expose them through an authenticated admin route instead of /api/public/stats.
AH-SR-012 Low Mitigated in repo; Git history cleanup open Current-tree generated artifacts no longer ship from the repo: the Vite visualizer HTML, ad hoc Go coverage profiles, and Hub test upload blobs were removed, and ignore rules now cover those generated surfaces. Residual risk remains because prior commits may still contain the old blobs until maintainers decide whether history rewrite is worth the coordination cost. .gitignore:13, app/desktop/stats.html, edge-server/cov_full, edge-server/$covPath, hub-server/tests/uploads/ Keep generated reports/uploads out of commits; if a public release requires removing old blob history, coordinate a dedicated history-rewrite/force-push window and secret scan afterward.
AH-SR-013 Medium Local-only Local untracked .env files contain secret-looking values. They are ignored, but this remains a workstation leakage risk if zipped, pasted, or force-added. .env:34, .env:41, .env:45 from local scan; not tracked Keep .env ignored, add secret scanning to hooks/CI, and rotate any value ever exposed outside the workstation.
AH-SR-014 Medium Mitigated in repo; remote Edge auth still open Edge remains loopback-only and origin-gated by default, and can now require an optional local bearer token for every Edge API except /v1/health and CORS preflight. REST accepts Authorization: Bearer <token> or X-AgentHub-Edge-Token; browser WebSocket clients use /v1/events?access_token=<token>. Desktop propagates the token from VITE_EDGE_AUTH_TOKEN or local storage, and the local client smoke can run the tokenized REST + WebSocket path. edge-server/internal/httpserver/server.go:57, edge-server/internal/httpserver/server.go:258, edge-server/internal/httpserver/server_test.go:303, edge-server/cmd/agenthub-edge/main.go:111, app/desktop/src/api/edgeAuth.ts:1, app/desktop/src/api/edgeClient.ts:46, app/desktop/src/api/eventClient.ts:168, scripts/client-smoke.ps1:18, api/openapi.yaml:22 Keep the no-token default for local development only; design Hub session/device proof and audited signed commands before enabling any non-loopback remote Edge mode.
AH-SR-015 Medium Mitigated in repo; WebSocket remains long-lived Edge keeps WriteTimeout=0 for WebSocket compatibility, but non-WebSocket REST requests are now wrapped by a 30s timeout middleware. WebSocket upgrade requests bypass that middleware and continue to rely on the existing WebSocket read/write deadlines. edge-server/internal/httpserver/server.go:33, edge-server/internal/httpserver/server.go:55, edge-server/internal/httpserver/server.go:256, edge-server/internal/httpserver/server_test.go:255, edge-server/internal/httpserver/server_test.go:281 Keep long-running operations asynchronous through run/task APIs; do not add blocking REST endpoints that exceed the REST timeout.
AH-SR-016 Low Mitigated in repo; deployed smoke verified Production CORS defaults and generated env files now allow only https://hub.vectorcontrol.tech, and production startup fails fast if AGENTHUB_ENV=production or GIN_MODE=release sees a loopback or localhost origin. hk2 live smoke verified AGENTHUB_ENV=production, AGENTHUB_CORS_ORIGINS=https://hub.vectorcontrol.tech, loopback origins rejected, official origin allowed, and /health stayed healthy. hub-server/internal/middleware/cors.go:15, hub-server/internal/middleware/cors_test.go:8, hub-server/deployments/.env.production.example:26, hub-server/deployments/docker-compose.prod.yml:107, hub-server/scripts/generate-secrets.sh:29 Re-run the deployed CORS OPTIONS smoke after future CORS, nginx, production env, or allowed-origin changes.
AH-SR-017 Low Mitigated in repo; deployed on hk2 and public 404 verified Admin pprof/metrics uses a separate loopback-only listener and requires Basic Auth before serving /metrics or /debug/pprof/; production compose also publishes the admin port on host loopback only. The public Hub API router now returns explicit JSON 404/405 responses for unknown routes, and the timeout middleware preserves header-only status codes, so public /metrics or /debug/pprof/ probes cannot be masked as empty 200 responses. hub-server/internal/app/app.go:585, hub-server/internal/app/app_test.go:22, hub-server/deployments/docker-compose.prod.yml:120, hub-server/internal/router/router.go:24, hub-server/internal/router/router_test.go:16, hub-server/internal/middleware/timeout.go:33, hub-server/internal/middleware/timeout_test.go:28 Keep production admin surfaces on the separate loopback/admin mux; re-run the public /metrics and /debug/pprof/ 404 probe after future router or timeout middleware changes.
AH-SR-018 Medium Mitigated in repo; live runtime truncation smoke open Raw Edge run stdout/stderr now share a 4 MiB per-run byte budget before temp-file writes and run.output.batch publish. Structured run.agent.* adapter payloads now have a separate 1 MiB per-event JSON payload budget before event-bus publish; oversized map payloads are recursively string-truncated with truncated, maxBytes, bytesBefore, and message, and can fall back to metadata-only dropped: true if non-string payloads cannot fit. Run lifecycle remains stable. edge-server/internal/lifecycle/process_executor.go:45, edge-server/internal/lifecycle/process_executor.go:102, edge-server/internal/lifecycle/process_executor.go:382, edge-server/internal/lifecycle/process_executor.go:426, edge-server/internal/lifecycle/process_executor.go:579, edge-server/internal/adapters/event_emitter.go:12, edge-server/internal/adapters/event_emitter.go:76, edge-server/internal/adapters/event_emitter_test.go:158, edge-server/internal/lifecycle/process_executor_test.go:239 Verify raw and structured truncation metadata in a live runtime smoke with a real adapter process.
AH-SR-019 Medium Mitigated in repo; deploy/client verification required Auth, OIDC, and device-registration HTTP boundaries now validate or document device_id as UUID before reaching service/repository code, return BAD_REQUEST for malformed values, and keep Edge protocol fixtures aligned with the real Hub UUID contract. A real Postgres/Redis integration cycle covers register, login, authenticated /edge/devices/register, and the UUID contract; OpenAPI contract tests now also reject duplicate mapping keys that can hide schema drift. hub-server/internal/handler/device.go:35, hub-server/internal/handler/validation.go:9, hub-server/internal/handler/device_test.go:46, hub-server/internal/handler/device_openapi_test.go:58, hub-server/internal/handler/oidc.go:47, hub-server/internal/handler/oidc.go:85, hub-server/tests/api_test.go:228, edge-server/tests/hub_integration_test.go:41, api/openapi.yaml:2022, api/openapi.yaml:4143, api/openapi.yaml:4187 Verify deployed Hub uses the same code path and existing Desktop/Web clients always send persisted UUID device IDs.
AH-SR-024 Medium Mitigated in repo; deployed on hk2; client verification required AgentTeam CRUD/read routes must enforce ownership boundaries: only the team owner can mutate, delete, route, approve, or resolve TeamRun decisions; team members can read team details, member lists, runs, tasks, events, and state only when they own an Agent Profile installed in the team. Listing teams must include only owned teams plus teams readable through an installed Agent Profile, without leaking unrelated private teams. hub-server/internal/repository/agent_team.go:27, hub-server/internal/repository/agent_team.go:39, hub-server/internal/service/agent_team.go:85, hub-server/internal/service/agent_team.go:106, hub-server/internal/service/agent_team.go:694, hub-server/internal/service/agent_team.go:831, hub-server/internal/service/agent_team.go:1548, hub-server/internal/service/agent_team_test.go:70, hub-server/internal/service/agent_team_test.go:127, hub-server/internal/service/agent_team_test.go:1527 Verify Desktop/Web clients against the readable-member list/detail/state flow, and keep TeamRun write actions owner-only in UI affordances.
AH-SR-025 Medium Mitigated in repo; deployed on hk2; TeamRun exercise pending AgentTeam assignment/reassignment now enforces configurable guardrails for delegation depth, active subagents per run, route repeats, assignment timeout, total tasks per TeamRun, and TeamRun budget. Direct CreateAssignment walks the ancestor chain with dirty-data cycle detection, preserves max ancestor depth, rejects duplicate/cyclic member chains, and applies total/active assignment caps before creating tasks. hub-server/internal/config/config.go:130, hub-server/internal/app/app.go:201, hub-server/internal/service/agent_team.go:45, hub-server/internal/service/agent_team.go:1645, hub-server/internal/service/agent_team.go:1935, hub-server/internal/service/agent_team_test.go:842, hub-server/internal/service/agent_team_test.go:940, hub-server/internal/config/config_test.go:465 Monitor Hub/Redis/Postgres resource use while real TeamRun routing is exercised; keep the no-server-build deployment path for future runtime updates.
AH-SR-026 Low Mitigated in repo; deployed on hk2; OIDC login smoke required TokenDance ID OIDC is now the only exposed Hub auth entrypoint under /client/auth; legacy local password login/register routes are absent. The nullable users.password_hash schema is reflected in model.User.PasswordHash as *string, so OIDC-only rows preserve NULL instead of being coerced to an empty password-capable value, and OIDC callback tests prove newly created Hub users keep a nil password hash while still receiving Hub-local sessions. hub-server/internal/router/router.go:44, hub-server/internal/router/router.go:48, hub-server/internal/router/router.go:57, hub-server/migrations/0035_unify_auth_password_nullable.up.sql:1, hub-server/internal/model/user.go:14, hub-server/internal/repository/repository_test.go:361, hub-server/internal/service/oidc_test.go:46, hub-server/internal/service/oidc_test.go:287, hub-server/tests/tokendance_oidc_e2e_test.go:149 Verify deployed TokenDance ID login can create/read OIDC-only Hub users with password_hash IS NULL; keep local password routes removed unless a future migration reintroduces an explicit password-capable user class.
AH-SR-027 Medium Mitigated in repo; deployed on hk2 Hub typed runtime events previously had a 1 MiB per-event payload cap but no per-task event-count cap, so an abnormal Edge callback loop could keep appending small agent_run_events and projected chat messages for one task, growing PostgreSQL storage and replay/query cost. HandleTaskStream now writes events through a capped transactional insert with MaxRunEventsPerTask=4096; once the cap is reached, Hub returns BAD_REQUEST and rolls back both the runtime event and projected message insert. hk2 is running commit f0894ea with image digest sha256:c8a628ef41701bddfb1932b5c1234487f3854d14e652c32bee8efe993087f0ea. hub-server/internal/config/constants.go:83, hub-server/internal/repository/agent.go:12, hub-server/internal/repository/agent.go:213, hub-server/internal/service/agent.go:723, hub-server/internal/service/agent_run_event_test.go:216 Keep the per-task cap in future runtime event/replay changes; monitor Hub/PG resources during real TeamRun and Runtime history exercises.
AH-SR-033 Medium Mitigated in repo; deploy verification required OIDC PKCE + loopback callback: Desktop uses system browser PKCE flow with TokenDance ID. The OIDC client supports dynamic loopback callback ports for native/local clients. Code exchange enforces state, PKCE code_verifier, issuer, and audience validation. app/desktop/src-tauri/src/oidc.rs, app/desktop/src/lib/oidc.ts Verify Desktop PKCE login from cold start through Hub session acquisition; confirm loopback callback closes after code exchange and token material never reaches browser-accessible storage.
AH-SR-034 Medium Mitigated in repo; deploy verification required Owner boundary: Agent Profile, Skill, MCP server, and Execution Target CRUD operations now verify the authenticated user owns or is authorized to modify the resource. Cross-owner mutation or read is rejected. hub-server/internal/handler/profile.go, hub-server/internal/handler/skill.go, hub-server/internal/handler/mcp.go, hub-server/internal/handler/target.go Smoke-test each entity type with owner and non-owner sessions; verify 403 on cross-owner mutation.
AH-SR-043 Medium Open Web preview/mock surfaces are labeled, but demo fallbacks and local-preview actions still share production UI paths. Release-quality Web must not report fake execution, fake private-chat success, or local catalog mutation as Hub-backed behavior. app/web/src/pages/mockConvergence.test.tsx, app/web/src/api/agentQueries.ts, app/web/src/api/hubClient.ts, app/web/src/i18n/locales/en/*.json Gate demo/mock surfaces behind explicit preview mode and route production run mutations through Hub /web/agent-tasks or team-run APIs; keep tests that fail on mock success states in authenticated flows.
AH-SR-044 Medium Open Runner compatibility health still leaks into Desktop/Web settings and workbench state, while the architecture has moved to Runtime adapters plus Execution Targets. This can mislead operators about what is actually dispatchable. edge-server/README.md, edge-server/internal/runners/, app/desktop/src/components/settings/sections/ExecutionTargetsSection.tsx, app/web/src/hooks/useWorkbenchProjection.ts Replace direct runner-centric UI assumptions with a compatibility adapter over Runtime inventory and Execution Target health; keep /v1/runners only as a documented legacy summary until clients stop depending on it.

Observability / Hygiene

ID Severity Status Risk Evidence Next action
AH-SR-038 Low Mitigated EventBus panic recovery: Hub EventBus subscribers have defer/recover wrappers. A panic in one subscriber does not crash the Hub or block other subscribers. hub-server/internal/eventbus/eventbus.go Keep recovery wrappers; add focused test for subscriber panic isolation.
AH-SR-039 Low Mitigated Edge race condition: concurrent workspace file access previously had a read/write race. The lifecycle manager now uses proper locking. edge-server/internal/lifecycle/lifecycle.go Keep lock ordering documented; add regression that concurrent run triggers do not race.
AH-SR-040 Low Mitigated Prometheus /metrics endpoint is wired on Edge Server for operational monitoring. edge-server/internal/middleware/metrics.go Keep metrics endpoint optionally auth-gated if Edge is exposed beyond localhost.
AH-SR-041 Low Mitigated Message/device/task/run payloads are capped at 1 MiB with UUID format validation before processing. CI enforces govulncheck as a hard block and gosec as warning-only. No known-vulnerable Go dependency can enter the build. hub-server/internal/middleware/bodylimit.go, hub-server/internal/validator/uuid.go, .github/workflows/ci.yml, scripts/ Keep body limits and CI gates; bump body cap only with explicit design review for new upload/streaming endpoints; promote gosec to hard block after clearing current warnings.

Recent Mitigation Evidence

  • 2026-05-29: AH-SR-027 Hub runtime event growth control was mitigated in repo. HandleTaskStream now persists agent_run_events through a capped transactional insert using config.MaxRunEventsPerTask; when the per-task cap is reached, the stream callback returns BAD_REQUEST and no projected messages row is inserted.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/service -run "TestHandleTaskStream(RejectsRunEventWhenTaskEventCapReached|PersistsTypedRunEventAndProjection|RejectsOversizedPayload|RejectsOversizedProjectedContent|RejectsOversizedEdgeRunID|_DispatchedTransitionConflictDoesNotPersist)" -count=1 -v
    • cd hub-server; go test ./internal/repository ./internal/service ./internal/handler -count=1
    • cd hub-server; go test ./... -short -count=1
  • Production deployment evidence:
    • hk2 git pull --ff-only advanced /opt/agenthub-hub to f0894ea, then docker load loaded the locally built tar and docker compose ... up -d --no-build --no-deps --force-recreate hub-server recreated only Hub Server.
    • hk2 verified repo=f0894ea, runtime image sha256:c8a628ef41701bddfb1932b5c1234487f3854d14e652c32bee8efe993087f0ea, running/healthy, /health status=ok with migrations=39, CORS loopback origins 403, official origin 204, and /tmp/agenthub-hub-*.tar count 0.
  • 2026-05-29: AH-SR-025 AgentTeam delegation/resource guardrails were mitigated in repo. Hub config now exposes agent_team defaults and AGENTHUB_AGENT_TEAM_* env overrides; App wiring injects those values into AgentTeamService; route decisions and direct assignment creation share the configured task, active-subagent, repeat, timeout, budget, depth, and cycle checks.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/config -run "TestLoadAgentTeamGuardrailDefaults|TestEnvOverrideAgentTeamGuardrails|TestValidateRejectsInvalidAgentTeamGuardrails|TestJWTSecretHardcodedOverriddenByEnv" -count=1 -v
    • cd hub-server; go test ./internal/service -run "TestAgentTeamService_CreateAssignmentRejects(DelegationDepthLimit|DelegationCycle|TeamRunTaskLimit)|TestAgentTeamService_CreateAssignmentUsesConfigured(DelegationDepthLimit|TeamRunTaskLimit)|TestAgentTeamService_HandleRouteDecisionRejectsWhen(ActiveSubagentLimitReached|TaskLimitReached|RouteRepeatLimitReached)|TestAgentTeamService_HandleRouteDecisionRejects(TimedOutAssignment|BudgetExceeded)" -count=1 -v
    • cd hub-server; go test ./internal/service -run TestAgentTeamService -count=1 -v
    • cd hub-server; go test ./... -short -count=1
    • git diff --check
  • 2026-05-29: AH-SR-017 public admin-surface probe ambiguity was mitigated in repo. Admin pprof/metrics still sits behind the separate Basic Auth admin mux, and the main API router now emits explicit 404/405 JSON for unknown paths; the timeout middleware now flushes status-only responses instead of defaulting them to 200.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/router -run TestNoRouteReturnsNotFound -count=1 -v
    • cd hub-server; go test ./internal/middleware -run "TestTimeout_(FlushesHeaderOnlyStatus|HandlerCompletesNormally|Returns504WhenHandlerSlow)" -count=1 -v
    • cd hub-server; go test ./... -short -count=1
    • git diff --check
  • 2026-05-29: commit f071d03 was deployed to hk2 through the no-server-build path: local Docker build, tar SHA-256 a5ec4311942947f5579b2b70463b04f1d2c2a14d7cf36e068f017432796def65, scp -J hk1, hk2 git pull --ff-only, docker load, and docker compose ... up -d --no-build --no-deps --force-recreate hub-server. Production image id/digest is sha256:9d074e9c1a2f9cd2fd95e2c3735a6c30c67f6caaca478fc69153bfcb64534356; Docker health is healthy; local and SNI HTTPS /health return status=ok with migrations=39; public /metrics, /debug/pprof/, and /does-not-exist return 404; local and hk2 /tmp/agenthub-hub-*.tar artifacts were cleaned.
  • 2026-05-29: B9 S3-compatible attachment storage hardening was mitigated in repo and deployed to hk2 as commit bc3af60 through the no-server-build path. Fresh checks passed: go test ./internal/config -run "Test(EnvOverrideS3Config|S3Config_IsConfigured|S3Config_IsEmpty|ValidateS3ConfigRequiresCompleteCredentials|ValidateS3ConfigDoesNotRequireLocalUploadDir)$" -count=1 -v, go test ./internal/service -run "Test(LocalStorage_PutAndGet|S3Storage_LocalPathReturnsEmpty|S3Storage_PutReturnsTrue|S3Storage_PutReturnsFalseWhenBlobAlreadyExists|SaveAttachment_StorageInjection)$" -count=1 -v, go test ./internal/config ./internal/service ./internal/app -count=1, go test ./... -short -count=1, docker compose -f deployments/docker-compose.prod.yml --env-file deployments/.env.production.example config --quiet, and git diff --check.
  • Deployment evidence: local Docker build, tar SHA-256 ae80e5eb67c2ff828d4062421d852a6a738870197a0188e2708e2b6cb1d5e4c7, scp -J hk1, hk2 git pull --ff-only, docker load, and docker compose ... up -d --no-build --no-deps --force-recreate hub-server. Production image id/digest is sha256:3e6c4c5402fd0e4bd578f8ffb32a4ac2e3ade441b8753eca7370a58c2c85a26a; Docker health is healthy; local, SNI HTTPS, and public /health return status=ok with migrations=39; public /metrics, /debug/pprof/, and /does-not-exist return 404; production S3 env values are empty, preserving local upload behavior; local and hk2 /tmp/agenthub-hub-*.tar artifacts were cleaned.
  • 2026-05-29: AH-SR-016 production CORS deployed smoke was verified on hk2 without a build or restart. Live repo was 83d6dd5; runtime image id stayed sha256:3e6c4c5402fd0e4bd578f8ffb32a4ac2e3ade441b8753eca7370a58c2c85a26a; container env showed AGENTHUB_ENV=production and AGENTHUB_CORS_ORIGINS=https://hub.vectorcontrol.tech. OPTIONS /health with origins http://localhost:5173 and http://127.0.0.1:5173 returned 403 with no Access-Control-Allow-Origin; origin https://hub.vectorcontrol.tech returned 204 with Access-Control-Allow-Origin: https://hub.vectorcontrol.tech; /health returned status=ok, migrations=39; hk2 /tmp/agenthub-hub-*.tar count was 0.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/middleware -run "Test(CORSRejectsProductionLoopbackOrigin|ValidateCORSOriginsForEnvironment)" -count=1 -v
  • 2026-05-29: AH-SR-024 AgentTeam ownership/read boundary was mitigated in repo. ListTeams now returns owned teams plus teams where the requester owns an installed Agent Profile; GetTeam and TeamRun read endpoints share the same readable-member check; HandleRouteDecision, DecideApproval, and ResolveConflict require the team owner so readable members cannot mutate TeamRun decisions.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/service -run "TestAgentTeamService_(GetTeamAllowsAgentProfileOwnerMemberRead|ListTeamsIncludesReadableMemberTeamsWithoutLeaking|MemberReadableTeamCannotMutateRunDecisions)$" -count=1 -v
    • cd hub-server; go test ./internal/service -run TestAgentTeamService -count=1 -v
    • cd hub-server; go test ./internal/repository -run TestAgentTeam -count=1 -v
    • cd hub-server; go test ./... -short -count=1
  • 2026-05-29: AH-SR-026 OIDC-only nullable password handling was mitigated in repo. model.User.PasswordHash now preserves SQL NULL as nil, repository fixtures match migration 0035's nullable column, and OIDC callback tests assert TokenDance ID users are created without a local password hash while still receiving Hub-local access/refresh sessions. The legacy local password login/register routes are not exposed in the current router.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/repository -run TestUserRepo_ReadsOIDCOnlyUserWithNullPasswordHash -count=1 -v
    • cd hub-server; go test ./internal/service -run "TestHandleCallback_SuccessUsesConfiguredJWKSAndIssuesHubSession|TestGenerateAuthorizationURL|TestHandleCallback_InvalidState|TestHandleCallback_StateExpired" -count=1 -v
    • cd hub-server; go test ./internal/model -count=1
    • cd hub-server; go test ./... -short -count=1
  • 2026-05-25: AH-SR-012 tracked generated-artifact exposure was mitigated in the current repo tree by deleting the tracked Vite visualizer report, ad hoc Edge coverage outputs, and Hub test upload blobs, then adding ignore rules for regenerated stats, coverage reports, and upload directories. Existing Git history may still contain the removed blobs until a separately coordinated history rewrite.
  • Fresh focused checks passed:
    • Test-Path confirmed the five removed artifact paths are absent from the working tree.
    • git diff --name-status -- .gitignore app/desktop/stats.html 'edge-server/$covPath' edge-server/cov_full 'hub-server/tests/uploads/**' shows .gitignore modified and those five tracked artifact files deleted.
    • git check-ignore --no-index -v -- app/desktop/stats.html edge-server/cov_full 'edge-server/$covPath' hub-server/tests/uploads/example/blob hub-server/coverage.out hub-server/coverage.html confirms the regenerated artifact paths are ignored after they are no longer tracked.
  • 2026-05-26: AH-SR-007 browser token exposure was further reduced in repo. Desktop packaged mode already uses the Tauri OS credential-store path for Hub access/refresh tokens, while Web now stores Hub access/refresh tokens and the token-source hint in sessionStorage and clears legacy localStorage keys (agenthub_hub_token, agenthub_hub_refresh_token, agenthub_token_source) on load/save. This is not the final public Web posture; BFF/HttpOnly-cookie session ownership remains the release target.
  • Fresh focused checks passed:
    • cd app; corepack.cmd pnpm --filter agenthub-web test -- src/api/hubTokenStorage.test.ts src/stores/hubStore.test.ts
    • cd app; corepack.cmd pnpm --filter agenthub-web typecheck
    • cd app; corepack.cmd pnpm --filter agenthub-web build passed with the existing Vite large-chunk warning.
    • cd app/desktop; pnpm vitest run src/api/hubTokenStorage.test.ts src/__tests__/hubClient.test.ts
    • cd app/desktop; pnpm exec tsc --noEmit
    • cd app/desktop/src-tauri; cargo check passed with the existing edge_manager.auto_restart dead-code warning.
  • 2026-05-25: AH-SR-016 production CORS localhost-origin risk was mitigated in repo by removing localhost defaults from production examples/generator output, setting AGENTHUB_ENV=production in production config paths, and adding startup validation that rejects loopback/localhost CORS origins in production-like environments.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/middleware -run "Test(CORSRejectsProductionLoopbackOrigin|ValidateCORSOriginsForEnvironment)" -count=1 -v
  • 2026-05-25: AH-SR-017 admin pprof/metrics exposure risk was mitigated in repo by keeping the admin server on 127.0.0.1, sharing one Basic Auth wrapper for pprof and metrics, and adding tests for unauthorized, wrong-password, and authorized access to /metrics and /debug/pprof/.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/app -run "Test(AdminListenAddrUsesLoopback|AdminMuxRequiresBasicAuthForMetricsAndPprof)" -count=1 -v
  • 2026-05-25: AH-SR-014 local Edge call boundary was mitigated in repo by adding optional --local-auth-token / AGENTHUB_EDGE_AUTH_TOKEN enforcement for non-health Edge APIs, propagating the token through Desktop REST/WebSocket clients, and adding a tokenized client smoke path. /v1/health and CORS preflight stay unauthenticated for readiness and browser setup.
  • Fresh focused checks passed:
    • cd edge-server; go test ./internal/httpserver ./cmd/agenthub-edge -count=1
    • cd edge-server; go test ./... -short -count=1
    • cd hub-server; go test ./... -short -count=1
    • cd app/desktop; pnpm vitest run src/__tests__/edgeClient.test.ts src/__tests__/eventClient.test.ts
    • cd app/desktop; pnpm exec tsc --noEmit
    • .\scripts\client-smoke.ps1 -EdgeAddr 127.0.0.1:3228 -EdgeAuthToken local-smoke-token passed 23/23.
  • 2026-05-25: AH-SR-011 public stats exposure was mitigated in repo by keeping /api/public/stats unauthenticated for the website but bucketizing public counts and uptime. The API still returns numeric totalUsers, totalAgents, onlineAgents, and totalMessages, but values are lower-bound buckets rather than exact live totals.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/handler -run TestPublicStatsBucketsCountsAndUptime -count=1
  • 2026-05-25: AH-SR-015 Edge REST timeout handling was mitigated in repo by wrapping non-WebSocket HTTP requests in a 30s timeout middleware while bypassing WebSocket upgrade requests so /v1/events stays long-lived.
  • Fresh focused checks passed:
    • cd edge-server; go test ./internal/httpserver -run "TestRESTTimeoutMiddleware" -count=1
    • cd hub-server; go test ./... -short -count=1
    • cd edge-server; go test ./... -short -count=1
  • 2026-05-25: AH-SR-002 TokenDance bearer/device-type confusion was mitigated in repo by keeping TokenDance ID RS256 bearer validation as an identity compatibility path while tagging it as tokendance_bearer; desktop-only Edge routes still require Hub-issued desktop device class through DeviceTypeCheck("desktop").
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/middleware -run "TestAuthMiddlewareTokenDanceBearerDoesNotSatisfyDesktopDeviceCheck|TestDeviceTypeCheck|TestAuthMiddleware" -count=1
    • cd hub-server; go test ./internal/jwtutil ./internal/middleware -count=1
  • 2026-05-25: AH-SR-006 WebSocket/REST auth drift was resolved as a documented boundary rather than by adding another TokenDance bearer path. Hub /client/ws accepts Hub-local HS256 access tokens on the first auth frame and rejects TokenDance RS256 bearer tokens, preserving Hub session/device routing semantics.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/handler -run "TestWebSocketAuth(AcceptsHubLocalSessionToken|RejectsTokenDanceBearerToken)" -count=1 -v
    • cd hub-server; go test ./internal/handler -count=1
  • 2026-05-25: Hub TokenDance ID OIDC exchange was implemented in repo. /client/auth/oidc/authorize now binds PKCE S256 state to a UUID Hub device proof, /client/auth/oidc/callback exchanges code with TokenDance ID, validates ID token through the configured JWKS/issuer/audience path, maps tokendance_sub, and issues Hub-local access/refresh sessions.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/service -run "TestGenerateAuthorizationURL_(InvalidDeviceType|RejectsNonS256PKCEMethod)|TestHandleCallback_SuccessUsesConfiguredJWKSAndIssuesHubSession" -count=1
    • cd hub-server; go test ./internal/handler -run "TestOIDCHandler_PostOIDC" -count=1
    • cd hub-server; go test ./internal/config -run "TestEnvOverrideTokenDanceID" -count=1
  • 2026-05-25: AH-SR-003 was mitigated in repo by making Hub login persist the caller device ID consistently in devices, access-token claims, and refresh-token rows; Desktop legacy login now uses the persisted agenthub_device_id UUID instead of generating a one-off desktop_* string; getState() now returns a copy of the auth snapshot to avoid external state mutation. A follow-up real Postgres/Redis integration pass changed devices(user_id, device_type) from a unique constraint to a non-unique index, allows one user to log in from multiple desktop UUIDs, and maps cross-user device_id ownership conflicts to BAD_REQUEST instead of INTERNAL_ERROR.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/service -run "TestLogin_Success|TestLogout|TestRefreshToken_Success" -count=1
    • cd hub-server; go test ./internal/repository -run TestDeviceRepo_Upsert -count=1 -v
    • cd hub-server; AGENTHUB_DB_PORT=15432 AGENTHUB_REDIS_PORT=16380 AGENTHUB_JWT_SECRET=<test-secret> go test ./tests -run "TestLoginAllowsMultipleDesktopDevicesForSameUser|TestLoginRejectsDeviceIDOwnedByAnotherUser" -count=1 -v
    • cd hub-server; go test ./internal/service -run TestDeviceRegisterMapsOwnershipMismatchToBadRequest -count=1 -v
    • cd app/desktop; pnpm test -- src/__tests__/hubClient.test.ts
  • 2026-05-25: AH-SR-009 attachment hash/header hardening was mitigated in repo by validating 64-char lowercase hex hashes before path derivation, making PathFromHash return an empty path for malformed hashes, sniffing upload MIME metadata from staged file bytes instead of trusting multipart headers, normalizing download Content-Type, adding X-Content-Type-Options: nosniff, and formatting Content-Disposition from a sanitized basename.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/service -run TestPathFromHashValidatesHashShape -count=1
    • cd hub-server; go test ./internal/handler -run "TestAttachment(UploadRejectsMalformedHashBeforePathDerivation|ProbeRejectsMalformedHashBeforeServiceLookup)" -count=1
    • cd hub-server; go test ./internal/handler -run "TestAttachment(UploadSniffsMimeTypeInsteadOfTrustingMultipartHeader|DownloadFormatsUnsafeFilenameSafely)$" -count=1 -v
    • cd hub-server; go test ./internal/service ./internal/handler -run "Test(PathFromHashValidatesHashShape|Attachment|AttachmentErrors)" -count=1
  • 2026-05-25: AH-SR-008 dev compose exposure was mitigated in repo by making the default published host 127.0.0.1 for PostgreSQL, Redis, Hub API, and Hub admin/metrics. .env.example now documents AGENTHUB_BIND_HOST=127.0.0.1; remote dev requires an explicit opt-in such as AGENTHUB_BIND_HOST=0.0.0.0.
  • Fresh focused checks passed:
    • docker compose config --services
    • docker compose config
  • 2026-05-25: AH-SR-010 Redis/cache nil behavior was mitigated in repo by adding service-layer cache resolvers with typed-nil detection and a no-op/fallback cache for test/offline construction paths. Production startup still requires Redis through the existing App.Run ping; fallback cache only prevents nil injection panics and preserves DB seq fallback for message/agent writes.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/service -run "Test(ResolveCacheUsesNoopForTypedNilClient|SendMessage_NilCacheUsesDBSeqFallback|ChangePassword_NilCacheDoesNotPanic|UpdateProfile_NilCacheDoesNotPanic|AcceptFriendRequest_NilCacheDoesNotPanic|ListContacts_NilCacheMarksOffline|CreatePrivateSession_NilCacheDoesNotPanic|HandleTaskDoneNilCacheUsesDBSeqFallback)$" -count=1 -v
    • cd hub-server; go test ./internal/service -count=1
    • cd hub-server; go test ./... -short -count=1
  • 2026-05-25: AH-SR-005 arbitrary permission-decision spoofing was mitigated by wiring an Edge pending permission registry to the event bus before WebSocket fanout, scoping adapter permission events with run/project/thread IDs, and making POST /v1/permissions/decide one-shot against a known run/request pair.
  • Fresh focused checks passed:
    • cd edge-server; go test ./internal/api ./internal/events ./internal/adapters ./internal/lifecycle -run "TestPermission|TestPostPermission|TestMuxPermission|TestAddObserver|TestScopedEventEmitter|TestBusEventEmitter|TestBudgetAwareEmitter" -count=1 -v
    • python -c "import yaml, pathlib; yaml.safe_load(pathlib.Path('api/openapi.yaml').read_text(encoding='utf-8')); print('yaml ok')"
  • 2026-05-25: AH-SR-004 remote Edge process-control exposure was mitigated in repo by rejecting wildcard, LAN, and non-loopback listen addresses in both agenthub-edge config parsing and httpserver.Run. Current unauthenticated Edge remains loopback-only; remote Edge still requires a separate authenticated mode design.
  • Fresh focused checks passed:
    • cd edge-server; go test ./internal/security ./cmd/agenthub-edge ./internal/httpserver -run "TestValidateLocalListenAddr|TestBuildConfig(EnvVarAddr|RejectsNonLoopbackAddr|FlagOverridesEnvVar)|TestRunRejectsNonLoopbackAddr" -count=1
  • 2026-05-25: AH-SR-018 raw and structured output flood risk was mitigated in repo by adding a shared per-run byte limiter to Edge ProcessExecutor raw stdout/stderr capture and a PayloadLimitEmitter for structured adapter events. Raw output truncates temp-file writes and run.output.batch text; structured run.agent.* map payloads are capped to a 1 MiB JSON payload budget before event-bus publish while retaining compatible truncation metadata.
  • Fresh focused checks passed:
    • cd edge-server; go test ./internal/adapters -run "TestPayloadLimitEmitter|TestBudgetAwareEmitter|TestScopedEventEmitter|TestBusEventEmitter" -count=1 -v
    • cd edge-server; go test ./internal/lifecycle -run "TestProcessExecutorTruncatesStructuredAdapterPayload|TestProcessExecutorTruncatesRawOutputAtRunBudget|TestRunOutputLimiter" -count=1 -v
    • cd edge-server; go test ./... -short -count=1
  • 2026-05-25: AH-SR-019 was mitigated in repo by normalizing and validating Hub device_id values as UUIDs at /client/auth/login and /edge/devices/register, rejecting malformed values before service/repository calls, and updating OpenAPI for the login and Edge device-registration request contracts.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/handler -run "Test(AuthHandler_Login|DeviceHandler_Register|EdgeHubProtocol)" -count=1 -v
    • cd hub-server; AGENTHUB_DB_PORT=15432 AGENTHUB_REDIS_PORT=16380 AGENTHUB_JWT_SECRET=<32-char-test-secret> go test ./tests -run "TestEdgeDevice(Register)?$" -count=1 -v against temporary local Postgres/Redis created with docker compose up -d postgres redis; containers and volumes were removed afterward with docker compose down -v.
  • 2026-05-25: AH-SR-020 was mitigated in repo by passing authenticated Edge device_id and callback edge_run_id through all task callback handlers (ack, stream, done, fail). Online WebSocket dispatch and offline pending-task replay record the routed desktop edge_device_id, service methods reject wrong Edge user/device/run id before state changes or agent-message inserts, and Desktop forwards the Edge run_id on stream/done/fail callbacks.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/service ./internal/handler -run "TestHandleTask|TestEdgeAgentTask|TestEdgeTask|TestEdgeHubProtocol|TestDispatchTask" -count=1 -v
    • cd hub-server; AGENTHUB_DB_PORT=15432 AGENTHUB_REDIS_PORT=16380 AGENTHUB_JWT_SECRET=<test-secret> go test ./tests -run "TestEdgeTaskCallbacksRejectWrong(DesktopDevice|RunID)$" -count=1 -v against temporary local Postgres/Redis created with docker compose up -d postgres redis.
    • cd app/desktop; pnpm vitest run src/__tests__/useHubIntegration.test.ts
    • cd app/desktop; pnpm exec tsc --noEmit
    • python -c "import yaml, pathlib; yaml.safe_load(pathlib.Path('api/openapi.yaml').read_text(encoding='utf-8')); print('yaml ok')"
  • 2026-05-25: AH-SR-021 attachment probe/download scope was partially mitigated in repo by binding ProbeAttachment and GetAttachmentByID to the authenticated uploader and adding cross-user not-found tests. Upload staging now writes to a temp file and only creates the content-addressed blob with O_EXCL after hash verification, so a mismatched upload cannot overwrite an existing blob for the same claimed hash.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/handler -run "TestAttachmentUploadHashMismatchDoesNotModifyExistingBlob$" -count=1 -v
    • cd hub-server; go test ./internal/handler ./internal/service -run "Test(Attachment|ProbeAttachment|GetAttachmentByID|PathFromHash|AttachmentErrors|HandleTask(Ack|Callbacks)|EdgeHubProtocol)" -count=1 -v
  • 2026-05-25: AH-SR-022 was mitigated in repo by changing pin creation to require GetMessageBySessionAndID(sessionID, msgID) and changing pinned-list hydration to GetMessagesBySessionAndIDs(sessionID, ids), so a message UUID from another session cannot be pinned into or exposed through the caller's session. 2026-05-29 follow-up verified migration 0039_message_pins_session_fk cleans historical cross-session pins and enforces message_pins(session_id,message_id) -> messages(session_id,id) with a composite FK.
  • Fresh focused checks passed:
    • cd hub-server; go test ./internal/service -run "Test(PinMessage|ListPinnedMessages)" -count=1 -v
    • cd hub-server; AGENTHUB_DB_PORT=15432 AGENTHUB_DB_NAME=<temp-db> AGENTHUB_REDIS_PORT=16380 AGENTHUB_JWT_SECRET=<test-secret> go test ./tests -run "Test(MessagePinRejectsCrossSessionMessage|MessagePinsRejectHistoricalCrossSessionPinAtDatabase)$" -count=1 -v against a temporary local Postgres database and Redis.
    • hk2 read-only schema verification: schema_migrations=39|f, message_pins has fk_message_pins_message_session, and cross_session_bad_pins=0.
    • cd hub-server; go test ./internal/repository ./internal/service ./internal/handler -count=1
    • cd hub-server; go test ./... -short -count=1

Verification Queue

Run these from D:\Code\TokenDance\AgentHub:

git status --short --branch
git ls-files -- app/desktop/stats.html 'edge-server/$covPath' edge-server/cov_full 'hub-server/tests/uploads/**'
go test ./hub-server/internal/jwtutil ./hub-server/internal/middleware ./hub-server/internal/handler ./hub-server/internal/service
go test ./hub-server/internal/jwtutil ./hub-server/internal/middleware -run "TestParseTokenDanceJWTRequiresExpectedIssuerAndAudience|TestAuthMiddlewareRejectsTokenDanceTokenWithoutExpectedAudience" -count=1
go test ./edge-server/internal/httpserver ./edge-server/internal/api ./edge-server/internal/security
# Backend full suite
cd hub-server; go test ./... -short -count=1
cd ..\edge-server; go test ./... -short -count=1
# Frontend tests
cd ..\app\desktop; pnpm test; pnpm typecheck
cd ..\web; corepack.cmd pnpm typecheck
# Return to repo root
cd ..\..
# Code searches
rg -n "ParseTokenDanceJWT|Issuer|Audience|ValidMethods|token.Valid" hub-server/internal/jwtutil hub-server/internal/middleware
rg -n 'device_type", "desktop"|DeviceTypeCheck\("desktop"\)' hub-server/internal
rg -n "AGENTHUB_ADDR|IsTrustedLocalOrigin|permissions/decide|PostRuns|exec.CommandContext" edge-server
rg -n "dev_password|6379:6379|5432:5432|6060:6060|AGENTHUB_JWT_SECRET|127.0.0.1" docker-compose.yml hub-server/deployments/docker-compose.prod.yml
rg -n 'PostForm\("hash"\)|PathFromHash|Content-Disposition|Content-Type' hub-server/internal/handler/attachment.go hub-server/internal/service/attachment.go
# Git diff check
git diff --check

Loop Notes

  • Keep this file focused on active risks and verification decisions.
  • When a finding becomes a design decision, link the ADR or architecture section and mark it Accepted.
  • When a code fix lands, include the test command and commit/PR in the status note.
  • Hub session/auth, OIDC, Edge auth, or workspace boundary changes must update this register.
  • Critical/High risks in Open state block public release; see ../docs/security/security-risk.md for release gates.
  • Production live endpoint, callback URL, client secret, session token, host path, backup path, and rotation evidence belong only in C:\Users\Ding\server or private ops docs.
  • Cross-repo identity/auth changes must also update ../docs/identity/identity-auth.md, ../docs/identity/authorization-model.md, and ../docs/identity/relying-party.md.
  • Feishu/Lark Gateway security items should be added here when the integration skeleton lands.
  • 2026-06-04: Merged 17 findings from root docs/security-risk-register.md (reviewed 2026-06-03) as AH-SR-028 through AH-SR-044. Root copy can now be deleted.

2026-05-27 — Dependabot 上游漏洞(等待上游修复)

ID 严重度 说明 状态
github.com/jackc/pgx/v5 v5.9.2 critical Memory-safety vulnerability 等待 pgx 发新版(当前 v5.9.2 是最新)
github.com/jackc/pgx/v5 v5.9.2 low SQL injection via dollar-quoted string literals 同上
github.com/google/uuid v1.6.0 medium Buffer bounds check missing 等待 uuid 发新版(当前 v1.6.0 是最新)
glib (Rust/Tauri) medium Iterator unsoundness Tauri 上游依赖,等待更新