summaryrefslogtreecommitdiffstats
path: root/extensions/AntiSpam/Extension.pm
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/AntiSpam/Extension.pm')
-rw-r--r--extensions/AntiSpam/Extension.pm476
1 files changed, 222 insertions, 254 deletions
diff --git a/extensions/AntiSpam/Extension.pm b/extensions/AntiSpam/Extension.pm
index 19eddb4e7..990130c8e 100644
--- a/extensions/AntiSpam/Extension.pm
+++ b/extensions/AntiSpam/Extension.pm
@@ -26,34 +26,29 @@ our $VERSION = '1';
#
sub _project_honeypot_blocking {
- my ($self, $api_key, $login) = @_;
- my $ip = remote_ip();
- return unless $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
- my $lookup = "$api_key.$4.$3.$2.$1.dnsbl.httpbl.org";
- return unless my $packed = gethostbyname($lookup);
- my $honeypot = inet_ntoa($packed);
- return unless $honeypot =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
- my ($status, $days, $threat, $type) = ($1, $2, $3, $4);
-
- return if $status != 127
- || $threat < Bugzilla->params->{honeypot_threat_threshold};
-
- Bugzilla->audit(sprintf("blocked <%s> from creating %s, honeypot %s", $ip, $login, $honeypot));
- ThrowUserError('account_creation_restricted');
+ my ($self, $api_key, $login) = @_;
+ my $ip = remote_ip();
+ return unless $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
+ my $lookup = "$api_key.$4.$3.$2.$1.dnsbl.httpbl.org";
+ return unless my $packed = gethostbyname($lookup);
+ my $honeypot = inet_ntoa($packed);
+ return unless $honeypot =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
+ my ($status, $days, $threat, $type) = ($1, $2, $3, $4);
+
+ return
+ if $status != 127 || $threat < Bugzilla->params->{honeypot_threat_threshold};
+
+ Bugzilla->audit(
+ sprintf("blocked <%s> from creating %s, honeypot %s", $ip, $login, $honeypot));
+ ThrowUserError('account_creation_restricted');
}
sub config_modify_panels {
- my ($self, $args) = @_;
- push @{ $args->{panels}->{auth}->{params} }, {
- name => 'honeypot_api_key',
- type => 't',
- default => '',
- };
- push @{ $args->{panels}->{auth}->{params} }, {
- name => 'honeypot_threat_threshold',
- type => 't',
- default => '32',
- };
+ my ($self, $args) = @_;
+ push @{$args->{panels}->{auth}->{params}},
+ {name => 'honeypot_api_key', type => 't', default => '',};
+ push @{$args->{panels}->{auth}->{params}},
+ {name => 'honeypot_threat_threshold', type => 't', default => '32',};
}
#
@@ -61,20 +56,22 @@ sub config_modify_panels {
#
sub _comment_blocking {
- my ($self, $params) = @_;
- my $user = Bugzilla->user;
- return if $user->in_group('editbugs');
-
- my $blocklist = Bugzilla->dbh->selectcol_arrayref(
- 'SELECT word FROM antispam_comment_blocklist'
- );
- return unless @$blocklist;
-
- my $regex = '\b(?:' . join('|', map { quotemeta } @$blocklist) . ')\b';
- if ($params->{thetext} =~ /$regex/i) {
- Bugzilla->audit(sprintf("blocked <%s> %s from commenting, blacklisted phrase", remote_ip(), $user->login));
- ThrowUserError('antispam_comment_blocked');
- }
+ my ($self, $params) = @_;
+ my $user = Bugzilla->user;
+ return if $user->in_group('editbugs');
+
+ my $blocklist = Bugzilla->dbh->selectcol_arrayref(
+ 'SELECT word FROM antispam_comment_blocklist');
+ return unless @$blocklist;
+
+ my $regex = '\b(?:' . join('|', map {quotemeta} @$blocklist) . ')\b';
+ if ($params->{thetext} =~ /$regex/i) {
+ Bugzilla->audit(sprintf(
+ "blocked <%s> %s from commenting, blacklisted phrase",
+ remote_ip(), $user->login
+ ));
+ ThrowUserError('antispam_comment_blocked');
+ }
}
#
@@ -82,17 +79,19 @@ sub _comment_blocking {
#
sub _domain_blocking {
- my ($self, $login) = @_;
- my $address = Email::Address->new(undef, $login);
- my $blocked = Bugzilla->dbh->selectrow_array(
- "SELECT 1 FROM antispam_domain_blocklist WHERE domain=?",
- undef,
- $address->host
- );
- if ($blocked) {
- Bugzilla->audit(sprintf("blocked <%s> from creating %s, blacklisted domain", remote_ip(), $login));
- ThrowUserError('account_creation_restricted');
- }
+ my ($self, $login) = @_;
+ my $address = Email::Address->new(undef, $login);
+ my $blocked
+ = Bugzilla->dbh->selectrow_array(
+ "SELECT 1 FROM antispam_domain_blocklist WHERE domain=?",
+ undef, $address->host);
+ if ($blocked) {
+ Bugzilla->audit(sprintf(
+ "blocked <%s> from creating %s, blacklisted domain",
+ remote_ip(), $login
+ ));
+ ThrowUserError('account_creation_restricted');
+ }
}
#
@@ -100,18 +99,18 @@ sub _domain_blocking {
#
sub _ip_blocking {
- my ($self, $login) = @_;
- my $ip = remote_ip();
- trick_taint($ip);
- my $blocked = Bugzilla->dbh->selectrow_array(
- "SELECT 1 FROM antispam_ip_blocklist WHERE ip_address=?",
- undef,
- $ip
- );
- if ($blocked) {
- Bugzilla->audit(sprintf("blocked <%s> from creating %s, blacklisted IP", $ip, $login));
- ThrowUserError('account_creation_restricted');
- }
+ my ($self, $login) = @_;
+ my $ip = remote_ip();
+ trick_taint($ip);
+ my $blocked
+ = Bugzilla->dbh->selectrow_array(
+ "SELECT 1 FROM antispam_ip_blocklist WHERE ip_address=?",
+ undef, $ip);
+ if ($blocked) {
+ Bugzilla->audit(
+ sprintf("blocked <%s> from creating %s, blacklisted IP", $ip, $login));
+ ThrowUserError('account_creation_restricted');
+ }
}
#
@@ -119,44 +118,50 @@ sub _ip_blocking {
#
sub _is_limited_user {
- return Bugzilla->user->creation_age < Bugzilla->params->{antispam_multi_user_limit_age};
+ return Bugzilla->user->creation_age
+ < Bugzilla->params->{antispam_multi_user_limit_age};
}
sub bug_before_create {
- my ($self, $args) = @_;
- $self->_cc_limit($args->{params}, 'cc');
+ my ($self, $args) = @_;
+ $self->_cc_limit($args->{params}, 'cc');
}
sub bug_start_of_set_all {
- my ($self, $args) = @_;
- $self->_cc_limit($args->{params}, 'newcc');
+ my ($self, $args) = @_;
+ $self->_cc_limit($args->{params}, 'newcc');
}
sub _cc_limit {
- my ($self, $params, $cc_field) = @_;
- return unless _is_limited_user();
- return unless exists $params->{$cc_field};
-
- my $cc_count = ref($params->{$cc_field}) ? scalar(@{ $params->{$cc_field} }) : 1;
- if ($cc_count > Bugzilla->params->{antispam_multi_user_limit_count}) {
- Bugzilla->audit(sprintf("blocked <%s> from CC'ing %s users", Bugzilla->user->login, $cc_count));
- delete $params->{$cc_field};
- if (exists $params->{cc} && exists $params->{cc}->{add}) {
- delete $params->{cc}->{add};
- }
+ my ($self, $params, $cc_field) = @_;
+ return unless _is_limited_user();
+ return unless exists $params->{$cc_field};
+
+ my $cc_count = ref($params->{$cc_field}) ? scalar(@{$params->{$cc_field}}) : 1;
+ if ($cc_count > Bugzilla->params->{antispam_multi_user_limit_count}) {
+ Bugzilla->audit(
+ sprintf("blocked <%s> from CC'ing %s users", Bugzilla->user->login, $cc_count));
+ delete $params->{$cc_field};
+ if (exists $params->{cc} && exists $params->{cc}->{add}) {
+ delete $params->{cc}->{add};
}
+ }
}
sub bug_set_flags {
- my ($self, $args) = @_;
- return unless _is_limited_user();
-
- my $flag_count = @{ $args->{new_flags} };
- if ($flag_count > Bugzilla->params->{antispam_multi_user_limit_count}) {
- Bugzilla->audit(sprintf("blocked <%s> from flaging %s users", Bugzilla->user->login, $flag_count));
- # empty the arrayref
- $#{ $args->{new_flags} } = -1;
- }
+ my ($self, $args) = @_;
+ return unless _is_limited_user();
+
+ my $flag_count = @{$args->{new_flags}};
+ if ($flag_count > Bugzilla->params->{antispam_multi_user_limit_count}) {
+ Bugzilla->audit(sprintf(
+ "blocked <%s> from flaging %s users",
+ Bugzilla->user->login, $flag_count
+ ));
+
+ # empty the arrayref
+ $#{$args->{new_flags}} = -1;
+ }
}
#
@@ -164,30 +169,31 @@ sub bug_set_flags {
#
sub comment_after_add_tag {
- my ($self, $args) = @_;
- my $tag = lc($args->{tag});
- return unless $tag eq 'spam' or $tag eq 'abusive' or $tag eq 'abuse';
- my $comment = $args->{comment};
- my $author = $comment->author;
-
- # exclude disabled users
- return if !$author->is_enabled;
-
- # exclude users by group
- return if $author->in_group(Bugzilla->params->{antispam_spammer_exclude_group});
-
- # exclude users who are no longer new
- return if !$author->is_new;
-
- # exclude users who haven't made enough comments
- my $count = $tag eq 'spam'
- ? Bugzilla->params->{antispam_spammer_comment_count}
- : Bugzilla->params->{antispam_abusive_comment_count};
- return if $author->comment_count < $count;
-
- # get user's comments
- trick_taint($tag);
- my $comments = Bugzilla->dbh->selectall_arrayref("
+ my ($self, $args) = @_;
+ my $tag = lc($args->{tag});
+ return unless $tag eq 'spam' or $tag eq 'abusive' or $tag eq 'abuse';
+ my $comment = $args->{comment};
+ my $author = $comment->author;
+
+ # exclude disabled users
+ return if !$author->is_enabled;
+
+ # exclude users by group
+ return if $author->in_group(Bugzilla->params->{antispam_spammer_exclude_group});
+
+ # exclude users who are no longer new
+ return if !$author->is_new;
+
+ # exclude users who haven't made enough comments
+ my $count
+ = $tag eq 'spam'
+ ? Bugzilla->params->{antispam_spammer_comment_count}
+ : Bugzilla->params->{antispam_abusive_comment_count};
+ return if $author->comment_count < $count;
+
+ # get user's comments
+ trick_taint($tag);
+ my $comments = Bugzilla->dbh->selectall_arrayref("
SELECT longdescs.comment_id,longdescs_tags.id
FROM longdescs
LEFT JOIN longdescs_tags
@@ -197,41 +203,39 @@ sub comment_after_add_tag {
ORDER BY longdescs.bug_when
", undef, $tag, $author->id);
- # this comment needs to be counted too
- my $comment_id = $comment->id;
- foreach my $ra (@$comments) {
- if ($ra->[0] == $comment_id) {
- $ra->[1] = 1;
- last;
- }
- }
-
- # throw away comment id and negate bool to make it a list of not-spam/abuse
- $comments = [ map { $_->[1] ? 0 : 1 } @$comments ];
-
- my $reason;
-
- # check if the first N comments are spam/abuse
- if (!scalar(grep { $_ } @$comments[0..($count - 1)])) {
- $reason = "first $count comments are $tag";
- }
-
- # check if the last N comments are spam/abuse
- elsif (!scalar(grep { $_ } @$comments[-$count..-1])) {
- $reason = "last $count comments are $tag";
- }
-
- # disable
- if ($reason) {
- $author->set_disabledtext(
- $tag eq 'spam'
- ? Bugzilla->params->{antispam_spammer_disable_text}
- : Bugzilla->params->{antispam_abusive_disable_text}
- );
- $author->set_disable_mail(1);
- $author->update();
- Bugzilla->audit(sprintf("antispam disabled <%s>: %s", $author->login, $reason));
+ # this comment needs to be counted too
+ my $comment_id = $comment->id;
+ foreach my $ra (@$comments) {
+ if ($ra->[0] == $comment_id) {
+ $ra->[1] = 1;
+ last;
}
+ }
+
+ # throw away comment id and negate bool to make it a list of not-spam/abuse
+ $comments = [map { $_->[1] ? 0 : 1 } @$comments];
+
+ my $reason;
+
+ # check if the first N comments are spam/abuse
+ if (!scalar(grep {$_} @$comments[0 .. ($count - 1)])) {
+ $reason = "first $count comments are $tag";
+ }
+
+ # check if the last N comments are spam/abuse
+ elsif (!scalar(grep {$_} @$comments[-$count .. -1])) {
+ $reason = "last $count comments are $tag";
+ }
+
+ # disable
+ if ($reason) {
+ $author->set_disabledtext($tag eq 'spam'
+ ? Bugzilla->params->{antispam_spammer_disable_text}
+ : Bugzilla->params->{antispam_abusive_disable_text});
+ $author->set_disable_mail(1);
+ $author->update();
+ Bugzilla->audit(sprintf("antispam disabled <%s>: %s", $author->login, $reason));
+ }
}
#
@@ -239,51 +243,54 @@ sub comment_after_add_tag {
#
sub object_end_of_create_validators {
- my ($self, $args) = @_;
- if ($args->{class} eq 'Bugzilla::Comment') {
- $self->_comment_blocking($args->{params});
- }
+ my ($self, $args) = @_;
+ if ($args->{class} eq 'Bugzilla::Comment') {
+ $self->_comment_blocking($args->{params});
+ }
}
sub user_verify_login {
- my ($self, $args) = @_;
- if (my $api_key = Bugzilla->params->{honeypot_api_key}) {
- $self->_project_honeypot_blocking($api_key, $args->{login});
- }
- $self->_ip_blocking($args->{login});
- $self->_domain_blocking($args->{login});
+ my ($self, $args) = @_;
+ if (my $api_key = Bugzilla->params->{honeypot_api_key}) {
+ $self->_project_honeypot_blocking($api_key, $args->{login});
+ }
+ $self->_ip_blocking($args->{login});
+ $self->_domain_blocking($args->{login});
}
sub editable_tables {
- my ($self, $args) = @_;
- my $tables = $args->{tables};
- # allow these tables to be edited with the EditTables extension
- $tables->{antispam_domain_blocklist} = {
- id_field => 'id',
- order_by => 'domain',
- blurb => 'List of fully qualified domain names to block at account creation time.',
- group => 'can_configure_antispam',
- };
- $tables->{antispam_comment_blocklist} = {
- id_field => 'id',
- order_by => 'word',
- blurb => "List of whole words that will cause comments containing \\b\$word\\b to be blocked.\n" .
- "This only applies to comments on bugs which the user didn't report.\n" .
- "Users in the editbugs group are exempt from comment blocking.",
- group => 'can_configure_antispam',
- };
- $tables->{antispam_ip_blocklist} = {
- id_field => 'id',
- order_by => 'ip_address',
- blurb => 'List of IPv4 addresses which are prevented from creating accounts.',
- group => 'can_configure_antispam',
- };
+ my ($self, $args) = @_;
+ my $tables = $args->{tables};
+
+ # allow these tables to be edited with the EditTables extension
+ $tables->{antispam_domain_blocklist} = {
+ id_field => 'id',
+ order_by => 'domain',
+ blurb =>
+ 'List of fully qualified domain names to block at account creation time.',
+ group => 'can_configure_antispam',
+ };
+ $tables->{antispam_comment_blocklist} = {
+ id_field => 'id',
+ order_by => 'word',
+ blurb =>
+ "List of whole words that will cause comments containing \\b\$word\\b to be blocked.\n"
+ . "This only applies to comments on bugs which the user didn't report.\n"
+ . "Users in the editbugs group are exempt from comment blocking.",
+ group => 'can_configure_antispam',
+ };
+ $tables->{antispam_ip_blocklist} = {
+ id_field => 'id',
+ order_by => 'ip_address',
+ blurb => 'List of IPv4 addresses which are prevented from creating accounts.',
+ group => 'can_configure_antispam',
+ };
}
sub config_add_panels {
- my ($self, $args) = @_;
- my $modules = $args->{panel_modules};
- $modules->{AntiSpam} = "Bugzilla::Extension::AntiSpam::Config";
+ my ($self, $args) = @_;
+ my $modules = $args->{panel_modules};
+ $modules->{AntiSpam} = "Bugzilla::Extension::AntiSpam::Config";
}
#
@@ -291,82 +298,43 @@ sub config_add_panels {
#
sub install_before_final_checks {
- if (!Bugzilla::Group->new({ name => 'can_configure_antispam' })) {
- Bugzilla::Group->create({
- name => 'can_configure_antispam',
- description => 'Can configure Anti-Spam measures',
- isbuggroup => 0,
- });
- }
+ if (!Bugzilla::Group->new({name => 'can_configure_antispam'})) {
+ Bugzilla::Group->create({
+ name => 'can_configure_antispam',
+ description => 'Can configure Anti-Spam measures',
+ isbuggroup => 0,
+ });
+ }
}
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'antispam_domain_blocklist'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- domain => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- comment => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- antispam_domain_blocklist_idx => {
- FIELDS => [ 'domain' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'antispam_comment_blocklist'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- word => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- antispam_comment_blocklist_idx => {
- FIELDS => [ 'word' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'antispam_ip_blocklist'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- ip_address => {
- TYPE => 'VARCHAR(15)',
- NOTNULL => 1,
- },
- comment => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- antispam_ip_blocklist_idx => {
- FIELDS => [ 'ip_address' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'antispam_domain_blocklist'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ domain => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ comment => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ ],
+ INDEXES =>
+ [antispam_domain_blocklist_idx => {FIELDS => ['domain'], TYPE => 'UNIQUE',},],
+ };
+ $args->{'schema'}->{'antispam_comment_blocklist'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ word => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ ],
+ INDEXES =>
+ [antispam_comment_blocklist_idx => {FIELDS => ['word'], TYPE => 'UNIQUE',},],
+ };
+ $args->{'schema'}->{'antispam_ip_blocklist'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ ip_address => {TYPE => 'VARCHAR(15)', NOTNULL => 1,},
+ comment => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ ],
+ INDEXES =>
+ [antispam_ip_blocklist_idx => {FIELDS => ['ip_address'], TYPE => 'UNIQUE',},],
+ };
}
__PACKAGE__->NAME;