############################################################################### # GuardianAdmin.pm # # $Date: 01 Sep 2026 $ # ############################################################################### # YaBB: Yet another Bulletin Board # # Open-Source Community Software for Webmasters # # Version: YaBBForum 3.2 # # Packaged: 01 Sep 2026 # # Distributed by: https://yabbforum.nz # # =========================================================================== # # Copyright (c) 2000-2026 YaBB (yabbforum.nz) - All Rights Reserved. # # Software by: The YaBB Development Team # # with assistance from the YaBB community. # ############################################################################### use CGI::Carp qw(fatalsToBrowser); our $VERSION = '3.2'; $guardianadminpmver = 'YaBBForum 3.2'; if ( $action eq 'detailedversion' ) { return 1; } LoadLanguage('Guardian'); $admin_images = "$yyhtml_root/Templates/Admin/default"; $guardimg = 'guardian.png'; sub setup_guardian { is_admin_or_gmod(); # figure out what to print if ($use_guardian) { $guardian_checked = ' checked="checked" ' } if ($use_htaccess) { $htaccess_checked = ' checked="checked" ' } if ($disallow_proxy_on) { $proxy_on_checked = ' checked="checked" ' } if ($disallow_proxy_notify) { $proxy_notify_checked = ' checked="checked" '; } if ($disallow_proxy_htaccess) { $proxy_htaccess_checked = ' checked="checked" '; } if ($referer_on) { $referer_on_checked = ' checked="checked" ' } if ($referer_notify) { $referer_notify_checked = ' checked="checked" ' } if ($referer_htaccess) { $referer_htaccess_checked = ' checked="checked" ' } if ($harvester_on) { $harvester_on_checked = ' checked="checked" ' } if ($harvester_notify) { $harvester_notify_checked = ' checked="checked" ' } if ($harvester_htaccess) { $harvester_htaccess_checked = ' checked="checked" '; } if ($request_on) { $request_on_checked = ' checked="checked" ' } if ($request_notify) { $request_notify_checked = ' checked="checked" ' } if ($request_htaccess) { $request_htaccess_checked = ' checked="checked" ' } if ($string_on) { $string_on_checked = ' checked="checked" ' } if ($string_notify) { $string_notify_checked = ' checked="checked" ' } if ($string_htaccess) { $string_htaccess_checked = ' checked="checked" ' } if ($union_on) { $union_on_checked = ' checked="checked" ' } if ($union_notify) { $union_notify_checked = ' checked="checked" ' } if ($union_htaccess) { $union_htaccess_checked = ' checked="checked" ' } if ($clike_on) { $clike_on_checked = ' checked="checked" ' } if ($clike_notify) { $clike_notify_checked = ' checked="checked" ' } if ($clike_htaccess) { $clike_htaccess_checked = ' checked="checked" ' } if ($script_on) { $script_on_checked = ' checked="checked" ' } if ($script_notify) { $script_notify_checked = ' checked="checked" ' } if ($script_htaccess) { $script_htaccess_checked = ' checked="checked" ' } ## make splits turn into linefeeds for the forms chomp $banned_harvesters; chomp $banned_referers; chomp $banned_requests; chomp $banned_strings; chomp $whitelist; $banned_harvesters =~ s/\|/\n/gxsm; $banned_referers =~ s/\|/\n/gxsm; $banned_requests =~ s/\|/\n/gxsm; $banned_strings =~ s/\|/\n/gxsm; $whitelist =~ s/\|/\n/gxsm; @access_denied = update_htaccess('load'); foreach (@access_denied) { chomp $_; $acc_denied .= "$_\n"; } $yymain .= qq~
$guardian_txt{'title'}
$guardian_txt{'description'}
$guardian_txt{'general'}
$guardian_txt{'proxy'}
$guardian_txt{'referer'}
$guardian_txt{'harvester'}
$guardian_txt{'request'}
$guardian_txt{'string'}
$guardian_txt{'script'}
$guardian_txt{'union'}
$guardian_txt{'clike'}
$admin_img{'prefimg'} $admin_txt{'10'}
~; $yytitle = $guardian_txt{'setup'}; $action_area = 'setup_guardian'; AdminTemplate(); return; } sub setup_guardian2 { is_admin_or_gmod(); my @onoff = qw{ use_guardian use_htaccess disallow_proxy_on disallow_proxy_htaccess referer_on referer_htaccess harvester_on harvester_htaccess request_on request_htaccess string_on string_htaccess union_on union_htaccess clike_on clike_htaccess script_on script_htaccess disallow_proxy_notify referer_notify harvester_notify request_notify string_notify union_notify clike_notify script_notify}; # Set as 0 or 1 if box was checked or not my $fi; map { $fi = lc $_; ${$_} = $FORM{$fi} == 1 ? 1 : 0; } @onoff; $banned_harvesters = $FORM{'banned_harvesters'}; $banned_referers = $FORM{'banned_referers'}; $banned_requests = $FORM{'banned_requests'}; $banned_strings = $FORM{'banned_strings'}; $access_denied = $FORM{'access_denied'}; $whitelist = $FORM{'whitelist'}; chomp $banned_harvesters; chomp $banned_referers; chomp $banned_requests; chomp $banned_strings; chomp $whitelist; $banned_harvesters =~ s/\r//gxsm; $banned_referers =~ s/\r//gxsm; $banned_requests =~ s/\r//gxsm; $banned_strings =~ s/\r//gxsm; $access_denied =~ s/\r//gxsm; $whitelist =~ s/\r//gxsm; $banned_harvesters =~ s/\n/|/gxsm; $banned_referers =~ s/\n/|/gxsm; $banned_requests =~ s/\n/|/gxsm; $banned_strings =~ s/\n/|/gxsm; $access_denied =~ s/\n/,/gxsm; $whitelist =~ s/\n/|/gxsm; # We shouldn't let them block POST and GET since it'll mess things up. $banned_requests =~ s/post//igsm; $banned_requests =~ s/get//igsm; $banned_requests =~ s/\|+/\|/igsm; # Clean up extra pipes require Admin::NewSettings; SaveSettingsTo('Settings.pm'); @access_denied = split /\,/xsm, $access_denied; update_htaccess( 'save', @access_denied ); $yySetLocation = qq~$adminurl?action=setup_guardian~; redirectexit(); return; } sub update_htaccess { my ( $action, @values ) = @_; my ( $htheader, $htfooter, @denies, @htout ); if ( !$action ) { return 0; } fopen( HTA, '.htaccess' ); @htlines = ; fclose(HTA); # header to determine only who has access to the main script, not the admin script $htheader = q~~; $htfooter = q~~; $start = 0; foreach (@htlines) { chomp $_; if ( $_ eq $htheader ) { $start = 1; } if ( $start == 0 && $_ !~ m{#}sm && $_ ne q{} ) { push @htout, "$_\n"; } if ( $_ eq $htfooter ) { $start = 0; } if ( $start == 1 && $_ =~ s/Deny from //gsm ) { push @denies, $_; } } if ( $action eq 'load' ) { return @denies; } elsif ( $action eq 'save' ) { fopen( HTA, '>.htaccess' ); print {HTA} '# Last modified by The Guardian: ' . timeformat( $date, 1 ) . " #\n\n" or croak "$croak{'print'} HTA"; print {HTA} @htout or croak "$croak{'print'} HTA"; if (@values) { print {HTA} "\n$htheader\n" or croak "$croak{'print'} HTA"; # --- START ADMIN PATCH: Remove duplicates AND ensure strictly valid IP/IPv6 structures --- my %seen; @values = grep { $_ ne q{} && !$seen{$_}++ && ( # Match valid IPv4 standard (e.g., 1.2.3.4, 192.168.*, or 10.0.0.0/24) $_ =~ m/^\d{1,3}\.[\d\*]{1,3}\.[\d\*]{1,3}\.[\d\*]{1,3}(\/\d{1,2})?$/ || # Match valid IPv6 characters and structure ($_ =~ m/^[0-9a-fA-F:]+(\/\d{1,3})?$/ && $_ =~ tr/:// > 1) ) } @values; # --- END ADMIN PATCH --- foreach (@values) { chomp $_; if ( $_ ne q{} ) { print {HTA} "Deny from $_\n" or croak "$croak{'print'} HTA"; } } print {HTA} "$htfooter\n" or croak "$croak{'print'} HTA"; } fclose(HTA); } elsif ( $action eq 'add' ) { push @denies, @values; update_htaccess( 'save', @denies ); } return; } sub guardian_block { is_admin_or_gmod(); if ( $use_guardian && $use_htaccess ) { my $blockIP = $INFO{'ip'}; update_htaccess( 'add', $blockIP ); $yySetLocation = qq~$adminurl?action=$INFO{'return'}~; redirectexit(); } return; } 1;