Browse Source

import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1174323 - Disable screenClientXYConst subtest of pointerlock test on OS X. rs=KWierso (2d0db6d1b)
- Bug 992096 - Implement Sub Resource Integrity [1/2]. r=baku,r=ckerschb (c30671ac0)
- Bug 992096 - Implement Sub Resource Integrity [2/2]. r=ckerschb (0afc64d88)
- Bug 1091883 - Added test, this is fixed by a fix to bug 1113438. r=sstamm CLOSED TREE (fd9a64b43)
- Bug 1196740 - Consider redirects when looking for SRI-eligibility. r=ckerschb (5c749cdc9)
- Bug 1202015 - Better document the SRI strings for translators. r=ckerschb (a7860e0fb)
- Bug 1202027 - Make SRI require CORS loads for cross-origin resources. r=ckerschb (ea451323d)
- bit of Bug 1202902 - Mass replace toplevel 'let' with 'var' (a6e8a587d)
- Bug 1208629 - Properly support data: and blob: URIs with an integrity atribute. r=ckerschb (6b2018fe4)
- Bug 1140129 - Don't clear tab title when location changes (r=Mossop) (ca1945ba8)
- Bug 1073462: Send synthetic property with Content:LocationChange message. r=felipe (1aa418acf)
- bug 1165017 - annotate content process URL on location change. r=mconley (cdca4fa75)
- Bug 1157561 - Add webRequest-like API to Firefox (r=Mossop) (546a57822)
- Bug 1163861 - Include windowID in all WebRequest notifications (r=Mossop) (c140af560)
- Bug 1171248 - Add MatchPattern support to WebRequest module (r=Mossop) (b09a05658)
pull/8/head
roytam1 10 months ago
parent
commit
ebd6e6dc19
  1. 1
      AUTHORS
  2. 1
      dom/base/moz.build
  3. 23
      dom/base/nsContentSink.cpp
  4. 5
      dom/base/nsDocument.cpp
  5. 3
      dom/base/nsDocument.h
  6. 1
      dom/base/nsGkAtomList.h
  7. 7
      dom/base/nsIDocument.h
  8. 54
      dom/base/nsScriptLoader.cpp
  9. 8
      dom/base/nsScriptLoader.h
  10. 10
      dom/base/nsStyleLinkElement.cpp
  11. 13
      dom/base/test/file_bug1091883_frame.html
  12. 6
      dom/base/test/file_bug1091883_subframe.html
  13. 13
      dom/base/test/file_bug1091883_target.html
  14. 4
      dom/base/test/mochitest.ini
  15. 89
      dom/base/test/test_bug1091883.html
  16. 5
      dom/html/HTMLLinkElement.cpp
  17. 8
      dom/html/HTMLLinkElement.h
  18. 14
      dom/html/HTMLScriptElement.cpp
  19. 8
      dom/html/HTMLScriptElement.h
  20. 16
      dom/locales/en-US/chrome/security/security.properties
  21. 349
      dom/security/SRICheck.cpp
  22. 60
      dom/security/SRICheck.h
  23. 172
      dom/security/SRIMetadata.cpp
  24. 74
      dom/security/SRIMetadata.h
  25. 4
      dom/security/moz.build
  26. 1
      dom/security/test/moz.build
  27. 135
      dom/security/test/sri/iframe_script_crossdomain.html
  28. 249
      dom/security/test/sri/iframe_script_sameorigin.html
  29. 74
      dom/security/test/sri/iframe_sri_disabled.html
  30. 100
      dom/security/test/sri/iframe_style_crossdomain.html
  31. 124
      dom/security/test/sri/iframe_style_sameorigin.html
  32. 36
      dom/security/test/sri/mochitest.ini
  33. 1
      dom/security/test/sri/script.js
  34. 1
      dom/security/test/sri/script.js^headers^
  35. 1
      dom/security/test/sri/script_301.js
  36. 2
      dom/security/test/sri/script_301.js^headers^
  37. 1
      dom/security/test/sri/script_302.js
  38. 2
      dom/security/test/sri/script_302.js^headers^
  39. 1
      dom/security/test/sri/script_401.js
  40. 2
      dom/security/test/sri/script_401.js^headers^
  41. 4
      dom/security/test/sri/script_crossdomain1.js
  42. 1
      dom/security/test/sri/script_crossdomain1.js^headers^
  43. 5
      dom/security/test/sri/script_crossdomain2.js
  44. 1
      dom/security/test/sri/script_crossdomain3.js
  45. 1
      dom/security/test/sri/script_crossdomain3.js^headers^
  46. 1
      dom/security/test/sri/script_crossdomain4.js
  47. 1
      dom/security/test/sri/script_crossdomain4.js^headers^
  48. 1
      dom/security/test/sri/script_crossdomain5.js
  49. 1
      dom/security/test/sri/script_crossdomain5.js^headers^
  50. 3
      dom/security/test/sri/style1.css
  51. 1
      dom/security/test/sri/style1.css^headers^
  52. 1
      dom/security/test/sri/style2.css
  53. 3
      dom/security/test/sri/style3.css
  54. 3
      dom/security/test/sri/style_301.css
  55. 2
      dom/security/test/sri/style_301.css^headers^
  56. 18
      dom/security/test/sri/test_script_crossdomain.html
  57. 18
      dom/security/test/sri/test_script_sameorigin.html
  58. 18
      dom/security/test/sri/test_sri_disabled.html
  59. 18
      dom/security/test/sri/test_style_crossdomain.html
  60. 18
      dom/security/test/sri/test_style_sameorigin.html
  61. 22
      dom/tests/mochitest/pointerlock/test_pointerlock-api.html
  62. 5
      dom/webidl/HTMLLinkElement.webidl
  63. 5
      dom/webidl/HTMLScriptElement.webidl
  64. 26
      layout/style/CSSStyleSheet.cpp
  65. 10
      layout/style/CSSStyleSheet.h
  66. 60
      layout/style/Loader.cpp
  67. 8
      layout/style/Loader.h
  68. 3
      modules/libpref/init/all.js
  69. 6
      parser/html/nsHtml5SpeculativeLoad.cpp
  70. 12
      parser/html/nsHtml5SpeculativeLoad.h
  71. 16
      parser/html/nsHtml5TreeBuilderCppSupplement.h
  72. 8
      parser/html/nsHtml5TreeOpExecutor.cpp
  73. 4
      parser/html/nsHtml5TreeOpExecutor.h
  74. 4
      toolkit/components/lz4/lz4_internal.js
  75. 38
      toolkit/content/browser-child.js
  76. 13
      toolkit/content/tests/browser/browser.ini
  77. 16
      toolkit/content/tests/browser/browser_contentTitle.js
  78. 139
      toolkit/content/tests/browser/browser_content_url_annotation.js
  79. 72
      toolkit/content/tests/browser/browser_isSynthetic.js
  80. BIN
      toolkit/content/tests/browser/empty.png
  81. 14
      toolkit/content/tests/browser/file_contentTitle.html
  82. 13
      toolkit/content/tests/browser/file_redirect.html
  83. 15
      toolkit/content/tests/browser/file_redirect_to.html
  84. 4
      toolkit/content/widgets/remote-browser.xml
  85. 1
      toolkit/devtools/webconsole/webconsole.js
  86. 3
      toolkit/modules/RemoteWebProgress.jsm
  87. 120
      toolkit/modules/addons/MatchPattern.jsm
  88. 450
      toolkit/modules/addons/WebRequest.jsm
  89. 42
      toolkit/modules/addons/WebRequestCommon.jsm
  90. 176
      toolkit/modules/addons/WebRequestContent.js
  91. 4
      toolkit/modules/moz.build
  92. 3
      toolkit/modules/tests/browser/browser.ini
  93. 188
      toolkit/modules/tests/browser/browser_WebRequest.js
  94. 89
      toolkit/modules/tests/browser/browser_WebRequest_cookies.js
  95. 118
      toolkit/modules/tests/browser/browser_WebRequest_filtering.js
  96. 23
      toolkit/modules/tests/browser/head.js
  97. 92
      toolkit/modules/tests/xpcshell/test_MatchPattern.js
  98. 1
      toolkit/modules/tests/xpcshell/xpcshell.ini
  99. 5
      xpcom/base/ErrorList.h

