File Coverage

lib/ThreatDetector/Handlers/RateLimiter.pm
Criterion Covered Total %
statement 33 33 100.0
branch 3 4 75.0
condition 1 2 50.0
subroutine 7 7 100.0
pod 0 2 0.0
total 44 48 91.6


line stmt bran cond sub pod time code
1             package ThreatDetector::Handlers::RateLimiter;
2            
3 2     2   161243 use strict;
  2         4  
  2         237  
4 2     2   13 use warnings;
  2         5  
  2         218  
5 2     2   14 use Exporter 'import';
  2         4  
  2         82  
6 2     2   723 use JSON;
  2         24839  
  2         13  
7 2     2   330 use Time::HiRes qw(gettimeofday);
  2         5  
  2         17  
8            
9             our $VERBOSE = 0;
10             our @EXPORT_OK = qw(handle_rate_burst get_rate_burst_events);
11             our @RATE_BURST_EVENTS;
12             our $VERSION = '0.04';
13            
14             my %ip_activity;
15            
16             my $TIME_WINDOW = 10;
17             my $MAX_REQUESTS = 20;
18            
19             sub handle_rate_burst {
20 25     25 0 211775 my ($entry) = @_;
21 25         31 my ($sec, $micro) = gettimeofday();
22 25         24 my $now = $sec + ($micro / 1_000_000);
23 25         23 my $ip = $entry->{ip};
24            
25 25         15 push @{ $ip_activity{$ip} }, $now;
  25         28  
26            
27 25         22 @{ $ip_activity{$ip} } = grep { $_ >= $now - $TIME_WINDOW } @{ $ip_activity{$ip} };
  25         42  
  241         206  
  25         24  
28            
29 25 100       22 if (@{ $ip_activity{$ip} } > $MAX_REQUESTS) {
  25         37  
30             my $alert = {
31             timestamp => "$sec.$micro",
32             type => 'rate_burst',
33             ip => $ip,
34 1         8 count => scalar @{ $ip_activity{$ip} },
35             method => $entry->{method},
36             uri => $entry->{uri},
37             status => $entry->{status},
38             user_agent => $entry->{user_agent},
39 1   50     3 referer => $entry->{referer} || '',
40             };
41 1         3 push @RATE_BURST_EVENTS, $alert;
42 1 50       2 print encode_json($alert) . "\n" if $VERBOSE;
43 1         3 $ip_activity{$ip} = [];
44             }
45             }
46            
47             sub get_rate_burst_events {
48 1     1 0 4 return @RATE_BURST_EVENTS;
49             }
50            
51             1;
52            
53             =head1 NAME
54            
55             ThreatDetector::Handlers::RateLimiter - Detects rate-based abuse by tracking burst activity
56            
57             =head1 SYNOPSIS
58            
59             use ThreatDetector::Handlers::RateLimiter qw(handle_rate_burst);
60            
61             handle_rate_burst($entry);
62            
63             =head1 DESCRIPTION
64            
65             Monitors how frequently a given IP sends requests. If the number of requests in a short time window exceeds a configured threshold, it emits an alert. This is useful for detecting denial-of-service attempts, scraping bots, or brute-force login attempts spread across different endpoints.
66            
67             =head1 AUTHOR
68            
69             Jason Hall
70            
71             =cut