r24675 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r24674‎ | r24675 | r24676 >
Date:16:24, 8 August 2007
Author:brion
Status:old
Tags:
Comment:
Increase robustness as done for SpamBlacklist:
* Suppress PHP warnings for PCRE work
* If there's an invalid fragment, break the combined regex into separate ones for minimal disruption
* Validate the blacklist on edit and display the bad lines to the editor, requiring correction
Modified paths:
  • /trunk/extensions/UsernameBlacklist/UsernameBlacklist.i18n.php (modified) (history)
  • /trunk/extensions/UsernameBlacklist/UsernameBlacklist.php (modified) (history)

Diff [purge]

Index: trunk/extensions/UsernameBlacklist/UsernameBlacklist.i18n.php
@@ -22,6 +22,7 @@
2323 # * Foo
2424 # * [Bb]ar
2525 </pre>',
 26+'usernameblacklist-invalid-lines' => 'The following lines in the username blacklist are invalid; please correct them before saving:',
2627
2728 ),
2829
Index: trunk/extensions/UsernameBlacklist/UsernameBlacklist.php
@@ -29,7 +29,9 @@
3030 global $wgHooks, $wgVersion, $wgMessageCache;
3131 require_once( dirname( __FILE__ ) . '/UsernameBlacklist.i18n.php' );
3232 $wgHooks['AbortNewAccount'][] = 'efUsernameBlacklist';
33 - $wgHooks['ArticleSave'][] = 'efUsernameBlacklistInvalidate';
 33+ $wgHooks['ArticleSaveComplete'][] = 'efUsernameBlacklistInvalidate';
 34+ $wgHooks['EditFilter'][] = 'efUsernameBlacklistValidate';
 35+
3436 if( version_compare( $wgVersion, '1.9alpha', '>=' ) ) {
3537 foreach( efUsernameBlacklistMessages() as $lang => $messages )
3638 $wgMessageCache->addMessages( $messages, $lang );
@@ -72,6 +74,36 @@
7375 return true;
7476 }
7577
 78+ /**
 79+ * If editing the username blacklist page, check for validity and whine at the user.
 80+ */
 81+ function efUsernameBlacklistValidate( $editPage, $text, $section, &$hookError ) {
 82+ if( $editPage->mTitle->getNamespace() == NS_MEDIAWIKI &&
 83+ $editPage->mTitle->getDbKey() == 'Usernameblacklist' ) {
 84+
 85+ $blacklist = UsernameBlacklist::fetch();
 86+ $badLines = $blacklist->validate( $text );
 87+
 88+ if( $badLines ) {
 89+ $badList = "*<tt>" .
 90+ implode( "</tt>\n*<tt>",
 91+ array_map( 'wfEscapeWikiText', $badLines ) ) .
 92+ "</tt>\n";
 93+ $hookError =
 94+ "<div class='errorbox'>" .
 95+ wfMsgExt( 'usernameblacklist-invalid-lines', array( 'parsemag' ), count( $badLines ) ) .
 96+ "\n" .
 97+ $badList .
 98+ "</div>\n" .
 99+ "<br clear='all' />\n";
 100+
 101+ // This is kind of odd, but... :D
 102+ return true;
 103+ }
 104+ }
 105+ return true;
 106+ }
 107+
