VYPR
Low severityNVD Advisory· Published Dec 29, 2023· Updated Aug 2, 2024

Winter CMS Local File Inclusion through Server Side Template Injection

CVE-2023-52085

Description

Winter is a free, open-source content management system. Users with access to backend forms that include a ColorPicker FormWidget can provide a value that would then be included without further processing in the compilation of custom stylesheets via LESS. This had the potential to lead to a Local File Inclusion vulnerability. This issue has been patched in v1.2.4.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
winter/wn-backend-modulePackagist
< 1.2.41.2.4

Affected products

1

Patches

1
5bc9257fe2bc

Added validation check for colourpicker form widget (#1020)

https://github.com/wintercms/winterJack WilkinsonDec 14, 2023via ghsa
3 files changed · +167 1
  • modules/backend/formwidgets/ColorPicker.php+38 1 modified
    @@ -59,6 +59,16 @@ class ColorPicker extends FormWidgetBase
          */
         public $formats = 'hex';
     
    +    /**
    +     * @var array|string[] Patterns to validate colour string on save
    +     */
    +    protected array $validationPatterns = [
    +        'cmyk' => '/^cmyk\((\d{1,2}\.?\d{0,2}%,? ?){4}\)$/',
    +        'hex' => '/^#[\w\d]{6}$/',
    +        'hsl' => '/^hsla\((\d{1,3}\.?\d{0,2}%?, ?){3}\d\.?\d{0,2}?\)$/',
    +        'rgb' => '/^rgba\((\d{1,3}\.?\d{0,2}, ?){3}\d\.?\d{0,2}?\)$/',
    +    ];
    +
         //
         // Object properties
         //
    @@ -244,6 +254,33 @@ protected function loadAssets()
          */
         public function getSaveValue($value)
         {
    -        return strlen($value) ? $value : null;
    +        if (!strlen($value)) {
    +            return null;
    +        }
    +
    +        switch (is_array($this->formats) ? 'all' : $this->formats) {
    +            case 'cmyk':
    +            case 'hex':
    +            case 'hsl':
    +            case 'rgb':
    +                if (!preg_match($this->validationPatterns[$this->formats], $value)) {
    +                    throw new ApplicationException(Lang::get('backend::lang.field.colors_invalid_input'));
    +                }
    +                break;
    +            case 'all':
    +                $valid = false;
    +                foreach ($this->validationPatterns as $pattern) {
    +                    if (preg_match($pattern, $value)) {
    +                        $valid = true;
    +                        break;
    +                    }
    +                }
    +                if (!$valid) {
    +                    throw new ApplicationException(Lang::get('backend::lang.field.colors_invalid_input'));
    +                }
    +                break;
    +        }
    +
    +        return $value;
         }
     }
    
  • modules/backend/lang/en/lang.php+1 0 modified
    @@ -11,6 +11,7 @@
             'options_method_not_exists' => "The model class :model must define a method :method() returning options for the ':field' form field.",
             'options_static_method_invalid_value' => "The static method ':method()' on :class did not return a valid options array.",
             'colors_method_not_exists' => "The model class :model must define a method :method() returning html color HEX codes for the ':field' form field.",
    +        'colors_invalid_input' => 'The color value supplied is invalid, please try again.',
         ],
         'widget' => [
             'not_registered' => "A widget class name ':name' has not been registered",
    
  • modules/backend/tests/formwidgets/ColorPickerTest.php+128 0 added
    @@ -0,0 +1,128 @@
    +<?php
    +
    +namespace Backend\Tests\FormWidgets;
    +
    +use Backend\Classes\Controller;
    +use Backend\Classes\FormField;
    +use Backend\FormWidgets\ColorPicker;
    +use System\Tests\Bootstrap\PluginTestCase;
    +use Winter\Storm\Exception\ApplicationException;
    +
    +class ColorPickerTest extends PluginTestCase
    +{
    +    public function testDefaultSaveValue(): void
    +    {
    +        $widget = $this->makeWidget();
    +
    +        // Default only expects hex
    +        $this->assertEquals('#3498DB', $widget->getSaveValue('#3498DB'));
    +
    +        // Getting a non-hex value should throw an exception
    +        $this->expectException(ApplicationException::class);
    +        $widget->getSaveValue('rgba(51.9, 152, 219, 1)');
    +
    +        // Test a bunch of hex values
    +        $this->assertEquals('#3498DB', $widget->getSaveValue('#3498DB'));
    +        $this->assertEquals('#2980B9', $widget->getSaveValue('#2980B9'));
    +        $this->assertEquals('#9B59B6', $widget->getSaveValue('#9B59B6'));
    +    }
    +
    +    public function testRgbSaveValue(): void
    +    {
    +        $widget = $this->makeWidget([
    +            'formats' => 'rgb'
    +        ]);
    +
    +        // Config specifies only rgb
    +        $this->assertEquals('rgba(51.9, 152, 219, 1)', $widget->getSaveValue('rgba(51.9, 152, 219, 1)'));
    +
    +        // Getting a non-rgb value should throw an exception
    +        $this->expectException(ApplicationException::class);
    +        $widget->getSaveValue('#3498DB');
    +
    +        // Test a bunch of rgb values
    +        $this->assertEquals('rgba(1, 1, 1, 1)', $widget->getSaveValue('rgba(1, 1, 1, 1)'));
    +        $this->assertEquals('rgba(155, 89, 182, 0.5)', $widget->getSaveValue('rgba(155, 89, 182, 0.5)'));
    +        $this->assertEquals('rgba(1, 89, 182, 0.55)', $widget->getSaveValue('rgba(1, 89, 182, 0.55)'));
    +    }
    +
    +    public function testCmykSaveValue(): void
    +    {
    +        $widget = $this->makeWidget([
    +            'formats' => 'cmyk'
    +        ]);
    +
    +        // Config specifies only cmyk
    +        $this->assertEquals('cmyk(76.3%, 30.6%, 0%, 14.1%)', $widget->getSaveValue('cmyk(76.3%, 30.6%, 0%, 14.1%)'));
    +
    +        // Getting a non-cmyk value should throw an exception
    +        $this->expectException(ApplicationException::class);
    +        $widget->getSaveValue('#3498DB');
    +
    +        // Test a bunch of cmyk values
    +        $this->assertEquals('cmyk(14.8%, 51.1%, 0%, 28.6%)', $widget->getSaveValue('cmyk(14.8%, 51.1%, 0%, 28.6%)'));
    +        $this->assertEquals('cmyk(17%, 60.75%, 0%, 32.22%)', $widget->getSaveValue('cmyk(17%, 60.75%, 0%, 32.22%)'));
    +        $this->assertEquals('cmyk(17.9%, 60.75%, 0%, 32.2%)', $widget->getSaveValue('cmyk(17.9%, 60.75%, 0%, 32.2%)'));
    +    }
    +
    +    public function testHslaSaveValue(): void
    +    {
    +        $widget = $this->makeWidget([
    +            'formats' => 'hsl'
    +        ]);
    +
    +        // Config specifies only hsl
    +        $this->assertEquals('hsla(204.1, 69.9%, 53.1%, 1)', $widget->getSaveValue('hsla(204.1, 69.9%, 53.1%, 1)'));
    +
    +        // Getting a non-hsl value should throw an exception
    +        $this->expectException(ApplicationException::class);
    +        $widget->getSaveValue('#3498DB');
    +
    +        // Test a bunch of hsl values
    +        $this->assertEquals('hsla(282.3, 43.6%, 47.2%, 1)', $widget->getSaveValue('hsla(282.3, 43.6%, 47.2%, 1)'));
    +        $this->assertEquals('hsla(282.3, 43.6%, 47.2%, 0.1)', $widget->getSaveValue('hsla(282.3, 43.6%, 47.2%, 0.1)'));
    +        $this->assertEquals('hsla(282, 43.6%, 47.2%, 0.1)', $widget->getSaveValue('hsla(282, 43.6%, 47.2%, 0.1)'));
    +        $this->assertEquals('hsla(282, 43.56%, 47.2%, 0.1)', $widget->getSaveValue('hsla(282, 43.56%, 47.2%, 0.1)'));
    +        $this->assertEquals('hsla(282.22, 43%, 47.2%, 0.1)', $widget->getSaveValue('hsla(282.22, 43%, 47.2%, 0.1)'));
    +    }
    +
    +    public function testAllSaveValue(): void
    +    {
    +        $widget = $this->makeWidget([
    +            'formats' => 'all'
    +        ]);
    +
    +        // Config allows for any valid format
    +        $this->assertEquals('#3498DB', $widget->getSaveValue('#3498DB'));
    +        $this->assertEquals('rgba(51.9, 152, 219, 1)', $widget->getSaveValue('rgba(51.9, 152, 219, 1)'));
    +        $this->assertEquals('cmyk(76.3%, 30.6%, 0%, 14.1%)', $widget->getSaveValue('cmyk(76.3%, 30.6%, 0%, 14.1%)'));
    +        $this->assertEquals('hsla(204.1, 69.9%, 53.1%, 1)', $widget->getSaveValue('hsla(204.1, 69.9%, 53.1%, 1)'));
    +
    +        // Getting a invalid value should throw an exception
    +        $this->expectException(ApplicationException::class);
    +        $widget->getSaveValue('#Winter Is Awesome');
    +
    +        $this->expectException(ApplicationException::class);
    +        $widget->getSaveValue('rgba(51.9, 152, 219, 1) -- test');
    +
    +        $this->expectException(ApplicationException::class);
    +        $widget->getSaveValue('Test(51.9, 152, 219, 1)');
    +    }
    +
    +    public function testAllowCustomSaveValue(): void
    +    {
    +        $widget = $this->makeWidget([
    +            'formats' => 'custom'
    +        ]);
    +
    +        // Config allows for any format
    +        $this->assertEquals('rgba(51.9, 152, 219, 1)', $widget->getSaveValue('rgba(51.9, 152, 219, 1)'));
    +        $this->assertEquals('#Winter Is Awesome', $widget->getSaveValue('#Winter Is Awesome'));
    +        $this->assertEquals('Test(51.9, 152, 219, 1)', $widget->getSaveValue('Test(51.9, 152, 219, 1)'));
    +    }
    +
    +    protected function makeWidget(array $config = []): ColorPicker
    +    {
    +        return new ColorPicker(new Controller(), new FormField('test', 'Test'), $config);
    +    }
    +}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.