Skip to content

Commit e4f9903

Browse files
authored
Version 1.5.0 - Add custom header and text settings for access-denial display
Version 1.5.0 (Mar 4, 2026) * Add custom header and text settings for access-denial display
1 parent a6bfd57 commit e4f9903

File tree

4 files changed

+41
-19
lines changed

4 files changed

+41
-19
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,7 @@ intensive.
4444
1.4.0 (Feb 19, 2026)
4545

4646
* Add configurable action protection with $wgCrawlerProtectedActions
47+
48+
1.5.0 (Mar 4, 2026)
49+
50+
* Add custom header and text settings for access-denial display

extension.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "CrawlerProtection",
33
"author": "MyWikis LLC",
4-
"version": "1.4.0",
4+
"version": "1.5.0",
55
"description": "Suite of protective measures to protect wikis from crawlers.",
66
"type": "hook",
77
"requires": {
@@ -35,6 +35,12 @@
3535
},
3636
"CrawlerProtectionUse418": {
3737
"value": false
38+
},
39+
"CrawlerProtectionDenialHeader": {
40+
"value": null
41+
},
42+
"CrawlerProtectionDenialText": {
43+
"value": null
3844
}
3945
},
4046
"license-name": "MIT",

includes/Hooks.php

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,13 @@ public function onMediaWikiPerformAction(
7272

7373
$config = MediaWikiServices::getInstance()->getMainConfig();
7474
$protectedActions = $config->get( 'CrawlerProtectedActions' );
75-
$denyFast = $config->get( 'CrawlerProtectionUse418' );
75+
$customDenialHeader = $config->get( 'CrawlerProtectionDenialHeader' );
76+
$customDenialText = $config->get( 'CrawlerProtectionDenialText' );
77+
$use418 = $config->get( 'CrawlerProtectionUse418' );
78+
if ( empty( $customDenialHeader ) && empty( $customDenialText ) && $use418 ) {
79+
$customDenialHeader = 'HTTP/1.0 I\'m a teapot';
80+
$customDenialText = 'I\'m a teapot';
81+
}
7682

7783
if (
7884
!$user->isRegistered()
@@ -83,8 +89,8 @@ public function onMediaWikiPerformAction(
8389
|| $oldId > 0
8490
)
8591
) {
86-
if ( $denyFast ) {
87-
$this->denyAccessWith418();
92+
if ( !empty( $customDenialHeader ) && !empty( $customDenialText ) ) {
93+
$this->denyAccessCustom( $customDenialHeader, $customDenialText );
8894
}
8995
$this->denyAccess( $output );
9096
return false;
@@ -110,7 +116,13 @@ public function onSpecialPageBeforeExecute( $special, $subPage ) {
110116

111117
$config = MediaWikiServices::getInstance()->getMainConfig();
112118
$protectedSpecialPages = $config->get( 'CrawlerProtectedSpecialPages' );
113-
$denyFast = $config->get( 'CrawlerProtectionUse418' );
119+
$customDenialHeader = $config->get( 'CrawlerProtectionDenialHeader' );
120+
$customDenialText = $config->get( 'CrawlerProtectionDenialText' );
121+
$use418 = $config->get( 'CrawlerProtectionUse418' );
122+
if ( empty( $customDenialHeader ) && empty( $customDenialText ) && $use418 ) {
123+
$customDenialHeader = 'HTTP/1.0 I\'m a teapot';
124+
$customDenialText = 'I\'m a teapot';
125+
}
114126

115127
// Normalize protected special pages: lowercase and strip 'Special:' prefix
116128
$normalizedProtectedPages = array_map(
@@ -122,8 +134,8 @@ public function onSpecialPageBeforeExecute( $special, $subPage ) {
122134

123135
$name = strtolower( $special->getName() );
124136
if ( in_array( $name, $normalizedProtectedPages, true ) ) {
125-
if ( $denyFast ) {
126-
$this->denyAccessWith418();
137+
if ( !empty( $customDenialHeader ) && !empty( $customDenialText ) ) {
138+
$this->denyAccessCustom( $customDenialHeader, $customDenialText );
127139
}
128140
$out = $special->getContext()->getOutput();
129141
$this->denyAccess( $out );
@@ -134,8 +146,8 @@ public function onSpecialPageBeforeExecute( $special, $subPage ) {
134146
$request = $special->getContext()->getRequest();
135147
$oldId = (int)$request->getVal( 'oldid' );
136148
if ( $oldId > 0 ) {
137-
if ( $denyFast ) {
138-
$this->denyAccessWith418();
149+
if ( !empty( $customDenialHeader ) && !empty( $customDenialText ) ) {
150+
$this->denyAccessCustom( $customDenialHeader, $customDenialText );
139151
}
140152
$out = $special->getContext()->getOutput();
141153
$this->denyAccess( $out );
@@ -146,14 +158,14 @@ public function onSpecialPageBeforeExecute( $special, $subPage ) {
146158
}
147159

148160
/**
149-
* Helper: output 418 Teapot and halt the processing immediately
161+
* Display custom text for the "access denied" message, to avoid unnecessarily taxing resources.
150162
*
151163
* @return void
152164
* @suppress PhanPluginNeverReturnMethod
153165
*/
154-
protected function denyAccessWith418() {
155-
header( 'HTTP/1.0 I\'m a teapot' );
156-
die( 'I\'m a teapot' );
166+
protected function denyAccessCustom( $customDenialHeader, $customDenialText ) {
167+
header( $customDenialHeader );
168+
die( htmlspecialchars( $customDenialText ) );
157169
}
158170

159171
/**

tests/phpunit/unit/HooksTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ public function testSpecialPageBlocksAnonymous( $specialPageName ) {
429429
$special->method( 'getContext' )->willReturn( $context );
430430

431431
$runner = $this->getMockBuilder( Hooks::class )
432-
->onlyMethods( [ 'denyAccess', 'denyAccessWith418' ] )
432+
->onlyMethods( [ 'denyAccess', 'denyAccessCustom' ] )
433433
->getMock();
434434
$runner->expects( $this->once() )->method( 'denyAccess' )->with( $output );
435435

@@ -543,9 +543,9 @@ public function getOutput() {
543543

544544
/**
545545
* @covers ::onSpecialPageBeforeExecute
546-
* @covers ::denyAccessWith418
546+
* @covers ::denyAccessCustom
547547
*/
548-
public function testSpecialPageCallsDenyAccessWith418WhenConfigured() {
548+
public function testSpecialPageCallsDenyAccessCustomWhenConfigured() {
549549
// This test only works with our test stubs, not in MediaWiki's PHPUnit environment
550550
if ( !property_exists( '\MediaWiki\MediaWikiServices', 'testUse418' ) ) {
551551
$this->markTestSkipped(
@@ -568,10 +568,10 @@ public function testSpecialPageCallsDenyAccessWith418WhenConfigured() {
568568
$special->method( 'getContext' )->willReturn( $context );
569569

570570
$runner = $this->getMockBuilder( Hooks::class )
571-
->onlyMethods( [ 'denyAccessWith418' ] )
571+
->onlyMethods( [ 'denyAccessCustom' ] )
572572
->getMock();
573-
// When denyFast is true, only denyAccessWith418 is called (it dies before denyAccess)
574-
$runner->expects( $this->once() )->method( 'denyAccessWith418' );
573+
// When $testUse418 is true, only denyAccessCustom is called (it dies before denyAccess)
574+
$runner->expects( $this->once() )->method( 'denyAccessCustom' );
575575

576576
$result = $runner->onSpecialPageBeforeExecute( $special, null );
577577
$this->assertFalse( $result );

0 commit comments

Comments
 (0)