CVE-2020-15246 (PHP, published 2020-11-23T20:15Z)
Description: value: October is a free, open-source, self-hosted CMS platform based on the Laravel PHP Framework. In October CMS from version 1.0.421 and before version 1.0.469, an attacker can read local files on an October CMS server via a specially crafted request. Issue has been patched in Build 469 (v1.0.469) and v1.1.0.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-22 \u2014 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
Repository: https://github.com/octobercms/library
Files changed:
src/Exception/SystemException.php
src/Filesystem/Definitions.php
src/Halcyon/Builder.php
Commit message: Security fixes for v1.0.469

Temporarily disables SVG uploads
Improves path validation in FileDatasource & reorgs / cleans up Halcyon Builder
Adds XSS filtering to SystemException messages (related https://github.com/octobercms/library/commit/c16fefde03080fe2b3937e565d9cdf7b70291384#diff-4915b631d3234e0c5232490be34effe4)


Vulnerable code:
 * @author Alexey Bobkov, Samuel Georges
            'svg',
            'svg',
     * Switches mode to select a single template by its name.
     * @param  string  $fileName
     * @return $this
    public function whereFileName($fileName)
        $this->selectSingle = $this->model->getFileNameParts($fileName);

        return $this;
     * Set the directory name which the query is targeting.
     * @param  string  $dirName
     * @return $this
    public function from($dirName)
        $this->from = $dirName;
        return $this;
     * Set the "offset" value of the query.
    public function offset($value)
        $this->offset = max(0, $value);
     * Alias to set the "offset" value of the query.
    public function skip($value)
        return $this->offset($value);
     * Set the "limit" value of the query.
    public function limit($value)
        if ($value >= 0) {
            $this->limit = $value;
        }
     * Alias to set the "limit" value of the query.
    public function take($value)
        return $this->limit($value);
     * Execute the query as a "select" statement.
     *
     * @param  array  $columns
     * @return \October\Rain\Halcyon\Collection|static[]
     */
    public function get($columns = ['*'])
    {
        if (!is_null($this->cacheMinutes)) {
            $results = $this->getCached($columns);
        }
        else {
            $results = $this->getFresh($columns);
        }

        $models = $this->getModels($results ?: []);

        return $this->model->newCollection($models);
    }

    /**
     * Get an array with the values of a given column.
     * @param  string  $column
     * @param  string  $key
     * @return array
    public function lists($column, $key = null)
        $select = is_null($key) ? [$column] : [$column, $key];

        if (!is_null($this->cacheMinutes)) {
            $results = $this->getCached($select);
        }
        else {
            $results = $this->getFresh($select);
        }
        $collection = new Collection($results);
        return $collection->lists($column, $key);
     * Set a model instance for the model being queried.
     * @param  \October\Rain\Halcyon\Model  $model
     * @return $this
    public function setModel(Model $model)
        $this->model = $model;

        $this->extensions = $this->model->getAllowedExtensions();

        $this->from($this->model->getObjectTypeDirName());
        return $this;
    }
    /**
     * Get the compiled file content representation of the query.
     *
     * @return string
     */
    public function toCompiled()
    {
        return $this->processor->processUpdate($this, []);
     * @param  string  $fileName
    public function delete($fileName = null)
    /**
     * Get the model instance being queried.
     *
     * @return \October\Rain\Halcyon\Model
     */
    public function getModel()
    {
        return $this->model;
    }

     * @return mixed
     *

Fixed code:
use Exception;
use October\Rain\Html\HtmlBuilder;

 * @author Alexey Bobkov, Samuel Georges, Luke Towers
    /**
     * Override the constructor to escape all messages to protect against potential XSS
     * from user provided inputs being included in the exception message
     *
     * @param string $message Error message.
     * @param int $code Error code.
     * @param Exception $previous Previous exception.
     */
    public function __construct($message = "", $code = 0, Exception $previous = null)
    {
        $message = HtmlBuilder::clean($message);

        parent::__construct($message, $code, $previous);
    }
     * Get the compiled file content representation of the query.
     * @return string
    public function toCompiled()
        return $this->processor->processUpdate($this, []);
     * Get an array with the values of a given column.
     * @param  string  $column
     * @param  string  $key
     * @return array
    public function lists($column, $key = null)
        $select = is_null($key) ? [$column] : [$column, $key];
        if (!is_null($this->cacheMinutes)) {
            $results = $this->getCached($select);
        }
        else {
            $results = $this->getFresh($select);
        }

        $collection = new Collection($results);

        return $collection->lists($column, $key);
     * Set the "limit" value of the query.
    public function limit($value)
        if ($value >= 0) {
            $this->limit = $value;
        }
     * Alias to set the "limit" value of the query.
    public function take($value)
        return $this->limit($value);
     * Set the "offset" value of the query.
    public function offset($value)
        $this->offset = max(0, $value);
     * Alias to set the "offset" value of the query.
    public function skip($value)
        return $this->offset($value);
    }

    /**
     * Set the directory name which the query is targeting.
     *
     * @param  string  $dirName
     * @return $this
     */
    public function from($dirName)
    {
        $this->from = $dirName;

        return $this;
     * Switches mode to select a single template by its name.
     * @param  string  $fileName
     * @return $this
    public function whereFileName($fileName)
        $this->validateFileName($fileName);
        $this->selectSingle = $this->model->getFileNameParts($fileName);
        return $this;
     * Execute the query as a "select" statement.
     * @param  array  $columns
     * @return \October\Rain\Halcyon\Collection|static[]
    public function get($columns = ['*'])
        if (!is_null($this->cacheMinutes)) {
            $results = $this->getCached($columns);
        }
        else {
            $results = $this->getFresh($columns);
        }
        $models = $this->getModels($results ?: []);
        return $this->model->newCollection($models);
    public function delete()
    /**
     * Set a model instance for the model being queried.
     *
     * @param  \October\Rain\Halcyon\Model  $model
     * @return $this
     */
    public function setModel(Model $model)
    {
        $this->model = $model;

        $this->extensions = $this->model->getAllowedExtensions();

        $this->from($this->model->getObjectTypeDirName());

        return $this;
    }

    /**
     * Get the model instance being queried.
     *
     * @return \October\Rain\Halcyon\Model
     */
    public function getModel()
    {
        return $this->model;
    }

     * @return void