76108 class UsernameBlacklist {
77109
78110 var $regex;
@@ -98,12 +130,12 @@
99131 /**
100132 * Attempt to fetch the blacklist from cache; build it if needs be
101133 *
102 - * @return string
 134+ * @return array
103135 */
104136 function fetchBlacklist() {
105137 global $wgMemc, $wgDBname;
106138 $list = $wgMemc->get( $this->key );
107 - if( $list ) {
 139+ if( is_array( $list ) ) {
108140 return $list;
109141 } else {
110142 $list = $this->buildBlacklist();
@@ -115,25 +147,87 @@
116148 /**
117149 * Build the blacklist from scratch, using the message page
118150 *
119 - * @return string
 151+ * @return array of regexes, potentially empty
120152 */
121153 function buildBlacklist() {
122154 $blacklist = wfMsgForContent( 'usernameblacklist' );
123 - $groups = array();
124155 if( $blacklist != '&lt;usernameblacklist&gt;' ) {
125 - $lines = explode( "\n", $blacklist );
126 - foreach( $lines as $line ) {
127 - $line = trim( $line );
128 - if( $this->isUsable( $line ) )
129 - $groups[] = $this->transform( $line );
 156+ return $this->safeBlacklist( $blacklist );
 157+ } else {
 158+ return array();
 159+ }
 160+ }
 161+
 162+ /**
 163+ * Build one or more blacklist regular expressions from the input.
 164+ * If a fragment causes an error, we'll return multiple items
 165+ * so they can be run separately.
 166+ *
 167+ * @param string $input
 168+ * @return array of regexes, potentially empty
 169+ */
 170+ function safeBlacklist( $input ) {
 171+ $groups = $this->fragmentsFromInput( $input );
 172+ if( count( $groups ) ) {
 173+ $combinedRegex = '/(' . implode( '|', $groups ) . ')/u';
 174+
 175+ wfSuppressWarnings();
 176+ $ok = ( preg_match( $combinedRegex, '' ) !== false );
 177+ wfRestoreWarnings();
 178+
 179+ if( $ok ) {
 180+ return array( $combinedRegex );
 181+ } else {
 182+ $regexes = array();
 183+ foreach( $groups as $fragment ) {
 184+ $regexes[] = '/' . $fragment . '/u';
 185+ }
 186+ return $regexes;
130187 }
131 - return count( $groups ) ? '/(' . implode( '|', $groups ) . ')/u' : false;
132188 } else {
133 - return false;
 189+ return array();
134190 }
135191 }
136192
137193 /**
 194+ * Break input down by line, remove comments, and strip to regex fragments.
 195+ * @input string
 196+ * @return array
 197+ */
 198+ function fragmentsFromInput( $input ) {
 199+ $lines = explode( "\n", $input );
 200+ $groups = array();
 201+ foreach( $lines as $line ) {
 202+ $line = trim( $line );
 203+ if( $this->isUsable( $line ) )
 204+ $groups[] = $this->transform( $line );
 205+ }
 206+ return $groups;
 207+ }
 208+
 209+ /**
 210+ * Go through a set of input and return a list of lines which
 211+ * produce invalid regexes.
 212+ * Empty set means good. :)
 213+ *
 214+ * @param string $input
 215+ * @return array
 216+ */
 217+ function validate( $input ) {
 218+ $bad = array();
 219+ $fragments = $this->fragmentsFromInput( $input );
 220+ foreach( $fragments as $fragment ) {
 221+ wfSuppressWarnings();
 222+ $ok = ( preg_match( "/$fragment/u", '' ) !== false );
 223+ wfRestoreWarnings();
 224+ if( !$ok ) {
 225+ $bad[] = $fragment;
 226+ }
 227+ }
 228+ return $bad;
 229+ }
 230+
 231+ /**
138232 * Invalidate the blacklist cache
139233 */
140234 function invalidateCache() {
@@ -147,7 +241,18 @@
148242 * @return bool
149243 */
150244 function match( $username ) {
151 - return $this->regex ? preg_match( $this->regex, $username ) : false;
 245+ foreach( $this->regexes as $regex ) {
 246+ wfSuppressWarnings();
 247+ $match = preg_match( $regex, $username );
 248+ wfRestoreWarnings();
 249+
 250+ if( $match ) {
 251+ return true;
 252+ } elseif( $match === false ) {
 253+ wfDebugLog( 'UsernameBlacklist', "Invalid username regex $regex" );
 254+ }
 255+ }
 256+ return false;
152257 }
153258
154259 /**
@@ -157,7 +262,7 @@
158263 function UsernameBlacklist() {
159264 global $wgDBname;
160265 $this->key = "{$wgDBname}:username-blacklist";
161 - $this->regex = $this->fetchBlacklist();
 266+ $this->regexes = $this->fetchBlacklist();
162267 }
163268
164269 /**

Status & tagging log