fix: ship all root-cause platform fixes (v2) - #1664
Closed
molecule-ai[bot] wants to merge 5 commits into
Closed
Conversation
Consolidates 5 recurring issues that required manual patching every 30-60 minutes into permanent fixes on main: 1. SSRF bypass for Docker-internal URLs (a2a_proxy.go) - 35/43 cron schedules were silently failing 2. Phantom-busy auto-sweeper (scheduler.go) - 10-min timeout auto-clears stuck active_tasks 3. Heartbeat status recovery (registry.go) - Auto-transitions "failed"→"online" on heartbeat 4. Docker socket GID detection (workspace-server/Dockerfile) - Platform can provision containers without root 5. Credential helper + token refresh daemon (workspace/) - Auto-refreshes GH tokens every 45 min, no manual restarts 6. IPv6 go vet fix (terminal.go) - net.JoinHostPort for IPv6-safe address formatting 7. Default tier T3 (workspace.go, org_import.go) Also includes canvas ContextMenu .some() fix and shared_runtime increment/decrement active_tasks. [Molecule-Platform-Evolvement-Manager] Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ssrf.go: add setSSRFCheckForTest() to disable SSRF for test URLs - handlers_test.go: setupTestDB disables SSRF check via t.Cleanup - workspace_test.go: expect tier=3 (matching new default) - handlers_additional_test.go: expect tier=3 Fixes 14 test failures caused by SSRF bypass + T3 default changes. [Molecule-Platform-Evolvement-Manager] Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[Molecule-Platform-Evolvement-Manager]
[Molecule-Platform-Evolvement-Manager]
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Apr 22, 2026
Contributor
|
Closing as superseded. After today's merge stream (PRs #1692, #1693, #1695, #1696, #1702, #1714), the majority of this PR's scope is already shipped or conflicts with the new state: Shipped elsewhere (conflict):
Unique & still valid — extract to focused PRs:
No CI ever ran, mergeStateStatus=DIRTY, 26 unrelated files bundled. Extracting the 6 valid changes into focused PRs is safer than rebasing this one through today's conflicts. |
auto-merge was automatically disabled
April 23, 2026 02:22
Pull request was closed
This was referenced Apr 23, 2026
HongmingWang-Rabbit
added a commit
that referenced
this pull request
Apr 23, 2026
chore: extract 3 small fixes from closed #1664
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…bypass + workspace-server Dockerfile GID entrypoint Three small, non-overlapping fixes extracted from closed PR #1664: 1. canvas/src/components/ContextMenu.tsx — Replace the useMemo-over-nodes pattern with a hashed-boolean selector (s.nodes.some(...)) so Zustand's useSyncExternalStore snapshot comparison is stable. Resolves React error #185 (infinite render loop). Moves the child-node list derivation into the delete handler via getState() so the render path no longer allocates a fresh array. 2. workspace-server/internal/handlers/a2a_proxy.go — Allow the Docker-bridge hostname path (ws-<id>:8000) to skip the SSRF guard in local-docker mode. Gated on !saasMode() so SaaS deployments keep the full private-IP blocklist (a remote workspace registration can't claim a ws-* hostname and reach a sensitive VPC IP). 3. workspace-server/Dockerfile — Add entrypoint.sh that discovers the docker.sock GID at boot and adds the platform user to that group, then exec's su-exec to drop privileges. Lets the platform container reach the host docker socket without running as root. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…ache Extracted from the now-closed PR #1664 (Molecule-AI/molecule-core). - New scripts/molecule-gh-token-refresh.sh background daemon — every 45 min (TOKEN_REFRESH_INTERVAL_SEC) calls the credential helper's _refresh_gh action to keep both gh CLI auth and the on-disk cache fresh through the GitHub App installation token's ~60 min TTL. - scripts/molecule-git-token-helper.sh rewritten with a ~50 min on-disk cache (${CACHE_DIR}/gh_installation_token + _expiry companion file), a cache > API > env-var fallback chain, a new _refresh_gh action (invoked by the daemon above), a _invalidate_cache action, and path references flipped from /workspace/scripts/... to /app/scripts/... to match the runtime image layout. - Dockerfile copies the new refresh daemon and extends mkdir to create /home/agent/.molecule-token-cache at build time. - entrypoint.sh configures the git credential helper for github.com while still root (so the global gitconfig is written before the gosu handoff), creates + chowns the token cache dir, then as agent starts the refresh daemon in the background and does an initial gh auth login from GITHUB_TOKEN/GH_TOKEN so gh works before the first refresh fires. Dropped from PR #1664: cosmetic em-dash -> ASCII hyphen rewrites (charset-normalizer noise) that would conflict with the repo's existing em-dash convention used elsewhere in workspace/.
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…shed runs (extracted from #1664) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…ful heartbeat (extracted from #1664) When a workspace is marked "failed" or "provisioning" but is actively sending heartbeats, transition it to "online". Transient boot failures or mid-setup provisioner crashes otherwise leave workspaces stuck in a stale terminal state even after they become healthy. Preserves existing online/degraded/offline transitions; only adds a new conditional branch for the failed/provisioning case with a guarded WHERE clause so a concurrent delete cannot flip 'removed' back to 'online'.
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
… loopback
Fixes 14 of the 18 failing tests that have been reddening Platform (Go)
CI on main since the 2026-04-18 open-source restructure + 2026-04-21
SSRF-backport. Reduces handlers package failure count 18 → 4
(remaining 4 are unrelated schema/behavior drift — see follow-ups).
Three root causes fixed:
1. httptest.NewServer binds to 127.0.0.1; isSafeURL rejects loopback.
Tests that stub workspace URLs via httptest therefore 502'd at
the SSRF guard before reaching the handler logic they wanted to
exercise.
Fix: add `testAllowLoopback` var to ssrf.go + `allowLoopbackForTest(t)`
helper in handlers_test.go. Only 127.0.0.0/8 and ::1 are relaxed;
169.254 metadata, RFC-1918, TEST-NET, CGNAT, and link-local
protections remain active. Flag is paired with t.Cleanup and is
never touched by production code.
2. ProxyA2A's checkWorkspaceBudget query (SELECT budget_limit, COALESCE
(monthly_spend, 0) FROM workspaces WHERE id = $1) was added with the
restructure but the a2a_proxy_test.go sqlmock expectations never
caught up, producing "call to Query ... was not expected" on every
ProxyA2A-exercising test.
Fix: `expectBudgetCheck(mock, workspaceID)` helper that registers
an empty-rows expectation (checkWorkspaceBudget fails-open on
sql.ErrNoRows, so an empty result = "no budget limit"). Added to
each of the 8 affected TestProxyA2A_* tests in the correct
position relative to access-control + activity-log expectations.
3. TestAdminMemories_Import_Success + _RedactsSecretsBeforeDedup
mocked a 5-arg INSERT when the handler actually issues a 4-arg
INSERT (workspace_id, content, scope, namespace) unless the
payload carries a created_at override. Removed the spurious 5th
AnyArg from both tests; _PreservesCreatedAt is untouched since it
legitimately uses the 5-arg form.
Also: TestResolveAgentURL_CacheHit and _CacheMissDBHit used bogus
`cached.example` / `dbhit.example` hostnames that fail DNS resolution
inside isSafeURL (which happens BEFORE the loopback check). Swapped to
`127.0.0.1` variants preserving test intent (they never hit the network).
Remaining 4 failures — out of scope for this PR, tracked separately:
- TestGitHubToken_NoTokenProvider (handler behavior drift — 500 vs 404)
- TestWorkspaceList + TestWorkspaceList_WithData (Scan arg count —
workspaces table gained a column, mock not updated)
- TestRegister_ProvisionerURLPreserved (request body shape drift)
Closes the 4 wrong-target PRs (#1710, #1718, #1719, #1664) that all
tried to silence the symptom by disabling golangci-lint — which has
`continue-on-error: true` in ci.yml and was never the actual blocker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…bypass + workspace-server Dockerfile GID entrypoint Three small, non-overlapping fixes extracted from closed PR #1664: 1. canvas/src/components/ContextMenu.tsx — Replace the useMemo-over-nodes pattern with a hashed-boolean selector (s.nodes.some(...)) so Zustand's useSyncExternalStore snapshot comparison is stable. Resolves React error #185 (infinite render loop). Moves the child-node list derivation into the delete handler via getState() so the render path no longer allocates a fresh array. 2. workspace-server/internal/handlers/a2a_proxy.go — Allow the Docker-bridge hostname path (ws-<id>:8000) to skip the SSRF guard in local-docker mode. Gated on !saasMode() so SaaS deployments keep the full private-IP blocklist (a remote workspace registration can't claim a ws-* hostname and reach a sensitive VPC IP). 3. workspace-server/Dockerfile — Add entrypoint.sh that discovers the docker.sock GID at boot and adds the platform user to that group, then exec's su-exec to drop privileges. Lets the platform container reach the host docker socket without running as root. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…ache Extracted from the now-closed PR #1664 (Molecule-AI/molecule-core). - New scripts/molecule-gh-token-refresh.sh background daemon — every 45 min (TOKEN_REFRESH_INTERVAL_SEC) calls the credential helper's _refresh_gh action to keep both gh CLI auth and the on-disk cache fresh through the GitHub App installation token's ~60 min TTL. - scripts/molecule-git-token-helper.sh rewritten with a ~50 min on-disk cache (${CACHE_DIR}/gh_installation_token + _expiry companion file), a cache > API > env-var fallback chain, a new _refresh_gh action (invoked by the daemon above), a _invalidate_cache action, and path references flipped from /workspace/scripts/... to /app/scripts/... to match the runtime image layout. - Dockerfile copies the new refresh daemon and extends mkdir to create /home/agent/.molecule-token-cache at build time. - entrypoint.sh configures the git credential helper for github.com while still root (so the global gitconfig is written before the gosu handoff), creates + chowns the token cache dir, then as agent starts the refresh daemon in the background and does an initial gh auth login from GITHUB_TOKEN/GH_TOKEN so gh works before the first refresh fires. Dropped from PR #1664: cosmetic em-dash -> ASCII hyphen rewrites (charset-normalizer noise) that would conflict with the repo's existing em-dash convention used elsewhere in workspace/.
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…shed runs (extracted from #1664) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…ful heartbeat (extracted from #1664) When a workspace is marked "failed" or "provisioning" but is actively sending heartbeats, transition it to "online". Transient boot failures or mid-setup provisioner crashes otherwise leave workspaces stuck in a stale terminal state even after they become healthy. Preserves existing online/degraded/offline transitions; only adds a new conditional branch for the failed/provisioning case with a guarded WHERE clause so a concurrent delete cannot flip 'removed' back to 'online'.
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
… loopback
Fixes 14 of the 18 failing tests that have been reddening Platform (Go)
CI on main since the 2026-04-18 open-source restructure + 2026-04-21
SSRF-backport. Reduces handlers package failure count 18 → 4
(remaining 4 are unrelated schema/behavior drift — see follow-ups).
Three root causes fixed:
1. httptest.NewServer binds to 127.0.0.1; isSafeURL rejects loopback.
Tests that stub workspace URLs via httptest therefore 502'd at
the SSRF guard before reaching the handler logic they wanted to
exercise.
Fix: add `testAllowLoopback` var to ssrf.go + `allowLoopbackForTest(t)`
helper in handlers_test.go. Only 127.0.0.0/8 and ::1 are relaxed;
169.254 metadata, RFC-1918, TEST-NET, CGNAT, and link-local
protections remain active. Flag is paired with t.Cleanup and is
never touched by production code.
2. ProxyA2A's checkWorkspaceBudget query (SELECT budget_limit, COALESCE
(monthly_spend, 0) FROM workspaces WHERE id = $1) was added with the
restructure but the a2a_proxy_test.go sqlmock expectations never
caught up, producing "call to Query ... was not expected" on every
ProxyA2A-exercising test.
Fix: `expectBudgetCheck(mock, workspaceID)` helper that registers
an empty-rows expectation (checkWorkspaceBudget fails-open on
sql.ErrNoRows, so an empty result = "no budget limit"). Added to
each of the 8 affected TestProxyA2A_* tests in the correct
position relative to access-control + activity-log expectations.
3. TestAdminMemories_Import_Success + _RedactsSecretsBeforeDedup
mocked a 5-arg INSERT when the handler actually issues a 4-arg
INSERT (workspace_id, content, scope, namespace) unless the
payload carries a created_at override. Removed the spurious 5th
AnyArg from both tests; _PreservesCreatedAt is untouched since it
legitimately uses the 5-arg form.
Also: TestResolveAgentURL_CacheHit and _CacheMissDBHit used bogus
`cached.example` / `dbhit.example` hostnames that fail DNS resolution
inside isSafeURL (which happens BEFORE the loopback check). Swapped to
`127.0.0.1` variants preserving test intent (they never hit the network).
Remaining 4 failures — out of scope for this PR, tracked separately:
- TestGitHubToken_NoTokenProvider (handler behavior drift — 500 vs 404)
- TestWorkspaceList + TestWorkspaceList_WithData (Scan arg count —
workspaces table gained a column, mock not updated)
- TestRegister_ProvisionerURLPreserved (request body shape drift)
Closes the 4 wrong-target PRs (#1710, #1718, #1719, #1664) that all
tried to silence the symptom by disabling golangci-lint — which has
`continue-on-error: true` in ci.yml and was never the actual blocker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…bypass + workspace-server Dockerfile GID entrypoint Three small, non-overlapping fixes extracted from closed PR #1664: 1. canvas/src/components/ContextMenu.tsx — Replace the useMemo-over-nodes pattern with a hashed-boolean selector (s.nodes.some(...)) so Zustand's useSyncExternalStore snapshot comparison is stable. Resolves React error #185 (infinite render loop). Moves the child-node list derivation into the delete handler via getState() so the render path no longer allocates a fresh array. 2. workspace-server/internal/handlers/a2a_proxy.go — Allow the Docker-bridge hostname path (ws-<id>:8000) to skip the SSRF guard in local-docker mode. Gated on !saasMode() so SaaS deployments keep the full private-IP blocklist (a remote workspace registration can't claim a ws-* hostname and reach a sensitive VPC IP). 3. workspace-server/Dockerfile — Add entrypoint.sh that discovers the docker.sock GID at boot and adds the platform user to that group, then exec's su-exec to drop privileges. Lets the platform container reach the host docker socket without running as root. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…ache Extracted from the now-closed PR #1664 (Molecule-AI/molecule-core). - New scripts/molecule-gh-token-refresh.sh background daemon — every 45 min (TOKEN_REFRESH_INTERVAL_SEC) calls the credential helper's _refresh_gh action to keep both gh CLI auth and the on-disk cache fresh through the GitHub App installation token's ~60 min TTL. - scripts/molecule-git-token-helper.sh rewritten with a ~50 min on-disk cache (${CACHE_DIR}/gh_installation_token + _expiry companion file), a cache > API > env-var fallback chain, a new _refresh_gh action (invoked by the daemon above), a _invalidate_cache action, and path references flipped from /workspace/scripts/... to /app/scripts/... to match the runtime image layout. - Dockerfile copies the new refresh daemon and extends mkdir to create /home/agent/.molecule-token-cache at build time. - entrypoint.sh configures the git credential helper for github.com while still root (so the global gitconfig is written before the gosu handoff), creates + chowns the token cache dir, then as agent starts the refresh daemon in the background and does an initial gh auth login from GITHUB_TOKEN/GH_TOKEN so gh works before the first refresh fires. Dropped from PR #1664: cosmetic em-dash -> ASCII hyphen rewrites (charset-normalizer noise) that would conflict with the repo's existing em-dash convention used elsewhere in workspace/.
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…shed runs (extracted from #1664) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…ful heartbeat (extracted from #1664) When a workspace is marked "failed" or "provisioning" but is actively sending heartbeats, transition it to "online". Transient boot failures or mid-setup provisioner crashes otherwise leave workspaces stuck in a stale terminal state even after they become healthy. Preserves existing online/degraded/offline transitions; only adds a new conditional branch for the failed/provisioning case with a guarded WHERE clause so a concurrent delete cannot flip 'removed' back to 'online'.
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
… loopback
Fixes 14 of the 18 failing tests that have been reddening Platform (Go)
CI on main since the 2026-04-18 open-source restructure + 2026-04-21
SSRF-backport. Reduces handlers package failure count 18 → 4
(remaining 4 are unrelated schema/behavior drift — see follow-ups).
Three root causes fixed:
1. httptest.NewServer binds to 127.0.0.1; isSafeURL rejects loopback.
Tests that stub workspace URLs via httptest therefore 502'd at
the SSRF guard before reaching the handler logic they wanted to
exercise.
Fix: add `testAllowLoopback` var to ssrf.go + `allowLoopbackForTest(t)`
helper in handlers_test.go. Only 127.0.0.0/8 and ::1 are relaxed;
169.254 metadata, RFC-1918, TEST-NET, CGNAT, and link-local
protections remain active. Flag is paired with t.Cleanup and is
never touched by production code.
2. ProxyA2A's checkWorkspaceBudget query (SELECT budget_limit, COALESCE
(monthly_spend, 0) FROM workspaces WHERE id = $1) was added with the
restructure but the a2a_proxy_test.go sqlmock expectations never
caught up, producing "call to Query ... was not expected" on every
ProxyA2A-exercising test.
Fix: `expectBudgetCheck(mock, workspaceID)` helper that registers
an empty-rows expectation (checkWorkspaceBudget fails-open on
sql.ErrNoRows, so an empty result = "no budget limit"). Added to
each of the 8 affected TestProxyA2A_* tests in the correct
position relative to access-control + activity-log expectations.
3. TestAdminMemories_Import_Success + _RedactsSecretsBeforeDedup
mocked a 5-arg INSERT when the handler actually issues a 4-arg
INSERT (workspace_id, content, scope, namespace) unless the
payload carries a created_at override. Removed the spurious 5th
AnyArg from both tests; _PreservesCreatedAt is untouched since it
legitimately uses the 5-arg form.
Also: TestResolveAgentURL_CacheHit and _CacheMissDBHit used bogus
`cached.example` / `dbhit.example` hostnames that fail DNS resolution
inside isSafeURL (which happens BEFORE the loopback check). Swapped to
`127.0.0.1` variants preserving test intent (they never hit the network).
Remaining 4 failures — out of scope for this PR, tracked separately:
- TestGitHubToken_NoTokenProvider (handler behavior drift — 500 vs 404)
- TestWorkspaceList + TestWorkspaceList_WithData (Scan arg count —
workspaces table gained a column, mock not updated)
- TestRegister_ProvisionerURLPreserved (request body shape drift)
Closes the 4 wrong-target PRs (#1710, #1718, #1719, #1664) that all
tried to silence the symptom by disabling golangci-lint — which has
`continue-on-error: true` in ci.yml and was never the actual blocker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…bypass + workspace-server Dockerfile GID entrypoint Three small, non-overlapping fixes extracted from closed PR #1664: 1. canvas/src/components/ContextMenu.tsx — Replace the useMemo-over-nodes pattern with a hashed-boolean selector (s.nodes.some(...)) so Zustand's useSyncExternalStore snapshot comparison is stable. Resolves React error #185 (infinite render loop). Moves the child-node list derivation into the delete handler via getState() so the render path no longer allocates a fresh array. 2. workspace-server/internal/handlers/a2a_proxy.go — Allow the Docker-bridge hostname path (ws-<id>:8000) to skip the SSRF guard in local-docker mode. Gated on !saasMode() so SaaS deployments keep the full private-IP blocklist (a remote workspace registration can't claim a ws-* hostname and reach a sensitive VPC IP). 3. workspace-server/Dockerfile — Add entrypoint.sh that discovers the docker.sock GID at boot and adds the platform user to that group, then exec's su-exec to drop privileges. Lets the platform container reach the host docker socket without running as root. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…ache Extracted from the now-closed PR #1664 (Molecule-AI/molecule-core). - New scripts/molecule-gh-token-refresh.sh background daemon — every 45 min (TOKEN_REFRESH_INTERVAL_SEC) calls the credential helper's _refresh_gh action to keep both gh CLI auth and the on-disk cache fresh through the GitHub App installation token's ~60 min TTL. - scripts/molecule-git-token-helper.sh rewritten with a ~50 min on-disk cache (${CACHE_DIR}/gh_installation_token + _expiry companion file), a cache > API > env-var fallback chain, a new _refresh_gh action (invoked by the daemon above), a _invalidate_cache action, and path references flipped from /workspace/scripts/... to /app/scripts/... to match the runtime image layout. - Dockerfile copies the new refresh daemon and extends mkdir to create /home/agent/.molecule-token-cache at build time. - entrypoint.sh configures the git credential helper for github.com while still root (so the global gitconfig is written before the gosu handoff), creates + chowns the token cache dir, then as agent starts the refresh daemon in the background and does an initial gh auth login from GITHUB_TOKEN/GH_TOKEN so gh works before the first refresh fires. Dropped from PR #1664: cosmetic em-dash -> ASCII hyphen rewrites (charset-normalizer noise) that would conflict with the repo's existing em-dash convention used elsewhere in workspace/.
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…shed runs (extracted from #1664) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
…ful heartbeat (extracted from #1664) When a workspace is marked "failed" or "provisioning" but is actively sending heartbeats, transition it to "online". Transient boot failures or mid-setup provisioner crashes otherwise leave workspaces stuck in a stale terminal state even after they become healthy. Preserves existing online/degraded/offline transitions; only adds a new conditional branch for the failed/provisioning case with a guarded WHERE clause so a concurrent delete cannot flip 'removed' back to 'online'.
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 23, 2026
… loopback
Fixes 14 of the 18 failing tests that have been reddening Platform (Go)
CI on main since the 2026-04-18 open-source restructure + 2026-04-21
SSRF-backport. Reduces handlers package failure count 18 → 4
(remaining 4 are unrelated schema/behavior drift — see follow-ups).
Three root causes fixed:
1. httptest.NewServer binds to 127.0.0.1; isSafeURL rejects loopback.
Tests that stub workspace URLs via httptest therefore 502'd at
the SSRF guard before reaching the handler logic they wanted to
exercise.
Fix: add `testAllowLoopback` var to ssrf.go + `allowLoopbackForTest(t)`
helper in handlers_test.go. Only 127.0.0.0/8 and ::1 are relaxed;
169.254 metadata, RFC-1918, TEST-NET, CGNAT, and link-local
protections remain active. Flag is paired with t.Cleanup and is
never touched by production code.
2. ProxyA2A's checkWorkspaceBudget query (SELECT budget_limit, COALESCE
(monthly_spend, 0) FROM workspaces WHERE id = $1) was added with the
restructure but the a2a_proxy_test.go sqlmock expectations never
caught up, producing "call to Query ... was not expected" on every
ProxyA2A-exercising test.
Fix: `expectBudgetCheck(mock, workspaceID)` helper that registers
an empty-rows expectation (checkWorkspaceBudget fails-open on
sql.ErrNoRows, so an empty result = "no budget limit"). Added to
each of the 8 affected TestProxyA2A_* tests in the correct
position relative to access-control + activity-log expectations.
3. TestAdminMemories_Import_Success + _RedactsSecretsBeforeDedup
mocked a 5-arg INSERT when the handler actually issues a 4-arg
INSERT (workspace_id, content, scope, namespace) unless the
payload carries a created_at override. Removed the spurious 5th
AnyArg from both tests; _PreservesCreatedAt is untouched since it
legitimately uses the 5-arg form.
Also: TestResolveAgentURL_CacheHit and _CacheMissDBHit used bogus
`cached.example` / `dbhit.example` hostnames that fail DNS resolution
inside isSafeURL (which happens BEFORE the loopback check). Swapped to
`127.0.0.1` variants preserving test intent (they never hit the network).
Remaining 4 failures — out of scope for this PR, tracked separately:
- TestGitHubToken_NoTokenProvider (handler behavior drift — 500 vs 404)
- TestWorkspaceList + TestWorkspaceList_WithData (Scan arg count —
workspaces table gained a column, mock not updated)
- TestRegister_ProvisionerURLPreserved (request body shape drift)
Closes the 4 wrong-target PRs (#1710, #1718, #1719, #1664) that all
tried to silence the symptom by disabling golangci-lint — which has
`continue-on-error: true` in ci.yml and was never the actual blocker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HongmingWang-Rabbit
pushed a commit
that referenced
this pull request
Jun 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Same as #1625 + test fixes. Re-created to trigger CI.
[Molecule-Platform-Evolvement-Manager]