Merge branch 'release/1.7.46'

This commit is contained in:
Andy Miller 2024-05-15 17:16:00 +01:00
commit faa8ee5fe1
No known key found for this signature in database
GPG Key ID: 9F2CF38AEBDB0AE0
9 changed files with 68 additions and 24 deletions

View File

@ -1,7 +1,19 @@
# v1.7.46
## 05/15/2024
1. [](#improved)
* Better handling of external protocols in `Utils::url()` such as `mailto:`, `tel:`, etc.
* Handle `GRAV_ROOT` or `GRAV_WEBROOT` when `/` [#3667](https://github.com/getgrav/grav/pull/3667)
1. [](#bugfix)
* Fixes for multi-lang taxonomy when reinitializing the languages (e.g. LangSwitcher plugin)
* Ensure the full filepath is checked for invalid filename in `MediaUploadTrait::checkFileMetadata()`
* Fixed a bug in the `on_events` REGEX pattern of `Security::detectXss()` as it was not matching correctly.
* Fixed an issue where `read_file()` Twig function could be used nefariously in content [#GHSA-f8v5-jmfh-pr69](https://github.com/getgrav/grav/security/advisories/GHSA-f8v5-jmfh-pr69)
# v1.7.45
## 03/18/2024
1. [](#news)
1. [](#new)
* Added new Image trait for `decoding` attribute [#3796](https://github.com/getgrav/grav/pull/3796)
1. [](#bugfix)
* Fixed some multibyte issues in Inflector class [#732](https://github.com/getgrav/grav/issues/732)
@ -117,6 +129,7 @@
1. [](#improved)
* Removed outdated `xcache` setting [#3615](https://github.com/getgrav/grav/pull/3615)
* Updated `robots.txt` [#3625](https://github.com/getgrav/grav/pull/3625)
* Handle the situation when GRAV_ROOT or GRAV_WEBROOT are `/` [#3625](https://github.com/getgrav/grav/pull/3667)
1. [](#bugfix)
* Fixed `force_ssl` redirect in case of undefined hostname [#3702](https://github.com/getgrav/grav/pull/3702)
* Fixed an issue with duplicate identical page paths

View File

@ -9,7 +9,7 @@
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '1.7.45');
define('GRAV_VERSION', '1.7.46');
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
define('GRAV_TESTING', false);
@ -26,12 +26,12 @@ if (!defined('DS')) {
// Absolute path to Grav root. This is where Grav is installed into.
if (!defined('GRAV_ROOT')) {
$path = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, getenv('GRAV_ROOT') ?: getcwd()), DS);
define('GRAV_ROOT', $path);
define('GRAV_ROOT', $path ?: DS);
}
// Absolute path to Grav webroot. This is the path where your site is located in.
if (!defined('GRAV_WEBROOT')) {
$path = rtrim(getenv('GRAV_WEBROOT') ?: GRAV_ROOT, DS);
define('GRAV_WEBROOT', $path);
define('GRAV_WEBROOT', $path ?: DS);
}
// Relative path to user folder. This path needs to be located under GRAV_WEBROOT.
if (!defined('GRAV_USER_PATH')) {

View File

@ -218,7 +218,7 @@ class Backups
if ($locator->isStream($backup_root)) {
$backup_root = $locator->findResource($backup_root);
} else {
$backup_root = rtrim(GRAV_ROOT . $backup_root, '/');
$backup_root = rtrim(GRAV_ROOT . $backup_root, DS) ?: DS;
}
if (!$backup_root || !file_exists($backup_root)) {

View File

@ -156,7 +156,7 @@ trait MediaUploadTrait
$filepath = $folder . $filename;
// Check if the filename is allowed.
if (!Utils::checkFilename($filename)) {
if (!Utils::checkFilename($filepath)) {
throw new RuntimeException(
sprintf($this->translate('PLUGIN_ADMIN.FILEUPLOAD_UNABLE_TO_UPLOAD'), $filepath, $this->translate('PLUGIN_ADMIN.BAD_FILENAME'))
);

View File

@ -225,7 +225,7 @@ class Security
// Set the patterns we'll test against
$patterns = [
// Match any attribute starting with "on" or xmlns
'on_events' => '#(<[^>]+[[a-z\x00-\x20\"\'\/])([\s\/]on|\sxmlns)[a-z].*=>?#iUu',
'on_events' => '#(<[^>]+[a-z\x00-\x20\"\'\/])(on[a-z]+|xmlns)\s*=[\s|\'\"].*[\s|\'\"]>#iUu',
// Match javascript:, livescript:, vbscript:, mocha:, feed: and data: protocols
'invalid_protocols' => '#(' . implode('|', array_map('preg_quote', $invalid_protocols, ['#'])) . ')(:|\&\#58)\S.*?#iUu',
@ -279,6 +279,7 @@ class Security
'twig.getFunction',
'core.setEscaper',
'twig.safe_functions',
'read_file',
];
$string = preg_replace('/(({{\s*|{%\s*)[^}]*?(' . implode('|', $bad_twig) . ')[^}]*?(\s*}}|\s*%}))/i', '{# $1 #}', $string);
return $string;

View File

@ -10,6 +10,7 @@
namespace Grav\Common;
use Grav\Common\Config\Config;
use Grav\Common\Language\Language;
use Grav\Common\Page\Collection;
use Grav\Common\Page\Interfaces\PageInterface;
use function is_string;
@ -37,6 +38,8 @@ class Taxonomy
protected $taxonomy_map;
/** @var Grav */
protected $grav;
/** @var Language */
protected $language;
/**
* Constructor that resets the map
@ -45,8 +48,9 @@ class Taxonomy
*/
public function __construct(Grav $grav)
{
$this->taxonomy_map = [];
$this->grav = $grav;
$this->language = $grav['language'];
$this->taxonomy_map[$this->language->getLanguage()] = [];
}
/**
@ -107,7 +111,8 @@ class Taxonomy
if (!empty($key)) {
$taxonomy .= $key;
}
$this->taxonomy_map[$taxonomy][(string) $value][$page->path()] = ['slug' => $page->slug()];
$active = $this->language->getLanguage();
$this->taxonomy_map[$active][$taxonomy][(string) $value][$page->path()] = ['slug' => $page->slug()];
}
}
@ -123,14 +128,11 @@ class Taxonomy
{
$matches = [];
$results = [];
$active = $this->language->getLanguage();
foreach ((array)$taxonomies as $taxonomy => $items) {
foreach ((array)$items as $item) {
if (isset($this->taxonomy_map[$taxonomy][$item])) {
$matches[] = $this->taxonomy_map[$taxonomy][$item];
} else {
$matches[] = [];
}
$matches[] = $this->taxonomy_map[$active][$taxonomy][$item] ?? [];
}
}
@ -156,11 +158,13 @@ class Taxonomy
*/
public function taxonomy($var = null)
{
$active = $this->language->getLanguage();
if ($var) {
$this->taxonomy_map = $var;
$this->taxonomy_map[$active] = $var;
}
return $this->taxonomy_map;
return $this->taxonomy_map[$active] ?? [];
}
/**
@ -171,6 +175,7 @@ class Taxonomy
*/
public function getTaxonomyItemKeys($taxonomy)
{
return isset($this->taxonomy_map[$taxonomy]) ? array_keys($this->taxonomy_map[$taxonomy]) : [];
$active = $this->language->getLanguage();
return isset($this->taxonomy_map[$active][$taxonomy]) ? array_keys($this->taxonomy_map[$active][$taxonomy]) : [];
}
}

View File

@ -206,7 +206,7 @@ class Uri
$uri = $language->setActiveFromUri($uri);
// split the URL and params (and make sure that the path isn't seen as domain)
$bits = parse_url('http://domain.com' . $uri);
$bits = static::parseUrl('http://domain.com' . $uri);
//process fragment
if (isset($bits['fragment'])) {
@ -265,6 +265,7 @@ class Uri
return $this->paths;
}
/**
* Return route to the current URI. By default route doesn't include base path.
*
@ -742,7 +743,7 @@ class Uri
*/
public static function isExternal($url)
{
return (0 === strpos($url, 'http://') || 0 === strpos($url, 'https://') || 0 === strpos($url, '//'));
return (0 === strpos($url, 'http://') || 0 === strpos($url, 'https://') || 0 === strpos($url, '//') || 0 === strpos($url, 'mailto:') || 0 === strpos($url, 'tel:') || 0 === strpos($url, 'ftp://') || 0 === strpos($url, 'ftps://') || 0 === strpos($url, 'news:') || 0 === strpos($url, 'irc:') || 0 === strpos($url, 'gopher:') || 0 === strpos($url, 'nntp:') || 0 === strpos($url, 'feed:') || 0 === strpos($url, 'cvs:') || 0 === strpos($url, 'ssh:') || 0 === strpos($url, 'git:') || 0 === strpos($url, 'svn:') || 0 === strpos($url, 'hg:'));
}
/**
@ -954,9 +955,7 @@ class Uri
$grav = Grav::instance();
// Remove extra slash from streams, parse_url() doesn't like it.
if ($pos = strpos($url, ':///')) {
$url = substr_replace($url, '://', $pos, 4);
}
$url = preg_replace('/([^:])(\/{2,})/', '$1/', $url);
$encodedUrl = preg_replace_callback(
'%[^:/@?&=#]+%usD',

View File

@ -1,6 +1,7 @@
<?php
use Codeception\Util\Fixtures;
use Grav\Common\Config\Config;
use Grav\Common\Grav;
use Grav\Common\Uri;
use Grav\Common\Utils;
@ -16,6 +17,9 @@ class UriTest extends \Codeception\TestCase\Test
/** @var Uri $uri */
protected $uri;
/** @var Config $config */
protected $config;
protected $tests = [
'/path' => [
'scheme' => '',
@ -704,7 +708,7 @@ class UriTest extends \Codeception\TestCase\Test
'route' => '/localhost',
'paths' => ['localhost'],
'params' => '/script%3E:',
'url' => '//localhost',
'url' => '/localhost',
'environment' => 'unknown',
'basename' => 'localhost',
'base' => '',
@ -859,6 +863,7 @@ class UriTest extends \Codeception\TestCase\Test
$grav = Fixtures::get('grav');
$this->grav = $grav();
$this->uri = $this->grav['uri'];
$this->config = $this->grav['config'];
}
protected function _after(): void
@ -1149,4 +1154,25 @@ class UriTest extends \Codeception\TestCase\Test
{
$this->runTestSet($this->tests, 'addNonce');
}
public function testCustomBase(): void
{
$current_base = $this->config->get('system.custom_base_url');
$this->config->set('system.custom_base_url', '/test');
$this->uri->initializeWithURL('https://mydomain.example.com:8090/test/korteles/kodai%20something?test=true#some-fragment')->init();
$this->assertSame([
"scheme" => "https",
"host" => "mydomain.example.com",
"port" => 8090,
"user" => null,
"pass" => null,
"path" => "/korteles/kodai%20something",
"params" => [],
"query" => "test=true",
"fragment" => "some-fragment",
], $this->uri->toArray());
$this->config->set('system.custom_base_url', $current_base);
}
}

View File

@ -461,7 +461,7 @@ class UtilsTest extends \Codeception\TestCase\Test
self::assertSame('pop://domain.com', Utils::url('pop://domain.com'));
self::assertSame('foo://bar/baz', Utils::url('foo://bar/baz'));
self::assertSame('foo://bar/baz', Utils::url('foo://bar/baz', true));
// self::assertSame('mailto:joe@domain.com', Utils::url('mailto:joe@domain.com', true)); // FIXME <-
self::assertSame('mailto:joe@domain.com', Utils::url('mailto:joe@domain.com', true)); // FIXME <-
}
public function testUrlWithRoot(): void