honeypot.test 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <?php
  2. /**
  3. * @file
  4. * Testing for Honeypot module.
  5. */
  6. /**
  7. * Test the functionality of the Honeypot module for forms.
  8. */
  9. class HoneypotFormTestCase extends DrupalWebTestCase {
  10. protected $adminUser;
  11. protected $webUser;
  12. protected $node;
  13. public static function getInfo() {
  14. return array(
  15. 'name' => 'Honeypot form protections',
  16. 'description' => 'Ensure that Honeypot protects site forms properly.',
  17. 'group' => 'Form API',
  18. );
  19. }
  20. public function setUp() {
  21. // Enable modules required for this test.
  22. parent::setUp(array('honeypot', 'comment', 'honeypot_test'));
  23. // Set up required Honeypot variables.
  24. variable_set('honeypot_element_name', 'url');
  25. // Disable time_limit protection.
  26. variable_set('honeypot_time_limit', 0);
  27. // Test protecting all forms.
  28. variable_set('honeypot_protect_all_forms', TRUE);
  29. variable_set('honeypot_log', FALSE);
  30. // Set up other required variables.
  31. variable_set('user_email_verification', TRUE);
  32. variable_set('user_register', USER_REGISTER_VISITORS);
  33. // Set up admin user.
  34. $this->adminUser = $this->drupalCreateUser(array(
  35. 'administer honeypot',
  36. 'bypass honeypot protection',
  37. 'administer content types',
  38. 'administer users',
  39. 'access comments',
  40. 'post comments',
  41. 'skip comment approval',
  42. 'administer comments',
  43. ));
  44. // Set up web user.
  45. $this->webUser = $this->drupalCreateUser(array(
  46. 'access comments',
  47. 'post comments',
  48. 'create article content',
  49. ));
  50. // Set up example node.
  51. $this->node = $this->drupalCreateNode(array(
  52. 'type' => 'article',
  53. 'promote' => 1,
  54. 'uid' => $this->webUser->uid,
  55. ));
  56. }
  57. /**
  58. * Test user registration (anonymous users).
  59. */
  60. public function testProtectRegisterUserNormal() {
  61. // Set up form and submit it.
  62. $edit['name'] = $this->randomName();
  63. $edit['mail'] = $edit['name'] . '@example.com';
  64. $this->drupalPost('user/register', $edit, t('Create new account'));
  65. // Form should have been submitted successfully.
  66. $this->assertText(t('A welcome message with further instructions has been sent to your e-mail address.'), 'User registered successfully.');
  67. }
  68. public function testProtectUserRegisterHoneypotFilled() {
  69. // Set up form and submit it.
  70. $edit['name'] = $this->randomName();
  71. $edit['mail'] = $edit['name'] . '@example.com';
  72. $edit['url'] = 'http://www.example.com/';
  73. $this->drupalPost('user/register', $edit, t('Create new account'));
  74. // Form should have error message.
  75. $this->assertText(t('There was a problem with your form submission. Please refresh the page and try again.'), 'Registration form protected by honeypot.');
  76. }
  77. public function testProtectRegisterUserTooFast() {
  78. // Enable time limit for honeypot.
  79. variable_set('honeypot_time_limit', 5);
  80. // Set up form and submit it.
  81. $edit['name'] = $this->randomName();
  82. $edit['mail'] = $edit['name'] . '@example.com';
  83. $this->drupalPost('user/register', $edit, t('Create new account'));
  84. // Form should have error message.
  85. $this->assertText(t('There was a problem with your form submission. Please wait 6 seconds and try again.'), 'Registration form protected by time limit.');
  86. }
  87. /**
  88. * Test comment form protection.
  89. */
  90. public function testProtectCommentFormNormal() {
  91. $comment = 'Test comment.';
  92. // Disable time limit for honeypot.
  93. variable_set('honeypot_time_limit', 0);
  94. // Log in the web user.
  95. $this->drupalLogin($this->webUser);
  96. // Set up form and submit it.
  97. $edit['comment_body[' . LANGUAGE_NONE . '][0][value]'] = $comment;
  98. $this->drupalPost('comment/reply/' . $this->node->nid, $edit, t('Save'));
  99. $this->assertText(t('Your comment has been posted.'), 'Comment posted successfully.');
  100. }
  101. public function testProtectCommentFormHoneypotFilled() {
  102. $comment = 'Test comment.';
  103. // Log in the web user.
  104. $this->drupalLogin($this->webUser);
  105. // Set up form and submit it.
  106. $edit['comment_body[' . LANGUAGE_NONE . '][0][value]'] = $comment;
  107. $edit['url'] = 'http://www.example.com/';
  108. $this->drupalPost('comment/reply/' . $this->node->nid, $edit, t('Save'));
  109. $this->assertText(t('There was a problem with your form submission. Please refresh the page and try again.'), 'Comment posted successfully.');
  110. }
  111. public function testProtectCommentFormHoneypotBypass() {
  112. // Log in the admin user.
  113. $this->drupalLogin($this->adminUser);
  114. // Get the comment reply form and ensure there's no 'url' field.
  115. $this->drupalGet('comment/reply/' . $this->node->nid);
  116. $this->assertNoText('id="edit-url" name="url"', 'Honeypot home page field not shown.');
  117. }
  118. /**
  119. * Test node form protection.
  120. */
  121. public function testProtectNodeFormTooFast() {
  122. // Log in the admin user.
  123. $this->drupalLogin($this->webUser);
  124. // Reset the time limit to 5 seconds.
  125. variable_set('honeypot_time_limit', 5);
  126. // Set up the form and submit it.
  127. $edit["title"] = 'Test Page';
  128. $this->drupalPost('node/add/article', $edit, t('Save'));
  129. $this->assertText(t('There was a problem with your form submission.'), 'Honeypot node form timestamp protection works.');
  130. }
  131. /**
  132. * Test node form protection.
  133. */
  134. public function testProtectNodeFormPreviewPassthru() {
  135. // Log in the admin user.
  136. $this->drupalLogin($this->webUser);
  137. // Post a node form using the 'Preview' button and make sure it's allowed.
  138. $edit["title"] = 'Test Page';
  139. $this->drupalPost('node/add/article', $edit, t('Preview'));
  140. $this->assertNoText(t('There was a problem with your form submission.'), 'Honeypot not blocking node form previews.');
  141. }
  142. /**
  143. * Test programmatic submission.
  144. */
  145. public function testProgrammaticSubmission() {
  146. // Enable time limit protection.
  147. variable_set('honeypot_time_limit', 5);
  148. // Create a user for which we are going to trigger the password reset.
  149. $edit = array();
  150. $edit['name'] = 'robo-user';
  151. $edit['mail'] = $edit['name'] . '@example.com';
  152. $edit['status'] = 1;
  153. user_save(drupal_anonymous_user(), $edit);
  154. // Trigger the password reset through a programmatic submission.
  155. $this->drupalGet('honeypot_test/submit_form');
  156. // Verify that the submission did not return any validation errors.
  157. $form_errors = drupal_json_decode($this->content);
  158. $this->assertNoRaw('There was a problem with your form submission. Please wait 6 seconds and try again.');
  159. $this->assertFalse($form_errors, 'The were no validation errors when submitting the form.');
  160. }
  161. }
  162. /**
  163. * Test the functionality of the Honeypot module for an admin user.
  164. */
  165. class HoneypotAdminFormTestCase extends DrupalWebTestCase {
  166. protected $adminUser;
  167. public static function getInfo() {
  168. return array(
  169. 'name' => 'Honeypot admin form',
  170. 'description' => 'Ensure the Honeypot admin form functions properly.',
  171. 'group' => 'Form API',
  172. );
  173. }
  174. public function setUp() {
  175. // Enable modules required for this test.
  176. parent::setUp(array('honeypot'));
  177. // Set up admin user.
  178. $this->adminUser = $this->drupalCreateUser(array(
  179. 'administer honeypot',
  180. 'bypass honeypot protection',
  181. ));
  182. }
  183. /**
  184. * Test a valid element name.
  185. */
  186. public function testElementNameUpdateSuccess() {
  187. // Log in the web user.
  188. $this->drupalLogin($this->adminUser);
  189. // Set up form and submit it.
  190. $edit['honeypot_element_name'] = "test";
  191. $this->drupalPost('admin/config/content/honeypot', $edit, t('Save configuration'));
  192. // Form should have been submitted successfully.
  193. $this->assertText(t('The configuration options have been saved.'), 'Honeypot element name assertion works for valid names.');
  194. // Set up form and submit it.
  195. $edit['honeypot_element_name'] = "test-1";
  196. $this->drupalPost('admin/config/content/honeypot', $edit, t('Save configuration'));
  197. // Form should have been submitted successfully.
  198. $this->assertText(t('The configuration options have been saved.'), 'Honeypot element name assertion works for valid names with dashes and numbers.');
  199. }
  200. /**
  201. * Test an invalid element name (invalid first character).
  202. */
  203. public function testElementNameUpdateFirstCharacterFail() {
  204. // Log in the admin user.
  205. $this->drupalLogin($this->adminUser);
  206. // Set up form and submit it.
  207. $edit['honeypot_element_name'] = "1test";
  208. $this->drupalPost('admin/config/content/honeypot', $edit, t('Save configuration'));
  209. // Form submission should fail.
  210. $this->assertText(t('The element name must start with a letter.'), 'Honeypot element name assertion works for invalid names.');
  211. }
  212. /**
  213. * Test an invalid element name (invalid character in name).
  214. */
  215. public function testElementNameUpdateInvalidCharacterFail() {
  216. // Log in the admin user.
  217. $this->drupalLogin($this->adminUser);
  218. // Set up form and submit it.
  219. $edit['honeypot_element_name'] = "special-character-&";
  220. $this->drupalPost('admin/config/content/honeypot', $edit, t('Save configuration'));
  221. // Form submission should fail.
  222. $this->assertText(t('The element name cannot contain spaces or other special characters.'), 'Honeypot element name assertion works for invalid names with special characters.');
  223. // Set up form and submit it.
  224. $edit['honeypot_element_name'] = "space in name";
  225. $this->drupalPost('admin/config/content/honeypot', $edit, t('Save configuration'));
  226. // Form submission should fail.
  227. $this->assertText(t('The element name cannot contain spaces or other special characters.'), 'Honeypot element name assertion works for invalid names with spaces.');
  228. }
  229. }
  230. /**
  231. * Test Honeypot's CSS generation routines.
  232. */
  233. class HoneypotCssTestCase extends DrupalWebTestCase {
  234. public static function getInfo() {
  235. return array(
  236. 'name' => 'Honeypot CSS tests',
  237. 'description' => 'Ensure that Honeypot rebuilds its CSS file correctly.',
  238. 'group' => 'Form API',
  239. );
  240. }
  241. public function setUp() {
  242. // Enable modules required for this test.
  243. parent::setUp(array('honeypot'));
  244. // Set up required Honeypot variables.
  245. variable_set('honeypot_element_name', 'url');
  246. }
  247. /**
  248. * Test CSS file regeneration.
  249. */
  250. public function testHoneypotCssRegeneration() {
  251. $honeypot_css = honeypot_get_css_file_path();
  252. // Delete the Honeypot CSS file (if it exists).
  253. file_unmanaged_delete($honeypot_css);
  254. // Make sure the Honeypot CSS file doesn't exist.
  255. $this->assertFalse(file_exists($honeypot_css));
  256. // Create the CSS file.
  257. honeypot_create_css(variable_get('honeypot_element_name', 'url'));
  258. // Make sure the Honeypot CSS file exists.
  259. $this->assertTrue(file_exists($honeypot_css));
  260. }
  261. /**
  262. * Test cron-based CSS file regeneration.
  263. */
  264. public function testHoneypotCssRegenerationOnCron() {
  265. $honeypot_css = honeypot_get_css_file_path();
  266. // Delete the Honeypot CSS file (if it exists).
  267. file_unmanaged_delete($honeypot_css);
  268. // Make sure the Honeypot CSS file doesn't exist.
  269. $this->assertFalse(file_exists($honeypot_css));
  270. // Run cron.
  271. honeypot_cron();
  272. // Make sure the Honeypot CSS file exists.
  273. $this->assertTrue(file_exists($honeypot_css));
  274. }
  275. /**
  276. * Test cron-based CSS file update.
  277. */
  278. public function testHoneypotCssUpdateOnCron() {
  279. $honeypot_css = honeypot_get_css_file_path();
  280. $original_element_name = variable_get('honeypot_element_name', 'url');
  281. // Update the honeypot element name.
  282. variable_set('honeypot_element_name', 'test');
  283. // Make sure the Honeypot CSS file still exists.
  284. $this->assertTrue(file_exists($honeypot_css));
  285. // Run cron.
  286. honeypot_cron();
  287. // Make sure the Honeypot CSS file was updated with the new element name.
  288. $handle = fopen($honeypot_css, 'r');
  289. $contents = fread($handle, filesize($honeypot_css));
  290. fclose($handle);
  291. $updated_element_name_in_css = (strpos($contents, 'test') === 1);
  292. $this->assertTrue($updated_element_name_in_css);
  293. // For debug.
  294. $this->verbose($contents);
  295. // Revert the honeypot element name back to the original.
  296. variable_set('honeypot_element_name', $original_element_name);
  297. }
  298. /**
  299. * Test CSS works when default file scheme is not public://
  300. */
  301. public function testHoneypotCssNonpublicFileSystem() {
  302. variable_set('file_default_scheme', 'private');
  303. $honeypot_css = honeypot_get_css_file_path();
  304. // Delete the Honeypot CSS file (if it exists).
  305. file_unmanaged_delete($honeypot_css);
  306. // Make sure the Honeypot CSS file doesn't exist.
  307. $this->assertFalse(file_exists($honeypot_css));
  308. // Run cron.
  309. honeypot_cron();
  310. // Make sure the Honeypot CSS file exists.
  311. $this->assertTrue(file_exists($honeypot_css));
  312. }
  313. /**
  314. * Test CSS file availability.
  315. */
  316. public function testHoneypotCssAvailability() {
  317. // Public CSS file can be consumed.
  318. variable_set('file_default_scheme', 'public');
  319. if ($wrapper = file_stream_wrapper_get_instance_by_uri(honeypot_get_css_file_path())) {
  320. $url = $wrapper->getExternalUrl();
  321. }
  322. $this->drupalGet($url);
  323. $this->assertResponse(200);
  324. // Private CSS file can not be consumed.
  325. variable_set('file_default_scheme', 'private');
  326. honeypot_cron();
  327. if ($wrapper = file_stream_wrapper_get_instance_by_uri(honeypot_get_css_file_path())) {
  328. $url = $wrapper->getExternalUrl();
  329. }
  330. $this->drupalGet($url);
  331. $this->assertNoResponse(200);
  332. // Site default is private, but override honeypot's to public to consume.
  333. variable_set('honeypot_file_default_scheme', 'public');
  334. honeypot_cron();
  335. if ($wrapper = file_stream_wrapper_get_instance_by_uri(honeypot_get_css_file_path())) {
  336. $url = $wrapper->getExternalUrl();
  337. }
  338. $this->drupalGet($url);
  339. $this->assertResponse(200);
  340. }
  341. }
  342. /**
  343. * Test the functionality of the Honeypot module's integration with Trigger.
  344. */
  345. class HoneypotTriggerTestCase extends DrupalWebTestCase {
  346. public static function getInfo() {
  347. return array(
  348. 'name' => 'Honeypot Trigger integration',
  349. 'description' => 'Ensure that Honeypot triggers events correctly.',
  350. 'group' => 'Form API',
  351. );
  352. }
  353. public function setUp() {
  354. // Enable modules required for this test.
  355. parent::setUp(array('honeypot', 'trigger'));
  356. // Set up required Honeypot variables.
  357. variable_set('honeypot_element_name', 'url');
  358. // Disable time_limit protection.
  359. variable_set('honeypot_time_limit', 0);
  360. // Test protecting all forms.
  361. variable_set('honeypot_protect_all_forms', TRUE);
  362. variable_set('honeypot_log', FALSE);
  363. // Set up other required variables.
  364. variable_set('user_email_verification', TRUE);
  365. variable_set('user_register', USER_REGISTER_VISITORS);
  366. // Assign new action to Honeypot form rejection Trigger.
  367. db_insert('trigger_assignments')
  368. ->fields(array(
  369. 'hook' => 'honeypot_reject',
  370. 'aid' => 'system_block_ip_action',
  371. 'weight' => 1,
  372. ))
  373. ->execute();
  374. }
  375. /**
  376. * Test trigger integration.
  377. */
  378. public function testHoneypotTriggerIntegration() {
  379. // Set up form and submit it.
  380. $edit['name'] = $this->randomName();
  381. $edit['mail'] = $edit['name'] . '@example.com';
  382. $edit['url'] = 'http://www.example.com/';
  383. $this->drupalPost('user/register', $edit, t('Create new account'));
  384. // Make sure Honeypot is working.
  385. $this->assertText(t('There was a problem with your form submission.'), 'Honeypot working correctly.');
  386. // Visit the home page and make sure the user is banned.
  387. $this->drupalGet('node');
  388. $this->assertText(t('has been banned'), 'User banned successfully.');
  389. }
  390. }