1
AUTHORS

@ -364,6 +364,7 @@ Florian Scholz <elchi3@elchi3.de>
<flying@dom.natm.ru>
France Telecom Research and Development
Franck
Francois Marier <francois@fmarier.org>
Frank Tang <ftang@netscape.com>
Frank Yan <fyan@mozilla.com>
Franky Braem

1
dom/base/moz.build

@ -429,6 +429,7 @@ LOCAL_INCLUDES += [
'/layout/svg',
'/layout/xul',
'/netwerk/base',
'/security/manager/ssl',
'/widget',
'/xpcom/ds',
]

23
dom/base/nsContentSink.cpp

@ -51,6 +51,16 @@
#include "nsParserConstants.h"
#include "nsSandboxFlags.h"
static PRLogModuleInfo*
GetSriLog()
{
static PRLogModuleInfo *gSriPRLog;
if (!gSriPRLog) {
gSriPRLog = PR_NewLogModule("SRI");
}
return gSriPRLog;
}
using namespace mozilla;
PRLogModuleInfo* gContentSinkLogModuleInfo;
@ -755,12 +765,23 @@ nsContentSink::ProcessStyleLink(nsIContent* aElement,
aElement->NodeType() == nsIDOMNode::PROCESSING_INSTRUCTION_NODE,
"We only expect processing instructions here");
nsAutoString integrity;
if (aElement) {
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
}
if (!integrity.IsEmpty()) {
MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
("nsContentSink::ProcessStyleLink, integrity=%s",
NS_ConvertUTF16toUTF8(integrity).get()));
}
// If this is a fragment parser, we don't want to observe.
// We don't support CORS for processing instructions
bool isAlternate;
rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, aAlternate,
CORS_NONE, mDocument->GetReferrerPolicy(),
mRunsToCompletion ? nullptr : this, &isAlternate);
integrity, mRunsToCompletion ? nullptr : this,
&isAlternate);
NS_ENSURE_SUCCESS(rv, rv);
if (!isAlternate && !mRunsToCompletion) {

5
dom/base/nsDocument.cpp

@ -9912,7 +9912,8 @@ NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
void
nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
const nsAString& aCrossOriginAttr,
const ReferrerPolicy aReferrerPolicy)
const ReferrerPolicy aReferrerPolicy,
const nsAString& aIntegrity)
{
// The CSSLoader will retain this object after we return.
nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
@ -9922,7 +9923,7 @@ nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
NS_LossyConvertUTF16toASCII(charset),
obs,
Element::StringToCORSMode(aCrossOriginAttr),
aReferrerPolicy);
aReferrerPolicy, aIntegrity);
}
nsresult

3
dom/base/nsDocument.h

@ -1126,7 +1126,8 @@ public:
virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
const nsAString& aCrossOriginAttr,
ReferrerPolicy aReferrerPolicy) override;
ReferrerPolicy aReferrerPolicy,
const nsAString& aIntegrity) override;
virtual nsresult LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
mozilla::CSSStyleSheet** sheet) override;

1
dom/base/nsGkAtomList.h

@ -488,6 +488,7 @@ GK_ATOM(instanceOf, "instanceOf")
GK_ATOM(int32, "int32")
GK_ATOM(int64, "int64")
GK_ATOM(integer, "integer")
GK_ATOM(integrity, "integrity")
GK_ATOM(intersection, "intersection")
GK_ATOM(is, "is")
GK_ATOM(iscontainer, "iscontainer")

7
dom/base/nsIDocument.h

