Diff
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (286859 => 286860)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2021-12-10 18:52:08 UTC (rev 286859)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2021-12-10 19:00:44 UTC (rev 286860)
@@ -1,3 +1,14 @@
+2021-12-10 Patrick Griffis <[email protected]>
+
+ CSP: Implement protections against nonce-hijacking
+ https://bugs.webkit.org/show_bug.cgi?id=233087
+
+ Reviewed by Brent Fulgham.
+
+ Update expectations.
+
+ * web-platform-tests/content-security-policy/script-src/nonce-enforce-blocked-expected.txt:
+
2021-12-10 Chris Dumez <[email protected]>
Radio buttons with no form owner are not grouped
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/content-security-policy/script-src/nonce-enforce-blocked-expected.txt (286859 => 286860)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/content-security-policy/script-src/nonce-enforce-blocked-expected.txt 2021-12-10 18:52:08 UTC (rev 286859)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/content-security-policy/script-src/nonce-enforce-blocked-expected.txt 2021-12-10 19:00:44 UTC (rev 286860)
@@ -1,3 +1,3 @@
-FAIL Unnonced scripts generate reports. assert_unreached: '<script' attribute, no execution. Reached unreachable code
+PASS Unnonced scripts generate reports.
Modified: trunk/Source/WebCore/ChangeLog (286859 => 286860)
--- trunk/Source/WebCore/ChangeLog 2021-12-10 18:52:08 UTC (rev 286859)
+++ trunk/Source/WebCore/ChangeLog 2021-12-10 19:00:44 UTC (rev 286860)
@@ -1,3 +1,26 @@
+2021-12-10 Patrick Griffis <[email protected]>
+
+ CSP: Prevent nonce-hijacking
+ https://bugs.webkit.org/show_bug.cgi?id=233087
+
+ Reviewed by Brent Fulgham.
+
+ Implement protections against nonce-hijacking as described in this spec:
+ https://www.w3.org/TR/CSP3/#security-nonce-hijacking
+
+ * dom/Element.cpp:
+ (WebCore::Element::isNonceable const):
+ (WebCore::Element::nonce const):
+ * dom/Element.h:
+ (WebCore::Element::hasDuplicateAttribute const):
+ (WebCore::Element::setHasDuplicateAttribute):
+ * html/parser/AtomHTMLToken.h:
+ (WebCore::AtomHTMLToken::hasDuplicateAttribute const):
+ (WebCore::AtomHTMLToken::initializeAttributes):
+ * html/parser/HTMLConstructionSite.cpp:
+ (WebCore::setAttributes):
+ (WebCore::HTMLConstructionSite::insertCustomElement):
+
2021-12-09 Darin Adler <[email protected]>
Use simpler idioms for std::less and std::greater possible in modern C++
Modified: trunk/Source/WebCore/dom/Element.cpp (286859 => 286860)
--- trunk/Source/WebCore/dom/Element.cpp 2021-12-10 18:52:08 UTC (rev 286859)
+++ trunk/Source/WebCore/dom/Element.cpp 2021-12-10 19:00:44 UTC (rev 286860)
@@ -74,6 +74,7 @@
#include "HTMLOptGroupElement.h"
#include "HTMLOptionElement.h"
#include "HTMLParserIdioms.h"
+#include "HTMLScriptElement.h"
#include "HTMLSelectElement.h"
#include "HTMLTemplateElement.h"
#include "IdChangeInvalidation.h"
@@ -109,6 +110,7 @@
#include "SVGElementTypeHelpers.h"
#include "SVGNames.h"
#include "SVGSVGElement.h"
+#include "SVGScriptElement.h"
#include "ScriptDisallowedScope.h"
#include "ScrollIntoViewOptions.h"
#include "ScrollLatchingController.h"
@@ -318,9 +320,40 @@
return -1;
}
+bool Element::isNonceable() const
+{
+ // https://www.w3.org/TR/CSP3/#is-element-nonceable
+ if (elementRareData()->nonce().isNull())
+ return false;
+
+ if (hasDuplicateAttribute())
+ return false;
+
+ if (hasAttributes()
+ && (is<HTMLScriptElement>(*this) || is<SVGScriptElement>(*this))) {
+ static const char scriptString[] = "<script";
+ static const char styleString[] = "<style";
+
+ for (const auto& attribute : attributesIterator()) {
+ auto name = attribute.localName().convertToASCIILowercase();
+ auto value = attribute.value().convertToASCIILowercase();
+ if (name.contains(scriptString)
+ || name.contains(styleString)
+ || value.contains(scriptString)
+ || value.contains(styleString))
+ return false;
+ }
+ }
+
+ return true;
+}
+
const AtomString& Element::nonce() const
{
- return hasRareData() ? elementRareData()->nonce() : emptyAtom();
+ if (hasRareData() && isNonceable())
+ return elementRareData()->nonce();
+
+ return emptyAtom();
}
void Element::setNonce(const AtomString& newValue)
Modified: trunk/Source/WebCore/dom/Element.h (286859 => 286860)
--- trunk/Source/WebCore/dom/Element.h 2021-12-10 18:52:08 UTC (rev 286859)
+++ trunk/Source/WebCore/dom/Element.h 2021-12-10 19:00:44 UTC (rev 286860)
@@ -652,6 +652,9 @@
String description() const override;
String debugDescription() const override;
+ bool hasDuplicateAttribute() const { return m_hasDuplicateAttribute; };
+ void setHasDuplicateAttribute(bool hasDuplicateAttribute) { m_hasDuplicateAttribute = hasDuplicateAttribute; };
+
protected:
Element(const QualifiedName&, Document&, ConstructionType);
@@ -688,6 +691,8 @@
bool isUserActionElementHasFocusVisible() const;
bool isUserActionElementHasFocusWithin() const;
+ bool isNonceable() const;
+
virtual void didAddUserAgentShadowRoot(ShadowRoot&) { }
void didAddAttribute(const QualifiedName&, const AtomString&);
@@ -765,6 +770,8 @@
QualifiedName m_tagName;
RefPtr<ElementData> m_elementData;
+
+ bool m_hasDuplicateAttribute { false };
};
void invalidateForSiblingCombinators(Element* sibling);
Modified: trunk/Source/WebCore/html/parser/AtomHTMLToken.h (286859 => 286860)
--- trunk/Source/WebCore/html/parser/AtomHTMLToken.h 2021-12-10 18:52:08 UTC (rev 286859)
+++ trunk/Source/WebCore/html/parser/AtomHTMLToken.h 2021-12-10 19:00:44 UTC (rev 286860)
@@ -31,6 +31,8 @@
namespace WebCore {
+enum class HasDuplicateAttribute : bool { No, Yes };
+
class AtomHTMLToken {
public:
explicit AtomHTMLToken(HTMLToken&);
@@ -70,6 +72,8 @@
const String& comment() const;
+ HasDuplicateAttribute hasDuplicateAttribute() const { return m_hasDuplicateAttribute; };
+
private:
HTMLToken::Type m_type;
@@ -90,6 +94,8 @@
bool m_selfClosing; // StartTag, EndTag.
Vector<Attribute> m_attributes; // StartTag, EndTag.
+
+ HasDuplicateAttribute m_hasDuplicateAttribute { HasDuplicateAttribute::No };
};
const Attribute* findAttribute(const Vector<Attribute>&, const QualifiedName&);
@@ -208,6 +214,8 @@
// FIXME: This is N^2 for the number of attributes.
if (!hasAttribute(m_attributes, localName))
m_attributes.uncheckedAppend(Attribute(QualifiedName(nullAtom(), localName, nullAtom()), HTMLAtomStringCache::makeAttributeValue(attribute.value)));
+ else
+ m_hasDuplicateAttribute = HasDuplicateAttribute::Yes;
}
}
Modified: trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp (286859 => 286860)
--- trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp 2021-12-10 18:52:08 UTC (rev 286859)
+++ trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp 2021-12-10 19:00:44 UTC (rev 286860)
@@ -55,16 +55,17 @@
using namespace HTMLNames;
-static inline void setAttributes(Element& element, Vector<Attribute>& attributes, ParserContentPolicy parserContentPolicy)
+static inline void setAttributes(Element& element, Vector<Attribute>& attributes, HasDuplicateAttribute hasDuplicateAttribute, ParserContentPolicy parserContentPolicy)
{
if (!scriptingContentIsAllowed(parserContentPolicy))
element.stripScriptingAttributes(attributes);
element.parserSetAttributes(attributes);
+ element.setHasDuplicateAttribute(hasDuplicateAttribute == HasDuplicateAttribute::Yes);
}
static inline void setAttributes(Element& element, AtomHTMLToken& token, ParserContentPolicy parserContentPolicy)
{
- setAttributes(element, token.attributes(), parserContentPolicy);
+ setAttributes(element, token.attributes(), token.hasDuplicateAttribute(), parserContentPolicy);
}
static bool hasImpliedEndTag(const HTMLStackItem& item)
@@ -516,7 +517,7 @@
void HTMLConstructionSite::insertCustomElement(Ref<Element>&& element, const AtomString& localName, Vector<Attribute>&& attributes)
{
- setAttributes(element, attributes, m_parserContentPolicy);
+ setAttributes(element, attributes, HasDuplicateAttribute::No, m_parserContentPolicy);
attachLater(currentNode(), element.copyRef());
m_openElements.push(HTMLStackItem::create(WTFMove(element), localName, WTFMove(attributes)));
executeQueuedTasks();