From 23d2e57527cc870c7216043b4e0b3b633eabf88e Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Fri, 13 Sep 2013 21:48:19 +0000 Subject: [PATCH] Added ability to lock factoids to prevent editing; improved detection of ban-evasion by devalidating accounts on part/quit and devalidating existing accounts that match a banmask when a ban occurs --- PBot/AntiFlood.pm | 41 ++++++++++++++++++++++++++++++++++++++--- PBot/BanTracker.pm | 12 ++++++------ PBot/FactoidCommands.pm | 9 +++++++++ PBot/VERSION.pm | 2 +- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/PBot/AntiFlood.pm b/PBot/AntiFlood.pm index 0b4f9819..55ca46b6 100644 --- a/PBot/AntiFlood.pm +++ b/PBot/AntiFlood.pm @@ -65,7 +65,7 @@ sub ban_whitelisted { $mask = lc $mask; $self->{pbot}->logger->log("whitelist check: $channel, $mask\n"); - return defined $self->{ban_whitelist}->hash->{$channel}->{$mask}->{ban_whitelisted} ? 1 : 0; + return (exists $self->{ban_whitelist}->hash->{$channel}->{$mask} and defined $self->{ban_whitelist}->hash->{$channel}->{$mask}->{ban_whitelisted}) ? 1 : 0; } sub whitelist { @@ -175,6 +175,8 @@ sub add_message { $self->message_history->{$account}->{channels}->{$channel}{join_watch}++; } else { # PART or QUIT + $self->message_history->{$account}->{channels}->{$channel}{validated} = 0; + # check QUIT message for netsplits, and decrement joinwatch if found if($text =~ /^QUIT .*\.net .*\.split/) { $self->message_history->{$account}->{channels}->{$channel}{join_watch}--; @@ -396,7 +398,7 @@ sub prune_message_history { next unless $length > 0; my %last = %{ @{ $self->{message_history}->{$mask}->{channels}->{$channel}{messages} }[$length - 1] }; - # delete channel key if no activity within 3 days + # delete channel key if no activity for a while if(gettimeofday - $last{timestamp} >= 60 * 60 * 24 * 90) { $self->{pbot}->logger->log("$mask in $channel hasn't spoken in ninety days; removing channel history.\n"); delete $self->{message_history}->{$mask}->{channels}->{$channel}; @@ -491,6 +493,39 @@ sub address_to_mask { return $banmask; } +sub devalidate_accounts { + # remove validation on accounts in $channel that match a ban/quiet $mask + my ($self, $mask, $channel) = @_; + my ($nickserv_account, $ban_account); + + if($mask =~ m/^\$a:(.*)/) { + $ban_account = lc $1; + } else { + $ban_account = ""; + } + + my $mask_original = $mask; + $mask = quotemeta $mask; + $mask =~ s/\\\*/.*?/g; + $mask =~ s/\\\?/./g; + + foreach my $account (keys %{ $self->{message_history} }) { + if(exists $self->{message_history}->{$account}->{channels}->{$channel}) { + if(exists $self->{message_history}->{$account}->{nickserv_account}) { + $nickserv_account = $self->{message_history}->{$account}->{nickserv_account}; + } else { + $nickserv_account = undef; + } + + if((defined $nickserv_account and $nickserv_account eq $ban_account) or $account =~ m/^$mask$/i) { + $self->{pbot}->logger->log("anti-flood: [devalidate-accounts] $account matches $mask_original, devalidating\n"); + + $self->message_history->{$mask}->{channels}->{$channel}{validated} = 0; + } + } + } +} + sub check_bans { my ($self, $mask, $channel) = @_; my ($bans, $nickserv_account, $nick, $host); @@ -552,7 +587,7 @@ sub check_bans { CHECKBAN: if($check_ban) { - # $self->{pbot}->logger->log("anti-flood: [check-bans] checking for bans in $channel on $account\n"); + # $self->{pbot}->logger->log("anti-flood: [check-bans] checking for bans in $channel on $account using $target_nickserv_account\n"); my $baninfos = $self->{pbot}->bantracker->get_baninfo($account, $channel, $target_nickserv_account); if(defined $baninfos) { diff --git a/PBot/BanTracker.pm b/PBot/BanTracker.pm index dbb3975a..8a0f78fc 100644 --- a/PBot/BanTracker.pm +++ b/PBot/BanTracker.pm @@ -70,18 +70,17 @@ sub get_baninfo { foreach my $mode (keys %{ $self->{banlist}{$channel} }) { foreach my $banmask (keys %{ $self->{banlist}{$channel}{$mode} }) { - my $banmask_key = $banmask; - $banmask = quotemeta $banmask; - - $banmask =~ s/\\\*/.*?/g; - $banmask =~ s/\\\?/./g; - if($banmask =~ m/^\$a:(.*)/) { $ban_account = lc $1; } else { $ban_account = ""; } + my $banmask_key = $banmask; + $banmask = quotemeta $banmask; + $banmask =~ s/\\\*/.*?/g; + $banmask =~ s/\\\?/./g; + if((defined $account and $account eq $ban_account) or $mask =~ m/^$banmask$/i) { if(not defined $bans) { $bans = []; @@ -136,6 +135,7 @@ sub track_mode { if($mode eq "+b" or $mode eq "+q") { $self->{pbot}->logger->log("ban-tracker: $target " . ($mode eq '+b' ? 'banned' : 'quieted') . " by $source in $channel.\n"); $self->{banlist}->{$channel}->{$mode}->{$target} = [ $source, gettimeofday ]; + $self->{pbot}->antiflood->devalidate_accounts($target, $channel); } elsif($mode eq "-b" or $mode eq "-q") { $self->{pbot}->logger->log("ban-tracker: $target " . ($mode eq '-b' ? 'unbanned' : 'unquieted') . " by $source in $channel.\n"); diff --git a/PBot/FactoidCommands.pm b/PBot/FactoidCommands.pm index f356d5e4..de4d51a2 100644 --- a/PBot/FactoidCommands.pm +++ b/PBot/FactoidCommands.pm @@ -41,6 +41,7 @@ my %factoid_metadata_levels = ( type => 60, edited_by => 60, edited_on => 60, + locked => 10, # all others are allowed to be factset by anybody/default to level 0 ); @@ -425,6 +426,10 @@ sub factrem { return "/msg $nick You are not the owner of '$trigger' for $chan"; } + if(exists $factoids->{$channel}->{$trigger}->{'locked'} and $factoids->{$channel}->{$trigger}->{'locked'} != 0) { + return "$trigger is locked; unlock before deleting."; + } + $self->{pbot}->logger->log("$nick!$user\@$host removed [$channel][$trigger][" . $factoids->{$channel}->{$trigger}->{action} . "]\n"); $self->{pbot}->factoids->remove_factoid($channel, $trigger); return "/msg $nick $trigger removed from " . ($channel eq '.*' ? 'the global channel' : $channel) . "."; @@ -738,6 +743,10 @@ sub factchange { return "/msg $nick $keyword not found in channel $from."; } + if(not $self->{pbot}->admins->loggedin($from, "$nick!$user\@$host") and exists $factoids->{$channel}->{$trigger}->{'locked'} and $factoids->{$channel}->{$trigger}->{'locked'} != 0) { + return "$trigger is locked and cannot be changed."; + } + my $ret = eval { if(not $factoids->{$channel}->{$trigger}->{action} =~ s|$tochange|$changeto|) { $self->{pbot}->logger->log("($from) $nick!$user\@$host: failed to change '$trigger' 's$delim$tochange$delim$changeto$delim\n"); diff --git a/PBot/VERSION.pm b/PBot/VERSION.pm index 76a2c5e6..238b13da 100644 --- a/PBot/VERSION.pm +++ b/PBot/VERSION.pm @@ -13,7 +13,7 @@ use warnings; # These are set automatically by the build/commit script use constant { BUILD_NAME => "PBot", - BUILD_REVISION => 430, + BUILD_REVISION => 431, BUILD_DATE => "2013-09-13", };