@ -158,8 +158,8 @@ struct FullScreenOptions {
} // namespace mozilla
#define NS_IDOCUMENT_IID \
{ 0x21bbd52a, 0xc2d2, 0x4b2f, \
{ 0xbc, 0x6c, 0xc9, 0x52, 0xbe, 0x23, 0x6b, 0x19 } }
{ 0x6d18ec0b, 0x1f68, 0x4ae6, \
{ 0x8b, 0x3d, 0x8d, 0x7d, 0x8b, 0x8e, 0x28, 0xd4 } }
// Enum for requesting a particular type of document when creating a doc
enum DocumentFlavor {
@ -2065,7 +2065,8 @@ public:
*/
virtual void PreloadStyle(nsIURI* aURI, const nsAString& aCharset,
const nsAString& aCrossOriginAttr,
ReferrerPolicyEnum aReferrerPolicy) = 0;
ReferrerPolicyEnum aReferrerPolicy,
const nsAString& aIntegrity) = 0;
/**
* Called by the chrome registry to load style sheets. Can be put

54
dom/base/nsScriptLoader.cpp

@ -53,9 +53,21 @@
#include "mozilla/Attributes.h"
#include "mozilla/unused.h"
#include "mozilla/dom/SRICheck.h"
#include "nsIScriptError.h"
static PRLogModuleInfo* gCspPRLog;
static PRLogModuleInfo*
GetSriLog()
{
static PRLogModuleInfo *gSriPRLog;
if (!gSriPRLog) {
gSriPRLog = PR_NewLogModule("SRI");
}
return gSriPRLog;
}
using namespace mozilla;
using namespace mozilla::dom;
@ -626,7 +638,22 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
if (!request) {
// no usable preload
request = new nsScriptLoadRequest(aElement, version, ourCORSMode);
SRIMetadata sriMetadata;
{
nsAutoString integrity;
scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity,
integrity);
if (!integrity.IsEmpty()) {
MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
("nsScriptLoader::ProcessScriptElement, integrity=%s",
NS_ConvertUTF16toUTF8(integrity).get()));
SRICheck::IntegrityMetadata(integrity, mDocument, &sriMetadata);
}
}
request = new nsScriptLoadRequest(aElement, version, ourCORSMode,
sriMetadata);
request->mURI = scriptURI;
request->mIsInline = false;
request->mLoading = true;
@ -740,7 +767,8 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
}
// Inline scripts ignore ther CORS mode and are always CORS_NONE
request = new nsScriptLoadRequest(aElement, version, CORS_NONE);
request = new nsScriptLoadRequest(aElement, version, CORS_NONE,
SRIMetadata()); // SRI doesn't apply
request->mJSVersion = version;
request->mLoading = false;
request->mIsInline = true;
@ -1436,8 +1464,14 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
NS_ASSERTION(request, "null request in stream complete handler");
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen,
aString);
nsresult rv = NS_ERROR_SRI_CORRUPT;
if (request->mIntegrity.IsEmpty() ||
NS_SUCCEEDED(SRICheck::VerifyIntegrity(request->mIntegrity, aLoader,
request->mCORSMode, aStringLen,
aString, mDocument))) {
rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen, aString);
}
if (NS_FAILED(rv)) {
/*
* Handle script not loading error because source was a tracking URL.
@ -1634,6 +1668,7 @@ void
nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
const nsAString &aType,
const nsAString &aCrossOrigin,
const nsAString& aIntegrity,
bool aScriptFromHead,
const mozilla::net::ReferrerPolicy aReferrerPolicy)
{
@ -1642,9 +1677,18 @@ nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
return;
}
SRIMetadata sriMetadata;
if (!aIntegrity.IsEmpty()) {
MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
("nsScriptLoader::PreloadURI, integrity=%s",
NS_ConvertUTF16toUTF8(aIntegrity).get()));
SRICheck::IntegrityMetadata(aIntegrity, mDocument, &sriMetadata);
}
nsRefPtr<nsScriptLoadRequest> request =
new nsScriptLoadRequest(nullptr, 0,
Element::StringToCORSMode(aCrossOrigin));
Element::StringToCORSMode(aCrossOrigin),
sriMetadata);
request->mURI = aURI;
request->mIsInline = false;
request->mLoading = true;

8
dom/base/nsScriptLoader.h

@ -19,6 +19,7 @@
#include "nsIDocument.h"
#include "nsIStreamLoader.h"
#include "mozilla/CORSMode.h"
#include "mozilla/dom/SRIMetadata.h"
#include "mozilla/LinkedList.h"
#include "mozilla/net/ReferrerPolicy.h"
@ -56,7 +57,8 @@ class nsScriptLoadRequest final : public nsISupports,
public:
nsScriptLoadRequest(nsIScriptElement* aElement,
uint32_t aVersion,
mozilla::CORSMode aCORSMode)
mozilla::CORSMode aCORSMode,
const mozilla::dom::SRIMetadata &aIntegrity)
: mElement(aElement),
mLoading(true),
mIsInline(true),
@ -72,6 +74,7 @@ public:
mJSVersion(aVersion),
mLineNo(1),
mCORSMode(aCORSMode),
mIntegrity(aIntegrity),
mReferrerPolicy(mozilla::net::RP_Default)
{
}
@ -129,6 +132,7 @@ public:
nsAutoCString mURL; // Keep the URI's filename alive during off thread parsing.
int32_t mLineNo;
const mozilla::CORSMode mCORSMode;
const mozilla::dom::SRIMetadata mIntegrity;
mozilla::net::ReferrerPolicy mReferrerPolicy;
};
@ -374,11 +378,13 @@ public:
* @param aType The type parameter for the script.
* @param aCrossOrigin The crossorigin attribute for the script.
* Void if not present.
* @param aIntegrity The expect hash url, if avail, of the request
* @param aScriptFromHead Whether or not the script was a child of head
*/
virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset,
const nsAString &aType,
const nsAString &aCrossOrigin,
const nsAString& aIntegrity,
bool aScriptFromHead,
const mozilla::net::ReferrerPolicy aReferrerPolicy);

10
dom/base/nsStyleLinkElement.cpp

