/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
nsGkAtoms::tabindex,
nsGkAtoms::target,
:const&)java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79
nsGkAtomsjava.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 18
nsGkAtoms::usemap,
nsGkAtoms::value,
nsGkAtoms::width,
nsGkAtoms::wrap,
mProxyInit) // clang-format on
};
// List of HTML attributes with URLs that the // browser will fetch. Should be kept in sync with // https://html.spec.whatwg.org/multipage/indices.html#attributes-3 const nsStaticAtom* const kURLAttributesHTML[] = { // clang-format off
nsGkAtoms::action,
nsGkAtoms::href,
nsGkAtoms::src,
nsGkAtoms::longdesc,
nsGkAtoms::cite,
nsGkAtoms::background,
nsGkAtoms::formaction,
nsGkAtoms::data,
nsGkAtoms::ping,
nsGkAtoms::poster,
nullptr // clang-format on
};
if (!sElementsHTML) { // Initialize lazily to avoid having to initialize at all if the user // doesn't paste HTML or load feeds.
InitializeStatics();
}
}
// Step 6. If element matches any name in config["blockElements"]: Return // block. if (mReplaceWithChildrenElements &&
MatchesElementName(*mReplaceWithChildrenElements, aNamespace, aLocal)) { returntrue;
}
// Step 7. Let allow list be null. // Step 8. If "allowElements" exists in config: // Step 8.1. Then : Set allow list to config["allowElements"]. if (mElements) { // Step 9. If element does not match any name in allow list: // Return block. if (!mElements->Contains(ElementName(aNamespace, aLocal))) { returntrue;
}
} else { // Step 8.2. Otherwise: Set allow list to the default configuration's // element allow list.
// Step 9. If element does not match any name in allow list: // Return block.
// The default configuration only contains HTML elements, so we can // reject everything else. if (aNamespace != kNameSpaceID_XHTML ||
!sDefaultConfigurationElementAllowlist->Contains(aLocal)) { returntrue;
}
}
// To avoid attacks where a MathML script becomes something that gets // serialized in a way that it parses back as an HTML script, let's just // drop elements with the local name 'script' regardless of namespace. if (nsGkAtoms::script == aLocal) { returntrue;
} if (aNamespace == kNameSpaceID_XHTML) { if (nsGkAtoms::title == aLocal && !mFullDocument) { // emulate the quirks of the old parser returntrue;
} if (mDropForms &&
(nsGkAtoms::select == aLocal || nsGkAtoms::button == aLocal ||
nsGkAtoms::datalist == aLocal)) { returntrue;
} if (mDropMedia &&
(nsGkAtoms::img == aLocal || nsGkAtoms::video == aLocal ||
nsGkAtoms::audio == aLocal || nsGkAtoms::source == aLocal)) { returntrue;
} if (nsGkAtoms::meta == aLocal &&
(aElement->HasAttr(nsGkAtoms::charset) ||
aElement->HasAttr(nsGkAtoms::httpEquiv))) { // Throw away charset declarations even if they also have microdata // which they can't validly have. returntrue;
} if (((!mFullDocument && nsGkAtoms::meta == aLocal) ||
nsGkAtoms::link == aLocal) &&
!(aElement->HasAttr(nsGkAtoms::itemprop) ||
aElement->HasAttr(nsGkAtoms::itemscope))) { // emulate old behavior for non-Microdata <meta> and <link> presumably // in <head>. <meta> and <link> are whitelisted in order to avoid // corrupting Microdata when they appear in <body>. Note that // SanitizeAttributes() will remove the rel attribute from <link> and // the name attribute from <meta>. returntrue;
}
} if (mAllowStyles) { return nsGkAtoms::style == aLocal && !(aNamespace == kNameSpaceID_XHTML ||
aNamespace == kNameSpaceID_SVG);
} if (nsGkAtoms::style == aLocal) { returntrue;
} returnfalse;
}
// custom, if element’s local name is a valid custom element name, // XXX shouldn't this happen after unknown. if (nsContentUtils::IsCustomElementName(aLocal, kNameSpaceID_XHTML)) { return ElementKind::Custom;
}
// unknown, if element is not in the [HTML] namespace // XXX this doesn't really make sense to me // https://github.com/WICG/sanitizer-api/issues/167 if (aNamespace != kNameSpaceID_XHTML) { return ElementKind::Unknown;
}
// or if element’s local name denotes an unknown element // — that is, if the element interface the [HTML] specification assigns to it // would be HTMLUnknownElement, if (nsCOMPtr<HTMLUnknownElement> el = do_QueryInterface(aElement)) { return ElementKind::Unknown;
}
// Step 1. Let kind be element’s element kind.
ElementKind kind = GetElementKind(aNamespace, aLocal, aElement);
switch (kind) { case ElementKind::Regular: // Step 2. If kind is regular and element does not match any name in the // baseline element allow list: Return drop. if (!sBaselineElementAllowlist->Contains(aLocal)) { returntrue;
} break;
case ElementKind::Custom: // Step 3. If kind is custom and if config["allowCustomElements"] does not // exist or if config["allowCustomElements"] is false: Return drop. if (!mAllowCustomElements) { returntrue;
} break;
case ElementKind::Unknown: // Step 4. If kind is unknown and if config["allowUnknownMarkup"] does not // exist or it config["allowUnknownMarkup"] is false: Return drop. if (!mAllowUnknownMarkup) { returntrue;
} break;
}
// Step 5. If element matches any name in config["dropElements"]: Return drop. if (mRemoveElements &&
MatchesElementName(*mRemoveElements, aNamespace, aLocal)) { returntrue;
}
template <size_t Len> staticbool UTF16StringStartsWith(const char16_t* aStr, uint32_t aLength, const char16_t (&aNeedle)[Len]) {
MOZ_ASSERT(aNeedle[Len - 1] == '\0', "needle should be a UTF-16 encoded string literal");
if (aLength < Len - 1) { returnfalse;
} for (size_t i = 0; i < Len - 1; i++) { if (aStr[i] != aNeedle[i]) { returnfalse;
}
} returntrue;
}
void nsTreeSanitizer::SanitizeAttributes(mozilla::dom::Element* aElement,
AllowedAttributes aAllowed) {
int32_t ac = (int)aElement->GetAttrCount();
for (int32_t i = ac - 1; i >= 0; --i) { const nsAttrName* attrName = aElement->GetAttrNameAt(i);
int32_t attrNs = attrName->NamespaceID();
RefPtr<nsAtom> attrLocal = attrName->LocalName();
if (mIsForSanitizerAPI) { if (MustDropAttribute(aElement, attrNs, attrLocal) ||
MustDropFunkyAttribute(aElement, attrNs, attrLocal)) {
aElement->UnsetAttr(kNameSpaceID_None, attrLocal, false); if (mLogRemovals) {
LogMessage("Removed unsafe attribute.", aElement->OwnerDoc(),
aElement, attrLocal);
}
// in case the attribute removal shuffled the attribute order, start // the loop again.
--ac;
i = ac; // i will be decremented immediately thanks to the for loop
} continue;
}
if (kNameSpaceID_None == attrNs) { if (aAllowed.mStyle && nsGkAtoms::style == attrLocal) { continue;
} if (aAllowed.mDangerousSrc && nsGkAtoms::src == attrLocal) { continue;
} if (IsURL(aAllowed.mURLs, attrLocal)) { bool fragmentOnly = aElement->IsSVGElement(nsGkAtoms::use); if (SanitizeURL(aElement, attrNs, attrLocal, fragmentOnly)) { // in case the attribute removal shuffled the attribute order, start // the loop again.
--ac;
i = ac; // i will be decremented immediately thanks to the for loop continue;
} // else fall through to see if there's another reason to drop this // attribute (in particular if the attribute is background="" on an // HTML element)
} if (!mDropNonCSSPresentation &&
(aAllowed.mNames == sAttributesHTML) && // element is HTML
sPresAttributesHTML->Contains(attrLocal)) { continue;
} if (aAllowed.mNames->Contains(attrLocal) &&
!((attrLocal == nsGkAtoms::rel &&
aElement->IsHTMLElement(nsGkAtoms::link)) ||
(!mFullDocument && attrLocal == nsGkAtoms::name &&
aElement->IsHTMLElement(nsGkAtoms::meta)))) { // name="" and rel="" are whitelisted, but treat them as blacklisted // for <meta name> (fragment case) and <link rel> (all cases) to avoid // document-wide metadata or styling overrides with non-conforming // <meta name itemprop> or // <link rel itemprop> continue;
} const char16_t* localStr = attrLocal->GetUTF16String();
uint32_t localLen = attrLocal->GetLength(); // Allow underscore to cater to the MCE editor library. // Allow data-* on SVG and MathML, too, as a forward-compat measure. // Allow aria-* on all for simplicity. if (UTF16StringStartsWith(localStr, localLen, u"_") ||
UTF16StringStartsWith(localStr, localLen, u"data-") ||
UTF16StringStartsWith(localStr, localLen, u"aria-")) { continue;
} // else not allowed
} elseif (kNameSpaceID_XML == attrNs) { if (nsGkAtoms::lang == attrLocal || nsGkAtoms::space == attrLocal) { continue;
} // else not allowed
} elseif (aAllowed.mXLink && kNameSpaceID_XLink == attrNs) { if (nsGkAtoms::href == attrLocal) { bool fragmentOnly = aElement->IsSVGElement(nsGkAtoms::use); if (SanitizeURL(aElement, attrNs, attrLocal, fragmentOnly)) { // in case the attribute removal shuffled the attribute order, start // the loop again.
--ac;
i = ac; // i will be decremented immediately thanks to the for loop
} continue;
} if (nsGkAtoms::type == attrLocal || nsGkAtoms::title == attrLocal ||
nsGkAtoms::show == attrLocal || nsGkAtoms::actuate == attrLocal) { continue;
} // else not allowed
}
aElement->UnsetAttr(kNameSpaceID_None, attrLocal, false); if (mLogRemovals) {
LogMessage("Removed unsafe attribute.", aElement->OwnerDoc(), aElement,
attrLocal);
} // in case the attribute removal shuffled the attribute order, start the // loop again.
--ac;
i = ac; // i will be decremented immediately thanks to the for loop
}
// If we've got HTML audio or video, add the controls attribute, because // otherwise the content is unplayable with scripts removed. if (aElement->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::controls, u""_ns, false);
}
}
// https://wicg.github.io/sanitizer-api/#sanitize-action-for-an-attribute bool nsTreeSanitizer::MustDropAttribute(Element* aElement,
int32_t aAttrNamespace,
nsAtom* aAttrLocalName) { // Step 1. Let kind be attribute’s attribute kind. // Step 2. If kind is unknown and if config["allowUnknownMarkup"] does not // exist or it config["allowUnknownMarkup"] is false: Return drop. // // TODO: Not clear how to determine if something is an "unknown" attribute. // https://github.com/WICG/sanitizer-api/issues/147 should probably define // an explicit list.
// Step 3. If kind is regular and attribute’s local name does not match any // name in the baseline attribute allow list: Return drop. if (!sBaselineAttributeAllowlist->Contains(aAttrLocalName)) { returntrue;
}
// TODO(not specified yet): An element to attributes mapping that is // considered before the attributes/removeAttributes lists that apply to // everything. if (mElements) {
int32_t namespaceID = aElement->NodeInfo()->NamespaceID();
RefPtr<nsAtom> nameAtom = aElement->NodeInfo()->NameAtom();
if (auto entry = mElements->Lookup(ElementName(namespaceID, nameAtom))) { if (entry->mRemoveAttributes &&
MatchesAttributeName(*entry->mRemoveAttributes, aAttrNamespace,
aAttrLocalName)) { returntrue;
}
if (entry->mAttributes) { if (!MatchesAttributeName(*entry->mAttributes, aAttrNamespace,
aAttrLocalName)) { returntrue;
} // Fall-through to the removeAttributes/attributes lists below.
}
}
}
// Step 4. If attribute matches any attribute match list in config’s attribute // drop list: Return drop. if (mRemoveAttributes &&
MatchesAttributeName(*mRemoveAttributes, aAttrNamespace,
aAttrLocalName)) { returntrue;
}
// Step 5. If attribute allow list exists in config: if (mAttributes) { // Step 5.1. Then let allow list be |config|["allowAttributes"]. // Step 6. If attribute does not match any attribute match list in allow // list: Return drop. if (!MatchesAttributeName(*mAttributes, aAttrNamespace, aAttrLocalName)) { returntrue;
}
} else { // Step 5.2. Otherwise: Let allow list be the default configuration's // attribute allow list. // Step 6. If attribute does not match any attribute // match list in allow list: Return drop. if (!sDefaultConfigurationAttributeAllowlist->Contains(aAttrLocalName)) { returntrue;
}
}
// Step 7. Return keep. returnfalse;
}
// https://wicg.github.io/sanitizer-api/#handle-funky-elements bool nsTreeSanitizer::MustDropFunkyAttribute(Element* aElement,
int32_t aAttrNamespace,
nsAtom* aAttrLocalName) { // Step 1. If element’s element interface is HTMLTemplateElement: // Note: This step is implemented in the main loop of SanitizeChildren.
// Step 2. If element’s element interface has a HTMLHyperlinkElementUtils // mixin, and if element’s protocol property is "javascript:": // TODO(https://github.com/WICG/sanitizer-api/issues/168) if (aAttrLocalName == nsGkAtoms::href) { if (nsCOMPtr<Link> link = do_QueryInterface(aElement)) {
nsCOMPtr<nsIURI> uri = link->GetURI(); if (uri && uri->SchemeIs("javascript")) { // Step 2.1. Remove the `href` attribute from element. returntrue;
}
}
}
// Step 3. if element’s element interface is HTMLFormElement, and if element’s // action attribute is a URL with "javascript:" protocol: if (auto* form = HTMLFormElement::FromNode(aElement)) { if (aAttrNamespace == kNameSpaceID_None &&
aAttrLocalName == nsGkAtoms::action) {
nsCOMPtr<nsIURI> uri;
form->GetURIAttr(aAttrLocalName, nullptr, getter_AddRefs(uri)); if (uri && uri->SchemeIs("javascript")) { // Step 3.1 Remove the `action` attribute from element. returntrue;
}
}
}
// Step 4. if element’s element interface is HTMLInputElement or // HTMLButtonElement, and if element’s formaction attribute is a [URL] with // javascript: protocol if (aElement->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::button) &&
aAttrNamespace == kNameSpaceID_None &&
aAttrLocalName == nsGkAtoms::formaction) { // XXX nsGenericHTMLFormControlElementWithState::GetFormAction falls back to // the document URI.
nsGenericHTMLElement* el = nsGenericHTMLElement::FromNode(aElement);
nsCOMPtr<nsIURI> uri;
el->GetURIAttr(aAttrLocalName, nullptr, getter_AddRefs(uri)); if (uri && uri->SchemeIs("javascript")) { // Step 4.1 Remove the `formaction` attribute from element. returntrue;
}
}
// Get value and remove mandatory quotes staticconstchar* kWhitespace = "\n\r\t\b"; const nsAString& v = nsContentUtils::TrimCharsInSet(kWhitespace, value); // Fragment-only url cannot be harmful. if (!v.IsEmpty() && v.First() == u'#') { returnfalse;
} // if we allow only same-document fragment URLs, stop and remove here if (aFragmentsOnly) {
aElement->UnsetAttr(aNamespace, aLocalName, false); if (mLogRemovals) {
LogMessage("Removed unsafe URI from element attribute.",
aElement->OwnerDoc(), aElement, aLocalName);
} returntrue;
}
nsCOMPtr<nsIURI> attrURI;
nsresult rv =
NS_NewURI(getter_AddRefs(attrURI), v, nullptr, aElement->GetBaseURI()); if (NS_SUCCEEDED(rv)) { if (mCidEmbedsOnly && kNameSpaceID_None == aNamespace) { if (nsGkAtoms::src == aLocalName || nsGkAtoms::background == aLocalName) { // comm-central uses a hack that makes nsIURIs created with cid: specs // actually have an about:blank spec. Therefore, nsIURI facilities are // useless for cid: when comm-central code is participating. if (!(v.Length() > 4 && (v[0] == 'c' || v[0] == 'C') &&
(v[1] == 'i' || v[1] == 'I') && (v[2] == 'd' || v[2] == 'D') &&
v[3] == ':')) {
rv = NS_ERROR_FAILURE;
}
} elseif (nsGkAtoms::cdgroup_ == aLocalName ||
nsGkAtoms::altimg_ == aLocalName ||
nsGkAtoms::definitionURL_ == aLocalName) { // Gecko doesn't fetch these now and shouldn't in the future, but // in case someone goofs with these in the future, let's drop them.
rv = NS_ERROR_FAILURE;
} else {
rv = secMan->CheckLoadURIWithPrincipal(sNullPrincipal, attrURI, flags, 0);
}
} else {
rv = secMan->CheckLoadURIWithPrincipal(sNullPrincipal, attrURI, flags, 0);
}
} if (NS_FAILED(rv)) {
aElement->UnsetAttr(aNamespace, aLocalName, false); if (mLogRemovals) {
LogMessage("Removed unsafe URI from element attribute.",
aElement->OwnerDoc(), aElement, aLocalName);
} returntrue;
} returnfalse;
}
void nsTreeSanitizer::Sanitize(DocumentFragment* aFragment) { // If you want to relax these preconditions, be sure to check the code in // here that notifies / does not notify or that fires mutation events if // in tree.
MOZ_ASSERT(!aFragment->IsInUncomposedDoc(), "The fragment is in doc?");
void nsTreeSanitizer::Sanitize(Document* aDocument) { // If you want to relax these preconditions, be sure to check the code in // here that notifies / does not notify or that fires mutation events if // in tree. #ifdef DEBUG
MOZ_ASSERT(!aDocument->GetContainer(), "The document is in a shell.");
RefPtr<mozilla::dom::Element> root = aDocument->GetRootElement();
MOZ_ASSERT(root->IsHTMLElement(nsGkAtoms::html), "Not HTML root."); #endif
void nsTreeSanitizer::InitializeStatics() {
MOZ_ASSERT(!sElementsHTML, "Initializing a second time.");
sElementsHTML = new AtomsTable(std::size(kElementsHTML)); for (uint32_t i = 0; kElementsHTML[i]; i++) {
sElementsHTML->Insert(kElementsHTML[i]);
}
sAttributesHTML = new AtomsTable(std::size(kAttributesHTML)); for (uint32_t i = 0; kAttributesHTML[i]; i++) {
sAttributesHTML->Insert(kAttributesHTML[i]);
}
sPresAttributesHTML = new AtomsTable(std::size(kPresAttributesHTML)); for (uint32_t i = 0; kPresAttributesHTML[i]; i++) {
sPresAttributesHTML->Insert(kPresAttributesHTML[i]);
}
sElementsSVG = new AtomsTable(std::size(kElementsSVG)); for (uint32_t i = 0; kElementsSVG[i]; i++) {
sElementsSVG->Insert(kElementsSVG[i]);
}
sAttributesSVG = new AtomsTable(std::size(kAttributesSVG)); for (uint32_t i = 0; kAttributesSVG[i]; i++) {
sAttributesSVG->Insert(kAttributesSVG[i]);
}
sElementsMathML = new AtomsTable(std::size(kElementsMathML)); for (uint32_t i = 0; kElementsMathML[i]; i++) {
sElementsMathML->Insert(kElementsMathML[i]);
}
sAttributesMathML = new AtomsTable(std::size(kAttributesMathML)); for (uint32_t i = 0; kAttributesMathML[i]; i++) {
sAttributesMathML->Insert(kAttributesMathML[i]);
}
sBaselineAttributeAllowlist = new AtomsTable(std::size(kBaselineAttributeAllowlist)); for (constauto* atom : kBaselineAttributeAllowlist) {
sBaselineAttributeAllowlist->Insert(atom);
}
sBaselineElementAllowlist = new AtomsTable(std::size(kBaselineElementAllowlist)); for (constauto* atom : kBaselineElementAllowlist) {
sBaselineElementAllowlist->Insert(atom);
}
sDefaultConfigurationAttributeAllowlist = new AtomsTable(std::size(kDefaultConfigurationAttributeAllowlist)); for (constauto* atom : kDefaultConfigurationAttributeAllowlist) {
sDefaultConfigurationAttributeAllowlist->Insert(atom);
}
sDefaultConfigurationElementAllowlist = new AtomsTable(std::size(kDefaultConfigurationElementAllowlist)); for (constauto* atom : kDefaultConfigurationElementAllowlist) {
sDefaultConfigurationElementAllowlist->Insert(atom);
}
nsCOMPtr<nsIPrincipal> principal =
NullPrincipal::CreateWithoutOriginAttributes();
principal.forget(&sNullPrincipal);
}
static int32_t ConvertNamespaceString(const nsAString& aNamespace, bool aForAttribute,
mozilla::ErrorResult& aRv) { if (aNamespace.IsVoid()) { // NOTE: Currently this ?should? never match any elements, only // attributes. return kNameSpaceID_None;
}
for (constauto& entry : aElements) { if (entry.IsString()) {
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(entry.GetAsString()); // The default namespace for elements is HTML.
ElementName elemName(kNameSpaceID_XHTML, std::move(nameAtom)); // No explicit list of attributes to allow/remove.
map.InsertOrUpdate(elemName, ElementWithAttributes{});
} else { constauto& elemNamespace =
entry.GetAsSanitizerElementNamespaceWithAttributes();
ElementWithAttributes elemWithAttributes;
if (elemNamespace.mAttributes.WasPassed()) {
elemWithAttributes.mAttributes.emplace(
ConvertAttributes(elemNamespace.mAttributes.Value(), aRv)); if (aRv.Failed()) { break;
}
}
if (elemNamespace.mRemoveAttributes.WasPassed()) {
elemWithAttributes.mRemoveAttributes.emplace(
ConvertAttributes(elemNamespace.mRemoveAttributes.Value(), aRv)); if (aRv.Failed()) { break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.