pomo/plural-forms.php000064400000020350147207017560010663 0ustar00 6, '<' => 5, '<=' => 5, '>' => 5, '>=' => 5, '==' => 4, '!=' => 4, '&&' => 3, '||' => 2, '?:' => 1, '?' => 1, '(' => 0, ')' => 0, ); /** * Tokens generated from the string. * * @since 4.9.0 * @var array $tokens List of tokens. */ protected $tokens = array(); /** * Cache for repeated calls to the function. * * @since 4.9.0 * @var array $cache Map of $n => $result */ protected $cache = array(); /** * Constructor. * * @since 4.9.0 * * @param string $str Plural function (just the bit after `plural=` from Plural-Forms) */ public function __construct( $str ) { $this->parse( $str ); } /** * Parse a Plural-Forms string into tokens. * * Uses the shunting-yard algorithm to convert the string to Reverse Polish * Notation tokens. * * @since 4.9.0 * * @param string $str String to parse. */ protected function parse( $str ) { $pos = 0; $len = strlen( $str ); // Convert infix operators to postfix using the shunting-yard algorithm. $output = array(); $stack = array(); while ( $pos < $len ) { $next = substr( $str, $pos, 1 ); switch ( $next ) { // Ignore whitespace. case ' ': case "\t": $pos++; break; // Variable (n). case 'n': $output[] = array( 'var' ); $pos++; break; // Parentheses. case '(': $stack[] = $next; $pos++; break; case ')': $found = false; while ( ! empty( $stack ) ) { $o2 = $stack[ count( $stack ) - 1 ]; if ( '(' !== $o2 ) { $output[] = array( 'op', array_pop( $stack ) ); continue; } // Discard open paren. array_pop( $stack ); $found = true; break; } if ( ! $found ) { throw new Exception( 'Mismatched parentheses' ); } $pos++; break; // Operators. case '|': case '&': case '>': case '<': case '!': case '=': case '%': case '?': $end_operator = strspn( $str, self::OP_CHARS, $pos ); $operator = substr( $str, $pos, $end_operator ); if ( ! array_key_exists( $operator, self::$op_precedence ) ) { throw new Exception( sprintf( 'Unknown operator "%s"', $operator ) ); } while ( ! empty( $stack ) ) { $o2 = $stack[ count( $stack ) - 1 ]; // Ternary is right-associative in C. if ( '?:' === $operator || '?' === $operator ) { if ( self::$op_precedence[ $operator ] >= self::$op_precedence[ $o2 ] ) { break; } } elseif ( self::$op_precedence[ $operator ] > self::$op_precedence[ $o2 ] ) { break; } $output[] = array( 'op', array_pop( $stack ) ); } $stack[] = $operator; $pos += $end_operator; break; // Ternary "else". case ':': $found = false; $s_pos = count( $stack ) - 1; while ( $s_pos >= 0 ) { $o2 = $stack[ $s_pos ]; if ( '?' !== $o2 ) { $output[] = array( 'op', array_pop( $stack ) ); $s_pos--; continue; } // Replace. $stack[ $s_pos ] = '?:'; $found = true; break; } if ( ! $found ) { throw new Exception( 'Missing starting "?" ternary operator' ); } $pos++; break; // Default - number or invalid. default: if ( $next >= '0' && $next <= '9' ) { $span = strspn( $str, self::NUM_CHARS, $pos ); $output[] = array( 'value', intval( substr( $str, $pos, $span ) ) ); $pos += $span; break; } throw new Exception( sprintf( 'Unknown symbol "%s"', $next ) ); } } while ( ! empty( $stack ) ) { $o2 = array_pop( $stack ); if ( '(' === $o2 || ')' === $o2 ) { throw new Exception( 'Mismatched parentheses' ); } $output[] = array( 'op', $o2 ); } $this->tokens = $output; } /** * Get the plural form for a number. * * Caches the value for repeated calls. * * @since 4.9.0 * * @param int $num Number to get plural form for. * @return int Plural form value. */ public function get( $num ) { if ( isset( $this->cache[ $num ] ) ) { return $this->cache[ $num ]; } $this->cache[ $num ] = $this->execute( $num ); return $this->cache[ $num ]; } /** * Execute the plural form function. * * @since 4.9.0 * * @param int $n Variable "n" to substitute. * @return int Plural form value. */ public function execute( $n ) { $stack = array(); $i = 0; $total = count( $this->tokens ); while ( $i < $total ) { $next = $this->tokens[ $i ]; $i++; if ( 'var' === $next[0] ) { $stack[] = $n; continue; } elseif ( 'value' === $next[0] ) { $stack[] = $next[1]; continue; } // Only operators left. switch ( $next[1] ) { case '%': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 % $v2; break; case '||': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 || $v2; break; case '&&': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 && $v2; break; case '<': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 < $v2; break; case '<=': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 <= $v2; break; case '>': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 > $v2; break; case '>=': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 >= $v2; break; case '!=': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 != $v2; break; case '==': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 == $v2; break; case '?:': $v3 = array_pop( $stack ); $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 ? $v2 : $v3; break; default: throw new Exception( sprintf( 'Unknown operator "%s"', $next[1] ) ); } } if ( count( $stack ) !== 1 ) { throw new Exception( 'Too many values remaining on the stack' ); } return (int) $stack[0]; } } pomo/entry.php000064400000010333147207017560007401 0ustar00 $value ) { $this->$varname = $value; } if ( isset( $args['plural'] ) && $args['plural'] ) { $this->is_plural = true; } if ( ! is_array( $this->translations ) ) { $this->translations = array(); } if ( ! is_array( $this->references ) ) { $this->references = array(); } if ( ! is_array( $this->flags ) ) { $this->flags = array(); } } /** * Generates a unique key for this entry * * @return string|bool the key or false if the entry is empty */ function key() { if ( null === $this->singular || '' === $this->singular ) { return false; } // Prepend context and EOT, like in MO files. $key = ! $this->context ? $this->singular : $this->context . "\4" . $this->singular; // Standardize on \n line endings. $key = str_replace( array( "\r\n", "\r" ), "\n", $key ); return $key; } /** * @param object $other */ function merge_with( &$other ) { $this->flags = array_unique( array_merge( $this->flags, $other->flags ) ); $this->references = array_unique( array_merge( $this->references, $other->references ) ); if ( $this->extracted_comments != $other->extracted_comments ) { $this->extracted_comments .= $other->extracted_comments; } } } endif; pomo/streams.php000064400000016510147207017560007721 0ustar00 * * @version $Id: streams.php 1157 2015-11-20 04:30:11Z dd32 $ * @package pomo * @subpackage streams */ if ( ! class_exists( 'wfPOMO_Reader', false ) ) : class wfPOMO_Reader { var $endian = 'little'; var $_post = ''; private $is_overloaded; protected $_pos; /** * PHP5 constructor. */ function __construct() { $this->is_overloaded = ( ( ini_get( 'mbstring.func_overload' ) & 2 ) != 0 ) && function_exists( 'mb_substr' ); // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated $this->_pos = 0; } /** * Sets the endianness of the file. * * @param string $endian Set the endianness of the file. Accepts 'big', or 'little'. */ function setEndian( $endian ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid $this->endian = $endian; } /** * Reads a 32bit Integer from the Stream * * @return mixed The integer, corresponding to the next 32 bits from * the stream of false if there are not enough bytes or on error */ function readint32() { $bytes = $this->read( 4 ); if ( 4 != $this->strlen( $bytes ) ) { return false; } $endian_letter = ( 'big' === $this->endian ) ? 'N' : 'V'; $int = unpack( $endian_letter, $bytes ); return reset( $int ); } /** * Reads an array of 32-bit Integers from the Stream * * @param integer $count How many elements should be read * @return mixed Array of integers or false if there isn't * enough data or on error */ function readint32array( $count ) { $bytes = $this->read( 4 * $count ); if ( 4 * $count != $this->strlen( $bytes ) ) { return false; } $endian_letter = ( 'big' === $this->endian ) ? 'N' : 'V'; return unpack( $endian_letter . $count, $bytes ); } /** * @param string $string * @param int $start * @param int $length * @return string */ function substr( $string, $start, $length ) { if ( $this->is_overloaded ) { return mb_substr( $string, $start, $length, 'ascii' ); } else { return substr( $string, $start, $length ); } } /** * @param string $string * @return int */ function strlen( $string ) { if ( $this->is_overloaded ) { return mb_strlen( $string, 'ascii' ); } else { return strlen( $string ); } } /** * @param string $string * @param int $chunk_size * @return array */ function str_split( $string, $chunk_size ) { if ( ! function_exists( 'str_split' ) ) { $length = $this->strlen( $string ); $out = array(); for ( $i = 0; $i < $length; $i += $chunk_size ) { $out[] = $this->substr( $string, $i, $chunk_size ); } return $out; } else { return str_split( $string, $chunk_size ); } } /** * @return int */ function pos() { return $this->_pos; } /** * @return true */ function is_resource() { return true; } /** * @return true */ function close() { return true; } } endif; if ( ! class_exists( 'wfPOMO_FileReader', false ) ) : class wfPOMO_FileReader extends wfPOMO_Reader { private $_f; /** * @param string $filename */ function __construct( $filename ) { parent::__construct(); $this->_f = fopen( $filename, 'rb' ); } /** * @param int $bytes * @return string|false Returns read string, otherwise false. */ function read( $bytes ) { return fread( $this->_f, $bytes ); } /** * @param int $pos * @return boolean */ function seekto( $pos ) { if ( -1 == fseek( $this->_f, $pos, SEEK_SET ) ) { return false; } $this->_pos = $pos; return true; } /** * @return bool */ function is_resource() { return is_resource( $this->_f ); } /** * @return bool */ function feof() { return feof( $this->_f ); } /** * @return bool */ function close() { return fclose( $this->_f ); } /** * @return string */ function read_all() { $all = ''; while ( ! $this->feof() ) { $all .= $this->read( 4096 ); } return $all; } } endif; if ( ! class_exists( 'wfPOMO_StringReader', false ) ) : /** * Provides file-like methods for manipulating a string instead * of a physical file. */ class wfPOMO_StringReader extends wfPOMO_Reader { var $_str = ''; /** * PHP5 constructor. */ function __construct( $str = '' ) { parent::__construct(); $this->_str = $str; $this->_pos = 0; } /** * @param string $bytes * @return string */ function read( $bytes ) { $data = $this->substr( $this->_str, $this->_pos, $bytes ); $this->_pos += $bytes; if ( $this->strlen( $this->_str ) < $this->_pos ) { $this->_pos = $this->strlen( $this->_str ); } return $data; } /** * @param int $pos * @return int */ function seekto( $pos ) { $this->_pos = $pos; if ( $this->strlen( $this->_str ) < $this->_pos ) { $this->_pos = $this->strlen( $this->_str ); } return $this->_pos; } /** * @return int */ function length() { return $this->strlen( $this->_str ); } /** * @return string */ function read_all() { return $this->substr( $this->_str, $this->_pos, $this->strlen( $this->_str ) ); } } endif; if ( ! class_exists( 'wfPOMO_CachedFileReader', false ) ) : /** * Reads the contents of the file in the beginning. */ class wfPOMO_CachedFileReader extends wfPOMO_StringReader { /** * PHP5 constructor. */ function __construct( $filename ) { parent::__construct(); $this->_str = file_get_contents( $filename ); if ( false === $this->_str ) { return false; } $this->_pos = 0; } } endif; if ( ! class_exists( 'wfPOMO_CachedIntFileReader', false ) ) : /** * Reads the contents of the file in the beginning. */ class wfPOMO_CachedIntFileReader extends wfPOMO_CachedFileReader { /** * PHP5 constructor. */ public function __construct( $filename ) { parent::__construct( $filename ); } } endif; pomo/translations.php000064400000025350147207017560010766 0ustar00key(); if ( false === $key ) { return false; } $this->entries[ $key ] = &$entry; return true; } /** * @param array|wfTranslation_Entry $entry * @return bool */ function add_entry_or_merge( $entry ) { if ( is_array( $entry ) ) { $entry = new wfTranslation_Entry( $entry ); } $key = $entry->key(); if ( false === $key ) { return false; } if ( isset( $this->entries[ $key ] ) ) { $this->entries[ $key ]->merge_with( $entry ); } else { $this->entries[ $key ] = &$entry; } return true; } /** * Sets $header PO header to $value * * If the header already exists, it will be overwritten * * TODO: this should be out of this class, it is gettext specific * * @param string $header header name, without trailing : * @param string $value header value, without trailing \n */ function set_header( $header, $value ) { $this->headers[ $header ] = $value; } /** * @param array $headers */ function set_headers( $headers ) { foreach ( $headers as $header => $value ) { $this->set_header( $header, $value ); } } /** * @param string $header */ function get_header( $header ) { return isset( $this->headers[ $header ] ) ? $this->headers[ $header ] : false; } /** * @param wfTranslation_Entry $entry */ function translate_entry( &$entry ) { $key = $entry->key(); return isset( $this->entries[ $key ] ) ? $this->entries[ $key ] : false; } /** * @param string $singular * @param string $context * @return string */ function translate( $singular, $context = null ) { $entry = new wfTranslation_Entry( array( 'singular' => $singular, 'context' => $context, ) ); $translated = $this->translate_entry( $entry ); return ( $translated && ! empty( $translated->translations ) ) ? $translated->translations[0] : $singular; } /** * Given the number of items, returns the 0-based index of the plural form to use * * Here, in the base Translations class, the common logic for English is implemented: * 0 if there is one element, 1 otherwise * * This function should be overridden by the subclasses. For example MO/PO can derive the logic * from their headers. * * @param integer $count number of items */ function select_plural_form( $count ) { return 1 == $count ? 0 : 1; } /** * @return int */ function get_plural_forms_count() { return 2; } /** * @param string $singular * @param string $plural * @param int $count * @param string $context */ function translate_plural( $singular, $plural, $count, $context = null ) { $entry = new wfTranslation_Entry( array( 'singular' => $singular, 'plural' => $plural, 'context' => $context, ) ); $translated = $this->translate_entry( $entry ); $index = $this->select_plural_form( $count ); $total_plural_forms = $this->get_plural_forms_count(); if ( $translated && 0 <= $index && $index < $total_plural_forms && is_array( $translated->translations ) && isset( $translated->translations[ $index ] ) ) { return $translated->translations[ $index ]; } else { return 1 == $count ? $singular : $plural; } } /** * Merge $other in the current object. * * @param Object $other Another Translation object, whose translations will be merged in this one (passed by reference). * @return void */ function merge_with( &$other ) { foreach ( $other->entries as $entry ) { $this->entries[ $entry->key() ] = $entry; } } /** * @param object $other */ function merge_originals_with( &$other ) { foreach ( $other->entries as $entry ) { if ( ! isset( $this->entries[ $entry->key() ] ) ) { $this->entries[ $entry->key() ] = $entry; } else { $this->entries[ $entry->key() ]->merge_with( $entry ); } } } } class wfGettext_Translations extends wfTranslations { private $_gettext_select_plural_form = null; /** * The gettext implementation of select_plural_form. * * It lives in this class, because there are more than one descendand, which will use it and * they can't share it effectively. * * @param int $count */ function gettext_select_plural_form( $count ) { if ( ! isset( $this->_gettext_select_plural_form ) || is_null( $this->_gettext_select_plural_form ) ) { list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header( $this->get_header( 'Plural-Forms' ) ); $this->_nplurals = $nplurals; $this->_gettext_select_plural_form = $this->make_plural_form_function( $nplurals, $expression ); } return call_user_func( $this->_gettext_select_plural_form, $count ); } /** * @param string $header * @return array */ function nplurals_and_expression_from_header( $header ) { if ( preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches ) ) { $nplurals = (int) $matches[1]; $expression = trim( $matches[2] ); return array( $nplurals, $expression ); } else { return array( 2, 'n != 1' ); } } /** * Makes a function, which will return the right translation index, according to the * plural forms header * * @param int $nplurals * @param string $expression */ function make_plural_form_function( $nplurals, $expression ) { try { $handler = new wfPlural_Forms( rtrim( $expression, ';' ) ); return array( $handler, 'get' ); } catch ( Exception $e ) { // Fall back to default plural-form function. return $this->make_plural_form_function( 2, 'n != 1' ); } } /** * Adds parentheses to the inner parts of ternary operators in * plural expressions, because PHP evaluates ternary oerators from left to right * * @param string $expression the expression without parentheses * @return string the expression with parentheses added */ function parenthesize_plural_exression( $expression ) { $expression .= ';'; $res = ''; $depth = 0; for ( $i = 0; $i < strlen( $expression ); ++$i ) { $char = $expression[ $i ]; switch ( $char ) { case '?': $res .= ' ? ('; $depth++; break; case ':': $res .= ') : ('; break; case ';': $res .= str_repeat( ')', $depth ) . ';'; $depth = 0; break; default: $res .= $char; } } return rtrim( $res, ';' ); } /** * @param string $translation * @return array */ function make_headers( $translation ) { $headers = array(); // Sometimes \n's are used instead of real new lines. $translation = str_replace( '\n', "\n", $translation ); $lines = explode( "\n", $translation ); foreach ( $lines as $line ) { $parts = explode( ':', $line, 2 ); if ( ! isset( $parts[1] ) ) { continue; } $headers[ trim( $parts[0] ) ] = trim( $parts[1] ); } return $headers; } /** * @param string $header * @param string $value */ function set_header( $header, $value ) { parent::set_header( $header, $value ); if ( 'Plural-Forms' === $header ) { list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header( $this->get_header( 'Plural-Forms' ) ); $this->_nplurals = $nplurals; $this->_gettext_select_plural_form = $this->make_plural_form_function( $nplurals, $expression ); } } } endif; if ( ! class_exists( 'wfNOOP_Translations', false ) ) : /** * Provides the same interface as Translations, but doesn't do anything */ class wfNOOP_Translations { var $entries = array(); var $headers = array(); function add_entry( $entry ) { return true; } /** * @param string $header * @param string $value */ function set_header( $header, $value ) { } /** * @param array $headers */ function set_headers( $headers ) { } /** * @param string $header * @return false */ function get_header( $header ) { return false; } /** * @param wfTranslation_Entry $entry * @return false */ function translate_entry( &$entry ) { return false; } /** * @param string $singular * @param string $context */ function translate( $singular, $context = null ) { return $singular; } /** * @param int $count * @return bool */ function select_plural_form( $count ) { return 1 == $count ? 0 : 1; } /** * @return int */ function get_plural_forms_count() { return 2; } /** * @param string $singular * @param string $plural * @param int $count * @param string $context */ function translate_plural( $singular, $plural, $count, $context = null ) { return 1 == $count ? $singular : $plural; } /** * @param object $other */ function merge_with( &$other ) { } } endif;pomo/po.php000064400000037406147207017560006670 0ustar00headers as $header => $value ) { $header_string .= "$header: $value\n"; } $poified = wfPO::poify( $header_string ); if ( $this->comments_before_headers ) { $before_headers = $this->prepend_each_line( rtrim( $this->comments_before_headers ) . "\n", '# ' ); } else { $before_headers = ''; } return rtrim( "{$before_headers}msgid \"\"\nmsgstr $poified" ); } /** * Exports all entries to PO format * * @return string sequence of mgsgid/msgstr PO strings, doesn't containt newline at the end */ function export_entries() { // TODO: Sorting. return implode( "\n\n", array_map( array('wfPO', 'export_entry' ), $this->entries ) ); } /** * Exports the whole PO file as a string * * @param bool $include_headers whether to include the headers in the export * @return string ready for inclusion in PO file string for headers and all the enrtries */ function export( $include_headers = true ) { $res = ''; if ( $include_headers ) { $res .= $this->export_headers(); $res .= "\n\n"; } $res .= $this->export_entries(); return $res; } /** * Same as {@link export}, but writes the result to a file * * @param string $filename Where to write the PO string. * @param bool $include_headers Whether to include the headers in the export. * @return bool true on success, false on error */ function export_to_file( $filename, $include_headers = true ) { $fh = fopen( $filename, 'w' ); if ( false === $fh ) { return false; } $export = $this->export( $include_headers ); $res = fwrite( $fh, $export ); if ( false === $res ) { return false; } return fclose( $fh ); } /** * Text to include as a comment before the start of the PO contents * * Doesn't need to include # in the beginning of lines, these are added automatically * * @param string $text Text to include as a comment. */ function set_comment_before_headers( $text ) { $this->comments_before_headers = $text; } /** * Formats a string in PO-style * * @param string $string the string to format * @return string the poified string */ public static function poify( $string ) { $quote = '"'; $slash = '\\'; $newline = "\n"; $replaces = array( "$slash" => "$slash$slash", "$quote" => "$slash$quote", "\t" => '\t', ); $string = str_replace( array_keys( $replaces ), array_values( $replaces ), $string ); $po = $quote . implode( "${slash}n$quote$newline$quote", explode( $newline, $string ) ) . $quote; // Add empty string on first line for readbility. if ( false !== strpos( $string, $newline ) && ( substr_count( $string, $newline ) > 1 || substr( $string, -strlen( $newline ) ) !== $newline ) ) { $po = "$quote$quote$newline$po"; } // Remove empty strings. $po = str_replace( "$newline$quote$quote", '', $po ); return $po; } /** * Gives back the original string from a PO-formatted string * * @param string $string PO-formatted string * @return string enascaped string */ public static function unpoify( $string ) { $escapes = array( 't' => "\t", 'n' => "\n", 'r' => "\r", '\\' => '\\', ); $lines = array_map( 'trim', explode( "\n", $string ) ); $lines = array_map( array('wfPO', 'trim_quotes' ), $lines ); $unpoified = ''; $previous_is_backslash = false; foreach ( $lines as $line ) { preg_match_all( '/./u', $line, $chars ); $chars = $chars[0]; foreach ( $chars as $char ) { if ( ! $previous_is_backslash ) { if ( '\\' === $char ) { $previous_is_backslash = true; } else { $unpoified .= $char; } } else { $previous_is_backslash = false; $unpoified .= isset( $escapes[ $char ] ) ? $escapes[ $char ] : $char; } } } // Standardise the line endings on imported content, technically PO files shouldn't contain \r. $unpoified = str_replace( array( "\r\n", "\r" ), "\n", $unpoified ); return $unpoified; } /** * Inserts $with in the beginning of every new line of $string and * returns the modified string * * @param string $string prepend lines in this string * @param string $with prepend lines with this string */ public static function prepend_each_line( $string, $with ) { $lines = explode( "\n", $string ); $append = ''; if ( "\n" === substr( $string, -1 ) && '' === end( $lines ) ) { /* * Last line might be empty because $string was terminated * with a newline, remove it from the $lines array, * we'll restore state by re-terminating the string at the end. */ array_pop( $lines ); $append = "\n"; } foreach ( $lines as &$line ) { $line = $with . $line; } unset( $line ); return implode( "\n", $lines ) . $append; } /** * Prepare a text as a comment -- wraps the lines and prepends # * and a special character to each line * * @access private * @param string $text the comment text * @param string $char character to denote a special PO comment, * like :, default is a space */ public static function comment_block( $text, $char = ' ' ) { $text = wordwrap( $text, WF_PO_MAX_LINE_LEN - 3 ); return wfPO::prepend_each_line( $text, "#$char " ); } /** * Builds a string from the entry for inclusion in PO file * * @param wfTranslation_Entry $entry the entry to convert to po string (passed by reference). * @return string|false PO-style formatted string for the entry or * false if the entry is empty */ public static function export_entry( &$entry ) { if ( null === $entry->singular || '' === $entry->singular ) { return false; } $po = array(); if ( ! empty( $entry->translator_comments ) ) { $po[] = wfPO::comment_block( $entry->translator_comments ); } if ( ! empty( $entry->extracted_comments ) ) { $po[] = wfPO::comment_block( $entry->extracted_comments, '.' ); } if ( ! empty( $entry->references ) ) { $po[] = wfPO::comment_block( implode( ' ', $entry->references ), ':' ); } if ( ! empty( $entry->flags ) ) { $po[] = wfPO::comment_block( implode( ', ', $entry->flags ), ',' ); } if ( $entry->context ) { $po[] = 'msgctxt ' . wfPO::poify( $entry->context ); } $po[] = 'msgid ' . wfPO::poify( $entry->singular ); if ( ! $entry->is_plural ) { $translation = empty( $entry->translations ) ? '' : $entry->translations[0]; $translation = wfPO::match_begin_and_end_newlines( $translation, $entry->singular ); $po[] = 'msgstr ' . wfPO::poify( $translation ); } else { $po[] = 'msgid_plural ' . wfPO::poify( $entry->plural ); $translations = empty( $entry->translations ) ? array( '', '' ) : $entry->translations; foreach ( $translations as $i => $translation ) { $translation = wfPO::match_begin_and_end_newlines( $translation, $entry->plural ); $po[] = "msgstr[$i] " . wfPO::poify( $translation ); } } return implode( "\n", $po ); } public static function match_begin_and_end_newlines( $translation, $original ) { if ( '' === $translation ) { return $translation; } $original_begin = "\n" === substr( $original, 0, 1 ); $original_end = "\n" === substr( $original, -1 ); $translation_begin = "\n" === substr( $translation, 0, 1 ); $translation_end = "\n" === substr( $translation, -1 ); if ( $original_begin ) { if ( ! $translation_begin ) { $translation = "\n" . $translation; } } elseif ( $translation_begin ) { $translation = ltrim( $translation, "\n" ); } if ( $original_end ) { if ( ! $translation_end ) { $translation .= "\n"; } } elseif ( $translation_end ) { $translation = rtrim( $translation, "\n" ); } return $translation; } /** * @param string $filename * @return boolean */ function import_from_file( $filename ) { $f = fopen( $filename, 'r' ); if ( ! $f ) { return false; } $lineno = 0; while ( true ) { $res = $this->read_entry( $f, $lineno ); if ( ! $res ) { break; } if ( '' === $res['entry']->singular ) { $this->set_headers( $this->make_headers( $res['entry']->translations[0] ) ); } else { $this->add_entry( $res['entry'] ); } } wfPO::read_line( $f, 'clear' ); if ( false === $res ) { return false; } if ( ! $this->headers && ! $this->entries ) { return false; } return true; } /** * Helper function for read_entry * * @param string $context * @return bool */ protected static function is_final( $context ) { return ( 'msgstr' === $context ) || ( 'msgstr_plural' === $context ); } /** * @param resource $f * @param int $lineno * @return null|false|array */ function read_entry( $f, $lineno = 0 ) { $entry = new wfTranslation_Entry(); // Where were we in the last step. // Can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural. $context = ''; $msgstr_index = 0; while ( true ) { $lineno++; $line = wfPO::read_line( $f ); if ( ! $line ) { if ( feof( $f ) ) { if ( self::is_final( $context ) ) { break; } elseif ( ! $context ) { // We haven't read a line and EOF came. return null; } else { return false; } } else { return false; } } if ( "\n" === $line ) { continue; } $line = trim( $line ); if ( preg_match( '/^#/', $line, $m ) ) { // The comment is the start of a new entry. if ( self::is_final( $context ) ) { wfPO::read_line( $f, 'put-back' ); $lineno--; break; } // Comments have to be at the beginning. if ( $context && 'comment' !== $context ) { return false; } // Add comment. $this->add_comment_to_entry( $entry, $line ); } elseif ( preg_match( '/^msgctxt\s+(".*")/', $line, $m ) ) { if ( self::is_final( $context ) ) { wfPO::read_line( $f, 'put-back' ); $lineno--; break; } if ( $context && 'comment' !== $context ) { return false; } $context = 'msgctxt'; $entry->context .= wfPO::unpoify( $m[1] ); } elseif ( preg_match( '/^msgid\s+(".*")/', $line, $m ) ) { if ( self::is_final( $context ) ) { wfPO::read_line( $f, 'put-back' ); $lineno--; break; } if ( $context && 'msgctxt' !== $context && 'comment' !== $context ) { return false; } $context = 'msgid'; $entry->singular .= wfPO::unpoify( $m[1] ); } elseif ( preg_match( '/^msgid_plural\s+(".*")/', $line, $m ) ) { if ( 'msgid' !== $context ) { return false; } $context = 'msgid_plural'; $entry->is_plural = true; $entry->plural .= wfPO::unpoify( $m[1] ); } elseif ( preg_match( '/^msgstr\s+(".*")/', $line, $m ) ) { if ( 'msgid' !== $context ) { return false; } $context = 'msgstr'; $entry->translations = array( wfPO::unpoify( $m[1] ) ); } elseif ( preg_match( '/^msgstr\[(\d+)\]\s+(".*")/', $line, $m ) ) { if ( 'msgid_plural' !== $context && 'msgstr_plural' !== $context ) { return false; } $context = 'msgstr_plural'; $msgstr_index = $m[1]; $entry->translations[ $m[1] ] = wfPO::unpoify( $m[2] ); } elseif ( preg_match( '/^".*"$/', $line ) ) { $unpoified = wfPO::unpoify( $line ); switch ( $context ) { case 'msgid': $entry->singular .= $unpoified; break; case 'msgctxt': $entry->context .= $unpoified; break; case 'msgid_plural': $entry->plural .= $unpoified; break; case 'msgstr': $entry->translations[0] .= $unpoified; break; case 'msgstr_plural': $entry->translations[ $msgstr_index ] .= $unpoified; break; default: return false; } } else { return false; } } $have_translations = false; foreach ( $entry->translations as $t ) { if ( $t || ( '0' === $t ) ) { $have_translations = true; break; } } if ( false === $have_translations ) { $entry->translations = array(); } return array( 'entry' => $entry, 'lineno' => $lineno, ); } /** * @param resource $f * @param string $action * @return boolean */ function read_line( $f, $action = 'read' ) { static $last_line = ''; static $use_last_line = false; if ( 'clear' === $action ) { $last_line = ''; return true; } if ( 'put-back' === $action ) { $use_last_line = true; return true; } $line = $use_last_line ? $last_line : fgets( $f ); $line = ( "\r\n" === substr( $line, -2 ) ) ? rtrim( $line, "\r\n" ) . "\n" : $line; $last_line = $line; $use_last_line = false; return $line; } /** * @param wfTranslation_Entry $entry * @param string $po_comment_line */ function add_comment_to_entry( &$entry, $po_comment_line ) { $first_two = substr( $po_comment_line, 0, 2 ); $comment = trim( substr( $po_comment_line, 2 ) ); if ( '#:' === $first_two ) { $entry->references = array_merge( $entry->references, preg_split( '/\s+/', $comment ) ); } elseif ( '#.' === $first_two ) { $entry->extracted_comments = trim( $entry->extracted_comments . "\n" . $comment ); } elseif ( '#,' === $first_two ) { $entry->flags = array_merge( $entry->flags, preg_split( '/,\s*/', $comment ) ); } else { $entry->translator_comments = trim( $entry->translator_comments . "\n" . $comment ); } } /** * @param string $s * @return string */ public static function trim_quotes( $s ) { if ( '"' === substr( $s, 0, 1 ) ) { $s = substr( $s, 1 ); } if ( '"' === substr( $s, -1, 1 ) ) { $s = substr( $s, 0, -1 ); } return $s; } } endif; pomo/mo.php000064400000024732147207017560006663 0ustar00filename; } /** * Fills up with the entries from MO file $filename * * @param string $filename MO file to load * @return bool True if the import from file was successful, otherwise false. */ function import_from_file( $filename ) { $reader = new wfPOMO_FileReader( $filename ); if ( ! $reader->is_resource() ) { return false; } $this->filename = (string) $filename; return $this->import_from_reader( $reader ); } /** * @param string $filename * @return bool */ function export_to_file( $filename ) { $fh = fopen( $filename, 'wb' ); if ( ! $fh ) { return false; } $res = $this->export_to_file_handle( $fh ); fclose( $fh ); return $res; } /** * @return string|false */ function export() { $tmp_fh = fopen( 'php://temp', 'r+' ); if ( ! $tmp_fh ) { return false; } $this->export_to_file_handle( $tmp_fh ); rewind( $tmp_fh ); return stream_get_contents( $tmp_fh ); } /** * @param wfTranslation_Entry $entry * @return bool */ function is_entry_good_for_export( $entry ) { if ( empty( $entry->translations ) ) { return false; } if ( ! array_filter( $entry->translations ) ) { return false; } return true; } /** * @param resource $fh * @return true */ function export_to_file_handle( $fh ) { $entries = array_filter( $this->entries, array( $this, 'is_entry_good_for_export' ) ); ksort( $entries ); $magic = 0x950412de; $revision = 0; $total = count( $entries ) + 1; // All the headers are one entry. $originals_lenghts_addr = 28; $translations_lenghts_addr = $originals_lenghts_addr + 8 * $total; $size_of_hash = 0; $hash_addr = $translations_lenghts_addr + 8 * $total; $current_addr = $hash_addr; fwrite( $fh, pack( 'V*', $magic, $revision, $total, $originals_lenghts_addr, $translations_lenghts_addr, $size_of_hash, $hash_addr ) ); fseek( $fh, $originals_lenghts_addr ); // Headers' msgid is an empty string. fwrite( $fh, pack( 'VV', 0, $current_addr ) ); $current_addr++; $originals_table = "\0"; $reader = new wfPOMO_Reader(); foreach ( $entries as $entry ) { $originals_table .= $this->export_original( $entry ) . "\0"; $length = $reader->strlen( $this->export_original( $entry ) ); fwrite( $fh, pack( 'VV', $length, $current_addr ) ); $current_addr += $length + 1; // Account for the NULL byte after. } $exported_headers = $this->export_headers(); fwrite( $fh, pack( 'VV', $reader->strlen( $exported_headers ), $current_addr ) ); $current_addr += strlen( $exported_headers ) + 1; $translations_table = $exported_headers . "\0"; foreach ( $entries as $entry ) { $translations_table .= $this->export_translations( $entry ) . "\0"; $length = $reader->strlen( $this->export_translations( $entry ) ); fwrite( $fh, pack( 'VV', $length, $current_addr ) ); $current_addr += $length + 1; } fwrite( $fh, $originals_table ); fwrite( $fh, $translations_table ); return true; } /** * @param wfTranslation_Entry $entry * @return string */ function export_original( $entry ) { // TODO: Warnings for control characters. $exported = $entry->singular; if ( $entry->is_plural ) { $exported .= "\0" . $entry->plural; } if ( $entry->context ) { $exported = $entry->context . "\4" . $exported; } return $exported; } /** * @param wfTranslation_Entry $entry * @return string */ function export_translations( $entry ) { // TODO: Warnings for control characters. return $entry->is_plural ? implode( "\0", $entry->translations ) : $entry->translations[0]; } /** * @return string */ function export_headers() { $exported = ''; foreach ( $this->headers as $header => $value ) { $exported .= "$header: $value\n"; } return $exported; } /** * @param int $magic * @return string|false */ function get_byteorder( $magic ) { // The magic is 0x950412de. // bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565 $magic_little = (int) - 1794895138; $magic_little_64 = (int) 2500072158; // 0xde120495 $magic_big = ( (int) - 569244523 ) & 0xFFFFFFFF; if ( $magic_little == $magic || $magic_little_64 == $magic ) { return 'little'; } elseif ( $magic_big == $magic ) { return 'big'; } else { return false; } } /** * @param wfPOMO_FileReader $reader * @return bool True if the import was successful, otherwise false. */ function import_from_reader( $reader ) { $endian_string = wfMO::get_byteorder( $reader->readint32() ); if ( false === $endian_string ) { return false; } $reader->setEndian( $endian_string ); $endian = ( 'big' === $endian_string ) ? 'N' : 'V'; $header = $reader->read( 24 ); if ( $reader->strlen( $header ) != 24 ) { return false; } // Parse header. $header = unpack( "{$endian}revision/{$endian}total/{$endian}originals_lenghts_addr/{$endian}translations_lenghts_addr/{$endian}hash_length/{$endian}hash_addr", $header ); if ( ! is_array( $header ) ) { return false; } // Support revision 0 of MO format specs, only. if ( 0 != $header['revision'] ) { return false; } // Seek to data blocks. $reader->seekto( $header['originals_lenghts_addr'] ); // Read originals' indices. $originals_lengths_length = $header['translations_lenghts_addr'] - $header['originals_lenghts_addr']; if ( $originals_lengths_length != $header['total'] * 8 ) { return false; } $originals = $reader->read( $originals_lengths_length ); if ( $reader->strlen( $originals ) != $originals_lengths_length ) { return false; } // Read translations' indices. $translations_lenghts_length = $header['hash_addr'] - $header['translations_lenghts_addr']; if ( $translations_lenghts_length != $header['total'] * 8 ) { return false; } $translations = $reader->read( $translations_lenghts_length ); if ( $reader->strlen( $translations ) != $translations_lenghts_length ) { return false; } // Transform raw data into set of indices. $originals = $reader->str_split( $originals, 8 ); $translations = $reader->str_split( $translations, 8 ); // Skip hash table. $strings_addr = $header['hash_addr'] + $header['hash_length'] * 4; $reader->seekto( $strings_addr ); $strings = $reader->read_all(); $reader->close(); for ( $i = 0; $i < $header['total']; $i++ ) { $o = unpack( "{$endian}length/{$endian}pos", $originals[ $i ] ); $t = unpack( "{$endian}length/{$endian}pos", $translations[ $i ] ); if ( ! $o || ! $t ) { return false; } // Adjust offset due to reading strings to separate space before. $o['pos'] -= $strings_addr; $t['pos'] -= $strings_addr; $original = $reader->substr( $strings, $o['pos'], $o['length'] ); $translation = $reader->substr( $strings, $t['pos'], $t['length'] ); if ( '' === $original ) { $this->set_headers( $this->make_headers( $translation ) ); } else { $entry = &$this->make_entry( $original, $translation ); $this->entries[ $entry->key() ] = &$entry; } } return true; } /** * Build a Translation_Entry from original string and translation strings, * found in a MO file * * @static * @param string $original original string to translate from MO file. Might contain * 0x04 as context separator or 0x00 as singular/plural separator * @param string $translation translation string from MO file. Might contain * 0x00 as a plural translations separator * @return wfTranslation_Entry Entry instance. */ function &make_entry( $original, $translation ) { $entry = new wfTranslation_Entry(); // Look for context, separated by \4. $parts = explode( "\4", $original ); if ( isset( $parts[1] ) ) { $original = $parts[1]; $entry->context = $parts[0]; } // Look for plural original. $parts = explode( "\0", $original ); $entry->singular = $parts[0]; if ( isset( $parts[1] ) ) { $entry->is_plural = true; $entry->plural = $parts[1]; } // Plural translations are also separated by \0. $entry->translations = explode( "\0", $translation ); return $entry; } /** * @param int $count * @return string */ function select_plural_form( $count ) { return $this->gettext_select_plural_form( $count ); } /** * @return int */ function get_plural_forms_count() { return $this->_nplurals; } } endif; bootstrap.php000064400000103667147207017560007320 0ustar00= 0) { $class = get_called_class(); $request = new $class(); } else { $request = new self(); } return parent::createFromGlobals($request); } public function getIP() { static $theIP = null; if (isset($theIP)) { return $theIP; } $ips = array(); $howGet = wfWAF::getInstance()->getStorageEngine()->getConfig('howGetIPs', null, 'synced'); if ($howGet) { if (is_string($howGet) && is_array($_SERVER) && array_key_exists($howGet, $_SERVER)) { $ips[] = array($_SERVER[$howGet], $howGet); } if ($howGet != 'REMOTE_ADDR') { $ips[] = array((is_array($_SERVER) && array_key_exists('REMOTE_ADDR', $_SERVER)) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1', 'REMOTE_ADDR'); } } else { $recommendedField = wfWAF::getInstance()->getStorageEngine()->getConfig('detectProxyRecommendation', null, 'synced'); if (!empty($recommendedField) && $recommendedField != 'UNKNOWN' && $recommendedField != 'DEFERRED') { if (isset($_SERVER[$recommendedField])) { $ips[] = array($_SERVER[$recommendedField], $recommendedField); } } $ips[] = array((is_array($_SERVER) && array_key_exists('REMOTE_ADDR', $_SERVER)) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1', 'REMOTE_ADDR'); if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips[] = array($_SERVER['HTTP_X_FORWARDED_FOR'], 'HTTP_X_FORWARDED_FOR'); } if (isset($_SERVER['HTTP_X_REAL_IP'])) { $ips[] = array($_SERVER['HTTP_X_REAL_IP'], 'HTTP_X_REAL_IP'); } } $cleanedIP = $this->_getCleanIPAndServerVar($ips); if (is_array($cleanedIP)) { list($ip, $variable) = $cleanedIP; $theIP = $ip; return $ip; } $theIP = $cleanedIP; return $cleanedIP; } /** * Expects an array of items. The items are either IPs or IPs separated by comma, space or tab. Or an array of IP's. * We then examine all IP's looking for a public IP and storing private IP's in an array. If we find no public IPs we return the first private addr we found. * * @param array $arr * @return bool|mixed */ private function _getCleanIPAndServerVar($arr) { $privates = array(); //Store private addrs until end as last resort. foreach ($arr as $entry) { list($item, $var) = $entry; if (is_array($item)) { foreach ($item as $j) { // try verifying the IP is valid before stripping the port off if (!$this->_isValidIP($j)) { $j = preg_replace('/:\d+$/', '', $j); //Strip off port } if ($this->_isValidIP($j)) { if ($this->_isIPv6MappedIPv4($j)) { $j = wfWAFUtils::inet_ntop(wfWAFUtils::inet_pton($j)); } if ($this->_isPrivateIP($j)) { $privates[] = array($j, $var); } else { return array($j, $var); } } } continue; //This was an array so we can skip to the next item } $skipToNext = false; $trustedProxyConfig = wfWAF::getInstance()->getStorageEngine()->getConfig('howGetIPs_trusted_proxies_unified', null, 'synced'); $trustedProxies = $trustedProxyConfig === null ? array() : explode("\n", $trustedProxyConfig); foreach (array(',', ' ', "\t") as $char) { if (strpos($item, $char) !== false) { $sp = explode($char, $item); $sp = array_reverse($sp); foreach ($sp as $index => $j) { $j = trim($j); if (!$this->_isValidIP($j)) { $j = preg_replace('/:\d+$/', '', $j); //Strip off port } if ($this->_isValidIP($j)) { if ($this->_isIPv6MappedIPv4($j)) { $j = wfWAFUtils::inet_ntop(wfWAFUtils::inet_pton($j)); } foreach ($trustedProxies as $proxy) { if (!empty($proxy)) { if (wfWAFUtils::subnetContainsIP($proxy, $j) && $index < count($sp) - 1) { continue 2; } } } if ($this->_isPrivateIP($j)) { $privates[] = array($j, $var); } else { return array($j, $var); } } } $skipToNext = true; break; } } if ($skipToNext){ continue; } //Skip to next item because this one had a comma, space or tab so was delimited and we didn't find anything. if (!$this->_isValidIP($item)) { $item = preg_replace('/:\d+$/', '', $item); //Strip off port } if ($this->_isValidIP($item)) { if ($this->_isIPv6MappedIPv4($item)) { $item = wfWAFUtils::inet_ntop(wfWAFUtils::inet_pton($item)); } if ($this->_isPrivateIP($item)) { $privates[] = array($item, $var); } else { return array($item, $var); } } } if (sizeof($privates) > 0) { return $privates[0]; //Return the first private we found so that we respect the order the IP's were passed to this function. } return false; } /** * @param string $ip * @return bool */ private function _isValidIP($ip) { return filter_var($ip, FILTER_VALIDATE_IP) !== false; } /** * @param string $ip * @return bool */ private function _isIPv6MappedIPv4($ip) { return preg_match('/^(?:\:(?:\:0{1,4}){0,4}\:|(?:0{1,4}\:){5})ffff\:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/i', $ip) > 0; } /** * @param string $addr Should be in dot or colon notation (127.0.0.1 or ::1) * @return bool */ private function _isPrivateIP($ip) { // Run this through the preset list for IPv4 addresses. if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) { $wordfenceLib = realpath(dirname(__FILE__) . '/../lib'); include($wordfenceLib . '/wfIPWhitelist.php'); // defines $wfIPWhitelist $private = $wfIPWhitelist['private']; foreach ($private as $a) { if (wfWAFUtils::subnetContainsIP($a, $ip)) { return true; } } } return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) !== false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false; } } class wfWAFWordPressObserver extends wfWAFBaseObserver { private $waf; public function __construct($waf){ $this->waf=$waf; } public function beforeRunRules() { // Whitelisted URLs (in WAF config) $whitelistedURLs = wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedURLs', null, 'livewaf'); if ($whitelistedURLs) { $whitelistPattern = ""; foreach ($whitelistedURLs as $whitelistedURL) { $whitelistPattern .= preg_replace('/\\\\\*/', '.*?', preg_quote($whitelistedURL, '/')) . '|'; } $whitelistPattern = '/^(?:' . wfWAFUtils::substr($whitelistPattern, 0, -1) . ')$/i'; wfWAFRule::create(wfWAF::getInstance(), 0x8000000, 'rule', 'whitelist', 0, 'User Supplied Allowlisted URL', 'allow', new wfWAFRuleComparisonGroup( new wfWAFRuleComparison(wfWAF::getInstance(), 'match', $whitelistPattern, array( 'request.uri', )) ) )->evaluate(); } // Whitelisted IPs (Wordfence config) $whitelistedIPs = wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedIPs', null, 'synced'); if ($whitelistedIPs) { if (!is_array($whitelistedIPs)) { $whitelistedIPs = explode(',', $whitelistedIPs); } foreach ($whitelistedIPs as $whitelistedIP) { $ipRange = new wfWAFUserIPRange($whitelistedIP); if ($ipRange->isIPInRange(wfWAF::getInstance()->getRequest()->getIP())) { throw new wfWAFAllowException('Wordfence allowlisted IP.'); } } } // Check plugin blocking if ($result = wfWAF::getInstance()->willPerformFinalAction(wfWAF::getInstance()->getRequest())) { if ($result === true) { $result = 'Not available'; } // Should not happen but can if the reason in the blocks table is empty wfWAF::getInstance()->getRequest()->setMetadata(array_merge(wfWAF::getInstance()->getRequest()->getMetadata(), array('finalAction' => $result))); } } public function afterRunRules() { //Blacklist if (!wfWAF::getInstance()->getStorageEngine()->getConfig('disableWAFBlacklistBlocking')) { $blockedPrefixes = wfWAF::getInstance()->getStorageEngine()->getConfig('blockedPrefixes', null, 'transient'); if ($blockedPrefixes && wfWAF::getInstance()->getStorageEngine()->getConfig('isPaid', null, 'synced')) { $blockedPrefixes = base64_decode($blockedPrefixes); if ($this->_prefixListContainsIP($blockedPrefixes, wfWAF::getInstance()->getRequest()->getIP()) !== false) { $allowedCacheJSON = wfWAF::getInstance()->getStorageEngine()->getConfig('blacklistAllowedCache', '', 'transient'); $allowedCache = @json_decode($allowedCacheJSON, true); if (!is_array($allowedCache)) { $allowedCache = array(); } $cacheTest = base64_encode(wfWAFUtils::inet_pton(wfWAF::getInstance()->getRequest()->getIP())); if (!in_array($cacheTest, $allowedCache)) { $guessSiteURL = sprintf('%s://%s/', wfWAF::getInstance()->getRequest()->getProtocol(), wfWAF::getInstance()->getRequest()->getHost()); try { $request = new wfWAFHTTP(); $response = wfWAFHTTP::get(WFWAF_API_URL_SEC . "?" . http_build_query(array( 'action' => 'is_ip_blacklisted', 'ip' => wfWAF::getInstance()->getRequest()->getIP(), 'k' => wfWAF::getInstance()->getStorageEngine()->getConfig('apiKey', null, 'synced'), 's' => wfWAF::getInstance()->getStorageEngine()->getConfig('siteURL', null, 'synced') ? wfWAF::getInstance()->getStorageEngine()->getConfig('siteURL', null, 'synced') : $guessSiteURL, 'h' => wfWAF::getInstance()->getStorageEngine()->getConfig('homeURL', null, 'synced') ? wfWAF::getInstance()->getStorageEngine()->getConfig('homeURL', null, 'synced') : $guessSiteURL, 't' => microtime(true), 'lang' => wfWAF::getInstance()->getStorageEngine()->getConfig('WPLANG', null, 'synced'), ), '', '&'), $request); if ($response instanceof wfWAFHTTPResponse && $response->getBody()) { $jsonData = wfWAFUtils::json_decode($response->getBody(), true); if (is_array($jsonData) && array_key_exists('data', $jsonData)) { if (preg_match('/^block:(\d+)$/i', $jsonData['data'], $matches)) { wfWAF::getInstance()->getStorageEngine()->blockIP((int)$matches[1] + time(), wfWAF::getInstance()->getRequest()->getIP(), wfWAFStorageInterface::IP_BLOCKS_BLACKLIST); $e = new wfWAFBlockException(); $e->setFailedRules(array('blocked')); $e->setRequest(wfWAF::getInstance()->getRequest()); throw $e; } else { //Allowed, cache until the next prefix list refresh $allowedCache[] = $cacheTest; wfWAF::getInstance()->getStorageEngine()->setConfig('blacklistAllowedCache', json_encode($allowedCache), 'transient'); } } } } catch (wfWAFHTTPTransportException $e) { error_log($e->getMessage()); } } } } } $watchedIPs = wfWAF::getInstance()->getStorageEngine()->getConfig('watchedIPs', null, 'transient'); if ($watchedIPs) { if (!is_array($watchedIPs)) { $watchedIPs = explode(',', $watchedIPs); } foreach ($watchedIPs as $watchedIP) { $ipRange = new wfWAFUserIPRange($watchedIP); if ($ipRange->isIPInRange(wfWAF::getInstance()->getRequest()->getIP())) { $this->waf->recordLogEvent(new wfWAFLogEvent()); } } } if ($reason = wfWAF::getInstance()->getRequest()->getMetadata('finalAction')) { $e = new wfWAFBlockException($reason['action']); $e->setRequest(wfWAF::getInstance()->getRequest()); throw $e; } } private function _prefixListContainsIP($prefixList, $ip) { $size = ord(wfWAFUtils::substr($prefixList, 0, 1)); $sha256 = hash('sha256', wfWAFUtils::inet_pton($ip), true); $p = wfWAFUtils::substr($sha256, 0, $size); $count = ceil((wfWAFUtils::strlen($prefixList) - 1) / $size); $low = 0; $high = $count - 1; while ($low <= $high) { $mid = (int) (($high + $low) / 2); $val = wfWAFUtils::substr($prefixList, 1 + $mid * $size, $size); $cmp = strcmp($val, $p); if ($cmp < 0) { $low = $mid + 1; } else if ($cmp > 0) { $high = $mid - 1; } else { return $mid; } } return false; } } /** * */ class wfWAFWordPress extends wfWAF { /** @var wfWAFRunException */ private $learningModeAttackException; /** * @param wfWAFBlockException $e * @param int $httpCode */ public function blockAction($e, $httpCode = 403, $redirect = false, $template = null) { $failedRules = $e->getFailedRules(); if (!is_array($failedRules)) { $failedRules = array(); } if ($this->isInLearningMode() && !$e->getRequest()->getMetadata('finalAction') && !in_array('blocked', $failedRules)) { register_shutdown_function(array( $this, 'whitelistFailedRulesIfNot404', )); $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest()); $this->setLearningModeAttackException($e); } else { if (empty($failedRules)) { $finalAction = $e->getRequest()->getMetadata('finalAction'); if (is_array($finalAction)) { $isLockedOut = isset($finalAction['lockout']) && $finalAction['lockout']; $finalAction = $finalAction['action']; if ($finalAction == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_REDIR) { $redirect = wfWAFIPBlocksController::currentController()->countryRedirURL(); } else if ($finalAction == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_BYPASS_REDIR) { $redirect = wfWAFIPBlocksController::currentController()->countryBypassRedirURL(); } else if ($finalAction == wfWAFIPBlocksController::WFWAF_BLOCK_UAREFIPRANGE) { wfWAF::getInstance()->getRequest()->setMetadata(array_merge(wfWAF::getInstance()->getRequest()->getMetadata(), array('503Reason' => 'Advanced blocking in effect.', '503Time' => 3600))); $httpCode = 503; } else if ($finalAction == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY) { wfWAF::getInstance()->getRequest()->setMetadata(array_merge(wfWAF::getInstance()->getRequest()->getMetadata(), array('503Reason' => 'Access from your area has been temporarily limited for security reasons.', '503Time' => 3600))); $httpCode = 503; } else if (is_string($finalAction) && strlen($finalAction) > 0) { wfWAF::getInstance()->getRequest()->setMetadata(array_merge(wfWAF::getInstance()->getRequest()->getMetadata(), array('503Reason' => $finalAction, '503Time' => 3600))); $httpCode = 503; if ($isLockedOut) { parent::blockAction($e, $httpCode, $redirect, '503-lockout'); //exits } } } } else if (array_search('blocked', $failedRules) !== false) { parent::blockAction($e, $httpCode, $redirect, '403-blacklist'); //exits } parent::blockAction($e, $httpCode, $redirect, $template); } } /** * @param wfWAFBlockXSSException $e * @param int $httpCode */ public function blockXSSAction($e, $httpCode = 403, $redirect = false) { if ($this->isInLearningMode() && !$e->getRequest()->getMetadata('finalAction')) { register_shutdown_function(array( $this, 'whitelistFailedRulesIfNot404', )); $this->getStorageEngine()->logAttack($e->getFailedRules(), $e->getParamKey(), $e->getParamValue(), $e->getRequest()); $this->setLearningModeAttackException($e); } else { $failedRules = $e->getFailedRules(); if (empty($failedRules)) { $finalAction = $e->getRequest()->getMetadata('finalAction'); if (is_array($finalAction)) { $finalAction = $finalAction['action']; if ($finalAction == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_REDIR) { $redirect = wfWAFIPBlocksController::currentController()->countryRedirURL(); } else if ($finalAction == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_BYPASS_REDIR) { $redirect = wfWAFIPBlocksController::currentController()->countryBypassRedirURL(); } else if ($finalAction == wfWAFIPBlocksController::WFWAF_BLOCK_UAREFIPRANGE) { wfWAF::getInstance()->getRequest()->setMetadata(array_merge(wfWAF::getInstance()->getRequest()->getMetadata(), array('503Reason' => 'Advanced blocking in effect.', '503Time' => 3600))); $httpCode = 503; } else if ($finalAction == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY) { wfWAF::getInstance()->getRequest()->setMetadata(array_merge(wfWAF::getInstance()->getRequest()->getMetadata(), array('503Reason' => 'Access from your area has been temporarily limited for security reasons.', '503Time' => 3600))); $httpCode = 503; } else if (is_string($finalAction) && strlen($finalAction) > 0) { wfWAF::getInstance()->getRequest()->setMetadata(array_merge(wfWAF::getInstance()->getRequest()->getMetadata(), array('503Reason' => $finalAction, '503Time' => 3600))); $httpCode = 503; } } } parent::blockXSSAction($e, $httpCode, $redirect); } } private function isCli() { return (php_sapi_name()==='cli') || !array_key_exists('REQUEST_METHOD', $_SERVER); } /** * */ public function runCron() { if($this->isCli()){ return; } /** * Removed sending attack data. Attack data is sent in @see wordfence::veryFirstAction */ $storage = $this->getStorageEngine(); $cron = (array) $storage->getConfig('cron', null, 'livewaf'); $run = array(); $updated = false; if (is_array($cron)) { /** @var wfWAFCronEvent $event */ $cronDeduplication = array(); foreach ($cron as $index => $event) { if (is_object($event) && $event instanceof wfWAFCronEvent) { $event->setWaf($this); if ($event->isInPast()) { $run[$index] = $event; $newEvent = $event->reschedule(); $className = get_class($newEvent); if ($newEvent instanceof wfWAFCronEvent && $newEvent !== $event && !in_array($className, $cronDeduplication)) { $cron[$index] = $newEvent; $cronDeduplication[] = $className; $updated = true; } else { unset($cron[$index]); $updated = true; } } else { $className = get_class($event); if (in_array($className, $cronDeduplication)) { unset($cron[$index]); $updated = true; } else { $cronDeduplication[] = $className; } } } else { //Remove bad/corrupt records unset($cron[$index]); $updated = true; } } } $storage->setConfig('cron', $cron, 'livewaf'); if ($updated && method_exists($storage, 'saveConfig')) { $storage->saveConfig('livewaf'); } foreach ($run as $index => $event) { $event->fire(); } } /** * */ public function whitelistFailedRulesIfNot404() { /** @var WP_Query $wp_query */ global $wp_query; if (defined('ABSPATH') && isset($wp_query) && class_exists('WP_Query') && $wp_query instanceof WP_Query && method_exists($wp_query, 'is_404') && $wp_query->is_404() && function_exists('is_admin') && !is_admin()) { return; } $this->whitelistFailedRules(); } /** * @param $ip * @return mixed */ public function isIPBlocked($ip) { return parent::isIPBlocked($ip); } /** * @param wfWAFRequest $request * @return bool|string false if it should not be blocked, otherwise true or a reason for blocking */ public function willPerformFinalAction($request) { try { $disableWAFIPBlocking = $this->getStorageEngine()->getConfig('disableWAFIPBlocking', null, 'synced'); $advancedBlockingEnabled = $this->getStorageEngine()->getConfig('advancedBlockingEnabled', null, 'synced'); } catch (Exception $e) { return false; } if ($disableWAFIPBlocking || !$advancedBlockingEnabled) { return false; } return wfWAFIPBlocksController::currentController()->shouldBlockRequest($request); } public function uninstall() { parent::uninstall(); @unlink(rtrim(WFWAF_LOG_PATH, '/') . '/.htaccess'); @unlink(rtrim(WFWAF_LOG_PATH, '/') . '/template.php'); @unlink(rtrim(WFWAF_LOG_PATH, '/') . '/GeoLite2-Country.mmdb'); self::_recursivelyRemoveWflogs(''); //Removes any remaining files and the directory itself } /** * Removes a path within wflogs, recursing as necessary. * * @param string $file * @param array $processedDirs * @return array The list of removed files/folders. */ private static function _recursivelyRemoveWflogs($file, $processedDirs = array()) { if (preg_match('~(?:^|/|\\\\)\.\.(?:/|\\\\|$)~', $file)) { return array(); } if (stripos(WFWAF_LOG_PATH, 'wflogs') === false) { //Sanity check -- if not in a wflogs folder, user will have to do removal manually return array(); } $path = rtrim(WFWAF_LOG_PATH, '/') . '/' . $file; if (is_link($path)) { if (@unlink($path)) { return array($file); } return array(); } if (is_dir($path)) { $real = realpath($file); if (in_array($real, $processedDirs)) { return array(); } $processedDirs[] = $real; $count = 0; $dir = opendir($path); if ($dir) { $contents = array(); while ($sub = readdir($dir)) { if ($sub == '.' || $sub == '..') { continue; } $contents[] = $sub; } closedir($dir); $filesRemoved = array(); foreach ($contents as $f) { $removed = self::_recursivelyRemoveWflogs($file . '/' . $f, $processedDirs); $filesRemoved = array($filesRemoved, $removed); } } if (@rmdir($path)) { $filesRemoved[] = $file; } return $filesRemoved; } if (@unlink($path)) { return array($file); } return array(); } public function fileList() { $fileList = parent::fileList(); $fileList[] = rtrim(WFWAF_LOG_PATH, '/') . '/.htaccess'; $fileList[] = rtrim(WFWAF_LOG_PATH, '/') . '/template.php'; $fileList[] = rtrim(WFWAF_LOG_PATH, '/') . '/GeoLite2-Country.mmdb'; return $fileList; } /** * @return wfWAFRunException */ public function getLearningModeAttackException() { return $this->learningModeAttackException; } /** * @param wfWAFRunException $learningModeAttackException */ public function setLearningModeAttackException($learningModeAttackException) { $this->learningModeAttackException = $learningModeAttackException; } public static function permissions() { if (defined('WFWAF_LOG_FILE_MODE')) { return WFWAF_LOG_FILE_MODE; } if (class_exists('wfWAFStorageFile') && method_exists('wfWAFStorageFile', 'permissions')) { return wfWAFStorageFile::permissions(); } static $_cachedPermissions = null; if ($_cachedPermissions === null) { if (defined('WFWAF_LOG_PATH')) { $template = rtrim(WFWAF_LOG_PATH . '/') . '/template.php'; if (file_exists($template)) { $stat = @stat($template); if ($stat !== false) { $mode = $stat[2]; $updatedMode = 0600; if (($mode & 0020) == 0020) { $updatedMode = $updatedMode | 0060; } $_cachedPermissions = $updatedMode; return $updatedMode; } } } return 0660; } return $_cachedPermissions; } public static function writeHtaccess() { @file_put_contents(rtrim(WFWAF_LOG_PATH, '/') . '/.htaccess', << Require all denied Order deny,allow Deny from all APACHE ); @chmod(rtrim(WFWAF_LOG_PATH, '/') . '/.htaccess', (wfWAFWordPress::permissions() | 0444)); } public function getGlobal($global) { if (wfWAFUtils::strpos($global, '.') === false) { return null; } list($prefix, $_global) = explode('.', $global); switch ($prefix) { case 'wordpress': if ($_global === 'core') { return $this->getStorageEngine()->getConfig('wordpressVersion', null, 'synced'); } else if ($_global === 'plugins') { return $this->getStorageEngine()->getConfig('wordpressPluginVersions', null, 'synced'); } else if ($_global === 'themes') { return $this->getStorageEngine()->getConfig('wordpressThemeVersions', null, 'synced'); } break; } return parent::getGlobal($global); } } class wfWAFWordPressStorageMySQL extends wfWAFStorageMySQL { public function getSerializedParams() { $params = parent::getSerializedParams(); $params[] = 'wordpressPluginVersions'; $params[] = 'wordpressThemeVersions'; return $params; } public function getAutoloadParams() { $params = parent::getAutoloadParams(); $params['synced'][] = 'wordpressVersion'; $params['synced'][] = 'wordpressPluginVersions'; $params['synced'][] = 'wordpressThemeVersions'; return $params; } } class wfWAFWordPressI18n implements wfWAFI18nEngine { protected $translations; /** @var wfWAFStorageInterface */ private $storageEngine; /** * @var wfMO */ private $mo; /** * @param wfWAFStorageInterface $storageEngine */ public function __construct($storageEngine) { $this->storageEngine = $storageEngine; $this->loadTranslations(); } /** * @param string $text * @return string */ public function __($text) { if (!$this->storageEngine->getConfig('wordfenceI18n', true, 'synced')) { return $text; } if ($this->mo) { $translated = $this->mo->translate($text); if ($translated) { return $translated; } } return $text; } protected function loadTranslations() { require_once dirname(__FILE__) . '/pomo/mo.php'; $currentLocale = $this->storageEngine->getConfig('WPLANG', '', 'synced'); // Find translation file for the current language. $mofile = dirname(__FILE__) . '/../languages/wordfence-' . $currentLocale . '.mo'; if (!file_exists($mofile)) { // No translation, use the default $mofile = dirname(__FILE__) . '/../languages/wordfence.mo'; } $this->mo = new wfMO(); return $this->mo->import_from_file( $mofile ); } } try { if (!defined('WFWAF_LOG_PATH')) { if (!defined('WP_CONTENT_DIR')) { //Loading before WordPress exit(); } define('WFWAF_LOG_PATH', WP_CONTENT_DIR . '/wflogs/'); } if (!is_dir(WFWAF_LOG_PATH)) { @mkdir(WFWAF_LOG_PATH, (wfWAFWordPress::permissions() | 0755)); @chmod(WFWAF_LOG_PATH, (wfWAFWordPress::permissions() | 0755)); wfWAFWordPress::writeHtaccess(); } try { if (!defined('WFWAF_STORAGE_ENGINE') && isset($_SERVER['WFWAF_STORAGE_ENGINE'])) { define('WFWAF_STORAGE_ENGINE', $_SERVER['WFWAF_STORAGE_ENGINE']); } else if (!defined('WFWAF_STORAGE_ENGINE') && (WF_IS_WP_ENGINE || WF_IS_FLYWHEEL)) { define('WFWAF_STORAGE_ENGINE', 'mysqli'); } $specifiedStorageEngine = defined('WFWAF_STORAGE_ENGINE'); $fallbackStorageEngine = false; if ($specifiedStorageEngine) { switch (WFWAF_STORAGE_ENGINE) { case 'mysqli': $wfWAFDBCredentials = array(); $sslOptions = array(); $overrideConstants = array( 'wfWAFDBCredentials' => array( 'WFWAF_DB_NAME' => 'database', 'WFWAF_DB_USER' => 'user', 'WFWAF_DB_PASSWORD' => 'pass', 'WFWAF_DB_HOST' => 'host', 'WFWAF_DB_CHARSET' => 'charset', 'WFWAF_DB_COLLATE' => 'collation', 'WFWAF_MYSQL_CLIENT_FLAGS' => 'flags', 'WFWAF_TABLE_PREFIX' => 'tablePrefix' ), 'sslOptions' => array( 'WFWAF_DB_SSL_KEY' => 'key', 'WFWAF_DB_SSL_CERTIFICATE' => 'certificate', 'WFWAF_DB_SSL_CA_CERTIFICATE' => 'ca_certificate', 'WFWAF_DB_SSL_CA_PATH' => 'ca_path', 'WFWAF_DB_SSL_CIPHER_ALGOS' => 'cipher_algos' ) ); foreach ($overrideConstants as $variable => $constants) { foreach ($constants as $constant => $key) { if (defined($constant)) { ${$variable}[$key] = constant($constant); } } } // Find the wp-config.php if (is_dir(dirname(WFWAF_LOG_PATH))) { if (file_exists(dirname(WFWAF_LOG_PATH) . '/../wp-config.php')) { wfWAFUtils::extractCredentialsWPConfig(dirname(WFWAF_LOG_PATH) . '/../wp-config.php', $wfWAFDBCredentials); } else if (file_exists(dirname(WFWAF_LOG_PATH) . '/../../wp-config.php')) { wfWAFUtils::extractCredentialsWPConfig(dirname(WFWAF_LOG_PATH) . '/../../wp-config.php', $wfWAFDBCredentials); } } else if (!empty($_SERVER['DOCUMENT_ROOT'])) { if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/wp-config.php')) { wfWAFUtils::extractCredentialsWPConfig($_SERVER['DOCUMENT_ROOT'] . '/wp-config.php', $wfWAFDBCredentials); } else if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/../wp-config.php')) { wfWAFUtils::extractCredentialsWPConfig($_SERVER['DOCUMENT_ROOT'] . '/../wp-config.php', $wfWAFDBCredentials); } } else { $wfWAFDBCredentials = false; } if (!empty($wfWAFDBCredentials)) { $wfWAFStorageEngine = new wfWAFWordPressStorageMySQL(new wfWAFStorageEngineMySQLi(), $wfWAFDBCredentials['tablePrefix'], wfShutdownRegistry::getDefaultInstance()); $wfWAFStorageEngine->getDb()->connect( $wfWAFDBCredentials['user'], $wfWAFDBCredentials['pass'], $wfWAFDBCredentials['database'], !empty($wfWAFDBCredentials['ipv6']) ? '[' . $wfWAFDBCredentials['host'] . ']' : $wfWAFDBCredentials['host'], !empty($wfWAFDBCredentials['port']) ? $wfWAFDBCredentials['port'] : null, !empty($wfWAFDBCredentials['socket']) ? $wfWAFDBCredentials['socket'] : null, array_key_exists('flags', $wfWAFDBCredentials) ? $wfWAFDBCredentials['flags'] : 0, $sslOptions ); if (array_key_exists('charset', $wfWAFDBCredentials)) { $wfWAFStorageEngine->getDb() ->setCharset($wfWAFDBCredentials['charset'], !empty($wfWAFDBCredentials['collation']) ? $wfWAFDBCredentials['collation'] : ''); } if (defined('ABSPATH')) { $tableExists = false; $optionName = 'wordfence_installed'; //Also exists in wfConfig.php if (is_multisite() && function_exists('get_network_option')) { $tableExists = get_network_option(null, $optionName, null); } else { $tableExists = get_option($optionName, null); } $wfWAFStorageEngine->installing = !$tableExists; $wfWAFStorageEngine->getDb()->installing = $wfWAFStorageEngine->installing; } } else { unset($wfWAFDBCredentials); } break; } } if (empty($wfWAFStorageEngine)) { $wfWAFStorageEngine = new wfWAFStorageFile( WFWAF_LOG_PATH . 'attack-data.php', WFWAF_LOG_PATH . 'ips.php', WFWAF_LOG_PATH . 'config.php', WFWAF_LOG_PATH . 'rules.php', WFWAF_LOG_PATH . 'wafRules.rules' ); if ($specifiedStorageEngine) $fallbackStorageEngine = true; } wfWAF::setSharedStorageEngine($wfWAFStorageEngine, $fallbackStorageEngine); wfWAF::setInstance(new wfWAFWordPress(wfWAFWordPressRequest::createFromGlobals(), wfWAF::getSharedStorageEngine())); wfWAF::getInstance()->getEventBus()->attach(new wfWAFWordPressObserver(wfWAF::getInstance())); if ($wfWAFStorageEngine instanceof wfWAFStorageFile) { $rulesFiles = array( WFWAF_LOG_PATH . 'rules.php', // WFWAF_PATH . 'rules.php', ); foreach ($rulesFiles as $rulesFile) { if (!file_exists($rulesFile) && !wfWAF::getInstance()->isReadOnly()) { @touch($rulesFile); } @chmod($rulesFile, (wfWAFWordPress::permissions() | 0444)); if (is_writable($rulesFile)) { wfWAF::getInstance()->setCompiledRulesFile($rulesFile); break; } } } else if ($wfWAFStorageEngine instanceof wfWAFStorageMySQL) { $wfWAFStorageEngine->runMigrations(); $wfWAFStorageEngine->setDefaults(); } if (!wfWAF::getInstance()->isReadOnly()) { if (wfWAF::getInstance()->getStorageEngine()->needsInitialRules()) { try { if (wfWAF::getInstance()->getStorageEngine()->getConfig('apiKey', null, 'synced') !== null && wfWAF::getInstance()->getStorageEngine()->getConfig('createInitialRulesDelay', null, 'transient') < time() ) { $event = new wfWAFCronFetchRulesEvent(time() - 60); $event->setWaf(wfWAF::getInstance()); $event->fire(); wfWAF::getInstance()->getStorageEngine()->setConfig('createInitialRulesDelay', time() + (5 * 60), 'transient'); } } catch (wfWAFBuildRulesException $e) { // Log this somewhere error_log($e->getMessage()); } catch (Exception $e) { // Suppress this error_log($e->getMessage()); } } } if (WFWAF_DEBUG && file_exists(wfWAF::getInstance()->getStorageEngine()->getRulesDSLCacheFile())) { try { wfWAF::getInstance()->updateRuleSet(file_get_contents(wfWAF::getInstance()->getStorageEngine()->getRulesDSLCacheFile()), false); } catch (wfWAFBuildRulesException $e) { $GLOBALS['wfWAFDebugBuildException'] = $e; } catch (Exception $e) { $GLOBALS['wfWAFDebugBuildException'] = $e; } } wfWAFI18n::setInstance(new wfWAFI18n(new wfWAFWordPressI18n($wfWAFStorageEngine))); try { wfWAF::getInstance()->run(); } catch (wfWAFBuildRulesException $e) { // Log this error_log($e->getMessage()); } } catch (wfWAFStorageFileConfigException $e) { // Let this request through for now error_log($e->getMessage()); } catch (wfWAFStorageEngineMySQLiException $e) { // Let this request through for now error_log($e->getMessage()); } catch (wfWAFStorageFileException $e) { // We need to choose another storage engine here. } } catch (Exception $e) { // In PHP 5, Throwable does not exist error_log("An unexpected exception occurred during WAF execution: {$e}"); $wf_waf_failure = array( 'throwable' => $e ); } catch (Throwable $t) { error_log("An unexpected exception occurred during WAF execution: {$t}"); if (class_exists('ParseError') && $t instanceof ParseError) { //Do nothing } else { $wf_waf_failure = array( 'throwable' => $t ); } } if (wfWAF::getInstance() === null) { require_once __DIR__ . '/dummy.php'; wfWAF::setInstance(new wfDummyWaf()); } define('WFWAF_RUN_COMPLETE', true); }wfWAFUserIPRange.php000064400000017327147207017560010317 0ustar00setIPString($ip_string); } public function isIPInRange($ip) { $ip_string = $this->getIPString(); if (strpos($ip_string, '/') !== false) { //CIDR range -- 127.0.0.1/24 return wfWAFUtils::subnetContainsIP($ip_string, $ip); } else if (strpos($ip_string, '[') !== false) //Bracketed range -- 127.0.0.[1-100] { // IPv4 range if (strpos($ip_string, '.') !== false && strpos($ip, '.') !== false) { // IPv4-mapped-IPv6 if (preg_match('/:ffff:([^:]+)$/i', $ip_string, $matches)) { $ip_string = $matches[1]; } if (preg_match('/:ffff:([^:]+)$/i', $ip, $matches)) { $ip = $matches[1]; } // Range check if (preg_match('/\[\d+\-\d+\]/', $ip_string)) { $IPparts = explode('.', $ip); $whiteParts = explode('.', $ip_string); $mismatch = false; if (count($whiteParts) != 4 || count($IPparts) != 4) { return false; } for ($i = 0; $i <= 3; $i++) { if (preg_match('/^\[(\d+)\-(\d+)\]$/', $whiteParts[$i], $m)) { if ($IPparts[$i] < $m[1] || $IPparts[$i] > $m[2]) { $mismatch = true; } } else if ($whiteParts[$i] != $IPparts[$i]) { $mismatch = true; } } if ($mismatch === false) { return true; // Is whitelisted because we did not get a mismatch } } else if ($ip_string == $ip) { return true; } // IPv6 range } else if (strpos($ip_string, ':') !== false && strpos($ip, ':') !== false) { $ip = strtolower(wfWAFUtils::expandIPv6Address($ip)); $ip_string = strtolower(self::expandIPv6Range($ip_string)); if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/i', $ip_string)) { $IPparts = explode(':', $ip); $whiteParts = explode(':', $ip_string); $mismatch = false; if (count($whiteParts) != 8 || count($IPparts) != 8) { return false; } for ($i = 0; $i <= 7; $i++) { if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) { $ip_group = hexdec($IPparts[$i]); $range_group_from = hexdec($m[1]); $range_group_to = hexdec($m[2]); if ($ip_group < $range_group_from || $ip_group > $range_group_to) { $mismatch = true; break; } } else if ($whiteParts[$i] != $IPparts[$i]) { $mismatch = true; break; } } if ($mismatch === false) { return true; // Is whitelisted because we did not get a mismatch } } else if ($ip_string == $ip) { return true; } } } else if (strpos($ip_string, '-') !== false) { //Linear range -- 127.0.0.1 - 127.0.1.100 list($ip1, $ip2) = explode('-', $ip_string); $ip1N = wfWAFUtils::inet_pton($ip1); $ip2N = wfWAFUtils::inet_pton($ip2); $ipN = wfWAFUtils::inet_pton($ip); return (strcmp($ip1N, $ipN) <= 0 && strcmp($ip2N, $ipN) >= 0); } else { //Treat as a literal IP $ip1 = @wfWAFUtils::inet_pton($ip_string); $ip2 = @wfWAFUtils::inet_pton($ip); if ($ip1 !== false && $ip1 == $ip2) { return true; } } return false; } private static function repeatString($string, $count) { if ($count <= 0) return ''; return str_repeat($string, $count); } /** * Expand a compressed printable range representation of an IPv6 address. * * @todo Hook up exceptions for better error handling. * @todo Allow IPv4 mapped IPv6 addresses (::ffff:192.168.1.1). * @param string $ip_range * @return string */ public static function expandIPv6Range($ip_range) { $colon_count = substr_count($ip_range, ':'); $dbl_colon_count = substr_count($ip_range, '::'); if ($dbl_colon_count > 1) { return false; } $dbl_colon_pos = strpos($ip_range, '::'); if ($dbl_colon_pos !== false) { $ip_range = str_replace('::', self::repeatString(':0000', (($dbl_colon_pos === 0 || $dbl_colon_pos === strlen($ip_range) - 2) ? 9 : 8) - $colon_count) . ':', $ip_range); $ip_range = trim($ip_range, ':'); } $colon_count = substr_count($ip_range, ':'); if ($colon_count != 7) { return false; } $groups = explode(':', $ip_range); $expanded = ''; foreach ($groups as $group) { if (preg_match('/\[([a-f0-9]{1,4})\-([a-f0-9]{1,4})\]/i', $group, $matches)) { $expanded .= sprintf('[%s-%s]', str_pad(strtolower($matches[1]), 4, '0', STR_PAD_LEFT), str_pad(strtolower($matches[2]), 4, '0', STR_PAD_LEFT)) . ':'; } else if (preg_match('/[a-f0-9]{1,4}/i', $group)) { $expanded .= str_pad(strtolower($group), 4, '0', STR_PAD_LEFT) . ':'; } else { return false; } } return trim($expanded, ':'); } /** * @return bool */ public function isValidRange() { return $this->isValidCIDRRange() || $this->isValidBracketedRange() || $this->isValidLinearRange() || filter_var($this->getIPString(), FILTER_VALIDATE_IP) !== false; } public function isValidCIDRRange() { //e.g., 192.0.2.1/24 $ip_string = $this->getIPString(); if (preg_match('/[^0-9a-f:\/\.]/i', $ip_string)) { return false; } $components = explode('/', $ip_string); if (count($components) != 2) { return false; } list($ip, $prefix) = $components; if (filter_var($ip, FILTER_VALIDATE_IP) === false) { return false; } if (!preg_match('/^\d+$/', $prefix)) { return false; } if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { if ($prefix < 0 || $prefix > 32) { return false; } } else { if ($prefix < 1 || $prefix > 128) { return false; } } return true; } public function isValidBracketedRange() { //e.g., 192.0.2.[1-10] $ip_string = $this->getIPString(); if (preg_match('/[^0-9a-f:\.\[\]\-]/i', $ip_string)) { return false; } if (strpos($ip_string, '.') !== false) { //IPv4 if (preg_match_all('/(\d+)/', $ip_string, $matches) > 0) { foreach ($matches[1] as $match) { $group = (int) $match; if ($group > 255 || $group < 0) { return false; } } } $group_regex = '([0-9]{1,3}|\[[0-9]{1,3}\-[0-9]{1,3}\])'; return preg_match('/^' . str_repeat("{$group_regex}\\.", 3) . $group_regex . '$/i', $ip_string) > 0; } //IPv6 if (strpos($ip_string, '::') !== false) { $ip_string = self::expandIPv6Range($ip_string); } if (!$ip_string) { return false; } $group_regex = '([a-f0-9]{1,4}|\[[a-f0-9]{1,4}\-[a-f0-9]{1,4}\])'; return preg_match('/^' . str_repeat("$group_regex:", 7) . $group_regex . '$/i', $ip_string) > 0; } public function isValidLinearRange() { //e.g., 192.0.2.1-192.0.2.100 $ip_string = $this->getIPString(); if (preg_match('/[^0-9a-f:\.\-]/i', $ip_string)) { return false; } list($ip1, $ip2) = explode("-", $ip_string); $ip1N = @wfWAFUtils::inet_pton($ip1); $ip2N = @wfWAFUtils::inet_pton($ip2); if ($ip1N === false || filter_var($ip1, FILTER_VALIDATE_IP) === false || $ip2N === false || filter_var($ip2, FILTER_VALIDATE_IP) === false) { return false; } return strcmp($ip1N, $ip2N) <= 0; } protected function _sanitizeIPRange($ip_string) { $ip_string = preg_replace('/\s/', '', $ip_string); //Strip whitespace $ip_string = preg_replace('/[\\x{2013}-\\x{2015}]/u', '-', $ip_string); //Non-hyphen dashes to hyphen $ip_string = strtolower($ip_string); if (preg_match('/^\d+-\d+$/', $ip_string)) { //v5 32 bit int style format list($start, $end) = explode('-', $ip_string); $start = long2ip($start); $end = long2ip($end); $ip_string = "{$start}-{$end}"; } return $ip_string; } /** * @return string|null */ public function getIPString() { return $this->ip_string; } /** * @param string|null $ip_string */ public function setIPString($ip_string) { $this->ip_string = $this->_sanitizeIPRange($ip_string); } } }wfWAFIPBlocksController.php000064400000047242147207017560011704 0ustar00register('wfWAFIPBlocksController::synchronizeConfigSettings'); } } public static function synchronizeConfigSettings() { if (!class_exists('wfConfig') || !wfConfig::tableExists() || !wfWAF::getInstance()) { // Ensure this is only called when WordPress and the plugin are fully loaded return; } static $isSynchronizing = false; if ($isSynchronizing) { return; } $isSynchronizing = true; global $wpdb; $suppressed = $wpdb->suppress_errors(!(defined('WFWAF_DEBUG') && WFWAF_DEBUG)); // Pattern Blocks $blocks = wfBlock::patternBlocks(true); $patternBlocks = array(); foreach ($blocks as $b) { $patternBlocks[] = array('id' => $b->id, 'ipRange' => $b->ipRange, 'hostnamePattern' => $b->hostname, 'uaPattern' => $b->userAgent, 'refPattern' => $b->referrer, 'expiration' => $b->expiration); } // Country Blocks $countryBlocks = array(); $countryBlockEntries = wfBlock::countryBlocks(true); $countryBlocks['blocks'] = array(); foreach ($countryBlockEntries as $b) { $reason = __('Access from your area has been temporarily limited for security reasons', 'wordfence'); $countryBlocks['blocks'][] = array( 'id' => $b->id, 'countries' => $b->countries, 'blockLogin' => $b->blockLogin, 'blockSite' => $b->blockSite, 'reason' => $reason, 'expiration' => $b->expiration, ); } $countryBlocks['action'] = wfConfig::get('cbl_action', false); $countryBlocks['loggedInBlocked'] = wfConfig::get('cbl_loggedInBlocked', false); $countryBlocks['bypassRedirURL'] = wfConfig::get('cbl_bypassRedirURL', ''); $countryBlocks['bypassRedirDest'] = wfConfig::get('cbl_bypassRedirDest', ''); $countryBlocks['bypassViewURL'] = wfConfig::get('cbl_bypassViewURL', ''); $countryBlocks['redirURL'] = wfConfig::get('cbl_redirURL', ''); $countryBlocks['cookieVal'] = wfBlock::countryBlockingBypassCookieValue(); //Other Blocks $otherBlocks = array('blockedTime' => wfConfig::get('blockedTime', 0)); $otherBlockEntries = wfBlock::ipBlocks(true); $otherBlocks['blocks'] = array(); foreach ($otherBlockEntries as $b) { $reason = $b->reason; if ($b->type == wfBlock::TYPE_IP_MANUAL || $b->type == wfBlock::TYPE_IP_AUTOMATIC_PERMANENT) { $reason = __('Manual block by administrator', 'wordfence'); } $otherBlocks['blocks'][] = array( 'id' => $b->id, 'IP' => base64_encode(wfUtils::inet_pton($b->ip)), 'reason' => $reason, 'expiration' => $b->expiration, ); } //Lockouts $lockoutEntries = wfBlock::lockouts(true); $lockoutSecs = wfConfig::get('loginSec_lockoutMins') * 60; $lockouts = array('lockedOutTime' => $lockoutSecs, 'lockouts' => array()); foreach ($lockoutEntries as $l) { $lockouts['lockouts'][] = array( 'id' => $l->id, 'IP' => base64_encode(wfUtils::inet_pton($l->ip)), 'reason' => $l->reason, 'expiration' => $l->expiration, ); } // Save it try { $patternBlocksJSON = wfWAFUtils::json_encode($patternBlocks); wfWAF::getInstance()->getStorageEngine()->setConfig('patternBlocks', $patternBlocksJSON, 'synced'); $countryBlocksJSON = wfWAFUtils::json_encode($countryBlocks); wfWAF::getInstance()->getStorageEngine()->setConfig('countryBlocks', $countryBlocksJSON, 'synced'); $otherBlocksJSON = wfWAFUtils::json_encode($otherBlocks); wfWAF::getInstance()->getStorageEngine()->setConfig('otherBlocks', $otherBlocksJSON, 'synced'); $lockoutsJSON = wfWAFUtils::json_encode($lockouts); wfWAF::getInstance()->getStorageEngine()->setConfig('lockouts', $lockoutsJSON, 'synced'); wfWAF::getInstance()->getStorageEngine()->setConfig('advancedBlockingEnabled', wfConfig::get('firewallEnabled'), 'synced'); wfWAF::getInstance()->getStorageEngine()->setConfig('disableWAFIPBlocking', wfConfig::get('disableWAFIPBlocking'), 'synced'); } catch (Exception $e) { // Do nothing } $isSynchronizing = false; $wpdb->suppress_errors($suppressed); } /** * @param wfWAFRequest $request * @return bool|string If not blocked, returns false. Otherwise a string of the reason it was blocked or true. */ public function shouldBlockRequest($request) { // Checking the user whitelist is done before reaching this call $ip = $request->getIP(); //Check the system whitelist if ($this->checkForWhitelisted($ip)) { return false; } //Let the plugin handle these $wfFunc = $request->getQueryString('_wfsf'); if ($wfFunc == 'unlockEmail' || $wfFunc == 'unlockAccess') { // Can't check validity here, let it pass through to plugin level where it can return false; } $logHuman = $request->getQueryString('wordfence_lh'); if ($logHuman !== null) { return false; } //Start block checks $ipNum = wfWAFUtils::inet_pton($ip); $hostname = null; $ua = $request->getHeaders('User-Agent'); if ($ua === null) { $ua = ''; } $referer = $request->getHeaders('Referer'); if ($referer === null) { $referer = ''; } $isPaid = false; try { $isPaid = wfWAF::getInstance()->getStorageEngine()->getConfig('isPaid', null, 'synced'); $pluginABSPATH = wfWAF::getInstance()->getStorageEngine()->getConfig('pluginABSPATH', null, 'synced'); $patternBlocksJSON = wfWAF::getInstance()->getStorageEngine()->getConfig('patternBlocks', null, 'synced'); $countryBlocksJSON = wfWAF::getInstance()->getStorageEngine()->getConfig('countryBlocks', null, 'synced'); $otherBlocksJSON = wfWAF::getInstance()->getStorageEngine()->getConfig('otherBlocks', null, 'synced'); $lockoutsJSON = wfWAF::getInstance()->getStorageEngine()->getConfig('lockouts', null, 'synced'); } catch (Exception $e) { // Do nothing } if (isset($_SERVER['SCRIPT_FILENAME']) && (strpos($_SERVER['SCRIPT_FILENAME'], $pluginABSPATH . "wp-admin/") === 0 || strpos($_SERVER['SCRIPT_FILENAME'], $pluginABSPATH . "wp-content/") === 0 || strpos($_SERVER['SCRIPT_FILENAME'], $pluginABSPATH . "wp-includes/") === 0)) { return false; //Rely on WordPress's own access control and blocking at the plugin level } // Pattern Blocks from the Advanced Blocking page (IP Range, UA, Referer) $patternBlocks = @wfWAFUtils::json_decode($patternBlocksJSON, true); if (is_array($patternBlocks)) { // Instead of a long block of if/else statements, using bitshifting to generate an expected value and a found value $ipRangeOffset = 1; $uaPatternOffset = 2; $refPatternOffset = 3; foreach ($patternBlocks as $b) { $expectedBits = 0; $foundBits = 0; if (isset($b['expiration']) && $b['expiration'] < time() && $b['expiration'] != 0) { continue; } if (!empty($b['ipRange'])) { $expectedBits |= (1 << $ipRangeOffset); $range = new wfWAFUserIPRange($b['ipRange']); if ($range->isIPInRange($ip)) { $foundBits |= (1 << $ipRangeOffset); } } if (!empty($b['hostnamePattern'])) { $expectedBits |= (1 << $ipRangeOffset); if ($hostname === null) { $hostname = wfWAFUtils::reverseLookup($ip); } if (preg_match(wfWAFUtils::patternToRegex($b['hostnamePattern']), $hostname)) { $foundBits |= (1 << $ipRangeOffset); } } if (!empty($b['uaPattern'])) { $expectedBits |= (1 << $uaPatternOffset); if (wfWAFUtils::isUABlocked($b['uaPattern'], $ua)) { $foundBits |= (1 << $uaPatternOffset); } } if (!empty($b['refPattern'])) { $expectedBits |= (1 << $refPatternOffset); if (wfWAFUtils::isRefererBlocked($b['refPattern'], $referer)) { $foundBits |= (1 << $refPatternOffset); } } if ($foundBits === $expectedBits && $expectedBits > 0) { return array('action' => self::WFWAF_BLOCK_UAREFIPRANGE, 'id' => $b['id']); } } } // End Pattern Blocks // Country Blocking if ($isPaid) { $countryBlocks = @wfWAFUtils::json_decode($countryBlocksJSON, true); if (is_array($countryBlocks) && isset($countryBlocks['blocks'])) { $blocks = $countryBlocks['blocks']; foreach ($blocks as $b) { $blockedCountries = $b['countries']; $bareRequestURI = wfWAFUtils::extractBareURI($request->getURI()); $bareBypassRedirURI = wfWAFUtils::extractBareURI($countryBlocks['bypassRedirURL']); $skipCountryBlocking = false; if ($bareBypassRedirURI && $bareRequestURI == $bareBypassRedirURI) { // Run this before country blocking because even if the user isn't blocked we need to set the bypass cookie so they can bypass future blocks. if ($countryBlocks['bypassRedirDest']) { setcookie('wfCBLBypass', $countryBlocks['cookieVal'], time() + (86400 * 365), '/', null, $this->isFullSSL(), true); return array('action' => self::WFWAF_BLOCK_COUNTRY_BYPASS_REDIR, 'id' => $b['id']); } } $bareBypassViewURI = wfWAFUtils::extractBareURI($countryBlocks['bypassViewURL']); if ($bareBypassViewURI && $bareBypassViewURI == $bareRequestURI) { setcookie('wfCBLBypass', $countryBlocks['cookieVal'], time() + (86400 * 365), '/', null, $this->isFullSSL(), true); $skipCountryBlocking = true; } $bypassCookieSet = false; $bypassCookie = $request->getCookies('wfCBLBypass'); if (isset($bypassCookie) && $bypassCookie == $countryBlocks['cookieVal']) { $bypassCookieSet = true; } if (!$skipCountryBlocking && $blockedCountries && !$bypassCookieSet) { $isAuthRequest = (strpos($bareRequestURI, '/wp-login.php') !== false); $isXMLRPC = (strpos($bareRequestURI, '/xmlrpc.php') !== false); $isUserLoggedIn = wfWAF::getInstance()->parseAuthCookie() !== false; // If everything is checked, make sure this always runs. if ($countryBlocks['loggedInBlocked'] && $b['blockLogin'] && $b['blockSite']) { if ($blocked = $this->checkForBlockedCountry($countryBlocks, $ip, $bareRequestURI)) { $blocked['id'] = $b['id']; return $blocked; } } // Block logged in users. if ($countryBlocks['loggedInBlocked'] && $isUserLoggedIn) { if ($blocked = $this->checkForBlockedCountry($countryBlocks, $ip, $bareRequestURI)) { $blocked['id'] = $b['id']; return $blocked; } } // Block the login form itself and any attempt to authenticate. if ($b['blockLogin'] && $isAuthRequest) { if ($blocked = $this->checkForBlockedCountry($countryBlocks, $ip, $bareRequestURI)) { $blocked['id'] = $b['id']; return $blocked; } } // Block requests that aren't to the login page, xmlrpc.php, or a user already logged in. if ($b['blockSite'] && !$isAuthRequest && !$isXMLRPC && !$isUserLoggedIn) { if ($blocked = $this->checkForBlockedCountry($countryBlocks, $ip, $bareRequestURI)) { $blocked['id'] = $b['id']; return $blocked; } } // XMLRPC is inaccesible when public portion of the site and auth is disabled. if ($b['blockLogin'] && $b['blockSite'] && $isXMLRPC) { if ($blocked = $this->checkForBlockedCountry($countryBlocks, $ip, $bareRequestURI)) { $blocked['id'] = $b['id']; return $blocked; } } // Any bypasses and other block possibilities will be checked at the plugin level once WordPress loads } } } } // End Country Blocking // Other Blocks $otherBlocks = @wfWAFUtils::json_decode($otherBlocksJSON, true); if (is_array($otherBlocks)) { $blocks = $otherBlocks['blocks']; $bareRequestURI = wfWAFUtils::extractBareURI($request->getURI()); $isAuthRequest = (stripos($bareRequestURI, '/wp-login.php') !== false); foreach ($blocks as $b) { if (isset($b['expiration']) && $b['expiration'] < time() && $b['expiration'] != 0) { continue; } if (base64_decode($b['IP']) != $ipNum) { continue; } if ($isAuthRequest && isset($b['wfsn']) && $b['wfsn']) { return array('action' => self::WFWAF_BLOCK_WFSN, 'id' => $b['id']); } return array('action' => (empty($b['reason']) ? '' : $b['reason']), 'id' => $b['id'], 'block' => true); } } // End Other Blocks // Lockouts $lockouts = @wfWAFUtils::json_decode($lockoutsJSON, true); if (is_array($lockouts)) { $lockouts = $lockouts['lockouts']; $isAuthRequest = (stripos($bareRequestURI, '/wp-login.php') !== false) || (stripos($bareRequestURI, '/xmlrpc.php') !== false); if ($isAuthRequest) { foreach ($lockouts as $l) { if (isset($l['expiration']) && $l['expiration'] < time()) { continue; } if (base64_decode($l['IP']) != $ipNum) { continue; } return array('action' => (empty($l['reason']) ? '' : $l['reason']), 'id' => $l['id'], 'lockout' => true); } } } // End Lockouts return false; } public function countryRedirURL($countryBlocks = null) { if (!isset($countryBlocks)) { try { $countryBlocksJSON = wfWAF::getInstance()->getStorageEngine()->getConfig('countryBlocks', null, 'synced'); } catch (Exception $e) { return false; } } $countryBlocks = @wfWAFUtils::json_decode($countryBlocksJSON, true); if (is_array($countryBlocks)) { if ($countryBlocks['action'] == 'redir') { return $countryBlocks['redirURL']; } } return false; } public function countryBypassRedirURL($countryBlocks = null) { if (!isset($countryBlocks)) { try { $countryBlocksJSON = wfWAF::getInstance()->getStorageEngine()->getConfig('countryBlocks', null, 'synced'); } catch (Exception $e) { return false; } } $countryBlocks = @wfWAFUtils::json_decode($countryBlocksJSON, true); if (is_array($countryBlocks)) { return $countryBlocks['bypassRedirDest']; } return false; } protected function checkForBlockedCountry($countryBlock, $ip, $bareRequestURI) { try { $homeURL = wfWAF::getInstance()->getStorageEngine()->getConfig('homeURL', null, 'synced'); } catch (Exception $e) { //Do nothing } $bareRequestURI = rtrim($bareRequestURI, '/\\'); if ($country = $this->ip2Country($ip)) { $blocks = $countryBlock['blocks']; foreach ($blocks as $b) { foreach ($b['countries'] as $blocked) { if (strtoupper($blocked) == strtoupper($country)) { if ($countryBlock['action'] == 'redir') { $redirURL = $countryBlock['redirURL']; $eRedirHost = wfWAFUtils::extractHostname($redirURL); $isExternalRedir = false; if ($eRedirHost && $homeURL && $eRedirHost != wfWAFUtils::extractHostname($homeURL)) { $isExternalRedir = true; } if ((!$isExternalRedir) && rtrim(wfWAFUtils::extractBareURI($redirURL), '/\\') == $bareRequestURI){ //Is this the URI we want to redirect to, then don't block it //Do nothing } else { return array('action' => self::WFWAF_BLOCK_COUNTRY_REDIR); } } else { return array('action' => self::WFWAF_BLOCK_COUNTRY); } } } } } return false; } protected function checkForWhitelisted($ip) { try { $pluginABSPATH = wfWAF::getInstance()->getStorageEngine()->getConfig('pluginABSPATH', null, 'synced'); $serverIPsJSON = wfWAF::getInstance()->getStorageEngine()->getConfig('serverIPs', null, 'synced'); $whitelistedServiceIPsJSON = wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedServiceIPs', null, 'synced'); } catch (Exception $e) { // Do nothing } $serverIPs = @wfWAFUtils::json_decode($serverIPsJSON, true); if (is_array($serverIPs)) { if ( (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == realpath($pluginABSPATH . DIRECTORY_SEPARATOR . 'wp-cron.php')) || //Safe -- plugin will do a final check to make sure the cron constant is defined (!empty($_GET['wordfence_syncAttackData'])) //Safe but plugin will do a final check to make sure it runs ) { foreach ($serverIPs as $testIP) { if (wfWAFUtils::inet_pton($ip) == wfWAFUtils::inet_pton($testIP)) { return true; } } } } $whitelistedServiceIPs = @wfWAFUtils::json_decode($whitelistedServiceIPsJSON, true); if (is_array($whitelistedServiceIPs)) { $wfIPWhitelist = $whitelistedServiceIPs; } else { $wordfenceLib = realpath(dirname(__FILE__) . '/../lib'); include($wordfenceLib . '/wfIPWhitelist.php'); /** @var array $wfIPWhitelist */ } foreach ($wfIPWhitelist as $group) { foreach ($group as $subnet) { if ($subnet instanceof wfWAFUserIPRange) { //Not currently reached if ($subnet->isIPInRange($ip)) { return true; } } elseif (wfWAFUtils::subnetContainsIP($subnet, $ip)) { return true; } } } return false; } protected function ip2Country($ip) { /** * It's possible this class is already loaded from a different installation of the plugin * by the time this is reached. See wfUtils::requireIpLocator for additional details. */ if (!class_exists('wfIpLocator')) require_once __DIR__ . '/../lib/wfIpLocator.php'; return wfIpLocator::getInstance()->getCountryCode($ip); } /** * Returns whether or not the site should be treated as if it's full-time SSL. * * @return bool */ protected function isFullSSL() { try { $is_ssl = false; //This is the same code from WP modified so we can use it here if ( isset( $_SERVER['HTTPS'] ) ) { if ( 'on' == strtolower( $_SERVER['HTTPS'] ) ) { $is_ssl = true; } if ( '1' == $_SERVER['HTTPS'] ) { $is_ssl = true; } } elseif ( isset($_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) { $is_ssl = true; } $homeURL = wfWAF::getInstance()->getStorageEngine()->getConfig('homeURL', null, 'synced'); return $is_ssl && parse_url($homeURL, PHP_URL_SCHEME) === 'https'; } catch (Exception $e) { //Do nothing } return false; } } }dummy.php000064400000005526147207017560006431 0ustar00 RewriteEngine On RewriteCond %{REQUEST_URI} \.php$ RewriteRule .* - [F,L,NC] Require all denied Order deny,allow Deny from all waf-uninstall.php000064400000015640147207175520010061 0ustar00 option-rules.php000064400000005165147207175520007736 0ustar00 option-whitelist.php000064400000017763147207175520010627 0ustar00 waf-uninstall-success.php000064400000002262147207175520011523 0ustar00

waf-install-success.php000064400000001517147207175520011162 0ustar00

firewall-status.php000064400000013355147207175520010424 0ustar00 debug.php000064400000013315147207175520006360 0ustar00id); $fields = array( 'URL' => $hit->URL, 'Timestamp' => date('r', $hit->ctime), 'IP' => wfUtils::inet_ntop($hit->IP), 'Status Code' => $hit->statusCode, 'User Agent' => $hit->UA, 'Referer' => $hit->referer, ); if (isset($hitData->fullRequest)) { $requestString = base64_decode($hitData->fullRequest); $request = wfWAFRequest::parseString($requestString); } else { $request = new wfWAFRequest(); $request->setAuth(array()); $request->setBody(array()); $request->setCookies(array()); $request->setFileNames(array()); $request->setFiles(array()); $request->setHeaders(array()); $request->setHost(''); $request->setIp(''); $request->setMethod('GET'); $request->setPath(''); $request->setProtocol('http'); $request->setQueryString(array()); $request->setTimestamp(''); $request->setUri(''); $headers = array(); $urlPieces = parse_url($hit->URL); if ($urlPieces) { if (array_key_exists('scheme', $urlPieces)) { $request->setProtocol($urlPieces['scheme']); } if (array_key_exists('host', $urlPieces)) { $request->setHost($urlPieces['host']); $headers['Host'] = $urlPieces['host']; } $uri = '/'; if (array_key_exists('path', $urlPieces)) { $request->setPath($urlPieces['path']); $uri = $urlPieces['path']; } if (array_key_exists('query', $urlPieces)) { $uri .= '?' . $urlPieces['query']; parse_str($urlPieces['query'], $query); $request->setQueryString($query); } $request->setUri($uri); } $headers['User-Agent'] = $hit->UA; $headers['Referer'] = $hit->referer; $request->setHeaders($headers); preg_match('/request\.([a-z]+)(?:\[(.*?)\](.*?))?/i', $hitData->paramKey, $matches); if ($matches) { switch ($matches[1]) { case 'body': $request->setMethod('POST'); parse_str("$matches[2]$matches[3]", $body); $request->setBody($body); break; } } } $request->setIP(wfUtils::inet_ntop($hit->IP)); $request->setTimestamp($hit->ctime); $waf = wfWAF::getInstance(); $waf->setRequest($request); $result = 'Passed'; $failedRules = array(); try { $waf->runRules(); } catch (wfWAFAllowException $e) { $result = 'Allowlisted'; } catch (wfWAFBlockException $e) { $result = 'Blocked'; $failedRules = $waf->getFailedRules(); } catch (wfWAFBlockSQLiException $e) { $result = 'Blocked For SQLi'; $failedRules = $waf->getFailedRules(); } catch (wfWAFBlockXSSException $e) { $result = 'Blocked For XSS'; $failedRules = $waf->getFailedRules(); } ?> <?php echo esc_html($title) ?>

