From 6e3aa08493faebb548e1fbd4d5a85960f99d4ea3 Mon Sep 17 00:00:00 2001 From: Logan Rosen Date: Sun, 23 Aug 2026 14:45:06 -0400 Subject: [PATCH 1/2] Bug 2065864 - Match webhook event selections exactly Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- extensions/Push/lib/Connector/Webhook.pm | 14 +-- extensions/Push/t/webhook.t | 152 +++++++++++++++++++++++ 2 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 extensions/Push/t/webhook.t diff --git a/extensions/Push/lib/Connector/Webhook.pm b/extensions/Push/lib/Connector/Webhook.pm index 0accb7af20..a7fa0fafc9 100644 --- a/extensions/Push/lib/Connector/Webhook.pm +++ b/extensions/Push/lib/Connector/Webhook.pm @@ -62,7 +62,7 @@ sub should_send { return 0 unless Bugzilla->params->{webhooks_enabled}; my $webhook = Bugzilla::Extension::Webhooks::Webhook->new($self->{webhook_id}); - my $event = $webhook->event; + my %events = map { $_ => 1 } split(',', $webhook->event); my $product = $webhook->product_name; my $component = $webhook->component_name; @@ -76,18 +76,18 @@ sub should_send { if (($product eq $bug_data->{product} || $product eq 'Any') && ($component eq $bug_data->{component} || $component eq 'Any')) { - if ( ($event =~ /create/ && $message->routing_key eq 'bug.create') - || ($event =~ /change/ && $message->routing_key =~ /^bug\.modify/) - || ($event =~ /comment/ && $message->routing_key eq 'comment.create') - || ($event =~ /attachment_change/ && $message->routing_key =~ /^attachment[.]modify/) - || ($event =~ /attachment/ && $message->routing_key eq 'attachment.create')) + if ( ($events{create} && $message->routing_key eq 'bug.create') + || ($events{change} && $message->routing_key =~ /^bug\.modify/) + || ($events{comment} && $message->routing_key eq 'comment.create') + || ($events{attachment_change} && $message->routing_key =~ /^attachment[.]modify/) + || ($events{attachment} && $message->routing_key eq 'attachment.create')) { return 1; } } # check if the bug was removed from a product/component we care about - if ($event =~ /change/ && $message->routing_key =~ /\Qbug.modify\E/) { + if ($events{change} && $message->routing_key =~ /\Qbug.modify\E/) { my $removed_product = ""; my $removed_component = ""; if (exists $payload->{event}->{changes}) { diff --git a/extensions/Push/t/webhook.t b/extensions/Push/t/webhook.t new file mode 100644 index 0000000000..4815336c87 --- /dev/null +++ b/extensions/Push/t/webhook.t @@ -0,0 +1,152 @@ +#!/usr/bin/env perl +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +use 5.10.1; +use strict; +use warnings; +use lib qw( . lib local/lib/perl5 ); + +use Bugzilla; +BEGIN { Bugzilla->extensions } + +use Test2::V0; + +use Bugzilla::Test::MockParams (webhooks_enabled => 1); +use Bugzilla::Extension::Push::Connector::Webhook; + +{ + package TestWebhookOwner; + + sub can_see_bug { return 1 } + sub can_see_product { return 1 } + sub is_insider { return 1 } +} + +{ + package TestWebhook; + + sub event { return $_[0]->{event} } + sub product_name { return 'Firefox' } + sub component_name { return 'Any' } + sub user { return bless({}, 'TestWebhookOwner') } +} + +{ + package TestMessage; + + sub routing_key { return $_[0]->{routing_key} } + sub payload_decoded { return $_[0]->{payload} } +} + +my $selected_events; +my $connector + = bless({webhook_id => 1}, 'Bugzilla::Extension::Push::Connector::Webhook'); + +sub make_payload { + my ($routing_key, %args) = @_; + my ($target) = split(/[.]/, $routing_key); + + my $bug = { + id => 1, + product => $args{product} // 'Firefox', + component => 'General', + }; + my $payload = { + event => { + target => $target, + changes => $args{changes} // [], + }, + }; + + if ($target eq 'bug') { + $payload->{bug} = $bug; + } + else { + $payload->{$target} = { + bug => $bug, + is_private => 0, + }; + } + + return $payload; +} + +sub should_send { + my ($events, $routing_key, %args) = @_; + $selected_events = $events; + my $message = bless( + { + routing_key => $routing_key, + payload => make_payload($routing_key, %args), + }, + 'TestMessage' + ); + return $connector->should_send($message); +} + +{ + no warnings qw(redefine once); + local *Bugzilla::Extension::Webhooks::Webhook::new + = sub { return bless({event => $selected_events}, 'TestWebhook') }; + + my @individual_events = ( + ['create', 'bug.create'], + ['change', 'bug.modify:summary'], + ['comment', 'comment.create'], + ['attachment', 'attachment.create'], + ['attachment_change', 'attachment.modify:is_obsolete'], + ); + + foreach my $test (@individual_events) { + my ($event, $routing_key) = @{$test}; + ok(should_send($event, $routing_key), "$event selects $routing_key"); + } + + ok(!should_send('attachment_change', 'bug.modify:summary'), + 'attachment_change does not select bug modifications'); + ok(!should_send('attachment_change', 'attachment.create'), + 'attachment_change does not select new attachments'); + ok(!should_send('attachment', 'attachment.modify:is_obsolete'), + 'attachment does not select attachment modifications'); + + ok(should_send('create,comment,attachment_change', 'bug.create'), + 'combined selection includes bug creation'); + ok(should_send('create,comment,attachment_change', 'comment.create'), + 'combined selection includes comments'); + ok( + should_send( + 'create,comment,attachment_change', + 'attachment.modify:is_obsolete' + ), + 'combined selection includes attachment modifications' + ); + ok(!should_send('create,comment,attachment_change', 'attachment.create'), + 'combined selection excludes unselected attachment creation'); + + my @product_change = ({field => 'product', removed => 'Firefox'}); + ok( + should_send( + 'change', + 'bug.modify:product', + product => 'Thunderbird', + changes => \@product_change + ), + 'change selects a bug moved out of the configured product' + ); + ok( + !should_send( + 'attachment_change', + 'bug.modify:product', + product => 'Thunderbird', + changes => \@product_change + ), + 'attachment_change does not select a bug moved out of the configured product' + ); +} + +done_testing; From 281fa13a2021959c0befdeb742fd6f7dd7a65197 Mon Sep 17 00:00:00 2001 From: Logan Rosen Date: Sun, 23 Aug 2026 14:57:46 -0400 Subject: [PATCH 2/2] Bug 2065864 - Use regex delimiter for event parsing Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- extensions/Push/lib/Connector/Webhook.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/Push/lib/Connector/Webhook.pm b/extensions/Push/lib/Connector/Webhook.pm index a7fa0fafc9..90d6057174 100644 --- a/extensions/Push/lib/Connector/Webhook.pm +++ b/extensions/Push/lib/Connector/Webhook.pm @@ -62,7 +62,7 @@ sub should_send { return 0 unless Bugzilla->params->{webhooks_enabled}; my $webhook = Bugzilla::Extension::Webhooks::Webhook->new($self->{webhook_id}); - my %events = map { $_ => 1 } split(',', $webhook->event); + my %events = map { $_ => 1 } split(/,/, $webhook->event); my $product = $webhook->product_name; my $component = $webhook->component_name;