diff options
12 files changed, 637 insertions, 68 deletions
diff --git a/extensions/AntiSpam/Extension.pm b/extensions/AntiSpam/Extension.pm index 5cb9928db..ef31a0b2d 100644 --- a/extensions/AntiSpam/Extension.pm +++ b/extensions/AntiSpam/Extension.pm @@ -12,7 +12,178 @@ use warnings; use base qw(Bugzilla::Extension); -our $VERSION = '0'; +use Bugzilla::Error; +use Bugzilla::Group; +use Bugzilla::Util qw(remote_ip trick_taint); +use Email::Address; +use Encode; +use Socket; +use Sys::Syslog qw(:DEFAULT setlogsock); + +our $VERSION = '1'; + +# +# project honeypot integration +# + +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}; + + _syslog(sprintf("[audit] 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', + }; +} + +# +# comment blocking +# + +sub _comment_blocking { + my ($self, $params) = @_; + + # as we want to use this sparingly, we only block comments on bugs which + # the user didn't report, and skip it completely if the user is in the + # editbugs group. + my $user = Bugzilla->user; + return if $user->in_group('editbugs'); + # new bug + return unless $params->{bug_id}; + # existing bug + my $bug = ref($params->{bug_id}) + ? $params->{bug_id} + : Bugzilla::Bug->new($params->{bug_id}); + return if $bug->reporter->id == $user->id; + + 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) { + ThrowUserError('antispam_comment_blocked'); + } +} + +# +# domain 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) { + _syslog(sprintf("[audit] blocked <%s> from creating %s, blacklisted domain", remote_ip(), $login)); + ThrowUserError('account_creation_restricted'); + } +} + +# +# ip 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) { + _syslog(sprintf("[audit] blocked <%s> from creating %s, blacklisted IP", $ip, $login)); + ThrowUserError('account_creation_restricted'); + } +} + +# +# hooks +# + +sub object_end_of_create_validators { + 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}); +} + +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', + }; +} + +# +# installation +# + +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, + }); + } +} sub db_schema_abstract_schema { my ($self, $args) = @_; @@ -83,4 +254,15 @@ sub db_schema_abstract_schema { }; } +# +# utilities +# + +sub _syslog { + my $message = shift; + openlog('apache', 'cons,pid', 'local4'); + syslog('notice', encode_utf8($message)); + closelog(); +} + __PACKAGE__->NAME; diff --git a/extensions/AntiSpam/template/en/default/hook/admin/admin-end_links_right.html.tmpl b/extensions/AntiSpam/template/en/default/hook/admin/admin-end_links_right.html.tmpl new file mode 100644 index 000000000..e55475d98 --- /dev/null +++ b/extensions/AntiSpam/template/en/default/hook/admin/admin-end_links_right.html.tmpl @@ -0,0 +1,16 @@ +[%# 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. + #%] + +[% IF user.in_group('can_configure_antispam') %] + <dt id="antispam" >AntiSpam</dt> + <dd> + <a href="page.cgi?id=edit_table.html&table=antispam_domain_blocklist">Domain Blocklist</a><br> + <a href="page.cgi?id=edit_table.html&table=antispam_comment_blocklist">Comment Blocklist</a><br> + <a href="page.cgi?id=edit_table.html&table=antispam_ip_blocklist">IP Address Blocklist</a><br> + </dd> +[% END %] diff --git a/extensions/ProjectHoneyPot/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl b/extensions/AntiSpam/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl index e8e67eccb..e8e67eccb 100644 --- a/extensions/ProjectHoneyPot/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl +++ b/extensions/AntiSpam/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl diff --git a/extensions/AntiSpam/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/AntiSpam/template/en/default/hook/global/user-error-errors.html.tmpl new file mode 100644 index 000000000..44410ca2f --- /dev/null +++ b/extensions/AntiSpam/template/en/default/hook/global/user-error-errors.html.tmpl @@ -0,0 +1,13 @@ +[%# 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. + #%] + +[% IF error == "antispam_comment_blocked" %] + [% title = "Comment Blocked" %] + Your comment contains one or more words deemed inappropriate for use by the + administrators of this site. +[% END %] diff --git a/extensions/ProjectHoneyPot/Config.pm b/extensions/EditTable/Config.pm index 83d8d313e..d601951a4 100644 --- a/extensions/ProjectHoneyPot/Config.pm +++ b/extensions/EditTable/Config.pm @@ -5,11 +5,11 @@ # This Source Code Form is "Incompatible With Secondary Licenses", as # defined by the Mozilla Public License, v. 2.0. -package Bugzilla::Extension::ProjectHoneyPot; +package Bugzilla::Extension::EditTable; use strict; -use constant NAME => 'ProjectHoneyPot'; -use constant REQUIRED_MODULES => [ ]; -use constant OPTIONAL_MODULES => [ ]; +use constant NAME => 'EditTable'; +use constant REQUIRED_MODULES => []; +use constant OPTIONAL_MODULES => []; __PACKAGE__->NAME; diff --git a/extensions/EditTable/Extension.pm b/extensions/EditTable/Extension.pm new file mode 100644 index 000000000..a10a30e57 --- /dev/null +++ b/extensions/EditTable/Extension.pm @@ -0,0 +1,180 @@ +# 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. + + +# this is a quick and dirty table editor, designed to allow admins to quickly +# maintain tables. +# +# each table must be defined via the editable_tables hook +# +# this extension doesn't currently provide any ability to modify or validate +# values. use with caution! + +package Bugzilla::Extension::EditTable; + +use strict; +use warnings; + +use base qw(Bugzilla::Extension); + +use Bugzilla::Error; +use Bugzilla::Hook; +use Bugzilla::Util qw(trick_taint); +use JSON; +use Storable qw(dclone); + +our $VERSION = '1'; + +# definitions for tables which we can edit with the quick-and-dirty editor +# +# $table_name => { +# id_field => name of the "id" field +# order_by => the field to sort rows by (optional, defaults to the id_field) +# blurb => text which describes the table +# group => group required to edit this table (optional, defaults to "admin") +# } +# +# example: +# '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', +# }, + +sub EDITABLE_TABLES { + my $tables = {}; + Bugzilla::Hook::process("editable_tables", { tables => $tables }); + return $tables; +} + +sub page_before_template { + my ($self, $args) = @_; + my ($vars, $page) = @$args{qw(vars page_id)}; + return unless $page eq 'edit_table.html'; + my $input = Bugzilla->input_params; + + # we only support editing a particular set of tables + my $table_name = $input->{table}; + exists $self->EDITABLE_TABLES()->{$table_name} + || ThrowUserError('edittable_unsupported', { table => $table_name } ); + my $table = $self->EDITABLE_TABLES()->{$table_name}; + my $id_field = $table->{id_field}; + my $order_by = $table->{order_by} || $id_field; + my $group = $table->{group} || 'admin'; + trick_taint($table_name); + + Bugzilla->user->in_group($group) + || ThrowUserError('auth_failure', { group => $group, + action => 'edit', + object => 'tables' }); + + # load columns + my $dbh = Bugzilla->dbh; + my @fields = sort + grep { $_ ne $id_field && $_ ne $order_by; } + $dbh->bz_table_columns($table_name); + if ($order_by ne $id_field) { + unshift @fields, $order_by; + } + + # update table + my $data = $input->{table_data}; + my $edits = []; + if ($data) { + $data = from_json($data)->{data}; + $edits = dclone($data); + eval { + $dbh->bz_start_transaction; + + foreach my $row (@$data) { + map { trick_taint($_) } @$row; + if ($row->[0] eq '-') { + # add + shift @$row; + next unless grep { $_ ne '' } @$row; + my $placeholders = join(',', split(//, '?' x scalar(@fields))); + $dbh->do( + "INSERT INTO $table_name(" . join(',', @fields) . ") " . + "VALUES ($placeholders)", + undef, + @$row + ); + } + elsif ($row->[0] < 0) { + # delete + $dbh->do( + "DELETE FROM $table_name WHERE $id_field=?", + undef, + -$row->[0] + ); + } + else { + # update + my $id = shift @$row; + $dbh->do( + "UPDATE $table_name " . + "SET " . join(',', map { "$_ = ?" } @fields) . " " . + "WHERE $id_field = ?", + undef, + @$row, $id + ); + } + } + + $dbh->bz_commit_transaction; + $vars->{updated} = 1; + $edits = []; + }; + if ($@) { + my $error = $@; + $error =~ s/^DBD::[^:]+::db do failed: //; + $error =~ s/^(.+) \[for Statement ".+$/$1/s; + $vars->{error} = $error; + $dbh->bz_rollback_transaction; + } + } + + # load data from table + unshift @fields, $id_field; + $data = $dbh->selectall_arrayref( + "SELECT " . join(',', @fields) . " FROM $table_name ORDER BY $order_by" + ); + + # we don't support nulls currently + foreach my $row (@$data) { + if (grep { !defined($_) } @$row) { + ThrowUserError('edittable_nulls', { table => $table_name } ); + } + } + + # apply failed edits + foreach my $edit (@$edits) { + if ($edit->[0] eq '-') { + push @$data, $edit; + } + else { + my $id = $edit->[0]; + foreach my $row (@$data) { + if ($row->[0] == $id) { + @$row = @$edit; + last; + } + } + } + } + + $vars->{table_name} = $table_name; + $vars->{blurb} = $table->{blurb}; + $vars->{table_data} = to_json({ + fields => \@fields, + id_field => $id_field, + data => $data, + }); +} + +__PACKAGE__->NAME; diff --git a/extensions/EditTable/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl b/extensions/EditTable/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl new file mode 100644 index 000000000..f86fb4c86 --- /dev/null +++ b/extensions/EditTable/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl @@ -0,0 +1,11 @@ +[%# 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. + #%] + +[% IF object == 'tables' %] + tables +[% END %] diff --git a/extensions/EditTable/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/EditTable/template/en/default/hook/global/user-error-errors.html.tmpl new file mode 100644 index 000000000..d87270b98 --- /dev/null +++ b/extensions/EditTable/template/en/default/hook/global/user-error-errors.html.tmpl @@ -0,0 +1,17 @@ +[%# 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. + #%] + +[% IF error == "edittable_unsupported" %] + [% title = "Unsupported Table" %] + You cannot edit the table '[% table FILTER html %]'. + +[% ELSIF error == "edittable_nulls" %] + [% title = "Table Contains NULLs" %] + EditTable cannot edit the table '[% table FILTER html %]' as it contains NULL + values. +[% END %] diff --git a/extensions/EditTable/template/en/default/pages/edit_table.html.tmpl b/extensions/EditTable/template/en/default/pages/edit_table.html.tmpl new file mode 100644 index 000000000..d81291640 --- /dev/null +++ b/extensions/EditTable/template/en/default/pages/edit_table.html.tmpl @@ -0,0 +1,43 @@ +[%# 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. + #%] + +[% PROCESS global/variables.none.tmpl %] + +[% PROCESS global/header.html.tmpl + title = "Edit Table" + javascript_urls = [ 'extensions/EditTable/web/js/edit_table.js' ] + style_urls = [ "extensions/EditTable/web/styles/edit_table.css" ] +%] + +<h2>[% table_name FILTER html %]</h2> + +[% IF updated %] + <p id="message">Table [% table_name FILTER html %] updated.</p> +[% ELSIF error %] + <p class="throw_error">[% error FILTER html FILTER html_line_break %]</p> +[% END %] + +<p>[% blurb FILTER html FILTER html_line_break %]</p> + +<div id="edit_table"></div> +<br> +<form method="post" action="page.cgi" enctype="multipart/form-data" + onsubmit="editTable.to_json('table_data')"> +<input type="hidden" name="id" value="edit_table.html"> +<input type="hidden" name="table" value="[% table_name FILTER html %]"> +<input type="hidden" name="table_data" id="table_data"> +<input type="submit" value="Commit Changes" id="commit_btn" class="bz_default_hidden"> +</form> + +<script> + var table_data = [% table_data FILTER none %]; + var editTable = new EditTable('edit_table', table_data); + editTable.render(); +</script> + +[% PROCESS global/footer.html.tmpl %] diff --git a/extensions/EditTable/web/js/edit_table.js b/extensions/EditTable/web/js/edit_table.js new file mode 100644 index 000000000..ae239759b --- /dev/null +++ b/extensions/EditTable/web/js/edit_table.js @@ -0,0 +1,131 @@ +/* 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. */ + + +function EditTable(parent_el, table_data) { + this.parent_el = YAHOO.util.Dom.get(parent_el); + this.table_data = table_data; + this.field_count = table_data.fields.length; + if (!JSON) JSON = YAHOO.lang.JSON; + + this.render = function() { + // create table + this.parent_el.innerHTML = ''; + var table = document.createElement('table'); + + // header + var tr = document.createElement('tr'); + for (var i = 0; i < this.field_count; i++) { + var th = document.createElement('th'); + th.appendChild(document.createTextNode(this.table_data.fields[i])); + tr.appendChild(th); + } + var td = document.createElement('td'); + td.innerHTML = ' '; + tr.appendChild(td); + table.appendChild(tr); + + // rows + for (var i = 0; i < table_data.data.length; i++) { + // skip deleted rows + if (this.table_data.data[i][0] < 0) + continue; + var tr = document.createElement('tr'); + for (var j = 0; j < this.field_count; j++) { + var td = document.createElement('td'); + td.appendChild(document.createTextNode(this.table_data.data[i][j])); + tr.appendChild(td); + + if (this.table_data.fields[j] != this.table_data.id_field) { + td.className = 'editable'; + td.contentEditable = true; + YAHOO.util.Event.addListener(td, 'keydown', this._edit_keydown, this); + YAHOO.util.Event.addListener(td, 'blur', this._save, this); + d = td; + } + } + var td = document.createElement('td'); + var a = document.createElement('a'); + a.href = '#'; + a.innerHTML = 'x'; + YAHOO.util.Event.addListener(a, 'click', this._remove_row, this); + td.appendChild(a); + td.className = 'action'; + tr.appendChild(td); + table.appendChild(tr); + } + + this.parent_el.appendChild(table); + + var add_btn = document.createElement('button'); + add_btn.innerHTML = 'Add'; + YAHOO.util.Event.addListener(add_btn, 'click', this._add_row, this); + this.parent_el.appendChild(add_btn); + }, + + this.to_json = function(target) { + YAHOO.util.Dom.get(target).value = JSON.stringify(this.table_data); + }, + + this._add_row = function(event, obj) { + var row = []; + for (var i = 0; i < obj.field_count; i++) { + row.push(obj.table_data.fields[i] == obj.table_data.id_field ? '-' : ''); + } + obj.table_data.data.push(row); + obj.render(); + YAHOO.util.Dom.removeClass('commit_btn', 'bz_default_hidden'); + event.preventDefault(); + }, + + this._remove_row = function(event, obj) { + var row = event.target.parentElement.parentElement.rowIndex - 1; + if (obj.table_data.data[row][0] == '-') { + // removing a newly added row + obj.table_data.data.splice(row, 1); + } + else { + // to remove a db row we set its id to negative + // it'll be skipped by render, and the update script knows which id to delete + obj.table_data.data[row][0] = -obj.table_data.data[row][0]; + } + obj.render(); + YAHOO.util.Dom.removeClass('commit_btn', 'bz_default_hidden'); + event.preventDefault(); + }, + + this._save = function(event, obj) { + var row = event.target.parentElement.rowIndex - 1; + var col = event.target.cellIndex; + var value = event.target.textContent; + if (obj.table_data.data[row][col] != event.target.textContent) { + obj.table_data.data[row][col] = event.target.textContent; + YAHOO.util.Dom.removeClass('commit_btn', 'bz_default_hidden'); + } + }, + + this._revert = function(event, obj) { + var row = event.target.parentElement.rowIndex - 1; + var col = event.target.cellIndex; + event.target.replaceChild( + document.createTextNode(obj.table_data.data[row][col]), + event.target.firstChild + ); + }, + + this._edit_keydown = function(event, obj) { + if (event.keyCode == 13) { + event.preventDefault(); + obj._save(event, obj); + document.activeElement.blur(event.target); + } + else if (event.keyCode == 27) { + event.preventDefault(); + obj._revert(event, obj); + } + } +}; diff --git a/extensions/EditTable/web/styles/edit_table.css b/extensions/EditTable/web/styles/edit_table.css new file mode 100644 index 000000000..0b1c72db6 --- /dev/null +++ b/extensions/EditTable/web/styles/edit_table.css @@ -0,0 +1,39 @@ +/* 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. */ + + +#edit_table table { + border-spacing: 0; + border-collapse: collapse; + margin-bottom: 1em; +} + +#edit_table td, #edit_table th { + padding: 5px; +} + +#edit_table th { + background: #ccc; + text-align: left; +} + +#edit_table .editable { + background: #fff; +} + +#edit_table tr:hover { + background: #eee; +} + +#edit_table .action { + display: none; +} + +#edit_table tr:hover .action { + display: block; +} + diff --git a/extensions/ProjectHoneyPot/Extension.pm b/extensions/ProjectHoneyPot/Extension.pm deleted file mode 100644 index 856fe7f1e..000000000 --- a/extensions/ProjectHoneyPot/Extension.pm +++ /dev/null @@ -1,63 +0,0 @@ -# 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. - -package Bugzilla::Extension::ProjectHoneyPot; - -use strict; -use warnings; - -use base qw(Bugzilla::Extension); - -use Encode; -use Bugzilla::Error; -use Bugzilla::Util qw(remote_ip); -use Socket; -use Sys::Syslog qw(:DEFAULT setlogsock); - -our $VERSION = '1'; - -sub user_verify_login { - my ($self, $args) = @_; - return unless my $api_key = Bugzilla->params->{honeypot_api_key}; - 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}; - - _syslog(sprintf("[audit] blocked <%s> from creating %s, honeypot %s", - $ip, $args->{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', - }; -} - -sub _syslog { - my $message = shift; - openlog('apache', 'cons,pid', 'local4'); - syslog('notice', encode_utf8($message)); - closelog(); -} - -__PACKAGE__->NAME; |