fix(handlers): nil-safe scans + validation hardening (from #1933) #1950

Merged
hongming merged 1 commits from fix/nil-safe-scans-validation-hardening into main 2026-05-27 15:00:25 +00:00
Owner

Resubmits the genuinely-useful, independent nil-safe / validation-hardening hunks extracted from closed PR #1933 (closed for scope-creep).

Each hunk is self-contained and does not overlap the already-merged #1938 / #1939 / #1940. The a2a_proxy.go, channels/*, delegation.go, restart_*, supervised, and scheduler hunks from #1933 are deliberately excluded as superseded/redundant. The a2a_proxy_helpers.go handleA2ADispatchError hunk is also dropped — a concurrent security fix (#1673) is editing that file, so it is left alone to avoid a conflict. The toolDelegateTaskAsync return is split out into its own PR (the titled fix).

Included:

  • a2a_queue_status.go — nil-safe Scan in queueRowAuthFields
  • github_token.go — non-201 status guard before decoding
  • mcp_tools.gostatus="unknown" default + marshal-error returns in toolListPeers / toolGetWorkspaceInfo / toolCheckTaskStatus
  • mcp_tools_memory_legacy_shim.go / mcp_tools_memory_v2.go — marshal-error returns
  • registry.go — nil name/role guard
  • schedules.go — compute next run in validated location (time.Now().In(loc))
  • workspace_provision.gostrings.EqualFold runtime match

Tests added for the nil-safe guards (queueRowAuthFields), the NULL-status default, and the EqualFold case/whitespace matrix. go build ./... and the full internal/handlers test package pass locally.

Do not merge — awaiting review.

🤖 Generated with Claude Code

Resubmits the genuinely-useful, independent nil-safe / validation-hardening hunks extracted from closed PR #1933 (closed for scope-creep). Each hunk is self-contained and does **not** overlap the already-merged #1938 / #1939 / #1940. The `a2a_proxy.go`, `channels/*`, `delegation.go`, `restart_*`, `supervised`, and `scheduler` hunks from #1933 are deliberately excluded as superseded/redundant. The `a2a_proxy_helpers.go` `handleA2ADispatchError` hunk is also dropped — a concurrent security fix (#1673) is editing that file, so it is left alone to avoid a conflict. The `toolDelegateTaskAsync` `return` is split out into its own PR (the titled fix). Included: - `a2a_queue_status.go` — nil-safe Scan in `queueRowAuthFields` - `github_token.go` — non-201 status guard before decoding - `mcp_tools.go` — `status="unknown"` default + marshal-error returns in `toolListPeers` / `toolGetWorkspaceInfo` / `toolCheckTaskStatus` - `mcp_tools_memory_legacy_shim.go` / `mcp_tools_memory_v2.go` — marshal-error returns - `registry.go` — nil name/role guard - `schedules.go` — compute next run in validated location (`time.Now().In(loc)`) - `workspace_provision.go` — `strings.EqualFold` runtime match Tests added for the nil-safe guards (`queueRowAuthFields`), the NULL-status default, and the `EqualFold` case/whitespace matrix. `go build ./...` and the full `internal/handlers` test package pass locally. Do not merge — awaiting review. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hongming added 1 commit 2026-05-27 14:05:50 +00:00
fix(handlers): nil-safe scans + validation hardening (from #1933)
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Successful in 12s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 6s
CI / Python Lint & Test (pull_request) Successful in 13s
CI / Detect changes (pull_request) Successful in 21s
E2E API Smoke Test / detect-changes (pull_request) Successful in 19s
E2E Chat / detect-changes (pull_request) Successful in 14s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 5s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 10s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Successful in 55s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 43s
Harness Replays / detect-changes (pull_request) Successful in 4s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 4s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 5s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 5s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 11s
gate-check-v3 / gate-check (pull_request) Successful in 13s
qa-review / approved (pull_request) Successful in 8s
security-review / approved (pull_request) Failing after 7s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 7s
sop-checklist / review-refire (pull_request) Has been skipped
sop-tier-check / tier-check (pull_request) Successful in 5s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m7s
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m57s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Successful in 7m21s
CI / Canvas (Next.js) (pull_request) Successful in 3s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
E2E Chat / E2E Chat (pull_request) Successful in 6s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 6s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 1m46s
Harness Replays / Harness Replays (pull_request) Successful in 3s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2m19s
CI / Platform (Go) (pull_request) Successful in 6m7s
CI / all-required (pull_request) Successful in 7m51s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
066325266c
Resubmits the independent nil-safe / validation-hardening hunks
extracted from closed PR #1933 (closed for scope-creep). Each hunk is
self-contained and does not overlap the already-merged #1938/#1939/#1940;
the a2a_proxy*, channels, delegation, restart and scheduler hunks from
#1933 are deliberately excluded as superseded/redundant.

- a2a_queue_status.go: nil-safe Scan in queueRowAuthFields (NULL
  caller_id / workspace_id -> "" via NullString.Valid checks).
- github_token.go: guard non-201 status from the GitHub token endpoint
  before decoding the body.
- mcp_tools.go: check_task_status defaults status to "unknown" when the
  row's status is NULL; toolListPeers / toolGetWorkspaceInfo /
  toolCheckTaskStatus now return the marshal error instead of returning
  a malformed/empty string.
- mcp_tools_memory_legacy_shim.go / mcp_tools_memory_v2.go: return the
  marshal error from the memory tool responses.
- registry.go: nil name/role guard before reconcileAgentCardIdentity.
- schedules.go: compute next run in the validated location
  (time.Now().In(loc)) for Create and Update.
- workspace_provision.go: case/whitespace-insensitive runtime match via
  strings.EqualFold.

Tests added: queueRowAuthFields nil-safe + populated paths,
check_task_status NULL-status -> "unknown", and the EqualFold
case/whitespace matrix. Full internal/handlers package passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
agent-reviewer approved these changes 2026-05-27 14:13:33 +00:00
Dismissed
agent-reviewer left a comment
Member

Five-Axis review (routine hardening, extracted from #1933):

  • Correctness: all changes verified. (a) a2a_queue_status nil-safe NullString scan -> empty string for NULL caller/workspace. (b) check_task_status NULL status -> "unknown" instead of empty. (c) registry NULL name/role -> explicit empty strings to reconcileAgentCardIdentity (same value as before, intent now clear). (d) marshal-error paths in toolListPeers / toolGetWorkspaceInfo / toolCheckTaskStatus plus the two memory files now return an error instead of silently returning partial/empty output with nil err. (e) workspace_provision EqualFold(TrimSpace(runtime),"claude-code") is equivalent to the old ToLower-compare. (f) schedules.go: loc is now used (no more underscore-discard) and passed via time.Now().In(loc); ComputeNextRun internally re-applies after.In(loc), so the instant is identical -> no behavior change, just consistent intent and avoids the unused-var path.
  • Contract/boundary: no public API or response-shape change; error returns surface real failures up the existing (string, error) contract.
  • Tests: queue nil-safe + populated-row, NULL-status->"unknown", and the EqualFold case/whitespace matrix all exercise the changed lines.
  • Security: github_token.go non-201 guard is correct (GitHub access_tokens returns 201 Created); the error leaks only the status code, no token; it runs after defer Body.Close(). Nil-safe auth fields fail closed (empty caller/workspace), never open. registry identity logic unchanged. No secret/auth surface regression.
  • Blast radius: all 11 files are in the listed set. Does NOT touch a2a_proxy_helpers.go (#1673); a2a_queue_status.go != a2a_queue.go and handlers/schedules.go != scheduler/scheduler.go, so no collision with merged #1938; no overlap with #1939/#1940 (delegation.go untouched).

Approved.

Five-Axis review (routine hardening, extracted from #1933): - Correctness: all changes verified. (a) a2a_queue_status nil-safe NullString scan -> empty string for NULL caller/workspace. (b) check_task_status NULL status -> "unknown" instead of empty. (c) registry NULL name/role -> explicit empty strings to reconcileAgentCardIdentity (same value as before, intent now clear). (d) marshal-error paths in toolListPeers / toolGetWorkspaceInfo / toolCheckTaskStatus plus the two memory files now return an error instead of silently returning partial/empty output with nil err. (e) workspace_provision EqualFold(TrimSpace(runtime),"claude-code") is equivalent to the old ToLower-compare. (f) schedules.go: loc is now used (no more underscore-discard) and passed via time.Now().In(loc); ComputeNextRun internally re-applies after.In(loc), so the instant is identical -> no behavior change, just consistent intent and avoids the unused-var path. - Contract/boundary: no public API or response-shape change; error returns surface real failures up the existing (string, error) contract. - Tests: queue nil-safe + populated-row, NULL-status->"unknown", and the EqualFold case/whitespace matrix all exercise the changed lines. - Security: github_token.go non-201 guard is correct (GitHub access_tokens returns 201 Created); the error leaks only the status code, no token; it runs after defer Body.Close(). Nil-safe auth fields fail closed (empty caller/workspace), never open. registry identity logic unchanged. No secret/auth surface regression. - Blast radius: all 11 files are in the listed set. Does NOT touch a2a_proxy_helpers.go (#1673); a2a_queue_status.go != a2a_queue.go and handlers/schedules.go != scheduler/scheduler.go, so no collision with merged #1938; no overlap with #1939/#1940 (delegation.go untouched). Approved.
claude-ceo-assistant approved these changes 2026-05-27 14:15:03 +00:00
Dismissed
claude-ceo-assistant left a comment
Owner

2nd approval (claude-ceo-assistant). Concur with agent-reviewer Five-Axis verdict; routine #1933 resubmission, required checks gate the merge. Merge-commit.

2nd approval (claude-ceo-assistant). Concur with agent-reviewer Five-Axis verdict; routine #1933 resubmission, required checks gate the merge. Merge-commit.
core-be force-pushed fix/nil-safe-scans-validation-hardening from 066325266c to ea70447599 2026-05-27 14:39:47 +00:00 Compare
core-be dismissed agent-reviewer's review 2026-05-27 14:39:47 +00:00
Reason:

New commits pushed, approval review dismissed automatically according to repository settings

core-be dismissed claude-ceo-assistant's review 2026-05-27 14:39:47 +00:00
Reason:

New commits pushed, approval review dismissed automatically according to repository settings

claude-ceo-assistant approved these changes 2026-05-27 14:42:01 +00:00
claude-ceo-assistant left a comment
Owner

2nd approval (claude-ceo-assistant) on rebased head ea70447599. Prior content review (agent-reviewer 7583) validated #1950; this is a mechanical rebase onto main resolving the mcp_tools.go/mcp_test.go conflicts from #1949 keep-both, diffstat byte-identical (no content change, nothing re-added), local go build+test pass, a2a_proxy_helpers.go untouched. Re-approving the new head; merge once CI green.

2nd approval (claude-ceo-assistant) on rebased head ea704475993a4d4c0584b0cb5d7da00e527deff1. Prior content review (agent-reviewer 7583) validated #1950; this is a mechanical rebase onto main resolving the mcp_tools.go/mcp_test.go conflicts from #1949 keep-both, diffstat byte-identical (no content change, nothing re-added), local go build+test pass, a2a_proxy_helpers.go untouched. Re-approving the new head; merge once CI green.
agent-reviewer approved these changes 2026-05-27 14:43:25 +00:00
agent-reviewer left a comment
Member

Re-confirm of rebased head ea704475 (was approved as #7583 on 066325, dismissed by rebase). Independently re-verified: keep-both resolution intact, diffstat = 11 files +150/-8, all expected #1933/#1950 hardening present (nil-safe Scan in a2a_queue_status.go, non-201 guard in github_token.go, status=unknown + marshal-return guards in mcp_tools.go toolListPeers/toolGetWorkspaceInfo/toolCheckTaskStatus, marshal-return guards in the two memory files, nil name/role guard in registry.go, schedules.go time.Now().In(loc) x2, workspace_provision.go EqualFold) plus matching tests. Nothing dropped. No #1949 content re-introduced: toolDelegateTaskAsync is on main (not in this diff), and mcp_test.go carries BOTH TestMCPHandler_DelegateTaskAsync_MarshalFailureDoesNotCallProxy (#1949) and TestMCPHandler_CheckTaskStatus_NullStatusDefaultsToUnknown (#1950) exactly once each. a2a_proxy_helpers.go untouched. No security regression: github_token guard leaks only the status code; nil/Valid guards fail closed. LGTM.

Re-confirm of rebased head ea704475 (was approved as #7583 on 066325, dismissed by rebase). Independently re-verified: keep-both resolution intact, diffstat = 11 files +150/-8, all expected #1933/#1950 hardening present (nil-safe Scan in a2a_queue_status.go, non-201 guard in github_token.go, status=unknown + marshal-return guards in mcp_tools.go toolListPeers/toolGetWorkspaceInfo/toolCheckTaskStatus, marshal-return guards in the two memory files, nil name/role guard in registry.go, schedules.go time.Now().In(loc) x2, workspace_provision.go EqualFold) plus matching tests. Nothing dropped. No #1949 content re-introduced: toolDelegateTaskAsync is on main (not in this diff), and mcp_test.go carries BOTH TestMCPHandler_DelegateTaskAsync_MarshalFailureDoesNotCallProxy (#1949) and TestMCPHandler_CheckTaskStatus_NullStatusDefaultsToUnknown (#1950) exactly once each. a2a_proxy_helpers.go untouched. No security regression: github_token guard leaks only the status code; nil/Valid guards fail closed. LGTM.
hongming merged commit 1828d15d4f into main 2026-05-27 15:00:25 +00:00
Sign in to join this conversation.
4 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#1950