$value): ?>
Request Details
:

HTTP Request:

fullRequest)): ?> This is a reconstruction of the request using what was flagged by the WAF. Full requests are only stored when WFWAF_DEBUG is enabled.
 '',
		"[/$paramKey]" => '',
		"[$matchKey]"  => '',
		"[/$matchKey]" => '',
	);
	$highlightParamFormat = "[$paramKey]%s[/$paramKey]";
	$highlightMatchFormat = "[$matchKey]%s[/$matchKey]";
	$requestOut = esc_html($request->highlightFailedParams($failedRules, $highlightParamFormat, $highlightMatchFormat));

	echo str_replace(array_keys($template), $template, $requestOut) ?>

Failed Rules

$categories) { foreach ($categories as $categoryKey => $failed) { foreach ($failed as $failedRule) { /** @var wfWAFRule $rule */ $rule = $failedRule['rule']; printf("", $rule->getRuleID(), $rule->getDescription()); } } } ?>
ID Category
%d%s

options-group-rate-limiting.php000064400000032103147207175520012656 0ustar00getStorageEngine(); if (!isset($collapseable)) { $collapseable = true; } ?>
  • 'firewallEnabled', 'value' => wfConfig::get('firewallEnabled') ? '1': '0', 'title' => __('Enable Rate Limiting and Advanced Blocking', 'wordfence'), 'subtitle' => __('NOTE: This checkbox enables ALL blocking/throttling functions including IP, country and advanced blocking, and the "Rate Limiting Rules" below.', 'wordfence'), 'states' => array( array('value' => '0', 'label' => __('Off', 'wordfence')), array('value' => '1', 'label' => __('On', 'wordfence')), ), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_ENABLE_ADVANCED_BLOCKING), 'noSpacer' => true, 'alignment' => 'wf-right', ))->render(); ?>
  • 'neverBlockBG', 'selectOptions' => array( array('value' => 'neverBlockVerified', 'label' => __('Verified Google crawlers will not be rate-limited', 'wordfence')), array('value' => 'neverBlockUA', 'label' => __('Anyone claiming to be Google will not be rate-limited', 'wordfence')), array('value' => 'treatAsOtherCrawlers', 'label' => __('Treat Google like any other Crawler', 'wordfence')), ), 'selectValue' => wfConfig::get('neverBlockBG'), 'title' => __('How should we treat Google\'s crawlers', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_GOOGLE_ACTION), ))->render(); ?>
  • 'DISABLED', 'label' => __('Unlimited', 'wordfence')), array('value' => 1920, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 1920)), array('value' => 960, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 960)), array('value' => 480, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 480)), array('value' => 240, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 240)), array('value' => 120, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 120)), array('value' => 60, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 60)), array('value' => 30, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 30)), array('value' => 15, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 15)), array('value' => 10, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 10)), array('value' => 5, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 5)), array('value' => 4, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 4)), array('value' => 3, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 3)), array('value' => 2, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 2)), array('value' => 1, 'label' => sprintf(/* translators: Number of HTTP requests. */__('%d per minute', 'wordfence'), 1)), ); $actionOptions = array( array('value' => 'throttle', 'label' => __('throttle it', 'wordfence')), array('value' => 'block', 'label' => __('block it', 'wordfence')), ); ?>
  • 'maxGlobalRequests_enabled', 'toggleValue' => !!wfConfig::get('maxGlobalRequests_enabled') ? 1 : 0, 'rateOptionName' => 'maxGlobalRequests', 'rateOptions' => $rateOptions, 'rateValue' => wfConfig::get('maxGlobalRequests'), 'lowValue' => 120, 'actionOptionName' => 'maxGlobalRequests_action', 'actionOptions' => $actionOptions, 'actionValue' => wfConfig::get('maxGlobalRequests_action'), 'title' => __('If anyone\'s requests exceed', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_RATE_LIMIT_ANY), ))->render(); ?>
  • 'maxRequestsCrawlers_enabled', 'toggleValue' => !!wfConfig::get('maxRequestsCrawlers_enabled') ? 1 : 0, 'rateOptionName' => 'maxRequestsCrawlers', 'rateOptions' => $rateOptions, 'rateValue' => wfConfig::get('maxRequestsCrawlers'), 'lowValue' => 120, 'actionOptionName' => 'maxRequestsCrawlers_action', 'actionOptions' => $actionOptions, 'actionValue' => wfConfig::get('maxRequestsCrawlers_action'), 'title' => __('If a crawler\'s page views exceed', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_RATE_LIMIT_CRAWLER), ))->render(); ?>
  • 'max404Crawlers_enabled', 'toggleValue' => !!wfConfig::get('max404Crawlers_enabled') ? 1 : 0, 'rateOptionName' => 'max404Crawlers', 'rateOptions' => $rateOptions, 'rateValue' => wfConfig::get('max404Crawlers'), 'lowValue' => 60, 'actionOptionName' => 'max404Crawlers_action', 'actionOptions' => $actionOptions, 'actionValue' => wfConfig::get('max404Crawlers_action'), 'title' => __('If a crawler\'s pages not found (404s) exceed', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_RATE_LIMIT_CRAWLER_404), ))->render(); ?>
  • 'maxRequestsHumans_enabled', 'toggleValue' => !!wfConfig::get('maxRequestsHumans_enabled') ? 1 : 0, 'rateOptionName' => 'maxRequestsHumans', 'rateOptions' => $rateOptions, 'rateValue' => wfConfig::get('maxRequestsHumans'), 'lowValue' => 120, 'actionOptionName' => 'maxRequestsHumans_action', 'actionOptions' => $actionOptions, 'actionValue' => wfConfig::get('maxRequestsHumans_action'), 'title' => __('If a human\'s page views exceed', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_RATE_LIMIT_HUMAN), ))->render(); ?>
  • 'max404Humans_enabled', 'toggleValue' => !!wfConfig::get('max404Humans_enabled') ? 1 : 0, 'rateOptionName' => 'max404Humans', 'rateOptions' => $rateOptions, 'rateValue' => wfConfig::get('max404Humans'), 'lowValue' => 60, 'actionOptionName' => 'max404Humans_action', 'actionOptions' => $actionOptions, 'actionValue' => wfConfig::get('max404Humans_action'), 'title' => __('If a human\'s pages not found (404s) exceed', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_RATE_LIMIT_HUMAN_404), ))->render(); ?>
  • $b, 'label' => wfUtils::makeDuration($b)); } echo wfView::create('options/option-select', array( 'selectOptionName' => 'blockedTime', 'selectOptions' => $options, 'selectValue' => wfConfig::getInt('blockedTime'), 'title' => __('How long is an IP address blocked when it breaks a rule', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_AUTOMATIC_BLOCK_DURATION), ))->render(); ?>
  • 'allowed404s', 'textValue' => wfUtils::cleanupOneEntryPerLine(wfConfig::get('allowed404s')), 'title' => __('Allowlisted 404 URLs', 'wordfence'), 'subtitle' => __('These URL patterns will be excluded from the throttling rules used to limit crawlers.', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_WHITELISTED_404), ))->render(); ?>
