/* -*- 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. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsTreeSanitizer.h" #include "mozilla/ArrayUtils.h" #include "mozilla/BindingStyleRule.h" #include "mozilla/DeclarationBlock.h" #include "mozilla/ServoDeclarationBlock.h" #include "mozilla/StyleSheetInlines.h" #ifdef MOZ_OLD_STYLE #include "mozilla/css/Declaration.h" #include "mozilla/css/StyleRule.h" #endif #include "mozilla/css/Rule.h" #include "mozilla/dom/CSSRuleList.h" #include "mozilla/dom/SRIMetadata.h" #include "nsCSSParser.h" #include "nsCSSPropertyID.h" #include "nsUnicharInputStream.h" #include "nsAttrName.h" #include "nsIScriptError.h" #include "nsIScriptSecurityManager.h" #include "nsNetUtil.h" #include "nsComponentManagerUtils.h" #include "NullPrincipal.h" #include "nsContentUtils.h" #include "nsIParserUtils.h" #include "nsIDocument.h" #include "nsQueryObject.h" using namespace mozilla; using namespace mozilla::dom; // // Thanks to Mark Pilgrim and Sam Ruby for the initial whitelist // nsStaticAtom** const kElementsHTML[] = { &nsGkAtoms::a, &nsGkAtoms::abbr, &nsGkAtoms::acronym, &nsGkAtoms::address, &nsGkAtoms::area, &nsGkAtoms::article, &nsGkAtoms::aside, &nsGkAtoms::audio, &nsGkAtoms::b, &nsGkAtoms::bdi, &nsGkAtoms::bdo, &nsGkAtoms::big, &nsGkAtoms::blockquote, // body checked specially &nsGkAtoms::br, &nsGkAtoms::button, &nsGkAtoms::canvas, &nsGkAtoms::caption, &nsGkAtoms::center, &nsGkAtoms::cite, &nsGkAtoms::code, &nsGkAtoms::col, &nsGkAtoms::colgroup, &nsGkAtoms::datalist, &nsGkAtoms::dd, &nsGkAtoms::del, &nsGkAtoms::details, &nsGkAtoms::dfn, &nsGkAtoms::dir, &nsGkAtoms::div, &nsGkAtoms::dl, &nsGkAtoms::dt, &nsGkAtoms::em, &nsGkAtoms::fieldset, &nsGkAtoms::figcaption, &nsGkAtoms::figure, &nsGkAtoms::font, &nsGkAtoms::footer, &nsGkAtoms::form, &nsGkAtoms::h1, &nsGkAtoms::h2, &nsGkAtoms::h3, &nsGkAtoms::h4, &nsGkAtoms::h5, &nsGkAtoms::h6, // head checked specially &nsGkAtoms::header, &nsGkAtoms::hgroup, &nsGkAtoms::hr, // html checked specially &nsGkAtoms::i, &nsGkAtoms::img, &nsGkAtoms::input, &nsGkAtoms::ins, &nsGkAtoms::kbd, &nsGkAtoms::label, &nsGkAtoms::legend, &nsGkAtoms::li, &nsGkAtoms::link, &nsGkAtoms::listing, &nsGkAtoms::map, &nsGkAtoms::mark, &nsGkAtoms::menu, &nsGkAtoms::meta, &nsGkAtoms::meter, &nsGkAtoms::nav, &nsGkAtoms::nobr, &nsGkAtoms::noscript, &nsGkAtoms::ol, &nsGkAtoms::optgroup, &nsGkAtoms::option, &nsGkAtoms::output, &nsGkAtoms::p, &nsGkAtoms::pre, &nsGkAtoms::progress, &nsGkAtoms::q, &nsGkAtoms::rb, &nsGkAtoms::rp, &nsGkAtoms::rt, &nsGkAtoms::rtc, &nsGkAtoms::ruby, &nsGkAtoms::s, &nsGkAtoms::samp, &nsGkAtoms::section, &nsGkAtoms::select, &nsGkAtoms::small, &nsGkAtoms::source, &nsGkAtoms::span, &nsGkAtoms::strike, &nsGkAtoms::strong, &nsGkAtoms::sub, &nsGkAtoms::summary, &nsGkAtoms::sup, // style checked specially &nsGkAtoms::table, &nsGkAtoms::tbody, &nsGkAtoms::td, &nsGkAtoms::textarea, &nsGkAtoms::tfoot, &nsGkAtoms::th, &nsGkAtoms::thead, &nsGkAtoms::time, // title checked specially &nsGkAtoms::tr, &nsGkAtoms::track, &nsGkAtoms::tt, &nsGkAtoms::u, &nsGkAtoms::ul, &nsGkAtoms::var, &nsGkAtoms::video, &nsGkAtoms::wbr, nullptr }; nsStaticAtom** const kAttributesHTML[] = { &nsGkAtoms::abbr, &nsGkAtoms::accept, &nsGkAtoms::acceptcharset, &nsGkAtoms::accesskey, &nsGkAtoms::action, &nsGkAtoms::alt, &nsGkAtoms::as, &nsGkAtoms::autocomplete, &nsGkAtoms::autofocus, &nsGkAtoms::autoplay, &nsGkAtoms::axis, &nsGkAtoms::_char, &nsGkAtoms::charoff, &nsGkAtoms::charset, &nsGkAtoms::checked, &nsGkAtoms::cite, &nsGkAtoms::_class, &nsGkAtoms::cols, &nsGkAtoms::colspan, &nsGkAtoms::content, &nsGkAtoms::contenteditable, &nsGkAtoms::contextmenu, &nsGkAtoms::controls, &nsGkAtoms::coords, &nsGkAtoms::crossorigin, &nsGkAtoms::datetime, &nsGkAtoms::dir, &nsGkAtoms::disabled, &nsGkAtoms::draggable, &nsGkAtoms::enctype, &nsGkAtoms::face, &nsGkAtoms::_for, &nsGkAtoms::frame, &nsGkAtoms::headers, &nsGkAtoms::height, &nsGkAtoms::hidden, &nsGkAtoms::high, &nsGkAtoms::href, &nsGkAtoms::hreflang, &nsGkAtoms::icon, &nsGkAtoms::id, &nsGkAtoms::integrity, &nsGkAtoms::ismap, &nsGkAtoms::itemid, &nsGkAtoms::itemprop, &nsGkAtoms::itemref, &nsGkAtoms::itemscope, &nsGkAtoms::itemtype, &nsGkAtoms::kind, &nsGkAtoms::label, &nsGkAtoms::lang, &nsGkAtoms::list, &nsGkAtoms::longdesc, &nsGkAtoms::loop, &nsGkAtoms::low, &nsGkAtoms::max, &nsGkAtoms::maxlength, &nsGkAtoms::media, &nsGkAtoms::method, &nsGkAtoms::min, &nsGkAtoms::minlength, &nsGkAtoms::multiple, &nsGkAtoms::muted, &nsGkAtoms::name, &nsGkAtoms::nohref, &nsGkAtoms::novalidate, &nsGkAtoms::nowrap, &nsGkAtoms::open, &nsGkAtoms::optimum, &nsGkAtoms::pattern, &nsGkAtoms::placeholder, &nsGkAtoms::playbackrate, &nsGkAtoms::poster, &nsGkAtoms::preload, &nsGkAtoms::prompt, &nsGkAtoms::pubdate, &nsGkAtoms::radiogroup, &nsGkAtoms::readonly, &nsGkAtoms::rel, &nsGkAtoms::required, &nsGkAtoms::rev, &nsGkAtoms::reversed, &nsGkAtoms::role, &nsGkAtoms::rows, &nsGkAtoms::rowspan, &nsGkAtoms::rules, &nsGkAtoms::scoped, &nsGkAtoms::scope, &nsGkAtoms::selected, &nsGkAtoms::shape, &nsGkAtoms::span, &nsGkAtoms::spellcheck, &nsGkAtoms::src, &nsGkAtoms::srclang, &nsGkAtoms::start, &nsGkAtoms::summary, &nsGkAtoms::tabindex, &nsGkAtoms::target, &nsGkAtoms::title, &nsGkAtoms::type, &nsGkAtoms::usemap, &nsGkAtoms::value, &nsGkAtoms::width, &nsGkAtoms::wrap, nullptr }; nsStaticAtom** const kPresAttributesHTML[] = { &nsGkAtoms::align, &nsGkAtoms::background, &nsGkAtoms::bgcolor, &nsGkAtoms::border, &nsGkAtoms::cellpadding, &nsGkAtoms::cellspacing, &nsGkAtoms::color, &nsGkAtoms::compact, &nsGkAtoms::clear, &nsGkAtoms::hspace, &nsGkAtoms::noshade, &nsGkAtoms::pointSize, &nsGkAtoms::size, &nsGkAtoms::valign, &nsGkAtoms::vspace, nullptr }; nsStaticAtom** const kURLAttributesHTML[] = { &nsGkAtoms::action, &nsGkAtoms::href, &nsGkAtoms::src, &nsGkAtoms::longdesc, &nsGkAtoms::cite, &nsGkAtoms::background, nullptr }; nsStaticAtom** const kElementsSVG[] = { &nsGkAtoms::a, // a &nsGkAtoms::circle, // circle &nsGkAtoms::clipPath, // clipPath &nsGkAtoms::colorProfile, // color-profile &nsGkAtoms::cursor, // cursor &nsGkAtoms::defs, // defs &nsGkAtoms::desc, // desc &nsGkAtoms::ellipse, // ellipse &nsGkAtoms::elevation, // elevation &nsGkAtoms::erode, // erode &nsGkAtoms::ex, // ex &nsGkAtoms::exact, // exact &nsGkAtoms::exponent, // exponent &nsGkAtoms::feBlend, // feBlend &nsGkAtoms::feColorMatrix, // feColorMatrix &nsGkAtoms::feComponentTransfer, // feComponentTransfer &nsGkAtoms::feComposite, // feComposite &nsGkAtoms::feConvolveMatrix, // feConvolveMatrix &nsGkAtoms::feDiffuseLighting, // feDiffuseLighting &nsGkAtoms::feDisplacementMap, // feDisplacementMap &nsGkAtoms::feDistantLight, // feDistantLight &nsGkAtoms::feDropShadow, // feDropShadow &nsGkAtoms::feFlood, // feFlood &nsGkAtoms::feFuncA, // feFuncA &nsGkAtoms::feFuncB, // feFuncB &nsGkAtoms::feFuncG, // feFuncG &nsGkAtoms::feFuncR, // feFuncR &nsGkAtoms::feGaussianBlur, // feGaussianBlur &nsGkAtoms::feImage, // feImage &nsGkAtoms::feMerge, // feMerge &nsGkAtoms::feMergeNode, // feMergeNode &nsGkAtoms::feMorphology, // feMorphology &nsGkAtoms::feOffset, // feOffset &nsGkAtoms::fePointLight, // fePointLight &nsGkAtoms::feSpecularLighting, // feSpecularLighting &nsGkAtoms::feSpotLight, // feSpotLight &nsGkAtoms::feTile, // feTile &nsGkAtoms::feTurbulence, // feTurbulence &nsGkAtoms::filter, // filter &nsGkAtoms::font, // font &nsGkAtoms::font_face, // font-face &nsGkAtoms::font_face_format, // font-face-format &nsGkAtoms::font_face_name, // font-face-name &nsGkAtoms::font_face_src, // font-face-src &nsGkAtoms::font_face_uri, // font-face-uri &nsGkAtoms::foreignObject, // foreignObject &nsGkAtoms::g, // g // glyph &nsGkAtoms::glyphRef, // glyphRef // hkern &nsGkAtoms::image, // image &nsGkAtoms::line, // line &nsGkAtoms::linearGradient, // linearGradient &nsGkAtoms::marker, // marker &nsGkAtoms::mask, // mask &nsGkAtoms::metadata, // metadata &nsGkAtoms::missingGlyph, // missingGlyph &nsGkAtoms::mpath, // mpath &nsGkAtoms::path, // path &nsGkAtoms::pattern, // pattern &nsGkAtoms::polygon, // polygon &nsGkAtoms::polyline, // polyline &nsGkAtoms::radialGradient, // radialGradient &nsGkAtoms::rect, // rect &nsGkAtoms::stop, // stop &nsGkAtoms::svg, // svg &nsGkAtoms::svgSwitch, // switch &nsGkAtoms::symbol, // symbol &nsGkAtoms::text, // text &nsGkAtoms::textPath, // textPath &nsGkAtoms::title, // title &nsGkAtoms::tref, // tref &nsGkAtoms::tspan, // tspan &nsGkAtoms::use, // use &nsGkAtoms::view, // view // vkern nullptr }; nsStaticAtom** const kAttributesSVG[] = { // accent-height &nsGkAtoms::accumulate, // accumulate &nsGkAtoms::additive, // additive &nsGkAtoms::alignment_baseline, // alignment-baseline // alphabetic &nsGkAtoms::amplitude, // amplitude // arabic-form // ascent &nsGkAtoms::attributeName, // attributeName &nsGkAtoms::attributeType, // attributeType &nsGkAtoms::azimuth, // azimuth &nsGkAtoms::baseFrequency, // baseFrequency &nsGkAtoms::baseline_shift, // baseline-shift // baseProfile // bbox &nsGkAtoms::begin, // begin &nsGkAtoms::bias, // bias &nsGkAtoms::by, // by &nsGkAtoms::calcMode, // calcMode // cap-height &nsGkAtoms::_class, // class &nsGkAtoms::clip_path, // clip-path &nsGkAtoms::clip_rule, // clip-rule &nsGkAtoms::clipPathUnits, // clipPathUnits &nsGkAtoms::color, // color &nsGkAtoms::colorInterpolation, // color-interpolation &nsGkAtoms::colorInterpolationFilters, // color-interpolation-filters &nsGkAtoms::cursor, // cursor &nsGkAtoms::cx, // cx &nsGkAtoms::cy, // cy &nsGkAtoms::d, // d // descent &nsGkAtoms::diffuseConstant, // diffuseConstant &nsGkAtoms::direction, // direction &nsGkAtoms::display, // display &nsGkAtoms::divisor, // divisor &nsGkAtoms::dominant_baseline, // dominant-baseline &nsGkAtoms::dur, // dur &nsGkAtoms::dx, // dx &nsGkAtoms::dy, // dy &nsGkAtoms::edgeMode, // edgeMode &nsGkAtoms::elevation, // elevation // enable-background &nsGkAtoms::end, // end &nsGkAtoms::fill, // fill &nsGkAtoms::fill_opacity, // fill-opacity &nsGkAtoms::fill_rule, // fill-rule &nsGkAtoms::filter, // filter &nsGkAtoms::filterUnits, // filterUnits &nsGkAtoms::flood_color, // flood-color &nsGkAtoms::flood_opacity, // flood-opacity // XXX focusable &nsGkAtoms::font, // font &nsGkAtoms::font_family, // font-family &nsGkAtoms::font_size, // font-size &nsGkAtoms::font_size_adjust, // font-size-adjust &nsGkAtoms::font_stretch, // font-stretch &nsGkAtoms::font_style, // font-style &nsGkAtoms::font_variant, // font-variant &nsGkAtoms::fontWeight, // font-weight &nsGkAtoms::format, // format &nsGkAtoms::from, // from &nsGkAtoms::fx, // fx &nsGkAtoms::fy, // fy // g1 // g2 // glyph-name // glyphRef // glyph-orientation-horizontal // glyph-orientation-vertical &nsGkAtoms::gradientTransform, // gradientTransform &nsGkAtoms::gradientUnits, // gradientUnits &nsGkAtoms::height, // height // horiz-adv-x // horiz-origin-x // horiz-origin-y &nsGkAtoms::id, // id // ideographic &nsGkAtoms::image_rendering, // image-rendering &nsGkAtoms::in, // in &nsGkAtoms::in2, // in2 &nsGkAtoms::intercept, // intercept // k &nsGkAtoms::k1, // k1 &nsGkAtoms::k2, // k2 &nsGkAtoms::k3, // k3 &nsGkAtoms::k4, // k4 // kerning &nsGkAtoms::kernelMatrix, // kernelMatrix &nsGkAtoms::kernelUnitLength, // kernelUnitLength &nsGkAtoms::keyPoints, // keyPoints &nsGkAtoms::keySplines, // keySplines &nsGkAtoms::keyTimes, // keyTimes &nsGkAtoms::lang, // lang // lengthAdjust &nsGkAtoms::letter_spacing, // letter-spacing &nsGkAtoms::lighting_color, // lighting-color &nsGkAtoms::limitingConeAngle, // limitingConeAngle // local &nsGkAtoms::marker, // marker &nsGkAtoms::marker_end, // marker-end &nsGkAtoms::marker_mid, // marker-mid &nsGkAtoms::marker_start, // marker-start &nsGkAtoms::markerHeight, // markerHeight &nsGkAtoms::markerUnits, // markerUnits &nsGkAtoms::markerWidth, // markerWidth &nsGkAtoms::mask, // mask &nsGkAtoms::maskContentUnits, // maskContentUnits &nsGkAtoms::maskUnits, // maskUnits // mathematical &nsGkAtoms::max, // max &nsGkAtoms::media, // media &nsGkAtoms::method, // method &nsGkAtoms::min, // min &nsGkAtoms::mode, // mode &nsGkAtoms::name, // name &nsGkAtoms::numOctaves, // numOctaves &nsGkAtoms::offset, // offset &nsGkAtoms::opacity, // opacity &nsGkAtoms::_operator, // operator &nsGkAtoms::order, // order &nsGkAtoms::orient, // orient &nsGkAtoms::orientation, // orientation // origin // overline-position // overline-thickness &nsGkAtoms::overflow, // overflow // panose-1 &nsGkAtoms::path, // path &nsGkAtoms::pathLength, // pathLength &nsGkAtoms::patternContentUnits, // patternContentUnits &nsGkAtoms::patternTransform, // patternTransform &nsGkAtoms::patternUnits, // patternUnits &nsGkAtoms::pointer_events, // pointer-events XXX is this safe? &nsGkAtoms::points, // points &nsGkAtoms::pointsAtX, // pointsAtX &nsGkAtoms::pointsAtY, // pointsAtY &nsGkAtoms::pointsAtZ, // pointsAtZ &nsGkAtoms::preserveAlpha, // preserveAlpha &nsGkAtoms::preserveAspectRatio, // preserveAspectRatio &nsGkAtoms::primitiveUnits, // primitiveUnits &nsGkAtoms::r, // r &nsGkAtoms::radius, // radius &nsGkAtoms::refX, // refX &nsGkAtoms::refY, // refY &nsGkAtoms::repeatCount, // repeatCount &nsGkAtoms::repeatDur, // repeatDur &nsGkAtoms::requiredExtensions, // requiredExtensions &nsGkAtoms::requiredFeatures, // requiredFeatures &nsGkAtoms::restart, // restart &nsGkAtoms::result, // result &nsGkAtoms::rotate, // rotate &nsGkAtoms::rx, // rx &nsGkAtoms::ry, // ry &nsGkAtoms::scale, // scale &nsGkAtoms::seed, // seed &nsGkAtoms::shape_rendering, // shape-rendering &nsGkAtoms::slope, // slope &nsGkAtoms::spacing, // spacing &nsGkAtoms::specularConstant, // specularConstant &nsGkAtoms::specularExponent, // specularExponent &nsGkAtoms::spreadMethod, // spreadMethod &nsGkAtoms::startOffset, // startOffset &nsGkAtoms::stdDeviation, // stdDeviation // stemh // stemv &nsGkAtoms::stitchTiles, // stitchTiles &nsGkAtoms::stop_color, // stop-color &nsGkAtoms::stop_opacity, // stop-opacity // strikethrough-position // strikethrough-thickness &nsGkAtoms::string, // string &nsGkAtoms::stroke, // stroke &nsGkAtoms::stroke_dasharray, // stroke-dasharray &nsGkAtoms::stroke_dashoffset, // stroke-dashoffset &nsGkAtoms::stroke_linecap, // stroke-linecap &nsGkAtoms::stroke_linejoin, // stroke-linejoin &nsGkAtoms::stroke_miterlimit, // stroke-miterlimit &nsGkAtoms::stroke_opacity, // stroke-opacity &nsGkAtoms::stroke_width, // stroke-width &nsGkAtoms::surfaceScale, // surfaceScale &nsGkAtoms::systemLanguage, // systemLanguage &nsGkAtoms::tableValues, // tableValues &nsGkAtoms::target, // target &nsGkAtoms::targetX, // targetX &nsGkAtoms::targetY, // targetY &nsGkAtoms::text_anchor, // text-anchor &nsGkAtoms::text_decoration, // text-decoration // textLength &nsGkAtoms::text_rendering, // text-rendering &nsGkAtoms::title, // title &nsGkAtoms::to, // to &nsGkAtoms::transform, // transform &nsGkAtoms::type, // type // u1 // u2 // underline-position // underline-thickness // unicode &nsGkAtoms::unicode_bidi, // unicode-bidi // unicode-range // units-per-em // v-alphabetic // v-hanging // v-ideographic // v-mathematical &nsGkAtoms::values, // values &nsGkAtoms::vector_effect, // vector-effect // vert-adv-y // vert-origin-x // vert-origin-y &nsGkAtoms::viewBox, // viewBox &nsGkAtoms::viewTarget, // viewTarget &nsGkAtoms::visibility, // visibility &nsGkAtoms::width, // width // widths &nsGkAtoms::word_spacing, // word-spacing &nsGkAtoms::writing_mode, // writing-mode &nsGkAtoms::x, // x // x-height &nsGkAtoms::x1, // x1 &nsGkAtoms::x2, // x2 &nsGkAtoms::xChannelSelector, // xChannelSelector &nsGkAtoms::y, // y &nsGkAtoms::y1, // y1 &nsGkAtoms::y2, // y2 &nsGkAtoms::yChannelSelector, // yChannelSelector &nsGkAtoms::z, // z &nsGkAtoms::zoomAndPan, // zoomAndPan nullptr }; nsStaticAtom** const kURLAttributesSVG[] = { &nsGkAtoms::href, nullptr }; nsStaticAtom** const kElementsMathML[] = { &nsGkAtoms::abs_, // abs &nsGkAtoms::_and, // and &nsGkAtoms::annotation_, // annotation &nsGkAtoms::annotation_xml_, // annotation-xml &nsGkAtoms::apply_, // apply &nsGkAtoms::approx_, // approx &nsGkAtoms::arccos_, // arccos &nsGkAtoms::arccosh_, // arccosh &nsGkAtoms::arccot_, // arccot &nsGkAtoms::arccoth_, // arccoth &nsGkAtoms::arccsc_, // arccsc &nsGkAtoms::arccsch_, // arccsch &nsGkAtoms::arcsec_, // arcsec &nsGkAtoms::arcsech_, // arcsech &nsGkAtoms::arcsin_, // arcsin &nsGkAtoms::arcsinh_, // arcsinh &nsGkAtoms::arctan_, // arctan &nsGkAtoms::arctanh_, // arctanh &nsGkAtoms::arg_, // arg &nsGkAtoms::bind_, // bind &nsGkAtoms::bvar_, // bvar &nsGkAtoms::card_, // card &nsGkAtoms::cartesianproduct_, // cartesianproduct &nsGkAtoms::cbytes_, // cbytes &nsGkAtoms::ceiling, // ceiling &nsGkAtoms::cerror_, // cerror &nsGkAtoms::ci_, // ci &nsGkAtoms::cn_, // cn &nsGkAtoms::codomain_, // codomain &nsGkAtoms::complexes_, // complexes &nsGkAtoms::compose_, // compose &nsGkAtoms::condition_, // condition &nsGkAtoms::conjugate_, // conjugate &nsGkAtoms::cos_, // cos &nsGkAtoms::cosh_, // cosh &nsGkAtoms::cot_, // cot &nsGkAtoms::coth_, // coth &nsGkAtoms::cs_, // cs &nsGkAtoms::csc_, // csc &nsGkAtoms::csch_, // csch &nsGkAtoms::csymbol_, // csymbol &nsGkAtoms::curl_, // curl &nsGkAtoms::declare, // declare &nsGkAtoms::degree_, // degree &nsGkAtoms::determinant_, // determinant &nsGkAtoms::diff_, // diff &nsGkAtoms::divergence_, // divergence &nsGkAtoms::divide_, // divide &nsGkAtoms::domain_, // domain &nsGkAtoms::domainofapplication_, // domainofapplication &nsGkAtoms::el_, // el &nsGkAtoms::emptyset_, // emptyset &nsGkAtoms::eq_, // eq &nsGkAtoms::equivalent_, // equivalent &nsGkAtoms::eulergamma_, // eulergamma &nsGkAtoms::exists_, // exists &nsGkAtoms::exp_, // exp &nsGkAtoms::exponentiale_, // exponentiale &nsGkAtoms::factorial_, // factorial &nsGkAtoms::factorof_, // factorof &nsGkAtoms::_false, // false &nsGkAtoms::floor, // floor &nsGkAtoms::fn_, // fn &nsGkAtoms::forall_, // forall &nsGkAtoms::gcd_, // gcd &nsGkAtoms::geq_, // geq &nsGkAtoms::grad, // grad &nsGkAtoms::gt_, // gt &nsGkAtoms::ident_, // ident &nsGkAtoms::image, // image &nsGkAtoms::imaginary_, // imaginary &nsGkAtoms::imaginaryi_, // imaginaryi &nsGkAtoms::implies_, // implies &nsGkAtoms::in, // in &nsGkAtoms::infinity, // infinity &nsGkAtoms::int_, // int &nsGkAtoms::integers_, // integers &nsGkAtoms::intersect_, // intersect &nsGkAtoms::interval_, // interval &nsGkAtoms::inverse_, // inverse &nsGkAtoms::lambda_, // lambda &nsGkAtoms::laplacian_, // laplacian &nsGkAtoms::lcm_, // lcm &nsGkAtoms::leq_, // leq &nsGkAtoms::limit_, // limit &nsGkAtoms::list_, // list &nsGkAtoms::ln_, // ln &nsGkAtoms::log_, // log &nsGkAtoms::logbase_, // logbase &nsGkAtoms::lowlimit_, // lowlimit &nsGkAtoms::lt_, // lt &nsGkAtoms::maction_, // maction &nsGkAtoms::maligngroup_, // maligngroup &nsGkAtoms::malignmark_, // malignmark &nsGkAtoms::math, // math &nsGkAtoms::matrix, // matrix &nsGkAtoms::matrixrow_, // matrixrow &nsGkAtoms::max, // max &nsGkAtoms::mean_, // mean &nsGkAtoms::median_, // median &nsGkAtoms::menclose_, // menclose &nsGkAtoms::merror_, // merror &nsGkAtoms::mfenced_, // mfenced &nsGkAtoms::mfrac_, // mfrac &nsGkAtoms::mglyph_, // mglyph &nsGkAtoms::mi_, // mi &nsGkAtoms::min, // min &nsGkAtoms::minus_, // minus &nsGkAtoms::mlabeledtr_, // mlabeledtr &nsGkAtoms::mlongdiv_, // mlongdiv &nsGkAtoms::mmultiscripts_, // mmultiscripts &nsGkAtoms::mn_, // mn &nsGkAtoms::mo_, // mo &nsGkAtoms::mode, // mode &nsGkAtoms::moment_, // moment &nsGkAtoms::momentabout_, // momentabout &nsGkAtoms::mover_, // mover &nsGkAtoms::mpadded_, // mpadded &nsGkAtoms::mphantom_, // mphantom &nsGkAtoms::mprescripts_, // mprescripts &nsGkAtoms::mroot_, // mroot &nsGkAtoms::mrow_, // mrow &nsGkAtoms::ms_, // ms &nsGkAtoms::mscarries_, // mscarries &nsGkAtoms::mscarry_, // mscarry &nsGkAtoms::msgroup_, // msgroup &nsGkAtoms::msline_, // msline &nsGkAtoms::mspace_, // mspace &nsGkAtoms::msqrt_, // msqrt &nsGkAtoms::msrow_, // msrow &nsGkAtoms::mstack_, // mstack &nsGkAtoms::mstyle_, // mstyle &nsGkAtoms::msub_, // msub &nsGkAtoms::msubsup_, // msubsup &nsGkAtoms::msup_, // msup &nsGkAtoms::mtable_, // mtable &nsGkAtoms::mtd_, // mtd &nsGkAtoms::mtext_, // mtext &nsGkAtoms::mtr_, // mtr &nsGkAtoms::munder_, // munder &nsGkAtoms::munderover_, // munderover &nsGkAtoms::naturalnumbers_, // naturalnumbers &nsGkAtoms::neq_, // neq &nsGkAtoms::none, // none &nsGkAtoms::_not, // not &nsGkAtoms::notanumber_, // notanumber &nsGkAtoms::note_, // note &nsGkAtoms::notin_, // notin &nsGkAtoms::notprsubset_, // notprsubset &nsGkAtoms::notsubset_, // notsubset &nsGkAtoms::_or, // or &nsGkAtoms::otherwise, // otherwise &nsGkAtoms::outerproduct_, // outerproduct &nsGkAtoms::partialdiff_, // partialdiff &nsGkAtoms::pi_, // pi &nsGkAtoms::piece_, // piece &nsGkAtoms::piecewise_, // piecewise &nsGkAtoms::plus_, // plus &nsGkAtoms::power_, // power &nsGkAtoms::primes_, // primes &nsGkAtoms::product_, // product &nsGkAtoms::prsubset_, // prsubset &nsGkAtoms::quotient_, // quotient &nsGkAtoms::rationals_, // rationals &nsGkAtoms::real_, // real &nsGkAtoms::reals_, // reals &nsGkAtoms::reln_, // reln &nsGkAtoms::rem, // rem &nsGkAtoms::root_, // root &nsGkAtoms::scalarproduct_, // scalarproduct &nsGkAtoms::sdev_, // sdev &nsGkAtoms::sec_, // sec &nsGkAtoms::sech_, // sech &nsGkAtoms::selector_, // selector &nsGkAtoms::semantics_, // semantics &nsGkAtoms::sep_, // sep &nsGkAtoms::set_, // set &nsGkAtoms::setdiff_, // setdiff &nsGkAtoms::share_, // share &nsGkAtoms::sin_, // sin &nsGkAtoms::sinh_, // sinh &nsGkAtoms::subset_, // subset &nsGkAtoms::sum, // sum &nsGkAtoms::tan_, // tan &nsGkAtoms::tanh_, // tanh &nsGkAtoms::tendsto_, // tendsto &nsGkAtoms::times_, // times &nsGkAtoms::transpose_, // transpose &nsGkAtoms::_true, // true &nsGkAtoms::union_, // union &nsGkAtoms::uplimit_, // uplimit &nsGkAtoms::variance_, // variance &nsGkAtoms::vector_, // vector &nsGkAtoms::vectorproduct_, // vectorproduct &nsGkAtoms::xor_, // xor nullptr }; nsStaticAtom** const kAttributesMathML[] = { &nsGkAtoms::accent_, // accent &nsGkAtoms::accentunder_, // accentunder &nsGkAtoms::actiontype_, // actiontype &nsGkAtoms::align, // align &nsGkAtoms::alignmentscope_, // alignmentscope &nsGkAtoms::alt, // alt &nsGkAtoms::altimg_, // altimg &nsGkAtoms::altimg_height_, // altimg-height &nsGkAtoms::altimg_valign_, // altimg-valign &nsGkAtoms::altimg_width_, // altimg-width &nsGkAtoms::background, // background &nsGkAtoms::base, // base &nsGkAtoms::bevelled_, // bevelled &nsGkAtoms::cd_, // cd &nsGkAtoms::cdgroup_, // cdgroup &nsGkAtoms::charalign_, // charalign &nsGkAtoms::close, // close &nsGkAtoms::closure_, // closure &nsGkAtoms::color, // color &nsGkAtoms::columnalign_, // columnalign &nsGkAtoms::columnalignment_, // columnalignment &nsGkAtoms::columnlines_, // columnlines &nsGkAtoms::columnspacing_, // columnspacing &nsGkAtoms::columnspan_, // columnspan &nsGkAtoms::columnwidth_, // columnwidth &nsGkAtoms::crossout_, // crossout &nsGkAtoms::decimalpoint_, // decimalpoint &nsGkAtoms::definitionURL_, // definitionURL &nsGkAtoms::denomalign_, // denomalign &nsGkAtoms::depth_, // depth &nsGkAtoms::dir, // dir &nsGkAtoms::display, // display &nsGkAtoms::displaystyle_, // displaystyle &nsGkAtoms::edge_, // edge &nsGkAtoms::encoding, // encoding &nsGkAtoms::equalcolumns_, // equalcolumns &nsGkAtoms::equalrows_, // equalrows &nsGkAtoms::fence_, // fence &nsGkAtoms::fontfamily_, // fontfamily &nsGkAtoms::fontsize_, // fontsize &nsGkAtoms::fontstyle_, // fontstyle &nsGkAtoms::fontweight_, // fontweight &nsGkAtoms::form, // form &nsGkAtoms::frame, // frame &nsGkAtoms::framespacing_, // framespacing &nsGkAtoms::groupalign_, // groupalign &nsGkAtoms::height, // height &nsGkAtoms::href, // href &nsGkAtoms::id, // id &nsGkAtoms::indentalign_, // indentalign &nsGkAtoms::indentalignfirst_, // indentalignfirst &nsGkAtoms::indentalignlast_, // indentalignlast &nsGkAtoms::indentshift_, // indentshift &nsGkAtoms::indentshiftfirst_, // indentshiftfirst &nsGkAtoms::indenttarget_, // indenttarget &nsGkAtoms::index, // index &nsGkAtoms::integer, // integer &nsGkAtoms::largeop_, // largeop &nsGkAtoms::length, // length &nsGkAtoms::linebreak_, // linebreak &nsGkAtoms::linebreakmultchar_, // linebreakmultchar &nsGkAtoms::linebreakstyle_, // linebreakstyle &nsGkAtoms::linethickness_, // linethickness &nsGkAtoms::location_, // location &nsGkAtoms::longdivstyle_, // longdivstyle &nsGkAtoms::lquote_, // lquote &nsGkAtoms::lspace_, // lspace &nsGkAtoms::ltr, // ltr &nsGkAtoms::mathbackground_, // mathbackground &nsGkAtoms::mathcolor_, // mathcolor &nsGkAtoms::mathsize_, // mathsize &nsGkAtoms::mathvariant_, // mathvariant &nsGkAtoms::maxsize_, // maxsize &nsGkAtoms::minlabelspacing_, // minlabelspacing &nsGkAtoms::minsize_, // minsize &nsGkAtoms::movablelimits_, // movablelimits &nsGkAtoms::msgroup_, // msgroup &nsGkAtoms::name, // name &nsGkAtoms::newline, // newline &nsGkAtoms::notation_, // notation &nsGkAtoms::numalign_, // numalign &nsGkAtoms::number, // number &nsGkAtoms::open, // open &nsGkAtoms::order, // order &nsGkAtoms::other_, // other &nsGkAtoms::overflow, // overflow &nsGkAtoms::position, // position &nsGkAtoms::role, // role &nsGkAtoms::rowalign_, // rowalign &nsGkAtoms::rowlines_, // rowlines &nsGkAtoms::rowspacing_, // rowspacing &nsGkAtoms::rowspan, // rowspan &nsGkAtoms::rquote_, // rquote &nsGkAtoms::rspace_, // rspace &nsGkAtoms::schemaLocation_, // schemaLocation &nsGkAtoms::scriptlevel_, // scriptlevel &nsGkAtoms::scriptminsize_, // scriptminsize &nsGkAtoms::scriptsize_, // scriptsize &nsGkAtoms::scriptsizemultiplier_, // scriptsizemultiplier &nsGkAtoms::selection_, // selection &nsGkAtoms::separator_, // separator &nsGkAtoms::separators_, // separators &nsGkAtoms::shift_, // shift &nsGkAtoms::side_, // side &nsGkAtoms::src, // src &nsGkAtoms::stackalign_, // stackalign &nsGkAtoms::stretchy_, // stretchy &nsGkAtoms::subscriptshift_, // subscriptshift &nsGkAtoms::superscriptshift_, // superscriptshift &nsGkAtoms::symmetric_, // symmetric &nsGkAtoms::type, // type &nsGkAtoms::voffset_, // voffset &nsGkAtoms::width, // width &nsGkAtoms::xref_, // xref nullptr }; nsStaticAtom** const kURLAttributesMathML[] = { &nsGkAtoms::href, &nsGkAtoms::src, &nsGkAtoms::cdgroup_, &nsGkAtoms::altimg_, &nsGkAtoms::definitionURL_, nullptr }; nsTHashtable>* nsTreeSanitizer::sElementsHTML = nullptr; nsTHashtable>* nsTreeSanitizer::sAttributesHTML = nullptr; nsTHashtable>* nsTreeSanitizer::sPresAttributesHTML = nullptr; nsTHashtable>* nsTreeSanitizer::sElementsSVG = nullptr; nsTHashtable>* nsTreeSanitizer::sAttributesSVG = nullptr; nsTHashtable>* nsTreeSanitizer::sElementsMathML = nullptr; nsTHashtable>* nsTreeSanitizer::sAttributesMathML = nullptr; nsIPrincipal* nsTreeSanitizer::sNullPrincipal = nullptr; nsTreeSanitizer::nsTreeSanitizer(uint32_t aFlags) : mAllowStyles(aFlags & nsIParserUtils::SanitizerAllowStyle) , mAllowComments(aFlags & nsIParserUtils::SanitizerAllowComments) , mDropNonCSSPresentation(aFlags & nsIParserUtils::SanitizerDropNonCSSPresentation) , mDropForms(aFlags & nsIParserUtils::SanitizerDropForms) , mCidEmbedsOnly(aFlags & nsIParserUtils::SanitizerCidEmbedsOnly) , mDropMedia(aFlags & nsIParserUtils::SanitizerDropMedia) , mFullDocument(false) , mLogRemovals(aFlags & nsIParserUtils::SanitizerLogRemovals) { if (mCidEmbedsOnly) { // Sanitizing styles for external references is not supported. mAllowStyles = false; } if (!sElementsHTML) { // Initialize lazily to avoid having to initialize at all if the user // doesn't paste HTML or load feeds. InitializeStatics(); } } bool nsTreeSanitizer::MustFlatten(int32_t aNamespace, nsAtom* aLocal) { if (aNamespace == kNameSpaceID_XHTML) { if (mDropNonCSSPresentation && (nsGkAtoms::font == aLocal || nsGkAtoms::center == aLocal)) { return true; } if (mDropForms && (nsGkAtoms::form == aLocal || nsGkAtoms::input == aLocal || nsGkAtoms::keygen == aLocal || nsGkAtoms::option == aLocal || nsGkAtoms::optgroup == aLocal)) { return true; } if (mFullDocument && (nsGkAtoms::title == aLocal || nsGkAtoms::html == aLocal || nsGkAtoms::head == aLocal || nsGkAtoms::body == aLocal)) { return false; } return !sElementsHTML->GetEntry(aLocal); } if (aNamespace == kNameSpaceID_SVG) { if (mCidEmbedsOnly || mDropMedia) { // Sanitizing CSS-based URL references inside SVG presentational // attributes is not supported, so flattening for cid: embed case. return true; } return !sElementsSVG->GetEntry(aLocal); } if (aNamespace == kNameSpaceID_MathML) { return !sElementsMathML->GetEntry(aLocal); } return true; } bool nsTreeSanitizer::IsURL(nsStaticAtom** const* aURLs, nsAtom* aLocalName) { nsStaticAtom** atomPtrPtr; while ((atomPtrPtr = *aURLs)) { if (*atomPtrPtr == aLocalName) { return true; } ++aURLs; } return false; } bool nsTreeSanitizer::MustPrune(int32_t aNamespace, nsAtom* aLocal, mozilla::dom::Element* aElement) { // 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) { return true; } if (aNamespace == kNameSpaceID_XHTML) { if (nsGkAtoms::title == aLocal && !mFullDocument) { // emulate the quirks of the old parser return true; } if (mDropForms && (nsGkAtoms::select == aLocal || nsGkAtoms::button == aLocal || nsGkAtoms::datalist == aLocal)) { return true; } if (mDropMedia && (nsGkAtoms::img == aLocal || nsGkAtoms::video == aLocal || nsGkAtoms::audio == aLocal || nsGkAtoms::source == aLocal)) { return true; } if (nsGkAtoms::meta == aLocal && (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::charset) || aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv))) { // Throw away charset declarations even if they also have microdata // which they can't validly have. return true; } if (((!mFullDocument && nsGkAtoms::meta == aLocal) || nsGkAtoms::link == aLocal) && !(aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::itemprop) || aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::itemscope))) { // emulate old behavior for non-Microdata and presumably // in . and are whitelisted in order to avoid // corrupting Microdata when they appear in . Note that // SanitizeAttributes() will remove the rel attribute from and // the name attribute from . return true; } } if (mAllowStyles) { if (nsGkAtoms::style == aLocal && !(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG)) { return true; } return false; } if (nsGkAtoms::style == aLocal) { return true; } return false; } bool nsTreeSanitizer::SanitizeStyleDeclaration(DeclarationBlock* aDeclaration) { return aDeclaration->RemovePropertyByID(eCSSProperty__moz_binding); } bool nsTreeSanitizer::SanitizeStyleSheet(const nsAString& aOriginal, nsAString& aSanitized, nsIDocument* aDocument, nsIURI* aBaseURI) { nsresult rv; aSanitized.Truncate(); // aSanitized will hold the permitted CSS text. // -moz-binding is blacklisted. bool didSanitize = false; // Create a sheet to hold the parsed CSS RefPtr sheet; if (aDocument->IsStyledByServo()) { sheet = new ServoStyleSheet(mozilla::css::eAuthorSheetFeatures, CORS_NONE, aDocument->GetReferrerPolicy(), SRIMetadata()); } else { #ifdef MOZ_OLD_STYLE sheet = new CSSStyleSheet(mozilla::css::eAuthorSheetFeatures, CORS_NONE, aDocument->GetReferrerPolicy()); #else MOZ_CRASH("old style system disabled"); #endif } sheet->SetURIs(aDocument->GetDocumentURI(), nullptr, aBaseURI); sheet->SetPrincipal(aDocument->NodePrincipal()); if (aDocument->IsStyledByServo()) { rv = sheet->AsServo()->ParseSheet( aDocument->CSSLoader(), NS_ConvertUTF16toUTF8(aOriginal), aDocument->GetDocumentURI(), aBaseURI, aDocument->NodePrincipal(), 0, aDocument->GetCompatibilityMode()); } else { #ifdef MOZ_OLD_STYLE // Create the CSS parser, and parse the CSS text. nsCSSParser parser(nullptr, sheet->AsGecko()); rv = parser.ParseSheet(aOriginal, aDocument->GetDocumentURI(), aBaseURI, aDocument->NodePrincipal(), 0); #else MOZ_CRASH("old style system disabled"); #endif } NS_ENSURE_SUCCESS(rv, true); // Mark the sheet as complete. MOZ_ASSERT(!sheet->HasForcedUniqueInner(), "should not get a forced unique inner during parsing"); sheet->SetComplete(); // Loop through all the rules found in the CSS text ErrorResult err; RefPtr rules = sheet->GetCssRules(*nsContentUtils::GetSystemPrincipal(), err); err.SuppressException(); if (!rules) { return true; } uint32_t ruleCount = rules->Length(); for (uint32_t i = 0; i < ruleCount; ++i) { mozilla::css::Rule* rule = rules->Item(i); if (!rule) continue; switch (rule->GetType()) { default: didSanitize = true; // Ignore these rule types. break; case mozilla::css::Rule::NAMESPACE_RULE: case mozilla::css::Rule::FONT_FACE_RULE: { // Append @namespace and @font-face rules verbatim. nsAutoString cssText; rule->GetCssText(cssText); aSanitized.Append(cssText); break; } case mozilla::css::Rule::STYLE_RULE: { // For style rules, we will just look for and remove the // -moz-binding properties. auto styleRule = static_cast(rule); DeclarationBlock* styleDecl = styleRule->GetDeclarationBlock(); MOZ_ASSERT(styleDecl); if (SanitizeStyleDeclaration(styleDecl)) { didSanitize = true; } nsAutoString decl; styleRule->GetCssText(decl); aSanitized.Append(decl); } } } if (didSanitize && mLogRemovals) { LogMessage("Removed some rules and/or properties from stylesheet.", aDocument); } return didSanitize; } void nsTreeSanitizer::SanitizeAttributes(mozilla::dom::Element* aElement, nsTHashtable>* aAllowed, nsStaticAtom** const* aURLs, bool aAllowXLink, bool aAllowStyle, bool aAllowDangerousSrc) { uint32_t ac = aElement->GetAttrCount(); for (int32_t i = ac - 1; i >= 0; --i) { const nsAttrName* attrName = aElement->GetAttrNameAt(i); int32_t attrNs = attrName->NamespaceID(); RefPtr attrLocal = attrName->LocalName(); if (kNameSpaceID_None == attrNs) { if (aAllowStyle && nsGkAtoms::style == attrLocal) { RefPtr decl; nsAutoString value; aElement->GetAttr(attrNs, attrLocal, value); nsIDocument* document = aElement->OwnerDoc(); if (document->IsStyledByServo()) { RefPtr urlExtra(aElement->GetURLDataForStyleAttr()); decl = ServoDeclarationBlock::FromCssText( value, urlExtra, document->GetCompatibilityMode(), document->CSSLoader()); } else { #ifdef MOZ_OLD_STYLE // Pass the CSS Loader object to the parser, to allow parser error // reports to include the outer window ID. nsCSSParser parser(document->CSSLoader()); decl = parser.ParseStyleAttribute(value, document->GetDocumentURI(), aElement->GetBaseURIForStyleAttr(), document->NodePrincipal()); #else MOZ_CRASH("old style system disabled"); #endif } if (decl) { if (SanitizeStyleDeclaration(decl)) { nsAutoString cleanValue; decl->ToString(cleanValue); aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style, cleanValue, false); if (mLogRemovals) { LogMessage("Removed -moz-binding styling from element style attribute.", aElement->OwnerDoc(), aElement); } } } continue; } if (aAllowDangerousSrc && nsGkAtoms::src == attrLocal) { continue; } if (IsURL(aURLs, attrLocal)) { if (SanitizeURL(aElement, attrNs, 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; } // 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 == sAttributesHTML) && // element is HTML sPresAttributesHTML->GetEntry(attrLocal)) { continue; } if (aAllowed->GetEntry(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 (fragment case) and (all cases) to avoid // document-wide metadata or styling overrides with non-conforming // or // continue; } const char16_t* localStr = attrLocal->GetUTF16String(); // Allow underscore to cater to the MCE editor library. // Allow data-* on SVG and MathML, too, as a forward-compat measure. if (*localStr == '_' || (attrLocal->GetLength() > 5 && localStr[0] == 'd' && localStr[1] == 'a' && localStr[2] == 't' && localStr[3] == 'a' && localStr[4] == '-')) { continue; } // else not allowed } else if (kNameSpaceID_XML == attrNs) { if (nsGkAtoms::base == attrLocal) { if (SanitizeURL(aElement, attrNs, 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 (nsGkAtoms::lang == attrLocal || nsGkAtoms::space == attrLocal) { continue; } // else not allowed } else if (aAllowXLink && kNameSpaceID_XLink == attrNs) { if (nsGkAtoms::href == attrLocal) { if (SanitizeURL(aElement, attrNs, 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 (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, EmptyString(), false); } } bool nsTreeSanitizer::SanitizeURL(mozilla::dom::Element* aElement, int32_t aNamespace, nsAtom* aLocalName) { nsAutoString value; aElement->GetAttr(aNamespace, aLocalName, value); // Get value and remove mandatory quotes static const char* kWhitespace = "\n\r\t\b"; const nsAString& v = nsContentUtils::TrimCharsInSet(kWhitespace, value); // Fragment-only url cannot be harmful. if (!v.IsEmpty() && v.First() == u'#') { return false; } nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); uint32_t flags = nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL; nsCOMPtr baseURI = aElement->GetBaseURI(); nsCOMPtr attrURI; nsresult rv = NS_NewURI(getter_AddRefs(attrURI), v, nullptr, baseURI); 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; } } else if (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); } } else { rv = secMan->CheckLoadURIWithPrincipal(sNullPrincipal, attrURI, flags); } } if (NS_FAILED(rv)) { aElement->UnsetAttr(aNamespace, aLocalName, false); if (mLogRemovals) { LogMessage("Removed unsafe URI from element attribute.", aElement->OwnerDoc(), aElement, aLocalName); } return true; } return false; } void nsTreeSanitizer::Sanitize(nsIContent* 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. NS_PRECONDITION(aFragment->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT), "Argument was not DOM fragment."); NS_PRECONDITION(!aFragment->IsInUncomposedDoc(), "The fragment is in doc?"); mFullDocument = false; SanitizeChildren(aFragment); } void nsTreeSanitizer::Sanitize(nsIDocument* 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 NS_PRECONDITION(!aDocument->GetContainer(), "The document is in a shell."); RefPtr root = aDocument->GetRootElement(); NS_PRECONDITION(root->IsHTMLElement(nsGkAtoms::html), "Not HTML root."); #endif mFullDocument = true; SanitizeChildren(aDocument); } void nsTreeSanitizer::SanitizeChildren(nsINode* aRoot) { nsIContent* node = aRoot->GetFirstChild(); while (node) { if (node->IsElement()) { mozilla::dom::Element* elt = node->AsElement(); mozilla::dom::NodeInfo* nodeInfo = node->NodeInfo(); nsAtom* localName = nodeInfo->NameAtom(); int32_t ns = nodeInfo->NamespaceID(); if (MustPrune(ns, localName, elt)) { if (mLogRemovals) { LogMessage("Removing unsafe node.", elt->OwnerDoc(), elt); } RemoveAllAttributes(elt); nsIContent* descendant = node; while ((descendant = descendant->GetNextNode(node))) { if (descendant->IsElement()) { RemoveAllAttributes(descendant->AsElement()); } } nsIContent* next = node->GetNextNonChildNode(aRoot); node->RemoveFromParent(); node = next; continue; } if (nsGkAtoms::style == localName) { // If styles aren't allowed, style elements got pruned above. Even // if styles are allowed, non-HTML, non-SVG style elements got pruned // above. NS_ASSERTION(ns == kNameSpaceID_XHTML || ns == kNameSpaceID_SVG, "Should have only HTML or SVG here!"); nsAutoString styleText; nsContentUtils::GetNodeTextContent(node, false, styleText); nsAutoString sanitizedStyle; nsCOMPtr baseURI = node->GetBaseURI(); if (SanitizeStyleSheet(styleText, sanitizedStyle, aRoot->OwnerDoc(), baseURI)) { nsContentUtils::SetNodeTextContent(node, sanitizedStyle, true); } else { // If the node had non-text child nodes, this operation zaps those. //XXXgijs: if we're logging, we should theoretically report about // this, but this way of removing those items doesn't allow for that // to happen. Seems less likely to be a problem for actual chrome // consumers though. nsContentUtils::SetNodeTextContent(node, styleText, true); } if (ns == kNameSpaceID_XHTML) { SanitizeAttributes(elt, sAttributesHTML, kURLAttributesHTML, false, mAllowStyles, false); } else { SanitizeAttributes(elt, sAttributesSVG, kURLAttributesSVG, true, mAllowStyles, false); } node = node->GetNextNonChildNode(aRoot); continue; } if (MustFlatten(ns, localName)) { if (mLogRemovals) { LogMessage("Flattening unsafe node (descendants are preserved).", elt->OwnerDoc(), elt); } RemoveAllAttributes(elt); nsCOMPtr next = node->GetNextNode(aRoot); nsCOMPtr parent = node->GetParent(); nsCOMPtr child; // Must keep the child alive during move ErrorResult rv; while ((child = node->GetFirstChild())) { nsCOMPtr refNode = node; parent->InsertBefore(*child, refNode, rv); if (rv.Failed()) { break; } } node->RemoveFromParent(); node = next; continue; } NS_ASSERTION(ns == kNameSpaceID_XHTML || ns == kNameSpaceID_SVG || ns == kNameSpaceID_MathML, "Should have only HTML, MathML or SVG here!"); if (ns == kNameSpaceID_XHTML) { SanitizeAttributes(elt, sAttributesHTML, kURLAttributesHTML, false, mAllowStyles, (nsGkAtoms::img == localName) && !mCidEmbedsOnly); } else if (ns == kNameSpaceID_SVG) { SanitizeAttributes(elt, sAttributesSVG, kURLAttributesSVG, true, mAllowStyles, false); } else { SanitizeAttributes(elt, sAttributesMathML, kURLAttributesMathML, true, false, false); } node = node->GetNextNode(aRoot); continue; } NS_ASSERTION(!node->GetFirstChild(), "How come non-element node had kids?"); nsIContent* next = node->GetNextNonChildNode(aRoot); if (!mAllowComments && node->IsNodeOfType(nsINode::eCOMMENT)) { node->RemoveFromParent(); } node = next; } } void nsTreeSanitizer::RemoveAllAttributes(Element* aElement) { const nsAttrName* attrName; while ((attrName = aElement->GetAttrNameAt(0))) { int32_t attrNs = attrName->NamespaceID(); RefPtr attrLocal = attrName->LocalName(); aElement->UnsetAttr(attrNs, attrLocal, false); } } void nsTreeSanitizer::LogMessage(const char* aMessage, nsIDocument* aDoc, Element* aElement, nsAtom* aAttr) { if (mLogRemovals) { nsAutoString msg; msg.Assign(NS_ConvertASCIItoUTF16(aMessage)); if (aElement) { msg.Append(NS_LITERAL_STRING(" Element: ") + aElement->LocalName() + NS_LITERAL_STRING(".")); } if (aAttr) { msg.Append(NS_LITERAL_STRING(" Attribute: ") + nsDependentAtomString(aAttr) + NS_LITERAL_STRING(".")); } nsContentUtils::ReportToConsoleNonLocalized( msg, nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"), aDoc); } } void nsTreeSanitizer::InitializeStatics() { NS_PRECONDITION(!sElementsHTML, "Initializing a second time."); sElementsHTML = new nsTHashtable>(ArrayLength(kElementsHTML)); for (uint32_t i = 0; kElementsHTML[i]; i++) { sElementsHTML->PutEntry(*kElementsHTML[i]); } sAttributesHTML = new nsTHashtable>(ArrayLength(kAttributesHTML)); for (uint32_t i = 0; kAttributesHTML[i]; i++) { sAttributesHTML->PutEntry(*kAttributesHTML[i]); } sPresAttributesHTML = new nsTHashtable>(ArrayLength(kPresAttributesHTML)); for (uint32_t i = 0; kPresAttributesHTML[i]; i++) { sPresAttributesHTML->PutEntry(*kPresAttributesHTML[i]); } sElementsSVG = new nsTHashtable>(ArrayLength(kElementsSVG)); for (uint32_t i = 0; kElementsSVG[i]; i++) { sElementsSVG->PutEntry(*kElementsSVG[i]); } sAttributesSVG = new nsTHashtable>(ArrayLength(kAttributesSVG)); for (uint32_t i = 0; kAttributesSVG[i]; i++) { sAttributesSVG->PutEntry(*kAttributesSVG[i]); } sElementsMathML = new nsTHashtable>(ArrayLength(kElementsMathML)); for (uint32_t i = 0; kElementsMathML[i]; i++) { sElementsMathML->PutEntry(*kElementsMathML[i]); } sAttributesMathML = new nsTHashtable>(ArrayLength(kAttributesMathML)); for (uint32_t i = 0; kAttributesMathML[i]; i++) { sAttributesMathML->PutEntry(*kAttributesMathML[i]); } nsCOMPtr principal = NullPrincipal::Create(); principal.forget(&sNullPrincipal); } void nsTreeSanitizer::ReleaseStatics() { delete sElementsHTML; sElementsHTML = nullptr; delete sAttributesHTML; sAttributesHTML = nullptr; delete sPresAttributesHTML; sPresAttributesHTML = nullptr; delete sElementsSVG; sElementsSVG = nullptr; delete sAttributesSVG; sAttributesSVG = nullptr; delete sElementsMathML; sElementsMathML = nullptr; delete sAttributesMathML; sAttributesMathML = nullptr; NS_IF_RELEASE(sNullPrincipal); }