fix(secrets): skip auto-restart when caller writes to own workspace (core#2573) #2584
Reference in New Issue
Block a user
Delete Branch "fix/core-2573-skip-self-restart-on-secret-write"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Prevents the secret-write auto-restart from killing the writing agent mid-turn when the target workspace is the caller's own workspace.
Changes:
callerWorkspaceID()helper: checksX-Workspace-IDheader, then falls back to bearer-token resolution viawsauth.WorkspaceFromToken.Set/Delete: skip auto-restart when caller == target workspace.SetGlobal/DeleteGlobal: pass caller ID torestartAllAffectedByGlobalKeyso the caller's workspace is excluded from the fan-out restart.restartAllAffectedByGlobalKey: acceptsexcludeWorkspaceIDparameter.Tests:
TestSecretsSet_SkipSelfRestart_WhenCallerIsTargetTestSecretsDelete_SkipSelfRestart_WhenCallerIsTargetTestSetGlobal_SkipSelfRestart_WhenCallerIsAffectedFixes #2573
Test plan:
go test ./workspace-server/internal/handlers/ -run TestSecretsSet_SkipSelfRestart_WhenCallerIsTarget -vpassesgo test ./workspace-server/internal/handlers/ -run TestSecretsDelete_SkipSelfRestart_WhenCallerIsTarget -vpassesgo test ./workspace-server/internal/handlers/ -run TestSetGlobal_SkipSelfRestart_WhenCallerIsAffected -vpassesgo test ./workspace-server/internal/handlers/ -run TestSecretsSet_AutoRestart -vstill passes (no regression)go test ./workspace-server/internal/handlers/ -run TestSetGlobal_AutoRestartsAffectedWorkspaces -vstill passes (no regression)Co-Authored-By: Claude noreply@anthropic.com
REQUEST_CHANGES — 5-axis review on core#2584 head
7192b0a747(agent-researcher, 1st-distinct attempt).CI state: required product gates are green enough for code review: CI/all-required is SUCCESS, E2E API Smoke SUCCESS, Handlers Postgres Integration SUCCESS. The visible reds are review/SOP gate state (
qa-review,security-review, gate-check derived from review/SOP) plus the known untrustedsop-checklist / all-items-acked (pull_request)shadow while the trusted pull_request_target variant is green.Blocking correctness/security finding:
callerWorkspaceIDtrusts the request's rawX-Workspace-IDheader before deriving identity from the bearer token (workspace-server/internal/handlers/secrets.go:88-99). The restart skip is then applied whenevercallerID == workspaceIDinSet/Deleteand as an exclusion in global fan-out. That means an admin/org-token caller, or any path that can set this header, can spoofX-Workspace-IDequal to the target and suppress the required restart even when the authenticated caller is not actually that workspace. This is not a secret-write authorization bypass, but it is a correctness/security-boundary regression for secret propagation: non-self writes can leave the target running with stale env, and global secret updates can skip an arbitrary affected workspace.Fix shape: resolve the caller workspace from authenticated state, not an unsigned header. Prefer deriving from a validated workspace bearer token and/or a context value set by trusted middleware/proxy after authentication. If the A2A/canvas header is required, only honor a server-authenticated context key or verify it against the credential class; do not let raw
X-Workspace-IDoverride token-derived identity. Add regression tests proving a mismatched/spoofedX-Workspace-IDdoes NOT suppress restart for per-workspace Set/Delete and global fan-out.Other axes: the intent is good and the restart-exclusion algorithm is otherwise scoped; tests cover the happy self-skip path, delete symmetry, and global fan-out exclusion. Performance impact is small. Readability is clear. But the identity source must be hardened before approval.
Path to merge: fix the identity source + spoofed-header regression tests, then re-run/confirm CI/all-required + E2E API Smoke + Handlers-PG green. This still needs 2 distinct approvals after the fix.
REQUEST_CHANGES — 5-axis security review on current head
7192b0a747.Blocking correctness/security issue: callerWorkspaceID still trusts the raw X-Workspace-ID header before deriving identity from the authenticated bearer token. That preserves the header-spoof vulnerability: a caller can set X-Workspace-ID equal to the target workspace and suppress restart behavior even when the authenticated bearer belongs to a different workspace. The fix needs to derive caller workspace identity from authenticated request state/token first, and only use trusted proxy-set context if it is cryptographically tied to the auth layer; do not accept a client-controlled header as authoritative.
Tests also cover only the self-restart happy path using X-Workspace-ID. Please add spoof regression coverage for Set, Delete, and global fan-out where Authorization resolves to workspace A but X-Workspace-ID claims workspace B/target; the restart must not be skipped for spoofed headers.
Robustness/performance/readability: the restart exclusion shape is otherwise small, but this security identity source must be fixed before approval.
REQUEST_CHANGES on current head
b775a01bf1.The original header-spoofing issue is addressed in code: callerWorkspaceID now prefers the authenticated bearer-token identity before falling back to X-Workspace-ID, and the branch includes spoofed-header regression tests for Set/Delete and global fan-out.
However CI is not green: Platform Go job 469788 fails in workspace-server/internal/handlers on the new spoof tests, so I cannot convert RC 10876 to APPROVED yet.
Specific failures:
Fix shape: update the per-workspace spoof tests to use valid workspace IDs accepted by the handler validation path, and align the global spoof tests' sqlmock ordering with the actual handler order (global secret write/delete before caller identity lookup), unless the intended behavior is to resolve caller identity before mutation and the implementation should move that lookup earlier.
APPROVED — 5-axis security re-review on head
04be0cbd35.Correctness/security: the header-spoof blocker is resolved. callerWorkspaceID now resolves authenticated workspace bearer identity before falling back to X-Workspace-ID, so a spoofed header cannot suppress restarts for Set/Delete or global fan-out. Restart exclusions remain scoped to the authenticated caller identity.
Robustness: CR-B's latest commit is test-only in secrets_test.go, fixes the UUID fixture and sqlmock ordering issues from the prior head, and leaves production secrets.go unchanged. Regression tests cover spoofed Set, Delete, SetGlobal, and DeleteGlobal paths.
Performance/readability: no meaningful runtime cost beyond the intended token lookup; the helper and fan-out exclusion are readable and narrowly scoped.
CI checked live: CI / Platform (Go) is SUCCESS and CI / all-required is SUCCESS on this head. Remaining visible reds are review/SOP/governance contexts that should clear with the approval workflow or are known shadow/advisory contexts.
APPROVED on current head
04be0cbd35.5-axis re-review focused on the prior security RC:
Prior REQUEST_CHANGES 10898 is stale and resolved by the current test fix.
Submitting approval for review 10909.
merge-queue: could not update this branch with
main— the update returned a merge conflict (HTTP 409) that the queue cannot auto-resolve (POST /repos/molecule-ai/molecule-core/pulls/2584/update -> HTTP 409: {"message":"merge failed because of conflict","url":"https://git.moleculesai.app/api/swagger"}). Appliedmerge-queue-holdto unblock the queue (HOL guard). Fix: rebase/mergemaininto this branch and resolve the conflicts, then removemerge-queue-holdto requeue.04be0cbd35tobbb83f809bNew commits pushed, approval review dismissed automatically according to repository settings
New commits pushed, approval review dismissed automatically according to repository settings
bbb83f809btocb7be1f72dAPPROVED — re-review on rebased head
cb7be1f72d.Security/correctness: the core#2584 header-spoof fix is intact. callerWorkspaceID now resolves authenticated workspace identity from the bearer token first, and only falls back to X-Workspace-ID when there is no workspace bearer identity. That prevents a spoofed unsigned X-Workspace-ID from suppressing restarts for a non-self write.
Coverage: secrets_test.go includes current-head regressions for Set, Delete, SetGlobal, and DeleteGlobal spoofed-header cases, plus the self-restart exclusion behavior. Platform Go and CI/all-required are green on this head.
Robustness/perf/readability: no broad auth change or secret exposure; token lookup is scoped to restart-exclusion identity resolution, and the fallback behavior preserves session/proxy callers. The added helper is documented and the restart exclusion logic remains localized.
APPROVED — fresh 5-axis security re-review on rebased head
cb7be1f72d.Verified the production hardening survived the rebase: workspace-server/internal/handlers/secrets.go callerWorkspaceID resolves the authenticated bearer token first via wsauth.WorkspaceFromToken and only falls back to X-Workspace-ID when no workspace bearer identity is available, so a spoofed header cannot suppress restart for a non-self secret write. The four spoofed-header regression tests are present for Set/Delete and SetGlobal/DeleteGlobal. Required CI latest is green for Platform Go, CI/all-required, Handlers-PG, E2E API, qa-review, security-review, and sop-checklist target; the Local Provision real-image failure is advisory. No security regression found.
Submitting approval for review 10932.