Prechádzať zdrojové kódy

LibWeb: Check for valid names in Document.createElement() & friends

We now validate that the provided tag names are valid XML tag names,
and otherwise throw an "invalid character" DOM exception.

2% progression on ACID3. :^)
Andreas Kling 3 rokov pred
rodič
commit
fe67fe3791

+ 4 - 4
Userland/DevTools/HackStudio/Debugger/EvaluateExpressionDialog.cpp

@@ -59,11 +59,11 @@ void EvaluateExpressionDialog::build(Window* parent_window)
 
     auto base_document = Web::DOM::Document::create();
     base_document->append_child(adopt_ref(*new Web::DOM::DocumentType(base_document)));
-    auto html_element = base_document->create_element("html");
+    auto html_element = base_document->create_element("html").release_value();
     base_document->append_child(html_element);
-    auto head_element = base_document->create_element("head");
+    auto head_element = base_document->create_element("head").release_value();
     html_element->append_child(head_element);
-    auto body_element = base_document->create_element("body");
+    auto body_element = base_document->create_element("body").release_value();
     html_element->append_child(body_element);
     m_output_container = body_element;
 
@@ -138,7 +138,7 @@ void EvaluateExpressionDialog::handle_evaluation(const String& expression)
 
 void EvaluateExpressionDialog::set_output(StringView html)
 {
-    auto paragraph = m_output_container->document().create_element("p");
+    auto paragraph = m_output_container->document().create_element("p").release_value();
     paragraph->set_inner_html(html);
 
     m_output_container->append_child(paragraph);

+ 7 - 3
Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp

@@ -20,7 +20,7 @@ DOMImplementation::DOMImplementation(Document& document)
 }
 
 // https://dom.spec.whatwg.org/#dom-domimplementation-createdocument
-NonnullRefPtr<Document> DOMImplementation::create_document(const String& namespace_, const String& qualified_name) const
+ExceptionOr<NonnullRefPtr<Document>> DOMImplementation::create_document(const String& namespace_, const String& qualified_name) const
 {
     // FIXME: This should specifically be an XML document.
     auto xml_document = Document::create();
@@ -29,8 +29,12 @@ NonnullRefPtr<Document> DOMImplementation::create_document(const String& namespa
 
     RefPtr<Element> element;
 
-    if (!qualified_name.is_empty())
-        element = xml_document->create_element_ns(namespace_, qualified_name /* FIXME: and an empty dictionary */);
+    if (!qualified_name.is_empty()) {
+        auto new_element = xml_document->create_element_ns(namespace_, qualified_name /* FIXME: and an empty dictionary */);
+        if (new_element.is_exception())
+            return new_element.exception();
+        element = new_element.release_value();
+    }
 
     // FIXME: If doctype is non-null, append doctype to document.
 

+ 1 - 1
Userland/Libraries/LibWeb/DOM/DOMImplementation.h

@@ -28,7 +28,7 @@ public:
     }
 
     // FIXME: Add optional DocumentType once supported by IDL
-    NonnullRefPtr<Document> create_document(const String&, const String&) const;
+    ExceptionOr<NonnullRefPtr<Document>> create_document(const String&, const String&) const;
     NonnullRefPtr<Document> create_html_document(const String& title) const;
     NonnullRefPtr<DocumentType> create_document_type(String const& qualified_name, String const& public_id, String const& system_id);
 

+ 55 - 3
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -444,7 +444,7 @@ void Document::set_title(const String& title)
 
     RefPtr<HTML::HTMLTitleElement> title_element = head_element->first_child_of_type<HTML::HTMLTitleElement>();
     if (!title_element) {
-        title_element = static_ptr_cast<HTML::HTMLTitleElement>(create_element(HTML::TagNames::title));
+        title_element = static_ptr_cast<HTML::HTMLTitleElement>(create_element(HTML::TagNames::title).release_value());
         head_element->append_child(*title_element);
     }
 
@@ -864,15 +864,18 @@ JS::Value Document::run_javascript(StringView source, StringView filename)
 
 // https://dom.spec.whatwg.org/#dom-document-createelement
 // FIXME: This only implements step 6 of the algorithm and does not take in options.
-NonnullRefPtr<Element> Document::create_element(const String& tag_name)
+DOM::ExceptionOr<NonnullRefPtr<Element>> Document::create_element(String const& tag_name)
 {
+    if (!is_valid_name(tag_name))
+        return DOM::InvalidCharacterError::create("Invalid character in tag name.");
+
     // FIXME: Let namespace be the HTML namespace, if this is an HTML document or this’s content type is "application/xhtml+xml", and null otherwise.
     return DOM::create_element(*this, tag_name, Namespace::HTML);
 }
 
 // https://dom.spec.whatwg.org/#internal-createelementns-steps
 // FIXME: This only implements step 4 of the algorithm and does not take in options.
-NonnullRefPtr<Element> Document::create_element_ns(const String& namespace_, const String& qualified_name)
+DOM::ExceptionOr<NonnullRefPtr<Element>> Document::create_element_ns(const String& namespace_, const String& qualified_name)
 {
     return DOM::create_element(*this, qualified_name, namespace_);
 }
@@ -1338,4 +1341,53 @@ void Document::detach_parser(Badge<HTML::HTMLParser>)
     m_parser = nullptr;
 }
 