@ -421,13 +421,21 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
scopeElement, aObserver, &doneLoading, &isAlternate);
}
else {
nsAutoString integrity;
thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
if (!integrity.IsEmpty()) {
MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
("nsStyleLinkElement::DoUpdateStyleSheet, integrity=%s",
NS_ConvertUTF16toUTF8(integrity).get()));
}
// XXXbz clone the URI here to work around content policies modifying URIs.
nsCOMPtr<nsIURI> clonedURI;
uri->Clone(getter_AddRefs(clonedURI));
NS_ENSURE_TRUE(clonedURI, NS_ERROR_OUT_OF_MEMORY);
rv = doc->CSSLoader()->
LoadStyleLink(thisContent, clonedURI, title, media, isAlternate,
GetCORSMode(), doc->GetReferrerPolicy(),
GetCORSMode(), doc->GetReferrerPolicy(), integrity,
aObserver, &isAlternate);
if (NS_FAILED(rv)) {
// Don't propagate LoadStyleLink() errors further than this, since some

13
dom/base/test/file_bug1091883_frame.html

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<body>
<h2>Frame I am.</h2>
<script>
var subframeOrigin = location.hash.substr(1); // e.g., "http://example.com"
var subframe = document.createElement("iframe");
subframe.src = subframeOrigin +
"/tests/dom/base/test/file_bug1091883_subframe.html";
document.body.appendChild(subframe);
</script>
</body>
</html>

6
dom/base/test/file_bug1091883_subframe.html

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<h3>Subframe I am.</h3>
</body>
</html>

13
dom/base/test/file_bug1091883_target.html

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<h3>Target I am.</h3>
<script>
var testRun = location.hash.substr(1);
parent.parent.postMessage(document.referrer + " " + testRun,
"http://mochi.test:8888");
</script>
</head>
<body>
</body>
</html>

4
dom/base/test/mochitest.ini

@ -92,6 +92,9 @@ support-files =
file_XHR_system_redirect.html^headers^
file_XHR_timeout.sjs
file_base_xbl.xml
file_bug1091883_frame.html
file_bug1091883_subframe.html
file_bug1091883_target.html
file_bug28293.sjs
file_bug326337.xml
file_bug326337_inner.html
@ -247,6 +250,7 @@ support-files =
[test_audioWindowUtils.html]
[test_audioNotification.html]
skip-if = buildapp == 'mulet'
[test_bug1091883.html]
[test_bug116083.html]
[test_bug793311.html]
[test_bug913761.html]

89
dom/base/test/test_bug1091883.html

@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1091883
-->
<head>
<meta charset="utf-8">
<meta name="referrer" content="origin-when-crossorigin">
<title>Test for Bug 1091883</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1091883">Mozilla Bug 1091883</a></p>
<h2>Results</h2>
<pre id="results">Running...</pre>
<script>
SimpleTest.waitForExplicitFinish();
var origins = [
"http://mochi.test:8888", "http://example.com", "http://example.org"];
var numOrigins = origins.length;
// For each combination of (frame, subframe, target) origins, this test
// includes a "frame" that includes a "subframe"; and then this test
// navigates this "subframe" to the "target". Both the referrer and
// the triggering principal are this test, i.e., "http://mochi.test:8888".
// Since the referrer policy is origin-when-crossorigin, we expect to have
// a full referrer if and only if the target is also "http://mochi.test:8888";
// in all other cases, the referrer needs to be the origin alone.
var numTests = numOrigins * numOrigins * numOrigins;
// Helpers to look up the approriate origins for a given test number.
function getFrameOrigin(i) {
return origins[(i / (numOrigins * numOrigins)) | 0];
}
function getSubframeOrigin(i) {
return origins[((i / numOrigins) | 0) % 3];
}
function getTargetOrigin(i) {
return origins[i % 3];
}
// Create the frames, and tell them which subframes to load.
for (var i = 0; i < numTests; i++) {
var frame = document.createElement("iframe");
frame.src = getFrameOrigin(i) +
"/tests/dom/base/test/file_bug1091883_frame.html#" +
getSubframeOrigin(i);
document.body.appendChild(frame);
}
// Navigate all subframes to the target.
window.onload = function() {
for (var i = 0; i < numTests; i++) {
frames[i].frames[0].location = getTargetOrigin(i) +
"/tests/dom/base/test/file_bug1091883_target.html#" + i;
}
};
// Check referrer messages from the target.
var results = {};
function makeResultsKey(i) {
return i + ": " + getFrameOrigin(i) + " | " + getSubframeOrigin(i) + " -> " +
getTargetOrigin(i);
}
window.addEventListener("message", function(event) {
var out = event.data.split(" ");
var referrer = out[0];
var testRun = +out[1];
results[makeResultsKey(testRun)] = referrer;
if (event.origin == "http://mochi.test:8888") {
is(referrer,
"http://mochi.test:8888/tests/dom/base/test/test_bug1091883.html",
"must be full referrer");
} else {
is(referrer, "http://mochi.test:8888", "must be origin referrer");
}
if (Object.keys(results).length == numTests) {
document.getElementById("results").textContent =
JSON.stringify(results, null, 4);
SimpleTest.finish();
}
});
</script>
</body>
</html>

5
dom/html/HTMLLinkElement.cpp

@ -214,6 +214,11 @@ HTMLLinkElement::ParseAttribute(int32_t aNamespaceID,
aResult.ParseAtomArray(aValue);
return true;
}
if (aAttribute == nsGkAtoms::integrity) {
aResult.ParseStringOrAtom(aValue);
return true;
}
}
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,

8
dom/html/HTMLLinkElement.h

@ -136,6 +136,14 @@ public:
{
SetHTMLAttr(nsGkAtoms::target, aTarget, aRv);
}
void GetIntegrity(nsAString& aIntegrity) const
{
GetHTMLAttr(nsGkAtoms::integrity, aIntegrity);
}
void SetIntegrity(const nsAString& aIntegrity, ErrorResult& aRv)
{
SetHTMLAttr(nsGkAtoms::integrity, aIntegrity, aRv);
}
already_AddRefed<nsIDocument> GetImport();
already_AddRefed<ImportLoader> GetImportLoader()

14
dom/html/HTMLScriptElement.cpp

@ -75,10 +75,16 @@ HTMLScriptElement::ParseAttribute(int32_t aNamespaceID,
const nsAString& aValue,
nsAttrValue& aResult)
{
if (aNamespaceID == kNameSpaceID_None &&
aAttribute == nsGkAtoms::crossorigin) {
ParseCORSValue(aValue, aResult);
return true;
if (aNamespaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::crossorigin) {
ParseCORSValue(aValue, aResult);
return true;
}
if (aAttribute == nsGkAtoms::integrity) {
aResult.ParseStringOrAtom(aValue);
return true;
}
}
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,

8
dom/html/HTMLScriptElement.h

@ -79,6 +79,14 @@ public:
{
SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError);
}
void GetIntegrity(nsAString& aIntegrity)
{
GetHTMLAttr(nsGkAtoms::integrity, aIntegrity);
}
void SetIntegrity(const nsAString& aIntegrity, ErrorResult& rv)
{
SetHTMLAttr(nsGkAtoms::integrity, aIntegrity, rv);
}
bool Async();
void SetAsync(bool aValue, ErrorResult& rv);

