fix(container_files.go): add validateRelPath + CWE-78 exec form on staging - #1328
Conversation
… form Issue #1317: validateRelPath was called in deleteViaEphemeral but never defined — staging ce2491e would fail Go build if CI completed. Changes: - Add validateRelPath function (filepath.Clean + abs/traversal guard) matching the pattern used on main (PR #1310). - Upgrade deleteViaEphemeral to exec form ([]string{...}) so filePath is passed as a plain argument, not interpolated into a shell string. This eliminates shell injection (CWE-78) entirely. - Add ContainerWait loop to guarantee rm completes before container removal (avoids race on fast delete vs container-stop). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Info: staging backport of CWE-78 fixPR #1328 backports Confirm staging's Note: |
There was a problem hiding this comment.
Review: PR #1328 — BLOCKER: duplicate function definition
GitHub reports mergeable_state: dirty.
BLOCKER — validateRelPath already exists in templates.go (Go duplicate symbol)
This PR adds validateRelPath to container_files.go (lines 21-28). However, validateRelPath was added to templates.go:65 in commit ce2491e (merged in the CWE-22 fix at staging). Since both files are in the handlers package, merging this PR creates a Go duplicate symbol error — two definitions of validateRelPath in the same package.
The function should NOT be duplicated. Since container_files.go and templates.go are in the same handlers package, container_files.go already has access to validateRelPath from templates.go without needing its own definition.
Fix: Remove the validateRelPath function block from the diff. Only keep the validateRelPath(filePath) call and the comment update in deleteViaEphemeral.
Already addressed by current staging
- CWE-78 exec form (
[]string{"rm", "-rf", "/configs", filePath}): Stagingce2491eonward already hasvalidateRelPathcallingIsAbs+HasPrefix("..")guards. The shell form/configs/" + filePathis technically still shell form but the validation layer provides protection. - ContainerWait race: Staging already has
ContainerWaitindeleteViaEphemeral(from #1310 merge to main, which was fast-forwarded to staging).
What to do
- Rebase onto current
origin/staging(72d825f) - Delete the
validateRelPathfunction block fromcontainer_files.go— it must NOT be re-defined - Keep only the
validateRelPath(filePath)guard call indeleteViaEphemeral+ comment update - The
Cmd: []string{"rm", "-rf", "/configs", filePath}change (separate args) is actually a good improvement — keep it
|
Please merge once CI passes — this unblocks the staging Go build (validateRelPath is missing on staging ce2491e). |
…aEphemeral (#1334) (#1337) * fix(canvas/test): restore test regressions from PR #1243 PR #1243 introduced two regressions in the canvas vitest suite: 1. ContextMenu.keyboard.test.tsx: the setPendingDelete call now passes `{hasChildren, id, name}` (not just `{id, name}`). Updated the keyboard-a11y test assertion to match the new store shape. 2. orgs-page.test.tsx: mockFetch.mockResolvedValueOnce() returned a plain object that didn't match the two-argument (url, options) call signature used by the component's fetch wrapper. Switched to mockImplementationOnce returning a rejected Promise — matching real fetch's rejection contract — and added runAllTimersAsync after advanceTimersByTimeAsync(50) to flush React state updates. 54 test files · 813 tests · all passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(canvas): replace bounding-box intersection with distance threshold for nest detection ReactFlow's getIntersectingNodes uses bounding-box overlap detection, which fires the drag-over state whenever any part of two nodes' position rectangles overlap — even when the dragged node is far from the target. This made the "Nest Workspace" dialog appear from large distances. Fix: scan all nodes on each drag tick and set dragOverNodeId to the closest node within NEST_PROXIMITY_THRESHOLD (150 px, center-to-center). This matches the intuitive behavior: nest only when the node is actually dropped near another. Constants: - NEST_PROXIMITY_THRESHOLD = 150px (~60% of a collapsed node's width) - DEFAULT_NODE_WIDTH = 245px (mid-range of min/max node widths) - DEFAULT_NODE_HEIGHT = 110px Also removed the unused getIntersectingNodes import (was causing duplicate identifier error when both onNodeDrag and the zoom handler called useReactFlow in the same component scope). Closes #1052. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(canvas): cascade-delete UX — show child count and require checkbox before Delete All Issue #1137: with ?confirm=true always sent, a single confirmation silently cascades — a team lead with 20 children gets nuked on one click. Changes: - store/canvas.ts: pendingDelete type now includes children: {id, name}[] - ContextMenu.tsx: passes child list to setPendingDelete on Delete click - DeleteCascadeConfirmDialog.tsx: new component — shows child names, a cascade warning, and requires the operator to tick a checkbox before Delete All activates. Disabled by default; only enables after checkbox. - Canvas.tsx: conditionally renders DeleteCascadeConfirmDialog for hasChildren workspaces, or plain ConfirmDialog for leaf workspaces. confirmDelete requires cascadeConfirmChecked=true when hasChildren. - ContextMenu.keyboard.test.tsx: updated setPendingDelete assertion to include children:[] (no children in the test fixture). 813 tests pass. Closes #1137. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(container_files): remove duplicate ContainerWait loop in deleteViaEphemeral Issue #1334: Staging HEAD d24ca1b (PR #1328) left two identical ContainerWait loops in deleteViaEphemeral. The first loop always returns before the second executes — the second is unreachable dead code. Remove it. No functional change (the remaining loop handles the wait correctly). --------- Co-authored-by: Molecule AI Core-UIUX <core-uiux@agents.moleculesai.app> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
… form (#1328) Issue #1317: validateRelPath was called in deleteViaEphemeral but never defined — staging dc21821 would fail Go build if CI completed. Changes: - Add validateRelPath function (filepath.Clean + abs/traversal guard) matching the pattern used on main (PR #1310). - Upgrade deleteViaEphemeral to exec form ([]string{...}) so filePath is passed as a plain argument, not interpolated into a shell string. This eliminates shell injection (CWE-78) entirely. - Add ContainerWait loop to guarantee rm completes before container removal (avoids race on fast delete vs container-stop). Co-authored-by: Molecule AI Infra-Runtime-BE <infra-runtime-be@agents.moleculesai.app> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…aEphemeral (#1334) (#1337) * fix(canvas/test): restore test regressions from PR #1243 PR #1243 introduced two regressions in the canvas vitest suite: 1. ContextMenu.keyboard.test.tsx: the setPendingDelete call now passes `{hasChildren, id, name}` (not just `{id, name}`). Updated the keyboard-a11y test assertion to match the new store shape. 2. orgs-page.test.tsx: mockFetch.mockResolvedValueOnce() returned a plain object that didn't match the two-argument (url, options) call signature used by the component's fetch wrapper. Switched to mockImplementationOnce returning a rejected Promise — matching real fetch's rejection contract — and added runAllTimersAsync after advanceTimersByTimeAsync(50) to flush React state updates. 54 test files · 813 tests · all passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(canvas): replace bounding-box intersection with distance threshold for nest detection ReactFlow's getIntersectingNodes uses bounding-box overlap detection, which fires the drag-over state whenever any part of two nodes' position rectangles overlap — even when the dragged node is far from the target. This made the "Nest Workspace" dialog appear from large distances. Fix: scan all nodes on each drag tick and set dragOverNodeId to the closest node within NEST_PROXIMITY_THRESHOLD (150 px, center-to-center). This matches the intuitive behavior: nest only when the node is actually dropped near another. Constants: - NEST_PROXIMITY_THRESHOLD = 150px (~60% of a collapsed node's width) - DEFAULT_NODE_WIDTH = 245px (mid-range of min/max node widths) - DEFAULT_NODE_HEIGHT = 110px Also removed the unused getIntersectingNodes import (was causing duplicate identifier error when both onNodeDrag and the zoom handler called useReactFlow in the same component scope). Closes #1052. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(canvas): cascade-delete UX — show child count and require checkbox before Delete All Issue #1137: with ?confirm=true always sent, a single confirmation silently cascades — a team lead with 20 children gets nuked on one click. Changes: - store/canvas.ts: pendingDelete type now includes children: {id, name}[] - ContextMenu.tsx: passes child list to setPendingDelete on Delete click - DeleteCascadeConfirmDialog.tsx: new component — shows child names, a cascade warning, and requires the operator to tick a checkbox before Delete All activates. Disabled by default; only enables after checkbox. - Canvas.tsx: conditionally renders DeleteCascadeConfirmDialog for hasChildren workspaces, or plain ConfirmDialog for leaf workspaces. confirmDelete requires cascadeConfirmChecked=true when hasChildren. - ContextMenu.keyboard.test.tsx: updated setPendingDelete assertion to include children:[] (no children in the test fixture). 813 tests pass. Closes #1137. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(container_files): remove duplicate ContainerWait loop in deleteViaEphemeral Issue #1334: Staging HEAD c90ada3 (PR #1328) left two identical ContainerWait loops in deleteViaEphemeral. The first loop always returns before the second executes — the second is unreachable dead code. Remove it. No functional change (the remaining loop handles the wait correctly). --------- Co-authored-by: Molecule AI Core-UIUX <core-uiux@agents.moleculesai.app> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Issue #1317:
validateRelPathwas called indeleteViaEphemeralbut never defined on staging (ce2491e). Staging CI was cancelled so this never surfaced, but if CI completes the Go build fails.Changes
validateRelPath()function (filepath.Clean + abs/traversal guard)deleteViaEphemeralto exec form[]string{"rm", "-rf", "/configs", filePath}ContainerWaitloop beforeContainerRemoveDiff
Test plan
Closes #1317.
🤖 Generated with Claude Code