diff --git a/publish/edit.php b/publish/edit.php index b398574..c4f7cc5 100644 --- a/publish/edit.php +++ b/publish/edit.php @@ -64,7 +64,7 @@ Ordinary web users can: write article $action = NULL; if (isset($_POST['action'])) { - $action = strtolower($_POST['action']); + $action = is_string($_POST['action']) ? strtolower($_POST['action']) : NULL; // We can trigger only choose and edit via GET variables. } else if (isset($_GET['action'])) { if ($_GET['action'] == 'choose') { diff --git a/publish/index.php b/publish/index.php index 06cb4f7..092bb8b 100644 --- a/publish/index.php +++ b/publish/index.php @@ -58,7 +58,7 @@ Ordinary web users can: write article */ if (isset($_POST['action'])) - $action = strtolower($_POST['action']); + $action = is_string($_POST['action']) ? strtolower($_POST['action']) : NULL; else $action = NULL; diff --git a/publish/login.php b/publish/login.php index 765e31d..529e4df 100644 --- a/publish/login.php +++ b/publish/login.php @@ -6,13 +6,14 @@ $denied = NULL; $wait_minutes = 0; $captcha_failed = FALSE; $is_admin = NULL; +$login_name = is_string($_POST['username'] ?? NULL) ? $_POST['username'] : ''; if (isset($_SESSION['editor'])) { session_write_close(); header("Location: /publish/edit"); return; } else if (isset($_POST['login']) && !csrf_valid()) { $denied = TRUE; -} else if (isset($_POST['login']) && ($wait = login_blocked_for((string)($_POST['username'] ?? ''))) > 0) { +} else if (isset($_POST['login']) && ($wait = login_blocked_for($login_name)) > 0) { $denied = TRUE; $wait_minutes = (int)ceil($wait / 60); } else if (isset($_POST['login']) && !captcha_verify_response()) { @@ -23,7 +24,7 @@ if (isset($_SESSION['editor'])) { $table = "editors"; open_db('open'); - $username = (string)($_POST['username'] ?? ''); + $username = $login_name; $query = "SELECT * FROM $table WHERE username = ? LIMIT 1"; $getid = $db->execute_query($query, array($username)) or db_fail(); $row = $getid->fetch_assoc(); diff --git a/tests/php/Pages/EditTest.php b/tests/php/Pages/EditTest.php index eceb86c..9bfd7ae 100644 --- a/tests/php/Pages/EditTest.php +++ b/tests/php/Pages/EditTest.php @@ -72,6 +72,13 @@ class EditTest extends PageTestCase { $this->assertSame(array('5', 'Changed', 'Needs work'), array($row['id'], $row['title'], $row['comments'])); } + public function testAnArrayActionIsIgnored(): void { + $this->loginAs(); + $page = $this->post('/publish/edit', array('action' => array('delete'), 'id' => '1')); + $this->assertSame(200, $page['status']); + $this->assertSame(array(), $this->queries('/^(DELETE|INSERT|UPDATE)/')); + } + public function testDeletingNeedsTheToken(): void { $page = $this->post('/publish/edit', array('action' => 'delete', 'id' => '1', 'queue' => '0', 'csrf_token' => 'wrong')); $this->assertStringContainsString('Your session has expired.', $page['body']); diff --git a/tests/php/Pages/LoginTest.php b/tests/php/Pages/LoginTest.php index 7ac7859..19065be 100644 --- a/tests/php/Pages/LoginTest.php +++ b/tests/php/Pages/LoginTest.php @@ -39,6 +39,14 @@ class LoginTest extends PageTestCase { $this->get('/publish/login', array('X-Test-Plain' => '1', 'Host' => 'evil.example'))); } + public function testAnArrayUsernameIsRefused(): void { + $token = $this->csrfToken($this->get('/publish/login')['body']); + $answer = $this->post('/publish/login', array('login' => '1', 'username' => array('ed'), 'password' => 'secret', + 'csrf_token' => $token, 'g-recaptcha-response' => 'pass')); + $this->assertSame(200, $answer['status']); + $this->assertStringContainsString('Access denied.', $answer['body']); + } + public function testTheFormHasATokenAndTheCaptcha(): void { $body = $this->get('/publish/login')['body']; $this->assertSame($this->session()['csrf_token'], $this->csrfToken($body)); diff --git a/tests/php/Pages/SubmitTest.php b/tests/php/Pages/SubmitTest.php index 7aeade2..77596b9 100644 --- a/tests/php/Pages/SubmitTest.php +++ b/tests/php/Pages/SubmitTest.php @@ -111,6 +111,13 @@ class SubmitTest extends PageTestCase { $this->assertSame(array(), $this->queries('/^INSERT/')); } + public function testAnArrayActionIsIgnored(): void { + $page = $this->post('/publish', array('action' => array('post'))); + $this->assertSame(200, $page['status']); + $this->assertStringNotContainsString('Please review your submission.', $page['body']); + $this->assertSame(array(), $this->queries('/^INSERT/')); + } + public function testFieldsAreEscapedInTheForm(): void { $token = $this->csrfToken($this->get('/publish')['body']); $page = $this->post('/publish', $this->story(array('action' => 'preview', 'csrf_token' => $token, 'g-recaptcha-response' => 'pass',