feat(#770): system settings surface allowlist + fail-closed editability#1081
feat(#770): system settings surface allowlist + fail-closed editability#1081mindfn wants to merge 6 commits into
Conversation
f6ed3ad to
f9a181b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6a0038ec82
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| excludeCategories ? data.variables.filter((v) => !excludeCategories.includes(v.category)) : data.variables | ||
| } | ||
| categories={visibleCategories} | ||
| variables={data.variables} |
There was a problem hiding this comment.
Preserve the system filter after env saves
When this component is rendered with surface="system", the initial GET is filtered, but a successful save replaces data.variables with /api/config/env's summary, which the route returns from buildEnvSummary() (the full hub-visible registry). Because this line then renders data.variables directly, editing any system variable such as FRONTEND_URL causes the System Settings page to repopulate with non-system variables (connector/evidence/telemetry and sensitive-editable entries), bypassing the new 23-variable surface allowlist until reload.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 8067d5e17. After successful PATCH, the save handler now re-fetches with ?surface=system instead of using body.summary (which is unfiltered buildEnvSummary()). Fallback path uses the already-filtered data.variables for optimistic update, never touching body.summary.
Regression test added: "System surface is preserved after save" — verifies non-system vars don't leak when PATCH returns full summary.
|
Responsible triage for reconciliation: This PR is not merge-ready as-is. Current state checked on 2026-07-07:
Please refresh this into a clean #770 PR before review/merge:
The feature direction matches #770, but the current PR shape is not clean enough to merge. [砚砚/GPT-5.5🐾] |
…itability Reduce env-registry.ts from 213 to 65 definitions (1930→735 lines). Only vars needed by the Settings UI or actively consumed remain. - Add SYSTEM_VARS ReadonlySet (23 platform vars) for System page - Add buildSystemEnvSummary() filtering by SYSTEM_VARS - Add ?surface=system query param to GET /api/config/env-summary - Change isEditableEnvVar() to fail-closed (runtimeEditable === true) - Add restartRequired metadata for accurate UI hints - Lock filesystem boundary vars (PROJECT_ALLOWED_ROOTS) as read-only - Lock startup-bound vars (ports, MEMORY_STORE) as read-only - Preserve F102 evidence toggle runtime editability - Remove 12 unused EnvCategory values, keep 8 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Frontend no longer filters env vars client-side — backend does it all. - HubEnvFilesTab: remove filterVariables/deriveCategories, add surface prop, fetch ?surface=system when surface is set - SettingsContent: use <HubEnvFilesTab surface="system" /> - EnvSubComponents: remove settingsSurface/settingsVisibility from EnvVar interface, keep fail-closed isEditableVariable() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…egistry - env-registry.test.js: 51 tests covering SYSTEM_VARS exact set, no-connector-leak, fail-closed write guard, P1/P2 lockdowns, F102 toggle preservation, restartRequired, buildSystemEnvSummary - config-event-bus.test.js: switch PATCH target from read-only PREVIEW_GATEWAY_PORT to runtimeEditable FRONTEND_URL - hub-env-files-tab.test.tsx: add surface="system" mock/test, remove settingsSurface/settingsVisibility from mock data - check-env-registry.test.mjs: add 107 vars to ALLOWLIST (internal infra / per-invocation / provider / tuning / credentials) - check-env-example.test.mjs: add 24 vars to EXAMPLE_ALLOWLIST (pruned from registry but still in .env.example) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests referencing deleted vars (OPENAI_API_KEY, KIMI_CONFIG_FILE, KIMI_QUOTA_API_FALLBACK_ENABLED, CAT_TEMPLATE_PATH, NEXT_PUBLIC_*) updated to use vars that remain in the registry. The PATCH route tests still verify rejection of unknown/non-editable var names. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P1-1: After successful PATCH, re-fetch with ?surface=system instead of using body.summary (which is unfiltered buildEnvSummary()). This prevents non-system vars from leaking onto the System Settings page after save. Rebase: resolve env-registry.ts conflicts with upstream/main (4 new commits including zts212653#1099 ACP credential refresh). Add CAT_CAFE_CREDENTIAL_FILE and CAT_CAFE_MCP_CREDS_DIR to check-env-registry ALLOWLIST. Red→Green: hub-env-files-tab.test.tsx regression test verifies System surface is preserved after save even when PATCH returns full summary. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6a0038e to
8067d5e
Compare
|
All issues addressed in
Commits (upstream/main..HEAD): Local gates: |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8067d5e171
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| category: 'evidence', | ||
| sensitive: false, | ||
| allowedValues: ['off', 'shadow', 'on'], | ||
| runtimeEditable: false, |
There was a problem hiding this comment.
Don't expose EMBED_MODE as an unsavable toggle
When EMBED_MODE is returned by /api/config/env-summary, IndexStatus.filterEvidenceVars still renders evidence vars with defaultValue === 'off' as clickable feature toggles and cycleEnvVar PATCHes them to /api/config/env. With this new runtimeEditable: false, the route now rejects that PATCH because isEditableEnvVarName only accepts explicit runtimeEditable: true, so the Memory page shows an EMBED_MODE control that never saves and the failure is swallowed. Either make it editable with restartRequired or keep it out of the toggle UI/read-only summary.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 022a95b50. Changed EMBED_MODE to runtimeEditable: true, restartRequired: true.
Root cause: EMBED_MODE is startup-captured (resolvedEmbedMode in index.ts) — env var changes don't take effect until restart. #770 marked it runtimeEditable: false (fail-closed), but the Memory page's filterEvidenceVars still renders it as a clickable toggle. Clicking silently failed because the fail-closed guard rejected the PATCH.
Fix: Same pattern as FRONTEND_URL — the value CAN be written to .env from Hub (persists for next startup), and restartRequired: true signals the UI to show a restart hint. Test updated to assert both flags.
EMBED_MODE was startup-captured (resolvedEmbedMode) so zts212653#770 marked it runtimeEditable: false. But filterEvidenceVars on the Memory page still rendered it as a clickable toggle — clicking silently failed because the fail-closed guard rejected the PATCH. Fix: runtimeEditable: true + restartRequired: true — the value CAN be written to .env from Hub (persists for next startup), which is the same pattern as FRONTEND_URL and other restart-bound vars. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Closes #770 — System Settings 页面从暴露 ~171 个 env var 收敛到 23 个平台级配置。
Changes
Backend (env-registry.ts)
SYSTEM_VARShardcodedReadonlySet<string>(23 platform vars)buildSystemEnvSummary()filtering bySYSTEM_VARS?surface=systemquery param toGET /api/config/env-summaryisEditableEnvVar()to fail-closed (runtimeEditable === true)restartRequiredmetadata for accurate UI hintsPROJECT_ALLOWED_ROOTSfamily) as read-only (P1 security)MEMORY_STORE) as read-only (P2 correctness)EnvCategoryvalues, keep 8Frontend
HubEnvFilesTab: remove client-side filtering, addsurfacepropSettingsContent: use<HubEnvFilesTab surface="system" />EnvSubComponents: removesettingsSurface/settingsVisibility, keep fail-closedTests & CI checks
Test plan
pnpm check— all gates passnode --test packages/api/test/env-registry.test.js— 51/51 passnode --test packages/api/test/config-event-bus.test.js— 13/13 passvitest run src/components/__tests__/hub-env-files-tab.test.tsx(from packages/web/) — 6/6 pass🐾 布偶猫/宪宪 [Claude Opus 4.6]