Audit metadata
- Priority: P1
- Estimated effort: M
- Implementation risk: MED
- Category: security
- Evidence baseline:
07be5be7ce69bea0c3118744ab90d148b010fce0 (origin/main on 2026-07-17)
Dependencies
- P1 proposal 038 — Replace the deprecated unpinned GitHub MCP package. Included in the current P1 issue wave.
Description and impact
Connector responses decide whether to mask a value from its key name. Credentials named Cookie, SESSION, CREDENTIAL, or a vendor-specific header are returned in plaintext to the renderer/API caller. Credentials embedded in connector URLs also bypass masking. Treat every environment/header value as secret by default, model masking explicitly, and reject credential-bearing URLs.
Existing issue overlap: #200 is Windows stdio transport portability, not secret classification or API disclosure.
Current state and reproduction
services/agent-runtime/src/connectors-service.ts:24-25,170-191 masks only names matching /token|key|secret|password|auth/i.
connector-contract.ts:18-38 models arbitrary string records and one flat secret_keys list.
frontend/src/app/api/agent/connectors/route.ts:18-23 returns toConnectorView directly.
connectors-service.test.ts:18-51 covers only an obvious token name and masked round-trip.
Save headers: { Cookie: <synthetic sentinel> } or env: { CREDENTIAL: <sentinel> }, then GET connectors; the sentinel appears in the response. A URL containing userinfo is also returned through the view.
Solution design
Remove name heuristics. Persist internal flat string records for MCP compatibility, but expose a view where every env/header value is the mask token and secret metadata is structured by location (env vs headers). On upsert, a mask token preserves only an existing value at the same location/key; if none exists, reject rather than storing the mask literal. Validate HTTP URLs with Effect Schema and reject nonempty username/password without echoing the URL. Keep connector files owner-only; encryption/vault migration is a separate issue. Add no source comments.
Verification commands
| Purpose |
Command |
Expected on success |
| Service tests |
cd frontend && bun test ../tests/frontend/agent-runtime/connectors-service.test.ts |
exit 0 |
| Route tests |
cd frontend && bun test ../tests/frontend/regression/connector-secrets-route.test.ts |
exit 0 |
| Runtime build |
npm --prefix services/agent-runtime run build |
exit 0 |
| Frontend quality |
npm --prefix frontend run check:quality |
exit 0 |
| Full integration |
npm run test:integration |
exit 0 |
Scope
In scope: connector contract/service, connector POST/GET route, connector settings only as required by the changed view schema, and focused service/route tests.
Out of scope: encrypting or vaulting stdio secrets, OAuth vault changes, changing the connector permission/approval design established by audit proposal 021, plugin trust, MCP process environments, and rotating existing credentials.
Implementation plan
Step 1: Add non-obvious secret disclosure tests
Use distinct synthetic sentinels under Cookie, SESSION, CREDENTIAL, custom env/header names, and obvious token names. Assert no GET/upsert response contains any sentinel. Add URL-userinfo and new-mask-without-stored-value cases.
Verify: non-obvious names and URL credentials fail on baseline.
Step 2: Make the view schema explicit and default-secret
Change ConnectorViewSchema so secret-key metadata distinguishes env/header keys and every corresponding view value is masked. Remove SECRET_KEY_PATTERN; keep one exported mask semantic through the service contract rather than duplicating literals in UI.
Verify: service tests pass and serialized views contain zero raw env/header values.
Step 3: Make masked updates safe
Merge by location/key. Preserve an existing value only for the exact mask token; reject a masked new key and prevent a mask placeholder from reaching connectors.json or a child process. Verify toggle/rename round-trips keep all stored values.
Verify: service and route tests prove stored sentinels remain internally while responses stay masked.
Step 4: Reject credential-bearing URLs
Add an Effect Schema refinement/helper used by both persisted config decoding and API upsert. Return a generic 400/409 without reflecting the URL. Do not guess how to migrate URL userinfo into headers.
Verify: URL tests pass; standard http/https connector URLs remain valid.
Step 5: Verify and reinstall desktop
Run runtime/frontend/integration gates, desktop:dist, the canonical AGENTS.md reinstall/relaunch, and desktop-health 200 check.
Verify: all gates and installed health succeed.
Test plan
Extend connectors-service.test.ts for storage/merge and add a route-level serialization test. Search raw response text for each sentinel. Cover env/header same key, deletion/omission, toggle with masks, missing stored value, managed connectors, URL userinfo, and invalid persisted config errors that do not echo credentials.
Acceptance criteria
Risks and stop conditions
- A supported connector requires URL userinfo and cannot move credentials to headers/env/OAuth; report it for a migration design.
- Existing persisted URL credentials would be destroyed or echoed by automatic migration; fail closed and request operator action.
- The change expands into encryption/key management or connector permission design.
- A test would use or print a real credential.
- Source drift or two verification failures occur.
Maintenance considerations
Secret classification must be explicit and default-deny; key names are never evidence of sensitivity. Review future connector fields for response redaction and masked-update semantics before exposing them to the renderer.
Generated from Local Studio 2.0 main-branch audit proposal 050.
Audit metadata
07be5be7ce69bea0c3118744ab90d148b010fce0(origin/mainon 2026-07-17)Dependencies
Description and impact
Connector responses decide whether to mask a value from its key name. Credentials named
Cookie,SESSION,CREDENTIAL, or a vendor-specific header are returned in plaintext to the renderer/API caller. Credentials embedded in connector URLs also bypass masking. Treat every environment/header value as secret by default, model masking explicitly, and reject credential-bearing URLs.Existing issue overlap: #200 is Windows stdio transport portability, not secret classification or API disclosure.
Current state and reproduction
services/agent-runtime/src/connectors-service.ts:24-25,170-191masks only names matching/token|key|secret|password|auth/i.connector-contract.ts:18-38models arbitrary string records and one flatsecret_keyslist.frontend/src/app/api/agent/connectors/route.ts:18-23returnstoConnectorViewdirectly.connectors-service.test.ts:18-51covers only an obvious token name and masked round-trip.Save
headers: { Cookie: <synthetic sentinel> }orenv: { CREDENTIAL: <sentinel> }, then GET connectors; the sentinel appears in the response. A URL containing userinfo is also returned through the view.Solution design
Remove name heuristics. Persist internal flat string records for MCP compatibility, but expose a view where every env/header value is the mask token and secret metadata is structured by location (
envvsheaders). On upsert, a mask token preserves only an existing value at the same location/key; if none exists, reject rather than storing the mask literal. Validate HTTP URLs with Effect Schema and reject nonempty username/password without echoing the URL. Keep connector files owner-only; encryption/vault migration is a separate issue. Add no source comments.Verification commands
cd frontend && bun test ../tests/frontend/agent-runtime/connectors-service.test.tscd frontend && bun test ../tests/frontend/regression/connector-secrets-route.test.tsnpm --prefix services/agent-runtime run buildnpm --prefix frontend run check:qualitynpm run test:integrationScope
In scope: connector contract/service, connector POST/GET route, connector settings only as required by the changed view schema, and focused service/route tests.
Out of scope: encrypting or vaulting stdio secrets, OAuth vault changes, changing the connector permission/approval design established by audit proposal 021, plugin trust, MCP process environments, and rotating existing credentials.
Implementation plan
Step 1: Add non-obvious secret disclosure tests
Use distinct synthetic sentinels under
Cookie,SESSION,CREDENTIAL, custom env/header names, and obvious token names. Assert no GET/upsert response contains any sentinel. Add URL-userinfo and new-mask-without-stored-value cases.Verify: non-obvious names and URL credentials fail on baseline.
Step 2: Make the view schema explicit and default-secret
Change
ConnectorViewSchemaso secret-key metadata distinguishes env/header keys and every corresponding view value is masked. RemoveSECRET_KEY_PATTERN; keep one exported mask semantic through the service contract rather than duplicating literals in UI.Verify: service tests pass and serialized views contain zero raw env/header values.
Step 3: Make masked updates safe
Merge by location/key. Preserve an existing value only for the exact mask token; reject a masked new key and prevent a mask placeholder from reaching
connectors.jsonor a child process. Verify toggle/rename round-trips keep all stored values.Verify: service and route tests prove stored sentinels remain internally while responses stay masked.
Step 4: Reject credential-bearing URLs
Add an Effect Schema refinement/helper used by both persisted config decoding and API upsert. Return a generic 400/409 without reflecting the URL. Do not guess how to migrate URL userinfo into headers.
Verify: URL tests pass; standard http/https connector URLs remain valid.
Step 5: Verify and reinstall desktop
Run runtime/frontend/integration gates,
desktop:dist, the canonical AGENTS.md reinstall/relaunch, and desktop-health 200 check.Verify: all gates and installed health succeed.
Test plan
Extend
connectors-service.test.tsfor storage/merge and add a route-level serialization test. Search raw response text for each sentinel. Cover env/header same key, deletion/omission, toggle with masks, missing stored value, managed connectors, URL userinfo, and invalid persisted config errors that do not echo credentials.Acceptance criteria
Risks and stop conditions
Maintenance considerations
Secret classification must be explicit and default-deny; key names are never evidence of sensitivity. Review future connector fields for response redaction and masked-update semantics before exposing them to the renderer.
Generated from Local Studio 2.0 main-branch audit proposal 050.