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.
- 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
| 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. |
| 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. |
| 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. |
- 2026-05-29:
AH-SR-027Hub runtime event growth control was mitigated in repo.HandleTaskStreamnow persistsagent_run_eventsthrough a capped transactional insert usingconfig.MaxRunEventsPerTask; when the per-task cap is reached, the stream callback returnsBAD_REQUESTand no projectedmessagesrow is inserted. - Fresh focused checks passed:
cd hub-server; go test ./internal/service -run "TestHandleTaskStream(RejectsRunEventWhenTaskEventCapReached|PersistsTypedRunEventAndProjection|RejectsOversizedPayload|RejectsOversizedProjectedContent|RejectsOversizedEdgeRunID|_DispatchedTransitionConflictDoesNotPersist)" -count=1 -vcd hub-server; go test ./internal/repository ./internal/service ./internal/handler -count=1cd hub-server; go test ./... -short -count=1
- Production deployment evidence:
- hk2
git pull --ff-onlyadvanced/opt/agenthub-hubtof0894ea, thendocker loadloaded the locally built tar anddocker compose ... up -d --no-build --no-deps --force-recreate hub-serverrecreated only Hub Server. - hk2 verified
repo=f0894ea, runtime imagesha256:c8a628ef41701bddfb1932b5c1234487f3854d14e652c32bee8efe993087f0ea,running/healthy,/healthstatus=okwithmigrations=39, CORS loopback origins403, official origin204, and/tmp/agenthub-hub-*.tarcount0.
- hk2
- 2026-05-29:
AH-SR-025AgentTeam delegation/resource guardrails were mitigated in repo. Hub config now exposesagent_teamdefaults andAGENTHUB_AGENT_TEAM_*env overrides; App wiring injects those values intoAgentTeamService; 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 -vcd 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 -vcd hub-server; go test ./internal/service -run TestAgentTeamService -count=1 -vcd hub-server; go test ./... -short -count=1git diff --check
- 2026-05-29:
AH-SR-017public 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 to200. - Fresh focused checks passed:
cd hub-server; go test ./internal/router -run TestNoRouteReturnsNotFound -count=1 -vcd hub-server; go test ./internal/middleware -run "TestTimeout_(FlushesHeaderOnlyStatus|HandlerCompletesNormally|Returns504WhenHandlerSlow)" -count=1 -vcd hub-server; go test ./... -short -count=1git diff --check
- 2026-05-29: commit
f071d03was deployed to hk2 through the no-server-build path: local Docker build, tar SHA-256a5ec4311942947f5579b2b70463b04f1d2c2a14d7cf36e068f017432796def65,scp -J hk1, hk2git pull --ff-only,docker load, anddocker compose ... up -d --no-build --no-deps --force-recreate hub-server. Production image id/digest issha256:9d074e9c1a2f9cd2fd95e2c3735a6c30c67f6caaca478fc69153bfcb64534356; Docker health ishealthy; local and SNI HTTPS/healthreturnstatus=okwithmigrations=39; public/metrics,/debug/pprof/, and/does-not-existreturn 404; local and hk2/tmp/agenthub-hub-*.tarartifacts were cleaned. - 2026-05-29:
B9S3-compatible attachment storage hardening was mitigated in repo and deployed to hk2 as commitbc3af60through 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, andgit diff --check. - Deployment evidence: local Docker build, tar SHA-256
ae80e5eb67c2ff828d4062421d852a6a738870197a0188e2708e2b6cb1d5e4c7,scp -J hk1, hk2git pull --ff-only,docker load, anddocker compose ... up -d --no-build --no-deps --force-recreate hub-server. Production image id/digest issha256:3e6c4c5402fd0e4bd578f8ffb32a4ac2e3ade441b8753eca7370a58c2c85a26a; Docker health ishealthy; local, SNI HTTPS, and public/healthreturnstatus=okwithmigrations=39; public/metrics,/debug/pprof/, and/does-not-existreturn 404; production S3 env values are empty, preserving local upload behavior; local and hk2/tmp/agenthub-hub-*.tarartifacts were cleaned. - 2026-05-29:
AH-SR-016production CORS deployed smoke was verified on hk2 without a build or restart. Live repo was83d6dd5; runtime image id stayedsha256:3e6c4c5402fd0e4bd578f8ffb32a4ac2e3ade441b8753eca7370a58c2c85a26a; container env showedAGENTHUB_ENV=productionandAGENTHUB_CORS_ORIGINS=https://hub.vectorcontrol.tech. OPTIONS/healthwith originshttp://localhost:5173andhttp://127.0.0.1:5173returned403with noAccess-Control-Allow-Origin; originhttps://hub.vectorcontrol.techreturned204withAccess-Control-Allow-Origin: https://hub.vectorcontrol.tech;/healthreturnedstatus=ok,migrations=39; hk2/tmp/agenthub-hub-*.tarcount 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-024AgentTeam ownership/read boundary was mitigated in repo.ListTeamsnow returns owned teams plus teams where the requester owns an installed Agent Profile;GetTeamand TeamRun read endpoints share the same readable-member check;HandleRouteDecision,DecideApproval, andResolveConflictrequire 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 -vcd hub-server; go test ./internal/service -run TestAgentTeamService -count=1 -vcd hub-server; go test ./internal/repository -run TestAgentTeam -count=1 -vcd hub-server; go test ./... -short -count=1
- 2026-05-29:
AH-SR-026OIDC-only nullable password handling was mitigated in repo.model.User.PasswordHashnow preserves SQLNULLas 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 -vcd hub-server; go test ./internal/service -run "TestHandleCallback_SuccessUsesConfiguredJWKSAndIssuesHubSession|TestGenerateAuthorizationURL|TestHandleCallback_InvalidState|TestHandleCallback_StateExpired" -count=1 -vcd hub-server; go test ./internal/model -count=1cd hub-server; go test ./... -short -count=1
- 2026-05-25:
AH-SR-012tracked 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-Pathconfirmed 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.gitignoremodified 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.htmlconfirms the regenerated artifact paths are ignored after they are no longer tracked.
- 2026-05-26:
AH-SR-007browser 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 insessionStorageand clears legacylocalStoragekeys (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.tscd app; corepack.cmd pnpm --filter agenthub-web typecheckcd app; corepack.cmd pnpm --filter agenthub-web buildpassed with the existing Vite large-chunk warning.cd app/desktop; pnpm vitest run src/api/hubTokenStorage.test.ts src/__tests__/hubClient.test.tscd app/desktop; pnpm exec tsc --noEmitcd app/desktop/src-tauri; cargo checkpassed with the existingedge_manager.auto_restartdead-code warning.
- 2026-05-25:
AH-SR-016production CORS localhost-origin risk was mitigated in repo by removing localhost defaults from production examples/generator output, settingAGENTHUB_ENV=productionin 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-017admin pprof/metrics exposure risk was mitigated in repo by keeping the admin server on127.0.0.1, sharing one Basic Auth wrapper for pprof and metrics, and adding tests for unauthorized, wrong-password, and authorized access to/metricsand/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-014local Edge call boundary was mitigated in repo by adding optional--local-auth-token/AGENTHUB_EDGE_AUTH_TOKENenforcement for non-health Edge APIs, propagating the token through Desktop REST/WebSocket clients, and adding a tokenized client smoke path./v1/healthand CORS preflight stay unauthenticated for readiness and browser setup. - Fresh focused checks passed:
cd edge-server; go test ./internal/httpserver ./cmd/agenthub-edge -count=1cd edge-server; go test ./... -short -count=1cd hub-server; go test ./... -short -count=1cd app/desktop; pnpm vitest run src/__tests__/edgeClient.test.ts src/__tests__/eventClient.test.tscd app/desktop; pnpm exec tsc --noEmit.\scripts\client-smoke.ps1 -EdgeAddr 127.0.0.1:3228 -EdgeAuthToken local-smoke-tokenpassed 23/23.
- 2026-05-25:
AH-SR-011public stats exposure was mitigated in repo by keeping/api/public/statsunauthenticated for the website but bucketizing public counts and uptime. The API still returns numerictotalUsers,totalAgents,onlineAgents, andtotalMessages, 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-015Edge 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/eventsstays long-lived. - Fresh focused checks passed:
cd edge-server; go test ./internal/httpserver -run "TestRESTTimeoutMiddleware" -count=1cd hub-server; go test ./... -short -count=1cd edge-server; go test ./... -short -count=1
- 2026-05-25:
AH-SR-002TokenDance bearer/device-type confusion was mitigated in repo by keeping TokenDance ID RS256 bearer validation as an identity compatibility path while tagging it astokendance_bearer; desktop-only Edge routes still require Hub-issued desktop device class throughDeviceTypeCheck("desktop"). - Fresh focused checks passed:
cd hub-server; go test ./internal/middleware -run "TestAuthMiddlewareTokenDanceBearerDoesNotSatisfyDesktopDeviceCheck|TestDeviceTypeCheck|TestAuthMiddleware" -count=1cd hub-server; go test ./internal/jwtutil ./internal/middleware -count=1
- 2026-05-25:
AH-SR-006WebSocket/REST auth drift was resolved as a documented boundary rather than by adding another TokenDance bearer path. Hub/client/wsaccepts Hub-local HS256 access tokens on the firstauthframe 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 -vcd hub-server; go test ./internal/handler -count=1
- 2026-05-25: Hub TokenDance ID OIDC exchange was implemented in repo.
/client/auth/oidc/authorizenow binds PKCE S256 state to a UUID Hub device proof,/client/auth/oidc/callbackexchanges code with TokenDance ID, validates ID token through the configured JWKS/issuer/audience path, mapstokendance_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=1cd hub-server; go test ./internal/handler -run "TestOIDCHandler_PostOIDC" -count=1cd hub-server; go test ./internal/config -run "TestEnvOverrideTokenDanceID" -count=1
- 2026-05-25:
AH-SR-003was mitigated in repo by making Hub login persist the caller device ID consistently indevices, access-token claims, and refresh-token rows; Desktop legacy login now uses the persistedagenthub_device_idUUID instead of generating a one-offdesktop_*string;getState()now returns a copy of the auth snapshot to avoid external state mutation. A follow-up real Postgres/Redis integration pass changeddevices(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-userdevice_idownership conflicts toBAD_REQUESTinstead ofINTERNAL_ERROR. - Fresh focused checks passed:
cd hub-server; go test ./internal/service -run "TestLogin_Success|TestLogout|TestRefreshToken_Success" -count=1cd hub-server; go test ./internal/repository -run TestDeviceRepo_Upsert -count=1 -vcd hub-server; AGENTHUB_DB_PORT=15432 AGENTHUB_REDIS_PORT=16380 AGENTHUB_JWT_SECRET=<test-secret> go test ./tests -run "TestLoginAllowsMultipleDesktopDevicesForSameUser|TestLoginRejectsDeviceIDOwnedByAnotherUser" -count=1 -vcd hub-server; go test ./internal/service -run TestDeviceRegisterMapsOwnershipMismatchToBadRequest -count=1 -vcd app/desktop; pnpm test -- src/__tests__/hubClient.test.ts
- 2026-05-25:
AH-SR-009attachment hash/header hardening was mitigated in repo by validating 64-char lowercase hex hashes before path derivation, makingPathFromHashreturn an empty path for malformed hashes, sniffing upload MIME metadata from staged file bytes instead of trusting multipart headers, normalizing downloadContent-Type, addingX-Content-Type-Options: nosniff, and formattingContent-Dispositionfrom a sanitized basename. - Fresh focused checks passed:
cd hub-server; go test ./internal/service -run TestPathFromHashValidatesHashShape -count=1cd hub-server; go test ./internal/handler -run "TestAttachment(UploadRejectsMalformedHashBeforePathDerivation|ProbeRejectsMalformedHashBeforeServiceLookup)" -count=1cd hub-server; go test ./internal/handler -run "TestAttachment(UploadSniffsMimeTypeInsteadOfTrustingMultipartHeader|DownloadFormatsUnsafeFilenameSafely)$" -count=1 -vcd hub-server; go test ./internal/service ./internal/handler -run "Test(PathFromHashValidatesHashShape|Attachment|AttachmentErrors)" -count=1
- 2026-05-25:
AH-SR-008dev compose exposure was mitigated in repo by making the default published host127.0.0.1for PostgreSQL, Redis, Hub API, and Hub admin/metrics..env.examplenow documentsAGENTHUB_BIND_HOST=127.0.0.1; remote dev requires an explicit opt-in such asAGENTHUB_BIND_HOST=0.0.0.0. - Fresh focused checks passed:
docker compose config --servicesdocker compose config
- 2026-05-25:
AH-SR-010Redis/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 existingApp.Runping; 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 -vcd hub-server; go test ./internal/service -count=1cd hub-server; go test ./... -short -count=1
- 2026-05-25:
AH-SR-005arbitrary 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 makingPOST /v1/permissions/decideone-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 -vpython -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-004remote Edge process-control exposure was mitigated in repo by rejecting wildcard, LAN, and non-loopback listen addresses in bothagenthub-edgeconfig parsing andhttpserver.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-018raw and structured output flood risk was mitigated in repo by adding a shared per-run byte limiter to EdgeProcessExecutorraw stdout/stderr capture and aPayloadLimitEmitterfor structured adapter events. Raw output truncates temp-file writes andrun.output.batchtext; structuredrun.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 -vcd edge-server; go test ./internal/lifecycle -run "TestProcessExecutorTruncatesStructuredAdapterPayload|TestProcessExecutorTruncatesRawOutputAtRunBudget|TestRunOutputLimiter" -count=1 -vcd edge-server; go test ./... -short -count=1
- 2026-05-25:
AH-SR-019was mitigated in repo by normalizing and validating Hubdevice_idvalues as UUIDs at/client/auth/loginand/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 -vcd 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 -vagainst temporary local Postgres/Redis created withdocker compose up -d postgres redis; containers and volumes were removed afterward withdocker compose down -v.
- 2026-05-25:
AH-SR-020was mitigated in repo by passing authenticated Edgedevice_idand callbackedge_run_idthrough all task callback handlers (ack,stream,done,fail). Online WebSocket dispatch and offline pending-task replay record the routed desktopedge_device_id, service methods reject wrong Edge user/device/run id before state changes or agent-message inserts, and Desktop forwards the Edgerun_idon 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 -vcd hub-server; AGENTHUB_DB_PORT=15432 AGENTHUB_REDIS_PORT=16380 AGENTHUB_JWT_SECRET=<test-secret> go test ./tests -run "TestEdgeTaskCallbacksRejectWrong(DesktopDevice|RunID)$" -count=1 -vagainst temporary local Postgres/Redis created withdocker compose up -d postgres redis.cd app/desktop; pnpm vitest run src/__tests__/useHubIntegration.test.tscd app/desktop; pnpm exec tsc --noEmitpython -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-021attachment probe/download scope was partially mitigated in repo by bindingProbeAttachmentandGetAttachmentByIDto 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 withO_EXCLafter 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 -vcd 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-022was mitigated in repo by changing pin creation to requireGetMessageBySessionAndID(sessionID, msgID)and changing pinned-list hydration toGetMessagesBySessionAndIDs(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 migration0039_message_pins_session_fkcleans historical cross-session pins and enforcesmessage_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 -vcd 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 -vagainst a temporary local Postgres database and Redis.- hk2 read-only schema verification:
schema_migrations=39|f,message_pinshasfk_message_pins_message_session, andcross_session_bad_pins=0. cd hub-server; go test ./internal/repository ./internal/service ./internal/handler -count=1cd hub-server; go test ./... -short -count=1
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- 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
Openstate block public release; see../docs/security/security-risk.mdfor release gates. - Production live endpoint, callback URL, client secret, session token, host path, backup path, and rotation evidence belong only in
C:\Users\Ding\serveror 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.
| 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 上游依赖,等待更新 |