Scrollreveal+cat+datenews

This commit is contained in:
2019-01-07 11:43:39 +01:00
parent e4cac3d01e
commit 762a1ad170
3158 changed files with 140699 additions and 73915 deletions
+2
View File
@@ -109,6 +109,8 @@ class Config extends Data
*/
public function getLanguages()
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use Grav::instance()[\'languages\'] instead', E_USER_DEPRECATED);
return Grav::instance()['languages'];
}
}
+163 -2
View File
@@ -9,6 +9,7 @@
namespace Grav\Common;
use DebugBar\DataCollector\ConfigCollector;
use DebugBar\DataCollector\MessagesCollector;
use DebugBar\JavascriptRenderer;
use DebugBar\StandardDebugBar;
use Grav\Common\Config\Config;
@@ -31,6 +32,11 @@ class Debugger
protected $timers = [];
/** @var string[] $deprecations */
protected $deprecations = [];
protected $errorHandler;
/**
* Debugger constructor.
*/
@@ -41,6 +47,9 @@ class Debugger
$this->debugbar = new StandardDebugBar();
$this->debugbar['time']->addMeasure('Loading', $this->debugbar['time']->getRequestStartTime(), microtime(true));
// Set deprecation collector.
$this->setErrorHandler();
}
/**
@@ -128,9 +137,9 @@ class Debugger
return $this;
}
public function getCaller($ignore = 2)
public function getCaller($limit = 2)
{
$trace = debug_backtrace(false, $ignore);
$trace = debug_backtrace(false, $limit);
return array_pop($trace);
}
@@ -177,6 +186,8 @@ class Debugger
return $this;
}
$this->addDeprecations();
echo $this->renderer->render();
}
@@ -191,6 +202,7 @@ class Debugger
public function sendDataInHeaders()
{
if ($this->enabled()) {
$this->addDeprecations();
$this->debugbar->sendDataInHeaders();
}
@@ -208,6 +220,7 @@ class Debugger
return null;
}
$this->addDeprecations();
$this->timers = [];
return $this->debugbar->getData();
@@ -279,4 +292,152 @@ class Debugger
return $this;
}
public function setErrorHandler()
{
$this->errorHandler = set_error_handler(
[$this, 'deprecatedErrorHandler']
);
}
/**
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @return bool
*/
public function deprecatedErrorHandler($errno, $errstr, $errfile, $errline)
{
if ($errno !== E_USER_DEPRECATED) {
if ($this->errorHandler) {
return \call_user_func($this->errorHandler, $errno, $errstr, $errfile, $errline);
}
return true;
}
if (!$this->enabled()) {
return true;
}
$backtrace = debug_backtrace(false);
// Skip current call.
array_shift($backtrace);
// Skip vendor libraries and the method where error was triggered.
while ($current = array_shift($backtrace)) {
if (isset($current['file']) && strpos($current['file'], 'vendor') !== false) {
continue;
}
if (isset($current['function']) && ($current['function'] === 'user_error' || $current['function'] === 'trigger_error')) {
$current = array_shift($backtrace);
}
break;
}
// Add back last call.
array_unshift($backtrace, $current);
// Filter arguments.
foreach ($backtrace as &$current) {
if (isset($current['args'])) {
$args = [];
foreach ($current['args'] as $arg) {
if (\is_string($arg)) {
$args[] = "'" . $arg . "'";
} elseif (\is_bool($arg)) {
$args[] = $arg ? 'true' : 'false';
} elseif (\is_scalar($arg)) {
$args[] = $arg;
} elseif (\is_object($arg)) {
$args[] = get_class($arg) . ' $object';
} elseif (\is_array($arg)) {
$args[] = '$array';
} else {
$args[] = '$object';
}
}
$current['args'] = $args;
}
}
unset($current);
$this->deprecations[] = [
'message' => $errstr,
'file' => $errfile,
'line' => $errline,
'trace' => $backtrace,
];
// Do not pass forward.
return true;
}
protected function addDeprecations()
{
if (!$this->deprecations) {
return;
}
$collector = new MessagesCollector('deprecated');
$this->addCollector($collector);
$collector->addMessage('Your site is using following deprecated features:');
/** @var array $deprecated */
foreach ($this->deprecations as $deprecated) {
list($message, $scope) = $this->getDepracatedMessage($deprecated);
$collector->addMessage($message, $scope);
}
}
protected function getDepracatedMessage($deprecated)
{
$scope = 'unknown';
if (stripos($deprecated['message'], 'grav') !== false) {
$scope = 'grav';
} elseif (!isset($deprecated['file'])) {
$scope = 'unknown';
} elseif (stripos($deprecated['file'], 'twig') !== false) {
$scope = 'twig';
} elseif (stripos($deprecated['file'], 'yaml') !== false) {
$scope = 'yaml';
} elseif (stripos($deprecated['file'], 'vendor') !== false) {
$scope = 'vendor';
}
$trace = [];
foreach ($deprecated['trace'] as $current) {
$class = isset($current['class']) ? $current['class'] : '';
$type = isset($current['type']) ? $current['type'] : '';
$function = $this->getFunction($current);
if (isset($current['file'])) {
$current['file'] = str_replace(GRAV_ROOT . '/', '', $current['file']);
}
unset($current['class'], $current['type'], $current['function'], $current['args']);
$trace[] = ['call' => $class . $type . $function] + $current;
}
return [
[
'message' => $deprecated['message'],
'trace' => $trace
],
$scope
];
}
protected function getFunction($trace)
{
if (!isset($trace['function'])) {
return '';
}
return $trace['function'] . '(' . implode(', ', $trace['args']) . ')';
}
}
@@ -18,6 +18,13 @@ class BareHandler extends Handler
*/
public function handle()
{
$inspector = $this->getInspector();
$code = $inspector->getException()->getCode();
if ( ($code >= 400) && ($code < 600) )
{
$this->getRun()->sendHttpCode($code);
}
return Handler::QUIT;
}
+3
View File
@@ -74,5 +74,8 @@ class Errors
}
$whoops->register();
// Re-register deprecation handler.
$grav['debugger']->setErrorHandler();
}
}
@@ -35,6 +35,10 @@ class SimplePageHandler extends Handler
$cssFile = $this->getResource("error.css");
$code = $inspector->getException()->getCode();
if ( ($code >= 400) && ($code < 600) )
{
$this->getRun()->sendHttpCode($code);
}
$message = $inspector->getException()->getMessage();
if ($inspector->getException() instanceof \ErrorException) {
@@ -82,4 +82,28 @@ trait CompiledFile
return parent::content($var);
}
/**
* Serialize file.
*/
public function __sleep()
{
return [
'filename',
'extension',
'raw',
'content',
'settings'
];
}
/**
* Unserialize file.
*/
public function __wakeup()
{
if (!isset(static::$instances[$this->filename])) {
static::$instances[$this->filename] = $this;
}
}
}
+2 -3
View File
@@ -9,7 +9,7 @@
namespace Grav\Common;
/**
* @deprecated 2.0
* @deprecated 1.4 Use Grav::instance() instead
*/
trait GravTrait
{
@@ -24,8 +24,7 @@ trait GravTrait
self::$grav = Grav::instance();
}
$caller = self::$grav['debugger']->getCaller();
self::$grav['debugger']->addMessage("Deprecated GravTrait used in {$caller['file']}", 'deprecated');
user_error(__TRAIT__ . ' is deprecated since Grav 1.4, use Grav::instance() instead', E_USER_DEPRECATED);
return self::$grav;
}
+1 -1
View File
@@ -117,7 +117,7 @@ class Excerpts
*/
public static function processLinkExcerpt($excerpt, Page $page, $type = 'link')
{
$url = htmlspecialchars_decode(urldecode($excerpt['element']['attributes']['href']));
$url = htmlspecialchars_decode(rawurldecode($excerpt['element']['attributes']['href']));
$url_parts = static::parseUrl($url);
+2 -2
View File
@@ -181,7 +181,7 @@ class Language
$uri = preg_replace("/\\" . $matches[1] . '/', '', $uri, 1);
// Store in session if language is different.
if (isset($this->grav['session']) && $this->grav['session']->started()
if (isset($this->grav['session']) && $this->grav['session']->isStarted()
&& $this->config->get('system.languages.session_store_active', true)
&& $this->grav['session']->active_language != $this->active
) {
@@ -189,7 +189,7 @@ class Language
}
} else {
// Try getting language from the session, else no active.
if (isset($this->grav['session']) && $this->grav['session']->started()
if (isset($this->grav['session']) && $this->grav['session']->isStarted()
&& $this->config->get('system.languages.session_store_active', true)) {
$this->active = $this->grav['session']->active_language ?: null;
}
+5 -3
View File
@@ -137,7 +137,7 @@ class Page implements PageInterface
$this->metadata();
$this->url();
$this->visible();
$this->modularTwig($this->slug[0] === '_');
$this->modularTwig(strpos($this->slug(), '_') === 0);
$this->setPublishState();
$this->published();
$this->urlExtension();
@@ -195,7 +195,7 @@ class Page implements PageInterface
$route = isset($aPage->header()->routes['default']) ? $aPage->header()->routes['default'] : $aPage->rawRoute();
if (!$route) {
$route = $aPage->slug();
$route = $aPage->route();
}
if ($onlyPublished && !$aPage->published()) {
@@ -764,6 +764,8 @@ class Page implements PageInterface
// pages.markdown_extra is deprecated, but still check it...
if (!isset($defaults['extra']) && (isset($this->markdown_extra) || $config->get('system.pages.markdown_extra') !== null)) {
user_error('Configuration option \'system.pages.markdown_extra\' is deprecated since Grav 1.5, use \'system.pages.markdown.extra\' instead', E_USER_DEPRECATED);
$defaults['extra'] = $this->markdown_extra ?: $config->get('system.pages.markdown_extra');
}
@@ -1582,7 +1584,7 @@ class Page implements PageInterface
}
if (empty($this->slug)) {
$this->slug = $this->adjustRouteCase(preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder));
$this->slug = $this->adjustRouteCase(preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder)) ?: null;
}
@@ -47,7 +47,7 @@ class InitializeProcessor extends ProcessorBase implements ProcessorInterface
// Redirect pages with trailing slash if configured to do so.
$path = $uri->path() ?: '/';
if ($path !== '/' && $config->get('system.pages.redirect_trailing_slash', false) && Utils::endsWith($path, '/')) {
$this->container->redirect(rtrim($path, '/'), 302);
$this->container->redirectLangSafe(rtrim($path, '/'));
}
$this->container->setLocale();
+158
View File
@@ -0,0 +1,158 @@
<?php
/**
* @package Grav.Common
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common;
class Security
{
public static function detectXssFromPages($pages, callable $status = null)
{
$routes = $pages->routes();
// Remove duplicate for homepage
unset($routes['/']);
$list = [];
// // This needs Symfony 4.1 to work
// $status && $status([
// 'type' => 'count',
// 'steps' => count($routes),
// ]);
foreach ($routes as $path) {
$status && $status([
'type' => 'progress',
]);
try {
$page = $pages->get($path);
// call the content to load/cache it
$header = (array) $page->header();
$content = $page->value('content');
$data = ['header' => $header, 'content' => $content];
$results = Security::detectXssFromArray($data);
if (!empty($results)) {
$list[$page->filePathClean()] = $results;
}
} catch (\Exception $e) {
continue;
}
}
return $list;
}
/**
* @param array $array Array such as $_POST or $_GET
* @param string $prefix Prefix for returned values.
* @return array Returns flatten list of potentially dangerous input values, such as 'data.content'.
*/
public static function detectXssFromArray(array $array, $prefix = '')
{
$list = [];
foreach ($array as $key => $value) {
if (\is_array($value)) {
$list[] = static::detectXssFromArray($value, $prefix . $key . '.');
}
if ($result = static::detectXss($value)) {
$list[] = [$prefix . $key => $result];
}
}
if (!empty($list)) {
return array_merge(...$list);
}
return $list;
}
/**
* Determine if string potentially has a XSS attack. This simple function does not catch all XSS and it is likely to
* return false positives because of it tags all potentially dangerous HTML tags and attributes without looking into
* their content.
*
* @param string $string The string to run XSS detection logic on
* @return boolean|string Type of XSS vector if the given `$string` may contain XSS, false otherwise.
*
* Copies the code from: https://github.com/symphonycms/xssfilter/blob/master/extension.driver.php#L138
*/
public static function detectXss($string)
{
// Skip any null or non string values
if (null === $string || !\is_string($string) || empty($string)) {
return false;
}
// Keep a copy of the original string before cleaning up
$orig = $string;
// URL decode
$string = urldecode($string);
// Convert Hexadecimals
$string = (string)preg_replace_callback('!(&#|\\\)[xX]([0-9a-fA-F]+);?!u', function($m) {
return \chr(hexdec($m[2]));
}, $string);
// Clean up entities
$string = preg_replace('!(&#0+[0-9]+)!u','$1;', $string);
// Decode entities
$string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
// Strip whitespace characters
$string = preg_replace('!\s!u','', $string);
$config = Grav::instance()['config'];
$dangerous_tags = $config->get('security.xss_dangerous_tags');
$dangerous_tags = array_map('preg_quote', array_map("trim", $dangerous_tags));
$enabled_rules = $config->get('security.xss_enabled');
// Set the patterns we'll test against
$patterns = [
// Match any attribute starting with "on" or xmlns
'on_events' => '#(<[^>]+[[a-z\x00-\x20\"\'\/])(\son|\sxmlns)[a-z].*=>?#iUu',
// Match javascript:, livescript:, vbscript:, mocha:, feed: and data: protocols
'invalid_protocols' => '#((java|live|vb)script|mocha|feed|data):.*?#iUu',
// Match -moz-bindings
'moz_binding' => '#-moz-binding[a-z\x00-\x20]*:#u',
// Match style attributes
'html_inline_styles' => '#(<[^>]+[a-z\x00-\x20\"\'\/])(style=[^>]*(url\:|x\:expression).*)>?#iUu',
// Match potentially dangerous tags
'dangerous_tags' => '#</*(' . implode('|', $dangerous_tags ) . ')[^>]*>?#ui'
];
// Iterate over rules and return label if fail
foreach ((array) $patterns as $name => $regex) {
if ($enabled_rules[$name] === true) {
if (preg_match($regex, $string) || preg_match($regex, $orig)) {
return $name;
}
}
}
return false;
}
}
@@ -91,7 +91,7 @@ class SessionServiceProvider implements ServiceProviderInterface
// Define session message service.
$container['messages'] = function ($c) {
if (!isset($c['session']) || !$c['session']->started()) {
if (!isset($c['session']) || !$c['session']->isStarted()) {
/** @var Debugger $debugger */
$debugger = $c['debugger'];
$debugger->addMessage('Inactive session: session messages may disappear', 'warming');
+9 -3
View File
@@ -15,10 +15,12 @@ class Session extends \Grav\Framework\Session\Session
/**
* @return \Grav\Framework\Session\Session
* @deprecated 1.5
* @deprecated 1.5 Use getInstance() method instead
*/
public static function instance()
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use getInstance() method instead', E_USER_DEPRECATED);
return static::getInstance();
}
@@ -51,10 +53,12 @@ class Session extends \Grav\Framework\Session\Session
* Returns attributes.
*
* @return array Attributes
* @deprecated 1.5
* @deprecated 1.5 Use getAll() method instead
*/
public function all()
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use getAll() method instead', E_USER_DEPRECATED);
return $this->getAll();
}
@@ -62,10 +66,12 @@ class Session extends \Grav\Framework\Session\Session
* Checks if the session was started.
*
* @return Boolean
* @deprecated 1.5
* @deprecated 1.5 Use isStarted() method instead
*/
public function started()
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use isStarted() method instead', E_USER_DEPRECATED);
return $this->isStarted();
}
@@ -8,7 +8,7 @@
namespace Grav\Common\Twig\Node;
class TwigNodeScript extends \Twig_Node implements \Twig_NodeOutputInterface
class TwigNodeScript extends \Twig_Node implements \Twig_NodeCaptureInterface
{
protected $tagName = 'script';
@@ -8,7 +8,7 @@
namespace Grav\Common\Twig\Node;
class TwigNodeStyle extends \Twig_Node implements \Twig_NodeOutputInterface
class TwigNodeStyle extends \Twig_Node implements \Twig_NodeCaptureInterface
{
protected $tagName = 'style';
@@ -8,7 +8,7 @@
namespace Grav\Common\Twig\Node;
class TwigNodeSwitch extends \Twig_Node implements \Twig_NodeOutputInterface
class TwigNodeSwitch extends \Twig_Node
{
public function __construct(
\Twig_Node $value,
+35 -3
View File
@@ -102,6 +102,28 @@ class Twig
$this->loader = new \Twig_Loader_Filesystem($this->twig_paths);
// Register all other prefixes as namespaces in twig
foreach ($locator->getPaths('theme') as $prefix => $_) {
if ($prefix === '') {
continue;
}
$twig_paths = [];
// handle language templates if available
if ($language->enabled()) {
$lang_templates = $locator->findResource('theme://'.$prefix.'templates/' . ($active_language ? $active_language : $language->getDefault()));
if ($lang_templates) {
$twig_paths[] = $lang_templates;
}
}
$twig_paths = array_merge($twig_paths, $locator->findResources('theme://'.$prefix.'templates'));
$namespace = trim($prefix, '/');
$this->loader->setPaths($twig_paths, $namespace);
}
$this->grav->fireEvent('onTwigLoader');
$this->loaderArray = new \Twig_Loader_Array([]);
@@ -115,9 +137,13 @@ class Twig
if (!$config->get('system.strict_mode.twig_compat', true)) {
// Force autoescape on for all files if in strict mode.
$params['autoescape'] = true;
$params['autoescape'] = 'html';
} elseif (!empty($this->autoescape)) {
$params['autoescape'] = $this->autoescape;
$params['autoescape'] = $this->autoescape ? 'html' : false;
}
if (empty($params['autoescape'])) {
user_error('Grav 2.0 will have Twig auto-escaping forced on (can be emulated by turning off \'system.strict_mode.twig_compat\' setting in your configuration)', E_USER_DEPRECATED);
}
$this->twig = new TwigEnvironment($loader_chain, $params);
@@ -411,8 +437,14 @@ class Twig
* Overrides the autoescape setting
*
* @param boolean $state
* @deprecated 1.5
*/
public function setAutoescape($state) {
public function setAutoescape($state)
{
if (!$state) {
user_error(__CLASS__ . '::' . __FUNCTION__ . '(false) is deprecated since Grav 1.5', E_USER_DEPRECATED);
}
$this->autoescape = (bool) $state;
}
}
+26 -3
View File
@@ -11,6 +11,7 @@ namespace Grav\Common\Twig;
use Grav\Common\Grav;
use Grav\Common\Page\Collection;
use Grav\Common\Page\Media;
use Grav\Common\Security;
use Grav\Common\Twig\TokenParser\TwigTokenParserScript;
use Grav\Common\Twig\TokenParser\TwigTokenParserStyle;
use Grav\Common\Twig\TokenParser\TwigTokenParserSwitch;
@@ -105,9 +106,9 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
// Casting values
new \Twig_SimpleFilter('string', [$this, 'stringFilter']),
new \Twig_SimpleFilter('int', [$this, 'intFilter'], ['is_safe' => true]),
new \Twig_SimpleFilter('int', [$this, 'intFilter'], ['is_safe' => ['all']]),
new \Twig_SimpleFilter('bool', [$this, 'boolFilter']),
new \Twig_SimpleFilter('float', [$this, 'floatFilter'], ['is_safe' => true]),
new \Twig_SimpleFilter('float', [$this, 'floatFilter'], ['is_safe' => ['all']]),
new \Twig_SimpleFilter('array', [$this, 'arrayFilter']),
];
}
@@ -155,7 +156,8 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
new \Twig_SimpleFunction('read_file', [$this, 'readFileFunc']),
new \Twig_SimpleFunction('nicenumber', [$this, 'niceNumberFunc']),
new \Twig_SimpleFunction('nicefilesize', [$this, 'niceFilesizeFunc']),
new \Twig_SimpleFunction('nicetime', [$this, 'nicetimeFilter']),
new \Twig_SimpleFunction('nicetime', [$this, 'nicetimeFunc']),
new \Twig_SimpleFunction('xss', [$this, 'xssFunc']),
// Translations
new \Twig_simpleFunction('t', [$this, 'translate']),
@@ -530,6 +532,27 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
return "$difference $periods[$j] {$tense}";
}
/**
* Allow quick check of a string for XSS Vulnerabilities
*
* @param $string
* @return bool|string|array
*/
public function xssFunc($data)
{
if (is_array($data)) {
$results = Security::detectXssFromArray($data);
} else {
return Security::detectXss($data);
}
$results_parts = array_map(function($value, $key) {
return $key.': \''.$value . '\'';
}, array_values($results), array_keys($results));
return implode(', ', $results_parts);
}
/**
* @param $string
*
+4
View File
@@ -15,6 +15,7 @@ use Grav\Common\Page\Pages;
use Grav\Framework\Route\RouteFactory;
use Grav\Framework\Uri\UriFactory;
use Grav\Framework\Uri\UriPartsFilter;
use RocketTheme\Toolbox\Event\Event;
class Uri
{
@@ -1284,6 +1285,9 @@ class Uri
} elseif (!empty($_POST)) {
$this->post = (array)$_POST;
}
$event = new Event(['post' => &$this->post]);
Grav::instance()->fireEvent('onHttpPostFilter', $event);
}
if ($this->post && null !== $element) {
+27
View File
@@ -266,6 +266,8 @@ class User extends Data
*/
public function authorise($action)
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use authorize() method instead', E_USER_DEPRECATED);
return $this->authorize($action);
}
@@ -284,4 +286,29 @@ class User extends Data
return 'https://www.gravatar.com/avatar/' . md5($this->email);
}
/**
* Serialize user.
*/
public function __sleep()
{
return [
'items',
'storage'
];
}
/**
* Unserialize user.
*/
public function __wakeup()
{
$this->gettersVariable = 'items';
$this->nestedSeparator = '.';
if (null === $this->blueprints) {
$blueprints = new Blueprints;
$this->blueprints = $blueprints->get('user/account');
}
}
}
+74
View File
@@ -478,6 +478,51 @@ abstract class Utils
return $default;
}
/**
* Return the mimetype based on filename
*
* @param string $filename Filename or path to file
* @param string $default default value
*
* @return string
*/
public static function getMimeByFilename($filename, $default = 'application/octet-stream')
{
return static::getMimeByExtension(pathinfo($filename, PATHINFO_EXTENSION), $default);
}
/**
* Return the mimetype based on existing local file
*
* @param string $filename Path to the file
*
* @return string|bool
*/
public static function getMimeByLocalFile($filename, $default = 'application/octet-stream')
{
$type = false;
// For local files we can detect type by the file content.
if (!stream_is_local($filename) || !file_exists($filename)) {
return false;
}
// Prefer using finfo if it exists.
if (\extension_loaded('fileinfo')) {
$finfo = finfo_open(FILEINFO_SYMLINK | FILEINFO_MIME_TYPE);
$type = finfo_file($finfo, $filename);
finfo_close($finfo);
} else {
// Fall back to use getimagesize() if it is available (not recommended, but better than nothing)
$info = @getimagesize($filename);
if ($info) {
$type = $info['mime'];
}
}
return $type ?: static::getMimeByFilename($filename, $default);
}
/**
* Return the mimetype based on filename extension
*
@@ -520,6 +565,33 @@ abstract class Utils
return $default;
}
/**
* Returns true if filename is considered safe.
*
* @param string $filename
* @return bool
*/
public static function checkFilename($filename)
{
$dangerous_extensions = Grav::instance()['config']->get('security.uploads_dangerous_extensions', []);
array_walk($dangerous_extensions, function(&$val) {
$val = '.' . $val;
});
$extension = '.' . pathinfo($filename, PATHINFO_EXTENSION);
return !(
// Empty filenames are not allowed.
!$filename
// Filename should not contain horizontal/vertical tabs, newlines, nils or back/forward slashes.
|| strtr($filename, "\t\v\n\r\0\\/", '_______') !== $filename
// Filename should not start or end with dot or space.
|| trim($filename, '. ') !== $filename
// Filename should not contain .php in it.
|| static::contains($extension, $dangerous_extensions)
);
}
/**
* Normalize path by processing relative `.` and `..` syntax and merging path
*
@@ -696,6 +768,8 @@ abstract class Utils
*/
public static function resolve(array $array, $path, $default = null)
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use getDotNotation() method instead', E_USER_DEPRECATED);
return static::getDotNotation($array, $path, $default);
}
+12 -13
View File
@@ -77,11 +77,11 @@ class InstallCommand extends ConsoleCommand
} else {
$this->output->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder');
if ($this->input->getArgument('destination')) {
$this->output->writeln('<yellow>HINT</yellow> <info>Are you trying to install a plugin or a theme? Make sure you use <cyan>bin/gpm install <something></cyan>, not <cyan>bin/grav install</cyan>. This command is only used to install Grav skeletons.');
$this->output->writeln('<yellow>HINT</yellow> <info>Are you trying to install a plugin or a theme? Make sure you use <cyan>bin/gpm install <something></cyan>, not <cyan>bin/grav install</cyan>. This command is only used to install Grav skeletons.');
} else {
$this->output->writeln('<yellow>HINT</yellow> <info>Are you trying to install Grav? Grav is already installed. You need to run this command only if you download a skeleton from GitHub directly.');
$this->output->writeln('<yellow>HINT</yellow> <info>Are you trying to install Grav? Grav is already installed. You need to run this command only if you download a skeleton from GitHub directly.');
}
return;
}
@@ -169,19 +169,18 @@ class InstallCommand extends ConsoleCommand
}
if (!$from) {
$this->output->writeln('<red>source: ' . $from . ' does not exists, skipping...</red>');
$this->output->writeln('');
}
if (!file_exists($to)) {
symlink($from, $to);
$this->output->writeln('<green>SUCCESS</green> symlinked <magenta>' . $data['src'] . '</magenta> -> <cyan>' . $data['path'] . '</cyan>');
$this->output->writeln('<red>source for ' . $data['src'] . ' does not exists, skipping...</red>');
$this->output->writeln('');
} else {
$this->output->writeln('<red>destination: ' . $to . ' already exists, skipping...</red>');
$this->output->writeln('');
if (!file_exists($to)) {
symlink($from, $to);
$this->output->writeln('<green>SUCCESS</green> symlinked <magenta>' . $data['src'] . '</magenta> -> <cyan>' . $data['path'] . '</cyan>');
$this->output->writeln('');
} else {
$this->output->writeln('<red>destination: ' . $to . ' already exists, skipping...</red>');
$this->output->writeln('');
}
}
}
}
}
@@ -0,0 +1,113 @@
<?php
/**
* @package Grav.Console
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Console\Cli;
use Grav\Common\Grav;
use Grav\Common\Security;
use Grav\Console\ConsoleCommand;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Style\SymfonyStyle;
class SecurityCommand extends ConsoleCommand
{
/** @var ProgressBar $progress */
protected $progress;
/**
*
*/
protected function configure()
{
$this
->setName("security")
->setDescription("Capable of running various Security checks")
->setHelp('The <info>security</info> runs various security checks on your Grav site');
$this->source = getcwd();
}
/**
* @return int|null|void
*/
protected function serve()
{
/** @var Grav $grav */
$grav = Grav::instance();
$grav['uri']->init();
$grav['config']->init();
$grav['debugger']->enabled(false);
$grav['streams'];
$grav['plugins']->init();
$grav['themes']->init();
$grav['twig']->init();
$grav['pages']->init();
$this->progress = new ProgressBar($this->output, (count($grav['pages']->routes()) - 1));
$this->progress->setFormat('Scanning <cyan>%current%</cyan> pages [<green>%bar%</green>] <white>%percent:3s%%</white> %elapsed:6s%');
$this->progress->setBarWidth(100);
$io = new SymfonyStyle($this->input, $this->output);
$io->title('Grav Security Check');
$output = Security::detectXssFromPages($grav['pages'], [$this, 'outputProgress']);
$io->newline(2);
if (!empty($output)) {
$counter = 1;
foreach ($output as $route => $results) {
$results_parts = array_map(function($value, $key) {
return $key.': \''.$value . '\'';
}, array_values($results), array_keys($results));
$io->writeln($counter++ .' - <cyan>' . $route . '</cyan> → <red>' . implode(', ', $results_parts) . '</red>');
}
$io->error('Security Scan complete: ' . count($output) . ' potential XSS issues found...');
} else {
$io->success('Security Scan complete: No issues found...');
}
$io->newline(1);
}
/**
* @param $args
*/
public function outputProgress($args)
{
switch ($args['type']) {
case 'count':
$steps = $args['steps'];
$freq = intval($steps > 100 ? round($steps / 100) : $steps);
$this->progress->setMaxSteps($steps);
$this->progress->setRedrawFrequency($freq);
break;
case 'progress':
if (isset($args['complete']) && $args['complete']) {
$this->progress->finish();
} else {
$this->progress->advance();
}
break;
}
}
}
@@ -29,6 +29,8 @@ class IniFormatter implements FormatterInterface
*/
public function getFileExtension()
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use getDefaultFileExtension() method instead', E_USER_DEPRECATED);
return $this->getDefaultFileExtension();
}
@@ -27,6 +27,8 @@ class JsonFormatter implements FormatterInterface
*/
public function getFileExtension()
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use getDefaultFileExtension() method instead', E_USER_DEPRECATED);
return $this->getDefaultFileExtension();
}
@@ -33,6 +33,8 @@ class MarkdownFormatter implements FormatterInterface
*/
public function getFileExtension()
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use getDefaultFileExtension() method instead', E_USER_DEPRECATED);
return $this->getDefaultFileExtension();
}
@@ -29,6 +29,8 @@ class SerializeFormatter implements FormatterInterface
*/
public function getFileExtension()
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use getDefaultFileExtension() method instead', E_USER_DEPRECATED);
return $this->getDefaultFileExtension();
}
@@ -34,6 +34,8 @@ class YamlFormatter implements FormatterInterface
*/
public function getFileExtension()
{
user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.5, use getDefaultFileExtension() method instead', E_USER_DEPRECATED);
return $this->getDefaultFileExtension();
}
+11 -3
View File
@@ -14,6 +14,8 @@ namespace Grav\Framework\Session;
*/
class Session implements SessionInterface
{
protected $options;
/**
* @var bool
*/
@@ -182,7 +184,10 @@ class Session implements SessionInterface
unset($_COOKIE[session_name()]);
}
$options = $readonly ? ['read_and_close' => '1'] : [];
$options = $this->options;
if ($readonly) {
$options['read_and_close'] = '1';
}
$success = @session_start($options);
if (!$success) {
@@ -224,8 +229,10 @@ class Session implements SessionInterface
$params['httponly']
);
session_unset();
session_destroy();
if ($this->isSessionStarted()) {
session_unset();
session_destroy();
}
$this->started = false;
@@ -335,6 +342,7 @@ class Session implements SessionInterface
$value = (string)$value;
}
$this->options[$key] = $value;
ini_set($key, $value);
}
}