Skip to content

Plugin Directory: Consume security scan callbacks exactly once - #805

Closed
obenland wants to merge 3 commits into
WordPress:trunkfrom
obenland:feature/gandalf-callback-consumption
Closed

Plugin Directory: Consume security scan callbacks exactly once#805
obenland wants to merge 3 commits into
WordPress:trunkfrom
obenland:feature/gandalf-callback-consumption

Conversation

@obenland

Copy link
Copy Markdown
Member

Summary

Spun out of #777: the exactly-once consumption of security scan callbacks, independent of any release-blocking policy. Splitting it out lets #795 (and other consumers of scan results) be based on trunk without carrying #777's policy change.

What it does

  • Consumed callbacks are recorded per scan_id under a canonical (key-order-insensitive) digest: an identical scanner retry — including one that re-marshals the same body with a different key order — is acknowledged without repeating effects, where previously a retry arriving after the pending entry was cleared was rejected as an unknown scan.
  • A different body for an already-consumed scan is rejected as a conflict (HTTP 409) and recorded for operators.
  • A completed verdict supersedes an earlier failure report for the same scan: a failure report keeps the pending entry alive for exactly that reason, and only a completed callback clears it.
  • A short per-plugin lock (released even if processing throws) serializes callback processing, so a scanner retry racing a slow first delivery can't double-process or clobber the consumed record. Consumption is recorded after the effects, failing closed: a crash mid-processing makes the retry re-apply the idempotent effects rather than acknowledge effects that never happened.
  • Consumed records are pruned after a week; the existing advisory behavior (Slack alert on findings, error recording on failure) is unchanged.

Testing

tests/Security_Scan_Consumption_Test.php (7 tests) covers the identical replay acknowledged without re-running effects, conflict rejection, a re-marshalled retry with different key order, a completed verdict superseding a failure report, the failure report keeping the pending entry, a mismatched version leaving no consumed record, and the per-plugin lock rejecting a concurrent callback without consuming anything. Full plugin-directory suite passes (206 tests); both files lint clean.

🤖 Generated with Claude Code

Consumed callbacks are recorded per scan_id under a canonical
(key-order-insensitive) digest: an identical scanner retry is
acknowledged without repeating effects, a different body for a consumed
scan is rejected as a conflict, and a completed verdict supersedes an
earlier failure report for the same scan — the pending entry survives a
failure for exactly that reason. A short per-plugin lock (released even
if processing throws) prevents concurrent callbacks from
double-processing or clobbering each other's records.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 13, 2026 13:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props obenland.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

obenland and others added 2 commits August 13, 2026 10:08
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bazza bazza closed this in 78a3984 Aug 13, 2026
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.

2 participants