options-group-whitelisted.php000064400000027521147207175520012446 0ustar00getStorageEngine(); if (!isset($collapseable)) { $collapseable = true; } ?>
    isSubDirectoryInstallation()): ?>
  • click here to configure the Firewall to run correctly on this site.', 'wordfence'), esc_attr(network_admin_url('admin.php?page=WordfenceWAF&subpage=waf_options#configureAutoPrepend'))), array('a'=>array('href'=>array()))); ?>

  • render(); ?>
  • array( array( 'name' => 'ajaxWatcherDisabled_front', 'enabledValue' => 0, 'disabledValue' => 1, 'value' => wfConfig::get('ajaxWatcherDisabled_front') ? 1 : 0, 'title' => __('Front-end Website', 'wordfence'), ), array( 'name' => 'ajaxWatcherDisabled_admin', 'enabledValue' => 0, 'disabledValue' => 1, 'value' => wfConfig::get('ajaxWatcherDisabled_admin') ? 1 : 0, 'title' => __('Admin Panel', 'wordfence'), ), ), 'noSpacer' => true, 'htmlTitle' => '' . esc_html__('Monitor background requests from an administrator\'s web browser for false positives', 'wordfence') . '', 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_MONITOR_AJAX), ))->render(); ?>
waf-install-manual.php000064400000002007147207175520010762 0ustar00

