Plugin Directory: Email committers the outcome of a security scan - #795
Draft
obenland wants to merge 6 commits into
Draft
Plugin Directory: Email committers the outcome of a security scan#795obenland wants to merge 6 commits into
obenland wants to merge 6 commits into
Conversation
… risk score. Security scan callbacks now carry a max_risk_score and a bounded findings array. A completed scan at or above the threshold blocks the scanned release pending review — the previously served version keeps being served — and the plugin review team receives the findings as context via an internal note, a Slack alert, and a stored evidence snapshot. A verdict for a version already served, or superseded by a newer release, can't un-ship anything and stays advisory. Scanner retries are acknowledged idempotently under a canonical digest: an identical retry repeats no effects, and a completed verdict supersedes an earlier failure report for the same scan. Finding fields beyond the risk score are optional per the callback contract and read defensively. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A completed security scan whose maximum risk score reaches the notification threshold (default 8.0, filterable via wporg_plugins_security_scan_notify_risk_score) emails the plugin's committers the findings: risk score, title, and a Trac browser link per finding. A blocked release is reflected in the email, including that a new version escapes the block; below the block threshold the email is advisory. Release blocks always email, advisory results deduplicate per verdict hash, and finding strings are treated as untrusted throughout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pending one. The blocked email now leads with the cause and the fix: the review found issues severe enough to block the version, address them and release a new version. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…and linked paths. Findings now carry their code snippet (an indented code block, escaped by Markdown and immune to fence breakout) and explanation, passed to the email unstripped since only the stored meta row needs bounding. Untrusted prose is entity-encoded rather than tag-stripped so text like <slug> or <?php survives, backticks become apostrophes to keep line-leading pairs out of Markdown::code_trick(), and the plain-text variant decodes what the Markdown source encodes. The file path now links to the Trac browser instead of a separate bare URL, Findings became a subheader, and the blocked wording drops the version-scope clause. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
obenland
force-pushed
the
feature/gandalf-scan-committer-notifications
branch
from
August 13, 2026 18:49
8d5e9d7 to
f51dd9e
Compare
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.
Summary
Adds the author-facing half of the security scan pipeline: a completed scan callback now emails the plugin's committers its findings, alongside the existing reviewer surfaces (Slack alert, internal note).
Builds on #777 — its commit is included here until it lands; only the latest commit is new.
What it does
Email\Security_Scan_Findings(Markdown, multipart HTML + plain text) tells committers what was found: per finding the risk score, title, the file path linked to the plugins.trac.wordpress.org browser, the reported code snippet as a code block, and the finding's explanation. Findings render highest-risk first under a Findings subheader, bounded to ten; snippets and explanations reach the email unstripped since only the stored meta row needs bounding. The internal report URL is deliberately not shared.Plugin_Scan_Gandalf::NOTIFY_RISK_SCORE(default 8.0, the block threshold) via thewporg_plugins_security_scan_notify_risk_scorefilter — lower it to start advising authors below the block bar, raise it above 10 to disable the emails entirely.cli/class-import.php. Dedup mirrors the Slack alert: release blocks always email, advisory results deduplicate per verdict hash (month window), so a blocked outcome can never be silenced by an earlier advisory email of the same verdict.<slug>or<?phpsurvives as text while markup can't go live; the plain-text variant decodes them back), length is bounded, Markdown link/image syntax is neutralized so a hostile title can't smuggle a masquerading anchor into an email from plugins@wordpress.org, backticks become apostrophes to keep line-leading pairs out ofMarkdown::code_trick(), and snippets use indented code blocks, which Markdown escapes and which have no fence to break out of. Trac URLs are built from per-segmentrawurlencode()d paths.Testing
tests/Security_Scan_Notification_Test.php(10 tests) covers the blocked and advisory email content, the inclusive threshold and both filter directions (lowering and disabling), advisory dedup across scans vs. blocks always emailing, bot-account exclusion (against a real account, so only the filter excludes it), hostile-string neutralization (escaped markup in titles and snippets, Markdown links), and a finding carrying only the contractually requiredrisk_score. Full plugin-directory suite passes (229 tests); both new files lint clean.🤖 Generated with Claude Code