PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/tests/TestCase/Controller/Component/SecurityComponentTest.php

http://github.com/cakephp/cakephp
PHP | 1399 lines | 1053 code | 108 blank | 238 comment | 1 complexity | 415ab628e3751dc3a44fa5a92984afca MD5 | raw file
Possible License(s): JSON
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 1.2.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. * @deprecated 4.0.0 SecurityComponent is deprecated.
  16. */
  17. namespace Cake\Test\TestCase\Controller\Component;
  18. use Cake\Controller\Component\SecurityComponent;
  19. use Cake\Controller\Exception\SecurityException;
  20. use Cake\Core\Configure;
  21. use Cake\Event\Event;
  22. use Cake\Http\Response;
  23. use Cake\Http\ServerRequest;
  24. use Cake\Http\Session;
  25. use Cake\Routing\Router;
  26. use Cake\TestSuite\TestCase;
  27. use Cake\Utility\Security;
  28. use TestApp\Controller\SecurityTestController;
  29. /**
  30. * SecurityComponentTest class
  31. *
  32. * @property \TestApp\Controller\Component\TestSecurityComponent $Security
  33. */
  34. class SecurityComponentTest extends TestCase
  35. {
  36. /**
  37. * SERVER variable backup.
  38. *
  39. * @var array
  40. */
  41. protected $server = [];
  42. /**
  43. * Controller property
  44. *
  45. * @var \TestApp\Controller\SecurityTestController
  46. */
  47. protected $Controller;
  48. /**
  49. * oldSalt property
  50. *
  51. * @var string
  52. */
  53. protected $oldSalt;
  54. /**
  55. * setUp method
  56. *
  57. * Initializes environment state.
  58. */
  59. public function setUp(): void
  60. {
  61. parent::setUp();
  62. $this->server = $_SERVER;
  63. $session = new Session();
  64. $request = new ServerRequest([
  65. 'url' => '/articles/index',
  66. 'session' => $session,
  67. 'params' => ['controller' => 'Articles', 'action' => 'index'],
  68. ]);
  69. $this->Controller = new SecurityTestController($request);
  70. $this->Controller->Security = $this->Controller->TestSecurity;
  71. $this->Controller->Security->setConfig('blackHoleCallback', 'fail');
  72. $this->Security = $this->Controller->Security;
  73. Security::setSalt('foo!');
  74. }
  75. /**
  76. * Resets environment state.
  77. */
  78. public function tearDown(): void
  79. {
  80. parent::tearDown();
  81. $_SERVER = $this->server;
  82. unset($this->Controller->Security);
  83. unset($this->Controller->Component);
  84. unset($this->Controller);
  85. }
  86. public function validatePost(
  87. string $expectedException = 'SecurityException',
  88. ?string $expectedExceptionMessage = null
  89. ): ?bool {
  90. try {
  91. return $this->Controller->Security->validatePost($this->Controller);
  92. } catch (SecurityException $ex) {
  93. $this->assertInstanceOf('Cake\\Controller\\Exception\\' . $expectedException, $ex);
  94. $this->assertEquals($expectedExceptionMessage, $ex->getMessage());
  95. return false;
  96. }
  97. }
  98. /**
  99. * testBlackholeWithBrokenCallback method
  100. *
  101. * Test that requests are still blackholed when controller has incorrect
  102. * visibility keyword in the blackhole callback.
  103. *
  104. * @triggers Controller.startup $Controller, $this->Controller
  105. */
  106. public function testBlackholeWithBrokenCallback(): void
  107. {
  108. $this->expectException(\Cake\Http\Exception\BadRequestException::class);
  109. $request = new ServerRequest([
  110. 'url' => 'posts/index',
  111. 'session' => new Session(),
  112. 'params' => [
  113. 'controller' => 'Posts',
  114. 'action' => 'index',
  115. ],
  116. ]);
  117. $Controller = new \TestApp\Controller\SomePagesController($request);
  118. $event = new Event('Controller.startup', $Controller);
  119. $Security = new SecurityComponent($Controller->components());
  120. $Security->setConfig('blackHoleCallback', '_fail');
  121. $Security->startup($event);
  122. $Security->blackHole($Controller, 'csrf');
  123. }
  124. /**
  125. * testExceptionWhenActionIsBlackholeCallback method
  126. *
  127. * Ensure that directly requesting the blackholeCallback as the controller
  128. * action results in an exception.
  129. *
  130. * @triggers Controller.startup $this->Controller
  131. */
  132. public function testExceptionWhenActionIsBlackholeCallback(): void
  133. {
  134. $this->Controller->setRequest($this->Controller->getRequest()
  135. ->withParam('controller', 'posts')
  136. ->withParam('action', 'fail'));
  137. $event = new Event('Controller.startup', $this->Controller);
  138. $this->assertFalse($this->Controller->failed);
  139. $this->Controller->Security->startup($event);
  140. $this->assertTrue($this->Controller->failed, 'Request was blackholed.');
  141. }
  142. /**
  143. * test blackholeCallback returning a response
  144. */
  145. public function testBlackholeReturnResponse(): void
  146. {
  147. $request = new ServerRequest([
  148. 'url' => 'posts/index',
  149. 'session' => $this->Security->session,
  150. 'method' => 'POST',
  151. 'params' => [
  152. 'controller' => 'Posts',
  153. 'action' => 'index',
  154. ],
  155. 'post' => [
  156. 'key' => 'value',
  157. ],
  158. ]);
  159. $Controller = new \TestApp\Controller\SomePagesController($request);
  160. $event = new Event('Controller.startup', $Controller);
  161. $Security = new SecurityComponent($Controller->components());
  162. $Security->setConfig('blackHoleCallback', 'responseGenerator');
  163. $result = $Security->startup($event);
  164. $this->assertInstanceOf(Response::class, $result);
  165. }
  166. /**
  167. * testConstructorSettingProperties method
  168. *
  169. * Test that initialize can set properties.
  170. */
  171. public function testConstructorSettingProperties(): void
  172. {
  173. $settings = [
  174. 'requireSecure' => ['update_account'],
  175. 'validatePost' => false,
  176. ];
  177. $Security = new SecurityComponent($this->Controller->components(), $settings);
  178. $this->assertEquals($Security->validatePost, $settings['validatePost']);
  179. }
  180. /**
  181. * testRequireSecureFail method
  182. *
  183. * @triggers Controller.startup $this->Controller
  184. */
  185. public function testRequireSecureFail(): void
  186. {
  187. $this->Controller->setRequest($this->Controller->getRequest()
  188. ->withParam('action', 'posted')
  189. ->withEnv('HTTPS', 'off')
  190. ->withEnv('REQUEST_METHOD', 'POST'));
  191. $event = new Event('Controller.startup', $this->Controller);
  192. $this->Controller->Security->requireSecure(['posted']);
  193. $this->Controller->Security->startup($event);
  194. $this->assertTrue($this->Controller->failed);
  195. }
  196. /**
  197. * testRequireSecureSucceed method
  198. *
  199. * @triggers Controller.startup $this->Controller
  200. */
  201. public function testRequireSecureSucceed(): void
  202. {
  203. $this->Controller->setRequest($this->Controller->getRequest()
  204. ->withParam('action', 'posted')
  205. ->withEnv('HTTPS', 'on')
  206. ->withEnv('REQUEST_METHOD', 'Secure'));
  207. $event = new Event('Controller.startup', $this->Controller);
  208. $this->Controller->Security->requireSecure('posted');
  209. $this->Controller->Security->startup($event);
  210. $this->assertFalse($this->Controller->failed);
  211. }
  212. /**
  213. * testRequireSecureEmptyFail method
  214. *
  215. * @triggers Controller.startup $this->Controller
  216. */
  217. public function testRequireSecureEmptyFail(): void
  218. {
  219. $this->Controller->setRequest($this->Controller->getRequest()
  220. ->withParam('action', 'posted')
  221. ->withEnv('HTTPS', 'off')
  222. ->withEnv('REQUEST_METHOD', 'POST'));
  223. $event = new Event('Controller.startup', $this->Controller);
  224. $this->Controller->Security->requireSecure();
  225. $this->Controller->Security->startup($event);
  226. $this->assertTrue($this->Controller->failed);
  227. }
  228. /**
  229. * testRequireSecureEmptySucceed method
  230. *
  231. * @triggers Controller.startup $this->Controller
  232. */
  233. public function testRequireSecureEmptySucceed(): void
  234. {
  235. $this->Controller->setRequest($this->Controller->getRequest()
  236. ->withParam('action', 'posted')
  237. ->withEnv('HTTPS', 'on')
  238. ->withEnv('REQUEST_METHOD', 'Secure'));
  239. $event = new Event('Controller.startup', $this->Controller);
  240. $this->Controller->Security->requireSecure();
  241. $this->Controller->Security->startup($event);
  242. $this->assertFalse($this->Controller->failed);
  243. }
  244. /**
  245. * testValidatePost method
  246. *
  247. * Simple hash validation test
  248. *
  249. * @triggers Controller.startup $this->Controller
  250. */
  251. public function testValidatePost(): void
  252. {
  253. $event = new Event('Controller.startup', $this->Controller);
  254. $this->Security->startup($event);
  255. $fields = '4697b45f7f430ff3ab73018c20f315eecb0ba5a6%3AModel.valid';
  256. $unlocked = '';
  257. $debug = '';
  258. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  259. 'Model' => ['username' => 'nate', 'password' => 'foo', 'valid' => '0'],
  260. '_Token' => compact('fields', 'unlocked', 'debug'),
  261. ]));
  262. $this->assertNull($this->validatePost());
  263. }
  264. /**
  265. * testValidatePostOnGetWithData method
  266. *
  267. * Test that validatePost fires on GET with request data.
  268. * This could happen when method overriding is used.
  269. *
  270. * @triggers Controller.startup $this->Controller
  271. */
  272. public function testValidatePostOnGetWithData(): void
  273. {
  274. $event = new Event('Controller.startup', $this->Controller);
  275. $this->Security->startup($event);
  276. $fields = 'an-invalid-token';
  277. $unlocked = '';
  278. $debug = urlencode(json_encode([
  279. 'some-action',
  280. [],
  281. [],
  282. ]));
  283. $this->Controller->setRequest($this->Controller->getRequest()
  284. ->withEnv('REQUEST_METHOD', 'GET')
  285. ->withData('Model', ['username' => 'nate', 'password' => 'foo', 'valid' => '0'])
  286. ->withData('_Token', compact('fields', 'unlocked', 'debug')));
  287. $this->Security->startup($event);
  288. $this->assertTrue($this->Controller->failed);
  289. }
  290. /**
  291. * testValidatePostNoSession method
  292. *
  293. * Test that validatePost fails if you are missing the session information.
  294. *
  295. * @triggers Controller.startup $this->Controller
  296. */
  297. public function testValidatePostNoSession(): void
  298. {
  299. $event = new Event('Controller.startup', $this->Controller);
  300. $this->Security->startup($event);
  301. $unlocked = '';
  302. $debug = urlencode(json_encode([
  303. '/articles/index',
  304. [],
  305. [],
  306. ]));
  307. $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
  308. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  309. 'Model' => ['username' => 'nate', 'password' => 'foo', 'valid' => '0'],
  310. '_Token' => compact('fields', 'unlocked', 'debug'),
  311. ]));
  312. $this->assertFalse($this->validatePost('AuthSecurityException', 'Unexpected field \'Model.password\' in POST data, Unexpected field \'Model.username\' in POST data'));
  313. }
  314. /**
  315. * testValidatePostNoUnlockedInRequestData method
  316. *
  317. * Test that validatePost fails if you are missing unlocked in request data.
  318. *
  319. * @triggers Controller.startup $this->Controller
  320. */
  321. public function testValidatePostNoUnlockedInRequestData(): void
  322. {
  323. $event = new Event('Controller.startup', $this->Controller);
  324. $this->Security->startup($event);
  325. $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
  326. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  327. 'Model' => ['username' => 'nate', 'password' => 'foo', 'valid' => '0'],
  328. '_Token' => compact('fields'),
  329. ]));
  330. $this->assertFalse($this->validatePost('AuthSecurityException', '\'_Token.unlocked\' was not found in request data.'));
  331. }
  332. /**
  333. * testValidatePostFormTampering method
  334. *
  335. * Test that validatePost fails if any of its required fields are missing.
  336. *
  337. * @triggers Controller.startup $this->Controller
  338. */
  339. public function testValidatePostFormTampering(): void
  340. {
  341. $event = new Event('Controller.startup', $this->Controller);
  342. $this->Security->startup($event);
  343. $unlocked = '';
  344. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  345. 'Model' => ['username' => 'nate', 'password' => 'foo', 'valid' => '0'],
  346. '_Token' => compact('unlocked'),
  347. ]));
  348. $result = $this->validatePost('AuthSecurityException', '\'_Token.fields\' was not found in request data.');
  349. $this->assertFalse($result, 'validatePost passed when fields were missing. %s');
  350. }
  351. /**
  352. * testValidatePostEmptyForm method
  353. *
  354. * Test that validatePost fails if empty form is submitted.
  355. *
  356. * @triggers Controller.startup $this->Controller
  357. */
  358. public function testValidatePostEmptyForm(): void
  359. {
  360. $this->Controller->setRequest($this->Controller->getRequest()
  361. ->withEnv('REQUEST_METHOD', 'POST')
  362. ->withParsedBody([]));
  363. $event = new Event('Controller.startup', $this->Controller);
  364. $this->Security->startup($event);
  365. $result = $this->validatePost('AuthSecurityException', '\'_Token\' was not found in request data.');
  366. $this->assertFalse($result, 'validatePost passed when empty form is submitted');
  367. }
  368. /**
  369. * testValidatePostObjectDeserialize
  370. *
  371. * Test that objects can't be passed into the serialized string. This was a vector for RFI and LFI
  372. * attacks. Thanks to Felix Wilhelm
  373. *
  374. * @triggers Controller.startup $this->Controller
  375. */
  376. public function testValidatePostObjectDeserialize(): void
  377. {
  378. $event = new Event('Controller.startup', $this->Controller);
  379. $this->Security->startup($event);
  380. $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877';
  381. $unlocked = '';
  382. $debug = urlencode(json_encode([
  383. '/articles/index',
  384. ['Model.password', 'Model.username', 'Model.valid'],
  385. [],
  386. ]));
  387. // a corrupted serialized object, so we can see if it ever gets to deserialize
  388. $attack = 'O:3:"App":1:{s:5:"__map";a:1:{s:3:"foo";s:7:"Hacked!";s:1:"fail"}}';
  389. $fields .= urlencode(':' . str_rot13($attack));
  390. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  391. 'Model' => ['username' => 'mark', 'password' => 'foo', 'valid' => '0'],
  392. '_Token' => compact('fields', 'unlocked', 'debug'),
  393. ]));
  394. $result = $this->validatePost('SecurityException', 'Bad Request');
  395. $this->assertFalse($result, 'validatePost passed when key was missing. %s');
  396. }
  397. /**
  398. * testValidatePostIgnoresCsrfToken method
  399. *
  400. * Tests validation post data ignores `_csrfToken`.
  401. *
  402. * @triggers Controller.startup $this->Controller
  403. */
  404. public function testValidatePostIgnoresCsrfToken(): void
  405. {
  406. $event = new Event('Controller.startup', $this->Controller);
  407. $this->Security->startup($event);
  408. $fields = 'f95b472a63f1d883b9eaacaf8a8e36e325e3fe82%3A';
  409. $unlocked = '';
  410. $debug = 'not used';
  411. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  412. 'Model' => ['multi_field' => ['1', '3']],
  413. '_Token' => compact('fields', 'unlocked', 'debug'),
  414. ]));
  415. $this->assertNull($this->validatePost());
  416. }
  417. /**
  418. * testValidatePostArray method
  419. *
  420. * Tests validation of checkbox arrays.
  421. *
  422. * @triggers Controller.startup $this->Controller
  423. */
  424. public function testValidatePostArray(): void
  425. {
  426. $event = new Event('Controller.startup', $this->Controller);
  427. $this->Security->startup($event);
  428. $fields = 'f95b472a63f1d883b9eaacaf8a8e36e325e3fe82%3A';
  429. $unlocked = '';
  430. $debug = urlencode(json_encode([
  431. 'some-action',
  432. [],
  433. [],
  434. ]));
  435. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  436. 'Model' => ['multi_field' => ['1', '3']],
  437. '_Token' => compact('fields', 'unlocked', 'debug'),
  438. ]));
  439. $this->assertNull($this->validatePost());
  440. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  441. 'Model' => ['multi_field' => [12 => '1', 20 => '3']],
  442. '_Token' => compact('fields', 'unlocked', 'debug'),
  443. ]));
  444. $this->assertNull($this->validatePost());
  445. }
  446. /**
  447. * testValidateIntFieldName method
  448. *
  449. * Tests validation of integer field names.
  450. */
  451. public function testValidateIntFieldName(): void
  452. {
  453. $event = new Event('Controller.startup', $this->Controller);
  454. $this->Security->startup($event);
  455. $fields = '11f87a5962db9ac26405e460cd3063bb6ff76cf8%3A';
  456. $unlocked = '';
  457. $debug = urlencode(json_encode([
  458. 'some-action',
  459. [],
  460. [],
  461. ]));
  462. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  463. 1 => 'value,',
  464. '_Token' => compact('fields', 'unlocked', 'debug'),
  465. ]));
  466. $this->assertNull($this->validatePost());
  467. }
  468. /**
  469. * testValidatePostNoModel method
  470. *
  471. * @triggers Controller.startup $this->Controller
  472. */
  473. public function testValidatePostNoModel(): void
  474. {
  475. $event = new Event('Controller.startup', $this->Controller);
  476. $this->Security->startup($event);
  477. $fields = 'a2a942f587deb20e90241c51b59d901d8a7f796b%3A';
  478. $unlocked = '';
  479. $debug = 'not used';
  480. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  481. 'anything' => 'some_data',
  482. '_Token' => compact('fields', 'unlocked', 'debug'),
  483. ]));
  484. $result = $this->validatePost();
  485. $this->assertNull($result);
  486. }
  487. /**
  488. * testValidatePostSimple method
  489. *
  490. * @triggers Controller.startup $this->Controller
  491. */
  492. public function testValidatePostSimple(): void
  493. {
  494. $event = new Event('Controller.startup', $this->Controller);
  495. $this->Security->startup($event);
  496. $fields = 'de2ca3670dd06c29558dd98482c8739e86da2c7c%3A';
  497. $unlocked = '';
  498. $debug = 'not used';
  499. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  500. 'Model' => ['username' => '', 'password' => ''],
  501. '_Token' => compact('fields', 'unlocked', 'debug'),
  502. ]));
  503. $result = $this->validatePost();
  504. $this->assertNull($result);
  505. }
  506. /**
  507. * test validatePost uses full URL
  508. *
  509. * @triggers Controller.startup $this->Controller
  510. */
  511. public function testValidatePostSubdirectory(): void
  512. {
  513. // set the base path.
  514. $this->Controller->setRequest($this->Controller->getRequest()
  515. ->withAttribute('base', 'subdir')
  516. ->withAttributE('webroot', 'subdir/'));
  517. Router::setRequest($this->Controller->getRequest());
  518. $event = new Event('Controller.startup', $this->Controller);
  519. $this->Security->startup($event);
  520. // Differs from testValidatePostSimple because of base url
  521. $fields = 'cc9b6af3f33147235ae8f8037b0a71399a2425f2%3A';
  522. $unlocked = '';
  523. $debug = '';
  524. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  525. 'Model' => ['username' => '', 'password' => ''],
  526. '_Token' => compact('fields', 'unlocked', 'debug'),
  527. ]));
  528. $result = $this->validatePost();
  529. $this->assertNull($result);
  530. }
  531. /**
  532. * testValidatePostComplex method
  533. *
  534. * Tests hash validation for multiple records, including locked fields.
  535. *
  536. * @triggers Controller.startup $this->Controller
  537. */
  538. public function testValidatePostComplex(): void
  539. {
  540. $event = new Event('Controller.startup', $this->Controller);
  541. $this->Security->startup($event);
  542. $fields = 'b00b7e5c2e3bf8bc474fb7cfde6f9c2aa06ab9bc%3AAddresses.0.id%7CAddresses.1.id';
  543. $unlocked = '';
  544. $debug = 'not used';
  545. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  546. 'Addresses' => [
  547. '0' => [
  548. 'id' => '123456', 'title' => '', 'first_name' => '', 'last_name' => '',
  549. 'address' => '', 'city' => '', 'phone' => '', 'primary' => '',
  550. ],
  551. '1' => [
  552. 'id' => '654321', 'title' => '', 'first_name' => '', 'last_name' => '',
  553. 'address' => '', 'city' => '', 'phone' => '', 'primary' => '',
  554. ],
  555. ],
  556. '_Token' => compact('fields', 'unlocked', 'debug'),
  557. ]));
  558. $result = $this->validatePost();
  559. $this->assertNull($result);
  560. }
  561. /**
  562. * testValidatePostMultipleSelect method
  563. *
  564. * Test ValidatePost with multiple select elements.
  565. *
  566. * @triggers Controller.startup $this->Controller
  567. */
  568. public function testValidatePostMultipleSelect(): void
  569. {
  570. $event = new Event('Controller.startup', $this->Controller);
  571. $this->Security->startup($event);
  572. $fields = '28dd05f0af314050784b18b3366857e8e8c78e73%3A';
  573. $unlocked = '';
  574. $debug = 'not used';
  575. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  576. 'Tag' => ['Tag' => [1, 2]],
  577. '_Token' => compact('fields', 'unlocked', 'debug'),
  578. ]));
  579. $result = $this->validatePost();
  580. $this->assertNull($result);
  581. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  582. 'Tag' => ['Tag' => [1, 2, 3]],
  583. '_Token' => compact('fields', 'unlocked', 'debug'),
  584. ]));
  585. $result = $this->validatePost();
  586. $this->assertNull($result);
  587. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  588. 'Tag' => ['Tag' => [1, 2, 3, 4]],
  589. '_Token' => compact('fields', 'unlocked', 'debug'),
  590. ]));
  591. $result = $this->validatePost();
  592. $this->assertNull($result);
  593. $fields = '1e4c9269b64756e9b141d364497c5f037b428a37%3A';
  594. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  595. 'User.password' => 'bar', 'User.name' => 'foo', 'User.is_valid' => '1',
  596. 'Tag' => ['Tag' => [1]],
  597. '_Token' => compact('fields', 'unlocked', 'debug'),
  598. ]));
  599. $result = $this->validatePost();
  600. $this->assertNull($result);
  601. }
  602. /**
  603. * testValidatePostCheckbox method
  604. *
  605. * First block tests un-checked checkbox
  606. * Second block tests checked checkbox
  607. *
  608. * @triggers Controller.startup $this->Controller
  609. */
  610. public function testValidatePostCheckbox(): void
  611. {
  612. $event = new Event('Controller.startup', $this->Controller);
  613. $this->Security->startup($event);
  614. $fields = '4697b45f7f430ff3ab73018c20f315eecb0ba5a6%3AModel.valid';
  615. $unlocked = '';
  616. $debug = 'not used';
  617. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  618. 'Model' => ['username' => '', 'password' => '', 'valid' => '0'],
  619. '_Token' => compact('fields', 'unlocked', 'debug'),
  620. ]));
  621. $result = $this->validatePost();
  622. $this->assertNull($result);
  623. $fields = '3f368401f9a8610bcace7746039651066cdcdc38%3A';
  624. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  625. 'Model' => ['username' => '', 'password' => '', 'valid' => '0'],
  626. '_Token' => compact('fields', 'unlocked', 'debug'),
  627. ]));
  628. $result = $this->validatePost();
  629. $this->assertNull($result);
  630. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([]));
  631. $this->Security->startup($event);
  632. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  633. 'Model' => ['username' => '', 'password' => '', 'valid' => '0'],
  634. '_Token' => compact('fields', 'unlocked', 'debug'),
  635. ]));
  636. $result = $this->validatePost();
  637. $this->assertNull($result);
  638. }
  639. /**
  640. * testValidatePostHidden method
  641. *
  642. * @triggers Controller.startup $this->Controller
  643. */
  644. public function testValidatePostHidden(): void
  645. {
  646. $event = new Event('Controller.startup', $this->Controller);
  647. $this->Security->startup($event);
  648. $fields = '96e61bded2b62b0c420116a0eb06a3b3acddb8f1%3AModel.hidden%7CModel.other_hidden';
  649. $unlocked = '';
  650. $debug = 'not used';
  651. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  652. 'Model' => [
  653. 'username' => '', 'password' => '', 'hidden' => '0',
  654. 'other_hidden' => 'some hidden value',
  655. ],
  656. '_Token' => compact('fields', 'unlocked', 'debug'),
  657. ]));
  658. $result = $this->validatePost();
  659. $this->assertNull($result);
  660. }
  661. /**
  662. * testValidatePostDisabledFieldsInData method
  663. *
  664. * Test validating post data with posted unlocked fields.
  665. *
  666. * @triggers Controller.startup $this->Controller
  667. */
  668. public function testValidatePostDisabledFieldsInData(): void
  669. {
  670. $event = new Event('Controller.startup', $this->Controller);
  671. $this->Security->startup($event);
  672. $unlocked = 'Model.username';
  673. $fields = ['Model.hidden', 'Model.password'];
  674. $fields = urlencode(
  675. hash_hmac('sha1', '/articles/index' . serialize($fields) . $unlocked . 'cli', Security::getSalt())
  676. );
  677. $debug = 'not used';
  678. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  679. 'Model' => [
  680. 'username' => 'mark',
  681. 'password' => 'sekret',
  682. 'hidden' => '0',
  683. ],
  684. '_Token' => compact('fields', 'unlocked', 'debug'),
  685. ]));
  686. $result = $this->validatePost();
  687. $this->assertNull($result);
  688. }
  689. /**
  690. * testValidatePostFailNoDisabled method
  691. *
  692. * Test that missing 'unlocked' input causes failure.
  693. *
  694. * @triggers Controller.startup $this->Controller
  695. */
  696. public function testValidatePostFailNoDisabled(): void
  697. {
  698. $event = new Event('Controller.startup', $this->Controller);
  699. $this->Security->startup($event);
  700. $fields = ['Model.hidden', 'Model.password', 'Model.username'];
  701. $fields = urlencode(Security::hash(serialize($fields) . Security::getSalt()));
  702. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  703. 'Model' => [
  704. 'username' => 'mark',
  705. 'password' => 'sekret',
  706. 'hidden' => '0',
  707. ],
  708. '_Token' => compact('fields'),
  709. ]));
  710. $result = $this->validatePost('SecurityException', '\'_Token.unlocked\' was not found in request data.');
  711. $this->assertFalse($result);
  712. }
  713. /**
  714. * testValidatePostFailNoDebug method
  715. *
  716. * Test that missing 'debug' input causes failure.
  717. *
  718. * @triggers Controller.startup $this->Controller
  719. */
  720. public function testValidatePostFailNoDebug(): void
  721. {
  722. $event = new Event('Controller.startup', $this->Controller);
  723. $this->Security->startup($event);
  724. $fields = ['Model.hidden', 'Model.password', 'Model.username'];
  725. $fields = urlencode(Security::hash(serialize($fields) . Security::getSalt()));
  726. $unlocked = '';
  727. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  728. 'Model' => [
  729. 'username' => 'mark',
  730. 'password' => 'sekret',
  731. 'hidden' => '0',
  732. ],
  733. '_Token' => compact('fields', 'unlocked'),
  734. ]));
  735. $result = $this->validatePost('SecurityException', '\'_Token.debug\' was not found in request data.');
  736. $this->assertFalse($result);
  737. }
  738. /**
  739. * testValidatePostFailNoDebugMode method
  740. *
  741. * Test that missing 'debug' input is not the problem when debug mode disabled.
  742. *
  743. * @triggers Controller.startup $this->Controller
  744. */
  745. public function testValidatePostFailNoDebugMode(): void
  746. {
  747. $event = new Event('Controller.startup', $this->Controller);
  748. $this->Security->startup($event);
  749. $fields = ['Model.hidden', 'Model.password', 'Model.username'];
  750. $fields = urlencode(Security::hash(serialize($fields) . Security::getSalt()));
  751. $unlocked = '';
  752. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  753. 'Model' => [
  754. 'username' => 'mark',
  755. 'password' => 'sekret',
  756. 'hidden' => '0',
  757. ],
  758. '_Token' => compact('fields', 'unlocked'),
  759. ]));
  760. Configure::write('debug', false);
  761. $result = $this->validatePost('SecurityException', 'The request has been black-holed');
  762. $this->assertFalse($result);
  763. }
  764. /**
  765. * testValidatePostFailDisabledFieldTampering method
  766. *
  767. * Test that validatePost fails when unlocked fields are changed.
  768. *
  769. * @triggers Controller.startup $this->Controller
  770. */
  771. public function testValidatePostFailDisabledFieldTampering(): void
  772. {
  773. $event = new Event('Controller.startup', $this->Controller);
  774. $this->Security->startup($event);
  775. $unlocked = 'Model.username';
  776. $fields = ['Model.hidden', 'Model.password'];
  777. $fields = urlencode(Security::hash(serialize($fields) . $unlocked . Security::getSalt()));
  778. $debug = urlencode(json_encode([
  779. '/articles/index',
  780. ['Model.hidden', 'Model.password'],
  781. ['Model.username'],
  782. ]));
  783. // Tamper the values.
  784. $unlocked = 'Model.username|Model.password';
  785. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  786. 'Model' => [
  787. 'username' => 'mark',
  788. 'password' => 'sekret',
  789. 'hidden' => '0',
  790. ],
  791. '_Token' => compact('fields', 'unlocked', 'debug'),
  792. ]));
  793. $result = $this->validatePost('SecurityException', 'Missing field \'Model.password\' in POST data, Unexpected unlocked field \'Model.password\' in POST data');
  794. $this->assertFalse($result);
  795. }
  796. /**
  797. * Test that invalid types cause failures.
  798. */
  799. public function testValidatePostFailArrayData(): void
  800. {
  801. $event = new Event('Controller.startup', $this->Controller);
  802. $this->Security->startup($event);
  803. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  804. 'Model' => [
  805. 'username' => 'mark',
  806. 'password' => 'sekret',
  807. ],
  808. '_Token' => [
  809. 'fields' => [],
  810. 'unlocked' => '',
  811. ],
  812. ]));
  813. Configure::write('debug', false);
  814. $result = $this->validatePost('SecurityException', "'_Token.fields' is invalid.");
  815. $this->assertFalse($result);
  816. }
  817. /**
  818. * testValidateHiddenMultipleModel method
  819. *
  820. * @triggers Controller.startup $this->Controller
  821. */
  822. public function testValidateHiddenMultipleModel(): void
  823. {
  824. $event = new Event('Controller.startup', $this->Controller);
  825. $this->Security->startup($event);
  826. $fields = '642b7a6db3b848fab88952b86ea36c572f93df40%3AModel.valid%7CModel2.valid%7CModel3.valid';
  827. $unlocked = '';
  828. $debug = 'not used';
  829. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  830. 'Model' => ['username' => '', 'password' => '', 'valid' => '0'],
  831. 'Model2' => ['valid' => '0'],
  832. 'Model3' => ['valid' => '0'],
  833. '_Token' => compact('fields', 'unlocked', 'debug'),
  834. ]));
  835. $result = $this->validatePost();
  836. $this->assertNull($result);
  837. }
  838. /**
  839. * testValidateHasManyModel method
  840. *
  841. * @triggers Controller.startup $this->Controller
  842. */
  843. public function testValidateHasManyModel(): void
  844. {
  845. $event = new Event('Controller.startup', $this->Controller);
  846. $this->Security->startup($event);
  847. $fields = '792324c8a374772ad82acfb28f0e77e70f8ed3af%3AModel.0.hidden%7CModel.0.valid';
  848. $fields .= '%7CModel.1.hidden%7CModel.1.valid';
  849. $unlocked = '';
  850. $debug = 'not used';
  851. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  852. 'Model' => [
  853. [
  854. 'username' => 'username', 'password' => 'password',
  855. 'hidden' => 'value', 'valid' => '0',
  856. ],
  857. [
  858. 'username' => 'username', 'password' => 'password',
  859. 'hidden' => 'value', 'valid' => '0',
  860. ],
  861. ],
  862. '_Token' => compact('fields', 'unlocked', 'debug'),
  863. ]));
  864. $result = $this->validatePost();
  865. $this->assertNull($result);
  866. }
  867. /**
  868. * testValidateHasManyRecordsPass method
  869. *
  870. * @triggers Controller.startup $this->Controller
  871. */
  872. public function testValidateHasManyRecordsPass(): void
  873. {
  874. $event = new Event('Controller.startup', $this->Controller);
  875. $this->Security->startup($event);
  876. $fields = '7f4bff67558e25ebeea44c84ea4befa8d50b080c%3AAddress.0.id%7CAddress.0.primary%7C';
  877. $fields .= 'Address.1.id%7CAddress.1.primary';
  878. $unlocked = '';
  879. $debug = 'not used';
  880. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  881. 'Address' => [
  882. 0 => [
  883. 'id' => '123',
  884. 'title' => 'home',
  885. 'first_name' => 'Bilbo',
  886. 'last_name' => 'Baggins',
  887. 'address' => '23 Bag end way',
  888. 'city' => 'the shire',
  889. 'phone' => 'N/A',
  890. 'primary' => '1',
  891. ],
  892. 1 => [
  893. 'id' => '124',
  894. 'title' => 'home',
  895. 'first_name' => 'Frodo',
  896. 'last_name' => 'Baggins',
  897. 'address' => '50 Bag end way',
  898. 'city' => 'the shire',
  899. 'phone' => 'N/A',
  900. 'primary' => '1',
  901. ],
  902. ],
  903. '_Token' => compact('fields', 'unlocked', 'debug'),
  904. ]));
  905. $result = $this->validatePost();
  906. $this->assertNull($result);
  907. }
  908. /**
  909. * testValidateNestedNumericSets method
  910. *
  911. * Test that values like Foo.0.1
  912. *
  913. * @triggers Controller.startup $this->Controller
  914. */
  915. public function testValidateNestedNumericSets(): void
  916. {
  917. $event = new Event('Controller.startup', $this->Controller);
  918. $this->Security->startup($event);
  919. $unlocked = '';
  920. $hashFields = ['TaxonomyData'];
  921. $fields = urlencode(
  922. hash_hmac('sha1', '/articles/index' . serialize($hashFields) . $unlocked . 'cli', Security::getSalt())
  923. );
  924. $debug = 'not used';
  925. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  926. 'TaxonomyData' => [
  927. 1 => [[2]],
  928. 2 => [[3]],
  929. ],
  930. '_Token' => compact('fields', 'unlocked', 'debug'),
  931. ]));
  932. $result = $this->validatePost();
  933. $this->assertNull($result);
  934. }
  935. /**
  936. * testValidateHasManyRecords method
  937. *
  938. * validatePost should fail, hidden fields have been changed.
  939. *
  940. * @triggers Controller.startup $this->Controller
  941. */
  942. public function testValidateHasManyRecordsFail(): void
  943. {
  944. $event = new Event('Controller.startup', $this->Controller);
  945. $this->Security->startup($event);
  946. $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3AAddress.0.id%7CAddress.0.primary%7C';
  947. $fields .= 'Address.1.id%7CAddress.1.primary';
  948. $unlocked = '';
  949. $debug = urlencode(json_encode([
  950. '/articles/index',
  951. [
  952. 'Address.0.address',
  953. 'Address.0.city',
  954. 'Address.0.first_name',
  955. 'Address.0.last_name',
  956. 'Address.0.phone',
  957. 'Address.0.title',
  958. 'Address.1.address',
  959. 'Address.1.city',
  960. 'Address.1.first_name',
  961. 'Address.1.last_name',
  962. 'Address.1.phone',
  963. 'Address.1.title',
  964. 'Address.0.id' => '123',
  965. 'Address.0.primary' => '5',
  966. 'Address.1.id' => '124',
  967. 'Address.1.primary' => '1',
  968. ],
  969. [],
  970. ]));
  971. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  972. 'Address' => [
  973. 0 => [
  974. 'id' => '123',
  975. 'title' => 'home',
  976. 'first_name' => 'Bilbo',
  977. 'last_name' => 'Baggins',
  978. 'address' => '23 Bag end way',
  979. 'city' => 'the shire',
  980. 'phone' => 'N/A',
  981. 'primary' => '5',
  982. ],
  983. 1 => [
  984. 'id' => '124',
  985. 'title' => 'home',
  986. 'first_name' => 'Frodo',
  987. 'last_name' => 'Baggins',
  988. 'address' => '50 Bag end way',
  989. 'city' => 'the shire',
  990. 'phone' => 'N/A',
  991. 'primary' => '1',
  992. ],
  993. ],
  994. '_Token' => compact('fields', 'unlocked', 'debug'),
  995. ]));
  996. $result = $this->validatePost('SecurityException', 'Bad Request');
  997. $this->assertFalse($result);
  998. }
  999. /**
  1000. * testValidatePostRadio method
  1001. *
  1002. * Test validatePost with radio buttons.
  1003. *
  1004. * @triggers Controller.startup $this->Controller
  1005. */
  1006. public function testValidatePostRadio(): void
  1007. {
  1008. $event = new Event('Controller.startup', $this->Controller);
  1009. $this->Security->startup($event);
  1010. $fields = 'a709dfdee0a0cce52c4c964a1b8a56159bb081b4%3An%3A0%3A%7B%7D';
  1011. $unlocked = '';
  1012. $debug = urlencode(json_encode([
  1013. '/articles/index',
  1014. [],
  1015. [],
  1016. ]));
  1017. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  1018. '_Token' => compact('fields', 'unlocked', 'debug'),
  1019. ]));
  1020. $result = $this->validatePost('SecurityException', 'Bad Request');
  1021. $this->assertFalse($result);
  1022. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  1023. '_Token' => compact('fields', 'unlocked', 'debug'),
  1024. 'Test' => ['test' => ''],
  1025. ]));
  1026. $result = $this->validatePost();
  1027. $this->assertNull($result);
  1028. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  1029. '_Token' => compact('fields', 'unlocked', 'debug'),
  1030. 'Test' => ['test' => '1'],
  1031. ]));
  1032. $result = $this->validatePost();
  1033. $this->assertNull($result);
  1034. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  1035. '_Token' => compact('fields', 'unlocked', 'debug'),
  1036. 'Test' => ['test' => '2'],
  1037. ]));
  1038. $result = $this->validatePost();
  1039. $this->assertNull($result);
  1040. }
  1041. /**
  1042. * testValidatePostUrlAsHashInput method
  1043. *
  1044. * Test validatePost uses here() as a hash input.
  1045. *
  1046. * @triggers Controller.startup $this->Controller
  1047. */
  1048. public function testValidatePostUrlAsHashInput(): void
  1049. {
  1050. $event = new Event('Controller.startup', $this->Controller);
  1051. $this->Security->startup($event);
  1052. $fields = 'de2ca3670dd06c29558dd98482c8739e86da2c7c%3A';
  1053. $unlocked = '';
  1054. $debug = urlencode(json_encode([
  1055. 'another-url',
  1056. ['Model.username', 'Model.password'],
  1057. [],
  1058. ]));
  1059. $this->Controller->setRequest($this->Controller->getRequest()
  1060. ->withData('Model', ['username' => '', 'password' => ''])
  1061. ->withData('_Token', compact('fields', 'unlocked', 'debug')));
  1062. $this->assertNull($this->validatePost());
  1063. $this->Controller->setRequest($this->Controller->getRequest()
  1064. ->withRequestTarget('/posts/index?page=1'));
  1065. $this->assertFalse($this->validatePost(
  1066. 'SecurityException',
  1067. 'URL mismatch in POST data (expected \'another-url\' but found \'/posts/index?page=1\')'
  1068. ));
  1069. $this->Controller->setRequest($this->Controller->getRequest()
  1070. ->withRequestTarget('/posts/edit/1'));
  1071. $this->assertFalse($this->validatePost(
  1072. 'SecurityException',
  1073. 'URL mismatch in POST data (expected \'another-url\' but found \'/posts/edit/1\')'
  1074. ));
  1075. }
  1076. /**
  1077. * testGenerateToken method
  1078. *
  1079. * Test generateToken().
  1080. */
  1081. public function testGenerateToken(): void
  1082. {
  1083. $request = $this->Controller->getRequest();
  1084. $request = $this->Security->generateToken($request);
  1085. $securityToken = $request->getAttribute('formTokenData');
  1086. $this->assertNotEmpty($securityToken);
  1087. $this->assertSame([], $securityToken['unlockedFields']);
  1088. }
  1089. /**
  1090. * testUnlockedActions method
  1091. *
  1092. * Test unlocked actions.
  1093. *
  1094. * @triggers Controller.startup $this->Controller
  1095. */
  1096. public function testUnlockedActions(): void
  1097. {
  1098. $_SERVER['REQUEST_METHOD'] = 'POST';
  1099. $event = new Event('Controller.startup', $this->Controller);
  1100. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody(['data']));
  1101. $this->Security->unlockedActions = 'index';
  1102. $this->Security->blackHoleCallback = null;
  1103. $result = $this->Controller->Security->startup($event);
  1104. $this->assertNull($result);
  1105. }
  1106. /**
  1107. * testValidatePostDebugFormat method
  1108. *
  1109. * Test that debug token format is right.
  1110. *
  1111. * @triggers Controller.startup $this->Controller
  1112. */
  1113. public function testValidatePostDebugFormat(): void
  1114. {
  1115. $event = new Event('Controller.startup', $this->Controller);
  1116. $this->Security->startup($event);
  1117. $unlocked = 'Model.username';
  1118. $fields = ['Model.hidden', 'Model.password'];
  1119. $fields = urlencode(Security::hash(serialize($fields) . $unlocked . Security::getSalt()));
  1120. $debug = urlencode(json_encode([
  1121. '/articles/index',
  1122. ['Model.hidden', 'Model.password'],
  1123. ['Model.username'],
  1124. ['not expected'],
  1125. ]));
  1126. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  1127. 'Model' => [
  1128. 'username' => 'mark',
  1129. 'password' => 'sekret',
  1130. 'hidden' => '0',
  1131. ],
  1132. '_Token' => compact('fields', 'unlocked', 'debug'),
  1133. ]));
  1134. $result = $this->validatePost('SecurityException', 'Invalid security debug token.');
  1135. $this->assertFalse($result);
  1136. $debug = urlencode(json_encode('not an array'));
  1137. $result = $this->validatePost('SecurityException', 'Invalid security debug token.');
  1138. $this->assertFalse($result);
  1139. }
  1140. /**
  1141. * testBlackholeThrowsException method
  1142. *
  1143. * Test blackhole will now throw passed exception if debug enabled.
  1144. */
  1145. public function testBlackholeThrowsException(): void
  1146. {
  1147. $this->expectException(\Cake\Controller\Exception\SecurityException::class);
  1148. $this->expectExceptionMessage('error description');
  1149. $this->Security->setConfig('blackHoleCallback', '');
  1150. $this->Security->blackHole($this->Controller, 'auth', new SecurityException('error description'));
  1151. }
  1152. /**
  1153. * testBlackholeThrowsBadRequest method
  1154. *
  1155. * Test blackhole will throw BadRequest if debug disabled.
  1156. */
  1157. public function testBlackholeThrowsBadRequest(): void
  1158. {
  1159. $this->Security->setConfig('blackHoleCallback', '');
  1160. $message = $reason = null;
  1161. Configure::write('debug', false);
  1162. try {
  1163. $this->Security->blackHole($this->Controller, 'auth', new SecurityException('error description'));
  1164. } catch (SecurityException $ex) {
  1165. $message = $ex->getMessage();
  1166. $reason = $ex->getReason();
  1167. }
  1168. $this->assertSame('The request has been black-holed', $message);
  1169. $this->assertSame('error description', $reason);
  1170. }
  1171. /**
  1172. * testValidatePostFailTampering method
  1173. *
  1174. * Test that validatePost fails with tampered fields and explanation.
  1175. *
  1176. * @triggers Controller.startup $this->Controller
  1177. */
  1178. public function testValidatePostFailTampering(): void
  1179. {
  1180. $event = new Event('Controller.startup', $this->Controller);
  1181. $this->Security->startup($event);
  1182. $unlocked = '';
  1183. $fields = ['Model.hidden' => 'value', 'Model.id' => '1'];
  1184. $debug = urlencode(json_encode([
  1185. '/articles/index',
  1186. $fields,
  1187. [],
  1188. ]));
  1189. $fields = urlencode(Security::hash(serialize($fields) . $unlocked . Security::getSalt()));
  1190. $fields .= urlencode(':Model.hidden|Model.id');
  1191. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  1192. 'Model' => [
  1193. 'hidden' => 'tampered',
  1194. 'id' => '1',
  1195. ],
  1196. '_Token' => compact('fields', 'unlocked', 'debug'),
  1197. ]));
  1198. $result = $this->validatePost('SecurityException', 'Tampered field \'Model.hidden\' in POST data (expected value \'value\' but found \'tampered\')');
  1199. $this->assertFalse($result);
  1200. }
  1201. /**
  1202. * testValidatePostFailTamperingMutatedIntoArray method
  1203. *
  1204. * Test that validatePost fails with tampered fields and explanation.
  1205. *
  1206. * @triggers Controller.startup $this->Controller
  1207. */
  1208. public function testValidatePostFailTamperingMutatedIntoArray(): void
  1209. {
  1210. $event = new Event('Controller.startup', $this->Controller);
  1211. $this->Security->startup($event);
  1212. $unlocked = '';
  1213. $fields = ['Model.hidden' => 'value', 'Model.id' => '1'];
  1214. $debug = urlencode(json_encode([
  1215. '/articles/index',
  1216. $fields,
  1217. [],
  1218. ]));
  1219. $fields = urlencode(Security::hash(serialize($fields) . $unlocked . Security::getSalt()));
  1220. $fields .= urlencode(':Model.hidden|Model.id');
  1221. $this->Controller->setRequest($this->Controller->getRequest()->withData('Model', [
  1222. 'hidden' => ['some-key' => 'some-value'],
  1223. 'id' => '1',
  1224. ])->withData('_Token', compact('fields', 'unlocked', 'debug')));
  1225. $result = $this->validatePost(
  1226. 'SecurityException',
  1227. 'Unexpected field \'Model.hidden.some-key\' in POST data, Missing field \'Model.hidden\' in POST data'
  1228. );
  1229. $this->assertFalse($result);
  1230. }
  1231. /**
  1232. * testValidatePostUnexpectedDebugToken method
  1233. *
  1234. * Test that debug token should not be sent if debug is disabled.
  1235. *
  1236. * @triggers Controller.startup $this->Controller
  1237. */
  1238. public function testValidatePostUnexpectedDebugToken(): void
  1239. {
  1240. $event = new Event('Controller.startup', $this->Controller);
  1241. $this->Security->startup($event);
  1242. $unlocked = '';
  1243. $fields = ['Model.hidden' => 'value', 'Model.id' => '1'];
  1244. $debug = urlencode(json_encode([
  1245. '/articles/index',
  1246. $fields,
  1247. [],
  1248. ]));
  1249. $fields = urlencode(Security::hash(serialize($fields) . $unlocked . Security::getSalt()));
  1250. $fields .= urlencode(':Model.hidden|Model.id');
  1251. $this->Controller->setRequest($this->Controller->getRequest()->withParsedBody([
  1252. 'Model' => [
  1253. 'hidden' => ['some-key' => 'some-value'],
  1254. 'id' => '1',
  1255. ],
  1256. '_Token' => compact('fields', 'unlocked', 'debug'),
  1257. ]));
  1258. Configure::write('debug', false);
  1259. $result = $this->validatePost('SecurityException', 'Unexpected \'_Token.debug\' found in request data');
  1260. $this->assertFalse($result);
  1261. }
  1262. }