Merge pull request #2798 from creative-commoners/pulls/4.11/cve-2022-37421

Sanitise ExtraMeta field for XSS
This commit is contained in:
Guy Sartorelli 2022-11-21 12:57:59 +13:00 committed by GitHub
commit 8526067c73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 0 deletions

View File

@ -69,6 +69,7 @@ use SilverStripe\Versioned\RecursivePublishable;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\ArrayData;
use SilverStripe\View\HTML;
use SilverStripe\View\Parsers\HTMLValue;
use SilverStripe\View\Parsers\ShortcodeParser;
use SilverStripe\View\Parsers\URLSegmentFilter;
use SilverStripe\View\Shortcodes\EmbedShortcodeProvider;
@ -1684,6 +1685,8 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
$this->setNextWriteWithoutVersion(true);
}
$this->sanitiseExtraMeta();
// Flush cached [embed] shortcodes
// Flush on both DRAFT and LIVE because VersionedCacheAdapter has separate caches for both
// Clear both caches at once for the scenario where a CMS-author updates a remote resource
@ -1703,6 +1706,27 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
}
}
private function sanitiseExtraMeta(): void
{
$htmlValue = HTMLValue::create($this->ExtraMeta);
/** @var DOMElement $el */
foreach ($htmlValue->query('//*') as $el) {
/** @var DOMAttr $attr */
$attributes = $el->attributes;
for ($i = count($attributes) - 1; $i >= 0; $i--) {
$attr = $attributes->item($i);
// remove any attribute starting with 'on' e.g. onclick
// and remove the accesskey attribute
if (substr($attr->name, 0, 2) === 'on' ||
$attr->name === 'accesskey'
) {
$el->removeAttributeNode($attr);
}
}
}
$this->ExtraMeta = $htmlValue->getContent();
}
/**
* Trigger synchronisation of link tracking
*
@ -1797,6 +1821,16 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
);
}
// Ensure ExtraMeta can be turned into valid HTML
if ($this->ExtraMeta && !HTMLValue::create($this->ExtraMeta)->getContent()) {
$result->addError(
_t(
'SilverStripe\\CMS\\Model\\SiteTree.InvalidExtraMeta',
'Custom Meta Tags does not contain valid HTML',
)
);
}
return $result;
}

View File

@ -208,6 +208,7 @@ en:
HTMLEDITORTITLE: Content
INHERIT: 'Inherit from parent page'
INHERITSITECONFIG: 'Inherit from site access settings'
InvalidExtraMeta: 'Custom Meta Tags does not contain valid HTML'
LASTPUBLISHED: 'Last published'
LASTSAVED: 'Last saved'
LASTUPDATED: 'Last Updated'

View File

@ -1986,4 +1986,51 @@ class SiteTreeTest extends SapphireTest
);
// END ARCHIVED
}
/**
* @dataProvider provideSanitiseExtraMeta
*/
public function testSanitiseExtraMeta(string $extraMeta, string $expected, string $message): void
{
$siteTree = new SiteTree();
$siteTree->ExtraMeta = $extraMeta;
$siteTree->write();
$this->assertSame($expected, $siteTree->ExtraMeta, $message);
}
public function provideSanitiseExtraMeta(): array
{
return [
[
'<link rel="canonical" accesskey="X" sometrigger="alert(1)" />',
'<link rel="canonical" sometrigger="alert(1)">',
'accesskey attribute is removed'
],
[
'<link rel="canonical" onclick="alert(1)" /><meta name="x" onerror="alert(0)">',
'<link rel="canonical"><meta name="x">',
'Attributes starting with "on" are removed'
],
[
'<link rel="canonical" onclick=alert(1) /><meta name="x" onerror=\'alert(0)\'>',
'<link rel="canonical"><meta name="x">',
'Attributes with different quote styles are removed'
],
[
'<link rel="canonical" ONCLICK=alert(1) /><meta name="x" oNeRrOr=\'alert(0)\'>',
'<link rel="canonical"><meta name="x">',
'Mixed case attributes are removed'
],
[
'<link rel="canonical" accesskey="X" onclick="alert(1)" name="x" />',
'<link rel="canonical" name="x">',
'Multiple attributes are removed'
],
[
'<link rel="canonical" href="valid" ;;// somethingdodgy < onmouseover=alert(1)',
'<link rel="canonical" href="valid" somethingdodgy="">',
'Invalid HTML is converted to valid HTML and parsed'
],
];
}
}