Skip to content

hub: fix and improve hub security and redaction from the October 2026 audit #327

Closed as duplicate

Description

@erkamyaman

Context

This comes from a code audit of the hub, the Vite plugin gate, serialize and the redaction helpers, done during a comparison with other devtools in October 2026. Roadmap items live in #310, #311, #312 and #313. Items marked (to confirm) were not reproduced; every item below was checked against the code on main.

Problems

Medium

  • No remote access in the Vite plugin: isAllowedHubRequest refuses every peer that is not a loopback socket, even when allowedHosts, allowedOrigins and auth are set. Docker port publishing (peer 172.17.0.1), devcontainer or VM forwarding and a phone on the LAN always get 403. When auth is on, allow non-loopback peers whose Origin and Host pass the policy, or add an explicit allowRemote option, and say why in the 403 message (packages/devtools/src/vite.ts:151, 27-31).
  • serialize skips key=value masking: serialize and redactText use the forms-privacy redactMessage, which has no TEXT_PAIR/URL_PAIR masking and no session knownSecrets, unlike router.ts redactText. A string such as 'password=hunter2&user=a' or 'api_key=XYZ' in component state, signals or NgRx state goes out unmasked. Move the pair logic and knownSecrets into one shared module used by serialize, router and HTTP, and add a leak test for { note: 'token=abc123' } (packages/devtools/src/serialize.ts:1, 20-22, 89; packages/devtools/src/router.ts:223-296).
  • Prefixed secrets not detected by value: value-shape detection covers only JWT, Bearer, Basic, Digest and Token. Stripe sk_live_/rk_live_, GitHub ghp_/gho_/github_pat_, Slack xoxb-/xoxp-, AWS AKIA…, Google AIza…, PEM private-key blocks and Sentry DSNs (https://key@host, which USERINFO misses because it needs user:pass) pass through under neutral keys. Add a small table of high-confidence prefixes and a -----BEGIN … PRIVATE KEY----- pattern to redactCredentials (packages/devtools/src/forms-privacy.ts:179-187; packages/devtools/src/credential-redact.ts:9).

Low

  • Secret-name gaps and over-matches: SECRET_WORDS misses signature, sig, salt, nonce, privkey, dsn, refresh, sid, mfa, recovery and auth on its own. card and cc are whole words, so cardTitle, cardId and ccList (email CC) are masked. Move card and cc to pairs only (cardnumber, ccnum, cardcvc, ccexp), add the missing words, and add table-driven tests for false negatives and false positives (packages/devtools/src/forms-privacy.ts:24-65, 131-134).
  • One bad node hides the whole value: Object.keys(v) and the instanceof checks run outside any per-node try, so a revoked Proxy throws to the outer catch and the whole root becomes '[Unreadable]'. Property reads also go through getters on live page objects, which can run side effects. Wrap each node in try/catch, and read through Object.getOwnPropertyDescriptor, showing accessors as '[Getter]' (packages/devtools/src/serialize.ts:97-160).
  • Errors lose cause: an Error is reduced to name: message, so cause (often the failing request) is lost. Serialize cause one level deep through walk, redacted (packages/devtools/src/serialize.ts:100).
  • Custom origin registry drops the extension: an array allowedOrigins gets PANGULAR_EXTENSION_ORIGINS appended, but a custom WsOriginRegistry is used as is, so the published extension is refused, and allowedOrigins: false turns every check off with no log line. Wrap custom registries so they also accept the extension origins, and warn once when the check is off (packages/devtools/src/hub.ts:65-71).
  • Any loopback port is trusted: isAllowedHubOrigin accepts any http(s) origin whose hostname is loopback, so another local app on a different port can open the hub WebSocket and RPC. By default accept only the dev server's own origins plus the extension and configured origins, and treat other loopback ports as remote, which needs the token (packages/devtools/src/vite.ts:55).

Improvements

  • One redaction module (redactText, redactUrl, redactValue, isSecretKey) replacing the two redactMessage functions, plus a leak test that pushes a corpus of secrets (Stripe, GitHub, AWS, PEM, DSN, URL-encoded) through every collector's output path (forms-privacy.ts, router.ts, serialize.ts, http-redact.ts, credential-redact.ts, json-text-redact.ts, analog-redact.ts).
  • An opt-in mask for high-entropy strings of 32 or more characters under neutral keys, as Sentry and Datadog scrubbers do (credential-redact.ts, config.ts).
  • Redaction counts in the panel and in agent responses (for example "3 values masked: key, bearer, jwt"), so users can tell masking happened and find the unmask options (serialize.ts, app/src).
  • Log refused requests by peer and Host as well as Origin, and add a loopback-only security status endpoint the panel can show (vite.ts blockedOriginReporter, hub.ts).
  • A pangular token --rotate CLI command to rotate the MCP token on demand (hub.ts, CLI).

Already tracked elsewhere

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or shows wrong datafeatureA feature request or a pull request that adds one

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions