Skip to content

Commit

Permalink
2.7.0 (#126)
Browse files Browse the repository at this point in the history
* Update index.php

- Adding improvements made by @NicolasCARPi (improved file fetcher, code clean up, minimum password constant).
- Added random rassword generator for fresh WonderCMS installs.

* Update index.php

Removing unnecessary default password message.

* Update index.php

Applying "patch" to prevent an admin being tricked into a self attack.

* Update index.php

Reverting for double check / code review.

* Update index.php

- All changes in one place.
- Also removed updateAction function, as it's irrelevant for upcoming version 3.0.0

* Update version

Updating to actual version.
  • Loading branch information
robiso committed Mar 31, 2019
1 parent dbeac2e commit f090f1b
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 54 deletions.
105 changes: 52 additions & 53 deletions index.php
@@ -1,7 +1,7 @@
<?php // WonderCMS (MIT license: wondercms.com/license)

session_start();
define('version', '2.6.0');
define('VERSION', '2.7.0');
mb_internal_encoding('UTF-8');

class wCMS
Expand All @@ -11,6 +11,7 @@ class wCMS
public static $currentPageExists = false;
private static $listeners = [];
private static $db = false;
private const MIN_PASSWORD_LENGTH = 8;

public static function init()
{
Expand All @@ -28,7 +29,6 @@ public static function init()
wCMS::deletePageAction();
wCMS::logoutAction();
wCMS::saveAction();
wCMS::updateAction();
wCMS::uploadFileAction();
wCMS::notifyAction();
wCMS::notFoundReponse();
Expand Down Expand Up @@ -98,14 +98,14 @@ private static function betterSecurityAction()
if (wCMS::$loggedIn && isset($_POST['betterSecurity']) && isset($_POST['token'])) {
if (hash_equals($_POST['token'], wCMS::generateToken())) {
if ($_POST['betterSecurity'] == 'on') {
$contents = wCMS::getExternalFile('https://raw.githubusercontent.com/robiso/wondercms/master/.htaccess-ultimate');
$contents = wCMS::getFileFromRepo('.htaccess-ultimate');
if ($contents) {
file_put_contents('.htaccess', trim($contents));
}
wCMS::alert('success', 'Better security turned ON.');
wCMS::redirect();
} elseif ($_POST['betterSecurity'] == 'off') {
$contents = wCMS::getExternalFile('https://raw.githubusercontent.com/robiso/wondercms/master/.htaccess');
$contents = wCMS::getFileFromRepo('.htaccess');
if ($contents) {
file_put_contents('.htaccess', trim($contents));
}
Expand All @@ -130,8 +130,8 @@ private static function changePasswordAction()
wCMS::alert('danger', 'Wrong password.');
wCMS::redirect();
}
if (strlen($_POST['new_password']) < 4) {
wCMS::alert('danger', 'Password must be longer than 4 characters.');
if (strlen($_POST['new_password']) < self::MIN_PASSWORD_LENGTH) {
wCMS::alert('danger', sprintf('Password must be longer than %d characters.', self::MIN_PASSWORD_LENGTH));
wCMS::redirect();
}
wCMS::set('config', 'password', password_hash($_POST['new_password'], PASSWORD_DEFAULT));
Expand All @@ -146,14 +146,15 @@ private static function createDatabase()
if (wCMS::db() !== false) {
return;
}
$password = wCMS::generatePassword();
wCMS::save([
'config' => [
'dbVersion' => '2.6.0',
'siteTitle' => 'Website title',
'theme' => 'default',
'defaultPage' => 'home',
'login' => 'loginURL',
'password' => password_hash('admin', PASSWORD_DEFAULT),
'password' => password_hash($password, PASSWORD_DEFAULT),
'menuItems' => [
'0' => [
'name' => 'Home',
Expand All @@ -180,7 +181,7 @@ private static function createDatabase()
'description' => 'A short description is also good.',
'content' => '<h1>Website alive!</h1>
<h4><a href="' . wCMS::url('loginURL') . '">Click to login, the password is <b>admin</b>.</a></h4>'
<h4><a href="' . wCMS::url('loginURL') . '">Click to login.</a> Your password is: <b>' . $password . '</b></a></h4>'
],
'example' => [
'title' => 'Example',
Expand Down Expand Up @@ -230,7 +231,7 @@ private static function createMenuItem($content, $menu, $visibility)
}
$db->config->{$field}->{$menuCount} = new stdClass;
wCMS::save($db);
wCMS::set($conf, $field, $menuCount, 'name', str_replace("-", " ", $content));
wCMS::set($conf, $field, $menuCount, 'name', str_replace('-', ' ', $content));
wCMS::set($conf, $field, $menuCount, 'slug', $slug);
wCMS::set($conf, $field, $menuCount, 'visibility', $visibility);
if ($menu) {
Expand All @@ -253,7 +254,7 @@ private static function createPage($slug = false)
$db = wCMS::db();
$db->pages->{(!$slug) ? wCMS::$currentPage : $slug} = new stdClass;
wCMS::save($db);
wCMS::set('pages', (!$slug) ? wCMS::slugify(wCMS::$currentPage) : $slug, 'title', (!$slug) ? mb_convert_case(str_replace("-", " ", wCMS::$currentPage), MB_CASE_TITLE) : mb_convert_case(str_replace("-", " ", $slug), MB_CASE_TITLE));
wCMS::set('pages', (!$slug) ? wCMS::slugify(wCMS::$currentPage) : $slug, 'title', (!$slug) ? mb_convert_case(str_replace('-', ' ', wCMS::$currentPage), MB_CASE_TITLE) : mb_convert_case(str_replace('-', ' ', $slug), MB_CASE_TITLE));
wCMS::set('pages', (!$slug) ? wCMS::slugify(wCMS::$currentPage) : $slug, 'keywords', 'Keywords, are, good, for, search, engines');
wCMS::set('pages', (!$slug) ? wCMS::slugify(wCMS::$currentPage) : $slug, 'description', 'A short description is also good.');
if (!$slug) {
Expand Down Expand Up @@ -291,7 +292,7 @@ private static function deleteFileThemePluginAction()
];
foreach ($deleteList as $entry) {
list($folder, $request) = $entry;
$filename = isset($_REQUEST[$request]) ? str_ireplace(['./', '../', '..', '~', '~/'], null, trim($_REQUEST[$request])) : false;
$filename = isset($_REQUEST[$request]) ? str_ireplace(['/', './', '../', '..', '~', '~/', '\\'], null, trim($_REQUEST[$request])) : false;
if (!$filename || empty($filename)) {
continue;
}
Expand Down Expand Up @@ -346,9 +347,15 @@ public static function footer()
return wCMS::hook('footer', $output)[0];
}

private static function generatePassword()
{
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcefghijklmnopqrstuvwxyz';
return substr(str_shuffle($characters), 0, self::MIN_PASSWORD_LENGTH);
}

public static function generateToken()
{
return (isset($_SESSION["token"])) ? $_SESSION["token"] : $_SESSION["token"] = bin2hex(openssl_random_pseudo_bytes(32));
return (isset($_SESSION['token'])) ? $_SESSION['token'] : $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
}

public static function get()
Expand All @@ -359,24 +366,29 @@ public static function get()
wCMS::$db = wCMS::db();
}
switch ($numArgs) {
case 1: return wCMS::$db->{$args[0]};
case 1:
return wCMS::$db->{$args[0]};
break;
case 2: return wCMS::$db->{$args[0]}->{$args[1]};
case 2:
return wCMS::$db->{$args[0]}->{$args[1]};
break;
case 3: return wCMS::$db->{$args[0]}->{$args[1]}->{$args[2]};
case 3:
return wCMS::$db->{$args[0]}->{$args[1]}->{$args[2]};
break;
case 4: return wCMS::$db->{$args[0]}->{$args[1]}->{$args[2]}->{$args[3]};
break;
default: return false;
case 4:
return wCMS::$db->{$args[0]}->{$args[1]}->{$args[2]}->{$args[3]};
break;
default:
return false;
}
}

public static function getExternalFile($url)
public static function getFileFromRepo($file)
{
$repoUrl = 'https://raw.githubusercontent.com/robiso/wondercms/master/';
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_URL, $repoUrl . $file);
$data = curl_exec($ch);
curl_close($ch);
return $data;
Expand All @@ -389,8 +401,7 @@ private static function getMenuSettings()

private static function getOfficialVersion()
{
$data = trim(wCMS::getExternalFile('https://raw.githubusercontent.com/robiso/wondercms/master/version'));
return $data;
return trim(wCMS::getFileFromRepo('version'));
}

private static function hook()
Expand Down Expand Up @@ -419,7 +430,7 @@ private static function installThemePluginAction()
if (isset($_POST['installLocation'])) {
$installLocation = trim(strtolower($_POST['installLocation']));
$addonURL = $_POST['addonURL'];
$validPaths = array("themes", "plugins");
$validPaths = array('themes', 'plugins');
} else {
wCMS::alert('danger', 'Choose between theme or plugin.');
wCMS::redirect();
Expand Down Expand Up @@ -547,14 +558,14 @@ public static function menu()
public static function notFoundReponse()
{
if (!wCMS::$loggedIn && !wCMS::$currentPageExists) {
header("HTTP/1.1 404 Not Found");
header('HTTP/1.1 404 Not Found');
}
}

public static function notFoundView()
{
if (wCMS::$loggedIn) {
return ['title' => str_replace("-", " ", wCMS::$currentPage), 'description' => '', 'keywords' => '', 'content' => '<h2>Click to create content</h2>'];
return ['title' => str_replace('-', ' ', wCMS::$currentPage), 'description' => '', 'keywords' => '', 'content' => '<h2>Click to create content</h2>'];
}
return wCMS::get('pages', '404');
}
Expand All @@ -568,13 +579,10 @@ private static function notifyAction()
wCMS::alert('info', '<b>This page (' . wCMS::$currentPage . ') doesn\'t exist.</b> Click inside the content below to create it.');
}
if (wCMS::get('config', 'login') === 'loginURL') {
wCMS::alert('warning', 'Change the default admin login URL. (<i>Settings -> Security</i>)', true);
wCMS::alert('danger', 'Change your default password and login URL. (<i>Settings -> Security</i>)', true);
}
if (password_verify('admin', wCMS::get('config', 'password'))) {
wCMS::alert('danger', 'Change the default password. (<i>Settings -> Security</i>)', true);
}
if (wCMS::getOfficialVersion() > version) {
wCMS::alert('info', '<h4><b>New WonderCMS update available</b></h4>- Backup your website and <a href="https://wondercms.com/whatsnew" target="_blank"><u>check what\'s new</u></a> before updating.<form action="' . wCMS::url(wCMS::$currentPage) . '" method="post" class="marginTop5"><button type="submit" class="btn btn-info" name="backup">Download backup</button><input type="hidden" name="token" value="' . wCMS::generateToken() . '"></form><form method="post" class="marginTop5"><button class="btn btn-info" name="update">Update WonderCMS ' . version . ' to ' . wCMS::getOfficialVersion() . '</button><input type="hidden" name="token" value="' . wCMS::generateToken() . '"></form>', true);
if (wCMS::getOfficialVersion() > VERSION) {
wCMS::alert('info', '<h4><b>New WonderCMS update available (requires manual updating)</b></h4>- Backup your website and <a href="https://wondercms.com/whatsnew" target="_blank"><u>check how to manually update WonderCMS</u></a>.<form action="' . wCMS::url(wCMS::$currentPage) . '" method="post" class="marginTop5"><button type="submit" class="btn btn-info" name="backup">Download backup</button><input type="hidden" name="token" value="' . wCMS::generateToken() . '"></form><div class="marginTop5"><a href="https://wondercms.com/whatsnew" target="_blank"><button class="btn btn-info">How to manually update WonderCMS ' . VERSION . ' to ' . wCMS::getOfficialVersion() . '</button></a></div>', true);
}
}

Expand Down Expand Up @@ -701,13 +709,17 @@ public static function set()
$args = func_get_args();
$db = wCMS::db();
switch ($numArgs) {
case 2: $db->{$args[0]} = $args[1];
case 2:
$db->{$args[0]} = $args[1];
break;
case 3: $db->{$args[0]}->{$args[1]} = $args[2];
case 3:
$db->{$args[0]}->{$args[1]} = $args[2];
break;
case 4: $db->{$args[0]}->{$args[1]}->{$args[2]} = $args[3];
case 4:
$db->{$args[0]}->{$args[1]}->{$args[2]} = $args[3];
break;
case 5: $db->{$args[0]}->{$args[1]}->{$args[2]}->{$args[3]} = $args[4];
case 5:
$db->{$args[0]}->{$args[1]}->{$args[2]}->{$args[3]} = $args[4];
break;
}
wCMS::save($db);
Expand Down Expand Up @@ -756,7 +768,7 @@ public static function settings()
foreach ($pluginList as $plugin) {
$output .= '<a href="' . wCMS::url('?deletePlugin=' . $plugin . '&token=' . wCMS::generateToken()) . '" class="btn btn-xs btn-danger" onclick="return confirm(\'Delete ' . $plugin . '?\')" title="Delete plugin">&times;</a> ' . $plugin . '<p></p>';
}
$output .= '</div></div><div role="tabpanel" class="tab-pane" id="security"><p class="subTitle">Admin login URL</p><div class="change"><div data-target="config" id="login" class="editText">' . wCMS::get('config', 'login') . '</div><p class="text-right marginTop5">Important: bookmark your login URL after changing<br /><span class="normalFont text-right"><b>' . wCMS::url(wCMS::get('config', 'login')) . '</b></span></div><p class="subTitle">Password</p><div class="change"><form action="' . wCMS::url(wCMS::$currentPage) . '" method="post"><div class="input-group"><input type="password" name="old_password" class="form-control" placeholder="Old password"><span class="input-group-btn"></span><input type="password" name="new_password" class="form-control" placeholder="New password"><span class="input-group-btn"><button type="submit" class="btn btn-info">Change password</button></span></div><input type="hidden" name="fieldname" value="password"><input type="hidden" name="token" value="' . wCMS::generateToken() . '"></form></div><p class="subTitle">Backup</p><div class="change"><form action="' . wCMS::url(wCMS::$currentPage) . '" method="post"><button type="submit" class="btn btn-block btn-info" name="backup">Backup website</button><input type="hidden" name="token" value="' . wCMS::generateToken() . '"></form></div><p class="text-right marginTop5"><a href="https://github.com/robiso/wondercms/wiki/Restore-backup#how-to-restore-a-backup-in-3-steps" target="_blank">How to restore backup</a></p><p class="subTitle">Better security (Apache only)</p><p>HTTPS redirect, 30 day caching, iframes allowed only from same origin, mime type sniffing prevention, stricter refferer and cookie policy.</p><div class="change"><form method="post"><div class="btn-group btn-group-justified"><div class="btn-group"><button type="submit" class="btn btn-success" name="betterSecurity" value="on">ON (warning: may break your website)</button></div><div class="btn-group"><button type="submit" class="btn btn-danger" name="betterSecurity" value="off">OFF (reset htaccess to default)</button></div></div><input type="hidden" name="token" value="' . wCMS::generateToken() . '"></form></div><p class="text-right marginTop5"><a href="https://github.com/robiso/wondercms/wiki/Better-security-mode-(HTTPS-and-other-features)#important-read-before-turning-this-feature-on" target="_blank">Read more before enabling</a></p></div></div></div><div class="modal-footer clear"><p class="small"><a href="https://wondercms.com" target="_blank">WonderCMS</a> ' . version . ' &nbsp; <b><a href="https://wondercms.com/whatsnew" target="_blank">News</a> &nbsp; <a href="https://wondercms.com/themes" target="_blank">Themes</a> &nbsp; <a href="https://wondercms.com/plugins" target="_blank">Plugins</a> &nbsp; <a href="https://wondercms.com/community" target="_blank">Community</a> &nbsp; <a href="https://github.com/robiso/wondercms/wiki#wondercms-documentation" target="_blank">Docs</a> &nbsp; <a href="https://wondercms.com/donate" target="_blank">Donate</a></b></p></div></div></div></div></div>';
$output .= '</div></div><div role="tabpanel" class="tab-pane" id="security"><p class="subTitle">Admin login URL</p><div class="change"><div data-target="config" id="login" class="editText">' . wCMS::get('config', 'login') . '</div><p class="text-right marginTop5">Important: bookmark your login URL after changing<br /><span class="normalFont text-right"><b>' . wCMS::url(wCMS::get('config', 'login')) . '</b></span></div><p class="subTitle">Password</p><div class="change"><form action="' . wCMS::url(wCMS::$currentPage) . '" method="post"><div class="input-group"><input type="password" name="old_password" class="form-control" placeholder="Old password"><span class="input-group-btn"></span><input type="password" name="new_password" class="form-control" placeholder="New password"><span class="input-group-btn"><button type="submit" class="btn btn-info">Change password</button></span></div><input type="hidden" name="fieldname" value="password"><input type="hidden" name="token" value="' . wCMS::generateToken() . '"></form></div><p class="subTitle">Backup</p><div class="change"><form action="' . wCMS::url(wCMS::$currentPage) . '" method="post"><button type="submit" class="btn btn-block btn-info" name="backup">Backup website</button><input type="hidden" name="token" value="' . wCMS::generateToken() . '"></form></div><p class="text-right marginTop5"><a href="https://github.com/robiso/wondercms/wiki/Restore-backup#how-to-restore-a-backup-in-3-steps" target="_blank">How to restore backup</a></p><p class="subTitle">Better security (Apache only)</p><p>HTTPS redirect, 30 day caching, iframes allowed only from same origin, mime type sniffing prevention, stricter refferer and cookie policy.</p><div class="change"><form method="post"><div class="btn-group btn-group-justified"><div class="btn-group"><button type="submit" class="btn btn-success" name="betterSecurity" value="on">ON (warning: may break your website)</button></div><div class="btn-group"><button type="submit" class="btn btn-danger" name="betterSecurity" value="off">OFF (reset htaccess to default)</button></div></div><input type="hidden" name="token" value="' . wCMS::generateToken() . '"></form></div><p class="text-right marginTop5"><a href="https://github.com/robiso/wondercms/wiki/Better-security-mode-(HTTPS-and-other-features)#important-read-before-turning-this-feature-on" target="_blank">Read more before enabling</a></p></div></div></div><div class="modal-footer clear"><p class="small"><a href="https://wondercms.com" target="_blank">WonderCMS ' . VERSION . '</a> &nbsp; <b><a href="https://wondercms.com/whatsnew" target="_blank">News</a> &nbsp; <a href="https://wondercms.com/themes" target="_blank">Themes</a> &nbsp; <a href="https://wondercms.com/plugins" target="_blank">Plugins</a> &nbsp; <a href="https://wondercms.com/community" target="_blank">Community</a> &nbsp; <a href="https://github.com/robiso/wondercms/wiki#wondercms-documentation" target="_blank">Docs</a> &nbsp; <a href="https://wondercms.com/donate" target="_blank">Donate</a></b></p></div></div></div></div></div>';
return wCMS::hook('settings', $output)[0];
}

Expand All @@ -768,25 +780,10 @@ public static function slugify($text)
return empty($text) ? "-" : $text;
}

private static function updateAction()
{
if (!wCMS::$loggedIn || !isset($_POST['update'])) {
return;
}
if (hash_equals($_POST['token'], wCMS::generateToken())) {
$contents = wCMS::getExternalFile('https://raw.githubusercontent.com/robiso/wondercms/master/index.php');
if ($contents) {
file_put_contents(__FILE__, $contents);
}
wCMS::alert('success', 'WonderCMS successfully updated. Wohoo!');
wCMS::redirect();
}
}

private static function updateDBVersion()
{
if (wCMS::get('config', 'dbVersion') < '2.6.0') {
wCMS::set('config', 'dbVersion', '2.6.0');
if (wCMS::get('config', 'dbVersion') < VERSION) {
wCMS::set('config', 'dbVersion', VERSION);
}
}

Expand All @@ -808,10 +805,12 @@ private static function uploadFileAction()
case UPLOAD_ERR_NO_FILE:
wCMS::alert('danger', 'No file selected.');
wCMS::redirect();
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
wCMS::alert('danger', 'File too large. Change maximum upload size limit or contact your host.');
wCMS::redirect();
break;
default:
wCMS::alert('danger', 'Unknown error.');
wCMS::redirect();
Expand Down
2 changes: 1 addition & 1 deletion version
@@ -1 +1 @@
2.6.0
2.7.0

0 comments on commit f090f1b

Please sign in to comment.