Version Description
- Fix is_pw_outside_ascii() to permit spaces.
- In multisite mode, send notifications to network admin, not blog admin.
- Add "Notifiations To" setting for admins to specify the email addresses the failure and breach notifications get sent to. (Request #1560)
- Clarify that the Change All Passwords link just goes to the UI.
- Get all unit tests to pass when mbstring isn't enabled.
- Internationalize the unit tests.
- Rename admin.inc to admin.php.
- Rename temporary files holding actual test results. (Bug #1552 redux)
- Unit tests pass using PHP 5.4.5-dev, 5.3.16-dev, and 5.2.18-dev.
- Tested under WordPress 3.4.1 using regular and multisite.
- Also tested on Windows 7 using PHP 5.4.5 and WordPress 3.4.1.
Download this release
Release Info
Developer | convissor |
Plugin | Login Security Solution |
Version | 0.21.0 |
Comparing to | |
See all releases |
Code changes from version 0.20.2 to 0.21.0
- admin.inc → admin.php +39 -10
- languages/login-security-solution.pot +386 -60
- languages/makemos.sh +9 -0
- languages/makepot.sh +10 -0
- login-security-solution.php +29 -14
- readme.txt +28 -7
- tests/Accessor.php +1 -1
- tests/LoginErrorsTest.php +14 -4
- tests/LoginMessageTest.php +1 -1
- tests/PasswordChangeTest.php +15 -3
- tests/PasswordValidationTest.php +30 -19
- tests/TestCase.php +10 -3
- tests/VerifiedIpTest.php +6 -2
- tests/expected/{LoginFailTest--test_process_login_fail__post_threshold → en_US/LoginFailTest--test_process_login_fail__post_threshold} +6 -5
- tests/expected/{LoginFailTest--test_wp_login__post_breach_threshold → en_US/LoginFailTest--test_wp_login__post_breach_threshold} +6 -5
- tests/expected/{LoginFailTest--test_wp_login__post_breach_threshold_verified_ip → en_US/LoginFailTest--test_wp_login__post_breach_threshold_verified_ip} +6 -5
- tests/expected/fr_FR/LoginFailTest--test_process_login_fail__post_threshold +15 -0
- tests/expected/fr_FR/LoginFailTest--test_wp_login__post_breach_threshold +15 -0
- tests/expected/fr_FR/LoginFailTest--test_wp_login__post_breach_threshold_verified_ip +25 -0
admin.inc → admin.php
RENAMED
@@ -123,7 +123,7 @@ class login_security_solution_admin extends login_security_solution {
|
|
123 |
|
124 |
// NON-STANDARD: This is for the password change page.
|
125 |
$this->option_pw_force_change_name = self::ID . '-pw-force-change-done';
|
126 |
-
$this->text_pw_force_change = __(
|
127 |
$this->text_button_remind = __("Do not remind me about this", self::ID);
|
128 |
$this->text_button_require = __("Require All Passwords Be Changed", self::ID);
|
129 |
}
|
@@ -286,6 +286,12 @@ class login_security_solution_admin extends login_security_solution {
|
|
286 |
'text' => sprintf(__("How many matching login failures should it take to get into this (%d - %d second) Delay Tier? Must be > Delay Tier 2.", self::ID), 25, 60),
|
287 |
'type' => 'int',
|
288 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
289 |
'login_fail_notify' => array(
|
290 |
'group' => 'login',
|
291 |
'label' => __("Failure Notification", self::ID),
|
@@ -480,7 +486,7 @@ class login_security_solution_admin extends login_security_solution {
|
|
480 |
*
|
481 |
* @uses login_security_solution_admin::input_radio() for rendering
|
482 |
* radio buttons
|
483 |
-
* @uses login_security_solution_admin::
|
484 |
* text input boxes
|
485 |
*/
|
486 |
public function __call($name, $params) {
|
@@ -492,7 +498,10 @@ class login_security_solution_admin extends login_security_solution {
|
|
492 |
$this->input_radio($name);
|
493 |
break;
|
494 |
case 'int':
|
495 |
-
$this->
|
|
|
|
|
|
|
496 |
break;
|
497 |
}
|
498 |
}
|
@@ -517,10 +526,10 @@ class login_security_solution_admin extends login_security_solution {
|
|
517 |
}
|
518 |
|
519 |
/**
|
520 |
-
* Renders the text input boxes
|
521 |
* @return void
|
522 |
*/
|
523 |
-
protected function
|
524 |
echo '<input type="text" size="3" name="'
|
525 |
. $this->hsc_utf8($this->option_name)
|
526 |
. '[' . $this->hsc_utf8($name) . ']"'
|
@@ -530,6 +539,21 @@ class login_security_solution_admin extends login_security_solution {
|
|
530 |
. $this->options_default[$name] . '.');
|
531 |
}
|
532 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
533 |
/**
|
534 |
* Validates the user input
|
535 |
*
|
@@ -856,15 +880,20 @@ class login_security_solution_admin extends login_security_solution {
|
|
856 |
echo '</strong></p>';
|
857 |
|
858 |
echo '<p><strong>';
|
859 |
-
echo $this->hsc_utf8(__("Speaking of which, do YOU have a strong password? Make sure by changing yours
|
|
|
|
|
|
|
|
|
860 |
echo '</strong></p>';
|
861 |
|
862 |
-
echo '<p><strong
|
|
|
863 |
. '?page=' . $this->hsc_utf8($this->option_pw_force_change_name)
|
864 |
-
. '">' . $this->hsc_utf8($this->text_pw_force_change)
|
865 |
-
|
866 |
|
867 |
-
echo "</div>\n";
|
868 |
}
|
869 |
|
870 |
/**
|
123 |
|
124 |
// NON-STANDARD: This is for the password change page.
|
125 |
$this->option_pw_force_change_name = self::ID . '-pw-force-change-done';
|
126 |
+
$this->text_pw_force_change = __("Change All Passwords", self::ID);
|
127 |
$this->text_button_remind = __("Do not remind me about this", self::ID);
|
128 |
$this->text_button_require = __("Require All Passwords Be Changed", self::ID);
|
129 |
}
|
286 |
'text' => sprintf(__("How many matching login failures should it take to get into this (%d - %d second) Delay Tier? Must be > Delay Tier 2.", self::ID), 25, 60),
|
287 |
'type' => 'int',
|
288 |
),
|
289 |
+
'admin_email' => array(
|
290 |
+
'group' => 'login',
|
291 |
+
'label' => __("Notifications To", self::ID),
|
292 |
+
'text' => __("The email address(es) the failure and breach notifications should be sent to. For multiple addresses, separate them with commas. WordPress' 'admin_email' setting is used if none is provided here.", self::ID),
|
293 |
+
'type' => 'string',
|
294 |
+
),
|
295 |
'login_fail_notify' => array(
|
296 |
'group' => 'login',
|
297 |
'label' => __("Failure Notification", self::ID),
|
486 |
*
|
487 |
* @uses login_security_solution_admin::input_radio() for rendering
|
488 |
* radio buttons
|
489 |
+
* @uses login_security_solution_admin::input_int() for rendering
|
490 |
* text input boxes
|
491 |
*/
|
492 |
public function __call($name, $params) {
|
498 |
$this->input_radio($name);
|
499 |
break;
|
500 |
case 'int':
|
501 |
+
$this->input_int($name);
|
502 |
+
break;
|
503 |
+
case 'string':
|
504 |
+
$this->input_string($name);
|
505 |
break;
|
506 |
}
|
507 |
}
|
526 |
}
|
527 |
|
528 |
/**
|
529 |
+
* Renders the text input boxes for editing integers
|
530 |
* @return void
|
531 |
*/
|
532 |
+
protected function input_int($name) {
|
533 |
echo '<input type="text" size="3" name="'
|
534 |
. $this->hsc_utf8($this->option_name)
|
535 |
. '[' . $this->hsc_utf8($name) . ']"'
|
539 |
. $this->options_default[$name] . '.');
|
540 |
}
|
541 |
|
542 |
+
/**
|
543 |
+
* Renders the text input boxes for editing strings
|
544 |
+
* @return void
|
545 |
+
*/
|
546 |
+
protected function input_string($name) {
|
547 |
+
echo '<input type="text" size="75" name="'
|
548 |
+
. $this->hsc_utf8($this->option_name)
|
549 |
+
. '[' . $this->hsc_utf8($name) . ']"'
|
550 |
+
. ' value="' . $this->hsc_utf8($this->options[$name]) . '" /> ';
|
551 |
+
echo '<br />';
|
552 |
+
echo $this->hsc_utf8($this->fields[$name]['text']
|
553 |
+
. ' ' . __('Default:', self::ID) . ' '
|
554 |
+
. $this->options_default[$name] . '.');
|
555 |
+
}
|
556 |
+
|
557 |
/**
|
558 |
* Validates the user input
|
559 |
*
|
880 |
echo '</strong></p>';
|
881 |
|
882 |
echo '<p><strong>';
|
883 |
+
echo $this->hsc_utf8(__("Speaking of which, do YOU have a strong password? Make sure by changing yours too.", self::ID));
|
884 |
+
echo '</strong></p>';
|
885 |
+
|
886 |
+
echo '<p><strong>';
|
887 |
+
echo $this->hsc_utf8(__("The following link leads to a user interface where you can either require all passwords to be reset or disable this notice.", self::ID));
|
888 |
echo '</strong></p>';
|
889 |
|
890 |
+
echo '<p><strong>';
|
891 |
+
echo '<a href="' . $this->hsc_utf8($this->page_options)
|
892 |
. '?page=' . $this->hsc_utf8($this->option_pw_force_change_name)
|
893 |
+
. '">' . $this->hsc_utf8($this->text_pw_force_change) . '</a>';
|
894 |
+
echo '</strong></p>';
|
895 |
|
896 |
+
echo "\n</div>\n";
|
897 |
}
|
898 |
|
899 |
/**
|
languages/login-security-solution.pot
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
# This file is distributed under the same license as the Login Security Solution package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
-
"Project-Id-Version: Login Security Solution 0.
|
6 |
-
"Report-Msgid-Bugs-To: http://wordpress.org/
|
7 |
-
"POT-Creation-Date: 2012-07
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
@@ -12,108 +12,432 @@ msgstr ""
|
|
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14 |
|
15 |
-
#:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
msgid "Invalid username or password."
|
17 |
msgstr ""
|
18 |
|
19 |
-
#: login-security-solution.php:
|
20 |
-
#: tests/LoginErrorsTest.php:
|
21 |
msgid "Password reset is not allowed for this user"
|
22 |
msgstr ""
|
23 |
|
24 |
-
#: login-security-solution.php:
|
25 |
msgid "It has been over %d minutes since your last action."
|
26 |
msgstr ""
|
27 |
|
28 |
-
#: login-security-solution.php:
|
29 |
msgid "Please log back in."
|
30 |
msgstr ""
|
31 |
|
32 |
-
#: login-security-solution.php:
|
33 |
msgid "The grace period for changing your password has expired."
|
34 |
msgstr ""
|
35 |
|
36 |
-
#: login-security-solution.php:
|
37 |
msgid "Please submit this form to reset your password."
|
38 |
msgstr ""
|
39 |
|
40 |
-
#: login-security-solution.php:
|
41 |
msgid "Your password must be reset."
|
42 |
msgstr ""
|
43 |
|
44 |
-
#: login-security-solution.php:
|
45 |
msgid "Please submit this form to reset it."
|
46 |
msgstr ""
|
47 |
|
48 |
-
#: login-security-solution.php:
|
49 |
msgid "Your password has expired. Please log and change it."
|
50 |
msgstr ""
|
51 |
|
52 |
-
#: login-security-solution.php:
|
53 |
msgid "We provide a %d minute grace period to do so."
|
54 |
msgstr ""
|
55 |
|
56 |
-
#: login-security-solution.php:
|
57 |
msgid "The password you tried to create is not secure. Please try again."
|
58 |
msgstr ""
|
59 |
|
60 |
-
#: login-security-solution.php:
|
61 |
#: tests/LoginMessageTest.php:144
|
62 |
msgid "The site is undergoing maintenance."
|
63 |
msgstr ""
|
64 |
|
65 |
-
#: login-security-solution.php:
|
66 |
#: tests/LoginMessageTest.php:145
|
67 |
msgid "Please try again later."
|
68 |
msgstr ""
|
69 |
|
70 |
-
#: login-security-solution.php:
|
71 |
msgid ""
|
72 |
"The password should either be: A) at least %d characters long and contain "
|
73 |
"upper and lower case letters plus numbers and punctuation, or B) at least %d "
|
74 |
"characters long."
|
75 |
msgstr ""
|
76 |
|
77 |
-
#: login-security-solution.php:
|
78 |
msgid "Passwords can not be reused."
|
79 |
msgstr ""
|
80 |
|
81 |
-
#: login-security-solution.php:
|
82 |
msgid "ERROR"
|
83 |
msgstr ""
|
84 |
|
85 |
-
#: login-security-solution.php:
|
86 |
-
msgid "
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
#: login-security-solution.php:962
|
94 |
-
msgid "Username %5d %s"
|
95 |
-
msgstr ""
|
96 |
-
|
97 |
-
#: login-security-solution.php:964
|
98 |
-
msgid "Password MD5 %5d %s"
|
99 |
msgstr ""
|
100 |
|
101 |
-
#: login-security-solution.php:
|
102 |
msgid "POTENTIAL INTRUSION AT %s"
|
103 |
msgstr ""
|
104 |
|
105 |
-
#: login-security-solution.php:
|
106 |
msgid "Your website, %s, may have been broken in to."
|
107 |
msgstr ""
|
108 |
|
109 |
-
#: login-security-solution.php:
|
110 |
msgid ""
|
111 |
"Someone just logged in using the following components. Prior to that, some "
|
112 |
"combination of those components were a part of %d failed attempts to log in "
|
113 |
"during the past %d minutes:"
|
114 |
msgstr ""
|
115 |
|
116 |
-
#: login-security-solution.php:
|
117 |
msgid ""
|
118 |
"The user's current IP address is one they have verified with your site in "
|
119 |
"the past. Therefore, the user will NOT be required to confirm their "
|
@@ -121,103 +445,105 @@ msgid ""
|
|
121 |
"just in case this actually was a breach."
|
122 |
msgstr ""
|
123 |
|
124 |
-
#: login-security-solution.php:
|
125 |
msgid ""
|
126 |
"The user has been logged out and will be required to confirm their identity "
|
127 |
"via the password reset functionality."
|
128 |
msgstr ""
|
129 |
|
130 |
-
#: login-security-solution.php:
|
131 |
msgid ""
|
132 |
"Someone just logged into your '%s' account at %s. Was it you that logged "
|
133 |
"in? We are asking because the site is being attacked."
|
134 |
msgstr ""
|
135 |
|
136 |
-
#: login-security-solution.php:
|
137 |
msgid "IF IT WAS NOT YOU, please do the following right away:"
|
138 |
msgstr ""
|
139 |
|
140 |
-
#: login-security-solution.php:
|
141 |
msgid "1) Log into %s and change your password."
|
142 |
msgstr ""
|
143 |
|
144 |
-
#: login-security-solution.php:
|
145 |
msgid "2) Send an email to %s letting them know it was not you who logged in."
|
146 |
msgstr ""
|
147 |
|
148 |
-
#: login-security-solution.php:
|
149 |
msgid "ATTACK HAPPENING TO %s"
|
150 |
msgstr ""
|
151 |
|
152 |
-
#: login-security-solution.php:
|
153 |
msgid "Your website, %s, is undergoing a brute force attack."
|
154 |
msgstr ""
|
155 |
|
156 |
-
#: login-security-solution.php:
|
157 |
msgid ""
|
158 |
"There have been at least %d failed attempts to log in during the past %d "
|
159 |
"minutes that used one or more of the following components:"
|
160 |
msgstr ""
|
161 |
|
162 |
-
#: login-security-solution.php:
|
163 |
msgid ""
|
164 |
"The %s plugin for WordPress is repelling the attack by making their login "
|
165 |
"failures take a very long time."
|
166 |
msgstr ""
|
167 |
|
168 |
-
#: login-security-solution.php:
|
169 |
msgid "Password not set."
|
170 |
msgstr ""
|
171 |
|
172 |
-
#: login-security-solution.php:
|
173 |
msgid "Passwords must be strings."
|
174 |
msgstr ""
|
175 |
|
176 |
-
#: login-security-solution.php:
|
177 |
msgid "Passwords must use ASCII characters."
|
178 |
msgstr ""
|
179 |
|
180 |
-
#: login-security-solution.php:
|
|
|
181 |
msgid "Password is too short."
|
182 |
msgstr ""
|
183 |
|
184 |
-
#: login-security-solution.php:
|
185 |
msgid "Passwords must either contain numbers or be %d characters long."
|
186 |
msgstr ""
|
187 |
|
188 |
-
#: login-security-solution.php:
|
189 |
msgid ""
|
190 |
"Passwords must either contain punctuation marks / symbols or be %d "
|
191 |
"characters long."
|
192 |
msgstr ""
|
193 |
|
194 |
-
#: login-security-solution.php:
|
195 |
msgid ""
|
196 |
"Passwords must either contain upper-case and lower-case letters or be %d "
|
197 |
"characters long."
|
198 |
msgstr ""
|
199 |
|
200 |
-
#: login-security-solution.php:
|
201 |
msgid "Passwords can't be sequential keys."
|
202 |
msgstr ""
|
203 |
|
204 |
-
#: login-security-solution.php:
|
205 |
msgid "Passwords can't have that many sequential characters."
|
206 |
msgstr ""
|
207 |
|
208 |
-
#: login-security-solution.php:
|
|
|
209 |
msgid "Passwords can't contain user data."
|
210 |
msgstr ""
|
211 |
|
212 |
-
#: login-security-solution.php:
|
213 |
msgid "Passwords can't contain site info."
|
214 |
msgstr ""
|
215 |
|
216 |
-
#: login-security-solution.php:
|
217 |
msgid "Password is too common."
|
218 |
msgstr ""
|
219 |
|
220 |
-
#: login-security-solution.php:
|
221 |
msgid "Passwords can't be variations of dictionary words."
|
222 |
msgstr ""
|
223 |
|
2 |
# This file is distributed under the same license as the Login Security Solution package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
+
"Project-Id-Version: Login Security Solution 0.21.0\n"
|
6 |
+
"Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/login-security-solution\n"
|
7 |
+
"POT-Creation-Date: 2012-08-07 15:10:37+00:00\n"
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14 |
|
15 |
+
#: admin.php:112 admin.php:362
|
16 |
+
msgid "Settings"
|
17 |
+
msgstr ""
|
18 |
+
|
19 |
+
#: admin.php:126
|
20 |
+
msgid "Change All Passwords"
|
21 |
+
msgstr ""
|
22 |
+
|
23 |
+
#: admin.php:127
|
24 |
+
msgid "Do not remind me about this"
|
25 |
+
msgstr ""
|
26 |
+
|
27 |
+
#: admin.php:128
|
28 |
+
msgid "Require All Passwords Be Changed"
|
29 |
+
msgstr ""
|
30 |
+
|
31 |
+
#: admin.php:143
|
32 |
+
msgid ""
|
33 |
+
"%s must be activated via the Network Admin interface when WordPress is in "
|
34 |
+
"multistie network mode."
|
35 |
+
msgstr ""
|
36 |
+
|
37 |
+
#: admin.php:249
|
38 |
+
msgid "Idle Timeout"
|
39 |
+
msgstr ""
|
40 |
+
|
41 |
+
#: admin.php:250
|
42 |
+
msgid ""
|
43 |
+
"Close inactive sessions after this many minutes. 0 disables this feature."
|
44 |
+
msgstr ""
|
45 |
+
|
46 |
+
#: admin.php:255
|
47 |
+
msgid "Maintenance Mode"
|
48 |
+
msgstr ""
|
49 |
+
|
50 |
+
#: admin.php:256
|
51 |
+
msgid ""
|
52 |
+
"Disable logins from users who are not administrators and disable posting of "
|
53 |
+
"comments?"
|
54 |
+
msgstr ""
|
55 |
+
|
56 |
+
#: admin.php:258
|
57 |
+
msgid "Off, let all users log in."
|
58 |
+
msgstr ""
|
59 |
+
|
60 |
+
#: admin.php:259
|
61 |
+
msgid "On, disable comments and only let administrators log in."
|
62 |
+
msgstr ""
|
63 |
+
|
64 |
+
#: admin.php:263
|
65 |
+
msgid "Deactivation"
|
66 |
+
msgstr ""
|
67 |
+
|
68 |
+
#: admin.php:264
|
69 |
+
msgid ""
|
70 |
+
"Should deactivating the plugin remove all of the plugin's data and settings?"
|
71 |
+
msgstr ""
|
72 |
+
|
73 |
+
#: admin.php:266
|
74 |
+
msgid "No, preserve the data for future use."
|
75 |
+
msgstr ""
|
76 |
+
|
77 |
+
#: admin.php:267
|
78 |
+
msgid "Yes, delete the damn data."
|
79 |
+
msgstr ""
|
80 |
+
|
81 |
+
#: admin.php:272
|
82 |
+
msgid "Match Time"
|
83 |
+
msgstr ""
|
84 |
+
|
85 |
+
#: admin.php:273
|
86 |
+
msgid "How far back, in minutes, should login failures look for matching data?"
|
87 |
+
msgstr ""
|
88 |
+
|
89 |
+
#: admin.php:278
|
90 |
+
msgid "Delay Tier 2"
|
91 |
+
msgstr ""
|
92 |
+
|
93 |
+
#: admin.php:279
|
94 |
+
msgid ""
|
95 |
+
"How many matching login failures should it take to get into this (%d - %d "
|
96 |
+
"second) Delay Tier? Must be >= %d."
|
97 |
+
msgstr ""
|
98 |
+
|
99 |
+
#: admin.php:285
|
100 |
+
msgid "Delay Tier 3"
|
101 |
+
msgstr ""
|
102 |
+
|
103 |
+
#: admin.php:286
|
104 |
+
msgid ""
|
105 |
+
"How many matching login failures should it take to get into this (%d - %d "
|
106 |
+
"second) Delay Tier? Must be > Delay Tier 2."
|
107 |
+
msgstr ""
|
108 |
+
|
109 |
+
#: admin.php:291
|
110 |
+
msgid "Notifications To"
|
111 |
+
msgstr ""
|
112 |
+
|
113 |
+
#: admin.php:292
|
114 |
+
msgid ""
|
115 |
+
"The email address(es) the failure and breach notifications should be sent "
|
116 |
+
"to. For multiple addresses, separate them with commas. WordPress' "
|
117 |
+
"'admin_email' setting is used if none is provided here."
|
118 |
+
msgstr ""
|
119 |
+
|
120 |
+
#: admin.php:297
|
121 |
+
msgid "Failure Notification"
|
122 |
+
msgstr ""
|
123 |
+
|
124 |
+
#: admin.php:298
|
125 |
+
msgid ""
|
126 |
+
"Notify the administrator upon every x matching login failures. 0 disables "
|
127 |
+
"this feature."
|
128 |
+
msgstr ""
|
129 |
+
|
130 |
+
#: admin.php:303
|
131 |
+
msgid "Breach Notification"
|
132 |
+
msgstr ""
|
133 |
+
|
134 |
+
#: admin.php:304
|
135 |
+
msgid ""
|
136 |
+
"Notify the administrator if a successful login uses data matching x login "
|
137 |
+
"failures. 0 disables this feature."
|
138 |
+
msgstr ""
|
139 |
+
|
140 |
+
#: admin.php:309
|
141 |
+
msgid "Breach Email Confirm"
|
142 |
+
msgstr ""
|
143 |
+
|
144 |
+
#: admin.php:310
|
145 |
+
msgid ""
|
146 |
+
"If a successful login uses data matching x login failures, immediately log "
|
147 |
+
"the user out and require them to use WordPress' lost password process. 0 "
|
148 |
+
"disables this feature."
|
149 |
+
msgstr ""
|
150 |
+
|
151 |
+
#: admin.php:316
|
152 |
+
msgid "Length"
|
153 |
+
msgstr ""
|
154 |
+
|
155 |
+
#: admin.php:317
|
156 |
+
msgid "How long must passwords be? Must be >= %d."
|
157 |
+
msgstr ""
|
158 |
+
|
159 |
+
#: admin.php:323
|
160 |
+
msgid "Complexity Exemption"
|
161 |
+
msgstr ""
|
162 |
+
|
163 |
+
#: admin.php:324
|
164 |
+
msgid ""
|
165 |
+
"How long must passwords be to be exempt from the complexity requirements? "
|
166 |
+
"Must be >= %d."
|
167 |
+
msgstr ""
|
168 |
+
|
169 |
+
#: admin.php:330
|
170 |
+
msgid "Aging"
|
171 |
+
msgstr ""
|
172 |
+
|
173 |
+
#: admin.php:331
|
174 |
+
msgid ""
|
175 |
+
"How many days old can a password be before requiring it be changed? Not "
|
176 |
+
"recommended. 0 disables this feature."
|
177 |
+
msgstr ""
|
178 |
+
|
179 |
+
#: admin.php:336
|
180 |
+
msgid "Grace Period"
|
181 |
+
msgstr ""
|
182 |
+
|
183 |
+
#: admin.php:337
|
184 |
+
msgid ""
|
185 |
+
"How many minutes should a user have to change their password once they know "
|
186 |
+
"it has expired? Must be >= %d."
|
187 |
+
msgstr ""
|
188 |
+
|
189 |
+
#: admin.php:343
|
190 |
+
msgid "History"
|
191 |
+
msgstr ""
|
192 |
+
|
193 |
+
#: admin.php:344
|
194 |
+
msgid ""
|
195 |
+
"How many passwords should be remembered? Prevents reuse of old passwords. 0 "
|
196 |
+
"disables this feature."
|
197 |
+
msgstr ""
|
198 |
+
|
199 |
+
#: admin.php:405
|
200 |
+
msgid "Login Failure Policies"
|
201 |
+
msgstr ""
|
202 |
+
|
203 |
+
#: admin.php:411
|
204 |
+
msgid "Password Policies"
|
205 |
+
msgstr ""
|
206 |
+
|
207 |
+
#: admin.php:417
|
208 |
+
msgid "Miscellaneous Policies"
|
209 |
+
msgstr ""
|
210 |
+
|
211 |
+
#: admin.php:467
|
212 |
+
msgid ""
|
213 |
+
"This plugin stores the IP address, username and password for each failed log "
|
214 |
+
"in attempt."
|
215 |
+
msgstr ""
|
216 |
+
|
217 |
+
#: admin.php:469
|
218 |
+
msgid ""
|
219 |
+
"The data from future login failures are compared against the historical data."
|
220 |
+
msgstr ""
|
221 |
+
|
222 |
+
#: admin.php:471
|
223 |
+
msgid ""
|
224 |
+
"If any of the data points match, the plugin delays printing out the failure "
|
225 |
+
"message."
|
226 |
+
msgstr ""
|
227 |
+
|
228 |
+
#: admin.php:473
|
229 |
+
msgid ""
|
230 |
+
"The goal is for the responses to take so long that the attackers give up and "
|
231 |
+
"go find an easier target."
|
232 |
+
msgstr ""
|
233 |
+
|
234 |
+
#: admin.php:475
|
235 |
+
msgid "The length of the delay is broken up into three tiers."
|
236 |
+
msgstr ""
|
237 |
+
|
238 |
+
#: admin.php:477
|
239 |
+
msgid "The amount of the delay increases in higher tiers."
|
240 |
+
msgstr ""
|
241 |
+
|
242 |
+
#: admin.php:479
|
243 |
+
msgid ""
|
244 |
+
"The delay time within each tier is randomized to complicate profiling by "
|
245 |
+
"attackers."
|
246 |
+
msgstr ""
|
247 |
+
|
248 |
+
#: admin.php:538 admin.php:553
|
249 |
+
msgid "Default:"
|
250 |
+
msgstr ""
|
251 |
+
|
252 |
+
#: admin.php:577
|
253 |
+
msgid "must be >= '%s',"
|
254 |
+
msgstr ""
|
255 |
+
|
256 |
+
#: admin.php:578
|
257 |
+
msgid "so we used the default value instead."
|
258 |
+
msgstr ""
|
259 |
+
|
260 |
+
#: admin.php:611
|
261 |
+
msgid "must be an integer,"
|
262 |
+
msgstr ""
|
263 |
+
|
264 |
+
#: admin.php:714
|
265 |
+
msgid "There may be cases where everyone's password should be reset."
|
266 |
+
msgstr ""
|
267 |
+
|
268 |
+
#: admin.php:716
|
269 |
+
msgid "This page, provided by the %s plugin, offers that functionality."
|
270 |
+
msgstr ""
|
271 |
+
|
272 |
+
#: admin.php:720
|
273 |
+
msgid ""
|
274 |
+
"Submitting this form sets a flag that forces all users to utilize WordPress' "
|
275 |
+
"built in password reset functionality."
|
276 |
+
msgstr ""
|
277 |
+
|
278 |
+
#: admin.php:722
|
279 |
+
msgid ""
|
280 |
+
"Users who are presently logged in will be logged out the next time they view "
|
281 |
+
"a page that requires authentication."
|
282 |
+
msgstr ""
|
283 |
+
|
284 |
+
#: admin.php:734
|
285 |
+
msgid ""
|
286 |
+
"Confirm that you want to force all users to change their passwords by "
|
287 |
+
"checking this box, then click the button, below."
|
288 |
+
msgstr ""
|
289 |
+
|
290 |
+
#: admin.php:753
|
291 |
+
msgid "No thanks. I know what I'm doing. Please don't remind me about this."
|
292 |
+
msgstr ""
|
293 |
+
|
294 |
+
#: admin.php:785
|
295 |
+
msgid ""
|
296 |
+
"You have checked a box that does not correspond with the button you pressed. "
|
297 |
+
"Please check and press buttons inside the same section."
|
298 |
+
msgstr ""
|
299 |
+
|
300 |
+
#: admin.php:787
|
301 |
+
msgid ""
|
302 |
+
"Please confirm that you really want to do this. Put a check in the '%s' box "
|
303 |
+
"before hitting the submit button."
|
304 |
+
msgstr ""
|
305 |
+
|
306 |
+
#: admin.php:803 admin.php:823
|
307 |
+
msgid "Success!"
|
308 |
+
msgstr ""
|
309 |
+
|
310 |
+
#: admin.php:855
|
311 |
+
msgid ""
|
312 |
+
"WARNING: The site is in maintenance mode. DO NOT TOUCH ANYTHING! Your "
|
313 |
+
"changes may get overwritten!"
|
314 |
+
msgstr ""
|
315 |
+
|
316 |
+
#: admin.php:879
|
317 |
+
msgid ""
|
318 |
+
"You have not asked your users to change their passwords since the plugin was "
|
319 |
+
"activated. Most users have weak passwords. This plugin's password policies "
|
320 |
+
"protect your site from brute force attacks. Please improve security for "
|
321 |
+
"everyone on the Internet by making all users pick new, strong, passwords."
|
322 |
+
msgstr ""
|
323 |
+
|
324 |
+
#: admin.php:883
|
325 |
+
msgid ""
|
326 |
+
"Speaking of which, do YOU have a strong password? Make sure by changing "
|
327 |
+
"yours too."
|
328 |
+
msgstr ""
|
329 |
+
|
330 |
+
#: admin.php:887
|
331 |
+
msgid ""
|
332 |
+
"The following link leads to a user interface where you can either require "
|
333 |
+
"all passwords to be reset or disable this notice."
|
334 |
+
msgstr ""
|
335 |
+
|
336 |
+
#: admin.php:917
|
337 |
+
msgid "You do not have sufficient permissions to access this page."
|
338 |
+
msgstr ""
|
339 |
+
|
340 |
+
#: admin.php:923
|
341 |
+
msgid "$user_ID variable not set. Another plugin is misbehaving."
|
342 |
+
msgstr ""
|
343 |
+
|
344 |
+
#: login-security-solution.php:500 tests/LoginErrorsTest.php:97
|
345 |
+
#: tests/LoginErrorsTest.php:111
|
346 |
msgid "Invalid username or password."
|
347 |
msgstr ""
|
348 |
|
349 |
+
#: login-security-solution.php:506 tests/LoginErrorsTest.php:125
|
350 |
+
#: tests/LoginErrorsTest.php:139
|
351 |
msgid "Password reset is not allowed for this user"
|
352 |
msgstr ""
|
353 |
|
354 |
+
#: login-security-solution.php:531 tests/LoginMessageTest.php:66
|
355 |
msgid "It has been over %d minutes since your last action."
|
356 |
msgstr ""
|
357 |
|
358 |
+
#: login-security-solution.php:532 tests/LoginMessageTest.php:67
|
359 |
msgid "Please log back in."
|
360 |
msgstr ""
|
361 |
|
362 |
+
#: login-security-solution.php:535 tests/LoginMessageTest.php:77
|
363 |
msgid "The grace period for changing your password has expired."
|
364 |
msgstr ""
|
365 |
|
366 |
+
#: login-security-solution.php:536 tests/LoginMessageTest.php:78
|
367 |
msgid "Please submit this form to reset your password."
|
368 |
msgstr ""
|
369 |
|
370 |
+
#: login-security-solution.php:539 tests/LoginMessageTest.php:88
|
371 |
msgid "Your password must be reset."
|
372 |
msgstr ""
|
373 |
|
374 |
+
#: login-security-solution.php:540 tests/LoginMessageTest.php:89
|
375 |
msgid "Please submit this form to reset it."
|
376 |
msgstr ""
|
377 |
|
378 |
+
#: login-security-solution.php:543 tests/LoginMessageTest.php:104
|
379 |
msgid "Your password has expired. Please log and change it."
|
380 |
msgstr ""
|
381 |
|
382 |
+
#: login-security-solution.php:544 tests/LoginMessageTest.php:105
|
383 |
msgid "We provide a %d minute grace period to do so."
|
384 |
msgstr ""
|
385 |
|
386 |
+
#: login-security-solution.php:547 tests/LoginMessageTest.php:115
|
387 |
msgid "The password you tried to create is not secure. Please try again."
|
388 |
msgstr ""
|
389 |
|
390 |
+
#: login-security-solution.php:553 tests/LoginMessageTest.php:129
|
391 |
#: tests/LoginMessageTest.php:144
|
392 |
msgid "The site is undergoing maintenance."
|
393 |
msgstr ""
|
394 |
|
395 |
+
#: login-security-solution.php:554 tests/LoginMessageTest.php:130
|
396 |
#: tests/LoginMessageTest.php:145
|
397 |
msgid "Please try again later."
|
398 |
msgstr ""
|
399 |
|
400 |
+
#: login-security-solution.php:625
|
401 |
msgid ""
|
402 |
"The password should either be: A) at least %d characters long and contain "
|
403 |
"upper and lower case letters plus numbers and punctuation, or B) at least %d "
|
404 |
"characters long."
|
405 |
msgstr ""
|
406 |
|
407 |
+
#: login-security-solution.php:659 tests/PasswordChangeTest.php:277
|
408 |
msgid "Passwords can not be reused."
|
409 |
msgstr ""
|
410 |
|
411 |
+
#: login-security-solution.php:834
|
412 |
msgid "ERROR"
|
413 |
msgstr ""
|
414 |
|
415 |
+
#: login-security-solution.php:971
|
416 |
+
msgid ""
|
417 |
+
"\n"
|
418 |
+
"Component Count Value from Current Attempt\n"
|
419 |
+
"------------------------ ----- --------------------------------\n"
|
420 |
+
"Network IP %5d %s\n"
|
421 |
+
"Username %5d %s\n"
|
422 |
+
"Password MD5 %5d %s\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
423 |
msgstr ""
|
424 |
|
425 |
+
#: login-security-solution.php:1776 login-security-solution.php:1813
|
426 |
msgid "POTENTIAL INTRUSION AT %s"
|
427 |
msgstr ""
|
428 |
|
429 |
+
#: login-security-solution.php:1780
|
430 |
msgid "Your website, %s, may have been broken in to."
|
431 |
msgstr ""
|
432 |
|
433 |
+
#: login-security-solution.php:1783
|
434 |
msgid ""
|
435 |
"Someone just logged in using the following components. Prior to that, some "
|
436 |
"combination of those components were a part of %d failed attempts to log in "
|
437 |
"during the past %d minutes:"
|
438 |
msgstr ""
|
439 |
|
440 |
+
#: login-security-solution.php:1789
|
441 |
msgid ""
|
442 |
"The user's current IP address is one they have verified with your site in "
|
443 |
"the past. Therefore, the user will NOT be required to confirm their "
|
445 |
"just in case this actually was a breach."
|
446 |
msgstr ""
|
447 |
|
448 |
+
#: login-security-solution.php:1791
|
449 |
msgid ""
|
450 |
"The user has been logged out and will be required to confirm their identity "
|
451 |
"via the password reset functionality."
|
452 |
msgstr ""
|
453 |
|
454 |
+
#: login-security-solution.php:1817
|
455 |
msgid ""
|
456 |
"Someone just logged into your '%s' account at %s. Was it you that logged "
|
457 |
"in? We are asking because the site is being attacked."
|
458 |
msgstr ""
|
459 |
|
460 |
+
#: login-security-solution.php:1818
|
461 |
msgid "IF IT WAS NOT YOU, please do the following right away:"
|
462 |
msgstr ""
|
463 |
|
464 |
+
#: login-security-solution.php:1819
|
465 |
msgid "1) Log into %s and change your password."
|
466 |
msgstr ""
|
467 |
|
468 |
+
#: login-security-solution.php:1820
|
469 |
msgid "2) Send an email to %s letting them know it was not you who logged in."
|
470 |
msgstr ""
|
471 |
|
472 |
+
#: login-security-solution.php:1846
|
473 |
msgid "ATTACK HAPPENING TO %s"
|
474 |
msgstr ""
|
475 |
|
476 |
+
#: login-security-solution.php:1850
|
477 |
msgid "Your website, %s, is undergoing a brute force attack."
|
478 |
msgstr ""
|
479 |
|
480 |
+
#: login-security-solution.php:1853
|
481 |
msgid ""
|
482 |
"There have been at least %d failed attempts to log in during the past %d "
|
483 |
"minutes that used one or more of the following components:"
|
484 |
msgstr ""
|
485 |
|
486 |
+
#: login-security-solution.php:1858
|
487 |
msgid ""
|
488 |
"The %s plugin for WordPress is repelling the attack by making their login "
|
489 |
"failures take a very long time."
|
490 |
msgstr ""
|
491 |
|
492 |
+
#: login-security-solution.php:2209 tests/PasswordValidationTest.php:450
|
493 |
msgid "Password not set."
|
494 |
msgstr ""
|
495 |
|
496 |
+
#: login-security-solution.php:2224 tests/PasswordValidationTest.php:461
|
497 |
msgid "Passwords must be strings."
|
498 |
msgstr ""
|
499 |
|
500 |
+
#: login-security-solution.php:2242 tests/PasswordValidationTest.php:474
|
501 |
msgid "Passwords must use ASCII characters."
|
502 |
msgstr ""
|
503 |
|
504 |
+
#: login-security-solution.php:2261 tests/PasswordChangeTest.php:310
|
505 |
+
#: tests/PasswordValidationTest.php:491 tests/PasswordValidationTest.php:505
|
506 |
msgid "Password is too short."
|
507 |
msgstr ""
|
508 |
|
509 |
+
#: login-security-solution.php:2270 tests/PasswordValidationTest.php:531
|
510 |
msgid "Passwords must either contain numbers or be %d characters long."
|
511 |
msgstr ""
|
512 |
|
513 |
+
#: login-security-solution.php:2279 tests/PasswordValidationTest.php:518
|
514 |
msgid ""
|
515 |
"Passwords must either contain punctuation marks / symbols or be %d "
|
516 |
"characters long."
|
517 |
msgstr ""
|
518 |
|
519 |
+
#: login-security-solution.php:2288 tests/PasswordValidationTest.php:544
|
520 |
msgid ""
|
521 |
"Passwords must either contain upper-case and lower-case letters or be %d "
|
522 |
"characters long."
|
523 |
msgstr ""
|
524 |
|
525 |
+
#: login-security-solution.php:2298 tests/PasswordValidationTest.php:557
|
526 |
msgid "Passwords can't be sequential keys."
|
527 |
msgstr ""
|
528 |
|
529 |
+
#: login-security-solution.php:2307 tests/PasswordValidationTest.php:570
|
530 |
msgid "Passwords can't have that many sequential characters."
|
531 |
msgstr ""
|
532 |
|
533 |
+
#: login-security-solution.php:2323 tests/PasswordValidationTest.php:583
|
534 |
+
#: tests/PasswordValidationTest.php:596
|
535 |
msgid "Passwords can't contain user data."
|
536 |
msgstr ""
|
537 |
|
538 |
+
#: login-security-solution.php:2334 tests/PasswordValidationTest.php:609
|
539 |
msgid "Passwords can't contain site info."
|
540 |
msgstr ""
|
541 |
|
542 |
+
#: login-security-solution.php:2343 tests/PasswordValidationTest.php:622
|
543 |
msgid "Password is too common."
|
544 |
msgstr ""
|
545 |
|
546 |
+
#: login-security-solution.php:2352 tests/PasswordValidationTest.php:638
|
547 |
msgid "Passwords can't be variations of dictionary words."
|
548 |
msgstr ""
|
549 |
|
languages/makemos.sh
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#! /bin/bash -e
|
2 |
+
|
3 |
+
cd "`dirname "$0"`"
|
4 |
+
|
5 |
+
while read file ; do
|
6 |
+
lang=${file%*.po}
|
7 |
+
echo "Building $lang..."
|
8 |
+
msgfmt -o $lang.mo $lang.po
|
9 |
+
done < <(ls *po)
|
languages/makepot.sh
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#! /bin/bash -e
|
2 |
+
|
3 |
+
cd "`dirname "$0"`/../../makepot"
|
4 |
+
|
5 |
+
svn up
|
6 |
+
|
7 |
+
php -d 'error_reporting=E_ALL^E_STRICT' \
|
8 |
+
makepot.php wp-plugin \
|
9 |
+
../login-security-solution \
|
10 |
+
../login-security-solution/languages/login-security-solution.pot
|
login-security-solution.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
* Description: Requires very strong passwords, repels brute force login attacks, prevents login information disclosures, expires idle sessions, notifies admins of attacks and breaches, permits administrators to disable logins for maintenance or emergency reasons and reset all passwords.
|
7 |
*
|
8 |
* Plugin URI: http://wordpress.org/extend/plugins/login-security-solution/
|
9 |
-
* Version: 0.
|
10 |
* Author: Daniel Convissor
|
11 |
* Author URI: http://www.analysisandsolutions.com/
|
12 |
* License: GPLv2
|
@@ -106,6 +106,7 @@ class login_security_solution {
|
|
106 |
* @var array
|
107 |
*/
|
108 |
protected $options_default = array(
|
|
|
109 |
'deactivate_deletes_data' => 0,
|
110 |
'disable_logins' => 0,
|
111 |
'idle_timeout' => 15,
|
@@ -214,7 +215,7 @@ class login_security_solution {
|
|
214 |
if (is_admin()) {
|
215 |
$this->load_plugin_textdomain();
|
216 |
|
217 |
-
require_once dirname(__FILE__) . '/admin.
|
218 |
$admin = new login_security_solution_admin;
|
219 |
|
220 |
if (is_multisite()) {
|
@@ -835,6 +836,18 @@ class login_security_solution {
|
|
835 |
return "<strong>$error</strong>: $message";
|
836 |
}
|
837 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
838 |
/**
|
839 |
* Obtains the IP address from $_SERVER['REMOTE_ADDR']
|
840 |
*
|
@@ -955,14 +968,16 @@ class login_security_solution {
|
|
955 |
protected function get_notify_counts($network_ip, $user_name, $pass_md5,
|
956 |
$fails)
|
957 |
{
|
958 |
-
return __("
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
|
|
|
|
|
966 |
}
|
967 |
|
968 |
/**
|
@@ -1444,7 +1459,7 @@ class login_security_solution {
|
|
1444 |
* @return bool
|
1445 |
*/
|
1446 |
protected function is_pw_outside_ascii($pw) {
|
1447 |
-
return !preg_match('/^[!-~]+$/u', $pw);
|
1448 |
}
|
1449 |
|
1450 |
/**
|
@@ -1755,7 +1770,7 @@ class login_security_solution {
|
|
1755 |
{
|
1756 |
$this->load_plugin_textdomain();
|
1757 |
|
1758 |
-
$to = $this->sanitize_whitespace(
|
1759 |
|
1760 |
$blog = get_option('blogname');
|
1761 |
$subject = sprintf(__("POTENTIAL INTRUSION AT %s", self::ID), $blog);
|
@@ -1802,7 +1817,7 @@ class login_security_solution {
|
|
1802 |
sprintf(__("Someone just logged into your '%s' account at %s. Was it you that logged in? We are asking because the site is being attacked.", self::ID), $user->user_login, get_option('siteurl')) . "\n\n"
|
1803 |
. __("IF IT WAS NOT YOU, please do the following right away:", self::ID) . "\n\n"
|
1804 |
. sprintf(__("1) Log into %s and change your password.", self::ID), wp_login_url()) . "\n\n"
|
1805 |
-
. sprintf(__("2) Send an email to %s letting them know it was not you who logged in.", self::ID),
|
1806 |
|
1807 |
return wp_mail($to, $subject, $message);
|
1808 |
}
|
@@ -1825,7 +1840,7 @@ class login_security_solution {
|
|
1825 |
{
|
1826 |
$this->load_plugin_textdomain();
|
1827 |
|
1828 |
-
$to = $this->sanitize_whitespace(
|
1829 |
|
1830 |
$blog = get_option('blogname');
|
1831 |
$subject = sprintf(__("ATTACK HAPPENING TO %s", self::ID), $blog);
|
6 |
* Description: Requires very strong passwords, repels brute force login attacks, prevents login information disclosures, expires idle sessions, notifies admins of attacks and breaches, permits administrators to disable logins for maintenance or emergency reasons and reset all passwords.
|
7 |
*
|
8 |
* Plugin URI: http://wordpress.org/extend/plugins/login-security-solution/
|
9 |
+
* Version: 0.21.0
|
10 |
* Author: Daniel Convissor
|
11 |
* Author URI: http://www.analysisandsolutions.com/
|
12 |
* License: GPLv2
|
106 |
* @var array
|
107 |
*/
|
108 |
protected $options_default = array(
|
109 |
+
'admin_email' => '',
|
110 |
'deactivate_deletes_data' => 0,
|
111 |
'disable_logins' => 0,
|
112 |
'idle_timeout' => 15,
|
215 |
if (is_admin()) {
|
216 |
$this->load_plugin_textdomain();
|
217 |
|
218 |
+
require_once dirname(__FILE__) . '/admin.php';
|
219 |
$admin = new login_security_solution_admin;
|
220 |
|
221 |
if (is_multisite()) {
|
836 |
return "<strong>$error</strong>: $message";
|
837 |
}
|
838 |
|
839 |
+
/**
|
840 |
+
* Obtains the email addresses the notifications should go to
|
841 |
+
* @return string
|
842 |
+
*/
|
843 |
+
protected function get_admin_email() {
|
844 |
+
$email = $this->options['admin_email'];
|
845 |
+
if (!$email) {
|
846 |
+
$email = get_site_option('admin_email');
|
847 |
+
}
|
848 |
+
return $email;
|
849 |
+
}
|
850 |
+
|
851 |
/**
|
852 |
* Obtains the IP address from $_SERVER['REMOTE_ADDR']
|
853 |
*
|
968 |
protected function get_notify_counts($network_ip, $user_name, $pass_md5,
|
969 |
$fails)
|
970 |
{
|
971 |
+
return sprintf(__("
|
972 |
+
Component Count Value from Current Attempt
|
973 |
+
------------------------ ----- --------------------------------
|
974 |
+
Network IP %5d %s
|
975 |
+
Username %5d %s
|
976 |
+
Password MD5 %5d %s
|
977 |
+
", self::ID),
|
978 |
+
$fails['network_ip'], $network_ip,
|
979 |
+
$fails['user_name'], $user_name,
|
980 |
+
$fails['pass_md5'], $pass_md5) . "\n";
|
981 |
}
|
982 |
|
983 |
/**
|
1459 |
* @return bool
|
1460 |
*/
|
1461 |
protected function is_pw_outside_ascii($pw) {
|
1462 |
+
return !preg_match('/^[!-~ ]+$/u', $pw);
|
1463 |
}
|
1464 |
|
1465 |
/**
|
1770 |
{
|
1771 |
$this->load_plugin_textdomain();
|
1772 |
|
1773 |
+
$to = $this->sanitize_whitespace($this->get_admin_email());
|
1774 |
|
1775 |
$blog = get_option('blogname');
|
1776 |
$subject = sprintf(__("POTENTIAL INTRUSION AT %s", self::ID), $blog);
|
1817 |
sprintf(__("Someone just logged into your '%s' account at %s. Was it you that logged in? We are asking because the site is being attacked.", self::ID), $user->user_login, get_option('siteurl')) . "\n\n"
|
1818 |
. __("IF IT WAS NOT YOU, please do the following right away:", self::ID) . "\n\n"
|
1819 |
. sprintf(__("1) Log into %s and change your password.", self::ID), wp_login_url()) . "\n\n"
|
1820 |
+
. sprintf(__("2) Send an email to %s letting them know it was not you who logged in.", self::ID), $this->get_admin_email()) . "\n";
|
1821 |
|
1822 |
return wp_mail($to, $subject, $message);
|
1823 |
}
|
1840 |
{
|
1841 |
$this->load_plugin_textdomain();
|
1842 |
|
1843 |
+
$to = $this->sanitize_whitespace($this->get_admin_email());
|
1844 |
|
1845 |
$blog = get_option('blogname');
|
1846 |
$subject = sprintf(__("ATTACK HAPPENING TO %s", self::ID), $blog);
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=danie
|
|
4 |
Tags: login, password, passwords, strength, strong, strong passwords, password strength, idle, timeout, maintenance, security, attack, hack, lock, ban, brute force, brute, force, authentication, auth, cookie, users
|
5 |
Requires at least: 3.3
|
6 |
Tested up to: 3.4.1
|
7 |
-
Stable tag: 0.
|
8 |
|
9 |
Security against brute force attacks by tracking IP, name, password; requiring very strong passwords. Idle timeout. Maintenance mode. Multisite ready!
|
10 |
|
@@ -86,6 +86,7 @@ The tests have caught every password dictionary entry I've tried.
|
|
86 |
* Monitors auth cookie failures
|
87 |
* Clean, documented code
|
88 |
* Unit tests covering 100% of the main class
|
|
|
89 |
|
90 |
|
91 |
= Securing Your WordPress Site is Important =
|
@@ -266,18 +267,38 @@ attacks are fairly easy to initiate these days. If someone really wants to
|
|
266 |
shut down your site, they'll be able to do it without even touching this
|
267 |
plugin's login failure process.
|
268 |
|
269 |
-
= How do developers generate the
|
270 |
|
271 |
-
|
272 |
-
then `cd` into that directory and run:
|
273 |
|
274 |
-
|
275 |
-
|
276 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
|
278 |
|
279 |
== Changelog ==
|
280 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
281 |
= 0.20.2 =
|
282 |
* Ugh, update the translation pot file.
|
283 |
|
4 |
Tags: login, password, passwords, strength, strong, strong passwords, password strength, idle, timeout, maintenance, security, attack, hack, lock, ban, brute force, brute, force, authentication, auth, cookie, users
|
5 |
Requires at least: 3.3
|
6 |
Tested up to: 3.4.1
|
7 |
+
Stable tag: 0.21.0
|
8 |
|
9 |
Security against brute force attacks by tracking IP, name, password; requiring very strong passwords. Idle timeout. Maintenance mode. Multisite ready!
|
10 |
|
86 |
* Monitors auth cookie failures
|
87 |
* Clean, documented code
|
88 |
* Unit tests covering 100% of the main class
|
89 |
+
* Internationalized unit tests
|
90 |
|
91 |
|
92 |
= Securing Your WordPress Site is Important =
|
267 |
shut down your site, they'll be able to do it without even touching this
|
268 |
plugin's login failure process.
|
269 |
|
270 |
+
= How do developers generate the translation files? =
|
271 |
|
272 |
+
To update the POT file, do this:
|
|
|
273 |
|
274 |
+
* cd into the directory above this one.
|
275 |
+
* `svn checkout http://i18n.svn.wordpress.org/tools/trunk/ makepot`
|
276 |
+
* `cd login-security-solution/languages`
|
277 |
+
* `./makepot.sh`
|
278 |
+
|
279 |
+
To produce the machine readable translations used by WordPress' gettext
|
280 |
+
implementation, use the script I made for generating all of the .mo files:
|
281 |
+
|
282 |
+
* `cd languages`
|
283 |
+
* `./makemos.sh`
|
284 |
|
285 |
|
286 |
== Changelog ==
|
287 |
|
288 |
+
= 0.21.0 =
|
289 |
+
* Fix is_pw_outside_ascii() to permit spaces.
|
290 |
+
* In multisite mode, send notifications to network admin, not blog admin.
|
291 |
+
* Add "Notifiations To" setting for admins to specify the email addresses
|
292 |
+
the failure and breach notifications get sent to. (Request #1560)
|
293 |
+
* Clarify that the Change All Passwords link just goes to the UI.
|
294 |
+
* Get all unit tests to pass when mbstring isn't enabled.
|
295 |
+
* Internationalize the unit tests.
|
296 |
+
* Rename admin.inc to admin.php.
|
297 |
+
* Rename temporary files holding actual test results. (Bug #1552 redux)
|
298 |
+
* Unit tests pass using PHP 5.4.5-dev, 5.3.16-dev, and 5.2.18-dev.
|
299 |
+
* Tested under WordPress 3.4.1 using regular and multisite.
|
300 |
+
* Also tested on Windows 7 using PHP 5.4.5 and WordPress 3.4.1.
|
301 |
+
|
302 |
= 0.20.2 =
|
303 |
* Ugh, update the translation pot file.
|
304 |
|
tests/Accessor.php
CHANGED
@@ -18,7 +18,7 @@ require_once dirname(dirname(__FILE__)) . '/login-security-solution.php';
|
|
18 |
/**
|
19 |
* Get the admin class
|
20 |
*/
|
21 |
-
require_once dirname(dirname(__FILE__)) . '/admin.
|
22 |
|
23 |
// Remove automatically created object.
|
24 |
unset($GLOBALS['login_security_solution']);
|
18 |
/**
|
19 |
* Get the admin class
|
20 |
*/
|
21 |
+
require_once dirname(dirname(__FILE__)) . '/admin.php';
|
22 |
|
23 |
// Remove automatically created object.
|
24 |
unset($GLOBALS['login_security_solution']);
|
tests/LoginErrorsTest.php
CHANGED
@@ -60,6 +60,10 @@ class LoginErrorsTest extends TestCase {
|
|
60 |
unset($_REQUEST['action']);
|
61 |
}
|
62 |
|
|
|
|
|
|
|
|
|
63 |
|
64 |
public function test_login_errors__nothing() {
|
65 |
global $errors, $user_name;
|
@@ -89,7 +93,8 @@ class LoginErrorsTest extends TestCase {
|
|
89 |
|
90 |
$actual = self::$lss->login_errors('input');
|
91 |
|
92 |
-
$this->assertEquals(
|
|
|
93 |
$actual, 'Output should have been modified.');
|
94 |
$this->assertArrayNotHasKey('log', $_POST, "POST log should be unset.");
|
95 |
}
|
@@ -102,7 +107,8 @@ class LoginErrorsTest extends TestCase {
|
|
102 |
|
103 |
$actual = self::$lss->login_errors('input');
|
104 |
|
105 |
-
$this->assertEquals(
|
|
|
106 |
$actual, 'Output should have been modified.');
|
107 |
$this->assertArrayNotHasKey('log', $_POST, "POST log should be unset.");
|
108 |
}
|
@@ -114,7 +120,9 @@ class LoginErrorsTest extends TestCase {
|
|
114 |
|
115 |
$actual = self::$lss->login_errors('input');
|
116 |
|
117 |
-
|
|
|
|
|
118 |
$actual, 'Output should have been modified.');
|
119 |
$this->assertArrayHasKey('log', $_POST, "POST log shouldn't be touched.");
|
120 |
}
|
@@ -126,7 +134,9 @@ class LoginErrorsTest extends TestCase {
|
|
126 |
|
127 |
$actual = self::$lss->login_errors('input');
|
128 |
|
129 |
-
|
|
|
|
|
130 |
$actual, 'Output should have been modified.');
|
131 |
$this->assertArrayHasKey('log', $_POST, "POST log shouldn't be touched.");
|
132 |
}
|
60 |
unset($_REQUEST['action']);
|
61 |
}
|
62 |
|
63 |
+
protected function err($message) {
|
64 |
+
return self::$lss->hsc_utf8($message);
|
65 |
+
}
|
66 |
+
|
67 |
|
68 |
public function test_login_errors__nothing() {
|
69 |
global $errors, $user_name;
|
93 |
|
94 |
$actual = self::$lss->login_errors('input');
|
95 |
|
96 |
+
$this->assertEquals(
|
97 |
+
$this->err(__("Invalid username or password.", self::ID)),
|
98 |
$actual, 'Output should have been modified.');
|
99 |
$this->assertArrayNotHasKey('log', $_POST, "POST log should be unset.");
|
100 |
}
|
107 |
|
108 |
$actual = self::$lss->login_errors('input');
|
109 |
|
110 |
+
$this->assertEquals(
|
111 |
+
$this->err(__("Invalid username or password.", self::ID)),
|
112 |
$actual, 'Output should have been modified.');
|
113 |
$this->assertArrayNotHasKey('log', $_POST, "POST log should be unset.");
|
114 |
}
|
120 |
|
121 |
$actual = self::$lss->login_errors('input');
|
122 |
|
123 |
+
// This text is lifted directly from WordPress.
|
124 |
+
$this->assertEquals(
|
125 |
+
$this->err(__('Password reset is not allowed for this user')),
|
126 |
$actual, 'Output should have been modified.');
|
127 |
$this->assertArrayHasKey('log', $_POST, "POST log shouldn't be touched.");
|
128 |
}
|
134 |
|
135 |
$actual = self::$lss->login_errors('input');
|
136 |
|
137 |
+
// This text is lifted directly from WordPress.
|
138 |
+
$this->assertEquals(
|
139 |
+
$this->err(__('Password reset is not allowed for this user')),
|
140 |
$actual, 'Output should have been modified.');
|
141 |
$this->assertArrayHasKey('log', $_POST, "POST log shouldn't be touched.");
|
142 |
}
|
tests/LoginMessageTest.php
CHANGED
@@ -31,7 +31,7 @@ class LoginMessageTest extends TestCase {
|
|
31 |
|
32 |
public function ours($ours) {
|
33 |
return '<p class="login message">'
|
34 |
-
.
|
35 |
}
|
36 |
|
37 |
public function test_login_message__unset() {
|
31 |
|
32 |
public function ours($ours) {
|
33 |
return '<p class="login message">'
|
34 |
+
. self::$lss->hsc_utf8($ours) . '</p>';
|
35 |
}
|
36 |
|
37 |
public function test_login_message__unset() {
|
tests/PasswordChangeTest.php
CHANGED
@@ -33,7 +33,11 @@ class PasswordChangeTest extends TestCase {
|
|
33 |
parent::$db_needed = true;
|
34 |
parent::set_up_before_class();
|
35 |
|
36 |
-
|
|
|
|
|
|
|
|
|
37 |
self::$pass_2 = '!AJd81aasjk2@';
|
38 |
self::$hash_1 = wp_hash_password(self::$pass_1);
|
39 |
self::$hash_2 = wp_hash_password(self::$pass_2);
|
@@ -48,6 +52,10 @@ class PasswordChangeTest extends TestCase {
|
|
48 |
$options['pw_reuse_count'] = 3;
|
49 |
self::$lss->options = $options;
|
50 |
|
|
|
|
|
|
|
|
|
51 |
self::$lss->set_pw_force_change($this->user->ID);
|
52 |
$actual = self::$lss->get_pw_force_change($this->user->ID);
|
53 |
$this->assertTrue($actual, 'Problem setting up force change.');
|
@@ -74,6 +82,10 @@ class PasswordChangeTest extends TestCase {
|
|
74 |
$this->assertGreaterThan(0, $actual, 'Grace period should not be cleared.');
|
75 |
}
|
76 |
|
|
|
|
|
|
|
|
|
77 |
|
78 |
/*
|
79 |
* HASHES / REUSED
|
@@ -262,7 +274,7 @@ class PasswordChangeTest extends TestCase {
|
|
262 |
$actual = self::$lss->user_profile_update_errors($errors, 1, $this->user);
|
263 |
$this->assertFalse($actual, 'Bad return value.');
|
264 |
$this->assertEquals(
|
265 |
-
"
|
266 |
$errors->get_error_message()
|
267 |
);
|
268 |
|
@@ -295,7 +307,7 @@ class PasswordChangeTest extends TestCase {
|
|
295 |
$actual = self::$lss->user_profile_update_errors($errors, 0, $this->user);
|
296 |
$this->assertFalse($actual, 'Bad return value.');
|
297 |
$this->assertEquals(
|
298 |
-
"
|
299 |
$errors->get_error_message()
|
300 |
);
|
301 |
|
33 |
parent::$db_needed = true;
|
34 |
parent::set_up_before_class();
|
35 |
|
36 |
+
if (extension_loaded('mbstring')) {
|
37 |
+
self::$pass_1 = self::USER_PASS;
|
38 |
+
} else {
|
39 |
+
self::$pass_1 = 'Some ASCII Only PW 4 You!';
|
40 |
+
}
|
41 |
self::$pass_2 = '!AJd81aasjk2@';
|
42 |
self::$hash_1 = wp_hash_password(self::$pass_1);
|
43 |
self::$hash_2 = wp_hash_password(self::$pass_2);
|
52 |
$options['pw_reuse_count'] = 3;
|
53 |
self::$lss->options = $options;
|
54 |
|
55 |
+
if (!extension_loaded('mbstring')) {
|
56 |
+
$this->user->user_pass = self::$pass_1;
|
57 |
+
}
|
58 |
+
|
59 |
self::$lss->set_pw_force_change($this->user->ID);
|
60 |
$actual = self::$lss->get_pw_force_change($this->user->ID);
|
61 |
$this->assertTrue($actual, 'Problem setting up force change.');
|
82 |
$this->assertGreaterThan(0, $actual, 'Grace period should not be cleared.');
|
83 |
}
|
84 |
|
85 |
+
protected function err($message) {
|
86 |
+
return self::$lss->err($message);
|
87 |
+
}
|
88 |
+
|
89 |
|
90 |
/*
|
91 |
* HASHES / REUSED
|
274 |
$actual = self::$lss->user_profile_update_errors($errors, 1, $this->user);
|
275 |
$this->assertFalse($actual, 'Bad return value.');
|
276 |
$this->assertEquals(
|
277 |
+
$this->err(__("Passwords can not be reused.", self::ID)),
|
278 |
$errors->get_error_message()
|
279 |
);
|
280 |
|
307 |
$actual = self::$lss->user_profile_update_errors($errors, 0, $this->user);
|
308 |
$this->assertFalse($actual, 'Bad return value.');
|
309 |
$this->assertEquals(
|
310 |
+
$this->err(__("Password is too short.", self::ID)),
|
311 |
$errors->get_error_message()
|
312 |
);
|
313 |
|
tests/PasswordValidationTest.php
CHANGED
@@ -227,6 +227,7 @@ class PasswordValidationTest extends TestCase {
|
|
227 |
$tests = array(
|
228 |
"aba!123",
|
229 |
"aba~123",
|
|
|
230 |
);
|
231 |
foreach ($tests as $pw) {
|
232 |
$actual = self::$lss->is_pw_outside_ascii($pw);
|
@@ -310,9 +311,13 @@ class PasswordValidationTest extends TestCase {
|
|
310 |
"aA1!",
|
311 |
"aAb",
|
312 |
"aA!",
|
313 |
-
"БбƤƥ", // Bicameral UTF-8.
|
314 |
-
"חح", // Unicameral UTF-8.
|
315 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
316 |
foreach ($tests as $pw) {
|
317 |
$actual = self::$lss->is_pw_missing_upper_lower_chars($pw);
|
318 |
$this->assertFalse($actual, "Should have passed: '$pw'");
|
@@ -442,7 +447,7 @@ class PasswordValidationTest extends TestCase {
|
|
442 |
$this->assertFalse($actual,
|
443 |
"password not being set should have failed.");
|
444 |
$this->assertEquals(
|
445 |
-
$this->err("Password not set."),
|
446 |
$errors->get_error_message()
|
447 |
);
|
448 |
}
|
@@ -453,7 +458,7 @@ class PasswordValidationTest extends TestCase {
|
|
453 |
$this->assertFalse($actual,
|
454 |
"'array('abc')' should have failed.");
|
455 |
$this->assertEquals(
|
456 |
-
$this->err("Passwords must be strings."),
|
457 |
$errors->get_error_message()
|
458 |
);
|
459 |
}
|
@@ -466,7 +471,7 @@ class PasswordValidationTest extends TestCase {
|
|
466 |
$this->assertFalse($actual,
|
467 |
"'" . $this->user->user_pass . "' should have failed.");
|
468 |
$this->assertEquals(
|
469 |
-
$this->err("Passwords must use ASCII characters."),
|
470 |
$errors->get_error_message()
|
471 |
);
|
472 |
}
|
@@ -483,7 +488,7 @@ class PasswordValidationTest extends TestCase {
|
|
483 |
$this->assertFalse($actual,
|
484 |
"'" . $this->user->user_pass . "' should have failed.");
|
485 |
$this->assertEquals(
|
486 |
-
$this->err("Password is too short."),
|
487 |
$errors->get_error_message()
|
488 |
);
|
489 |
}
|
@@ -497,7 +502,7 @@ class PasswordValidationTest extends TestCase {
|
|
497 |
$this->assertFalse($actual,
|
498 |
"'" . $this->user->user_pass . "' should have failed.");
|
499 |
$this->assertEquals(
|
500 |
-
$this->err("Password is too short."),
|
501 |
$errors->get_error_message()
|
502 |
);
|
503 |
}
|
@@ -510,7 +515,7 @@ class PasswordValidationTest extends TestCase {
|
|
510 |
$this->assertFalse($actual,
|
511 |
"'" . $this->user->user_pass . "' should have failed.");
|
512 |
$this->assertEquals(
|
513 |
-
$this->err(sprintf("Passwords must either contain punctuation marks / symbols or be %d characters long.", self::$lss->options['pw_complexity_exemption_length'])),
|
514 |
$errors->get_error_message()
|
515 |
);
|
516 |
}
|
@@ -523,7 +528,7 @@ class PasswordValidationTest extends TestCase {
|
|
523 |
$this->assertFalse($actual,
|
524 |
"'" . $this->user->user_pass . "' should have failed.");
|
525 |
$this->assertEquals(
|
526 |
-
$this->err(sprintf("Passwords must either contain numbers or be %d characters long.", self::$lss->options['pw_complexity_exemption_length'])),
|
527 |
$errors->get_error_message()
|
528 |
);
|
529 |
}
|
@@ -536,7 +541,7 @@ class PasswordValidationTest extends TestCase {
|
|
536 |
$this->assertFalse($actual,
|
537 |
"'" . $this->user->user_pass . "' should have failed.");
|
538 |
$this->assertEquals(
|
539 |
-
$this->err(sprintf("Passwords must either contain upper-case and lower-case letters or be %d characters long.", self::$lss->options['pw_complexity_exemption_length'])),
|
540 |
$errors->get_error_message()
|
541 |
);
|
542 |
}
|
@@ -549,7 +554,7 @@ class PasswordValidationTest extends TestCase {
|
|
549 |
$this->assertFalse($actual,
|
550 |
"'" . $this->user->user_pass . "' should have failed.");
|
551 |
$this->assertEquals(
|
552 |
-
$this->err("Passwords can't be sequential keys."),
|
553 |
$errors->get_error_message()
|
554 |
);
|
555 |
}
|
@@ -562,7 +567,7 @@ class PasswordValidationTest extends TestCase {
|
|
562 |
$this->assertFalse($actual,
|
563 |
"'" . $this->user->user_pass . "' should have failed.");
|
564 |
$this->assertEquals(
|
565 |
-
$this->err("Passwords can't have that many sequential characters."),
|
566 |
$errors->get_error_message()
|
567 |
);
|
568 |
}
|
@@ -575,7 +580,7 @@ class PasswordValidationTest extends TestCase {
|
|
575 |
$this->assertFalse($actual,
|
576 |
"'" . $this->user->user_pass . "' should have failed.");
|
577 |
$this->assertEquals(
|
578 |
-
$this->err("Passwords can't contain user data."),
|
579 |
$errors->get_error_message()
|
580 |
);
|
581 |
}
|
@@ -588,7 +593,7 @@ class PasswordValidationTest extends TestCase {
|
|
588 |
$this->assertFalse($actual,
|
589 |
"'" . $this->user->user_pass . "' should have failed.");
|
590 |
$this->assertEquals(
|
591 |
-
$this->err("Passwords can't contain user data."),
|
592 |
$errors->get_error_message()
|
593 |
);
|
594 |
}
|
@@ -601,7 +606,7 @@ class PasswordValidationTest extends TestCase {
|
|
601 |
$this->assertFalse($actual,
|
602 |
"'" . $this->user->user_pass . "' should have failed.");
|
603 |
$this->assertEquals(
|
604 |
-
$this->err("Passwords can't contain site info."),
|
605 |
$errors->get_error_message()
|
606 |
);
|
607 |
}
|
@@ -614,7 +619,7 @@ class PasswordValidationTest extends TestCase {
|
|
614 |
$this->assertFalse($actual,
|
615 |
"'" . $this->user->user_pass . "' should have failed.");
|
616 |
$this->assertEquals(
|
617 |
-
$this->err("Password is too common."),
|
618 |
$errors->get_error_message()
|
619 |
);
|
620 |
}
|
@@ -630,16 +635,21 @@ class PasswordValidationTest extends TestCase {
|
|
630 |
$this->assertFalse($actual,
|
631 |
"'" . $this->user->user_pass . "' should have failed.");
|
632 |
$this->assertEquals(
|
633 |
-
$this->err("Passwords can't be variations of dictionary words."),
|
634 |
$errors->get_error_message()
|
635 |
);
|
636 |
}
|
637 |
|
638 |
public function test_validate_pw__good() {
|
|
|
|
|
|
|
|
|
639 |
$errors = new WP_Error;
|
640 |
$actual = self::$lss->validate_pw($this->user, $errors);
|
641 |
$this->assertTrue($actual,
|
642 |
-
"'" . $this->user->user_pass . "' should have passed
|
|
|
643 |
$this->assertEmpty($errors->get_error_message());
|
644 |
}
|
645 |
|
@@ -649,7 +659,8 @@ class PasswordValidationTest extends TestCase {
|
|
649 |
$errors = new WP_Error;
|
650 |
$actual = self::$lss->validate_pw($this->user, $errors);
|
651 |
$this->assertTrue($actual,
|
652 |
-
"'" . $this->user->user_pass . "' should have passed
|
|
|
653 |
$this->assertEmpty($errors->get_error_message());
|
654 |
}
|
655 |
|
227 |
$tests = array(
|
228 |
"aba!123",
|
229 |
"aba~123",
|
230 |
+
"aba 123",
|
231 |
);
|
232 |
foreach ($tests as $pw) {
|
233 |
$actual = self::$lss->is_pw_outside_ascii($pw);
|
311 |
"aA1!",
|
312 |
"aAb",
|
313 |
"aA!",
|
|
|
|
|
314 |
);
|
315 |
+
|
316 |
+
if (self::$mbstring_available) {
|
317 |
+
$tests[] = "БбƤƥ"; // Bicameral UTF-8.
|
318 |
+
$tests[] = "חح"; // Unicameral UTF-8.
|
319 |
+
}
|
320 |
+
|
321 |
foreach ($tests as $pw) {
|
322 |
$actual = self::$lss->is_pw_missing_upper_lower_chars($pw);
|
323 |
$this->assertFalse($actual, "Should have passed: '$pw'");
|
447 |
$this->assertFalse($actual,
|
448 |
"password not being set should have failed.");
|
449 |
$this->assertEquals(
|
450 |
+
$this->err(__("Password not set.", self::ID)),
|
451 |
$errors->get_error_message()
|
452 |
);
|
453 |
}
|
458 |
$this->assertFalse($actual,
|
459 |
"'array('abc')' should have failed.");
|
460 |
$this->assertEquals(
|
461 |
+
$this->err(__("Passwords must be strings.", self::ID)),
|
462 |
$errors->get_error_message()
|
463 |
);
|
464 |
}
|
471 |
$this->assertFalse($actual,
|
472 |
"'" . $this->user->user_pass . "' should have failed.");
|
473 |
$this->assertEquals(
|
474 |
+
$this->err(__("Passwords must use ASCII characters.", self::ID)),
|
475 |
$errors->get_error_message()
|
476 |
);
|
477 |
}
|
488 |
$this->assertFalse($actual,
|
489 |
"'" . $this->user->user_pass . "' should have failed.");
|
490 |
$this->assertEquals(
|
491 |
+
$this->err(__("Password is too short.", self::ID)),
|
492 |
$errors->get_error_message()
|
493 |
);
|
494 |
}
|
502 |
$this->assertFalse($actual,
|
503 |
"'" . $this->user->user_pass . "' should have failed.");
|
504 |
$this->assertEquals(
|
505 |
+
$this->err(__("Password is too short.", self::ID)),
|
506 |
$errors->get_error_message()
|
507 |
);
|
508 |
}
|
515 |
$this->assertFalse($actual,
|
516 |
"'" . $this->user->user_pass . "' should have failed.");
|
517 |
$this->assertEquals(
|
518 |
+
$this->err(sprintf(__("Passwords must either contain punctuation marks / symbols or be %d characters long.", self::ID), self::$lss->options['pw_complexity_exemption_length'])),
|
519 |
$errors->get_error_message()
|
520 |
);
|
521 |
}
|
528 |
$this->assertFalse($actual,
|
529 |
"'" . $this->user->user_pass . "' should have failed.");
|
530 |
$this->assertEquals(
|
531 |
+
$this->err(sprintf(__("Passwords must either contain numbers or be %d characters long.", self::ID), self::$lss->options['pw_complexity_exemption_length'])),
|
532 |
$errors->get_error_message()
|
533 |
);
|
534 |
}
|
541 |
$this->assertFalse($actual,
|
542 |
"'" . $this->user->user_pass . "' should have failed.");
|
543 |
$this->assertEquals(
|
544 |
+
$this->err(sprintf(__("Passwords must either contain upper-case and lower-case letters or be %d characters long.", self::ID), self::$lss->options['pw_complexity_exemption_length'])),
|
545 |
$errors->get_error_message()
|
546 |
);
|
547 |
}
|
554 |
$this->assertFalse($actual,
|
555 |
"'" . $this->user->user_pass . "' should have failed.");
|
556 |
$this->assertEquals(
|
557 |
+
$this->err(__("Passwords can't be sequential keys.", self::ID)),
|
558 |
$errors->get_error_message()
|
559 |
);
|
560 |
}
|
567 |
$this->assertFalse($actual,
|
568 |
"'" . $this->user->user_pass . "' should have failed.");
|
569 |
$this->assertEquals(
|
570 |
+
$this->err(__("Passwords can't have that many sequential characters.", self::ID)),
|
571 |
$errors->get_error_message()
|
572 |
);
|
573 |
}
|
580 |
$this->assertFalse($actual,
|
581 |
"'" . $this->user->user_pass . "' should have failed.");
|
582 |
$this->assertEquals(
|
583 |
+
$this->err(__("Passwords can't contain user data.", self::ID)),
|
584 |
$errors->get_error_message()
|
585 |
);
|
586 |
}
|
593 |
$this->assertFalse($actual,
|
594 |
"'" . $this->user->user_pass . "' should have failed.");
|
595 |
$this->assertEquals(
|
596 |
+
$this->err(__("Passwords can't contain user data.", self::ID)),
|
597 |
$errors->get_error_message()
|
598 |
);
|
599 |
}
|
606 |
$this->assertFalse($actual,
|
607 |
"'" . $this->user->user_pass . "' should have failed.");
|
608 |
$this->assertEquals(
|
609 |
+
$this->err(__("Passwords can't contain site info.", self::ID)),
|
610 |
$errors->get_error_message()
|
611 |
);
|
612 |
}
|
619 |
$this->assertFalse($actual,
|
620 |
"'" . $this->user->user_pass . "' should have failed.");
|
621 |
$this->assertEquals(
|
622 |
+
$this->err(__("Password is too common.", self::ID)),
|
623 |
$errors->get_error_message()
|
624 |
);
|
625 |
}
|
635 |
$this->assertFalse($actual,
|
636 |
"'" . $this->user->user_pass . "' should have failed.");
|
637 |
$this->assertEquals(
|
638 |
+
$this->err(__("Passwords can't be variations of dictionary words.", self::ID)),
|
639 |
$errors->get_error_message()
|
640 |
);
|
641 |
}
|
642 |
|
643 |
public function test_validate_pw__good() {
|
644 |
+
if (!self::$mbstring_available) {
|
645 |
+
$this->user->user_pass = 'Some ASCII Only PW 4 You!';
|
646 |
+
}
|
647 |
+
|
648 |
$errors = new WP_Error;
|
649 |
$actual = self::$lss->validate_pw($this->user, $errors);
|
650 |
$this->assertTrue($actual,
|
651 |
+
"'" . $this->user->user_pass . "' should have passed, but got: "
|
652 |
+
. $errors->get_error_message());
|
653 |
$this->assertEmpty($errors->get_error_message());
|
654 |
}
|
655 |
|
659 |
$errors = new WP_Error;
|
660 |
$actual = self::$lss->validate_pw($this->user, $errors);
|
661 |
$this->assertTrue($actual,
|
662 |
+
"'" . $this->user->user_pass . "' should have passed, but got: "
|
663 |
+
. $errors->get_error_message());
|
664 |
$this->assertEmpty($errors->get_error_message());
|
665 |
}
|
666 |
|
tests/TestCase.php
CHANGED
@@ -226,7 +226,7 @@ abstract class TestCase extends PHPUnit_Framework_TestCase {
|
|
226 |
|
227 |
$this->user = new WP_User;
|
228 |
$this->user->data = new StdClass;
|
229 |
-
$this->user->ID =
|
230 |
$this->user->user_login = 'aaaa';
|
231 |
$this->user->user_email = 'bbbb';
|
232 |
$this->user->user_url = 'cccc';
|
@@ -355,7 +355,8 @@ abstract class TestCase extends PHPUnit_Framework_TestCase {
|
|
355 |
throw new Exception('wp_mail() called at unexpected time'
|
356 |
. ' (mail_file_basename was not set).');
|
357 |
}
|
358 |
-
|
|
|
359 |
|
360 |
$contents = 'To: ' . implode(', ', (array) $to) . "\n"
|
361 |
. "Subject: $subject\n\n$message";
|
@@ -371,9 +372,15 @@ abstract class TestCase extends PHPUnit_Framework_TestCase {
|
|
371 |
$this->fail('wp_mail() has not been called.');
|
372 |
}
|
373 |
|
|
|
|
|
|
|
|
|
|
|
|
|
374 |
$basename = str_replace('::', '--', self::$mail_file_basename);
|
375 |
$this->assertStringMatchesFormatFile(
|
376 |
-
|
377 |
file_get_contents(self::$mail_file)
|
378 |
);
|
379 |
}
|
226 |
|
227 |
$this->user = new WP_User;
|
228 |
$this->user->data = new StdClass;
|
229 |
+
$this->user->ID = 9999999;
|
230 |
$this->user->user_login = 'aaaa';
|
231 |
$this->user->user_email = 'bbbb';
|
232 |
$this->user->user_url = 'cccc';
|
355 |
throw new Exception('wp_mail() called at unexpected time'
|
356 |
. ' (mail_file_basename was not set).');
|
357 |
}
|
358 |
+
$basename = str_replace('::', '--', self::$mail_file_basename);
|
359 |
+
self::$mail_file = self::$temp_dir . '/' . $basename;
|
360 |
|
361 |
$contents = 'To: ' . implode(', ', (array) $to) . "\n"
|
362 |
. "Subject: $subject\n\n$message";
|
372 |
$this->fail('wp_mail() has not been called.');
|
373 |
}
|
374 |
|
375 |
+
$basedir = dirname(__FILE__) . '/expected/';
|
376 |
+
$locale = get_locale();
|
377 |
+
if (!file_exists("$basedir/$locale")) {
|
378 |
+
$locale = 'en_US';
|
379 |
+
}
|
380 |
+
|
381 |
$basename = str_replace('::', '--', self::$mail_file_basename);
|
382 |
$this->assertStringMatchesFormatFile(
|
383 |
+
"$basedir/$locale/$basename",
|
384 |
file_get_contents(self::$mail_file)
|
385 |
);
|
386 |
}
|
tests/VerifiedIpTest.php
CHANGED
@@ -124,7 +124,7 @@ class VerifiedIpTest extends TestCase {
|
|
124 |
|
125 |
// Check the outcome.
|
126 |
$actual = self::$lss->get_verified_ips($this->user->ID);
|
127 |
-
$this->assertSame(array($ip), $actual, 'Expected IP
|
128 |
|
129 |
$wpdb->query('ROLLBACK TO empty');
|
130 |
wp_cache_reset();
|
@@ -141,12 +141,16 @@ class VerifiedIpTest extends TestCase {
|
|
141 |
// So user id = current user id in our profile update errors method.
|
142 |
$current_user = $this->user;
|
143 |
|
|
|
|
|
|
|
|
|
144 |
$errors = new WP_Error;
|
145 |
$actual = self::$lss->user_profile_update_errors($errors, 1, $this->user);
|
146 |
$this->assertTrue($actual, 'Bad return value.');
|
147 |
|
148 |
// Check the outcome.
|
149 |
$actual = self::$lss->get_verified_ips($this->user->ID);
|
150 |
-
$this->assertSame(array($ip), $actual, 'Expected IP
|
151 |
}
|
152 |
}
|
124 |
|
125 |
// Check the outcome.
|
126 |
$actual = self::$lss->get_verified_ips($this->user->ID);
|
127 |
+
$this->assertSame(array($ip), $actual, 'Expected IP was not found.');
|
128 |
|
129 |
$wpdb->query('ROLLBACK TO empty');
|
130 |
wp_cache_reset();
|
141 |
// So user id = current user id in our profile update errors method.
|
142 |
$current_user = $this->user;
|
143 |
|
144 |
+
if (!extension_loaded('mbstring')) {
|
145 |
+
$this->user->user_pass = 'Some ASCII Only PW 4 You!';
|
146 |
+
}
|
147 |
+
|
148 |
$errors = new WP_Error;
|
149 |
$actual = self::$lss->user_profile_update_errors($errors, 1, $this->user);
|
150 |
$this->assertTrue($actual, 'Bad return value.');
|
151 |
|
152 |
// Check the outcome.
|
153 |
$actual = self::$lss->get_verified_ips($this->user->ID);
|
154 |
+
$this->assertSame(array($ip), $actual, 'Expected IP was not found.');
|
155 |
}
|
156 |
}
|
tests/expected/{LoginFailTest--test_process_login_fail__post_threshold → en_US/LoginFailTest--test_process_login_fail__post_threshold}
RENAMED
@@ -5,10 +5,11 @@ Your website, %s, is undergoing a brute force attack.
|
|
5 |
|
6 |
There have been at least 4 failed attempts to log in during the past 60 minutes that used one or more of the following components:
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
13 |
|
14 |
The Login Security Solution plugin for WordPress is repelling the attack by making their login failures take a very long time.
|
5 |
|
6 |
There have been at least 4 failed attempts to log in during the past 60 minutes that used one or more of the following components:
|
7 |
|
8 |
+
|
9 |
+
Component Count Value from Current Attempt
|
10 |
+
------------------------ ----- --------------------------------
|
11 |
+
Network IP 4 1.2.38
|
12 |
+
Username 4 test
|
13 |
+
Password MD5 %d %s
|
14 |
|
15 |
The Login Security Solution plugin for WordPress is repelling the attack by making their login failures take a very long time.
|
tests/expected/{LoginFailTest--test_wp_login__post_breach_threshold → en_US/LoginFailTest--test_wp_login__post_breach_threshold}
RENAMED
@@ -5,10 +5,11 @@ Your website, %s, may have been broken in to.
|
|
5 |
|
6 |
Someone just logged in using the following components. Prior to that, some combination of those components were a part of 4 failed attempts to log in during the past 60 minutes:
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
13 |
|
14 |
The user has been logged out and will be required to confirm their identity via the password reset functionality.
|
5 |
|
6 |
Someone just logged in using the following components. Prior to that, some combination of those components were a part of 4 failed attempts to log in during the past 60 minutes:
|
7 |
|
8 |
+
|
9 |
+
Component Count Value from Current Attempt
|
10 |
+
------------------------ ----- --------------------------------
|
11 |
+
Network IP 4 1.2.38
|
12 |
+
Username 4 test
|
13 |
+
Password MD5 %d %s
|
14 |
|
15 |
The user has been logged out and will be required to confirm their identity via the password reset functionality.
|
tests/expected/{LoginFailTest--test_wp_login__post_breach_threshold_verified_ip → en_US/LoginFailTest--test_wp_login__post_breach_threshold_verified_ip}
RENAMED
@@ -5,11 +5,12 @@ Your website, %s, may have been broken in to.
|
|
5 |
|
6 |
Someone just logged in using the following components. Prior to that, some combination of those components were a part of 4 failed attempts to log in during the past 60 minutes:
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
13 |
|
14 |
The user's current IP address is one they have verified with your site in the past. Therefore, the user will NOT be required to confirm their identity via the password reset process. An email will be sent to them, just in case this actually was a breach.
|
15 |
To: %a
|
5 |
|
6 |
Someone just logged in using the following components. Prior to that, some combination of those components were a part of 4 failed attempts to log in during the past 60 minutes:
|
7 |
|
8 |
+
|
9 |
+
Component Count Value from Current Attempt
|
10 |
+
------------------------ ----- --------------------------------
|
11 |
+
Network IP 0 1.2.33
|
12 |
+
Username 4 test
|
13 |
+
Password MD5 %d %s
|
14 |
|
15 |
The user's current IP address is one they have verified with your site in the past. Therefore, the user will NOT be required to confirm their identity via the password reset process. An email will be sent to them, just in case this actually was a breach.
|
16 |
To: %a
|
tests/expected/fr_FR/LoginFailTest--test_process_login_fail__post_threshold
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
To: %a
|
2 |
+
Subject: ATTAQUE SUR %a
|
3 |
+
|
4 |
+
Votre site, %s, subi une attaque de type brute force.
|
5 |
+
|
6 |
+
Au moins %d tentatives infructueuses de connexion au cours des dernières %d minutes ont utilisées les données suivantes:
|
7 |
+
|
8 |
+
|
9 |
+
Composant Nombre Valeur de la tentative courante
|
10 |
+
------------------------ ----- --------------------------------
|
11 |
+
Réseau IP 4 1.2.38
|
12 |
+
Nom d'utilisateur 4 test
|
13 |
+
MD5 du mot de passe %d %s
|
14 |
+
|
15 |
+
Le plugin %s pour WordPress pare l'attaque en ralentissant la réponse à chaque tentative échouée.
|
tests/expected/fr_FR/LoginFailTest--test_wp_login__post_breach_threshold
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
To: %a
|
2 |
+
Subject: INTRUSION POSSIBLE A %a
|
3 |
+
|
4 |
+
Votre site, %s, a peut-être été corrompu.
|
5 |
+
|
6 |
+
Quelqu'un vient de se connecter avec les données qui suivent. Avant cela, plusieurs de ces éléments ont été utilisés parmi les %d tentatives de connexion au cours des %d dernières minutes:
|
7 |
+
|
8 |
+
|
9 |
+
Composant Nombre Valeur de la tentative courante
|
10 |
+
------------------------ ----- --------------------------------
|
11 |
+
Réseau IP 4 1.2.38
|
12 |
+
Nom d'utilisateur 4 test
|
13 |
+
MD5 du mot de passe %d %s
|
14 |
+
|
15 |
+
L'utilisateur à été déconnecté et il devra confirmer son identité via le processus de changement de mot de passe.
|
tests/expected/fr_FR/LoginFailTest--test_wp_login__post_breach_threshold_verified_ip
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
To: %a
|
2 |
+
Subject: INTRUSION POSSIBLE A %a
|
3 |
+
|
4 |
+
Votre site, %s, a peut-être été corrompu.
|
5 |
+
|
6 |
+
Quelqu'un vient de se connecter avec les données qui suivent. Avant cela, plusieurs de ces éléments ont été utilisés parmi les %d tentatives de connexion au cours des %d dernières minutes:
|
7 |
+
|
8 |
+
|
9 |
+
Composant Nombre Valeur de la tentative courante
|
10 |
+
------------------------ ----- --------------------------------
|
11 |
+
Réseau IP 0 1.2.33
|
12 |
+
Nom d'utilisateur 4 test
|
13 |
+
MD5 du mot de passe %d %s
|
14 |
+
|
15 |
+
L'adresse IP utilisée à déjà été vérifiée auparavant. C'est pourquoi l'utilisateur ne devra PAS nécessairement confirmer son identité via le processus de changement de mot de passe. Un email lui sera envoyé, au cas où ce serait effectivement un intrusion.
|
16 |
+
To: %a
|
17 |
+
Subject: INTRUSION POSSIBLE A %a
|
18 |
+
|
19 |
+
Quelqu'un a %s vient de se connecter en tant que '%s'. Était-ce vous? Nous vous posons la question parce que le site semble subir une attaque.
|
20 |
+
|
21 |
+
SI CE N'ÉTAIT PAS VOUS, veuillez suivre ces consignes au plus vite:
|
22 |
+
|
23 |
+
1) Connectez vous à %s et changez votre mot de passe.
|
24 |
+
|
25 |
+
2) Envoyez un message à %s pour les prévenir que ce n'était pas vous qui vous êtes connecté récemment.
|