File: DOMSecurityMonitor.cpp

package info (click to toggle)
firefox 147.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,683,324 kB
  • sloc: cpp: 7,607,156; javascript: 6,532,492; ansic: 3,775,158; python: 1,415,368; xml: 634,556; asm: 438,949; java: 186,241; sh: 62,751; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (129 lines) | stat: -rw-r--r-- 5,118 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* -*- 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 "DOMSecurityMonitor.h"

#include "mozilla/BasePrincipal.h"
#include "mozilla/StaticPrefs_dom.h"
#include "nsContentUtils.h"
#include "nsIChannel.h"
#include "nsILoadInfo.h"
#include "nsIPrincipal.h"
#include "nsIURI.h"
#include "nsJSUtils.h"
#include "xpcpublic.h"

/* static */
void DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(
    nsIPrincipal* aPrincipal, const nsAString& aFragment) {
  // if the fragment parser (e.g. innerHTML()) is not called in chrome: code
  // or any of our about: pages, then there is nothing to do here.
  if (!aPrincipal->IsSystemPrincipal() && !aPrincipal->SchemeIs("about")) {
    return;
  }

  // check if the fragment is empty, if so, we can return early.
  if (aFragment.IsEmpty()) {
    return;
  }

  // check if there is a JS caller, if not, then we can can return early here
  // because we only care about calls to the fragment parser (e.g. innerHTML)
  // originating from JS code.
  auto loc = mozilla::JSCallingLocation::Get();
  if (!loc) {
    return;
  }

  // check if we should skip assertion. Please only ever set this pref to
  // true if really needed for testing purposes.
  if (mozilla::StaticPrefs::dom_security_skip_html_fragment_assertion()) {
    return;
  }

  /*
   * WARNING: Do not add any new entries to the htmlFragmentAllowlist
   * without proper review from a dom:security peer!
   */
  static nsLiteralCString htmlFragmentAllowlist[] = {
      "chrome://global/content/elements/marquee.js"_ns,
      nsLiteralCString("chrome://devtools/content/shared/sourceeditor/"
                       "codemirror/codemirror.bundle.js"),
      nsLiteralCString(
          "resource://newtab/data/content/activity-stream.bundle.js"),
      nsLiteralCString("resource://devtools/client/debugger/src/components/"
                       "Editor/Breakpoint.js"),
      nsLiteralCString("resource://devtools/client/debugger/src/components/"
                       "Editor/ColumnBreakpoint.js"),
      nsLiteralCString(
          "resource://devtools/client/shared/vendor/fluent-react.js"),
      "resource://devtools/client/shared/vendor/react-dom.mjs"_ns,
      nsLiteralCString(
          "resource://devtools/client/shared/vendor/react-dom-dev.mjs"),
      nsLiteralCString(
          "resource://devtools/client/shared/widgets/FilterWidget.js"),
      nsLiteralCString("resource://devtools/client/shared/widgets/tooltip/"
                       "inactive-css-tooltip-helper.js"),
      "resource://devtools/client/shared/widgets/Spectrum.js"_ns,
      "resource://gre/modules/narrate/VoiceSelect.sys.mjs"_ns,
      "chrome://global/content/vendor/react-dom.js"_ns,
      // ------------------------------------------------------------------
      // test pages
      // ------------------------------------------------------------------
      "chrome://mochikit/content/browser-harness.xhtml"_ns,
      "chrome://mochikit/content/harness.xhtml"_ns,
      "chrome://mochikit/content/tests/"_ns,
      "chrome://mochitests/content/"_ns,
      "chrome://reftest/content/"_ns,
  };

  for (const nsLiteralCString& allowlistEntry : htmlFragmentAllowlist) {
    if (StringBeginsWith(loc.FileName(), allowlistEntry)) {
      return;
    }
  }

  nsAutoCString uriSpec;
  aPrincipal->GetAsciiSpec(uriSpec);

  // Ideally we should not call the fragment parser (e.g. innerHTML()) in
  // chrome: code or any of our about: pages. If you hit that assertion,
  // please do *not* add your filename to the allowlist above, but rather
  // refactor your code.
  fprintf(stderr,
          "Do not call the fragment parser (e.g innerHTML()) in chrome code "
          "or in about: pages, (uri: %s), (caller: %s, line: %d, col: %d), "
          "(fragment: %s)",
          uriSpec.get(), loc.FileName().get(), loc.mLine, loc.mColumn,
          NS_ConvertUTF16toUTF8(aFragment).get());

  xpc_DumpJSStack(true, true, false);
  MOZ_ASSERT(false);
}

/* static */
void DOMSecurityMonitor::AuditUseOfJavaScriptURI(nsIChannel* aChannel) {
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
  nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->GetLoadingPrincipal();

  // We only ever have no loadingPrincipal in case of a new top-level load.
  // The purpose of this assertion is to make sure we do not allow loading
  // javascript: URIs in system privileged contexts. Hence there is nothing
  // to do here in case there is no loadingPrincipal.
  if (!loadingPrincipal) {
    return;
  }

  // if the javascript: URI is not loaded by a system privileged context
  // or an about: page, there there is nothing to do here.
  if (!loadingPrincipal->IsSystemPrincipal() &&
      !loadingPrincipal->SchemeIs("about")) {
    return;
  }

  MOZ_ASSERT(false,
             "Do not use javascript: URIs in chrome code or in about: pages");
}