16
dom/locales/en-US/chrome/security/security.properties

@ -32,6 +32,22 @@ LoadingMixedDisplayContent2=Loading mixed (insecure) display content "%1$S" on a
# LOCALIZATION NOTE: Do not translate "allow-scripts", "allow-same-origin", "sandbox" or "iframe"
BothAllowScriptsAndSameOriginPresent=An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can remove its sandboxing.
# Sub-Resource Integrity
# LOCALIZATION NOTE: Do not translate "script" or "integrity". "%1$S" is the invalid token found in the attribute.
MalformedIntegrityHash=The script element has a malformed hash in its integrity attribute: "%1$S". The correct format is "<hash algorithm>-<hash value>".
# LOCALIZATION NOTE: Do not translate "integrity"
InvalidIntegrityLength=The hash contained in the integrity attribute has the wrong length.
# LOCALIZATION NOTE: Do not translate "integrity"
InvalidIntegrityBase64=The hash contained in the integrity attribute could not be decoded.
# LOCALIZATION NOTE: Do not translate "integrity". "%1$S" is the type of hash algorigthm in use (e.g. "sha256").
IntegrityMismatch=None of the "%1$S" hashes in the integrity attribute match the content of the subresource.
# LOCALIZATION NOTE: "%1$S" is the URI of the sub-resource that cannot be protected using SRI.
IneligibleResource="%1$S" is not eligible for integrity checks since it's neither CORS-enabled nor same-origin.
# LOCALIZATION NOTE: Do not translate "integrity". "%1$S" is the invalid hash algorithm found in the attribute.
UnsupportedHashAlg=Unsupported hash algorithm in the integrity attribute: "%1$S"
# LOCALIZATION NOTE: Do not translate "integrity"
NoValidMetadata=The integrity attribute does not contain any valid metadata.
# LOCALIZATION NOTE: Do not translate "SSL 3.0".
WeakProtocolVersionWarning=This site uses the protocol SSL 3.0 for encryption, which is deprecated and insecure.
# LOCALIZATION NOTE: Do not translate "RC4".

349
dom/security/SRICheck.cpp