php.ini to finish installation:', 'wordfence'), array('code'=>array())); ?>

auto_prepend_file = ''

in our documentation (opens in new tab).', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_FIREWALL_WAF_INSTALL_MANUALLY)), array('a'=>array('href'=>array(), 'target'=>array(), 'rel'=>array()), 'span'=>array('class'=>array()))); ?>

waf-modal-wrapper.php000064400000003371147207175520010620 0ustar00
options-group-basic-firewall.php000064400000066002147207175520013002 0ustar00getStorageEngine(); if (!isset($collapseable)) { $collapseable = true; } ?>
  • isSubDirectoryInstallation()): ?>

    click here to configure the Firewall to run correctly on this site.', 'wordfence'), esc_attr(network_admin_url('admin.php?page=WordfenceWAF&subpage=waf_options#configureAutoPrepend'))), array('a'=>array('href'=>array()))); ?>

    firewallMode(); ?>

  • protectionMode() == wfFirewall::PROTECTION_MODE_EXTENDED && !$firewall->isSubDirectoryInstallation()): ?>

    remove them manually (opens in new tab).', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_FIREWALL_WAF_REMOVE_MANUALLY)), array('a'=>array('href'=>array(), 'target'=>array(), 'rel'=>array()), 'span'=>array('class'=>array()))); ?>

    isSubDirectoryInstallation()): ?>

  • ruleMode() == wfFirewall::RULE_MODE_COMMUNITY): ?>

       ()

    isSubDirectoryInstallation()): ?>

    click here to configure the Firewall to run correctly on this site.', 'wordfence'), esc_attr(network_admin_url('admin.php?page=WordfenceWAF&subpage=waf_options#configureAutoPrepend'))), array('a'=>array('href'=>array()))); ?>

      '1', 'label' => __('Disabled', 'wordfence')), array('value' => '0', 'label' => __('Enabled', 'wordfence')), ); foreach ($states as $s): $disableBlacklist = false; try { $disableBlacklist = !!$config->getConfig('disableWAFBlacklistBlocking'); } catch (Exception $e) { } ?> data-option-value="" role="radio" aria-checked="" tabindex="0">
