Skip to content

Fix: global dismiss leaks across rules sharing an object (PRO-1264) - #1861

Merged
pattonwebz merged 4 commits into
developfrom
william/pro-1264-global-dismiss-still-leaks-across-rules-sharing-an-object
Aug 4, 2026
Merged

Fix: global dismiss leaks across rules sharing an object (PRO-1264)#1861
pattonwebz merged 4 commits into
developfrom
william/pro-1264-global-dismiss-still-leaks-across-rules-sharing-an-object

Conversation

@pattonwebz

@pattonwebz pattonwebz commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

  • Fixes PRO-1264: a large-batch/global dismiss on one rule violation was also silently dismissing unrelated rule violations that happened to share the same object.
  • Root cause: dismiss_issue()'s largeBatch path in class-rest-api.php selected and updated every row matching siteid + object only. The pro plugin's parallel fix (PR Backport: Release/1.17.0 #832 on the paired PRO-1239 branch) scoped its own query by rule + object, but never touched this one — the actual query that flips ignre/ignre_global on live rows.
  • Fetches the representative issue's rule alongside object and filters the batch-selection query by both, mirroring the pro plugin's already-correct scoping.

Test plan

  • Added test_large_batch_dismiss_only_affects_matching_rule — seeds two posts sharing rule+object and a third sharing only the object; confirms the third is left untouched after a global dismiss. Confirmed failing against the pre-fix query, passing after.
  • Updated two pre-existing largeBatch tests (test_large_batch_dismiss_authorized_on_all, test_large_batch_dismiss_authorized_on_some) whose fixtures used different rules per row within one "batch" — that setup encoded the same assumption behind the bug and needed to share a rule to still be considered one batch under the corrected query.
  • Full RestApiEndpointsTest suite passes (13/13).
  • Full PHPUnit suite passes (875 tests) via docker compose + scripts/setup-phpunit.sh flow.

Linear: PRO-1264 (related to PRO-1239)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed large-batch issue dismissal and reopening so only issues matching both the selected rule and object are updated.
    • Prevented unrelated issues sharing the same object from being changed accidentally.
    • Improved batch operation handling when unrelated issues involve posts the user cannot edit.

Adds a failing regression test proving a large-batch/global dismiss
touches every row sharing the same object regardless of rule, when it
should only affect rows matching both rule and object. Confirmed
failing against current dismiss_issue() largeBatch query, which
filters only by siteid + object.
dismiss_issue()'s largeBatch path selected and updated every row
matching only siteid+object, so a global/large-batch dismiss on one
rule violation also silently dismissed unrelated rule violations that
happened to share the same object. Fetch the representative issue's
rule alongside its object and filter the batch query by both, matching
the scoping the pro plugin's global-ignore lookup already uses.

Also fixes two existing largeBatch tests whose fixtures used different
rules per row within a single "batch" object — that setup encoded the
same assumption behind the bug, so those rows would no longer be
recognized as belonging to the same batch under the corrected query.
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b79119dd-b86e-4ce0-b397-96b3697ac48d

📥 Commits

Reviewing files that changed from the base of the PR and between 3b2f718 and f75afaa.

📒 Files selected for processing (1)
  • tests/phpunit/includes/classes/RestApiEndpointsTest.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/phpunit/includes/classes/RestApiEndpointsTest.php

📝 Walkthrough

Walkthrough

Large-batch issue dismissal and reopen operations now scope matching rows by both rule and object. Regression tests verify unrelated rules remain unchanged and unauthorized posts are excluded from the batch.

Changes

Large-batch operation scoping

Layer / File(s) Summary
Scope batch operations by rule and object
includes/classes/class-rest-api.php
The representative issue provides both rule and object values. The batch permission query matches siteid, object, and rule.
Validate dismissal, reopen, and authorization behavior
tests/phpunit/includes/classes/RestApiEndpointsTest.php
Fixtures use shared batch rules. Regression tests cover dismissal, reopen, unrelated-rule rows, unauthorized posts, and partial authorization.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant REST_API
  participant Representative_Issue_Query
  participant Batch_Permission_Query
  participant Issue_Rows
  REST_API->>Representative_Issue_Query: Retrieve rule and object
  REST_API->>Batch_Permission_Query: Match siteid, rule, and object
  Batch_Permission_Query->>Issue_Rows: Select matching issue rows
  REST_API->>Issue_Rows: Apply dismiss or reopen operation
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: preventing global dismissals from affecting violations that share an object but use different rules.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch william/pro-1264-global-dismiss-still-leaks-across-rules-sharing-an-object

Comment @coderabbitai help to get the list of available commands.

- dismiss_issue(): guard on the representative row itself (not just
  the object it contains) before proceeding, and reword the stale
  largeBatch comment that still described an "object-based update".
- Regression test now asserts ignre_global, not just ignre, since
  this bug is specifically about global-dismiss scoping.
- Add test_large_batch_reopen_only_affects_matching_rule mirroring the
  dismiss-side regression test for the reopen/undismiss direction.
- Add test_large_batch_dismiss_succeeds_when_unrelated_rule_row_is_on_unauthorized_post
  proving the practical benefit of the narrower scope: a batch no
  longer 403s over an unrelated rule violation on a post the caller
  can't edit.

Found by a multi-persona code review (correctness, security,
adversarial, testing, maintainability, project-standards,
agent-native, learnings) of PR #1861. Full suite (877 tests) and
phpcs pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/phpunit/includes/classes/RestApiEndpointsTest.php (1)

1093-1124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert that the unrelated-rule row stays untouched.

The test only checks the 200 status. It does not prove that the batch excluded the admin-owned row. Capture the second insert_id and assert its ignre is still 0. This makes the test fail if the rule filter is later removed but the permission check is relaxed.

♻️ Proposed additional assertion
 			[ '%d', '%d', '%s', '%s', '%s', '%s', '%d', '%d', '%d', '%d' ]
 		);
