NAME
Hook::Filter - A runtime filtering layer on top of subroutine calls
DESCRIPTION
Hook::Filter is a runtime firewall for subroutine calls.
Hook::Filter lets you wrap one or more subroutines with a filter that either forwards calls to the subroutine or blocks them, depending on a number of rules that you define yourself. Those rules are simply Perl one-liners that must evaluate to false (block the call) or true (allow it).
The filtering rules are stored in a file, called the rules file.
Each time a call is made to one of the filtered subroutines, all the filtering rules are eval-ed, and if one of them returns true, the call is forwarded, otherwise it is blocked. If no rules file exists, or if a rule dies or contains syntax errors, all calls are forwarded by default.
Filtering rules are very flexible. You can block or allow calls to a subroutine based on things such as the caller's identity, the values of the arguments passed to the subroutine, the structure of the call stack, or basically any other test that can be implemented in Perl.
SYNOPSIS
To hook a number of subroutines:
# filter the subs mydebug() and myinfo() located in the current
# module, as well as sub mywarn() located in Some::Other::Module
use Hook::Filter hook => ["mydebug","myinfo","Some::Other::Module::mywarn"];
Then create a rules file. By default it is a file called ./hook_filter.rules, and could look like:
# allow calls to 'mydebug' only inside package 'My::Filthy:Attempt'
subname eq 'mydebug' && from =~ /^My::Filthy::Attempt/
# allow calls only if the caller's fully qualified name matches a pattern
from =~ /^My::Filthy::Attempt::func$/
# allow calls only if the subroutine's 2nd argument matches /bob/
args(1) =~ /bob/
# all other calls to 'myinfo', 'mydebug' or 'mywarn' will be skipped
To see which test functions can be used in rules, see Hook::Filter::Plugins::Library.
RULES
A rule is one line of valid perl code that returns either true or false when eval-ed. This line of code is usually made of boolean operators combining functions that are exported by the modules located under Hook::Filter::Plugins::. See those modules for more details.
Rules are loaded from a file. By default this file is called hook_filter.rules
and must be located either in the running program current directory or in the user's home directory.
You can change the default name and location of the rules file with the import parameter rules
.
If no rules file is found, all subroutine calls will be allowed by default.
Rules are parsed from the rules file only once, when the module inits.
The rules file has a straightforward syntax:
any line starting with
#
is a commentany empty line is skipped
any other line is considered to be a rule, ie a valid line of perl code
Each time one of the filtered subroutines is called, all loaded rules are eval-ed until one returns true or all returned false. If one returns true, the call is forwarded to filtered subroutine, otherwise it is skipped and a return value spoofed: either undef or an empty list, depending on the context.
If a rule dies/croaks/confess upon being eval-ed (f.ex. when you left a syntax error in your rules file), it will be assumed to have returned true. This is a form of fail-safe policy. A warning message with a complete diagnostic will be emitted with warn
.
EXTENDING THE PLUGIN LIBRARY
The default plugin Hook::Filter::Plugins::Library offers a number of functions that can be used inside the filter rules, but you may want extend those functions with your own ones.
You can easily do that by writing a new plugin module having the same structure as Hook::Filter::Plugins::Library and placing it under Hook/Filter/Plugins/. See Hook::Filter::Hooker and Hook::Filter::Plugins::Library for details on how to do that.
CAVEATS
Return values: when a call to a subroutine is allowed, the input and output arguments of the subroutine are forwarded without modification. But when the call is blocked, the subroutine response is simulated and will be
undef
in SCALAR context and an empty list in ARRAY context. Therefore, DO NOT filter subroutines whose return values are significant for the rest of your code.Execution time: Hook::Filter evaluates all filter rules for each call to a filtered subroutine. It would therefore be very unappropriate to filter a heavily used subroutine in speed requiring applications.
USE CASE
Why would one need a runtime function call firewall?? Here are a couple of relevant use cases:
A large application logs a lot of information. You want to implement a logging policy to limit the amount of logged information, but you don't want to modify the logging code. You do that by filtering the functions defined in the logging API with Hook::Filter, and by defining a rules file that implements your logging policy.
A large application crashes regularly so you decide to turn on debugging messages system wide with full verbosity. You get gazillions of log messages. Instead of greping your way through them or starting your debugger, you use Hook::Filter to filter the function that logs debug messages and define tailored rules that allow only relevant debug messages to be logged.
The concept of a blocking/allowing subroutine call dynamically is somewhat mind bobbling. Don't let yourself get too excited though. Doing that kind of dynamic stuff makes your code harder to understand for non-dynamic developers, hence reducing code stability.
INTERFACE - API
INTERFACE - IMPORT PARAMETERS
Hook::Filter accepts the following import parameters:
rules => $rules_file
-
Specify the complete path to the rules file. This import parameter can be used only once in a program (usually in package
main
) independently of how many timesHook::Filter
is used.See the RULES section for details.
Example:
# look for rules in the local file 'my_rules' use Hook::Filter rules => 'my_rules';
hook => $subname1
orhook => [$subname1,$subname2...]
-
Specify which subroutines should be filtered in the current module.
$subname
can either be a fully qualified name or just a subroutine name from a subroutine located in the current package.If you use Hook::Filter without specifying
hook
, the same subroutines as specified in packagemain
are assumed.Example:
# filter function debug in the current package use Hook::Filter hook => 'debug'; # filter function debug in an other package use Hook::Filter hook=> 'Other::Package::debug'; # do both at once use Hook::Filter hook=> [ 'Other::Package::debug', 'debug' ];
DIAGNOSTICS
- Passing wrong arguments to Hook::Filter's import parameters will cause it to croak.
- The import parameter
hook
must be used at least in packagemain
otherwise Hook::Filter croaks with an error message. - An IO error when opening the rules file causes Hook::Filter to die.
- An error in a filter rule will be reported with a perl warning.
SECURITY
Hook::Filter gives anybody who has the rights to create or manipulate a rules file the possibility to inject code into your running application at runtime. This can be highly dangerous! Protect your filesystem.
THREADS
Hook::Filter is not thread safe.
SEE ALSO
See Hook::WrapSub, Log::Localized, Log::Log4perl, Log::Dispatch.
BUGS AND LIMITATIONS
Please report any bugs or feature requests to bug-hook-filter@rt.cpan.org
, or through the web interface at http://rt.cpan.org.
REPOSITORY
The source of Hook::Filter is hosted at sourceforge. You can access it at https://sourceforge.net/projects/hook-filter/.
AUTHOR
Written by Erwan Lemonnier <erwan@cpan.org>
based on inspiration received during the 2005 perl Nordic Workshops. Kind thanks to Claes Jacobsson & Jerker Montelius for their suggestions and support!
LICENSE
This code was developed partly during free-time and partly at the Swedish Premium Pension Authority as part of the Authority's software development activities. This code is distributed under the same terms as Perl itself. We encourage you to help us improving this code by sending feedback and bug reports to the author(s).
This code comes with no warranty. The Swedish Premium Pension Authority and the author(s) decline any responsibility regarding the possible use of this code or any consequence of its use.