protectionMode() == wfFirewall::PROTECTION_MODE_BASIC || ($firewall->protectionMode() == wfFirewall::PROTECTION_MODE_EXTENDED && $firewall->isSubDirectoryInstallation())) { echo wfView::create('waf/waf-install', array( ))->render(); } else { echo wfView::create('waf/waf-uninstall', array( ))->render(); } ?>waf-install.php000064400000021600147207175520007507 0ustar00 status-tooltip-learning-mode.php000064400000001320147207175520013015 0ustar00 options-group-brute-force.php000064400000031153147207175520012332 0ustar00getStorageEngine(); if (!isset($collapseable)) { $collapseable = true; } ?>
  • 'loginSecurityEnabled', 'value' => wfConfig::get('loginSecurityEnabled') ? '1': '0', 'titleHTML' => '' . esc_html__('Enable brute force protection', 'wordfence') . '', 'subtitle' => __('This option enables all "Brute Force Protection" options, including strong password enforcement and invalid login throttling. You can modify individual options below.', 'wordfence'), 'states' => array( array('value' => '0', 'label' => __('Off', 'wordfence')), array('value' => '1', 'label' => __('On', 'wordfence')), ), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_ENABLE_LOGIN_SECURITY), 'noSpacer' => true, 'alignment' => 'wf-right', ))->render(); ?>
  • $b, 'label' => $b); } echo wfView::create('options/option-select', array( 'selectOptionName' => 'loginSec_maxFailures', 'selectOptions' => $options, 'selectValue' => wfConfig::get('loginSec_maxFailures'), 'title' => __('Lock out after how many login failures', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_LOCK_OUT_FAILURE_COUNT), ))->render(); ?>
  • $b, 'label' => $b); } echo wfView::create('options/option-select', array( 'selectOptionName' => 'loginSec_maxForgotPasswd', 'selectOptions' => $options, 'selectValue' => wfConfig::get('loginSec_maxForgotPasswd'), 'title' => __('Lock out after how many forgot password attempts', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_LOCK_OUT_FORGOT_PASSWORD_COUNT), ))->render(); ?>
  • $b, 'label' => wfUtils::makeDuration($b * 60)); } echo wfView::create('options/option-select', array( 'selectOptionName' => 'loginSec_countFailMins', 'selectOptions' => $options, 'selectValue' => wfConfig::getInt('loginSec_countFailMins'), 'title' => __('Count failures over what time period', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_COUNT_TIME_PERIOD), ))->render(); ?>
  • $b, 'label' => wfUtils::makeDuration($b * 60)); } echo wfView::create('options/option-select', array( 'selectOptionName' => 'loginSec_lockoutMins', 'selectOptions' => $options, 'selectValue' => wfConfig::getInt('loginSec_lockoutMins'), 'title' => __('Amount of time a user is locked out', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_LOCKOUT_DURATION), ))->render(); ?>
  • 'loginSec_lockInvalidUsers', 'enabledValue' => 1, 'disabledValue' => 0, 'value' => wfConfig::get('loginSec_lockInvalidUsers') ? 1 : 0, 'title' => __('Immediately lock out invalid usernames', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_IMMEDIATELY_LOCK_OUT_INVALID_USERS), ))->render(); ?>
  • 'loginSec_userBlacklist', 'tokenValue' => $users, 'title' => __('Immediately block the IP of users who try to sign in as these usernames', 'wordfence'), 'subtitle' => __('Hit enter to add a username', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_IMMEDIATELY_BLOCK_USERS), ))->render(); ?>
  • 'loginSec_breachPasswds_enabled', 'enabledToggleValue' => 1, 'disabledToggleValue' => 0, 'toggleValue' => !!wfConfig::get('loginSec_breachPasswds_enabled') ? 1 : 0, 'selectOptionName' => 'loginSec_breachPasswds', 'selectOptions' => array(array('value' => 'admins', 'label' => __('For admins only', 'wordfence')), array('value' => 'pubs', 'label' => __('For all users with "publish posts" capability', 'wordfence'))), 'selectValue' => wfConfig::get('loginSec_breachPasswds'), 'title' => __('Prevent the use of passwords leaked in data breaches', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_PREVENT_BREACH_PASSWORDS), ))->render(); ?>
  • '' . esc_html__('Additional Options', 'wordfence') . '', 'noSpacer' => true, ))->render(); ?>
  • 'loginSec_strongPasswds_enabled', 'enabledToggleValue' => 1, 'disabledToggleValue' => 0, 'toggleValue' => !!wfConfig::get('loginSec_strongPasswds_enabled') ? 1 : 0, 'selectOptionName' => 'loginSec_strongPasswds', 'selectOptions' => array(array('value' => 'pubs', 'label' => __('Force admins and publishers to use strong passwords (recommended)', 'wordfence')), array('value' => 'all', 'label' => __('Force all members to use strong passwords', 'wordfence'))), 'selectValue' => wfConfig::get('loginSec_strongPasswds'), 'title' => __('Enforce strong passwords', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_ENFORCE_STRONG_PASSWORDS), ))->render(); ?>
  • 'loginSec_maskLoginErrors', 'enabledValue' => 1, 'disabledValue' => 0, 'value' => wfConfig::get('loginSec_maskLoginErrors') ? 1 : 0, 'title' => __('Don\'t let WordPress reveal valid users in login errors', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_MASK_LOGIN_ERRORS), ))->render(); ?>
  • 'loginSec_blockAdminReg', 'enabledValue' => 1, 'disabledValue' => 0, 'value' => wfConfig::get('loginSec_blockAdminReg') ? 1 : 0, 'title' => __('Prevent users registering \'admin\' username if it doesn\'t exist', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_PREVENT_ADMIN_REGISTRATION), ))->render(); ?>
  • 'loginSec_disableAuthorScan', 'enabledValue' => 1, 'disabledValue' => 0, 'value' => wfConfig::get('loginSec_disableAuthorScan') ? 1 : 0, 'title' => __('Prevent discovery of usernames through \'/?author=N\' scans, the oEmbed API, the WordPress REST API, and WordPress XML Sitemaps', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_PREVENT_AUTHOR_SCAN), ))->render(); ?>
  • 'loginSec_disableApplicationPasswords', 'enabledValue' => 1, 'disabledValue' => 0, 'value' => wfConfig::get('loginSec_disableApplicationPasswords') ? 1 : 0, 'title' => __('Disable WordPress application passwords', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_DISABLE_APPLICATION_PASSWORDS), ))->render(); ?>
  • 'other_blockBadPOST', 'enabledValue' => 1, 'disabledValue' => 0, 'value' => wfConfig::get('other_blockBadPOST') ? 1 : 0, 'title' => __('Block IPs who send POST requests with blank User-Agent and Referer', 'wordfence'), 'subtitleHTML' => esc_html__('If you use external services that may send POST requests without these headers, do not use this option, as they will be blocked.', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_BLOCK_BAD_POST), ))->render(); ?>
  • 'blockCustomText', 'textValue' => wfConfig::get('blockCustomText'), 'title' => __('Custom text shown on block pages', 'wordfence'), 'alignTitle' => 'top', 'subtitleHTML' => esc_html__('HTML tags will be stripped prior to output and line breaks will be converted into the appropriate tags.', 'wordfence'), 'subtitlePosition' => 'value', 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_CUSTOM_BLOCK_TEXT), ))->render(); ?>
  • 'other_pwStrengthOnUpdate', 'enabledValue' => 1, 'disabledValue' => 0, 'value' => wfConfig::get('other_pwStrengthOnUpdate') ? 1 : 0, 'title' => __('Check password strength on profile update', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_CHECK_PASSWORD), ))->render(); ?>
  • 'other_WFNet', 'enabledValue' => 1, 'disabledValue' => 0, 'value' => wfConfig::get('other_WFNet') ? 1 : 0, 'title' => __('Participate in the Real-Time Wordfence Security Network', 'wordfence'), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_PARTICIPATE_WFSN), ))->render(); ?>
options-group-advanced-firewall.php000064400000024130147207175520013462 0ustar00getStorageEngine(); if (!isset($collapseable)) { $collapseable = true; } ?>
  • 'disableWAFIPBlocking', 'enabledValue' => 1, 'disabledValue' => 0, 'value' => wfConfig::get('disableWAFIPBlocking') ? 1 : 0, 'title' => __('Delay IP and Country blocking until after WordPress and plugins have loaded (only process firewall rules early)', 'wordfence'), 'subtitle' => ($firewall->isSubDirectoryInstallation() ? __('You are currently running the WAF from another WordPress installation. This option can be changed once you configure the firewall to run correctly on this site.', 'wordfence') : ''), 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_DELAY_BLOCKING), 'disabled' => $firewall->isSubDirectoryInstallation(), ))->render(); ?>
  • 'whitelisted', 'textValue' => wfUtils::cleanupOneEntryPerLine(wfConfig::get('whitelisted')), 'title' => __('Allowlisted IP addresses that bypass all rules', 'wordfence'), 'alignTitle' => 'top', 'subtitleHTML' => wp_kses(__('Allowlisted IPs must be separated by commas or placed on separate lines. You can specify ranges using the following formats: 127.0.0.1/24, 127.0.0.[1-100], or 127.0.0.1-127.0.1.100
    Wordfence automatically allowlists private networks (opens in new tab) because these are not routable on the public Internet.', 'wordfence'), array('br'=>array(), 'a'=>array('href'=>array(), 'target'=>array(), 'rel'=>array()), 'span'=>array('class'=>array()))), 'subtitlePosition' => 'value', 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_WHITELISTED_IPS), ))->render(); ?>
  • $preset) { if (!isset($preset['n'])) { continue; } //Not named, omitted from configurable list if ((isset($preset['h']) && $preset['h']) || (isset($preset['f']) && $preset['f'])) { continue; } //Flagged as hidden or always enabled, omitted from configurable list $names[$tag] = $preset['n']; if (!isset($whitelistedServices[$tag]) && isset($preset['d']) && $preset['d']) { $whitelistedServices[$tag] = 1; } } $options = array(); foreach ($names as $tag => $name) { $options[] = array( 'name' => 'whitelistedServices.' . preg_replace('/[^a-z0-9]/i', '', $tag), 'enabledValue' => 1, 'disabledValue' => 0, 'value' => (isset($whitelistedServices[$tag]) && $whitelistedServices[$tag]) ? 1 : 0, 'title' => $name, ); } echo wfView::create('options/option-toggled-multiple', array( 'options' => $options, 'title' => __('Allowlisted services', 'wordfence'), 'id' => 'wf-option-whitelistedServices', 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_WHITELISTED_SERVICES), ))->render(); ?>
  • 'bannedURLs', 'textValue' => wfUtils::cleanupOneEntryPerLine(wfConfig::get('bannedURLs')), 'title' => __('Immediately block IPs that access these URLs', 'wordfence'), 'alignTitle' => 'top', 'subtitle' => __('Separate multiple URLs with commas or place them on separate lines. Asterisks are wildcards, but use with care. If you see an attacker repeatedly probing your site for a known vulnerability you can use this to immediately block them. All URLs must start with a "/" without quotes and must be relative. e.g. /badURLone/, /bannedPage.html, /dont-access/this/URL/, /starts/with-*', 'wordfence'), 'subtitlePosition' => 'value', 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_IMMEDIATELY_BLOCK_URLS), ))->render(); ?>
  • 'wafAlertWhitelist', 'textValue' => wfUtils::cleanupOneEntryPerLine(wfConfig::get('wafAlertWhitelist')), 'title' => __('Ignored IP addresses for Wordfence Web Application Firewall alerting', 'wordfence'), 'alignTitle' => 'top', 'subtitle' => __('Ignored IPs must be separated by commas or placed on separate lines. These addresses will be ignored from any alerts about increased attacks and can be used to ignore things like standalone website security scanners.', 'wordfence'), 'subtitlePosition' => 'value', 'helpLink' => wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_IGNORED_ALERT_IPS), ))->render(); ?>
  • $firewall, ))->render(); ?>
option-rate-limit.php000064400000010171147207175520010644 0ustar00 , 'label' => ), ...) * @var string $rateValue The current value of $rateOptionName. * @var int $lowValue The value below which the false positive warning should begin showing * @var string $actionOptionName The option name for the rate portion. * @var array $actionOptions An array of the possible values for $actionOptionName. The array is of the format array(array('value' => , 'label' => ), ...) * @var string $actionValue The current value of $actionOptionName. * @var string $title The title shown for the option. * @var string $helpLink If defined, the link to the corresponding external help page. * @var bool $premium If defined, the option will be tagged as premium only and not allow its value to change for free users. */ $rateID = 'wf-option-' . preg_replace('/[^a-z0-9]/i', '-', $rateOptionName); $actionID = 'wf-option-' . preg_replace('/[^a-z0-9]/i', '-', $actionOptionName); ?>