DOMSecurityMonitor.cpp (5118B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "DOMSecurityMonitor.h" 8 9 #include "mozilla/BasePrincipal.h" 10 #include "mozilla/StaticPrefs_dom.h" 11 #include "nsContentUtils.h" 12 #include "nsIChannel.h" 13 #include "nsILoadInfo.h" 14 #include "nsIPrincipal.h" 15 #include "nsIURI.h" 16 #include "nsJSUtils.h" 17 #include "xpcpublic.h" 18 19 /* static */ 20 void DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments( 21 nsIPrincipal* aPrincipal, const nsAString& aFragment) { 22 // if the fragment parser (e.g. innerHTML()) is not called in chrome: code 23 // or any of our about: pages, then there is nothing to do here. 24 if (!aPrincipal->IsSystemPrincipal() && !aPrincipal->SchemeIs("about")) { 25 return; 26 } 27 28 // check if the fragment is empty, if so, we can return early. 29 if (aFragment.IsEmpty()) { 30 return; 31 } 32 33 // check if there is a JS caller, if not, then we can can return early here 34 // because we only care about calls to the fragment parser (e.g. innerHTML) 35 // originating from JS code. 36 auto loc = mozilla::JSCallingLocation::Get(); 37 if (!loc) { 38 return; 39 } 40 41 // check if we should skip assertion. Please only ever set this pref to 42 // true if really needed for testing purposes. 43 if (mozilla::StaticPrefs::dom_security_skip_html_fragment_assertion()) { 44 return; 45 } 46 47 /* 48 * WARNING: Do not add any new entries to the htmlFragmentAllowlist 49 * without proper review from a dom:security peer! 50 */ 51 static nsLiteralCString htmlFragmentAllowlist[] = { 52 "chrome://global/content/elements/marquee.js"_ns, 53 nsLiteralCString("chrome://devtools/content/shared/sourceeditor/" 54 "codemirror/codemirror.bundle.js"), 55 nsLiteralCString( 56 "resource://newtab/data/content/activity-stream.bundle.js"), 57 nsLiteralCString("resource://devtools/client/debugger/src/components/" 58 "Editor/Breakpoint.js"), 59 nsLiteralCString("resource://devtools/client/debugger/src/components/" 60 "Editor/ColumnBreakpoint.js"), 61 nsLiteralCString( 62 "resource://devtools/client/shared/vendor/fluent-react.js"), 63 "resource://devtools/client/shared/vendor/react-dom.mjs"_ns, 64 nsLiteralCString( 65 "resource://devtools/client/shared/vendor/react-dom-dev.mjs"), 66 nsLiteralCString( 67 "resource://devtools/client/shared/widgets/FilterWidget.js"), 68 nsLiteralCString("resource://devtools/client/shared/widgets/tooltip/" 69 "inactive-css-tooltip-helper.js"), 70 "resource://devtools/client/shared/widgets/Spectrum.js"_ns, 71 "resource://gre/modules/narrate/VoiceSelect.sys.mjs"_ns, 72 "chrome://global/content/vendor/react-dom.js"_ns, 73 // ------------------------------------------------------------------ 74 // test pages 75 // ------------------------------------------------------------------ 76 "chrome://mochikit/content/browser-harness.xhtml"_ns, 77 "chrome://mochikit/content/harness.xhtml"_ns, 78 "chrome://mochikit/content/tests/"_ns, 79 "chrome://mochitests/content/"_ns, 80 "chrome://reftest/content/"_ns, 81 }; 82 83 for (const nsLiteralCString& allowlistEntry : htmlFragmentAllowlist) { 84 if (StringBeginsWith(loc.FileName(), allowlistEntry)) { 85 return; 86 } 87 } 88 89 nsAutoCString uriSpec; 90 aPrincipal->GetAsciiSpec(uriSpec); 91 92 // Ideally we should not call the fragment parser (e.g. innerHTML()) in 93 // chrome: code or any of our about: pages. If you hit that assertion, 94 // please do *not* add your filename to the allowlist above, but rather 95 // refactor your code. 96 fprintf(stderr, 97 "Do not call the fragment parser (e.g innerHTML()) in chrome code " 98 "or in about: pages, (uri: %s), (caller: %s, line: %d, col: %d), " 99 "(fragment: %s)", 100 uriSpec.get(), loc.FileName().get(), loc.mLine, loc.mColumn, 101 NS_ConvertUTF16toUTF8(aFragment).get()); 102 103 xpc_DumpJSStack(true, true, false); 104 MOZ_ASSERT(false); 105 } 106 107 /* static */ 108 void DOMSecurityMonitor::AuditUseOfJavaScriptURI(nsIChannel* aChannel) { 109 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 110 nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->GetLoadingPrincipal(); 111 112 // We only ever have no loadingPrincipal in case of a new top-level load. 113 // The purpose of this assertion is to make sure we do not allow loading 114 // javascript: URIs in system privileged contexts. Hence there is nothing 115 // to do here in case there is no loadingPrincipal. 116 if (!loadingPrincipal) { 117 return; 118 } 119 120 // if the javascript: URI is not loaded by a system privileged context 121 // or an about: page, there there is nothing to do here. 122 if (!loadingPrincipal->IsSystemPrincipal() && 123 !loadingPrincipal->SchemeIs("about")) { 124 return; 125 } 126 127 MOZ_ASSERT(false, 128 "Do not use javascript: URIs in chrome code or in about: pages"); 129 }