summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Fulton <mikefultonpersonal@gmail.com>2022-01-14 16:23:47 -0800
committerKarl Williamson <khw@cpan.org>2022-01-19 18:15:54 -0700
commit0b66d70fe7769af01a6bea67bd2ea91b2404eb4b (patch)
tree786bb10be872bda482b2b8de7b5de699cc40c8bf
parent7c5e8757892abf1a831f67919b4c01339c8cac7c (diff)
downloadperl-0b66d70fe7769af01a6bea67bd2ea91b2404eb4b.tar.gz
Provide inclusive names allowlist and denylist
Updated the following files: AUTHORS: - corrected my email address MANIFEST: - added new testcases for Locale-Maketext dist/Locale-Maketext/ChangeLog: - short summary of changes made dist/Locale-Maketext/lib/Locale/Maketext.pm: - updated the version to 1.30 - refactored the code to create a common method _include called by the allowlist and whitelist methods (whitelist is now an alias, deprecated method, for allowlist). The _include method now has an internal list called allowlist that is updated by calls to _include through whitelist or allowlist methods. - refactored the code to create a common method _exclude called by the denylist and blacklist methods (blacklist is now an alias, deprecated method, for denylist). The _exclude method now has an internal list called denylist that is updated by calls to _exclude through blacklist or denylist methods. dist/Locale-Maketext/lib/Locale/Maketext.pod: - documented allowlist and denylist as new methods - added a NOTE for allowlist and denylist indicating that they are now the preferred method names to use instead of whitelist and blacklist dist/Locale-Maketext/t/92_blacklist.t - updated the code to have messages in terms of allowlist and denylist. Actual code continues to test blacklist method. dist/Locale-Maketext/t/93_whitelist.t - updated the code to have messages in terms of allowlist and denylist. Actual code continues to test whitelist method. Created 2 new testcases: dist/Locale-Maketext/t/94_denylist.t - this testcase is analagous to 92_blacklist.t but calls the denylist method instead of the blacklist method. dist/Locale-Maketext/t/95_allowlist.t - this testcase is analagous to 93_whitelist.t but calls the allowlist method instead of the whitelist method.
-rw-r--r--AUTHORS2
-rw-r--r--MANIFEST2
-rw-r--r--dist/Locale-Maketext/ChangeLog5
-rw-r--r--dist/Locale-Maketext/lib/Locale/Maketext.pm49
-rw-r--r--dist/Locale-Maketext/lib/Locale/Maketext.pod54
-rw-r--r--dist/Locale-Maketext/t/92_blacklist.t34
-rw-r--r--dist/Locale-Maketext/t/93_whitelist.t42
-rw-r--r--dist/Locale-Maketext/t/94_denylist.t93
-rw-r--r--dist/Locale-Maketext/t/95_allowlist.t96
9 files changed, 307 insertions, 70 deletions
diff --git a/AUTHORS b/AUTHORS
index 1ce7bb8349..3b75243c1f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -915,7 +915,7 @@ Michiel Beijen <mb@x14.nl>
Mik Firestone <fireston@lexmark.com>
Mike Doherty <mike@mikedoherty.ca>
Mike Fletcher <fletch@phydeaux.org>
-Mike Fulton <61100689+mikefultondev@users.noreply.github.com>
+Mike Fulton <mikefultonpersonal@gmail.com>
Mike Giroux <rmgiroux@acm.org>
Mike Guy <mjtg@cam.ac.uk>
Mike Heins <mike@bill.iac.net>
diff --git a/MANIFEST b/MANIFEST
index 9b70dd0a06..46d60b0c19 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -3828,6 +3828,8 @@ dist/Locale-Maketext/t/90_utf8.t See if Locale::Maketext works
dist/Locale-Maketext/t/91_backslash.t See if Locale::Maketext works
dist/Locale-Maketext/t/92_blacklist.t See if Locale::Maketext works
dist/Locale-Maketext/t/93_whitelist.t See if Locale::Maketext works
+dist/Locale-Maketext/t/94_denylist.t See if Locale::Maketext works
+dist/Locale-Maketext/t/95_allowlist.t See if Locale::Maketext works
dist/Module-CoreList/Changes Module::CoreList Changes
dist/Module-CoreList/corelist The corelist command-line utility
dist/Module-CoreList/identify-dependencies A usage example for Module::CoreList
diff --git a/dist/Locale-Maketext/ChangeLog b/dist/Locale-Maketext/ChangeLog
index d9b524f8ac..2edc0aedd9 100644
--- a/dist/Locale-Maketext/ChangeLog
+++ b/dist/Locale-Maketext/ChangeLog
@@ -1,5 +1,10 @@
Revision history for Perl suite Locale::Maketext
+2022-01-14
+ * Release 1.30 to CPAN
+ * Provide alternate methods allowlist and denylist for
+ whitelist and blacklist, respectively
+
2020-01-19
* Release 1.29 to CPAN
* Replace multiple 'use vars' by 'our'
diff --git a/dist/Locale-Maketext/lib/Locale/Maketext.pm b/dist/Locale-Maketext/lib/Locale/Maketext.pm
index f70438b78d..ca2242664d 100644
--- a/dist/Locale-Maketext/lib/Locale/Maketext.pm
+++ b/dist/Locale-Maketext/lib/Locale/Maketext.pm
@@ -25,7 +25,7 @@ BEGIN {
}
-our $VERSION = '1.29';
+our $VERSION = '1.30';
our @ISA = ();
our $MATCH_SUPERS = 1;
@@ -136,19 +136,20 @@ sub fail_with { # an actual attribute method!
#--------------------------------------------------------------------------
-sub blacklist {
- my ( $handle, @methods ) = @_;
+sub _exclude {
+ my ( $handle, @methods ) = @_;
- unless ( defined $handle->{'blacklist'} ) {
+ unless ( defined $handle->{'denylist'} ) {
no strict 'refs';
# Don't let people call methods they're not supposed to from maketext.
# Explicitly exclude all methods in this package that start with an
# underscore on principle.
- $handle->{'blacklist'} = {
+ $handle->{'denylist'} = {
map { $_ => 1 } (
qw/
blacklist
+ denylist
encoding
fail_with
failure_handler_auto
@@ -160,30 +161,55 @@ sub blacklist {
maketext
new
whitelist
+ allowlist
/, grep { /^_/ } keys %{ __PACKAGE__ . "::" }
),
};
}
if ( scalar @methods ) {
- $handle->{'blacklist'} = { %{ $handle->{'blacklist'} }, map { $_ => 1 } @methods };
+ $handle->{'denylist'} = { %{ $handle->{'denylist'} }, map { $_ => 1 } @methods };
}
delete $handle->{'_external_lex_cache'};
return;
}
-sub whitelist {
+sub blacklist {
+ my ( $handle, @methods ) = @_;
+ _exclude ( $handle, @methods );
+ return;
+}
+
+sub denylist {
+ my ( $handle, @methods ) = @_;
+ _exclude ( $handle, @methods );
+ return;
+}
+
+sub _include {
my ( $handle, @methods ) = @_;
if ( scalar @methods ) {
- $handle->{'whitelist'} = {} unless defined $handle->{'whitelist'};
- $handle->{'whitelist'} = { %{ $handle->{'whitelist'} }, map { $_ => 1 } @methods };
+ $handle->{'allowlist'} = {} unless defined $handle->{'allowlist'};
+ $handle->{'allowlist'} = { %{ $handle->{'allowlist'} }, map { $_ => 1 } @methods };
}
delete $handle->{'_external_lex_cache'};
return;
}
+sub whitelist {
+ my ( $handle, @methods ) = @_;
+ _include ( $handle, @methods );
+ return;
+}
+
+sub allowlist {
+ my ( $handle, @methods ) = @_;
+ _include ( $handle, @methods );
+ return;
+}
+
#--------------------------------------------------------------------------
sub failure_handler_auto {
@@ -228,6 +254,7 @@ sub new {
my $class = ref($_[0]) || $_[0];
my $handle = bless {}, $class;
$handle->blacklist;
+ $handle->denylist;
$handle->init;
return $handle;
}
@@ -679,8 +706,10 @@ sub _compile {
}
elsif($m =~ /^\w+$/s
&& !$handle->{'blacklist'}{$m}
+ && !$handle->{'denylist'}{$m}
&& ( !defined $handle->{'whitelist'} || $handle->{'whitelist'}{$m} )
- # exclude anything fancy and restrict to the whitelist/blacklist.
+ && ( !defined $handle->{'allowlist'} || $handle->{'allowlist'}{$m} )
+ # exclude anything fancy and restrict to the allowlist/denylist (and historical whitelist/blacklist).
) {
push @code, ' $_[0]->' . $m . '(';
}
diff --git a/dist/Locale-Maketext/lib/Locale/Maketext.pod b/dist/Locale-Maketext/lib/Locale/Maketext.pod
index 26be348353..3ea5ea478b 100644
--- a/dist/Locale-Maketext/lib/Locale/Maketext.pod
+++ b/dist/Locale-Maketext/lib/Locale/Maketext.pod
@@ -307,9 +307,9 @@ interested in hearing about it.)
These two methods are discussed in the section "Controlling
Lookup Failure".
-=item $lh->blacklist(@list)
+=item $lh->denylist(@list) <or> $lh->blacklist(@list)
-=item $lh->whitelist(@list)
+=item $lh->allowlist(@list) <or> $lh->whitelist(@list)
These methods are discussed in the section "Bracket Notation
Security".
@@ -875,17 +875,17 @@ bracket notation methods from normal class or object methods. This
design makes it vulnerable to format string attacks whenever it is
used to process strings provided by untrusted users.
-Locale::Maketext does support blacklist and whitelist functionality
+Locale::Maketext does support denylist and allowlist functionality
to limit which methods may be called as bracket notation methods.
-By default, Locale::Maketext blacklists all methods in the
+By default, Locale::Maketext denies all methods in the
Locale::Maketext namespace that begin with the '_' character,
and all methods which include Perl's namespace separator characters.
-The default blacklist for Locale::Maketext also prevents use of the
+The default denylist for Locale::Maketext also prevents use of the
following methods in bracket notation:
- blacklist
+ denylist
encoding
fail_with
failure_handler_auto
@@ -896,44 +896,56 @@ following methods in bracket notation:
language_tag
maketext
new
+ allowlist
whitelist
+ blacklist
-This list can be extended by either blacklisting additional "known bad"
-methods, or whitelisting only "known good" methods.
+This list can be extended by either deny-listing additional "known bad"
+methods, or allow-listing only "known good" methods.
To prevent specific methods from being called in bracket notation, use
-the blacklist() method:
+the denylist() method:
my $lh = MyProgram::L10N->get_handle();
- $lh->blacklist(qw{my_internal_method my_other_method});
+ $lh->denylist(qw{my_internal_method my_other_method});
$lh->maketext('[my_internal_method]'); # dies
To limit the allowed bracked notation methods to a specific list, use the
-whitelist() method:
+allowlist() method:
my $lh = MyProgram::L10N->get_handle();
- $lh->whitelist('numerate', 'numf');
+ $lh->allowlist('numerate', 'numf');
$lh->maketext('[_1] [numerate, _1,shoe,shoes]', 12); # works
$lh->maketext('[my_internal_method]'); # dies
-The blacklist() and whitelist() methods extend their internal lists
-whenever they are called. To reset the blacklist or whitelist, create
+The denylist() and allowlist() methods extend their internal lists
+whenever they are called. To reset the denylist or allowlist, create
a new maketext object.
my $lh = MyProgram::L10N->get_handle();
- $lh->blacklist('numerate');
- $lh->blacklist('numf');
+ $lh->denylist('numerate');
+ $lh->denylist('numf');
$lh->maketext('[_1] [numerate,_1,shoe,shoes]', 12); # dies
For lexicons that use an internal cache, translations which have already
been cached in their compiled form are not affected by subsequent changes
-to the whitelist or blacklist settings. Lexicons that use an external
-cache will have their cache cleared whenever the whitelist of blacklist
-setings change. The difference between the two types of caching is explained
+to the allowlist or denylist settings. Lexicons that use an external
+cache will have their cache cleared whenever the allowlist or denylist
+settings change. The difference between the two types of caching is explained
in the "Readonly Lexicons" section.
-Methods disallowed by the blacklist cannot be permitted by the
-whitelist.
+Methods disallowed by the denylist cannot be permitted by the
+allowlist.
+
+NOTE: denylist() is the preferred method name to use instead of the
+historical and non-inclusive method blacklist(). blacklist() may be
+removed in a future release of this package and so it's use should be
+removed from usage.
+
+NOTE: allowlist() is the preferred method name to use instead of the
+historical and non-inclusive method whitelist(). whitelist() may be
+removed in a future release of this package and so it's use should be
+removed from usage.
=head1 AUTO LEXICONS
diff --git a/dist/Locale-Maketext/t/92_blacklist.t b/dist/Locale-Maketext/t/92_blacklist.t
index 6ed36d1edd..6605d835ce 100644
--- a/dist/Locale-Maketext/t/92_blacklist.t
+++ b/dist/Locale-Maketext/t/92_blacklist.t
@@ -48,46 +48,46 @@ my $res;
# get_handle blocked by default
$res = eval { $lh->maketext('[get_handle,en]') };
is( $res, undef, 'no return value from blocked expansion' );
-like( $@, qr/Can't use .* as a method name/, 'get_handle blocked in bracket notation by default blacklist' );
+like( $@, qr/Can't use .* as a method name/, 'get_handle blocked in bracket notation by default denylist' );
# _ambient_langprefs blocked by default
$res = eval { $lh->maketext('[_ambient_langprefs]') };
is( $res, undef, 'no return value from blocked expansion' );
-like( $@, qr/Can't use .* as a method name/, '_ambient_langprefs blocked in bracket notation by default blacklist' );
+like( $@, qr/Can't use .* as a method name/, '_ambient_langprefs blocked in bracket notation by default denylist' );
# _internal_method not blocked by default
$res = eval { $lh->maketext('[_internal_method]') };
-is( $res, "_internal_method_response", '_internal_method allowed in bracket notation by default blacklist' );
-is( $@, '', 'no exception thrown by use of _internal_method under default blacklist' );
+is( $res, "_internal_method_response", '_internal_method allowed in bracket notation by default denylist' );
+is( $@, '', 'no exception thrown by use of _internal_method under default denylist' );
# sprintf not blocked by default
$res = eval { $lh->maketext('[sprintf,%s,hello]') };
-is( $res, "hello", 'sprintf allowed in bracket notation by default blacklist' );
-is( $@, '', 'no exception thrown by use of sprintf under default blacklist' );
+is( $res, "hello", 'sprintf allowed in bracket notation by default denylist' );
+is( $@, '', 'no exception thrown by use of sprintf under default denylist' );
-# blacklisting sprintf and numerate
+# denylisting sprintf and numerate
$lh->blacklist( 'sprintf', 'numerate' );
-# sprintf blocked by custom blacklist
+# sprintf blocked by custom denylist
$res = eval { $lh->maketext('[sprintf,%s,hello]') };
is( $res, undef, 'no return value from blocked expansion' );
-like( $@, qr/Can't use .* as a method name/, 'sprintf blocked in bracket notation by custom blacklist' );
+like( $@, qr/Can't use .* as a method name/, 'sprintf blocked in bracket notation by custom denylist' );
-# blacklisting numf and _internal_method
+# denylisting numf and _internal_method
$lh->blacklist('numf');
$lh->blacklist('_internal_method');
-# sprintf blocked by custom blacklist
+# sprintf blocked by custom denylist
$res = eval { $lh->maketext('[sprintf,%s,hello]') };
is( $res, undef, 'no return value from blocked expansion' );
-like( $@, qr/Can't use .* as a method name/, 'sprintf blocked in bracket notation by custom blacklist after extension of blacklist' );
+like( $@, qr/Can't use .* as a method name/, 'sprintf blocked in bracket notation by custom denylist after extension of denylist' );
-# _internal_method blocked by custom blacklist
+# _internal_method blocked by custom denylist
$res = eval { $lh->maketext('[_internal_method]') };
is( $res, undef, 'no return value from blocked expansion' );
-like( $@, qr/Can't use .* as a method name/, 'sprintf blocked in bracket notation by custom blacklist after extension of blacklist' );
+like( $@, qr/Can't use .* as a method name/, 'sprintf blocked in bracket notation by custom denylist after extension of denylist' );
-# custom_handler not in default or custom blacklist
+# custom_handler not in default or custom denylist
$res = eval { $lh->maketext('[custom_handler]') };
-is( $res, "custom_handler_response", 'custom_handler allowed in bracket notation by default and custom blacklists' );
-is( $@, '', 'no exception thrown by use of custom_handler under default and custom blacklists' );
+is( $res, "custom_handler_response", 'custom_handler allowed in bracket notation by default and custom denylist' );
+is( $@, '', 'no exception thrown by use of custom_handler under default and custom denylist' );
diff --git a/dist/Locale-Maketext/t/93_whitelist.t b/dist/Locale-Maketext/t/93_whitelist.t
index 21f2d8574e..15e6678a8f 100644
--- a/dist/Locale-Maketext/t/93_whitelist.t
+++ b/dist/Locale-Maketext/t/93_whitelist.t
@@ -47,50 +47,50 @@ my $res;
# _internal_method not blocked by default
$res = eval { $lh->maketext('[_internal_method]') };
-is( $res, "_internal_method_response", '_internal_method allowed when no whitelist defined' );
-is( $@, '', 'no exception thrown by use of _internal_method without whitelist setting' );
+is( $res, "_internal_method_response", '_internal_method allowed when no allowlist defined' );
+is( $@, '', 'no exception thrown by use of _internal_method without allowlist setting' );
-# whitelisting sprintf
+# allowlisting sprintf
$lh->whitelist('sprintf');
-# _internal_method blocked by whitelist
+# _internal_method blocked by allowlist
$res = eval { $lh->maketext('[_internal_method]') };
is( $res, undef, 'no return value from blocked expansion' );
-like( $@, qr/Can't use .* as a method name/, '_internal_method blocked in bracket notation by whitelist' );
+like( $@, qr/Can't use .* as a method name/, '_internal_method blocked in bracket notation by allowlist' );
-# sprintf allowed by whitelist
+# sprintf allowed by allowlist
$res = eval { $lh->maketext('[sprintf,%s,hello]') };
-is( $res, "hello", 'sprintf allowed in bracket notation by whitelist' );
-is( $@, '', 'no exception thrown by use of sprintf with whitelist' );
+is( $res, "hello", 'sprintf allowed in bracket notation by allowlist' );
+is( $@, '', 'no exception thrown by use of sprintf with allowlist' );
-# custom_handler blocked by whitelist
+# custom_handler blocked by allowlist
$res = eval { $lh->maketext('[custom_handler]') };
is( $res, undef, 'no return value from blocked expansion' );
-like( $@, qr/Can't use .* as a method name/, 'custom_handler blocked in bracket notation by whitelist' );
+like( $@, qr/Can't use .* as a method name/, 'custom_handler blocked in bracket notation by allowlist' );
-# adding custom_handler to whitelist
+# adding custom_handler to allowlist
$lh->whitelist('custom_handler');
-# sprintf still allowed by whitelist
+# sprintf still allowed by allowlist
$res = eval { $lh->maketext('[sprintf,%s,hello]') };
-is( $res, "hello", 'sprintf allowed in bracket notation by whitelist' );
-is( $@, '', 'no exception thrown by use of sprintf with whitelist' );
+is( $res, "hello", 'sprintf allowed in bracket notation by allowlist' );
+is( $@, '', 'no exception thrown by use of sprintf with allowlist' );
-# custom_handler allowed by whitelist
+# custom_handler allowed by allowlist
$res = eval { $lh->maketext('[custom_handler]') };
-is( $res, "custom_handler_response", 'custom_handler allowed in bracket notation by whitelist' );
-is( $@, '', 'no exception thrown by use of custom_handler with whitelist' );
+is( $res, "custom_handler_response", 'custom_handler allowed in bracket notation by allowlist' );
+is( $@, '', 'no exception thrown by use of custom_handler with allowlist' );
-# _internal_method blocked by whitelist
+# _internal_method blocked by allowlist
$res = eval { $lh->maketext('[_internal_method]') };
is( $res, undef, 'no return value from blocked expansion' );
-like( $@, qr/Can't use .* as a method name/, '_internal_method blocked in bracket notation by whitelist' );
+like( $@, qr/Can't use .* as a method name/, '_internal_method blocked in bracket notation by allowlist' );
-# adding fail_with to whitelist
+# adding fail_with to allowlist
$lh->whitelist('fail_with');
# fail_with still blocked by blacklist
$res = eval { $lh->maketext('[fail_with,xyzzy]') };
is( $res, undef, 'no return value from blocked expansion' );
-like( $@, qr/Can't use .* as a method name/, 'fail_with blocked in bracket notation by blacklist even when whitelisted' );
+like( $@, qr/Can't use .* as a method name/, 'fail_with blocked in bracket notation by blacklist even when allowlisted' );
diff --git a/dist/Locale-Maketext/t/94_denylist.t b/dist/Locale-Maketext/t/94_denylist.t
new file mode 100644
index 0000000000..73574e67f8
--- /dev/null
+++ b/dist/Locale-Maketext/t/94_denylist.t
@@ -0,0 +1,93 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use warnings;
+use Test::More tests => 17;
+
+BEGIN {
+ use_ok("Locale::Maketext");
+}
+
+{
+
+ package MyTestLocale;
+ no warnings 'once';
+
+ @MyTestLocale::ISA = qw(Locale::Maketext);
+ %MyTestLocale::Lexicon = ();
+}
+
+{
+
+ package MyTestLocale::en;
+ no warnings 'once';
+
+ @MyTestLocale::en::ISA = qw(MyTestLocale);
+
+ %MyTestLocale::en::Lexicon = ( '_AUTO' => 1 );
+
+ sub custom_handler {
+ return "custom_handler_response";
+ }
+
+ sub _internal_method {
+ return "_internal_method_response";
+ }
+
+ sub new {
+ my ( $class, @args ) = @_;
+ my $lh = $class->SUPER::new(@args);
+ $lh->{use_external_lex_cache} = 1;
+ return $lh;
+ }
+}
+
+my $lh = MyTestLocale->get_handle('en');
+my $res;
+
+# get_handle blocked by default
+$res = eval { $lh->maketext('[get_handle,en]') };
+is( $res, undef, 'no return value from blocked expansion' );
+like( $@, qr/Can't use .* as a method name/, 'get_handle blocked in bracket notation by default denylist' );
+
+# _ambient_langprefs blocked by default
+$res = eval { $lh->maketext('[_ambient_langprefs]') };
+is( $res, undef, 'no return value from blocked expansion' );
+like( $@, qr/Can't use .* as a method name/, '_ambient_langprefs blocked in bracket notation by default denylist' );
+
+# _internal_method not blocked by default
+$res = eval { $lh->maketext('[_internal_method]') };
+is( $res, "_internal_method_response", '_internal_method allowed in bracket notation by default denylist' );
+is( $@, '', 'no exception thrown by use of _internal_method under default denylist' );
+
+# sprintf not blocked by default
+$res = eval { $lh->maketext('[sprintf,%s,hello]') };
+is( $res, "hello", 'sprintf allowed in bracket notation by default denylist' );
+is( $@, '', 'no exception thrown by use of sprintf under default denylist' );
+
+# denylisting sprintf and numerate
+$lh->denylist( 'sprintf', 'numerate' );
+
+# sprintf blocked by custom denylist
+$res = eval { $lh->maketext('[sprintf,%s,hello]') };
+is( $res, undef, 'no return value from blocked expansion' );
+like( $@, qr/Can't use .* as a method name/, 'sprintf blocked in bracket notation by custom denylist' );
+
+# denylisting numf and _internal_method
+$lh->denylist('numf');
+$lh->denylist('_internal_method');
+
+# sprintf blocked by custom denylist
+$res = eval { $lh->maketext('[sprintf,%s,hello]') };
+is( $res, undef, 'no return value from blocked expansion' );
+like( $@, qr/Can't use .* as a method name/, 'sprintf blocked in bracket notation by custom denylist after extension of denylist' );
+
+# _internal_method blocked by custom denylist
+$res = eval { $lh->maketext('[_internal_method]') };
+is( $res, undef, 'no return value from blocked expansion' );
+like( $@, qr/Can't use .* as a method name/, 'sprintf blocked in bracket notation by custom denylist after extension of denylist' );
+
+# custom_handler not in default or custom denylist
+$res = eval { $lh->maketext('[custom_handler]') };
+is( $res, "custom_handler_response", 'custom_handler allowed in bracket notation by default and custom denylist' );
+is( $@, '', 'no exception thrown by use of custom_handler under default and custom denylist' );
diff --git a/dist/Locale-Maketext/t/95_allowlist.t b/dist/Locale-Maketext/t/95_allowlist.t
new file mode 100644
index 0000000000..eb12e64286
--- /dev/null
+++ b/dist/Locale-Maketext/t/95_allowlist.t
@@ -0,0 +1,96 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use warnings;
+use Test::More tests => 17;
+
+BEGIN {
+ use_ok("Locale::Maketext");
+}
+
+{
+
+ package MyTestLocale;
+ no warnings 'once';
+
+ @MyTestLocale::ISA = qw(Locale::Maketext);
+ %MyTestLocale::Lexicon = ();
+}
+
+{
+
+ package MyTestLocale::en;
+ no warnings 'once';
+
+ @MyTestLocale::en::ISA = qw(MyTestLocale);
+
+ %MyTestLocale::en::Lexicon = ( '_AUTO' => 1 );
+
+ sub custom_handler {
+ return "custom_handler_response";
+ }
+
+ sub _internal_method {
+ return "_internal_method_response";
+ }
+
+ sub new {
+ my ( $class, @args ) = @_;
+ my $lh = $class->SUPER::new(@args);
+ $lh->{use_external_lex_cache} = 1;
+ return $lh;
+ }
+}
+
+my $lh = MyTestLocale->get_handle('en');
+my $res;
+
+# _internal_method not blocked by default
+$res = eval { $lh->maketext('[_internal_method]') };
+is( $res, "_internal_method_response", '_internal_method allowed when no allowlist defined' );
+is( $@, '', 'no exception thrown by use of _internal_method without allowlist setting' );
+
+# allowlisting sprintf
+$lh->allowlist('sprintf');
+
+# _internal_method blocked by allowlist
+$res = eval { $lh->maketext('[_internal_method]') };
+is( $res, undef, 'no return value from blocked expansion' );
+like( $@, qr/Can't use .* as a method name/, '_internal_method blocked in bracket notation by allowlist' );
+
+# sprintf allowed by allowlist
+$res = eval { $lh->maketext('[sprintf,%s,hello]') };
+is( $res, "hello", 'sprintf allowed in bracket notation by allowlist' );
+is( $@, '', 'no exception thrown by use of sprintf with allowlist' );
+
+# custom_handler blocked by allowlist
+$res = eval { $lh->maketext('[custom_handler]') };
+is( $res, undef, 'no return value from blocked expansion' );
+like( $@, qr/Can't use .* as a method name/, 'custom_handler blocked in bracket notation by allowlist' );
+
+# adding custom_handler to allowlist
+$lh->allowlist('custom_handler');
+
+# sprintf still allowed by allowlist
+$res = eval { $lh->maketext('[sprintf,%s,hello]') };
+is( $res, "hello", 'sprintf allowed in bracket notation by allowlist' );
+is( $@, '', 'no exception thrown by use of sprintf with allowlist' );
+
+# custom_handler allowed by allowlist
+$res = eval { $lh->maketext('[custom_handler]') };
+is( $res, "custom_handler_response", 'custom_handler allowed in bracket notation by allowlist' );
+is( $@, '', 'no exception thrown by use of custom_handler with allowlist' );
+
+# _internal_method blocked by allowlist
+$res = eval { $lh->maketext('[_internal_method]') };
+is( $res, undef, 'no return value from blocked expansion' );
+like( $@, qr/Can't use .* as a method name/, '_internal_method blocked in bracket notation by allowlist' );
+
+# adding fail_with to allowlist
+$lh->allowlist('fail_with');
+
+# fail_with still blocked by denylist
+$res = eval { $lh->maketext('[fail_with,xyzzy]') };
+is( $res, undef, 'no return value from blocked expansion' );
+like( $@, qr/Can't use .* as a method name/, 'fail_with blocked in bracket notation by denylist even when allowlisted' );
+