@ -0,0 +1,349 @@
/* -*- 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 "SRICheck.h"
#include "mozilla/Base64.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "nsContentUtils.h"
#include "nsIChannel.h"
#include "nsICryptoHash.h"
#include "nsIDocument.h"
#include "nsIProtocolHandler.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsIStreamLoader.h"
#include "nsIUnicharStreamLoader.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsWhitespaceTokenizer.h"
static PRLogModuleInfo*
GetSriLog()
{
static PRLogModuleInfo *gSriPRLog;
if (!gSriPRLog) {
gSriPRLog = PR_NewLogModule("SRI");
}
return gSriPRLog;
}
#define SRILOG(args) MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug, args)
#define SRIERROR(args) MOZ_LOG(GetSriLog(), mozilla::LogLevel::Error, args)
namespace mozilla {
namespace dom {
/**
* Returns whether or not the sub-resource about to be loaded is eligible
* for integrity checks. If it's not, the checks will be skipped and the
* sub-resource will be loaded.
*/
static nsresult
IsEligible(nsIChannel* aChannel, const CORSMode aCORSMode,
const nsIDocument* aDocument)
{
NS_ENSURE_ARG_POINTER(aDocument);
if (!aChannel) {
SRILOG(("SRICheck::IsEligible, null channel"));
return NS_ERROR_SRI_NOT_ELIGIBLE;
}
// Was the sub-resource loaded via CORS?
if (aCORSMode != CORS_NONE) {
SRILOG(("SRICheck::IsEligible, CORS mode"));
return NS_OK;
}
nsCOMPtr<nsIURI> finalURI;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> originalURI;
rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString requestSpec;
rv = originalURI->GetSpec(requestSpec);
NS_ENSURE_SUCCESS(rv, rv);
if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) {
nsAutoCString documentSpec, finalSpec;
aDocument->GetDocumentURI()->GetAsciiSpec(documentSpec);
if (finalURI) {
finalURI->GetSpec(finalSpec);
}
SRILOG(("SRICheck::IsEligible, documentURI=%s; requestURI=%s; finalURI=%s",
documentSpec.get(), requestSpec.get(), finalSpec.get()));
}
// Is the sub-resource same-origin?
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
if (NS_SUCCEEDED(ssm->CheckSameOriginURI(aDocument->GetDocumentURI(),
finalURI, false))) {
SRILOG(("SRICheck::IsEligible, same-origin"));
return NS_OK;
}
SRILOG(("SRICheck::IsEligible, NOT same origin"));
NS_ConvertUTF8toUTF16 requestSpecUTF16(requestSpec);
const char16_t* params[] = { requestSpecUTF16.get() };
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
NS_LITERAL_CSTRING("Sub-resource Integrity"),
aDocument,
nsContentUtils::eSECURITY_PROPERTIES,
"IneligibleResource",
params, ArrayLength(params));
return NS_ERROR_SRI_NOT_ELIGIBLE;
}
/**
* Compute the hash of a sub-resource and compare it with the expected
* value.
*/
static nsresult
VerifyHash(const SRIMetadata& aMetadata, uint32_t aHashIndex,
uint32_t aStringLen, const uint8_t* aString,
const nsIDocument* aDocument)
{
NS_ENSURE_ARG_POINTER(aString);
NS_ENSURE_ARG_POINTER(aDocument);
nsAutoCString base64Hash;
aMetadata.GetHash(aHashIndex, &base64Hash);
SRILOG(("SRICheck::VerifyHash, hash[%u]=%s", aHashIndex, base64Hash.get()));
nsAutoCString binaryHash;
if (NS_WARN_IF(NS_FAILED(Base64Decode(base64Hash, binaryHash)))) {
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
NS_LITERAL_CSTRING("Sub-resource Integrity"),
aDocument,
nsContentUtils::eSECURITY_PROPERTIES,
"InvalidIntegrityBase64");
return NS_ERROR_SRI_CORRUPT;
}
uint32_t hashLength;
int8_t hashType;
aMetadata.GetHashType(&hashType, &hashLength);
if (binaryHash.Length() != hashLength) {
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
NS_LITERAL_CSTRING("Sub-resource Integrity"),
aDocument,
nsContentUtils::eSECURITY_PROPERTIES,
"InvalidIntegrityLength");
return NS_ERROR_SRI_CORRUPT;
}
nsresult rv;
nsCOMPtr<nsICryptoHash> cryptoHash =
do_CreateInstance("@mozilla.org/security/hash;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = cryptoHash->Init(hashType);
NS_ENSURE_SUCCESS(rv, rv);
rv = cryptoHash->Update(aString, aStringLen);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString computedHash;
rv = cryptoHash->Finish(false, computedHash);
NS_ENSURE_SUCCESS(rv, rv);
if (!binaryHash.Equals(computedHash)) {
SRILOG(("SRICheck::VerifyHash, hash[%u] did not match", aHashIndex));
return NS_ERROR_SRI_CORRUPT;
}
SRILOG(("SRICheck::VerifyHash, hash[%u] verified successfully", aHashIndex));
return NS_OK;
}
/* static */ nsresult
SRICheck::IntegrityMetadata(const nsAString& aMetadataList,
const nsIDocument* aDocument,
SRIMetadata* outMetadata)
{
NS_ENSURE_ARG_POINTER(outMetadata);
NS_ENSURE_ARG_POINTER(aDocument);
MOZ_ASSERT(outMetadata->IsEmpty()); // caller must pass empty metadata
if (!Preferences::GetBool("security.sri.enable", false)) {
SRILOG(("SRICheck::IntegrityMetadata, sri is disabled (pref)"));
return NS_ERROR_SRI_DISABLED;
}
// put a reasonable bound on the length of the metadata
NS_ConvertUTF16toUTF8 metadataList(aMetadataList);
if (metadataList.Length() > SRICheck::MAX_METADATA_LENGTH) {
metadataList.Truncate(SRICheck::MAX_METADATA_LENGTH);
}
MOZ_ASSERT(metadataList.Length() <= aMetadataList.Length());
// the integrity attribute is a list of whitespace-separated hashes
// and options so we need to look at them one by one and pick the
// strongest (valid) one
nsCWhitespaceTokenizer tokenizer(metadataList);
nsAutoCString token;
for (uint32_t i=0; tokenizer.hasMoreTokens() &&
i < SRICheck::MAX_METADATA_TOKENS; ++i) {
token = tokenizer.nextToken();
SRIMetadata metadata(token);
if (metadata.IsMalformed()) {
NS_ConvertUTF8toUTF16 tokenUTF16(token);
const char16_t* params[] = { tokenUTF16.get() };
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Sub-resource Integrity"),
aDocument,
nsContentUtils::eSECURITY_PROPERTIES,
"MalformedIntegrityHash",
params, ArrayLength(params));
} else if (!metadata.IsAlgorithmSupported()) {
nsAutoCString alg;
metadata.GetAlgorithm(&alg);
NS_ConvertUTF8toUTF16 algUTF16(alg);
const char16_t* params[] = { algUTF16.get() };
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Sub-resource Integrity"),
aDocument,
nsContentUtils::eSECURITY_PROPERTIES,
"UnsupportedHashAlg",
params, ArrayLength(params));
}
nsAutoCString alg1, alg2;
if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) {
outMetadata->GetAlgorithm(&alg1);
metadata.GetAlgorithm(&alg2);
}
if (*outMetadata == metadata) {
SRILOG(("SRICheck::IntegrityMetadata, alg '%s' is the same as '%s'",
alg1.get(), alg2.get()));
*outMetadata += metadata; // add new hash to strongest metadata
} else if (*outMetadata < metadata) {
SRILOG(("SRICheck::IntegrityMetadata, alg '%s' is weaker than '%s'",
alg1.get(), alg2.get()));
*outMetadata = metadata; // replace strongest metadata with current
}
}
if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) {
if (outMetadata->IsValid()) {
nsAutoCString alg;
outMetadata->GetAlgorithm(&alg);
SRILOG(("SRICheck::IntegrityMetadata, using a '%s' hash", alg.get()));
} else if (outMetadata->IsEmpty()) {
SRILOG(("SRICheck::IntegrityMetadata, no metadata"));
} else {
SRILOG(("SRICheck::IntegrityMetadata, no valid metadata found"));
}
}
return NS_OK;
}
static nsresult
VerifyIntegrityInternal(const SRIMetadata& aMetadata,
nsIChannel* aChannel,
const CORSMode aCORSMode,
uint32_t aStringLen,
const uint8_t* aString,
const nsIDocument* aDocument)
{
MOZ_ASSERT(!aMetadata.IsEmpty()); // should be checked by caller
// IntegrityMetadata() checks this and returns "no metadata" if
// it's disabled so we should never make it this far
MOZ_ASSERT(Preferences::GetBool("security.sri.enable", false));
if (NS_FAILED(IsEligible(aChannel, aCORSMode, aDocument))) {
return NS_ERROR_SRI_NOT_ELIGIBLE;
}
if (!aMetadata.IsValid()) {
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Sub-resource Integrity"),
aDocument,
nsContentUtils::eSECURITY_PROPERTIES,
"NoValidMetadata");
return NS_OK; // ignore invalid metadata for forward-compatibility
}
for (uint32_t i = 0; i < aMetadata.HashCount(); i++) {
if (NS_SUCCEEDED(VerifyHash(aMetadata, i, aStringLen,
aString, aDocument))) {
return NS_OK; // stop at the first valid hash
}
}
nsAutoCString alg;
aMetadata.GetAlgorithm(&alg);
NS_ConvertUTF8toUTF16 algUTF16(alg);
const char16_t* params[] = { algUTF16.get() };
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
NS_LITERAL_CSTRING("Sub-resource Integrity"),
aDocument,
nsContentUtils::eSECURITY_PROPERTIES,
"IntegrityMismatch",
params, ArrayLength(params));
return NS_ERROR_SRI_CORRUPT;
}
/* static */ nsresult
SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata,
nsIUnicharStreamLoader* aLoader,
const CORSMode aCORSMode,
const nsAString& aString,
const nsIDocument* aDocument)
{
NS_ENSURE_ARG_POINTER(aLoader);
NS_ConvertUTF16toUTF8 utf8Hash(aString);
nsCOMPtr<nsIChannel> channel;
aLoader->GetChannel(getter_AddRefs(channel));
if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) {
nsAutoCString requestURL;
nsCOMPtr<nsIURI> originalURI;
if (channel &&
NS_SUCCEEDED(channel->GetOriginalURI(getter_AddRefs(originalURI))) &&
originalURI) {
originalURI->GetAsciiSpec(requestURL);
}
SRILOG(("SRICheck::VerifyIntegrity (unichar stream), url=%s (length=%u)",
requestURL.get(), utf8Hash.Length()));
}
return VerifyIntegrityInternal(aMetadata, channel, aCORSMode,
utf8Hash.Length(), (uint8_t*)utf8Hash.get(),
aDocument);
}
/* static */ nsresult
SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata,
nsIStreamLoader* aLoader,
const CORSMode aCORSMode,
uint32_t aStringLen,
const uint8_t* aString,
const nsIDocument* aDocument)
{
NS_ENSURE_ARG_POINTER(aLoader);
nsCOMPtr<nsIRequest> request;
aLoader->GetRequest(getter_AddRefs(request));
NS_ENSURE_ARG_POINTER(request);
nsCOMPtr<nsIChannel> channel;
channel = do_QueryInterface(request);
if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) {
nsAutoCString requestURL;
request->GetName(requestURL);
SRILOG(("SRICheck::VerifyIntegrity (stream), url=%s (length=%u)",
requestURL.get(), aStringLen));
}
return VerifyIntegrityInternal(aMetadata, channel, aCORSMode,
aStringLen, aString, aDocument);
}
} // namespace dom
} // namespace mozilla