+// https://www.w3.org/TR/xml/#NT-NameStartChar
+static bool is_valid_name_start_character(u32 code_point)
+{
+    return code_point == ':'
+        || (code_point >= 'A' && code_point <= 'Z')
+        || code_point == '_'
+        || (code_point >= 'a' && code_point <= 'z')
+        || (code_point >= 0xc0 && code_point <= 0xd6)
+        || (code_point >= 0xd8 && code_point <= 0xf6)
+        || (code_point >= 0xf8 && code_point <= 0x2ff)
+        || (code_point >= 0x370 && code_point <= 0x37d)
+        || (code_point >= 0x37f && code_point <= 0x1fff)
+        || (code_point >= 0x200c && code_point <= 0x200d)
+        || (code_point >= 0x2070 && code_point <= 0x218f)
+        || (code_point >= 0x2c00 && code_point <= 0x2fef)
+        || (code_point >= 0x3001 && code_point <= 0xD7ff)
+        || (code_point >= 0xf900 && code_point <= 0xfdcf)
+        || (code_point >= 0xfdf0 && code_point <= 0xfffd)
+        || (code_point >= 0x10000 && code_point <= 0xeffff);
+}
+
+// https://www.w3.org/TR/xml/#NT-NameChar
+static inline bool is_valid_name_character(u32 code_point)
+{
+    return is_valid_name_start_character(code_point)
+        || code_point == '-'
+        || code_point == '.'
+        || (code_point >= '0' && code_point <= '9')
+        || code_point == 0xb7
+        || (code_point >= 0x300 && code_point <= 0x36f)
+        || (code_point >= 0x203f && code_point <= 0x2040);
+}
+
+bool Document::is_valid_name(String const& name)
+{
+    if (name.is_empty())
+        return false;
+
+    if (!is_valid_name_start_character(name[0]))
+        return false;
+
+    for (size_t i = 1; i < name.length(); ++i) {
+        if (!is_valid_name_character(name[i]))
+            return false;
+    }
+
+    return true;
+}
+
 }

+ 4 - 2
Userland/Libraries/LibWeb/DOM/Document.h

@@ -177,8 +177,8 @@ public:
 
     JS::Value run_javascript(StringView source, StringView filename = "(unknown)");
 
-    NonnullRefPtr<Element> create_element(const String& tag_name);
-    NonnullRefPtr<Element> create_element_ns(const String& namespace_, const String& qualified_name);
+    ExceptionOr<NonnullRefPtr<Element>> create_element(const String& tag_name);
+    ExceptionOr<NonnullRefPtr<Element>> create_element_ns(const String& namespace_, const String& qualified_name);
     NonnullRefPtr<DocumentFragment> create_document_fragment();
     NonnullRefPtr<Text> create_text_node(const String& data);
     NonnullRefPtr<Comment> create_comment(const String& data);
@@ -316,6 +316,8 @@ public:
     void set_parser(Badge<HTML::HTMLParser>, HTML::HTMLParser&);
     void detach_parser(Badge<HTML::HTMLParser>);
 
+    static bool is_valid_name(String const&);
+
 private:
     explicit Document(const AK::URL&);
 

+ 1 - 1
Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp

@@ -171,7 +171,7 @@ void HTMLInputElement::create_shadow_tree_if_needed()
     auto initial_value = attribute(HTML::AttributeNames::value);
     if (initial_value.is_null())
         initial_value = String::empty();
-    auto element = document().create_element(HTML::TagNames::div);
+    auto element = document().create_element(HTML::TagNames::div).release_value();
     element->set_attribute(HTML::AttributeNames::style, "white-space: pre; padding-top: 1px; padding-bottom: 1px; padding-left: 2px; padding-right: 2px");
     m_text_node = adopt_ref(*new DOM::Text(document(), initial_value));
     m_text_node->set_always_editable(true);

+ 10 - 10
Userland/Libraries/LibWeb/Loader/FrameLoader.cpp

@@ -53,21 +53,21 @@ static bool build_markdown_document(DOM::Document& document, const ByteBuffer& d
 
 static bool build_text_document(DOM::Document& document, const ByteBuffer& data)
 {
-    auto html_element = document.create_element("html");
+    auto html_element = document.create_element("html").release_value();
     document.append_child(html_element);
 
-    auto head_element = document.create_element("head");
+    auto head_element = document.create_element("head").release_value();
     html_element->append_child(head_element);
-    auto title_element = document.create_element("title");
+    auto title_element = document.create_element("title").release_value();
     head_element->append_child(title_element);
 
     auto title_text = document.create_text_node(document.url().basename());
     title_element->append_child(title_text);
 
-    auto body_element = document.create_element("body");
+    auto body_element = document.create_element("body").release_value();
     html_element->append_child(body_element);
 
-    auto pre_element = document.create_element("pre");
+    auto pre_element = document.create_element("pre").release_value();
     body_element->append_child(pre_element);
 
     pre_element->append_child(document.create_text_node(String::copy(data)));
@@ -85,22 +85,22 @@ static bool build_image_document(DOM::Document& document, ByteBuffer const& data
     if (!bitmap)
         return false;
 
-    auto html_element = document.create_element("html");
+    auto html_element = document.create_element("html").release_value();
     document.append_child(html_element);
 
-    auto head_element = document.create_element("head");
+    auto head_element = document.create_element("head").release_value();
     html_element->append_child(head_element);
-    auto title_element = document.create_element("title");
+    auto title_element = document.create_element("title").release_value();
     head_element->append_child(title_element);
 
     auto basename = LexicalPath::basename(document.url().path());
     auto title_text = adopt_ref(*new DOM::Text(document, String::formatted("{} [{}x{}]", basename, bitmap->width(), bitmap->height())));
     title_element->append_child(title_text);
 
-    auto body_element = document.create_element("body");
+    auto body_element = document.create_element("body").release_value();
     html_element->append_child(body_element);
 
-    auto image_element = document.create_element("img");
+    auto image_element = document.create_element("img").release_value();
     image_element->set_attribute(HTML::AttributeNames::src, document.url().to_string());
     body_element->append_child(image_element);