+		$unrelated_rule_issue_id = $wpdb->insert_id;
 		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
@@
 		$this->assertSame(
 			200,
 			$response->get_status(),
 			'Large batch dismiss should succeed even though an unrelated-rule issue on an unauthorized post shares the object.'
 		);
+
+		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Need fresh data for assertions.
+		$unrelated_ignre = $wpdb->get_var(
+			$wpdb->prepare( 'SELECT ignre FROM %i WHERE id = %d', $table_name, $unrelated_rule_issue_id )
+		);
+		$this->assertSame( '0', $unrelated_ignre, 'The unrelated-rule issue on the unauthorized post must stay open.' );
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/phpunit/includes/classes/RestApiEndpointsTest.php` around lines 1093 -
1124, Update the test around the two database inserts and the large-batch
dispatch to capture the second insert’s ID, then query that row afterward and
assert its ignre value remains 0. Keep the existing status assertion and use the
captured ID to verify the unrelated-rule row was not modified.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/phpunit/includes/classes/RestApiEndpointsTest.php`:
- Around line 1093-1124: Update the test around the two database inserts and the
large-batch dispatch to capture the second insert’s ID, then query that row
afterward and assert its ignre value remains 0. Keep the existing status
assertion and use the captured ID to verify the unrelated-rule row was not
modified.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eaa4d416-062c-4607-b4a4-85054998d549

📥 Commits

Reviewing files that changed from the base of the PR and between cf77916 and 3b2f718.

📒 Files selected for processing (2)
  • includes/classes/class-rest-api.php
  • tests/phpunit/includes/classes/RestApiEndpointsTest.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • includes/classes/class-rest-api.php

test_large_batch_dismiss_succeeds_when_unrelated_rule_row_is_on_unauthorized_post
only checked the 200 response status, which would also pass if the rule
filter were removed and the permission check merely relaxed instead.
Capture the admin-owned row's id and assert its ignre stays 0, so the
test actually proves the batch excluded it rather than just succeeding.
@pattonwebz
pattonwebz merged commit 9709201 into develop Aug 4, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant