WP fail2ban - Version 4.2.1

Version Description

  • Completed support for WP_FAIL2BAN_COMMENT_EXTRA_LOG.
  • Add support for 3rd-party plugins; see Developers.
    • Add-on for Contact Form 7 (experimental).
    • Add-on for Gravity Forms (experimental).
  • Change logging for known-user with incorrect password; previously logged as unknown user and matched by hard filters (due to limitations in older versions of WordPress), now logged as known user and matched by soft.
  • Bugfix for email-as-username - now logged correctly and matched by soft, not hard, filters.
  • Bugfix for regression in code to prevent Free/Premium conflict.
Download this release

Release Info

Developer invisnet
Plugin Icon 128x128 WP fail2ban
Version 4.2.1
Comparing to
See all releases

Code changes from version 4.1.0 to 4.2.1

admin/admin.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Admin
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.0.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ require dirname( __FILE__ ) . '/config.php';
15
+ require dirname( __FILE__ ) . '/lib/about.php';
16
+ /**
17
+ * Register admin menus
18
+ *
19
+ * @since 4.0.0
20
+ */
21
+ function admin_menu()
22
+ {
23
+ global $submenu ;
24
+ add_menu_page(
25
+ 'WP fail2ban',
26
+ 'WP fail2ban',
27
+ 'manage_options',
28
+ 'wp-fail2ban',
29
+ __NAMESPACE__ . '\\about',
30
+ 'dashicons-analytics'
31
+ );
32
+ add_submenu_page(
33
+ 'wp-fail2ban',
34
+ 'Settings',
35
+ 'Settings',
36
+ 'manage_options',
37
+ 'wp-fail2ban-settings',
38
+ __NAMESPACE__ . '\\settings'
39
+ );
40
+ $submenu['wp-fail2ban'][0][0] = __( 'Welcome' );
41
+ }
42
+
43
+ add_action( 'admin_menu', __NAMESPACE__ . '\\admin_menu' );
44
+ /**
45
+ * Add Settings link on Plugins page
46
+ *
47
+ * @since 4.2.0
48
+ *
49
+ * @param array $links
50
+ * @param string $file
51
+ */
52
+ function plugin_action_links( $links, $file )
53
+ {
54
+ if ( preg_match( "|{$file}\$|", WP_FAIL2BAN_FILE ) ) {
55
+ // Add Settings at the start
56
+ array_unshift( $links, '<a href="' . admin_url( 'options-general.php' ) . '?page=wp-fail2ban-settings&tab=welcome">Settings</a>' );
57
+ }
58
+ return $links;
59
+ }
60
+
61
+ add_filter(
62
+ 'plugin_action_links',
63
+ __NAMESPACE__ . '\\plugin_action_links',
64
+ 10,
65
+ 2
66
+ );
admin/config.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Config
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.0.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ require_once 'lib/tab.php';
15
+ foreach ( glob( dirname( __FILE__ ) . '/config/*.php' ) as $filename ) {
16
+ require_once $filename;
17
+ }
18
+ /**
19
+ * Render Settings
20
+ *
21
+ * @since 4.0.0
22
+ */
23
+ function settings()
24
+ {
25
+ $tabs = [
26
+ 'logging',
27
+ 'syslog',
28
+ 'block',
29
+ 'remote-ips',
30
+ 'plugins'
31
+ ];
32
+ $title = 'WP fail2ban';
33
+ ?>
34
+ <div class="wrap">
35
+ <h1><?php
36
+ echo $title ;
37
+ ?></h1>
38
+ <hr class="wp-header-end">
39
+
40
+ <h2 class="nav-tab-wrapper wp-clearfix">
41
+ <?php
42
+ $active_tab = Tab::getActiveTab( 'logging' );
43
+ foreach ( $tabs as $slug ) {
44
+ $class = 'nav-tab';
45
+ if ( $active_tab->getSlug() == $slug ) {
46
+ $class .= ' nav-tab-active';
47
+ }
48
+ printf(
49
+ '<a class="%s" href="?page=wp-fail2ban-settings&tab=%s">%s</a>',
50
+ $class,
51
+ $slug,
52
+ Tab::getTabName( $slug )
53
+ );
54
+ }
55
+ ?>
56
+ </h2>
57
+
58
+ <form action="options.php?tab=<?php
59
+ echo $active_tab->getSlug() ;
60
+ ?>" method="post">
61
+ <?php
62
+ settings_fields( 'wp-fail2ban' );
63
+ $active_tab->render();
64
+ ?>
65
+ </form>
66
+ </div>
67
+ <?php
68
+ }
admin/config/block.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Settings - Block
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.0.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ /**
15
+ * Tab: Block
16
+ *
17
+ * @since 4.0.0
18
+ */
19
+ class TabBlock extends Tab
20
+ {
21
+ /**
22
+ * {@inheritDoc}
23
+ *
24
+ * @since 4.0.0
25
+ */
26
+ public function __construct()
27
+ {
28
+ add_action( 'admin_init', [ $this, 'admin_init' ] );
29
+ parent::__construct( 'block', 'Users' );
30
+ }
31
+
32
+ /**
33
+ * {@inheritDoc}
34
+ *
35
+ * @since 4.0.0
36
+ */
37
+ public function admin_init()
38
+ {
39
+ // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing
40
+ add_settings_section(
41
+ 'wp-fail2ban-block',
42
+ __( 'Block' ),
43
+ [ $this, 'section' ],
44
+ 'wp-fail2ban-block'
45
+ );
46
+ add_settings_field(
47
+ 'block-user-enumeration',
48
+ parent::doc_link( 'WP_FAIL2BAN_BLOCK_USER_ENUMERATION', __( 'User Enumeration' ) ),
49
+ [ $this, 'userEnumeration' ],
50
+ 'wp-fail2ban-block',
51
+ 'wp-fail2ban-block'
52
+ );
53
+ add_settings_field(
54
+ 'block-users',
55
+ parent::doc_link( 'WP_FAIL2BAN_BLOCKED_USERS', __( 'Usernames' ) ),
56
+ [ $this, 'usernames' ],
57
+ 'wp-fail2ban-block',
58
+ 'wp-fail2ban-block'
59
+ );
60
+ // phpcs:enable
61
+ }
62
+
63
+ /**
64
+ * {@inheritDoc}
65
+ *
66
+ * @since 4.0.0
67
+ *
68
+ * @param array $settings
69
+ * @param array $input
70
+ */
71
+ public function sanitize( array $settings, array $input = null )
72
+ {
73
+ return $settings;
74
+ }
75
+
76
+ /**
77
+ * {@inheritDoc}
78
+ *
79
+ * @since 4.0.0
80
+ */
81
+ public function section()
82
+ {
83
+ echo '' ;
84
+ }
85
+
86
+ /**
87
+ * User Enumeration
88
+ *
89
+ * @since 4.0.0
90
+ */
91
+ public function userEnumeration()
92
+ {
93
+ printf( '<input type="checkbox" disabled="disabled" %s>', checked( WP_FAIL2BAN_BLOCK_USER_ENUMERATION, true, false ) );
94
+ }
95
+
96
+ /**
97
+ * Blocked usernames
98
+ *
99
+ * @since 4.0.0
100
+ */
101
+ public function usernames()
102
+ {
103
+
104
+ if ( defined( 'WP_FAIL2BAN_BLOCKED_USERS' ) ) {
105
+
106
+ if ( is_array( WP_FAIL2BAN_BLOCKED_USERS ) ) {
107
+ $value = join( ', ', WP_FAIL2BAN_BLOCKED_USERS );
108
+ } else {
109
+ $value = WP_FAIL2BAN_BLOCKED_USERS;
110
+ }
111
+
112
+ } else {
113
+ $value = '';
114
+ }
115
+
116
+ printf( '<input class="regular-text" type="text" disabled="disabled" value="%s">', esc_attr( $value ) );
117
+ }
118
+
119
+ }
120
+ new TabBlock();
admin/config/logging.php ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Settings - Logging
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.0.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ /**
15
+ * Tab: Logging
16
+ *
17
+ * @since 4.0.0
18
+ */
19
+ class TabLogging extends Tab
20
+ {
21
+ /**
22
+ * {@inheritDoc}
23
+ */
24
+ public function __construct()
25
+ {
26
+ add_action( 'admin_init', [ $this, 'admin_init' ], 100 );
27
+ parent::__construct( 'logging', 'Logging' );
28
+ }
29
+
30
+ /**
31
+ * {@inheritDoc}
32
+ *
33
+ * @since 4.0.0
34
+ */
35
+ public function admin_init()
36
+ {
37
+ // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing
38
+ add_settings_section(
39
+ 'wp-fail2ban-logging',
40
+ __( 'What & Where' ),
41
+ [ $this, 'sectionWhatWhere' ],
42
+ 'wp-fail2ban-logging'
43
+ );
44
+ add_settings_field(
45
+ 'logging-log-authentication',
46
+ parent::doc_link( 'WP_FAIL2BAN_AUTH_LOG', __( 'Authentication' ) ),
47
+ [ $this, 'authentication' ],
48
+ 'wp-fail2ban-logging',
49
+ 'wp-fail2ban-logging'
50
+ );
51
+ add_settings_field(
52
+ 'logging-log-comments',
53
+ parent::doc_link( 'WP_FAIL2BAN_LOG_COMMENTS', __( 'Comments' ) ),
54
+ [ $this, 'comments' ],
55
+ 'wp-fail2ban-logging',
56
+ 'wp-fail2ban-logging'
57
+ );
58
+ add_settings_field(
59
+ 'logging-log-spam',
60
+ parent::doc_link( 'WP_FAIL2BAN_LOG_SPAM', __( 'Spam' ) ),
61
+ [ $this, 'spam' ],
62
+ 'wp-fail2ban-logging',
63
+ 'wp-fail2ban-logging'
64
+ );
65
+ add_settings_field(
66
+ 'logging-log-password-request',
67
+ parent::doc_link( 'WP_FAIL2BAN_LOG_PASSWORD_REQUEST', __( 'Password Requests' ) ),
68
+ [ $this, 'passwordRequest' ],
69
+ 'wp-fail2ban-logging',
70
+ 'wp-fail2ban-logging'
71
+ );
72
+ add_settings_field(
73
+ 'logging-log-pingbacks',
74
+ parent::doc_link( 'WP_FAIL2BAN_LOG_PINGBACKS', __( 'Pingbacks' ) ),
75
+ [ $this, 'pingbacks' ],
76
+ 'wp-fail2ban-logging',
77
+ 'wp-fail2ban-logging'
78
+ );
79
+ // phpcs:enable
80
+ }
81
+
82
+ /**
83
+ * {@inheritDoc}
84
+ *
85
+ * @since 4.0.0
86
+ */
87
+ public function render()
88
+ {
89
+ parent::render();
90
+ }
91
+
92
+ /**
93
+ * {@inheritDoc}
94
+ *
95
+ * @since 4.0.0
96
+ *
97
+ * @param array $settings {@inheritDoc}
98
+ * @param array $input {@inheritDoc}
99
+ *
100
+ * @return array {@inheritDoc}
101
+ */
102
+ public function sanitize( array $settings, array $input = null )
103
+ {
104
+ return $settings;
105
+ }
106
+
107
+ /**
108
+ * Section summary.
109
+ *
110
+ * @since 4.0.0
111
+ */
112
+ public function sectionWhatWhere()
113
+ {
114
+ echo '' ;
115
+ }
116
+
117
+ /**
118
+ * Authentication.
119
+ *
120
+ * @since 4.0.0
121
+ */
122
+ public function authentication()
123
+ {
124
+ printf( '<label>%s: %s</label>', __( 'Use facility' ), $this->getLogFacilities( 'WP_FAIL2BAN_AUTH_LOG', true ) );
125
+ }
126
+
127
+ /**
128
+ * Comments.
129
+ *
130
+ * @since 4.0.0
131
+ */
132
+ public function comments()
133
+ {
134
+ add_filter(
135
+ 'wp_fail2ban_log_WP_FAIL2BAN_LOG_COMMENTS',
136
+ [ $this, 'commentsExtra' ],
137
+ 10,
138
+ 3
139
+ );
140
+ $this->log(
141
+ 'WP_FAIL2BAN_LOG_COMMENTS',
142
+ 'WP_FAIL2BAN_COMMENT_LOG',
143
+ '',
144
+ [ 'comments-extra', 'logging-comments-extra-facility' ]
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Comments extra helper - checked.
150
+ *
151
+ * @since 4.0.0
152
+ *
153
+ * @param int $value Value to check
154
+ */
155
+ protected function commentExtraChecked( $value )
156
+ {
157
+ if ( !defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
158
+ return '';
159
+ }
160
+ return checked( $value & WP_FAIL2BAN_LOG_COMMENTS_EXTRA, $value, false );
161
+ }
162
+
163
+ /**
164
+ * Comments extra helper - disabled.
165
+ *
166
+ * @since 4.0.0
167
+ */
168
+ protected function commentExtraDisabled()
169
+ {
170
+ return 'disabled="disabled';
171
+ }
172
+
173
+ /**
174
+ * Comments extra.
175
+ *
176
+ * @since 4.0.0
177
+ *
178
+ * @param string $html HTML prefixed to output
179
+ * @param string $define_name Not used
180
+ * @param string $define_log Not used
181
+ *
182
+ * @return string
183
+ */
184
+ public function commentsExtra( $html, $define_name, $define_log )
185
+ {
186
+ $fmt = <<<___HTML___
187
+ <table>
188
+ <tr>
189
+ <th>%s</th>
190
+ <td>
191
+ <fieldset id="comments-extra" disabled="disabled">
192
+ <label><input type="checkbox" %s> %s</label><br>
193
+ <label><input type="checkbox" %s> %s</label><br>
194
+ <label><input type="checkbox" %s> %s</label><br>
195
+ <label><input type="checkbox" %s> %s</label><br>
196
+ <label><input type="checkbox" %s> %s</label>
197
+ </fieldset>
198
+ </td>
199
+ </tr>
200
+ <tr>
201
+ <th>%s</th>
202
+ <td>%s</td>
203
+ </tr>
204
+ </table>
205
+ ___HTML___;
206
+ return $html . sprintf(
207
+ $fmt,
208
+ parent::doc_link( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA', __( 'Also log:' ) ),
209
+ $this->commentExtraChecked( WPF2B_EVENT_COMMENT_NOT_FOUND ),
210
+ __( 'Post not found' ),
211
+ $this->commentExtraChecked( WPF2B_EVENT_COMMENT_CLOSED ),
212
+ __( 'Comments closed' ),
213
+ $this->commentExtraChecked( WPF2B_EVENT_COMMENT_TRASH ),
214
+ __( 'Trash post' ),
215
+ $this->commentExtraChecked( WPF2B_EVENT_COMMENT_DRAFT ),
216
+ __( 'Draft post' ),
217
+ $this->commentExtraChecked( WPF2B_EVENT_COMMENT_PASSWORD ),
218
+ __( 'Password-protected post' ),
219
+ parent::doc_link( 'WP_FAIL2BAN_COMMENTS_EXTRA_LOG', __( 'Use facility:' ) ),
220
+ $this->getLogFacilities( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG', false )
221
+ );
222
+ }
223
+
224
+ /**
225
+ * Password request
226
+ *
227
+ * @since 4.0.0
228
+ */
229
+ public function passwordRequest()
230
+ {
231
+ $this->log( 'WP_FAIL2BAN_LOG_PASSWORD_REQUEST', 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG' );
232
+ }
233
+
234
+ /**
235
+ * Pingbacks
236
+ *
237
+ * @since 4.0.0
238
+ */
239
+ public function pingbacks()
240
+ {
241
+ $this->log( 'WP_FAIL2BAN_LOG_PINGBACKS', 'WP_FAIL2BAN_PINGBACK_LOG' );
242
+ }
243
+
244
+ /**
245
+ * Spam
246
+ *
247
+ * @since 4.0.0
248
+ */
249
+ public function spam()
250
+ {
251
+ $this->log( 'WP_FAIL2BAN_LOG_SPAM', 'WP_FAIL2BAN_SPAM_LOG' );
252
+ }
253
+
254
+ }
255
+ new TabLogging();
admin/config/plugins.php ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Settings - Plugins
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.2.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ /**
15
+ * Tab: Plugins
16
+ *
17
+ * @since 4.2.0
18
+ */
19
+ class TabPlugins extends Tab
20
+ {
21
+ /**
22
+ * {@inheritDoc}
23
+ */
24
+ public function __construct()
25
+ {
26
+ add_action( 'admin_init', [ $this, 'admin_init' ], 100 );
27
+ parent::__construct( 'plugins', 'Plugins' );
28
+ }
29
+
30
+ /**
31
+ * {@inheritDoc}
32
+ *
33
+ * @since 4.0.0
34
+ */
35
+ public function admin_init()
36
+ {
37
+ // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing
38
+ add_settings_section(
39
+ 'wp-fail2ban-plugins',
40
+ __( 'Event Class Facilities' ),
41
+ [ $this, 'sectionLoggingEventClasses' ],
42
+ 'wp-fail2ban-plugins'
43
+ );
44
+ add_settings_field(
45
+ 'plugins-log-auth',
46
+ parent::doc_link( 'WP_FAIL2BAN_PLUGIN_LOG_AUTH', __( 'Authentication' ) ),
47
+ [ $this, 'auth' ],
48
+ 'wp-fail2ban-plugins',
49
+ 'wp-fail2ban-plugins'
50
+ );
51
+ add_settings_field(
52
+ 'plugins-log-comment',
53
+ parent::doc_link( 'WP_FAIL2BAN_PLUGIN_LOG_COMMENT', __( 'Comment' ) ),
54
+ [ $this, 'comment' ],
55
+ 'wp-fail2ban-plugins',
56
+ 'wp-fail2ban-plugins'
57
+ );
58
+ add_settings_field(
59
+ 'plugins-log-password',
60
+ parent::doc_link( 'WP_FAIL2BAN_PLUGIN_LOG_PASSWORD', __( 'Password' ) ),
61
+ [ $this, 'password' ],
62
+ 'wp-fail2ban-plugins',
63
+ 'wp-fail2ban-plugins'
64
+ );
65
+ add_settings_field(
66
+ 'plugins-log-rest',
67
+ parent::doc_link( 'WP_FAIL2BAN_PLUGIN_LOG_REST', __( 'REST' ) ),
68
+ [ $this, 'rest' ],
69
+ 'wp-fail2ban-plugins',
70
+ 'wp-fail2ban-plugins'
71
+ );
72
+ add_settings_field(
73
+ 'plugins-log-spam',
74
+ parent::doc_link( 'WP_FAIL2BAN_PLUGIN_LOG_SPAM', __( 'Spam' ) ),
75
+ [ $this, 'spam' ],
76
+ 'wp-fail2ban-plugins',
77
+ 'wp-fail2ban-plugins'
78
+ );
79
+ add_settings_field(
80
+ 'plugins-log-xmlrpc',
81
+ parent::doc_link( 'WP_FAIL2BAN_PLUGIN_LOG_XMLRPC', __( 'XML-RPC' ) ),
82
+ [ $this, 'xmlrpc' ],
83
+ 'wp-fail2ban-plugins',
84
+ 'wp-fail2ban-plugins'
85
+ );
86
+ // phpcs:enable
87
+ }
88
+
89
+ /**
90
+ * {@inheritDoc}
91
+ *
92
+ * @since 4.2.0
93
+ */
94
+ public function render()
95
+ {
96
+ parent::render();
97
+ }
98
+
99
+ /**
100
+ * {@inheritDoc}
101
+ *
102
+ * @since 4.2.0
103
+ *
104
+ * @param array $settings {@inheritDoc}
105
+ * @param array $input {@inheritDoc}
106
+ *
107
+ * @return array {@inheritDoc}
108
+ */
109
+ public function sanitize( array $settings, array $input = null )
110
+ {
111
+ return $settings;
112
+ }
113
+
114
+ /**
115
+ * Section summary.
116
+ *
117
+ * @since 4.2.0
118
+ */
119
+ public function sectionLoggingEventClasses()
120
+ {
121
+ echo __( 'Facilities to use for plugin-generated messages. The defaults follow the Core defaults.' ) ;
122
+ }
123
+
124
+ /**
125
+ * Auth
126
+ *
127
+ * @since 4.2.0
128
+ */
129
+ public function auth()
130
+ {
131
+ $this->log( 'WP_FAIL2BAN_PLUGIN_LOG_AUTH', 'WP_FAIL2BAN_PLUGIN_AUTH_LOG' );
132
+ }
133
+
134
+ /**
135
+ * Comment
136
+ *
137
+ * @since 4.2.0
138
+ */
139
+ public function comment()
140
+ {
141
+ $this->log( 'WP_FAIL2BAN_PLUGIN_LOG_COMMENT', 'WP_FAIL2BAN_PLUGIN_COMMENT_LOG' );
142
+ }
143
+
144
+ /**
145
+ * Password
146
+ *
147
+ * @since 4.2.0
148
+ */
149
+ public function password()
150
+ {
151
+ $this->log( 'WP_FAIL2BAN_PLUGIN_LOG_PASSWORD', 'WP_FAIL2BAN_PLUGIN_PASSWORD_LOG' );
152
+ }
153
+
154
+ /**
155
+ * REST
156
+ *
157
+ * @since 4.2.0
158
+ */
159
+ public function rest()
160
+ {
161
+ $this->log( 'WP_FAIL2BAN_PLUGIN_LOG_REST', 'WP_FAIL2BAN_PLUGIN_REST_LOG' );
162
+ }
163
+
164
+ /**
165
+ * Spam
166
+ *
167
+ * @since 4.2.0
168
+ */
169
+ public function spam()
170
+ {
171
+ $this->log( 'WP_FAIL2BAN_PLUGIN_LOG_SPAM', 'WP_FAIL2BAN_PLUGIN_SPAM_LOG' );
172
+ }
173
+
174
+ /**
175
+ * XML-RPC
176
+ *
177
+ * @since 4.2.0
178
+ */
179
+ public function xmlrpc()
180
+ {
181
+ $this->log( 'WP_FAIL2BAN_PLUGIN_LOG_XMLRPC', 'WP_FAIL2BAN_PLUGIN_XMLRPC_LOG' );
182
+ }
183
+
184
+ }
185
+ new TabPlugins();
admin/config/remote-ips.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Settings - Remote IPs
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.0.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ /**
15
+ * Tab: Remote IPs
16
+ *
17
+ * @since 4.0.0
18
+ */
19
+ class TabRemoteIPs extends Tab
20
+ {
21
+ /**
22
+ * {@inheritDoc}
23
+ *
24
+ * @since 4.0.0
25
+ */
26
+ public function __construct()
27
+ {
28
+ add_action( 'admin_init', [ $this, 'admin_init' ] );
29
+ parent::__construct( 'remote-ips', 'Remote IPs' );
30
+ }
31
+
32
+ /**
33
+ * {@inheritDoc}
34
+ *
35
+ * @since 4.0.0
36
+ */
37
+ public function admin_init()
38
+ {
39
+ // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing
40
+ add_settings_section(
41
+ 'wp-fail2ban-proxies',
42
+ __( 'Proxies' ),
43
+ [ $this, 'section' ],
44
+ 'wp-fail2ban-remote-ips'
45
+ );
46
+ add_settings_field(
47
+ 'remote-ips-proxies',
48
+ parent::doc_link( 'WP_FAIL2BAN_PROXIES', __( 'IP list' ) ),
49
+ [ $this, 'proxies' ],
50
+ 'wp-fail2ban-remote-ips',
51
+ 'wp-fail2ban-proxies'
52
+ );
53
+ // phpcs:enable
54
+ }
55
+
56
+ /**
57
+ * {@inheritDoc}
58
+ *
59
+ * @since 4.0.0
60
+ *
61
+ * @param array $settings
62
+ * @param array $input
63
+ */
64
+ public function sanitize( array $settings, array $input = null )
65
+ {
66
+ return $settings;
67
+ }
68
+
69
+ /**
70
+ * Section blurb.
71
+ *
72
+ * @since 4.0.0
73
+ */
74
+ public function section()
75
+ {
76
+ echo '' ;
77
+ }
78
+
79
+ /**
80
+ * Proxies.
81
+ *
82
+ * @since 4.0.0
83
+ */
84
+ public function proxies()
85
+ {
86
+ $value = '';
87
+ if ( defined( 'WP_FAIL2BAN_PROXIES' ) ) {
88
+
89
+ if ( is_array( WP_FAIL2BAN_PROXIES ) ) {
90
+ $value = join( "\n", WP_FAIL2BAN_PROXIES );
91
+ } else {
92
+ $value = join( "\n", array_map( 'trim', explode( ',', WP_FAIL2BAN_PROXIES ) ) );
93
+ }
94
+
95
+ }
96
+ printf( '<fieldset><textarea class="code" cols="20" rows="10" disabled="disabled">%s</textarea></fieldset>', esc_html( $value ) );
97
+ }
98
+
99
+ /**
100
+ * Section blurb.
101
+ *
102
+ * @since 4.0.0
103
+ */
104
+ public function sectionGeo()
105
+ {
106
+ }
107
+
108
+ /**
109
+ * MaxMind database.
110
+ *
111
+ * @since 4.0.0
112
+ */
113
+ public function database()
114
+ {
115
+ $settings = get_option( 'wp-fail2ban' );
116
+ $exists = file_exists( @$settings['remote-ip']['maxmind']['path'] );
117
+ $fmt = <<<__FMT__
118
+ <fieldset>
119
+ <label><input type="checkbox" name="wp-fail2ban[remote-ip][maxmind]" %s> Download now</label>
120
+ <p class="description">%s</p>
121
+ </fieldset>
122
+ __FMT__;
123
+ printf( $fmt, checked( $exists, false, false ), ( $exists ? __( 'Last modified: ' ) . gmdate( DATE_RFC1123, $settings['remote-ip']['maxmind']['modified'] ) : __( 'No database found.' ) ) );
124
+ }
125
+
126
+ }
127
+ new TabRemoteIPs();
admin/config/syslog.php ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Settings - syslog
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.0.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ /**
15
+ * Tab: Syslog
16
+ *
17
+ * @since 4.0.0
18
+ */
19
+ class TabSyslog extends Tab
20
+ {
21
+ /**
22
+ * {@inheritDoc}
23
+ */
24
+ public function __construct()
25
+ {
26
+ add_action( 'admin_init', [ $this, 'admin_init' ], 100 );
27
+ parent::__construct( 'syslog', '<tt>syslog</tt>' );
28
+ }
29
+
30
+ /**
31
+ * {@inheritDoc}
32
+ *
33
+ * @since 4.0.0
34
+ */
35
+ public function admin_init()
36
+ {
37
+ // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing
38
+ add_settings_section(
39
+ 'wp-fail2ban-connection',
40
+ __( 'Connection' ),
41
+ [ $this, 'sectionConnection' ],
42
+ 'wp-fail2ban-syslog'
43
+ );
44
+ add_settings_field(
45
+ 'logging-connection',
46
+ parent::doc_link( 'WP_FAIL2BAN_OPENLOG_OPTIONS', __( 'Options' ) ),
47
+ [ $this, 'connection' ],
48
+ 'wp-fail2ban-syslog',
49
+ 'wp-fail2ban-connection'
50
+ );
51
+ add_settings_section(
52
+ 'wp-fail2ban-workarounds',
53
+ __( 'Workarounds' ),
54
+ [ $this, 'sectionWorkarounds' ],
55
+ 'wp-fail2ban-syslog'
56
+ );
57
+ add_settings_field(
58
+ 'logging-workarounds',
59
+ parent::doc_link( 'workarounds', __( 'Options' ) ),
60
+ [ $this, 'workarounds' ],
61
+ 'wp-fail2ban-syslog',
62
+ 'wp-fail2ban-workarounds'
63
+ );
64
+ // phpcs:enable
65
+ }
66
+
67
+ /**
68
+ * {@inheritDoc}
69
+ *
70
+ * @since 4.0.0
71
+ *
72
+ * @param array $settings {@inheritDoc}
73
+ * @param array $input {@inheritDoc}
74
+ *
75
+ * @return array {@inheritDoc}
76
+ */
77
+ public function sanitize( array $settings, array $input = null )
78
+ {
79
+ return $settings;
80
+ }
81
+
82
+ /**
83
+ * Connection section blurb.
84
+ *
85
+ * @since 4.0.0
86
+ */
87
+ public function sectionConnection()
88
+ {
89
+ echo '' ;
90
+ }
91
+
92
+ /**
93
+ * Connection.
94
+ *
95
+ * @since 4.0.0
96
+ */
97
+ public function connection()
98
+ {
99
+ $class = '';
100
+ $fmt = <<<___STR___
101
+ <fieldset>
102
+ <label><input type="checkbox" disabled="disabled" %s> <code>LOG_CONS</code></label><br>
103
+ <label><input type="checkbox" disabled="disabled" %s> <code>LOG_PERROR</code></label><br>
104
+ <label><input type="checkbox" disabled="disabled" %s> <code>LOG_PID</code> <em>(%s)</em></label><br>
105
+ <label><input type="radio" disabled="disabled" %s> <code>LOG_NDELAY</code> <em>(%s)</em></label><br>
106
+ <label><input type="radio" disabled="disabled" %s> <code>LOG_ODELAY</code></label>
107
+ </fieldset>
108
+ ___STR___;
109
+ printf(
110
+ $fmt,
111
+ checked( WP_FAIL2BAN_OPENLOG_OPTIONS & LOG_CONS, LOG_CONS, false ),
112
+ checked( WP_FAIL2BAN_OPENLOG_OPTIONS & LOG_PERROR, LOG_PERROR, false ),
113
+ checked( WP_FAIL2BAN_OPENLOG_OPTIONS & LOG_PID, LOG_PID, false ),
114
+ __( 'default' ),
115
+ checked( WP_FAIL2BAN_OPENLOG_OPTIONS & LOG_NDELAY, LOG_NDELAY, false ),
116
+ __( 'default' ),
117
+ checked( WP_FAIL2BAN_OPENLOG_OPTIONS & LOG_ODELAY, LOG_ODELAY, false )
118
+ );
119
+ }
120
+
121
+ /**
122
+ * Workarounds section blurb.
123
+ *
124
+ * @since 4.0.0
125
+ */
126
+ public function sectionWorkarounds()
127
+ {
128
+ echo '' ;
129
+ }
130
+
131
+ /**
132
+ * Workarounds.
133
+ *
134
+ * @since 4.0.0
135
+ */
136
+ public function workarounds()
137
+ {
138
+ $fmt = <<<___STR___
139
+ <fieldset>
140
+ <label><input type="checkbox" disabled="disabled" %s> %s</label>
141
+ <br>
142
+ <label><input type="checkbox" disabled="disabled" %s> %s</label>
143
+ <br>
144
+ <label><input type="checkbox" disabled="disabled" %s> %s</label>
145
+ </fieldset>
146
+ ___STR___;
147
+ printf(
148
+ $fmt,
149
+ checked( @WP_FAIL2BAN_SYSLOG_SHORT_TAG, true, false ),
150
+ __( 'Short Tag' ),
151
+ checked( @WP_FAIL2BAN_HTTP_HOST, true, false ),
152
+ __( 'Specify Host' ),
153
+ checked( @WP_FAIL2BAN_TRUNCATE_HOST, true, false ),
154
+ __( 'Truncate Host' )
155
+ );
156
+ }
157
+
158
+ }
159
+ new TabSyslog();
admin/lib/about.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * About
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.2.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ /**
15
+ * About content
16
+ *
17
+ * @since 4.2.0
18
+ *
19
+ * @param bool $hide_title
20
+ */
21
+ function about( $hide_title = false )
22
+ {
23
+ $wp_f2b_ver = substr( WP_FAIL2BAN_VER, 0, strrpos( WP_FAIL2BAN_VER, '.' ) );
24
+ ?>
25
+ <div class="wrap">
26
+ <style>
27
+ div.inside ul {
28
+ list-style: disc;
29
+ padding-left: 2em;
30
+ }
31
+ </style>
32
+ <?php
33
+ if ( !$hide_title ) {
34
+ ?>
35
+ <h1>WP fail2ban</h1>
36
+ <?php
37
+ }
38
+ ?>
39
+ <div id="poststuff">
40
+ <div id="post-body" class="metabox-holder columns-2">
41
+ <div id="post-body-content">
42
+ <div class="meta-box-sortables ui-sortable">
43
+ <div class="postbox">
44
+ <h2>Version <?php
45
+ echo WP_FAIL2BAN_VER ;
46
+ ?></h2>
47
+ <div class="inside">
48
+ <ul>
49
+ <li><p>Completed support for <tt><a href="https://docs.wp-fail2ban.com/en/4.2/defines/WP_FAIL2BAN_COMMENT_EXTRA_LOG.html" target="docs.wp-fail2ban.com">WP_FAIL2BAN_COMMENT_EXTRA_LOG</a></tt>.</p></li>
50
+ <li><p>Add support for 3rd-party plugins; see <a href="https://docs.wp-fail2ban.com/en/4.2/developers.html" target="docs.wp-fail2ban.com">Developers</a>.</p>
51
+ <ul>
52
+ <li>Add-on for <a href="https://wordpress.org/plugins/wp-fail2ban-addon-contact-form-7/">Contact Form 7</a> (experimental).</li>
53
+ <li>Add-on for <a href="https://wordpress.org/plugins/wp-fail2ban-addon-gravity-forms/">Gravity Forms</a> (experimental).</li>
54
+ </ul>
55
+ </li>
56
+ <li><p>Change logging for known-user with incorrect password; previously logged as unknown user and matched by <tt>hard</tt> filters (due to limitations in older versions of WordPress), now logged as known user and matched by <tt>soft</tt>.</p></li>
57
+ <li><p>Bugfix for email-as-username - now logged correctly and matched by <tt>soft</tt>, not <tt>hard</tt>, filters.</p></li>
58
+ <li><p>Bugfix for regression in code to prevent Free/Premium conflict.</p></li>
59
+ </ul>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ <div id="postbox-container-1" class="postbox-container">
65
+ <div class="meta-box-sortables">
66
+ <div class="postbox">
67
+ <h2>Getting Started</h2>
68
+ <div class="inside">
69
+ <ol>
70
+ <li><a href="https://docs.wp-fail2ban.com/en/<?php
71
+ echo $wp_f2b_ver ;
72
+ ?>/introduction.html" target="docs.wp-fail2ban.com">Introduction</a></li>
73
+ <li><a href="https://docs.wp-fail2ban.com/en/<?php
74
+ echo $wp_f2b_ver ;
75
+ ?>/configuration.html" target="docs.wp-fail2ban.com">Configuration</a></li>
76
+ </ol>
77
+ </div>
78
+ </div>
79
+ <div class="postbox">
80
+ <h2>Getting Help</h2>
81
+ <div class="inside">
82
+ <ul>
83
+ <?php
84
+ ?>
85
+ <li><a href="https://wordpress.org/support/plugin/wp-fail2ban/" target="_blank">WordPress.org Forum</a></li>
86
+ <?php
87
+ ?>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ &nbsp;
94
+ </div>
95
+ </div>
96
+ <?php
97
+ }
admin/lib/tab.php ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Tab base class
5
+ *
6
+ * @package wp-fail2ban-premium
7
+ * @since 4.0.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ /**
15
+ * Base Tab class
16
+ *
17
+ * @since 4.0.0
18
+ */
19
+ abstract class Tab
20
+ {
21
+ /**
22
+ * @var array Array of Tab objects
23
+ */
24
+ protected static $tabs = array() ;
25
+ /**
26
+ * @var string Active tab slug
27
+ */
28
+ protected static $active_tab ;
29
+ /**
30
+ * @var string Tab slug
31
+ */
32
+ protected $tab_slug ;
33
+ /**
34
+ * @var string Tab name
35
+ */
36
+ protected $tab_name ;
37
+ /**
38
+ * Hook: admin_init
39
+ *
40
+ * @since 4.0.0
41
+ */
42
+ public abstract function admin_init();
43
+
44
+ /**
45
+ * Sanitize and store form fields
46
+ *
47
+ * @since 4.0.0
48
+ *
49
+ * @param array $settings Settings to update
50
+ * @param array $input Form fields
51
+ *
52
+ * @return array $settings
53
+ */
54
+ public abstract function sanitize( array $settings, array $input = null );
55
+
56
+ /**
57
+ * Contruct.
58
+ *
59
+ * @since 4.0.0
60
+ *
61
+ * @param string $slug Tab slug
62
+ * @param string $name Tab name
63
+ */
64
+ public function __construct( $slug, $name )
65
+ {
66
+ $this->tab_slug = $slug;
67
+ $this->tab_name = $name;
68
+ self::$tabs[$slug] = $this;
69
+ }
70
+
71
+ /**
72
+ * Getter - slug
73
+ *
74
+ * @since 4.0.0
75
+ *
76
+ * @return string Tab slug
77
+ */
78
+ public function getSlug()
79
+ {
80
+ return $this->tab_slug;
81
+ }
82
+
83
+ /**
84
+ * Getter - name
85
+ *
86
+ * @since 4.0.0
87
+ *
88
+ * @return string Tab name
89
+ */
90
+ public function getName()
91
+ {
92
+ return $this->tab_name;
93
+ }
94
+
95
+ /**
96
+ * Render settings section
97
+ *
98
+ * @since 4.0.0
99
+ */
100
+ public function render()
101
+ {
102
+ do_settings_sections( 'wp-fail2ban-' . $this->tab_slug );
103
+ }
104
+
105
+ /**
106
+ * Helper - tab
107
+ *
108
+ * @since 4.0.0
109
+ *
110
+ * @param string $slug Tab slug
111
+ *
112
+ * @return Tab Tab
113
+ */
114
+ public static function getTab( $slug )
115
+ {
116
+ return self::$tabs[$slug];
117
+ }
118
+
119
+ /**
120
+ * Helper - current tab
121
+ *
122
+ * @since 4.0.0
123
+ *
124
+ * @param string $default Default slug
125
+ *
126
+ * @return Tab Tab
127
+ */
128
+ public static function getActiveTab( $default = null )
129
+ {
130
+ if ( !empty(self::$active_tab) ) {
131
+ return self::$active_tab;
132
+ }
133
+ return self::$active_tab = ( array_key_exists( @$_GET['tab'], self::$tabs ) ? self::$tabs[$_GET['tab']] : self::$tabs[$default] );
134
+ }
135
+
136
+ /**
137
+ * Helper - tab name
138
+ *
139
+ * @since 4.0.0
140
+ *
141
+ * @param string $slug Tab slug
142
+ *
143
+ * @return string Tab name
144
+ */
145
+ public static function getTabName( $slug )
146
+ {
147
+ return self::getTab( $slug )->getName();
148
+ }
149
+
150
+ /**
151
+ * Link to documentation
152
+ *
153
+ * @since 4.2.0
154
+ *
155
+ * @param string $define
156
+ * @param string $name
157
+ *
158
+ * @return string
159
+ */
160
+ public static function doc_link( $define, $name )
161
+ {
162
+ static $wp_f2b_ver ;
163
+ if ( empty($wp_f2b_ver) ) {
164
+ $wp_f2b_ver = substr( WP_FAIL2BAN_VER, 0, strrpos( WP_FAIL2BAN_VER, '.' ) );
165
+ }
166
+ return sprintf(
167
+ '<a href="https://wp-fail2ban.readthedocs.io/en/%s/defines/%s.html" style="text-decoration: none;" target="_blank" title="Documentation"><span class="dashicons dashicons-external" style="vertical-align: text-bottom"></span></a> %s',
168
+ $wp_f2b_ver,
169
+ $define,
170
+ $name
171
+ );
172
+ }
173
+
174
+ /**
175
+ * Helper - drop-down list of facilities
176
+ *
177
+ * @since 4.0.0
178
+ *
179
+ * @param string $def Name of define for selected value
180
+ * @param bool $_enabled Enabled?
181
+ */
182
+ protected function getLogFacilities( $def, bool $_enabled = false )
183
+ {
184
+ $enabled = false;
185
+ $facilities = [
186
+ LOG_AUTH => 'LOG_AUTH',
187
+ LOG_AUTHPRIV => 'LOG_AUTHPRIV',
188
+ LOG_CRON => 'LOG_CRON',
189
+ LOG_DAEMON => 'LOG_DAEMON',
190
+ LOG_KERN => 'LOG_KERN',
191
+ LOG_LOCAL0 => 'LOG_LOCAL0',
192
+ LOG_LOCAL1 => 'LOG_LOCAL1',
193
+ LOG_LOCAL2 => 'LOG_LOCAL2',
194
+ LOG_LOCAL3 => 'LOG_LOCAL3',
195
+ LOG_LOCAL4 => 'LOG_LOCAL4',
196
+ LOG_LOCAL5 => 'LOG_LOCAL5',
197
+ LOG_LOCAL6 => 'LOG_LOCAL6',
198
+ LOG_LOCAL7 => 'LOG_LOCAL7',
199
+ LOG_LPR => 'LOG_LPR',
200
+ LOG_MAIL => 'LOG_MAIL',
201
+ LOG_NEWS => 'LOG_NEWS',
202
+ LOG_SYSLOG => 'LOG_SYSLOG',
203
+ LOG_USER => 'LOG_USER',
204
+ LOG_UUCP => 'LOG_UUCP',
205
+ ];
206
+ $default = constant( "DEFAULT_{$def}" );
207
+ $value = ( defined( $def ) ? constant( $def ) : $default );
208
+ $str = '<select disabled="disabled">';
209
+ foreach ( $facilities as $facility => $name ) {
210
+ $str .= sprintf(
211
+ '<option value="%s" %s>%s%s</option>',
212
+ $facility,
213
+ selected( $value, $facility, false ),
214
+ $name,
215
+ ( $facility == $default ? __( ' (default)' ) : '' )
216
+ );
217
+ }
218
+ $str .= '</select>';
219
+ return $str;
220
+ }
221
+
222
+ /**
223
+ * Log helper - enable/disable+facility
224
+ *
225
+ * @since 4.2.0 Moved to Tab
226
+ * @since 4.0.0
227
+ *
228
+ * @param string $define_name Name of define to enable logging
229
+ * @param string $define_log Name of define for log facility
230
+ * @param string $description Description
231
+ * @param array $toggle Array of IDs to sync toggle state
232
+ */
233
+ protected function log(
234
+ $define_name,
235
+ $define_log,
236
+ $description = '',
237
+ array $toggle = array()
238
+ )
239
+ {
240
+ $enabled = defined( $define_name ) && true === constant( $define_name ) && constant( "{$define_log}_NDEF" );
241
+ $fmt = <<<___FMT___
242
+ <label><input type="checkbox" disabled="disabled" %s> Enable logging</label>,
243
+ <label>use facility: %s</label>
244
+ <p class="description">%s</p>
245
+ ___FMT___;
246
+ $html = sprintf(
247
+ $fmt,
248
+ checked( $enabled, true, false ),
249
+ $this->getLogFacilities( $define_log, $enabled ),
250
+ $description
251
+ );
252
+ echo apply_filters(
253
+ "wp_fail2ban_log_{$define_name}",
254
+ $html,
255
+ $define_name,
256
+ $define_log
257
+ ) ;
258
+ }
259
+
260
+ }
admin/summary.php DELETED
@@ -1,282 +0,0 @@
1
- <?php
2
- /**
3
- * Configuration summary
4
- *
5
- * @package wp-fail2ban
6
- * @since 4.0.0
7
- */
8
- namespace org\lecklider\charles\wordpress\wp_fail2ban;
9
-
10
- /**
11
- * Add options page
12
- *
13
- * @since 4.0.0
14
- */
15
- function admin_menu()
16
- {
17
- add_options_page(
18
- 'WP fail2ban - Summary',
19
- 'WP fail2ban',
20
- 'manage_options',
21
- 'wp-fail2ban',
22
- __NAMESPACE__.'\summary'
23
- );
24
- }
25
- add_action('admin_menu', __NAMESPACE__.'\admin_menu');
26
-
27
-
28
- /**
29
- * Link to documentation
30
- *
31
- * @since 4.0.0
32
- *
33
- * @param string $define
34
- * @param string $name
35
- *
36
- * @return string
37
- */
38
- function _doc_link($define, $name)
39
- {
40
- static $wp_f2b_ver;
41
-
42
- if (empty($wp_f2b_ver)) {
43
- $wp_f2b_ver = substr(WP_FAIL2BAN_VER, 0, strrpos(WP_FAIL2BAN_VER, '.'));
44
- }
45
-
46
- return sprintf('<a href="https://wp-fail2ban.readthedocs.io/en/%s/defines.html#%s" target="_blank">%s <span class="dashicons dashicons-external"></span></a>', $wp_f2b_ver, str_replace('_', '-', strtolower($define)), $name);
47
- }
48
-
49
-
50
- /**
51
- * List log facilities
52
- *
53
- * @param int $facility
54
- *
55
- * @since 4.0.0
56
- */
57
- function _get_log_facility($facility)
58
- {
59
- $facilities = array(
60
- LOG_AUTH => 'LOG_AUTH',
61
- LOG_AUTHPRIV => 'LOG_AUTHPRIV',
62
- LOG_CRON => 'LOG_CRON',
63
- LOG_DAEMON => 'LOG_DAEMON',
64
- LOG_KERN => 'LOG_KERN',
65
- LOG_LOCAL0 => 'LOG_LOCAL0',
66
- LOG_LOCAL1 => 'LOG_LOCAL1',
67
- LOG_LOCAL2 => 'LOG_LOCAL2',
68
- LOG_LOCAL3 => 'LOG_LOCAL3',
69
- LOG_LOCAL4 => 'LOG_LOCAL4',
70
- LOG_LOCAL5 => 'LOG_LOCAL5',
71
- LOG_LOCAL6 => 'LOG_LOCAL6',
72
- LOG_LOCAL7 => 'LOG_LOCAL7',
73
- LOG_LPR => 'LOG_LPR',
74
- LOG_MAIL => 'LOG_MAIL',
75
- LOG_NEWS => 'LOG_NEWS',
76
- LOG_SYSLOG => 'LOG_SYSLOG',
77
- LOG_USER => 'LOG_USER',
78
- LOG_UUCP => 'LOG_UUCP',
79
- );
80
-
81
- return (array_key_exists($facility, $facilities))
82
- ? $facilities[$facility]
83
- : '(unknown)';
84
- }
85
-
86
-
87
- /**
88
- *
89
- *
90
- * @since 4.0.0
91
- *
92
- * @param string $name
93
- * @param string $enabled
94
- * @param string $location
95
- */
96
- function _log($name, $enabled, $location)
97
- {
98
- ?>
99
- <tr>
100
- <th><?php echo _doc_link($enabled, $name); ?></th>
101
- <?php if (defined($enabled) && true === constant($enabled)) : ?>
102
- <td><?php _e('Yes'); ?></td>
103
- <th><?php _e('Facility'); ?></th>
104
- <td><?php echo _get_log_facility(constant($location))?> <?php echo (constant($location) == constant("DEFAULT_$location")) ? ' <em>('.__('default').')</em>' : ''; ?></td>
105
- <?php else : ?>
106
- <td><?php _e('No'); ?></td>
107
- <?php endif; ?>
108
- </tr>
109
- <?php
110
- }
111
-
112
-
113
- /**
114
- * Display syslog connection options
115
- *
116
- * @since 4.0.0
117
- */
118
- function _log_options()
119
- {
120
- $options = (defined('WP_FAIL2BAN_OPENLOG_OPTIONS'))
121
- ? WP_FAIL2BAN_OPENLOG_OPTIONS
122
- : DEFAULT_WP_FAIL2BAN_OPENLOG_OPTIONS;
123
-
124
- $opts = array();
125
- (LOG_CONS == ($options & LOG_CONS)) and $opts[] = 'LOG_CONS';
126
- (LOG_PERROR == ($options & LOG_PERROR)) and $opts[] = 'LOG_PERROR';
127
- (LOG_PID == ($options & LOG_PID)) and $opts[] = 'LOG_PID';
128
- (LOG_NDELAY == ($options & LOG_NDELAY)) and $opts[] = 'LOG_NDELAY';
129
- (LOG_ODELAY == ($options & LOG_ODELAY)) and $opts[] = 'LOG_ODELAY';
130
- ?>
131
- <tr>
132
- <th><?php echo _doc_link('WP_FAIL2BAN_OPENLOG_OPTIONS', __('Connection')); ?></th>
133
- <td colspan="3"><?php sprintf((DEFAULT_WP_FAIL2BAN_OPENLOG_OPTIONS == $options)
134
- ? '%s <em>(%s)</em>'
135
- : '%s', join(', ', $opts), __('default')); ?></td>
136
- </tr>
137
- <?php
138
- }
139
-
140
-
141
- /**
142
- * Display Yes/No/array values
143
- *
144
- * @param string $name
145
- * @param string|array $option
146
- * @param bool|null $ary
147
- *
148
- * @since 4.0.0
149
- */
150
- function _yes_no_array($name, $option, $ary = false)
151
- {
152
- $th = _doc_link($option, $name);
153
-
154
- if ($ary) {
155
- if (defined($option)) {
156
- $items = (is_array(constant($option)))
157
- ? constant($option)
158
- : trim(' "', explode(',', constant($option)));
159
- } else {
160
- $items = array('-');
161
- }
162
- $td = join(', ', $items);
163
- } elseif (is_null($ary)) {
164
- if (defined($option)) {
165
- $td = (is_array(constant($option)))
166
- ? join(' ,', constant($option))
167
- : constant($option);
168
- } else {
169
- $td = '-';
170
- }
171
- } else {
172
- $td = (defined($option) && true === constant($option))
173
- ? __('Yes')
174
- : __('No');
175
- }
176
-
177
- echo <<<___YNA___
178
- <tr>
179
- <th>$th</th>
180
- <td>$td</td>
181
- </tr>
182
- ___YNA___;
183
- }
184
-
185
-
186
- /**
187
- * Display configuration summary
188
- *
189
- * @since 4.0.0
190
- */
191
- function summary()
192
- {
193
- ?>
194
- <div class="wrap">
195
- <style>
196
- table.form-table a {
197
- text-decoration: none;
198
- }
199
- </style>
200
- <h1>WP fail2ban - Summary</h1>
201
- <?php if (wf_fs()->is_not_paying()):
202
- $ver = PHP_VERSION;
203
- if (version_compare($ver, '7.1.0', '<')):
204
- $wp = '<p>WordPress will <a href="https://make.wordpress.org/core/2018/12/08/updating-the-minimum-php-version/">soon require</a> at least PHP 5.6.</p>';
205
- ?>
206
- <h2>Please <a href="https://wordpress.org/support/update-php/">update PHP!</a></h2>
207
- <?php if (version_compare($ver, '5.4.0', '<')): ?>
208
- <p>Support for PHP 5.3 ended 14 Aug 2014.</p>
209
- <p><em>WP fail2ban Premium</em> currently requires at least PHP 5.4.</p>
210
- <?php echo $wp; ?>
211
- <?php elseif (version_compare($ver, '5.5.0', '<')): ?>
212
- <p>Support for PHP 5.4 ended 3 Sep 2015.</p>
213
- <?php echo $wp; ?>
214
- <?php elseif (version_compare($ver, '5.6.0', '<')): ?>
215
- <p>Support for PHP 5.5 ended 21 Jul 2016.</p>
216
- <?php echo $wp; ?>
217
- <?php elseif (version_compare($ver, '7.0.0', '<')): ?>
218
- <p>Support for PHP 5.6 ended 31 Dec 2018.</p>
219
- <?php else: ?>
220
- <p>Support for PHP 7.0 ended 3 Dec 2018.</p>
221
- <?php endif; ?>
222
- <hr>
223
- <?php endif; ?>
224
- <?php if (version_compare($ver, '5.3.0', '>')): ?>
225
- <p>Do you want a simple GUI to change these settings?</p>
226
- <p>Do you want reports on where and when attacks are happening?</p>
227
- <p><a href="<?php echo wf_fs()->get_upgrade_url(); ?>">Upgrade now!</a></p>
228
- <hr>
229
- <?php endif; ?>
230
- <?php endif; ?>
231
- <div class="card">
232
- <h2 class="title"><?php _e('Logging'); ?></h2>
233
- <table class="form-table">
234
- <tbody>
235
- <?php
236
- _log(__('Comments'), 'WP_FAIL2BAN_LOG_COMMENTS', 'WP_FAIL2BAN_COMMENT_LOG');
237
- _log(__('Password Requests'), 'WP_FAIL2BAN_LOG_PASSWORD_REQUEST', 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG');
238
- _log(__('Pingbacks'), 'WP_FAIL2BAN_LOG_PINGBACKS', 'WP_FAIL2BAN_PINGBACK_LOG');
239
- _log(__('Spam'), 'WP_FAIL2BAN_LOG_SPAM', 'WP_FAIL2BAN_SPAM_LOG');
240
- _log_options();
241
- ?>
242
- </tbody>
243
- </table>
244
- </div>
245
- <div class="card">
246
- <h2 class="title"><?php _e('Workarounds'); ?></h2>
247
- <table class="form-table">
248
- <tbody>
249
- <?php
250
- _yes_no_array(__('Short Tag'), 'WP_FAIL2BAN_SYSLOG_SHORT_TAG');
251
- _yes_no_array(__('Specify Host'), 'WP_FAIL2BAN_HTTP_HOST');
252
- _yes_no_array(__('Truncate Host'), 'WP_FAIL2BAN_TRUNCATE_HOST');
253
- ?>
254
- </tbody>
255
- </table>
256
- </div>
257
- <div class="card">
258
- <h2 class="title"><?php _e('Block'); ?></h2>
259
- <table class="form-table">
260
- <tbody>
261
- <?php
262
- _yes_no_array(__('User Enumeration'), 'WP_FAIL2BAN_BLOCK_USER_ENUMERATION');
263
- _yes_no_array(__('Usernames'), 'WP_FAIL2BAN_BLOCKED_USERS', null);
264
- ?>
265
- </tbody>
266
- </table>
267
- </div>
268
- <div class="card">
269
- <h2 class="title"><?php _e('Misc'); ?></h2>
270
- <table class="form-table">
271
- <tbody>
272
- <?php
273
- _yes_no_array(__('Proxies'), 'WP_FAIL2BAN_PROXIES', true);
274
- _yes_no_array(__('Remote Address'), 'WP_FAIL2BAN_REMOTE_ADDR', null);
275
- ?>
276
- </tbody>
277
- </table>
278
- </div>
279
- </div>
280
- <?php
281
- }
282
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
feature/comments.php CHANGED
@@ -32,6 +32,7 @@ if ( !function_exists( __NAMESPACE__ . '\\notify_post_author' ) ) {
32
  {
33
  openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
34
  syslog( LOG_INFO, "Comment {$comment_ID}" );
 
35
  // @codeCoverageIgnoreEnd
36
  return $maybe_notify;
37
  }
@@ -46,7 +47,7 @@ if ( !function_exists( __NAMESPACE__ . '\\notify_post_author' ) ) {
46
 
47
 
48
  if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
49
- /** WPF2B_ACTION_COMMENT_NOT_FOUND */
50
  if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20002 ) {
51
  /**
52
  * @since 4.0.5 Guard
@@ -64,8 +65,9 @@ if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
64
  */
65
  function comment_id_not_found( $comment_post_ID )
66
  {
67
- openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
68
  syslog( LOG_NOTICE, "Comment post not found {$comment_post_ID}" );
 
69
  // @codeCoverageIgnoreEnd
70
  }
71
 
@@ -91,8 +93,9 @@ if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
91
  */
92
  function comment_closed( $comment_post_ID )
93
  {
94
- openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
95
  syslog( LOG_NOTICE, "Comments closed on post {$comment_post_ID}" );
 
96
  // @codeCoverageIgnoreEnd
97
  }
98
 
@@ -119,8 +122,9 @@ if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
119
  */
120
  function comment_on_trash( $comment_post_ID )
121
  {
122
- openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
123
  syslog( LOG_NOTICE, "Comment attempt on trash post {$comment_post_ID}" );
 
124
  // @codeCoverageIgnoreEnd
125
  }
126
 
@@ -147,8 +151,9 @@ if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
147
  */
148
  function comment_on_draft( $comment_post_ID )
149
  {
150
- openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
151
  syslog( LOG_NOTICE, "Comment attempt on draft post {$comment_post_ID}" );
 
152
  // @codeCoverageIgnoreEnd
153
  }
154
 
@@ -175,8 +180,9 @@ if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
175
  */
176
  function comment_on_password_protected( $comment_post_ID )
177
  {
178
- openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
179
  syslog( LOG_NOTICE, "Comment attempt on password-protected post {$comment_post_ID}" );
 
180
  // @codeCoverageIgnoreEnd
181
  }
182
 
32
  {
33
  openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
34
  syslog( LOG_INFO, "Comment {$comment_ID}" );
35
+ closelog();
36
  // @codeCoverageIgnoreEnd
37
  return $maybe_notify;
38
  }
47
 
48
 
49
  if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
50
+ /** WPF2B_EVENT_COMMENT_NOT_FOUND */
51
  if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20002 ) {
52
  /**
53
  * @since 4.0.5 Guard
65
  */
66
  function comment_id_not_found( $comment_post_ID )
67
  {
68
+ openlog( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG' );
69
  syslog( LOG_NOTICE, "Comment post not found {$comment_post_ID}" );
70
+ closelog();
71
  // @codeCoverageIgnoreEnd
72
  }
73
 
93
  */
94
  function comment_closed( $comment_post_ID )
95
  {
96
+ openlog( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG' );
97
  syslog( LOG_NOTICE, "Comments closed on post {$comment_post_ID}" );
98
+ closelog();
99
  // @codeCoverageIgnoreEnd
100
  }
101
 
122
  */
123
  function comment_on_trash( $comment_post_ID )
124
  {
125
+ openlog( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG' );
126
  syslog( LOG_NOTICE, "Comment attempt on trash post {$comment_post_ID}" );
127
+ closelog();
128
  // @codeCoverageIgnoreEnd
129
  }
130
 
151
  */
152
  function comment_on_draft( $comment_post_ID )
153
  {
154
+ openlog( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG' );
155
  syslog( LOG_NOTICE, "Comment attempt on draft post {$comment_post_ID}" );
156
+ closelog();
157
  // @codeCoverageIgnoreEnd
158
  }
159
 
180
  */
181
  function comment_on_password_protected( $comment_post_ID )
182
  {
183
+ openlog( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG' );
184
  syslog( LOG_NOTICE, "Comment attempt on password-protected post {$comment_post_ID}" );
185
+ closelog();
186
  // @codeCoverageIgnoreEnd
187
  }
188
 
feature/lib.php CHANGED
@@ -73,6 +73,8 @@ function syslog( $level, $msg, $remote_addr = null )
73
  *
74
  * @since 4.0.5 Add JSON support
75
  * @since 3.5.0 Refactored for unit testing
 
 
76
  */
77
  function bail( $is_json = false )
78
  {
73
  *
74
  * @since 4.0.5 Add JSON support
75
  * @since 3.5.0 Refactored for unit testing
76
+ *
77
+ * @param bool $is_json
78
  */
79
  function bail( $is_json = false )
80
  {
feature/password.php CHANGED
@@ -29,6 +29,7 @@ if ( !function_exists( __NAMESPACE__ . '\\retrieve_password' ) ) {
29
  {
30
  openlog( 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG' );
31
  syslog( LOG_NOTICE, "Password reset requested for {$user_login}" );
 
32
  // @codeCoverageIgnoreEnd
33
  }
34
 
29
  {
30
  openlog( 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG' );
31
  syslog( LOG_NOTICE, "Password reset requested for {$user_login}" );
32
+ closelog();
33
  // @codeCoverageIgnoreEnd
34
  }
35
 
feature/plugins.php ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Library functions
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.2.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ /**
15
+ * Hook: plugins_loaded
16
+ *
17
+ * @since 4.2.0
18
+ */
19
+ function plugins_loaded()
20
+ {
21
+ do_action( 'wp_fail2ban_register' );
22
+ }
23
+
24
+ add_action( 'plugins_loaded', __NAMESPACE__ . '\\plugins_loaded' );
25
+ /**
26
+ * Register plugin
27
+ *
28
+ * @since 4.2.0
29
+ *
30
+ * @param string $slug Plugin slug. This must be the actual plugin slug. Maximum length is 255 which should be more than enough.
31
+ * @param string $name Plugin display name. This should be an unescaped string - HTML is allowed.
32
+ *
33
+ * @return int|false ID
34
+ */
35
+ function register_plugin( $slug, $name )
36
+ {
37
+ global $wp_fail2ban, $wpdb ;
38
+ if ( 255 < strlen( $slug ) ) {
39
+ throw new \LengthException( 'slug too long' );
40
+ }
41
+ if ( 255 < strlen( $name ) ) {
42
+ throw new \LengthException( 'name too long' );
43
+ }
44
+ if ( !is_array( @$wp_fail2ban['plugins'] ) ) {
45
+ $wp_fail2ban['plugins'] = [];
46
+ }
47
+ if ( array_key_exists( $slug, $wp_fail2ban['plugins'] ) ) {
48
+ return $wp_fail2ban['plugins'][$slug];
49
+ }
50
+ static $id = 0 ;
51
+ return $wp_fail2ban['plugins'][$slug] = [
52
+ 'id' => ++$id,
53
+ 'name' => $name,
54
+ 'messages' => [],
55
+ ];
56
+ }
57
+
58
+ add_action(
59
+ 'wp_fail2ban_register_plugin',
60
+ __NAMESPACE__ . '\\register_plugin',
61
+ 1,
62
+ 2
63
+ );
64
+ /**
65
+ * Check if plugin is registered.
66
+ *
67
+ * @since 4.2.0
68
+ *
69
+ * @param string $plugin_slug
70
+ *
71
+ * @return bool
72
+ */
73
+ function is_registered_plugin( $plugin_slug )
74
+ {
75
+ global $wp_fail2ban ;
76
+ return array_key_exists( $plugin_slug, $wp_fail2ban['plugins'] );
77
+ }
78
+
79
+ /**
80
+ * Register plugin message.
81
+ *
82
+ * @since 4.2.0
83
+ *
84
+ * @param string $plugin_slug
85
+ * @param array $msg [
86
+ * string slug: Message slug
87
+ * string fail: hard|soft|extra
88
+ * int facility: syslog facility
89
+ * int priority: syslog priority
90
+ * string event_class: Event Class
91
+ * int event_id: Event ID
92
+ * string message: Message with placeholders
93
+ * HOST: Remote IP
94
+ * USER: Current user name
95
+ * array vars: Array of [name => regex] pairs
96
+ */
97
+ function register_message( $plugin_slug, array $msg )
98
+ {
99
+ global $wp_fail2ban ;
100
+ $event_classes = [
101
+ 'auth' => WPF2B_EVENT_CLASS_AUTH,
102
+ 'comment' => WPF2B_EVENT_CLASS_COMMENT,
103
+ 'password' => WPF2B_EVENT_CLASS_PASSWORD,
104
+ 'rest' => WPF2B_EVENT_CLASS_REST,
105
+ 'spam' => WPF2B_EVENT_CLASS_SPAM,
106
+ 'xmlrpc' => WPF2B_EVENT_CLASS_XMLRPC,
107
+ 'other' => 0,
108
+ ];
109
+ $args = [];
110
+ if ( !is_registered_plugin( $plugin_slug ) ) {
111
+ throw new \InvalidArgumentException( 'plugin not registered' );
112
+ }
113
+ if ( !array_key_exists( 'slug', $msg ) ) {
114
+ throw new \InvalidArgumentException( "Missing 'slug'" );
115
+ }
116
+ if ( !is_string( $msg['slug'] ) ) {
117
+ throw new \InvalidArgumentException( "'slug' must be string" );
118
+ }
119
+ if ( !array_key_exists( 'fail', $msg ) ) {
120
+ throw new \InvalidArgumentException( "Missing 'fail'" );
121
+ }
122
+ if ( !in_array( $msg['fail'], [ 'hard', 'soft', 'extra' ] ) ) {
123
+ throw new \UnexpectedValueException( "'fail' must be one of 'hard', 'soft', 'extra'" );
124
+ }
125
+ $args['fail'] = $msg['fail'];
126
+ if ( !array_key_exists( 'priority', $msg ) ) {
127
+ throw new \InvalidArgumentException( "Missing 'priority'" );
128
+ }
129
+ if ( !in_array( $msg['priority'], [
130
+ LOG_CRIT,
131
+ LOG_ERR,
132
+ LOG_WARNING,
133
+ LOG_NOTICE,
134
+ LOG_INFO,
135
+ LOG_DEBUG
136
+ ] ) ) {
137
+ throw new \UnexpectedValueException( "Invalid 'priority'" );
138
+ }
139
+ $args['priority'] = $msg['priority'];
140
+ if ( !array_key_exists( 'event_class', $msg ) ) {
141
+ throw new \InvalidArgumentException( "Missing 'event_class'" );
142
+ }
143
+ if ( !array_key_exists( $event_class = strtolower( $msg['event_class'] ), $event_classes ) ) {
144
+ throw new \UnexpectedValueException( "Invalid 'event_class'" );
145
+ }
146
+ $args['class'] = $event_class;
147
+ $event_class = $event_classes[$event_class];
148
+ $log = sprintf( "WP_FAIL2BAN_%s_LOG", strtoupper( $event_class ) );
149
+ if ( !array_key_exists( 'event_id', $msg ) ) {
150
+ throw new \InvalidArgumentException( "Missing 'event_id'" );
151
+ }
152
+ if ( ($msg['event_id'] & 0xffff) !== $msg['event_id'] ) {
153
+ throw new \UnexpectedValueException( "Invalid 'event_id'" );
154
+ }
155
+ $args['event_id'] = WPF2B_EVENT_TYPE_PLUGIN | $event_class | $msg['event_id'];
156
+ if ( !array_key_exists( 'message', $msg ) ) {
157
+ throw new \InvalidArgumentException( "Missing 'message'" );
158
+ }
159
+ if ( !is_string( $msg['message'] ) ) {
160
+ throw new \UnexpectedValueException( "Invalid 'message'" );
161
+ }
162
+ $args['message'] = $msg['message'];
163
+ if ( !array_key_exists( 'vars', $msg ) ) {
164
+ throw new \InvalidArgumentException( "Missing 'vars'" );
165
+ }
166
+ if ( !is_array( $msg['vars'] ) ) {
167
+ throw new \UnexpectedValueException( "Invalid 'vars'" );
168
+ }
169
+ $args['vars'] = $msg['vars'];
170
+ $wp_fail2ban['plugins'][$plugin_slug]['messages'][$msg['slug']] = $args;
171
+ }
172
+
173
+ add_action(
174
+ 'wp_fail2ban_register_message',
175
+ __NAMESPACE__ . '\\register_message',
176
+ 1,
177
+ 2
178
+ );
179
+ /**
180
+ * Check if message is registered.
181
+ *
182
+ * NB: Assumes plugin is registered.
183
+ *
184
+ * @since 4.2.0
185
+ *
186
+ * @param string $plugin_slug
187
+ * @param string $message_slug
188
+ *
189
+ * @return bool
190
+ */
191
+ function is_registered_plugin_message( $plugin_slug, $message_slug )
192
+ {
193
+ global $wp_fail2ban ;
194
+ return array_key_exists( $message_slug, $wp_fail2ban['plugins'][$plugin_slug]['messages'] );
195
+ }
196
+
197
+ /**
198
+ * Log plugin message.
199
+ *
200
+ * @since 4.2.0
201
+ *
202
+ * @param string $plugin_slug Plugin slug for registered message
203
+ * @param string $message_slug Message slug for registered message
204
+ * @param array $vars Substitution vars
205
+ */
206
+ function log_message( $plugin_slug, $message_slug = null, array $vars = array() )
207
+ {
208
+ global $wp_fail2ban ;
209
+ if ( !is_registered_plugin( $plugin_slug ) ) {
210
+ throw new \InvalidArgumentException( 'plugin not registered' );
211
+ }
212
+ if ( !is_registered_plugin_message( $plugin_slug, $message_slug ) ) {
213
+ throw new \InvalidArgumentException( 'message not registered' );
214
+ }
215
+ $args = $wp_fail2ban['plugins'][$plugin_slug]['messages'][$message_slug];
216
+ $msg = $args['message'];
217
+ foreach ( $args['vars'] as $name => $regex ) {
218
+ if ( array_key_exists( $name, $vars ) ) {
219
+ $msg = str_replace( "___{$name}___", $vars[$name], $msg );
220
+ }
221
+ }
222
+ openlog( sprintf( 'WP_FAIL2BAN_PLUGIN_%s_LOG', strtoupper( $args['class'] ) ) );
223
+ syslog( $args['priority'], "({$plugin_slug}) {$msg}" );
224
+ closelog();
225
+ // @codeCoverageIgnoreEnd
226
+ }
227
+
228
+ add_action(
229
+ 'wp_fail2ban_log_message',
230
+ __NAMESPACE__ . '\\log_message',
231
+ 1,
232
+ 3
233
+ );
feature/spam.php CHANGED
@@ -38,6 +38,7 @@ if ( !function_exists( __NAMESPACE__ . '\\log_spam_comment' ) ) {
38
  $remote_addr = ( empty($comment['comment_author_IP']) ? 'unknown' : $comment['comment_author_IP'] );
39
  openlog( 'WP_FAIL2BAN_SPAM_LOG' );
40
  syslog( LOG_NOTICE, "Spam comment {$comment_id}", $remote_addr );
 
41
  // @codeCoverageIgnoreEnd
42
  }
43
 
38
  $remote_addr = ( empty($comment['comment_author_IP']) ? 'unknown' : $comment['comment_author_IP'] );
39
  openlog( 'WP_FAIL2BAN_SPAM_LOG' );
40
  syslog( LOG_NOTICE, "Spam comment {$comment_id}", $remote_addr );
41
+ closelog();
42
  // @codeCoverageIgnoreEnd
43
  }
44
 
feature/user-enum.php CHANGED
@@ -31,6 +31,7 @@ if ( !function_exists( __NAMESPACE__ . '\\_log_bail_user_enum' ) ) {
31
  {
32
  openlog();
33
  syslog( LOG_NOTICE, 'Blocked user enumeration attempt' );
 
34
  // @codeCoverageIgnoreEnd
35
  return bail( $is_json );
36
  }
31
  {
32
  openlog();
33
  syslog( LOG_NOTICE, 'Blocked user enumeration attempt' );
34
+ closelog();
35
  // @codeCoverageIgnoreEnd
36
  return bail( $is_json );
37
  }
feature/user.php CHANGED
@@ -42,6 +42,7 @@ if ( !function_exists( __NAMESPACE__ . '\\authenticate' ) ) {
42
  if ( $matched ) {
43
  openlog();
44
  syslog( LOG_NOTICE, "Blocked authentication attempt for {$username}" );
 
45
  // @codeCoverageIgnoreEnd
46
  bail();
47
  }
42
  if ( $matched ) {
43
  openlog();
44
  syslog( LOG_NOTICE, "Blocked authentication attempt for {$username}" );
45
+ closelog();
46
  // @codeCoverageIgnoreEnd
47
  bail();
48
  }
feature/xmlrpc.php CHANGED
@@ -39,6 +39,7 @@ if ( !function_exists( __NAMESPACE__ . '\\xmlrpc_login_error' ) ) {
39
  if ( ++$attempts > 1 ) {
40
  openlog();
41
  syslog( LOG_NOTICE, 'XML-RPC multicall authentication failure' );
 
42
  // @codeCoverageIgnoreEnd
43
  bail();
44
  } else {
@@ -81,6 +82,7 @@ if ( !function_exists( __NAMESPACE__ . '\\xmlrpc_pingback_error' ) ) {
81
  if ( 48 !== $ixr_error->code ) {
82
  openlog();
83
  syslog( LOG_NOTICE, 'Pingback error ' . $ixr_error->code . ' generated' );
 
84
  // @codeCoverageIgnoreEnd
85
  }
86
 
39
  if ( ++$attempts > 1 ) {
40
  openlog();
41
  syslog( LOG_NOTICE, 'XML-RPC multicall authentication failure' );
42
+ closelog();
43
  // @codeCoverageIgnoreEnd
44
  bail();
45
  } else {
82
  if ( 48 !== $ixr_error->code ) {
83
  openlog();
84
  syslog( LOG_NOTICE, 'Pingback error ' . $ixr_error->code . ' generated' );
85
+ closelog();
86
  // @codeCoverageIgnoreEnd
87
  }
88
 
feature/xmlrpc/pingback.php CHANGED
@@ -30,6 +30,7 @@ if ( !function_exists( __NAMESPACE__ . '\\xmlrpc_call' ) ) {
30
  if ( 'pingback.ping' == $call ) {
31
  openlog( 'WP_FAIL2BAN_PINGBACK_LOG' );
32
  syslog( LOG_INFO, 'Pingback requested' );
 
33
  // @codeCoverageIgnoreEnd
34
  }
35
 
30
  if ( 'pingback.ping' == $call ) {
31
  openlog( 'WP_FAIL2BAN_PINGBACK_LOG' );
32
  syslog( LOG_INFO, 'Pingback requested' );
33
+ closelog();
34
  // @codeCoverageIgnoreEnd
35
  }
36
 
filters.d/wordpress-extra.conf CHANGED
@@ -1,5 +1,5 @@
1
  # Fail2Ban filter for WordPress extra failures
2
- # Auto-generated: 2019-03-13T01:12:18+00:00
3
  #
4
 
5
  [INCLUDES]
1
  # Fail2Ban filter for WordPress extra failures
2
+ # Auto-generated: 2019-04-18T14:58:18+00:00
3
  #
4
 
5
  [INCLUDES]
filters.d/wordpress-hard.conf CHANGED
@@ -1,5 +1,5 @@
1
  # Fail2Ban filter for WordPress hard failures
2
- # Auto-generated: 2019-03-13T01:12:18+00:00
3
  #
4
 
5
  [INCLUDES]
1
  # Fail2Ban filter for WordPress hard failures
2
+ # Auto-generated: 2019-04-18T14:58:18+00:00
3
  #
4
 
5
  [INCLUDES]
filters.d/wordpress-soft.conf CHANGED
@@ -1,5 +1,5 @@
1
  # Fail2Ban filter for WordPress soft failures
2
- # Auto-generated: 2019-03-13T01:12:18+00:00
3
  #
4
 
5
  [INCLUDES]
1
  # Fail2Ban filter for WordPress soft failures
2
+ # Auto-generated: 2019-04-18T14:58:18+00:00
3
  #
4
 
5
  [INCLUDES]
lib/constants.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Constants
4
+ *
5
+ * @package wp-fail2ban
6
+ * @since 4.2.0
7
+ */
8
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
9
+
10
+ if (!defined('ABSPATH')) {
11
+ exit;
12
+ }
13
+
14
+ // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing
15
+ /**
16
+ * Defaults
17
+ *
18
+ * @since 4.0.0
19
+ */
20
+ define('DEFAULT_WP_FAIL2BAN_OPENLOG_OPTIONS', LOG_PID|LOG_NDELAY);
21
+ define('DEFAULT_WP_FAIL2BAN_AUTH_LOG', LOG_AUTH);
22
+ define('DEFAULT_WP_FAIL2BAN_COMMENT_LOG', LOG_USER);
23
+ define('DEFAULT_WP_FAIL2BAN_PINGBACK_LOG', LOG_USER);
24
+ define('DEFAULT_WP_FAIL2BAN_PASSWORD_REQUEST_LOG', LOG_USER);
25
+ define('DEFAULT_WP_FAIL2BAN_SPAM_LOG', LOG_AUTH);
26
+ /**
27
+ * @since 4.0.5
28
+ */
29
+ define('DEFAULT_WP_FAIL2BAN_COMMENT_EXTRA_LOG', LOG_AUTH);
30
+ define('DEFAULT_WP_FAIL2BAN_PINGBACK_ERROR_LOG', LOG_AUTH);
31
+ /**
32
+ * @since 4.2.0
33
+ */
34
+ define('DEFAULT_WP_FAIL2BAN_PLUGIN_AUTH_LOG', LOG_AUTH);
35
+ define('DEFAULT_WP_FAIL2BAN_PLUGIN_COMMENT_LOG', LOG_USER);
36
+ define('DEFAULT_WP_FAIL2BAN_PLUGIN_OTHER_LOG', LOG_USER);
37
+ define('DEFAULT_WP_FAIL2BAN_PLUGIN_PASSWORD_LOG', LOG_USER);
38
+ define('DEFAULT_WP_FAIL2BAN_PLUGIN_REST_LOG', LOG_USER);
39
+ define('DEFAULT_WP_FAIL2BAN_PLUGIN_SPAM_LOG', LOG_AUTH);
40
+ define('DEFAULT_WP_FAIL2BAN_PLUGIN_XMLRPC_LOG', LOG_USER);
41
+
42
+ /*
43
+ 31 | Test
44
+ 30 | Plugin
45
+ 29 |
46
+ 28 |
47
+ 27 |
48
+ 26 |
49
+ 25 |
50
+ 24 |
51
+ ---
52
+ 23 | Event Class
53
+ 22 | ..
54
+ 21 | ..
55
+ 20 | ..
56
+ 19 | ..
57
+ 18 | ..
58
+ 17 | ..
59
+ 16 | ..
60
+ ---
61
+ 15 | ID
62
+ 14 | ..
63
+ 13 | ..
64
+ 12 | ..
65
+ 11 | ..
66
+ 10 | ..
67
+ 09 | ..
68
+ 08 | ..
69
+ ---
70
+ 07 | ..
71
+ 06 | ..
72
+ 05 | ..
73
+ 04 | ..
74
+ 03 | ..
75
+ 02 | ..
76
+ 01 | ..
77
+ 00 | ..
78
+ */
79
+
80
+
81
+
82
+ define('WPF2B_EVENT_CLASS_AUTH', 0x00010000);
83
+ define('WPF2B_EVENT_CLASS_COMMENT', 0x00020000);
84
+ define('WPF2B_EVENT_CLASS_XMLRPC', 0x00040000);
85
+ define('WPF2B_EVENT_CLASS_PASSWORD', 0x00080000);
86
+ define('WPF2B_EVENT_CLASS_REST', 0x00100000); /** @since 4.1.0 */
87
+ define('WPF2B_EVENT_CLASS_SPAM', 0x00200000); /** @since 4.2.0 */
88
+ define('WPF2B_EVENT_TYPE_PLUGIN', 0x40000000); /** @since 4.2.0 */
89
+ define('WPF2B_EVENT_TYPE_TEST', 0x80000000); /** @since 4.2.0 */
90
+
91
+
92
+ /**
93
+ *
94
+ */
95
+ define('WPF2B_EVENT_ACTIVATED', 0xffffffff);
96
+
97
+
98
+ /**
99
+ * Auth
100
+ */
101
+ define('WPF2B_EVENT_AUTH_OK', WPF2B_EVENT_CLASS_AUTH | 0x0001);
102
+ define('WPF2B_EVENT_AUTH_FAIL', WPF2B_EVENT_CLASS_AUTH | 0x0002);
103
+ define('WPF2B_EVENT_AUTH_BLOCK_USER', WPF2B_EVENT_CLASS_AUTH | 0x0004);
104
+ define('WPF2B_EVENT_AUTH_BLOCK_USER_ENUM', WPF2B_EVENT_CLASS_AUTH | 0x0008);
105
+
106
+ /**
107
+ * Comment
108
+ */
109
+ define('WPF2B_EVENT_COMMENT_SPAM', WPF2B_EVENT_CLASS_COMMENT | WPF2B_EVENT_CLASS_SPAM | 0x0001); // 0x00220001
110
+ // comment extra
111
+ define('WPF2B_EVENT_COMMENT_NOT_FOUND', WPF2B_EVENT_CLASS_COMMENT | 0x0002); // 0x00020002
112
+ define('WPF2B_EVENT_COMMENT_CLOSED', WPF2B_EVENT_CLASS_COMMENT | 0x0004); // 0x00020004
113
+ define('WPF2B_EVENT_COMMENT_TRASH', WPF2B_EVENT_CLASS_COMMENT | 0x0008); // 0x00020008
114
+ define('WPF2B_EVENT_COMMENT_DRAFT', WPF2B_EVENT_CLASS_COMMENT | 0x0010); // 0x00020010
115
+ define('WPF2B_EVENT_COMMENT_PASSWORD', WPF2B_EVENT_CLASS_COMMENT | WPF2B_EVENT_CLASS_PASSWORD | 0x0020); // 0x00020020
116
+
117
+ /**
118
+ * XML-RPC
119
+ */
120
+ define('WPF2B_EVENT_XMLRPC_PINGBACK', WPF2B_EVENT_CLASS_XMLRPC | 0x0001);
121
+ define('WPF2B_EVENT_XMLRPC_PINGBACK_ERROR', WPF2B_EVENT_CLASS_XMLRPC | 0x0002);
122
+ define('WPF2B_EVENT_XMLRPC_MULTI_AUTH_FAIL', WPF2B_EVENT_CLASS_XMLRPC | WPF2B_EVENT_CLASS_AUTH | 0x0004);
123
+ define('WPF2B_EVENT_XMLRPC_AUTH_OK', WPF2B_EVENT_CLASS_XMLRPC | WPF2B_EVENT_CLASS_AUTH | 0x0008);
124
+ define('WPF2B_EVENT_XMLRPC_AUTH_FAIL', WPF2B_EVENT_CLASS_XMLRPC | WPF2B_EVENT_CLASS_AUTH | 0x0010);
125
+
126
+ /**
127
+ * Password
128
+ */
129
+ define('WPF2B_ACTION_PASSWORD_REQUEST', WPF2B_EVENT_CLASS_PASSWORD | 0x0001);
130
+
131
+ /**
132
+ * REST
133
+ * @since 4.1.0
134
+ */
135
+ define('WPF2B_EVENT_REST_AUTH_OK', WPF2B_EVENT_CLASS_REST | WPF2B_EVENT_CLASS_AUTH | 0x0001);
136
+ define('WPF2B_EVENT_REST_AUTH_FAIL', WPF2B_EVENT_CLASS_REST | WPF2B_EVENT_CLASS_AUTH | 0x0002);
137
+
138
+ /**
139
+ *
140
+ */
141
+ define('WPF2B_EVENT_DEACTIVATED', 0x00000000);
142
+ // phpcs:enable
143
+
lib/defaults.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Default Constants
4
+ *
5
+ * @package wp-fail2ban
6
+ * @since 4.2.0
7
+ */
8
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
9
+
10
+ if (!defined('ABSPATH')) {
11
+ exit;
12
+ }
13
+
14
+ /**
15
+ * Allow custom openlog options.
16
+ * e.g. you may not want the PID if logging remotely.
17
+ *
18
+ * @since 3.6.0 Add LOG_NDELAY
19
+ * @since 3.5.0
20
+ */
21
+ if (!defined('WP_FAIL2BAN_OPENLOG_OPTIONS')) {
22
+ define('WP_FAIL2BAN_OPENLOG_OPTIONS', DEFAULT_WP_FAIL2BAN_OPENLOG_OPTIONS);
23
+ }
24
+ /**
25
+ * Make sure all custom logs are defined.
26
+ * @since 3.5.0
27
+ */
28
+ if (!defined('WP_FAIL2BAN_AUTH_LOG')) {
29
+ define('WP_FAIL2BAN_AUTH_LOG', DEFAULT_WP_FAIL2BAN_AUTH_LOG);
30
+ }
31
+ if (!defined('WP_FAIL2BAN_COMMENT_LOG')) {
32
+ define('WP_FAIL2BAN_COMMENT_LOG', DEFAULT_WP_FAIL2BAN_COMMENT_LOG);
33
+ }
34
+ if (!defined('WP_FAIL2BAN_PINGBACK_LOG')) {
35
+ define('WP_FAIL2BAN_PINGBACK_LOG', DEFAULT_WP_FAIL2BAN_PINGBACK_LOG);
36
+ }
37
+ /**
38
+ * @since 4.0.0
39
+ */
40
+ if (!defined('WP_FAIL2BAN_PASSWORD_REQUEST_LOG')) {
41
+ define('WP_FAIL2BAN_PASSWORD_REQUEST_LOG', DEFAULT_WP_FAIL2BAN_PASSWORD_REQUEST_LOG);
42
+ }
43
+ if (!defined('WP_FAIL2BAN_SPAM_LOG')) {
44
+ define('WP_FAIL2BAN_SPAM_LOG', DEFAULT_WP_FAIL2BAN_SPAM_LOG);
45
+ }
46
+ /**
47
+ * @since 4.0.5
48
+ */
49
+ if (!defined('WP_FAIL2BAN_COMMENT_EXTRA_LOG')) {
50
+ define('WP_FAIL2BAN_COMMENT_EXTRA_LOG', DEFAULT_WP_FAIL2BAN_COMMENT_EXTRA_LOG);
51
+ }
52
+ if (!defined('WP_FAIL2BAN_PINGBACK_ERROR_LOG')) {
53
+ define('WP_FAIL2BAN_PINGBACK_ERROR_LOG', DEFAULT_WP_FAIL2BAN_PINGBACK_ERROR_LOG);
54
+ }
55
+ /**
56
+ * @since 4.2.0
57
+ */
58
+ if (!defined('WP_FAIL2BAN_PLUGIN_AUTH_LOG')) {
59
+ define('WP_FAIL2BAN_PLUGIN_AUTH_LOG', DEFAULT_WP_FAIL2BAN_PLUGIN_AUTH_LOG);
60
+ }
61
+ if (!defined('WP_FAIL2BAN_PLUGIN_COMMENT_LOG')) {
62
+ define('WP_FAIL2BAN_PLUGIN_COMMENT_LOG', DEFAULT_WP_FAIL2BAN_PLUGIN_COMMENT_LOG);
63
+ }
64
+ if (!defined('WP_FAIL2BAN_PLUGIN_OTHER_LOG')) {
65
+ define('WP_FAIL2BAN_PLUGIN_OTHER_LOG', DEFAULT_WP_FAIL2BAN_PLUGIN_OTHER_LOG);
66
+ }
67
+ if (!defined('WP_FAIL2BAN_PLUGIN_PASSWORD_LOG')) {
68
+ define('WP_FAIL2BAN_PLUGIN_PASSWORD_LOG', DEFAULT_WP_FAIL2BAN_PLUGIN_PASSWORD_LOG);
69
+ }
70
+ if (!defined('WP_FAIL2BAN_PLUGIN_REST_LOG')) {
71
+ define('WP_FAIL2BAN_PLUGIN_REST_LOG', DEFAULT_WP_FAIL2BAN_PLUGIN_REST_LOG);
72
+ }
73
+ if (!defined('WP_FAIL2BAN_PLUGIN_SPAM_LOG')) {
74
+ define('WP_FAIL2BAN_PLUGIN_SPAM_LOG', DEFAULT_WP_FAIL2BAN_PLUGIN_SPAM_LOG);
75
+ }
76
+ if (!defined('WP_FAIL2BAN_PLUGIN_XMLRPC_LOG')) {
77
+ define('WP_FAIL2BAN_PLUGIN_XMLRPC_LOG', DEFAULT_WP_FAIL2BAN_PLUGIN_XMLRPC_LOG);
78
+ }
79
+
lib/loader.php ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Loader
5
+ *
6
+ * @package wp-fail2ban
7
+ * @since 4.2.0
8
+ */
9
+ namespace org\lecklider\charles\wordpress\wp_fail2ban;
10
+
11
+ if ( !defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+ if ( defined( 'PHPUNIT_COMPOSER_INSTALL' ) ) {
15
+ return;
16
+ }
17
+ /**
18
+ * Helper
19
+ *
20
+ * @since 4.0.0
21
+ *
22
+ * @param string $define
23
+ * @param callable $cast
24
+ * @param bool $unset
25
+ * @param array $field
26
+ */
27
+ function _load(
28
+ $define,
29
+ $cast,
30
+ $unset,
31
+ array $field
32
+ )
33
+ {
34
+ define( "{$define}_NDEF", !defined( $define ) );
35
+ if ( constant( "{$define}_NDEF" ) ) {
36
+
37
+ if ( defined( "DEFAULT_{$define}" ) ) {
38
+ // we've got a default
39
+ define( $define, $cast( constant( "DEFAULT_{$define}" ) ) );
40
+ } else {
41
+ // bah
42
+ define( $define, $cast( false ) );
43
+ }
44
+
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Validate IP list
50
+ *
51
+ * @since 4.0.0
52
+ *
53
+ * @param array|string $value
54
+ *
55
+ * @return string
56
+ */
57
+ function validate_ips( $value )
58
+ {
59
+ return $value;
60
+ }
61
+
62
+ // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing
63
+ _load(
64
+ 'WP_FAIL2BAN_AUTH_LOG',
65
+ 'intval',
66
+ true,
67
+ [ 'logging', 'authentication', 'facility' ]
68
+ );
69
+ _load(
70
+ 'WP_FAIL2BAN_LOG_COMMENTS',
71
+ 'boolval',
72
+ true,
73
+ [ 'logging', 'comments', 'enabled' ]
74
+ );
75
+ _load(
76
+ 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA',
77
+ 'intval',
78
+ true,
79
+ [ 'logging', 'comments', 'extra' ]
80
+ );
81
+ _load(
82
+ 'WP_FAIL2BAN_COMMENT_LOG',
83
+ 'intval',
84
+ false,
85
+ [ 'logging', 'comments', 'facility' ]
86
+ );
87
+ _load(
88
+ 'WP_FAIL2BAN_COMMENT_EXTRA_LOG',
89
+ 'intval',
90
+ false,
91
+ [ 'logging', 'comments-extra', 'facility' ]
92
+ );
93
+ _load(
94
+ 'WP_FAIL2BAN_LOG_PASSWORD_REQUEST',
95
+ 'boolval',
96
+ true,
97
+ [ 'logging', 'password-request', 'enabled' ]
98
+ );
99
+ _load(
100
+ 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG',
101
+ 'intval',
102
+ false,
103
+ [ 'logging', 'password-request', 'facility' ]
104
+ );
105
+ _load(
106
+ 'WP_FAIL2BAN_LOG_PINGBACKS',
107
+ 'boolval',
108
+ true,
109
+ [ 'logging', 'pingback', 'enabled' ]
110
+ );
111
+ _load(
112
+ 'WP_FAIL2BAN_PINGBACK_LOG',
113
+ 'intval',
114
+ false,
115
+ [ 'logging', 'pingback', 'facility' ]
116
+ );
117
+ _load(
118
+ 'WP_FAIL2BAN_LOG_SPAM',
119
+ 'boolval',
120
+ true,
121
+ [ 'logging', 'spam', 'enabled' ]
122
+ );
123
+ _load(
124
+ 'WP_FAIL2BAN_SPAM_LOG',
125
+ 'intval',
126
+ false,
127
+ [ 'logging', 'spam', 'facility' ]
128
+ );
129
+ _load(
130
+ 'WP_FAIL2BAN_OPENLOG_OPTIONS',
131
+ 'intval',
132
+ true,
133
+ [ 'syslog', 'connection' ]
134
+ );
135
+ _load(
136
+ 'WP_FAIL2BAN_SYSLOG_SHORT_TAG',
137
+ 'boolval',
138
+ true,
139
+ [ 'syslog', 'workaround', 'short_tag' ]
140
+ );
141
+ _load(
142
+ 'WP_FAIL2BAN_HTTP_HOST',
143
+ 'boolval',
144
+ true,
145
+ [ 'syslog', 'workaround', 'http_host' ]
146
+ );
147
+ _load(
148
+ 'WP_FAIL2BAN_TRUNCATE_HOST',
149
+ 'boolval',
150
+ true,
151
+ [ 'syslog', 'workaround', 'truncate_host' ]
152
+ );
153
+ _load(
154
+ 'WP_FAIL2BAN_BLOCK_USER_ENUMERATION',
155
+ 'boolval',
156
+ true,
157
+ [ 'block', 'user_enumeration' ]
158
+ );
159
+ _load(
160
+ 'WP_FAIL2BAN_BLOCKED_USERS',
161
+ 'strval',
162
+ true,
163
+ [ 'block', 'users' ]
164
+ );
165
+ _load(
166
+ 'WP_FAIL2BAN_PROXIES',
167
+ __NAMESPACE__ . '\\validate_ips',
168
+ true,
169
+ [ 'remote-ip', 'proxies' ]
170
+ );
171
+ _load(
172
+ 'WP_FAIL2BAN_PLUGIN_LOG_AUTH',
173
+ 'boolval',
174
+ true,
175
+ [
176
+ 'logging',
177
+ 'plugins',
178
+ 'auth',
179
+ 'enabled'
180
+ ]
181
+ );
182
+ _load(
183
+ 'WP_FAIL2BAN_PLUGIN_LOG_COMMENT',
184
+ 'boolval',
185
+ true,
186
+ [
187
+ 'logging',
188
+ 'plugins',
189
+ 'comment',
190
+ 'enabled'
191
+ ]
192
+ );
193
+ _load(
194
+ 'WP_FAIL2BAN_PLUGIN_LOG_PASSWORD',
195
+ 'boolval',
196
+ true,
197
+ [
198
+ 'logging',
199
+ 'plugins',
200
+ 'password',
201
+ 'enabled'
202
+ ]
203
+ );
204
+ _load(
205
+ 'WP_FAIL2BAN_PLUGIN_LOG_REST',
206
+ 'boolval',
207
+ true,
208
+ [
209
+ 'logging',
210
+ 'plugins',
211
+ 'rest',
212
+ 'enabled'
213
+ ]
214
+ );
215
+ _load(
216
+ 'WP_FAIL2BAN_PLUGIN_LOG_SPAM',
217
+ 'boolval',
218
+ true,
219
+ [
220
+ 'logging',
221
+ 'plugins',
222
+ 'spam',
223
+ 'enabled'
224
+ ]
225
+ );
226
+ _load(
227
+ 'WP_FAIL2BAN_PLUGIN_LOG_XMLRPC',
228
+ 'boolval',
229
+ true,
230
+ [
231
+ 'logging',
232
+ 'plugins',
233
+ 'xmlrpc',
234
+ 'enabled'
235
+ ]
236
+ );
237
+ _load(
238
+ 'WP_FAIL2BAN_PLUGIN_AUTH_LOG',
239
+ 'intval',
240
+ false,
241
+ [
242
+ 'logging',
243
+ 'plugins',
244
+ 'auth',
245
+ 'facility'
246
+ ]
247
+ );
248
+ _load(
249
+ 'WP_FAIL2BAN_PLUGIN_COMMENT_LOG',
250
+ 'intval',
251
+ false,
252
+ [
253
+ 'logging',
254
+ 'plugins',
255
+ 'comment',
256
+ 'facility'
257
+ ]
258
+ );
259
+ _load(
260
+ 'WP_FAIL2BAN_PLUGIN_PASSWORD_LOG',
261
+ 'intval',
262
+ false,
263
+ [
264
+ 'logging',
265
+ 'plugins',
266
+ 'password',
267
+ 'facility'
268
+ ]
269
+ );
270
+ _load(
271
+ 'WP_FAIL2BAN_PLUGIN_REST_LOG',
272
+ 'intval',
273
+ false,
274
+ [
275
+ 'logging',
276
+ 'plugins',
277
+ 'rest',
278
+ 'facility'
279
+ ]
280
+ );
281
+ _load(
282
+ 'WP_FAIL2BAN_PLUGIN_SPAM_LOG',
283
+ 'intval',
284
+ false,
285
+ [
286
+ 'logging',
287
+ 'plugins',
288
+ 'spam',
289
+ 'facility'
290
+ ]
291
+ );
292
+ _load(
293
+ 'WP_FAIL2BAN_PLUGIN_XMLRPC_LOG',
294
+ 'intval',
295
+ false,
296
+ [
297
+ 'logging',
298
+ 'plugins',
299
+ 'xmlrpc',
300
+ 'facility'
301
+ ]
302
+ );
303
+ // phpcs:enable
readme.txt CHANGED
@@ -4,9 +4,9 @@ Donate link: https://paypal.me/invisnet/
4
  Author URI: https://charles.lecklider.org/
5
  Plugin URI: https://wp-fail2ban.com/
6
  Tags: fail2ban, login, security, syslog
7
- Requires at least: 3.4
8
  Tested up to: 5.1
9
- Stable tag: 4.1.0
10
  Requires PHP: 5.3
11
  License: GPLv2 or later
12
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -26,38 +26,55 @@ Write a myriad of WordPress events to syslog for integration with fail2ban.
26
 
27
  = Features =
28
 
 
 
 
 
 
29
  * **CloudFlare and Proxy Servers**
30
- *WPf2b* can be configured to work with CloudFlare and other proxy servers. For an overview see [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-proxies).
31
 
32
  * **Comments**
33
- *WPf2b* can log comments (see [`WP_FAIL2BAN_LOG_COMMENTS`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-log-comments)) and attempted comments (see [`WP_FAIL2BAN_LOG_COMMENTS_EXTRA`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-log-comments-extra)).
34
 
35
  * **Pingbacks**
36
- *WPf2b* logs failed pingbacks, and can log all pingbacks. For an overview see [`WP_FAIL2BAN_LOG_PINGBACKS`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-log-pingbacks).
37
 
38
  * **Spam**
39
- *WPf2b* can log comments marked as spam. See [`WP_FAIL2BAN_LOG_SPAM`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-log-spam).
40
 
41
  * **Block User Enumeration**
42
- *WPf2b* can block user enumeration. See [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-block-user-enumeration).
43
 
44
  * **Work-Arounds for Broken syslogd**
45
- *WPf2b* can be configured to work around most syslogd weirdness. For an overview see [`WP_FAIL2BAN_SYSLOG_SHORT_TAG`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-syslog-short-tag) and [`WP_FAIL2BAN_HTTP_HOST`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-http-host).
46
 
47
  * **Blocking Users**
48
- *WPf2b* can be configured to short-cut the login process when the username matches a regex. For an overview see [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-blocked-users).
49
 
50
  * **`mu-plugins` Support**
51
- *WPf2b* can easily be configured as a must-use plugin - see [Configuration](https://docs.wp-fail2ban.com/en/4.1/configuration.html#mu-plugins-support).
52
 
53
  == Installation ==
54
 
55
  1. Install via the Plugin Directory, or upload to your plugins directory.
56
  1. Activate the plugin through the 'Plugins' menu in WordPress.
57
- 1. Edit `wp-config.php` to suit your needs - see [Configuration](https://docs.wp-fail2ban.com/en/4.1/configuration.html).
58
 
59
  == Changelog ==
60
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  = 4.1.0 =
62
  * Add separate logging for REST authentication.
63
  * Fix conflict with earlier versions pre-installed in `mu-plugins`. See [Is *WPf2b* Already Installed?](https://docs.wp-fail2ban.com/en/4.1/installation.html#is-wp-fail2ban-already-installed).
@@ -171,11 +188,14 @@ Write a myriad of WordPress events to syslog for integration with fail2ban.
171
 
172
  == Upgrade Notice ==
173
 
 
 
 
174
  = 4.1.0 =
175
  To take advantage of the new features you will need up update your `fail2ban` filters; existing filters will continue to work as before.
176
 
177
  = 4.0.5 =
178
- This is a security fix: all 4.x users are strongly advised to upgrade immediately. You do not need to update your filters from 4.0.1.
179
 
180
  = 4.0.4 =
181
  This is a bugfix. You do not need to update your filters from 4.0.1.
4
  Author URI: https://charles.lecklider.org/
5
  Plugin URI: https://wp-fail2ban.com/
6
  Tags: fail2ban, login, security, syslog
7
+ Requires at least: 4.2
8
  Tested up to: 5.1
9
+ Stable tag: 4.2.1
10
  Requires PHP: 5.3
11
  License: GPLv2 or later
12
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
26
 
27
  = Features =
28
 
29
+ * **NEW - Support for 3rd-party Plugins**
30
+ Version 4.2 introduces a simple API for authors to integrate their plugins with *WPf2b*, with 2 *experimental* add-ons:
31
+ * [Contact Form 7](https://wordpress.org/plugins/wp-fail2ban-addon-contact-form-7/)
32
+ * [Gravity Forms](https://wordpress.org/plugins/wp-fail2ban-addon-gravity-forms/)
33
+
34
  * **CloudFlare and Proxy Servers**
35
+ *WPf2b* can be configured to work with CloudFlare and other proxy servers. For an overview see [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.2/defines.html#wp-fail2ban-proxies).
36
 
37
  * **Comments**
38
+ *WPf2b* can log comments (see [`WP_FAIL2BAN_LOG_COMMENTS`](https://docs.wp-fail2ban.com/en/4.2/defines.html#wp-fail2ban-log-comments)) and attempted comments (see [`WP_FAIL2BAN_LOG_COMMENTS_EXTRA`](https://docs.wp-fail2ban.com/en/4.2/defines.html#wp-fail2ban-log-comments-extra)).
39
 
40
  * **Pingbacks**
41
+ *WPf2b* logs failed pingbacks, and can log all pingbacks. For an overview see [`WP_FAIL2BAN_LOG_PINGBACKS`](https://docs.wp-fail2ban.com/en/4.2/defines.html#wp-fail2ban-log-pingbacks).
42
 
43
  * **Spam**
44
+ *WPf2b* can log comments marked as spam. See [`WP_FAIL2BAN_LOG_SPAM`](https://docs.wp-fail2ban.com/en/4.2/defines.html#wp-fail2ban-log-spam).
45
 
46
  * **Block User Enumeration**
47
+ *WPf2b* can block user enumeration. See [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.2/defines.html#wp-fail2ban-block-user-enumeration).
48
 
49
  * **Work-Arounds for Broken syslogd**
50
+ *WPf2b* can be configured to work around most syslogd weirdness. For an overview see [`WP_FAIL2BAN_SYSLOG_SHORT_TAG`](https://docs.wp-fail2ban.com/en/4.2/defines.html#wp-fail2ban-syslog-short-tag) and [`WP_FAIL2BAN_HTTP_HOST`](https://docs.wp-fail2ban.com/en/4.2/defines.html#wp-fail2ban-http-host).
51
 
52
  * **Blocking Users**
53
+ *WPf2b* can be configured to short-cut the login process when the username matches a regex. For an overview see [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.2/defines.html#wp-fail2ban-blocked-users).
54
 
55
  * **`mu-plugins` Support**
56
+ *WPf2b* can easily be configured as a must-use plugin - see [Configuration](https://docs.wp-fail2ban.com/en/4.2/configuration.html#mu-plugins-support).
57
 
58
  == Installation ==
59
 
60
  1. Install via the Plugin Directory, or upload to your plugins directory.
61
  1. Activate the plugin through the 'Plugins' menu in WordPress.
62
+ 1. Edit `wp-config.php` to suit your needs - see [Configuration](https://docs.wp-fail2ban.com/en/4.2/configuration.html).
63
 
64
  == Changelog ==
65
 
66
+ = 4.2.1 =
67
+ * Completed support for [`WP_FAIL2BAN_COMMENT_EXTRA_LOG`](https://docs.wp-fail2ban.com/en/4.2/defines/WP_FAIL2BAN_COMMENT_EXTRA_LOG.html).
68
+ * Add support for 3rd-party plugins; see [Developers](https://docs.wp-fail2ban.com/en/4.2/developers.html).
69
+ * Add-on for [Contact Form 7](https://wordpress.org/plugins/wp-fail2ban-addon-contact-form-7/) (experimental).
70
+ * Add-on for [Gravity Forms](https://wordpress.org/plugins/wp-fail2ban-addon-gravity-forms/) (experimental).
71
+ * Change logging for known-user with incorrect password; previously logged as unknown user and matched by `hard` filters (due to limitations in older versions of WordPress), now logged as known user and matched by `soft`.
72
+ * Bugfix for email-as-username - now logged correctly and matched by `soft`, not `hard`, filters.
73
+ * Bugfix for regression in code to prevent Free/Premium conflict.
74
+
75
+ = 4.2.0 =
76
+ * Not released.
77
+
78
  = 4.1.0 =
79
  * Add separate logging for REST authentication.
80
  * Fix conflict with earlier versions pre-installed in `mu-plugins`. See [Is *WPf2b* Already Installed?](https://docs.wp-fail2ban.com/en/4.1/installation.html#is-wp-fail2ban-already-installed).
188
 
189
  == Upgrade Notice ==
190
 
191
+ = 4.2.1 =
192
+ You do not need to update your filters from 4.1.0.
193
+
194
  = 4.1.0 =
195
  To take advantage of the new features you will need up update your `fail2ban` filters; existing filters will continue to work as before.
196
 
197
  = 4.0.5 =
198
+ This is a security fix (Freemius SDK): all 4.x users are strongly advised to upgrade immediately. You do not need to update your filters from 4.0.1.
199
 
200
  = 4.0.4 =
201
  This is a bugfix. You do not need to update your filters from 4.0.1.
wp-fail2ban-main.php CHANGED
@@ -11,37 +11,24 @@ namespace org\lecklider\charles\wordpress\wp_fail2ban;
11
  if ( !defined( 'ABSPATH' ) ) {
12
  exit;
13
  }
14
- // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing
15
- /**
16
- * Defaults
17
- *
18
- * @since 4.0.0
19
- */
20
- define( 'DEFAULT_WP_FAIL2BAN_OPENLOG_OPTIONS', LOG_PID | LOG_NDELAY );
21
- define( 'DEFAULT_WP_FAIL2BAN_AUTH_LOG', LOG_AUTH );
22
- define( 'DEFAULT_WP_FAIL2BAN_COMMENT_LOG', LOG_USER );
23
- define( 'DEFAULT_WP_FAIL2BAN_PINGBACK_LOG', LOG_USER );
24
- define( 'DEFAULT_WP_FAIL2BAN_PASSWORD_REQUEST_LOG', LOG_USER );
25
- define( 'DEFAULT_WP_FAIL2BAN_SPAM_LOG', LOG_AUTH );
26
- /**
27
- * @since 4.0.5
28
- */
29
- define( 'DEFAULT_WP_FAIL2BAN_COMMENT_EXTRA_LOG', LOG_AUTH );
30
- define( 'DEFAULT_WP_FAIL2BAN_PINGBACK_ERROR_LOG', LOG_AUTH );
31
  register_activation_hook( WP_FAIL2BAN_FILE, function () {
32
  foreach ( get_mu_plugins() as $plugin => $data ) {
33
 
34
- if ( 'WP fail2ban' == substr( $data['Name'], 0, 11 ) ) {
35
  $wp_f2b_ver = substr( WP_FAIL2BAN_VER, 0, strrpos( WP_FAIL2BAN_VER, '.' ) );
36
  $wpf2b = 'WP fail2ban';
37
- $error_msg = "<h1>Cannot activate {$wpf2b}</h1>";
38
  $mu_file = WPMU_PLUGIN_DIR . '/' . $plugin;
39
 
40
  if ( is_link( $mu_file ) ) {
41
 
42
  if ( false === ($link = readlink( $mu_file )) || false === ($path = realpath( $mu_file )) ) {
 
43
  $error_msg .= <<<__ERROR__
44
- <h3>A broken symbolic link was found in <tt>mu-plugins</tt>:</h3>
45
  <p><tt>{$mu_file}</tt></p>
46
  __ERROR__;
47
  } elseif ( WP_FAIL2BAN_FILE == $path ) {
@@ -49,8 +36,9 @@ __ERROR__;
49
  } else {
50
  $mu_file = str_replace( '/', '/<wbr>', $mu_file );
51
  $mu_file = substr( $mu_file, strlen( WPMU_PLUGIN_DIR ) - 1 );
 
52
  $error_msg .= <<<__ERROR__
53
- <h3>A conflicting symbolic link was found in <tt>mu-plugins</tt>:</h3>
54
  <style>
55
  table { text-align: center; }
56
  td { width: 50%; }
@@ -77,64 +65,25 @@ __ERROR__;
77
  } else {
78
  $mu_file = str_replace( '/', '/<wbr>', $mu_file );
79
  $mu_file = substr( $mu_file, strlen( WPMU_PLUGIN_DIR ) - 1 );
 
80
  $error_msg .= <<<__ERROR__
81
- <h3>A conflicting file was found in <tt>mu-plugins</tt>:</h3>
82
  <p><tt>{$mu_file}</tt></p>
83
  __ERROR__;
84
  }
85
 
86
- $error_msg .= "<p>Please see the <a href=\"https://docs.wp-fail2ban.com/en/{$wp_f2b_ver}/configuration.html#mu-plugins-support\" target=\"_blank\">documentation</a> for how to configure {$wpf2b} for <tt>mu-plugins</tt>.</p>";
87
- $error_msg .= '<p>Click <a href="?">here</a> to return to the plugins page.</p>';
88
  deactivate_plugins( plugin_basename( WP_FAIL2BAN_FILE ) );
89
  wp_die( $error_msg );
90
  }
91
 
92
  }
93
  } );
94
- /**
95
- * Allow custom openlog options.
96
- * e.g. you may not want the PID if logging remotely.
97
- *
98
- * @since 3.6.0 Add LOG_NDELAY
99
- * @since 3.5.0
100
- */
101
- if ( !defined( 'WP_FAIL2BAN_OPENLOG_OPTIONS' ) ) {
102
- define( 'WP_FAIL2BAN_OPENLOG_OPTIONS', DEFAULT_WP_FAIL2BAN_OPENLOG_OPTIONS );
103
- }
104
- /**
105
- * Make sure all custom logs are defined.
106
- * @since 3.5.0
107
- */
108
- if ( !defined( 'WP_FAIL2BAN_AUTH_LOG' ) ) {
109
- define( 'WP_FAIL2BAN_AUTH_LOG', DEFAULT_WP_FAIL2BAN_AUTH_LOG );
110
- }
111
- if ( !defined( 'WP_FAIL2BAN_COMMENT_LOG' ) ) {
112
- define( 'WP_FAIL2BAN_COMMENT_LOG', DEFAULT_WP_FAIL2BAN_COMMENT_LOG );
113
- }
114
- if ( !defined( 'WP_FAIL2BAN_PINGBACK_LOG' ) ) {
115
- define( 'WP_FAIL2BAN_PINGBACK_LOG', DEFAULT_WP_FAIL2BAN_PINGBACK_LOG );
116
- }
117
- /**
118
- * @since 4.0.0
119
- */
120
- if ( !defined( 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG' ) ) {
121
- define( 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG', DEFAULT_WP_FAIL2BAN_PASSWORD_REQUEST_LOG );
122
- }
123
- if ( !defined( 'WP_FAIL2BAN_SPAM_LOG' ) ) {
124
- define( 'WP_FAIL2BAN_SPAM_LOG', DEFAULT_WP_FAIL2BAN_SPAM_LOG );
125
- }
126
- /**
127
- * @since 4.0.5
128
- */
129
- if ( !defined( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG' ) ) {
130
- define( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG', DEFAULT_WP_FAIL2BAN_COMMENT_EXTRA_LOG );
131
- }
132
- if ( !defined( 'WP_FAIL2BAN_PINGBACK_ERROR_LOG' ) ) {
133
- define( 'WP_FAIL2BAN_PINGBACK_ERROR_LOG', DEFAULT_WP_FAIL2BAN_PINGBACK_ERROR_LOG );
134
- }
135
 
136
  if ( is_admin() ) {
137
- require 'admin/summary.php';
138
  } else {
139
  require dirname( __FILE__ ) . '/feature/lib.php';
140
  /**
@@ -143,26 +92,30 @@ if ( is_admin() ) {
143
 
144
  if ( !function_exists( __NAMESPACE__ . '\\wp_login' ) ) {
145
  /**
146
- * @since 4.1.0 Add REST support
147
- * @since 3.5.0 Refactored for unit testing
 
 
148
  * @since 1.0.0
149
  *
150
  * @param string $user_login
 
151
  */
152
  function wp_login( $user_login, $user )
153
  {
154
  global $wp_xmlrpc_server ;
155
 
156
  if ( defined( 'REST_REQUEST' ) ) {
157
- $action = 'WPF2B_ACTION_REST_AUTH_OK';
158
  } elseif ( $wp_xmlrpc_server ) {
159
- $action = 'WPF2B_ACTION_XMLRPC_AUTH_OK';
160
  } else {
161
- $action = 'WPF2B_ACTION_AUTH_OK';
162
  }
163
 
164
  openlog();
165
  syslog( LOG_INFO, "Accepted password for {$user_login}" );
 
166
  // @codeCoverageIgnoreEnd
167
  }
168
 
@@ -180,8 +133,11 @@ if ( is_admin() ) {
180
 
181
  if ( !function_exists( __NAMESPACE__ . '\\wp_login_failed' ) ) {
182
  /**
183
- * @since 4.1.0 Add REST support
184
- * @since 3.5.0 Refactored for unit testing
 
 
 
185
  * @since 1.0.0
186
  *
187
  * @param string $username
@@ -198,19 +154,21 @@ if ( is_admin() ) {
198
  global $wp_xmlrpc_server ;
199
 
200
  if ( defined( 'REST_REQUEST' ) ) {
201
- $action = 'WPF2B_ACTION_REST_AUTH_FAIL';
202
  $msg = 'REST a';
203
  } elseif ( $wp_xmlrpc_server ) {
204
- $action = 'WPF2B_ACTION_XMLRPC_AUTH_FAIL';
205
  $msg = 'XML-RPC a';
206
  } else {
207
- $action = 'WPF2B_ACTION_AUTH_FAIL';
208
  $msg = 'A';
209
  }
210
 
211
- $msg .= ( wp_cache_get( $username, 'userlogins' ) ? "uthentication failure for {$username}" : "uthentication attempt for unknown user {$username}" );
 
212
  openlog();
213
  syslog( LOG_NOTICE, $msg );
 
214
  // @codeCoverageIgnoreEnd
215
  }
216
 
@@ -220,7 +178,7 @@ if ( is_admin() ) {
220
  /**
221
  * Comments
222
  *
223
- * @since 4.0.0 Refactored
224
  * @since 3.5.0
225
  */
226
  if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS' ) && true === WP_FAIL2BAN_LOG_COMMENTS ) {
@@ -229,7 +187,7 @@ if ( is_admin() ) {
229
  /**
230
  * Password
231
  *
232
- * @since 4.0.0 Refactored
233
  * @since 3.5.0
234
  */
235
  if ( defined( 'WP_FAIL2BAN_LOG_PASSWORD_REQUEST' ) && true === WP_FAIL2BAN_LOG_PASSWORD_REQUEST ) {
@@ -238,7 +196,7 @@ if ( is_admin() ) {
238
  /**
239
  * Spam
240
  *
241
- * @since 4.0.0 Refactored
242
  * @since 3.5.0
243
  */
244
  if ( defined( 'WP_FAIL2BAN_LOG_SPAM' ) && true === WP_FAIL2BAN_LOG_SPAM ) {
@@ -247,7 +205,7 @@ if ( is_admin() ) {
247
  /**
248
  * Users
249
  *
250
- * @since 4.0.0 Refactored
251
  * @since 2.0.0
252
  */
253
  if ( defined( 'WP_FAIL2BAN_BLOCKED_USERS' ) && '' < WP_FAIL2BAN_BLOCKED_USERS ) {
@@ -256,7 +214,7 @@ if ( is_admin() ) {
256
  /**
257
  * User enumeration
258
  *
259
- * @since 4.0.0 Refactored
260
  * @since 2.1.0
261
  */
262
  if ( defined( 'WP_FAIL2BAN_BLOCK_USER_ENUMERATION' ) && true === WP_FAIL2BAN_BLOCK_USER_ENUMERATION ) {
@@ -265,7 +223,7 @@ if ( is_admin() ) {
265
  /**
266
  * XML-RPC
267
  *
268
- * @since 4.0.0 Refactored
269
  * @since 3.0.0
270
  */
271
  if ( defined( 'XMLRPC_REQUEST' ) && true === XMLRPC_REQUEST ) {
11
  if ( !defined( 'ABSPATH' ) ) {
12
  exit;
13
  }
14
+ require_once dirname( __FILE__ ) . '/lib/constants.php';
15
+ require_once dirname( __FILE__ ) . '/lib/loader.php';
16
+ require_once dirname( __FILE__ ) . '/lib/defaults.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  register_activation_hook( WP_FAIL2BAN_FILE, function () {
18
  foreach ( get_mu_plugins() as $plugin => $data ) {
19
 
20
+ if ( 0 === strpos( $data['Name'], 'WP fail2ban' ) ) {
21
  $wp_f2b_ver = substr( WP_FAIL2BAN_VER, 0, strrpos( WP_FAIL2BAN_VER, '.' ) );
22
  $wpf2b = 'WP fail2ban';
23
+ $error_msg = sprintf( __( '<h1>Cannot activate %s</h1>' ), $wpf2b );
24
  $mu_file = WPMU_PLUGIN_DIR . '/' . $plugin;
25
 
26
  if ( is_link( $mu_file ) ) {
27
 
28
  if ( false === ($link = readlink( $mu_file )) || false === ($path = realpath( $mu_file )) ) {
29
+ $h3 = __( 'A broken symbolic link was found in <tt>mu-plugins</tt>:' );
30
  $error_msg .= <<<__ERROR__
31
+ <h3>{$h3}</h3>
32
  <p><tt>{$mu_file}</tt></p>
33
  __ERROR__;
34
  } elseif ( WP_FAIL2BAN_FILE == $path ) {
36
  } else {
37
  $mu_file = str_replace( '/', '/<wbr>', $mu_file );
38
  $mu_file = substr( $mu_file, strlen( WPMU_PLUGIN_DIR ) - 1 );
39
+ $h3 = __( 'A conflicting symbolic link was found in <tt>mu-plugins</tt>:' );
40
  $error_msg .= <<<__ERROR__
41
+ <h3>{$h3}</h3>
42
  <style>
43
  table { text-align: center; }
44
  td { width: 50%; }
65
  } else {
66
  $mu_file = str_replace( '/', '/<wbr>', $mu_file );
67
  $mu_file = substr( $mu_file, strlen( WPMU_PLUGIN_DIR ) - 1 );
68
+ $h3 = __( 'A conflicting file was found in <tt>mu-plugins</tt>:' );
69
  $error_msg .= <<<__ERROR__
70
+ <h3>{$h3}</h3>
71
  <p><tt>{$mu_file}</tt></p>
72
  __ERROR__;
73
  }
74
 
75
+ $error_msg .= sprintf( __( '<p>Please see the <a href="%s" target="_blank">documentation</a> for how to configure %s for <tt>mu-plugins</tt>.</p>' ), "https://docs.wp-fail2ban.com/en/{$wp_f2b_ver}/configuration.html#mu-plugins-support", $wpf2b );
76
+ $error_msg .= sprintf( __( '<p>Click <a href="%s">here</a> to return to the plugins page.</p>' ), admin_url( 'plugins.php' ) );
77
  deactivate_plugins( plugin_basename( WP_FAIL2BAN_FILE ) );
78
  wp_die( $error_msg );
79
  }
80
 
81
  }
82
  } );
83
+ require dirname( __FILE__ ) . '/feature/plugins.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
  if ( is_admin() ) {
86
+ require 'admin/admin.php';
87
  } else {
88
  require dirname( __FILE__ ) . '/feature/lib.php';
89
  /**
92
 
93
  if ( !function_exists( __NAMESPACE__ . '\\wp_login' ) ) {
94
  /**
95
+ * Hook: wp_login
96
+ *
97
+ * @since 4.1.0 Add REST support
98
+ * @since 3.5.0 Refactored for unit testing
99
  * @since 1.0.0
100
  *
101
  * @param string $user_login
102
+ * @param mixed $user
103
  */
104
  function wp_login( $user_login, $user )
105
  {
106
  global $wp_xmlrpc_server ;
107
 
108
  if ( defined( 'REST_REQUEST' ) ) {
109
+ $event = 'WPF2B_EVENT_REST_AUTH_OK';
110
  } elseif ( $wp_xmlrpc_server ) {
111
+ $event = 'WPF2B_EVENT_XMLRPC_AUTH_OK';
112
  } else {
113
+ $event = 'WPF2B_EVENT_AUTH_OK';
114
  }
115
 
116
  openlog();
117
  syslog( LOG_INFO, "Accepted password for {$user_login}" );
118
+ closelog();
119
  // @codeCoverageIgnoreEnd
120
  }
121
 
133
 
134
  if ( !function_exists( __NAMESPACE__ . '\\wp_login_failed' ) ) {
135
  /**
136
+ * Hook: wp_login_failed
137
+ *
138
+ * @since 4.2.0 Change username check
139
+ * @since 4.1.0 Add REST support
140
+ * @since 3.5.0 Refactored for unit testing
141
  * @since 1.0.0
142
  *
143
  * @param string $username
154
  global $wp_xmlrpc_server ;
155
 
156
  if ( defined( 'REST_REQUEST' ) ) {
157
+ $event = 'WPF2B_EVENT_REST_AUTH_FAIL';
158
  $msg = 'REST a';
159
  } elseif ( $wp_xmlrpc_server ) {
160
+ $event = 'WPF2B_EVENT_XMLRPC_AUTH_FAIL';
161
  $msg = 'XML-RPC a';
162
  } else {
163
+ $event = 'WPF2B_EVENT_AUTH_FAIL';
164
  $msg = 'A';
165
  }
166
 
167
+ $username = trim( $username );
168
+ $msg .= ( wp_cache_get( $username, 'useremail' ) || wp_cache_get( sanitize_user( $username ), 'userlogins' ) ? "uthentication failure for {$username}" : "uthentication attempt for unknown user {$username}" );
169
  openlog();
170
  syslog( LOG_NOTICE, $msg );
171
+ closelog();
172
  // @codeCoverageIgnoreEnd
173
  }
174
 
178
  /**
179
  * Comments
180
  *
181
+ * @since 4.0.0 Refactored
182
  * @since 3.5.0
183
  */
184
  if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS' ) && true === WP_FAIL2BAN_LOG_COMMENTS ) {
187
  /**
188
  * Password
189
  *
190
+ * @since 4.0.0 Refactored
191
  * @since 3.5.0
192
  */
193
  if ( defined( 'WP_FAIL2BAN_LOG_PASSWORD_REQUEST' ) && true === WP_FAIL2BAN_LOG_PASSWORD_REQUEST ) {
196
  /**
197
  * Spam
198
  *
199
+ * @since 4.0.0 Refactored
200
  * @since 3.5.0
201
  */
202
  if ( defined( 'WP_FAIL2BAN_LOG_SPAM' ) && true === WP_FAIL2BAN_LOG_SPAM ) {
205
  /**
206
  * Users
207
  *
208
+ * @since 4.0.0 Refactored
209
  * @since 2.0.0
210
  */
211
  if ( defined( 'WP_FAIL2BAN_BLOCKED_USERS' ) && '' < WP_FAIL2BAN_BLOCKED_USERS ) {
214
  /**
215
  * User enumeration
216
  *
217
+ * @since 4.0.0 Refactored
218
  * @since 2.1.0
219
  */
220
  if ( defined( 'WP_FAIL2BAN_BLOCK_USER_ENUMERATION' ) && true === WP_FAIL2BAN_BLOCK_USER_ENUMERATION ) {
223
  /**
224
  * XML-RPC
225
  *
226
+ * @since 4.0.0 Refactored
227
  * @since 3.0.0
228
  */
229
  if ( defined( 'XMLRPC_REQUEST' ) && true === XMLRPC_REQUEST ) {
wp-fail2ban.php CHANGED
@@ -5,7 +5,7 @@
5
  * Plugin URI: https://wp-fail2ban.com/
6
  * Description: Write a myriad of WordPress events to syslog for integration with fail2ban.
7
  * Text Domain: wp-fail2ban
8
- * Version: 4.1.0
9
  * Author: Charles Lecklider
10
  * Author URI: https://charles.lecklider.org/
11
  * License: GPLv2
@@ -30,6 +30,8 @@
30
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31
  */
32
  /**
 
 
33
  * @package wp-fail2ban
34
  */
35
  namespace org\lecklider\charles\wordpress\wp_fail2ban;
@@ -37,20 +39,27 @@ namespace org\lecklider\charles\wordpress\wp_fail2ban;
37
  /**
38
  * @since 4.0.5
39
  */
40
- define( 'WP_FAIL2BAN_VER', '4.1.0' );
41
  define( 'WP_FAIL2BAN_FILE', __FILE__ );
 
 
 
 
 
42
  /**
43
  * Freemius integration
44
  *
45
  * @since 4.0.0
46
  */
47
 
48
- if ( function_exists( 'wf_fs' ) ) {
49
  // @codeCoverageIgnoreStart
50
  wf_fs()->set_basename( false, __FILE__ );
51
  return;
52
  } else {
53
- // Create a helper function for easy SDK access.
 
 
54
  function wf_fs()
55
  {
56
  global $wf_fs ;
@@ -64,7 +73,7 @@ if ( function_exists( 'wf_fs' ) ) {
64
  'type' => 'plugin',
65
  'public_key' => 'pk_146d2c2a5bee3b157e43501ef8682',
66
  'is_premium' => false,
67
- 'has_addons' => false,
68
  'has_paid_plans' => true,
69
  'trial' => array(
70
  'days' => 7,
@@ -72,11 +81,8 @@ if ( function_exists( 'wf_fs' ) ) {
72
  ),
73
  'menu' => array(
74
  'slug' => 'wp-fail2ban',
75
- 'first-path' => 'options-general.php?page=wp-fail2ban&tab=welcome',
76
  'support' => false,
77
- 'parent' => array(
78
- 'slug' => 'options-general.php',
79
- ),
80
  ),
81
  'is_live' => true,
82
  ) );
5
  * Plugin URI: https://wp-fail2ban.com/
6
  * Description: Write a myriad of WordPress events to syslog for integration with fail2ban.
7
  * Text Domain: wp-fail2ban
8
+ * Version: 4.2.1
9
  * Author: Charles Lecklider
10
  * Author URI: https://charles.lecklider.org/
11
  * License: GPLv2
30
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31
  */
32
  /**
33
+ * WP fail2ban
34
+ *
35
  * @package wp-fail2ban
36
  */
37
  namespace org\lecklider\charles\wordpress\wp_fail2ban;
39
  /**
40
  * @since 4.0.5
41
  */
42
+ define( 'WP_FAIL2BAN_VER', '4.2.1' );
43
  define( 'WP_FAIL2BAN_FILE', __FILE__ );
44
+ /**
45
+ * @since 4.2.0
46
+ */
47
+ global $wp_fail2ban ;
48
+ $wp_fail2ban['plugins'] = [];
49
  /**
50
  * Freemius integration
51
  *
52
  * @since 4.0.0
53
  */
54
 
55
+ if ( function_exists( __NAMESPACE__ . '\\wf_fs' ) ) {
56
  // @codeCoverageIgnoreStart
57
  wf_fs()->set_basename( false, __FILE__ );
58
  return;
59
  } else {
60
+ /**
61
+ * Create a helper function for easy SDK access.
62
+ */
63
  function wf_fs()
64
  {
65
  global $wf_fs ;
73
  'type' => 'plugin',
74
  'public_key' => 'pk_146d2c2a5bee3b157e43501ef8682',
75
  'is_premium' => false,
76
+ 'has_addons' => true,
77
  'has_paid_plans' => true,
78
  'trial' => array(
79
  'days' => 7,
81
  ),
82
  'menu' => array(
83
  'slug' => 'wp-fail2ban',
84
+ 'first-path' => 'admin.php?page=wp-fail2ban',
85
  'support' => false,
 
 
 
86
  ),
87
  'is_live' => true,
88
  ) );