60
dom/security/SRICheck.h

@ -0,0 +1,60 @@
/* -*- 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/. */
#ifndef mozilla_dom_SRICheck_h
#define mozilla_dom_SRICheck_h
#include "mozilla/CORSMode.h"
#include "nsCOMPtr.h"
#include "SRIMetadata.h"
class nsIDocument;
class nsIStreamLoader;
class nsIUnicharStreamLoader;
namespace mozilla {
namespace dom {
class SRICheck final
{
public:
static const uint32_t MAX_METADATA_LENGTH = 24*1024;
static const uint32_t MAX_METADATA_TOKENS = 512;
/**
* Parse the multiple hashes specified in the integrity attribute and
* return the strongest supported hash.
*/
static nsresult IntegrityMetadata(const nsAString& aMetadataList,
const nsIDocument* aDocument,
SRIMetadata* outMetadata);
/**
* Process the integrity attribute of the element. A result of false
* must prevent the resource from loading.
*/
static nsresult VerifyIntegrity(const SRIMetadata& aMetadata,
nsIUnicharStreamLoader* aLoader,
const CORSMode aCORSMode,
const nsAString& aString,
const nsIDocument* aDocument);
/**
* Process the integrity attribute of the element. A result of false
* must prevent the resource from loading.
*/
static nsresult VerifyIntegrity(const SRIMetadata& aMetadata,
nsIStreamLoader* aLoader,
const CORSMode aCORSMode,
uint32_t aStringLen,
const uint8_t* aString,
const nsIDocument* aDocument);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_SRICheck_h

172
dom/security/SRIMetadata.cpp

@ -0,0 +1,172 @@
/* -*- 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 "SRIMetadata.h"
#include "hasht.h"
#include "mozilla/dom/URLSearchParams.h"
#include "mozilla/Logging.h"
#include "nsICryptoHash.h"
static PRLogModuleInfo*
GetSriMetadataLog()
{
static PRLogModuleInfo *gSriMetadataPRLog;
if (!gSriMetadataPRLog) {
gSriMetadataPRLog = PR_NewLogModule("SRIMetadata");
}
return gSriMetadataPRLog;
}
#define SRIMETADATALOG(args) MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Debug, args)
#define SRIMETADATAERROR(args) MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Error, args)
namespace mozilla {
namespace dom {
SRIMetadata::SRIMetadata(const nsACString& aToken)
: mAlgorithmType(SRIMetadata::UNKNOWN_ALGORITHM), mEmpty(false)
{
MOZ_ASSERT(!aToken.IsEmpty()); // callers should check this first
SRIMETADATALOG(("SRIMetadata::SRIMetadata, aToken='%s'",
PromiseFlatCString(aToken).get()));
int32_t hyphen = aToken.FindChar('-');
if (hyphen == -1) {
SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (no hyphen)"));
return; // invalid metadata
}
// split the token into its components
mAlgorithm = Substring(aToken, 0, hyphen);
uint32_t hashStart = hyphen + 1;
if (hashStart >= aToken.Length()) {
SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (missing digest)"));
return; // invalid metadata
}
int32_t question = aToken.FindChar('?');
if (question == -1) {
mHashes.AppendElement(Substring(aToken, hashStart,
aToken.Length() - hashStart));
} else {
MOZ_ASSERT(question > 0);
if (static_cast<uint32_t>(question) <= hashStart) {
SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (options w/o digest)"));
return; // invalid metadata
}
mHashes.AppendElement(Substring(aToken, hashStart,
question - hashStart));
}
if (mAlgorithm.EqualsLiteral("sha256")) {
mAlgorithmType = nsICryptoHash::SHA256;
} else if (mAlgorithm.EqualsLiteral("sha384")) {
mAlgorithmType = nsICryptoHash::SHA384;
} else if (mAlgorithm.EqualsLiteral("sha512")) {
mAlgorithmType = nsICryptoHash::SHA512;
}
SRIMETADATALOG(("SRIMetadata::SRIMetadata, hash='%s'; alg='%s'",
mHashes[0].get(), mAlgorithm.get()));
}
bool
SRIMetadata::operator<(const SRIMetadata& aOther) const
{
static_assert(nsICryptoHash::SHA256 < nsICryptoHash::SHA384,
"We rely on the order indicating relative alg strength");
static_assert(nsICryptoHash::SHA384 < nsICryptoHash::SHA512,
"We rely on the order indicating relative alg strength");
MOZ_ASSERT(mAlgorithmType == SRIMetadata::UNKNOWN_ALGORITHM ||
mAlgorithmType == nsICryptoHash::SHA256 ||
mAlgorithmType == nsICryptoHash::SHA384 ||
mAlgorithmType == nsICryptoHash::SHA512);
MOZ_ASSERT(aOther.mAlgorithmType == SRIMetadata::UNKNOWN_ALGORITHM ||
aOther.mAlgorithmType == nsICryptoHash::SHA256 ||
aOther.mAlgorithmType == nsICryptoHash::SHA384 ||
aOther.mAlgorithmType == nsICryptoHash::SHA512);
if (mEmpty) {
SRIMETADATALOG(("SRIMetadata::operator<, first metadata is empty"));
return true; // anything beats the empty metadata (incl. invalid ones)
}
SRIMETADATALOG(("SRIMetadata::operator<, alg1='%d'; alg2='%d'",
mAlgorithmType, aOther.mAlgorithmType));
return (mAlgorithmType < aOther.mAlgorithmType);
}
bool
SRIMetadata::operator>(const SRIMetadata& aOther) const
{
MOZ_ASSERT(false);
return false;
}
SRIMetadata&
SRIMetadata::operator+=(const SRIMetadata& aOther)
{
MOZ_ASSERT(!aOther.IsEmpty() && !IsEmpty());
MOZ_ASSERT(aOther.IsValid() && IsValid());
MOZ_ASSERT(mAlgorithmType == aOther.mAlgorithmType);
// We only pull in the first element of the other metadata
MOZ_ASSERT(aOther.mHashes.Length() == 1);
if (mHashes.Length() < SRIMetadata::MAX_ALTERNATE_HASHES) {
SRIMETADATALOG(("SRIMetadata::operator+=, appending another '%s' hash (new length=%d)",
mAlgorithm.get(), mHashes.Length()));
mHashes.AppendElement(aOther.mHashes[0]);
}
MOZ_ASSERT(mHashes.Length() > 1);
MOZ_ASSERT(mHashes.Length() <= SRIMetadata::MAX_ALTERNATE_HASHES);
return *this;
}
bool
SRIMetadata::operator==(const SRIMetadata& aOther) const
{
if (IsEmpty() || !IsValid()) {
return false;
}
return mAlgorithmType == aOther.mAlgorithmType;
}
void
SRIMetadata::GetHash(uint32_t aIndex, nsCString* outHash) const
{
MOZ_ASSERT(aIndex < SRIMetadata::MAX_ALTERNATE_HASHES);
if (NS_WARN_IF(aIndex >= mHashes.Length())) {
*outHash = nullptr;
return;
}
*outHash = mHashes[aIndex];
}
void
SRIMetadata::GetHashType(int8_t* outType, uint32_t* outLength) const
{
// these constants are defined in security/nss/lib/util/hasht.h and
// netwerk/base/public/nsICryptoHash.idl
switch (mAlgorithmType) {
case nsICryptoHash::SHA256:
*outLength = SHA256_LENGTH;
break;
case nsICryptoHash::SHA384:
*outLength = SHA384_LENGTH;
break;
case nsICryptoHash::SHA512:
*outLength = SHA512_LENGTH;
break;
default:
*outLength = 0;
}
*outType = mAlgorithmType;
}
} // namespace dom
} // namespace mozilla

74
dom/security/SRIMetadata.h

@ -0,0 +1,74 @@
/* -*- 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/. */
#ifndef mozilla_dom_SRIMetadata_h
#define mozilla_dom_SRIMetadata_h
#include "nsTArray.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
class SRIMetadata final
{
public:
static const uint32_t MAX_ALTERNATE_HASHES = 256;
static const int8_t UNKNOWN_ALGORITHM = -1;
/**
* Create an empty metadata object.
*/
SRIMetadata() : mAlgorithmType(UNKNOWN_ALGORITHM), mEmpty(true) {}
/**
* Split a string token into the components of an SRI metadata
* attribute.
*/
explicit SRIMetadata(const nsACString& aToken);
/**
* Returns true when this object's hash algorithm is weaker than the
* other object's hash algorithm.
*/
bool operator<(const SRIMetadata& aOther) const;
/**
* Not implemented. Should not be used.
*/
bool operator>(const SRIMetadata& aOther) const;
/**
* Add another metadata's hash to this one.
*/
SRIMetadata& operator+=(const SRIMetadata& aOther);
/**
* Returns true when the two metadata use the same hash algorithm.
*/
bool operator==(const SRIMetadata& aOther) const;
bool IsEmpty() const { return mEmpty; }
bool IsMalformed() const { return mHashes.IsEmpty() || mAlgorithm.IsEmpty(); }
bool IsAlgorithmSupported() const { return mAlgorithmType != UNKNOWN_ALGORITHM; }
bool IsValid() const { return !IsMalformed() && IsAlgorithmSupported(); }
uint32_t HashCount() const { return mHashes.Length(); }
void GetHash(uint32_t aIndex, nsCString* outHash) const;
void GetAlgorithm(nsCString* outAlg) const { *outAlg = mAlgorithm; }
void GetHashType(int8_t* outType, uint32_t* outLength) const;
private:
nsTArray<nsCString> mHashes;
nsCString mAlgorithm;
int8_t mAlgorithmType;
bool mEmpty;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_SRIMetadata_h

4
dom/security/moz.build

@ -11,6 +11,8 @@ EXPORTS.mozilla.dom += [
'nsCSPService.h',
'nsCSPUtils.h',
'nsMixedContentBlocker.h',
'SRICheck.h',
'SRIMetadata.h',
]
EXPORTS += [
@ -24,6 +26,8 @@ UNIFIED_SOURCES += [
'nsCSPService.cpp',
'nsCSPUtils.cpp',
'nsMixedContentBlocker.cpp',
'SRICheck.cpp',
'SRIMetadata.cpp',
]
FAIL_ON_WARNINGS = True

1
dom/security/test/moz.build

@ -16,6 +16,7 @@ MOCHITEST_MANIFESTS += [
'cors/mochitest.ini',
'csp/mochitest.ini',
'mixedcontentblocker/mochitest.ini',
'sri/mochitest.ini',
]
MOCHITEST_CHROME_MANIFESTS += [

135
dom/security/test/sri/iframe_script_crossdomain.html

@ -0,0 +1,135 @@
<!DOCTYPE HTML>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();