ROCK-9044: Close connect-gate enforcement gaps and fix review findings - #19
Conversation
Follow-up to the ROCK-8640 Safety & Security connect gate (PR #8). Addresses all 13 findings from the code review of the v16 port. Enforcement - Bulk Update Requests was ungated and could move requests into Connected. Add a Safety & Security Role block setting and run the shared gate per request, against the target opportunity and status, before any write. - Card-menu and modal Connect now require the request to be in the selected opportunity, since the edit check is evaluated against that opportunity and the request id is client-supplied. - The edit-modal save gates against the opportunity and status the request is being saved with, not its pre-save opportunity. Crashes and feedback - Resolve and null-check the opportunity before the edit check that dereferenced it (yellow screen when the opportunity was deactivated). - Refused connects now show a message instead of silently doing nothing. Display - The card-menu gate list is evaluated with no request in context so one request's per-request Edit result no longer applies to every card. This also keeps the client options hash stable. - Clear the cached request after a transfer so the modal gates against the new opportunity. - Re-render the card after a drag-and-drop so Connect reflects the new status. Cleanup - Hoist the status-independent half of the gate into SeccConnectGateHelper.GetConnectableStatusIds (one evaluation per board render instead of one per status) and only LoadAttributes when needed. - Use the request's ConnectionOpportunity navigation property instead of a hand-rolled query on an unowned RockContext. - Collapse the helper's forwarding wrapper; drop the fabricated Active state. - JS: capture the gate list before fetchAndRefreshCard's early return. Cherry-pick: applies cleanly on the ROCK-8640 commit alone. Depends on secc/ROCK-8640-connect-gate; independent of ROCK-9046 and ROCK-9138. Config after deploy: set "Safety & Security Role" on the Bulk Update Requests block instance (fails closed to Rock Administrators until set). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
Authorization-sensitive changes span several mutation paths and have not been exercised at runtime.
Review effort: Balanced
Findings: None
What changed in this PR
Closes Safety & Security connect-gate gaps across board and bulk-update workflows.
Changes:
- Enforces authorization against target opportunities and statuses.
- Refreshes card gate state after transfers and drag/drop.
- Consolidates and optimizes shared gate evaluation.
| File | Description |
|---|---|
connectionRequestBoard.js |
Captures updated gate data before early return. |
ConnectionRequestBoard.ascx.cs |
Strengthens connect enforcement and refresh behavior. |
BulkUpdateRequests.ascx.cs |
Adds bulk Connected-state authorization. |
SeccConnectGateHelper.cs |
Refactors and optimizes shared gate logic. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Connection Request Board: - Refuse the modal edit-save when the request is not in the selected opportunity, so Edit rights on one opportunity cannot rewrite or connect a request in another. - Refuse the edit-save with a notification when the connect gate blocks a Connected state, instead of silently saving the old state. - Require the request to be in the selected opportunity for the view-mode Connect button, matching its click handler. - Null-guard the selected opportunity inside CanUserEditConnectionRequest so every caller fails closed instead of throwing when the opportunity is deactivated mid-session. - Reset the block-level notification box on every postback so a refused connect does not leave a persistent banner; title the refusal "Not Authorized". - Drop GetGateConnectionOpportunity; the gate now always evaluates the selected opportunity, which every caller already requires to be the request's own. Bulk Update Requests: - Null-check the target opportunity before the gate so the refusal message cannot throw. - Also gate already-Connected requests being moved into a different opportunity with State left blank. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Review of PR 19 (head I ran this PR's code on a local Rock 1.16 site against DEV data, as a non-S&S connector on a gated opportunity (via impersonation) and as an admin. Every DEV change the tests made was reverted. Seven issues are confirmed. The first six need a small change each; the seventh is a feedback gap. 1. Editing an already-Connected request is refused ( 2. A forged request ID can still be acted on in another opportunity ( 3. A forged card drop changes any request's status ( 4. A modal refusal message carries over to the next request ( 5. Bulk moves of Connected requests skip the target's ConnectableStatuses ( 6. Bulk updates are refused for requests already Connected ( 7. A refused connect from the card menu is silent (card-menu Checked, no change needed here
🤖 Generated with Claude Code |
jwakefield-secc
left a comment
There was a problem hiding this comment.
Left some thoughts below on what would be helpful to fix post a local run of this branch.
- Modal save no longer refuses edits to a request that is already Connected; only a transition into Connected is gated (regression from the refuse-the-save change). - Bulk update skips requests already Connected on the target opportunity, and gates every other request entering Connected as a new connect, so the target's ConnectableStatuses always apply. - Clear the request modal notification when a request is opened from a card or the grid, so a refusal does not carry over. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Agreed on #1, #4, #5, #6, fixing in this PR. #1 was a regression I introduced. For #4 I'm hiding the box at the request-open entry points, not inside ShowRequestModal, because the modal connect path calls that right after MarkRequestConnected. #2 and #3 are stock IDORs and don't get around the gate, so they're going in a separate hardening ticket. #7 is ROCK-9138 code and goes in a follow-up there |
Summary
Follow-up to the ROCK-8640 Safety & Security connect gate (#8). Fixes all 13 findings from the code review of the v16 port. JIRA: ROCK-9044.
Enforcement (the reason this exists)
BulkUpdateRequestsoffered and wrote every state, including Connected, with no gate. New "Safety & Security Role" block setting; on confirm, if State = Connected, every request is checked with the shared gate against the target opportunity and status before anything is written. Any refusal hides the confirm modal and shows a Danger notice. Nothing persisted.IsRequestInSelectedOpportunity, fails closed). The edit check runs against the selected opportunity and the request id is client-supplied, so a forgedconnect|<id>could pass edit via one opportunity and the gate via another. Transfer already moves the board selection with the request, so transfer-then-connect still works.Crashes and feedback
Display
CanUserEditConnectionRequest( ConnectionRequest )overload called withnull. Previously one request's per-request Edit result was applied to every card, and the request-dependent list destabilised the client options hash (full re-render + flicker)._connectionRequestcache cleared after transfer so the modal gates against the new opportunity.RefreshRequestCard()after a confirmed drop so Connect reflects the new status.Cleanup
SeccConnectGateHelper.GetConnectableStatusIdsevaluates the status-independent half of the gate once per board render instead of once per status.LoadAttributesonly whenAttributes == null.GetGateConnectionOpportunityuses the request'sConnectionOpportunitynavigation property (same asConnectionRequestDetail) instead of a hand-rolled query on an unownedRockContext.ConnectionState.Activein the status loop is gone with the loop.fetchAndRefreshCardcaptures the gate list before its early return, matchinginitialize.Cherry-pick
git apply --checkof this diff against the ROCK-8640 squash commit (010d9f70a6) alone: applies cleanly. No context lines from ROCK-9046 or ROCK-9138. Pipeline order:secc/ROCK-8640-connect-gate, then this.Config after deploy
Set Safety & Security Role on the Bulk Update Requests block instance. Until set, gated opportunities are Rock-Administrator-only in bulk (fails closed, same as the board and detail blocks).
Verification
aspnet_compilerprecompile of RockWeb (Plugins excluded): zero C# errors.🤖 Generated with Claude Code