diff options
Diffstat (limited to 'extensions/AntiSpam')
-rw-r--r-- | extensions/AntiSpam/Config.pm | 9 | ||||
-rw-r--r-- | extensions/AntiSpam/Extension.pm | 476 | ||||
-rw-r--r-- | extensions/AntiSpam/lib/Config.pm | 116 |
3 files changed, 282 insertions, 319 deletions
diff --git a/extensions/AntiSpam/Config.pm b/extensions/AntiSpam/Config.pm index e16add9b7..18cd3efa2 100644 --- a/extensions/AntiSpam/Config.pm +++ b/extensions/AntiSpam/Config.pm @@ -12,13 +12,8 @@ use strict; use warnings; use constant NAME => 'AntiSpam'; -use constant REQUIRED_MODULES => [ - { - package => 'Email-Address', - module => 'Email::Address', - version => 0, - }, -]; +use constant REQUIRED_MODULES => + [{package => 'Email-Address', module => 'Email::Address', version => 0,},]; use constant OPTIONAL_MODULES => []; __PACKAGE__->NAME; 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; diff --git a/extensions/AntiSpam/lib/Config.pm b/extensions/AntiSpam/lib/Config.pm index 278baea8f..3cddb77ed 100644 --- a/extensions/AntiSpam/lib/Config.pm +++ b/extensions/AntiSpam/lib/Config.pm @@ -17,66 +17,66 @@ use Bugzilla::Group; our $sortkey = 511; sub get_param_list { - my ($class) = @_; + my ($class) = @_; - my @param_list = ( - { - name => 'antispam_spammer_exclude_group', - type => 's', - choices => \&get_all_group_names, - default => 'canconfirm', - checker => \&check_group - }, - { - name => 'antispam_spammer_comment_count', - type => 't', - default => '3', - checker => \&check_numeric - }, - { - name => 'antispam_spammer_disable_text', - type => 'l', - default => - "This account has been automatically disabled as a result of " . - "a high number of spam comments.<br>\n<br>\n" . - "Please contact the address at the end of this message if " . - "you believe this to be an error." - }, - { - name => 'antispam_abusive_comment_count', - type => 't', - default => '5', - checker => \&check_numeric - }, - { - name => 'antispam_abusive_disable_text', - type => 'l', - default => - "This account has been automatically disabled as a result of " . - "a high number of comments tagged as abusive.<br>\n<br>\n" . - "All interactions on Bugzilla should follow our " . - "<a href=\"" . Bugzilla->localconfig->{'urlbase'} . "page.cgi?id=etiquette.html\">" . - "etiquette guidelines</a>.<br>\n<br>\n" . - "Please contact the address at the end of this message if you " . - "believe this to be an error, or if you would like your account " . - "reactivated in order to interact within our etiquette " . - "guidelines." - }, - { - name => 'antispam_multi_user_limit_age', - type => 't', - default => '2', - checker => \&check_numeric, - }, - { - name => 'antispam_multi_user_limit_count', - type => 't', - default => '5', - checker => \&check_numeric, - }, - ); + my @param_list = ( + { + name => 'antispam_spammer_exclude_group', + type => 's', + choices => \&get_all_group_names, + default => 'canconfirm', + checker => \&check_group + }, + { + name => 'antispam_spammer_comment_count', + type => 't', + default => '3', + checker => \&check_numeric + }, + { + name => 'antispam_spammer_disable_text', + type => 'l', + default => "This account has been automatically disabled as a result of " + . "a high number of spam comments.<br>\n<br>\n" + . "Please contact the address at the end of this message if " + . "you believe this to be an error." + }, + { + name => 'antispam_abusive_comment_count', + type => 't', + default => '5', + checker => \&check_numeric + }, + { + name => 'antispam_abusive_disable_text', + type => 'l', + default => "This account has been automatically disabled as a result of " + . "a high number of comments tagged as abusive.<br>\n<br>\n" + . "All interactions on Bugzilla should follow our " + . "<a href=\"" + . Bugzilla->localconfig->{'urlbase'} + . "page.cgi?id=etiquette.html\">" + . "etiquette guidelines</a>.<br>\n<br>\n" + . "Please contact the address at the end of this message if you " + . "believe this to be an error, or if you would like your account " + . "reactivated in order to interact within our etiquette " + . "guidelines." + }, + { + name => 'antispam_multi_user_limit_age', + type => 't', + default => '2', + checker => \&check_numeric, + }, + { + name => 'antispam_multi_user_limit_count', + type => 't', + default => '5', + checker => \&check_numeric, + }, + ); - return @param_list; + return @param_list; } 1; |