diff options
Diffstat (limited to 'dom/security')
76 files changed, 1483 insertions, 266 deletions
diff --git a/dom/security/ReferrerInfo.cpp b/dom/security/ReferrerInfo.cpp index 70cdddb8ab..565d4d3284 100644 --- a/dom/security/ReferrerInfo.cpp +++ b/dom/security/ReferrerInfo.cpp @@ -131,16 +131,9 @@ ReferrerPolicy ReferrerPolicyFromToken(const nsAString& aContent, } } - // Supported tokes - ReferrerPolicyValues, are generated from - // ReferrerPolicy.webidl - for (uint8_t i = 0; ReferrerPolicyValues::strings[i].value; i++) { - if (lowerContent.EqualsASCII(ReferrerPolicyValues::strings[i].value)) { - return static_cast<enum ReferrerPolicy>(i); - } - } - - // Return no referrer policy (empty string) if none of the previous match - return ReferrerPolicy::_empty; + // Return no referrer policy (empty string) if it's not a valid enum value. + return StringToEnum<ReferrerPolicy>(lowerContent) + .valueOr(ReferrerPolicy::_empty); } // static @@ -187,18 +180,6 @@ ReferrerPolicy ReferrerInfo::ReferrerPolicyFromHeaderString( return referrerPolicy; } -// static -const char* ReferrerInfo::ReferrerPolicyToString(ReferrerPolicyEnum aPolicy) { - uint8_t index = static_cast<uint8_t>(aPolicy); - uint8_t referrerPolicyCount = ArrayLength(ReferrerPolicyValues::strings); - MOZ_ASSERT(index < referrerPolicyCount); - if (index >= referrerPolicyCount) { - return ""; - } - - return ReferrerPolicyValues::strings[index].value; -} - /* static */ uint32_t ReferrerInfo::GetUserReferrerSendingPolicy() { return clamped<uint32_t>( @@ -831,11 +812,8 @@ bool ReferrerInfo::ShouldIgnoreLessRestrictedPolicies( nsresult rv = aChannel->GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, true); - uint32_t idx = static_cast<uint32_t>(aPolicy); - AutoTArray<nsString, 2> params = { - NS_ConvertUTF8toUTF16( - nsDependentCString(ReferrerPolicyValues::strings[idx].value)), + NS_ConvertUTF8toUTF16(GetEnumString(aPolicy)), NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault())}; LogMessageToConsole(aChannel, "ReferrerPolicyDisallowRelaxingMessage", params); @@ -1051,7 +1029,7 @@ ReferrerInfo::GetReferrerPolicy( NS_IMETHODIMP ReferrerInfo::GetReferrerPolicyString(nsACString& aResult) { - aResult.AssignASCII(ReferrerPolicyToString(mPolicy)); + aResult.AssignASCII(GetEnumString(mPolicy)); return NS_OK; } @@ -1719,7 +1697,9 @@ void ReferrerInfo::RecordTelemetry(nsIHttpChannel* aChannel) { // requests and the rest 9 buckets are for cross-site requests. uint32_t telemetryOffset = IsCrossSiteRequest(aChannel) - ? static_cast<uint32_t>(ReferrerPolicy::EndGuard_) + ? UnderlyingValue( + MaxContiguousEnumValue<dom::ReferrerPolicy>::value) + + 1 : 0; Telemetry::Accumulate(Telemetry::REFERRER_POLICY_COUNT, diff --git a/dom/security/ReferrerInfo.h b/dom/security/ReferrerInfo.h index 78440a5a70..b62afbb934 100644 --- a/dom/security/ReferrerInfo.h +++ b/dom/security/ReferrerInfo.h @@ -258,13 +258,6 @@ class ReferrerInfo : public nsIReferrerInfo { static ReferrerPolicyEnum ReferrerPolicyFromHeaderString( const nsAString& aContent); - /* - * Helper function to convert ReferrerPolicy enum to string - * - * @param aPolicy referrer policy to convert. - */ - static const char* ReferrerPolicyToString(ReferrerPolicyEnum aPolicy); - /** * Hash function for this object */ diff --git a/dom/security/featurepolicy/test/mochitest/mochitest.toml b/dom/security/featurepolicy/test/mochitest/mochitest.toml index c621331596..b61b067b97 100644 --- a/dom/security/featurepolicy/test/mochitest/mochitest.toml +++ b/dom/security/featurepolicy/test/mochitest/mochitest.toml @@ -10,5 +10,7 @@ support-files = [ ["test_featureList.html"] +["test_initial_aboutblank.html"] + ["test_parser.html"] fail-if = ["xorigin"] diff --git a/dom/security/featurepolicy/test/mochitest/test_initial_aboutblank.html b/dom/security/featurepolicy/test/mochitest/test_initial_aboutblank.html new file mode 100644 index 0000000000..79d1bfd4c4 --- /dev/null +++ b/dom/security/featurepolicy/test/mochitest/test_initial_aboutblank.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test feature policy - Initial about:blank</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe name="parentIframe" allow="fullscreen 'none'" srcdoc=" + <iframe name='regular'></iframe> + <iframe name='transient'></iframe> + <script>transient.stop()</script> +"></iframe> + +<script type="text/javascript"> +add_task(function testAboutBlank() { + isDeeply(frames[0].transient.document.featurePolicy.allowedFeatures(), + frames[0].document.featurePolicy.allowedFeatures(), + "check allowed feature list for initial about:blank"); + isDeeply(frames[0].regular.document.featurePolicy.allowedFeatures(), + frames[0].document.featurePolicy.allowedFeatures(), + "check allowed feature list for real load about:blank"); +}); +</script> +</body> +</html> diff --git a/dom/security/metrics.yaml b/dom/security/metrics.yaml new file mode 100644 index 0000000000..02084407b1 --- /dev/null +++ b/dom/security/metrics.yaml @@ -0,0 +1,153 @@ +# 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/. + +# Adding a new metric? We have docs for that! +# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html + +--- +$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0 +$tags: + - 'Core :: DOM: Security' + +httpsfirst: + upgraded: + type: counter + description: > + Counts how often a load is marked to be upgraded to HTTPS because of + HTTPS-First (`dom.security.https_first` enabled). + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380#c10 + data_sensitivity: + - technical + notification_emails: + - mjurgens@mozilla.com + - seceng-telemetry@mozilla.com + expires: never + + upgraded_schemeless: + type: counter + description: > + Counts how often a load is marked to be upgraded to HTTPS because of + schemeless HTTPS-First (`dom.security.https_first` disabled, but load + marked as schemeless). + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380#c10 + data_sensitivity: + - technical + notification_emails: + - mjurgens@mozilla.com + - seceng-telemetry@mozilla.com + expires: never + + downgraded: + type: counter + description: > + How many regular HTTPS-First (`dom.security.https_first` enabled) + upgrades get downgraded again. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380#c10 + data_sensitivity: + - technical + notification_emails: + - mjurgens@mozilla.com + - seceng-telemetry@mozilla.com + expires: never + + downgraded_schemeless: + type: counter + description: > + How many schemeless HTTPS-First (`dom.security.https_first` disabled, but + load marked as schemeless) upgrades get downgraded again. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380#c10 + data_sensitivity: + - technical + notification_emails: + - mjurgens@mozilla.com + - seceng-telemetry@mozilla.com + expires: never + + downgraded_on_timer: + type: rate + description: > + How many HTTPS-First (`dom.security.https_first` enabled) upgrades get + downgraded again because the HTTP request fired after 3s received a answer + faster than the HTTPS request. + denominator_metric: httpsfirst.downgraded + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380#c10 + data_sensitivity: + - technical + notification_emails: + - mjurgens@mozilla.com + - seceng-telemetry@mozilla.com + expires: never + + downgraded_on_timer_schemeless: + type: rate + description: > + How many of schemeless HTTPS-First (`dom.security.https_first` disabled, + but load marked as schemeless) upgrades get downgraded again because the + HTTP request fired after 3s received a answer faster than the HTTPS + request + denominator_metric: httpsfirst.downgraded_schemeless + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380#c10 + data_sensitivity: + - technical + notification_emails: + - mjurgens@mozilla.com + - seceng-telemetry@mozilla.com + expires: never + + downgrade_time: + type: timing_distribution + description: > + If a HTTPS-First (`dom.security.https_first` enabled) upgrade isn't + successful, measures the timespan between the navigation start and the + downgrade. This is essentially the overhead caused by HTTPS-First if a + site does not support HTTPS. + time_unit: millisecond + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380#c10 + data_sensitivity: + - technical + notification_emails: + - mjurgens@mozilla.com + - seceng-telemetry@mozilla.com + expires: never + + downgrade_time_schemeless: + type: timing_distribution + description: > + If a schemeless HTTPS-First (`dom.security.https_first` disabled, but + load marked as schemeless) upgrade isn't successful, measures the + timespan between the navigation start and the downgrade. This is + essentially the overhead caused by HTTPS-First if a site does not support + HTTPS. + time_unit: millisecond + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1868380#c10 + data_sensitivity: + - technical + notification_emails: + - mjurgens@mozilla.com + - seceng-telemetry@mozilla.com + expires: never diff --git a/dom/security/moz.build b/dom/security/moz.build index 0f2aed6a1f..f63fa22db6 100644 --- a/dom/security/moz.build +++ b/dom/security/moz.build @@ -9,7 +9,7 @@ with Files("*"): TEST_DIRS += ["test"] -DIRS += ["featurepolicy", "sanitizer"] +DIRS += ["featurepolicy", "sanitizer", "trusted-types"] EXPORTS.mozilla.dom += [ "CSPEvalChecker.h", diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index aafd2b64f2..ed44304484 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -189,10 +189,11 @@ bool nsCSPContext::permitsInternal( bool permits = true; nsAutoString violatedDirective; + nsAutoString violatedDirectiveString; for (uint32_t p = 0; p < mPolicies.Length(); p++) { if (!mPolicies[p]->permits(aDir, aLoadInfo, aContentLocation, !!aOriginalURIIfRedirect, aSpecific, - violatedDirective)) { + violatedDirective, violatedDirectiveString)) { // If the policy is violated and not report-only, reject the load and // report to the console if (!mPolicies[p]->getReportOnlyFlag()) { @@ -200,12 +201,6 @@ bool nsCSPContext::permitsInternal( permits = false; } - // In CSP 3.0 the effective directive doesn't become the actually used - // directive in case of a fallback from e.g. script-src-elem to - // script-src or default-src. - nsAutoString effectiveDirective; - effectiveDirective.AssignASCII(CSP_CSPDirectiveToString(aDir)); - // Callers should set |aSendViolationReports| to false if this is a // preload - the decision may be wrong due to the inability to get the // nonce, and will incorrectly fail the unit tests. @@ -226,9 +221,11 @@ bool nsCSPContext::permitsInternal( BlockedContentSource::eUnknown, /* a BlockedContentSource */ aOriginalURIIfRedirect, /* in case of redirect originalURI is not null */ - violatedDirective, effectiveDirective, p, /* policy index */ - u""_ns, /* no observer subject */ - spec, /* source file */ + violatedDirective, violatedDirectiveString, + aDir, // aViolatedDirective + p, // policy index + u""_ns, // no observer subject + spec, // source file false, // aReportSample (no sample) u""_ns, /* no script sample */ lineNumber, /* line number */ @@ -519,7 +516,8 @@ void nsCSPContext::reportInlineViolation( CSPDirective aDirective, Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener, const nsAString& aNonce, bool aReportSample, const nsAString& aSample, - const nsAString& aViolatedDirective, const nsAString& aEffectiveDirective, + const nsAString& aViolatedDirective, + const nsAString& aViolatedDirectiveString, CSPDirective aEffectiveDirective, uint32_t aViolatedPolicyIndex, // TODO, use report only flag for that uint32_t aLineNumber, uint32_t aColumnNumber) { nsString observerSubject; @@ -564,14 +562,15 @@ void nsCSPContext::reportInlineViolation( BlockedContentSource::eInline, // aBlockedSource mSelfURI, // aOriginalURI aViolatedDirective, // aViolatedDirective - aEffectiveDirective, // aEffectiveDirective - aViolatedPolicyIndex, // aViolatedPolicyIndex - observerSubject, // aObserverSubject - sourceFile, // aSourceFile - aReportSample, // aReportSample - aSample, // aScriptSample - lineNumber, // aLineNum - columnNumber); // aColumnNum + aViolatedDirectiveString, + aEffectiveDirective, // aEffectiveDirective + aViolatedPolicyIndex, // aViolatedPolicyIndex + observerSubject, // aObserverSubject + sourceFile, // aSourceFile + aReportSample, // aReportSample + aSample, // aScriptSample + lineNumber, // aLineNum + columnNumber); // aColumnNum } NS_IMETHODIMP @@ -672,19 +671,16 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, bool aHasUnsafeHash, *outAllowsInline = false; } nsAutoString violatedDirective; + nsAutoString violatedDirectiveString; bool reportSample = false; - mPolicies[i]->getDirectiveStringAndReportSampleForContentType( - aDirective, violatedDirective, &reportSample); - - // In CSP 3.0 the effective directive doesn't become the actually used - // directive in case of a fallback from e.g. script-src-elem to - // script-src or default-src. - nsAutoString effectiveDirective; - effectiveDirective.AssignASCII(CSP_CSPDirectiveToString(aDirective)); + mPolicies[i]->getViolatedDirectiveInformation( + aDirective, violatedDirective, violatedDirectiveString, + &reportSample); reportInlineViolation(aDirective, aTriggeringElement, aCSPEventListener, aNonce, reportSample, content, violatedDirective, - effectiveDirective, i, aLineNumber, aColumnNumber); + violatedDirectiveString, aDirective, i, aLineNumber, + aColumnNumber); } } @@ -747,15 +743,18 @@ nsCSPContext::LogViolationDetails( } nsAutoString violatedDirective; + nsAutoString violatedDirectiveString; bool reportSample = false; - mPolicies[p]->getDirectiveStringAndReportSampleForContentType( - SCRIPT_SRC_DIRECTIVE, violatedDirective, &reportSample); + mPolicies[p]->getViolatedDirectiveInformation( + SCRIPT_SRC_DIRECTIVE, violatedDirective, violatedDirectiveString, + &reportSample); - AsyncReportViolation(aTriggeringElement, aCSPEventListener, nullptr, - blockedContentSource, nullptr, violatedDirective, - u"script-src"_ns /* aEffectiveDirective */, p, - observerSubject, aSourceFile, reportSample, - aScriptSample, aLineNum, aColumnNum); + AsyncReportViolation( + aTriggeringElement, aCSPEventListener, nullptr, blockedContentSource, + nullptr, violatedDirective, violatedDirectiveString, + CSPDirective::SCRIPT_SRC_DIRECTIVE /* aEffectiveDirective */, p, + observerSubject, aSourceFile, reportSample, aScriptSample, aLineNum, + aColumnNum); } return NS_OK; } @@ -1375,10 +1374,12 @@ class CSPReportSenderRunnable final : public Runnable { nsIURI* aBlockedURI, nsCSPContext::BlockedContentSource aBlockedContentSource, nsIURI* aOriginalURI, uint32_t aViolatedPolicyIndex, bool aReportOnlyFlag, - const nsAString& aViolatedDirective, const nsAString& aEffectiveDirective, - const nsAString& aObserverSubject, const nsAString& aSourceFile, - bool aReportSample, const nsAString& aScriptSample, uint32_t aLineNum, - uint32_t aColumnNum, nsCSPContext* aCSPContext) + const nsAString& aViolatedDirective, + const nsAString& aViolatedDirectiveString, + const CSPDirective aEffectiveDirective, const nsAString& aObserverSubject, + const nsAString& aSourceFile, bool aReportSample, + const nsAString& aScriptSample, uint32_t aLineNum, uint32_t aColumnNum, + nsCSPContext* aCSPContext) : mozilla::Runnable("CSPReportSenderRunnable"), mTriggeringElement(aTriggeringElement), mCSPEventListener(aCSPEventListener), @@ -1389,6 +1390,7 @@ class CSPReportSenderRunnable final : public Runnable { mReportOnlyFlag(aReportOnlyFlag), mReportSample(aReportSample), mViolatedDirective(aViolatedDirective), + mViolatedDirectiveString(aViolatedDirectiveString), mEffectiveDirective(aEffectiveDirective), mSourceFile(aSourceFile), mScriptSample(aScriptSample), @@ -1436,16 +1438,18 @@ class CSPReportSenderRunnable final : public Runnable { NS_IMETHOD Run() override { MOZ_ASSERT(NS_IsMainThread()); - nsresult rv; - // 0) prepare violation data mozilla::dom::SecurityPolicyViolationEventInit init; nsAutoCString blockedContentSource; BlockedContentSourceToString(mBlockedContentSource, blockedContentSource); - rv = mCSPContext->GatherSecurityPolicyViolationEventData( - mBlockedURI, blockedContentSource, mOriginalURI, mEffectiveDirective, + nsAutoString effectiveDirective; + effectiveDirective.AssignASCII( + CSP_CSPDirectiveToString(mEffectiveDirective)); + + nsresult rv = mCSPContext->GatherSecurityPolicyViolationEventData( + mBlockedURI, blockedContentSource, mOriginalURI, effectiveDirective, mViolatedPolicyIndex, mSourceFile, mReportSample ? mScriptSample : EmptyString(), mLineNum, mColumnNum, init); @@ -1464,32 +1468,7 @@ class CSPReportSenderRunnable final : public Runnable { mCSPContext->SendReports(init, mViolatedPolicyIndex); // 3) log to console (one per policy violation) - - if (mBlockedURI) { - mBlockedURI->GetSpec(blockedContentSource); - if (blockedContentSource.Length() > - nsCSPContext::ScriptSampleMaxLength()) { - bool isData = mBlockedURI->SchemeIs("data"); - if (NS_SUCCEEDED(rv) && isData && - blockedContentSource.Length() > - nsCSPContext::ScriptSampleMaxLength()) { - blockedContentSource.Truncate(nsCSPContext::ScriptSampleMaxLength()); - blockedContentSource.Append( - NS_ConvertUTF16toUTF8(nsContentUtils::GetLocalizedEllipsis())); - } - } - } - - if (blockedContentSource.Length() > 0) { - nsString blockedContentSource16 = - NS_ConvertUTF8toUTF16(blockedContentSource); - AutoTArray<nsString, 2> params = {mViolatedDirective, - blockedContentSource16}; - mCSPContext->logToConsole( - mReportOnlyFlag ? "CSPROViolationWithURI" : "CSPViolationWithURI", - params, mSourceFile, mScriptSample, mLineNum, mColumnNum, - nsIScriptError::errorFlag); - } + ReportToConsole(); // 4) fire violation event // A frame-ancestors violation has occurred, but we should not dispatch @@ -1503,6 +1482,104 @@ class CSPReportSenderRunnable final : public Runnable { } private: + void ReportToConsole() const { + NS_ConvertUTF8toUTF16 effectiveDirective( + CSP_CSPDirectiveToString(mEffectiveDirective)); + + switch (mBlockedContentSource) { + case nsCSPContext::BlockedContentSource::eInline: { + const char* errorName = nullptr; + if (mEffectiveDirective == CSPDirective::STYLE_SRC_ATTR_DIRECTIVE || + mEffectiveDirective == CSPDirective::STYLE_SRC_ELEM_DIRECTIVE) { + errorName = mReportOnlyFlag ? "CSPROInlineStyleViolation" + : "CSPInlineStyleViolation"; + } else if (mEffectiveDirective == + CSPDirective::SCRIPT_SRC_ATTR_DIRECTIVE) { + errorName = mReportOnlyFlag ? "CSPROEventHandlerScriptViolation" + : "CSPEventHandlerScriptViolation"; + } else { + MOZ_ASSERT(mEffectiveDirective == + CSPDirective::SCRIPT_SRC_ELEM_DIRECTIVE); + errorName = mReportOnlyFlag ? "CSPROInlineScriptViolation" + : "CSPInlineScriptViolation"; + } + + AutoTArray<nsString, 2> params = {mViolatedDirectiveString, + effectiveDirective}; + mCSPContext->logToConsole(errorName, params, mSourceFile, mScriptSample, + mLineNum, mColumnNum, + nsIScriptError::errorFlag); + break; + } + + case nsCSPContext::BlockedContentSource::eEval: { + AutoTArray<nsString, 2> params = {mViolatedDirectiveString, + effectiveDirective}; + mCSPContext->logToConsole(mReportOnlyFlag ? "CSPROEvalScriptViolation" + : "CSPEvalScriptViolation", + params, mSourceFile, mScriptSample, mLineNum, + mColumnNum, nsIScriptError::errorFlag); + break; + } + + case nsCSPContext::BlockedContentSource::eWasmEval: { + AutoTArray<nsString, 2> params = {mViolatedDirectiveString, + effectiveDirective}; + mCSPContext->logToConsole(mReportOnlyFlag + ? "CSPROWasmEvalScriptViolation" + : "CSPWasmEvalScriptViolation", + params, mSourceFile, mScriptSample, mLineNum, + mColumnNum, nsIScriptError::errorFlag); + break; + } + + case nsCSPContext::BlockedContentSource::eSelf: + case nsCSPContext::BlockedContentSource::eUnknown: { + nsAutoString source(u"<unknown>"_ns); + if (mBlockedURI) { + nsAutoCString uri; + mBlockedURI->GetSpec(uri); + + if (mBlockedURI->SchemeIs("data") && + uri.Length() > nsCSPContext::ScriptSampleMaxLength()) { + uri.Truncate(nsCSPContext::ScriptSampleMaxLength()); + uri.Append( + NS_ConvertUTF16toUTF8(nsContentUtils::GetLocalizedEllipsis())); + } + + if (!uri.IsEmpty()) { + CopyUTF8toUTF16(uri, source); + } + } + + const char* errorName = nullptr; + switch (mEffectiveDirective) { + case CSPDirective::STYLE_SRC_ELEM_DIRECTIVE: + errorName = + mReportOnlyFlag ? "CSPROStyleViolation" : "CSPStyleViolation"; + break; + case CSPDirective::SCRIPT_SRC_ELEM_DIRECTIVE: + errorName = + mReportOnlyFlag ? "CSPROScriptViolation" : "CSPScriptViolation"; + break; + case CSPDirective::WORKER_SRC_DIRECTIVE: + errorName = + mReportOnlyFlag ? "CSPROWorkerViolation" : "CSPWorkerViolation"; + break; + default: + errorName = mReportOnlyFlag ? "CSPROGenericViolation" + : "CSPGenericViolation"; + } + + AutoTArray<nsString, 3> params = {mViolatedDirectiveString, source, + effectiveDirective}; + mCSPContext->logToConsole(errorName, params, mSourceFile, mScriptSample, + mLineNum, mColumnNum, + nsIScriptError::errorFlag); + } + } + } + RefPtr<Element> mTriggeringElement; nsCOMPtr<nsICSPEventListener> mCSPEventListener; nsCOMPtr<nsIURI> mBlockedURI; @@ -1512,7 +1589,8 @@ class CSPReportSenderRunnable final : public Runnable { bool mReportOnlyFlag; bool mReportSample; nsString mViolatedDirective; - nsString mEffectiveDirective; + nsString mViolatedDirectiveString; + CSPDirective mEffectiveDirective; nsCOMPtr<nsISupports> mObserverSubject; nsString mSourceFile; nsString mScriptSample; @@ -1554,7 +1632,8 @@ nsresult nsCSPContext::AsyncReportViolation( Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener, nsIURI* aBlockedURI, BlockedContentSource aBlockedContentSource, nsIURI* aOriginalURI, const nsAString& aViolatedDirective, - const nsAString& aEffectiveDirective, uint32_t aViolatedPolicyIndex, + const nsAString& aViolatedDirectiveString, + const CSPDirective aEffectiveDirective, uint32_t aViolatedPolicyIndex, const nsAString& aObserverSubject, const nsAString& aSourceFile, bool aReportSample, const nsAString& aScriptSample, uint32_t aLineNum, uint32_t aColumnNum) { @@ -1565,8 +1644,8 @@ nsresult nsCSPContext::AsyncReportViolation( aTriggeringElement, aCSPEventListener, aBlockedURI, aBlockedContentSource, aOriginalURI, aViolatedPolicyIndex, mPolicies[aViolatedPolicyIndex]->getReportOnlyFlag(), aViolatedDirective, - aEffectiveDirective, aObserverSubject, aSourceFile, aReportSample, - aScriptSample, aLineNum, aColumnNum, this); + aViolatedDirectiveString, aEffectiveDirective, aObserverSubject, + aSourceFile, aReportSample, aScriptSample, aLineNum, aColumnNum, this); if (XRE_IsContentProcess()) { if (mEventTarget) { diff --git a/dom/security/nsCSPContext.h b/dom/security/nsCSPContext.h index 0c1438e573..ae47e34cb3 100644 --- a/dom/security/nsCSPContext.h +++ b/dom/security/nsCSPContext.h @@ -126,10 +126,12 @@ class nsCSPContext : public nsIContentSecurityPolicy { mozilla::dom::Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener, nsIURI* aBlockedURI, BlockedContentSource aBlockedContentSource, nsIURI* aOriginalURI, - const nsAString& aViolatedDirective, const nsAString& aEffectiveDirective, - uint32_t aViolatedPolicyIndex, const nsAString& aObserverSubject, - const nsAString& aSourceFile, bool aReportSample, - const nsAString& aScriptSample, uint32_t aLineNum, uint32_t aColumnNum); + const nsAString& aViolatedDirective, + const nsAString& aViolatedDirectiveString, + const CSPDirective aEffectiveDirective, uint32_t aViolatedPolicyIndex, + const nsAString& aObserverSubject, const nsAString& aSourceFile, + bool aReportSample, const nsAString& aScriptSample, uint32_t aLineNum, + uint32_t aColumnNum); // Hands off! Don't call this method unless you know what you // are doing. It's only supposed to be called from within @@ -168,7 +170,8 @@ class nsCSPContext : public nsIContentSecurityPolicy { const nsAString& aNonce, bool aReportSample, const nsAString& aSample, const nsAString& aViolatedDirective, - const nsAString& aEffectiveDirective, + const nsAString& aViolatedDirectiveString, + CSPDirective aEffectiveDirective, uint32_t aViolatedPolicyIndex, uint32_t aLineNumber, uint32_t aColumnNumber); diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp index 50730b691b..11d09909f7 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -1569,7 +1569,8 @@ nsCSPPolicy::~nsCSPPolicy() { bool nsCSPPolicy::permits(CSPDirective aDir, nsILoadInfo* aLoadInfo, nsIURI* aUri, bool aWasRedirected, bool aSpecific, - nsAString& outViolatedDirective) const { + nsAString& outViolatedDirective, + nsAString& outViolatedDirectiveString) const { if (CSPUTILSLOGENABLED()) { CSPUTILSLOG(("nsCSPPolicy::permits, aUri: %s, aDir: %s, aSpecific: %s", aUri->GetSpecOrDefault().get(), CSP_CSPDirectiveToString(aDir), @@ -1578,6 +1579,7 @@ bool nsCSPPolicy::permits(CSPDirective aDir, nsILoadInfo* aLoadInfo, NS_ASSERTION(aUri, "permits needs an uri to perform the check!"); outViolatedDirective.Truncate(); + outViolatedDirectiveString.Truncate(); nsCSPDirective* defaultDir = nullptr; @@ -1589,6 +1591,7 @@ bool nsCSPPolicy::permits(CSPDirective aDir, nsILoadInfo* aLoadInfo, if (!mDirectives[i]->permits(aDir, aLoadInfo, aUri, aWasRedirected, mReportOnly, mUpgradeInsecDir)) { mDirectives[i]->getDirName(outViolatedDirective); + mDirectives[i]->toString(outViolatedDirectiveString); return false; } return true; @@ -1604,6 +1607,7 @@ bool nsCSPPolicy::permits(CSPDirective aDir, nsILoadInfo* aLoadInfo, if (!defaultDir->permits(aDir, aLoadInfo, aUri, aWasRedirected, mReportOnly, mUpgradeInsecDir)) { defaultDir->getDirName(outViolatedDirective); + defaultDir->toString(outViolatedDirectiveString); return false; } return true; @@ -1692,43 +1696,22 @@ bool nsCSPPolicy::allowsAllInlineBehavior(CSPDirective aDir) const { * The parameter outDirective is the equivalent of 'outViolatedDirective' * for the ::permits() function family. */ -void nsCSPPolicy::getDirectiveStringAndReportSampleForContentType( - CSPDirective aDirective, nsAString& outDirective, - bool* aReportSample) const { - MOZ_ASSERT(aReportSample); +void nsCSPPolicy::getViolatedDirectiveInformation(CSPDirective aDirective, + nsAString& outDirective, + nsAString& outDirectiveString, + bool* aReportSample) const { *aReportSample = false; - - nsCSPDirective* defaultDir = nullptr; - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->isDefaultDirective()) { - defaultDir = mDirectives[i]; - continue; - } - if (mDirectives[i]->equals(aDirective)) { - mDirectives[i]->getDirName(outDirective); - *aReportSample = mDirectives[i]->hasReportSampleKeyword(); - return; - } - } - // if we haven't found a matching directive yet, - // the contentType must be restricted by the default directive - if (defaultDir) { - defaultDir->getDirName(outDirective); - *aReportSample = defaultDir->hasReportSampleKeyword(); + nsCSPDirective* directive = matchingOrDefaultDirective(aDirective); + if (!directive) { + MOZ_ASSERT_UNREACHABLE("Can not query violated directive"); + outDirective.AppendLiteral("couldNotQueryViolatedDirective"); + outDirective.Truncate(); return; } - NS_ASSERTION(false, "Can not query directive string for contentType!"); - outDirective.AppendLiteral("couldNotQueryViolatedDirective"); -} -void nsCSPPolicy::getDirectiveAsString(CSPDirective aDir, - nsAString& outDirective) const { - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->equals(aDir)) { - mDirectives[i]->toString(outDirective); - return; - } - } + directive->getDirName(outDirective); + directive->toString(outDirectiveString); + *aReportSample = directive->hasReportSampleKeyword(); } /* diff --git a/dom/security/nsCSPUtils.h b/dom/security/nsCSPUtils.h index 2692681d03..eeccaf0c4a 100644 --- a/dom/security/nsCSPUtils.h +++ b/dom/security/nsCSPUtils.h @@ -619,7 +619,8 @@ class nsCSPPolicy { bool permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo, nsIURI* aUri, bool aWasRedirected, bool aSpecific, - nsAString& outViolatedDirective) const; + nsAString& outViolatedDirective, + nsAString& outViolatedDirectiveString) const; bool allows(CSPDirective aDirective, enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const; void toString(nsAString& outStr) const; @@ -650,11 +651,10 @@ class nsCSPPolicy { void getReportURIs(nsTArray<nsString>& outReportURIs) const; - void getDirectiveStringAndReportSampleForContentType( - CSPDirective aDirective, nsAString& outDirective, - bool* aReportSample) const; - - void getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const; + void getViolatedDirectiveInformation(CSPDirective aDirective, + nsAString& outDirective, + nsAString& outDirectiveString, + bool* aReportSample) const; uint32_t getSandboxFlags() const; diff --git a/dom/security/nsContentSecurityUtils.cpp b/dom/security/nsContentSecurityUtils.cpp index a483522499..01e9c6d5db 100644 --- a/dom/security/nsContentSecurityUtils.cpp +++ b/dom/security/nsContentSecurityUtils.cpp @@ -1365,6 +1365,7 @@ void nsContentSecurityUtils::AssertAboutPageHasCSP(Document* aDocument) { // preferences and downloads allow legacy inline scripts through hash src. MOZ_ASSERT(!foundScriptSrc || StringBeginsWith(aboutSpec, "about:preferences"_ns) || + StringBeginsWith(aboutSpec, "about:settings"_ns) || StringBeginsWith(aboutSpec, "about:downloads"_ns) || StringBeginsWith(aboutSpec, "about:asrouter"_ns) || StringBeginsWith(aboutSpec, "about:newtab"_ns) || @@ -1383,6 +1384,7 @@ void nsContentSecurityUtils::AssertAboutPageHasCSP(Document* aDocument) { // remote web resources MOZ_ASSERT(!foundWebScheme || StringBeginsWith(aboutSpec, "about:preferences"_ns) || + StringBeginsWith(aboutSpec, "about:settings"_ns) || StringBeginsWith(aboutSpec, "about:addons"_ns) || StringBeginsWith(aboutSpec, "about:newtab"_ns) || StringBeginsWith(aboutSpec, "about:debugging"_ns) || @@ -1411,6 +1413,7 @@ void nsContentSecurityUtils::AssertAboutPageHasCSP(Document* aDocument) { // Bug 1579160: Remove 'unsafe-inline' from style-src within // about:preferences "about:preferences"_ns, + "about:settings"_ns, // Bug 1571346: Remove 'unsafe-inline' from style-src within about:addons "about:addons"_ns, // Bug 1584485: Remove 'unsafe-inline' from style-src within: @@ -1553,7 +1556,7 @@ bool nsContentSecurityUtils::ValidateScriptFilename(JSContext* cx, // and this is the most reasonable. See 1727770 u"about:downloads"_ns, // We think this is the same problem as about:downloads - u"about:preferences"_ns, + u"about:preferences"_ns, u"about:settings"_ns, // Browser console will give a filename of 'debugger' See 1763943 // Sometimes it's 'debugger eager eval code', other times just 'debugger // eval code' @@ -1667,37 +1670,25 @@ long nsContentSecurityUtils::ClassifyDownload( nsCOMPtr<nsIURI> contentLocation; aChannel->GetURI(getter_AddRefs(contentLocation)); - nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->GetLoadingPrincipal(); - if (!loadingPrincipal) { - loadingPrincipal = loadInfo->TriggeringPrincipal(); - } - // Creating a fake Loadinfo that is just used for the MCB check. - nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo( - loadingPrincipal, loadInfo->TriggeringPrincipal(), nullptr, - nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, - nsIContentPolicy::TYPE_FETCH); - // Disable HTTPS-Only checks for that loadinfo. This is required because - // otherwise nsMixedContentBlocker::ShouldLoad would assume that the request - // is safe, because HTTPS-Only is handling it. - secCheckLoadInfo->SetHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_EXEMPT); - - int16_t decission = nsIContentPolicy::ACCEPT; - nsMixedContentBlocker::ShouldLoad(false, // aHadInsecureImageRedirect - contentLocation, // aContentLocation, - secCheckLoadInfo, // aLoadinfo - false, // aReportError - &decission // aDecision - ); - Telemetry::Accumulate(mozilla::Telemetry::MIXED_CONTENT_DOWNLOADS, - decission != nsIContentPolicy::ACCEPT); - - if (StaticPrefs::dom_block_download_insecure() && - decission != nsIContentPolicy::ACCEPT) { - nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); - if (httpChannel) { - LogMessageToConsole(httpChannel, "MixedContentBlockedDownload"); + if (StaticPrefs::dom_block_download_insecure()) { + // If we are not dealing with a potentially trustworthy origin, or a URI + // that is safe to be loaded like e.g. data:, then we block the load. + bool isInsecureDownload = + !nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin( + contentLocation) && + !nsMixedContentBlocker::URISafeToBeLoadedInSecureContext( + contentLocation); + + Telemetry::Accumulate(mozilla::Telemetry::INSECURE_DOWNLOADS, + isInsecureDownload); + + if (isInsecureDownload) { + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); + if (httpChannel) { + LogMessageToConsole(httpChannel, "BlockedInsecureDownload"); + } + return nsITransfer::DOWNLOAD_POTENTIALLY_UNSAFE; } - return nsITransfer::DOWNLOAD_POTENTIALLY_UNSAFE; } if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal()) { diff --git a/dom/security/nsHTTPSOnlyUtils.cpp b/dom/security/nsHTTPSOnlyUtils.cpp index 2a3880ba70..535efaba4e 100644 --- a/dom/security/nsHTTPSOnlyUtils.cpp +++ b/dom/security/nsHTTPSOnlyUtils.cpp @@ -6,6 +6,8 @@ #include "mozilla/Components.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/glean/GleanMetrics.h" #include "mozilla/NullPrincipal.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/net/DNS.h" @@ -438,7 +440,7 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI, // We can upgrade the request - let's log to the console and set the status // so we know that we upgraded the request. if (aLoadInfo->GetWasSchemelessInput() && - mozilla::StaticPrefs::dom_security_https_first_schemeless()) { + !IsHttpsFirstModeEnabled(isPrivateWin)) { nsAutoCString urlCString; aURI->GetSpec(urlCString); NS_ConvertUTF8toUTF16 urlString(urlCString); @@ -447,6 +449,8 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI, nsHTTPSOnlyUtils::LogLocalizedString("HTTPSFirstSchemeless", params, nsIScriptError::warningFlag, aLoadInfo, aURI, true); + + mozilla::glean::httpsfirst::upgraded_schemeless.Add(); } else { nsAutoCString scheme; @@ -461,7 +465,12 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI, isSpeculative ? "HTTPSOnlyUpgradeSpeculativeConnection" : "HTTPSOnlyUpgradeRequest", params, nsIScriptError::warningFlag, aLoadInfo, aURI, true); + + if (!isSpeculative) { + mozilla::glean::httpsfirst::upgraded.Add(); + } } + // Set flag so we know that we upgraded the request httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST; aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); @@ -470,9 +479,11 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI, /* static */ already_AddRefed<nsIURI> -nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(nsIChannel* aChannel, - nsresult aStatus) { - nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); +nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest( + mozilla::net::DocumentLoadListener* aDocumentLoadListener, + nsresult aStatus) { + nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel(); + nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); // Only downgrade if we this request was upgraded using HTTPS-First Mode if (!(httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) { @@ -488,7 +499,7 @@ nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(nsIChannel* aChannel, // to check each NS_OK for those errors. // Only downgrade an NS_OK status if it is an 4xx or 5xx error. if (NS_SUCCEEDED(aStatus)) { - nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); // If no httpChannel exists we have nothing to do here. if (!httpChannel) { return nullptr; @@ -532,7 +543,7 @@ nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(nsIChannel* aChannel, } nsCOMPtr<nsIURI> uri; - nsresult rv = aChannel->GetURI(getter_AddRefs(uri)); + nsresult rv = channel->GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, nullptr); nsAutoCString spec; @@ -584,6 +595,33 @@ nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(nsIChannel* aChannel, nsIScriptError::warningFlag, loadInfo, uri, true); + // Record telemety + nsDOMNavigationTiming* timing = aDocumentLoadListener->GetTiming(); + if (timing) { + mozilla::TimeStamp navigationStart = timing->GetNavigationStartTimeStamp(); + if (navigationStart) { + mozilla::TimeDuration duration = + mozilla::TimeStamp::Now() - navigationStart; + bool isPrivateWin = + loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0; + + if (loadInfo->GetWasSchemelessInput() && + !IsHttpsFirstModeEnabled(isPrivateWin)) { + mozilla::glean::httpsfirst::downgraded_schemeless.Add(); + if (timing) { + mozilla::glean::httpsfirst::downgrade_time_schemeless + .AccumulateRawDuration(duration); + } + } else { + mozilla::glean::httpsfirst::downgraded.Add(); + if (timing) { + mozilla::glean::httpsfirst::downgrade_time.AccumulateRawDuration( + duration); + } + } + } + } + return newURI.forget(); } @@ -954,6 +992,19 @@ TestHTTPAnswerRunnable::OnStartRequest(nsIRequest* aRequest) { nsresult httpsOnlyChannelStatus; httpsOnlyChannel->GetStatus(&httpsOnlyChannelStatus); if (httpsOnlyChannelStatus == NS_OK) { + bool isPrivateWin = + loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0; + if (!nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(isPrivateWin)) { + // Record HTTPS-First Telemetry + if (loadInfo->GetWasSchemelessInput() && + !nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin)) { + mozilla::glean::httpsfirst::downgraded_on_timer_schemeless + .AddToNumerator(); + } else { + mozilla::glean::httpsfirst::downgraded_on_timer.AddToNumerator(); + } + } + httpsOnlyChannel->Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL); } } diff --git a/dom/security/nsHTTPSOnlyUtils.h b/dom/security/nsHTTPSOnlyUtils.h index 73a5219082..7e36bfadbd 100644 --- a/dom/security/nsHTTPSOnlyUtils.h +++ b/dom/security/nsHTTPSOnlyUtils.h @@ -95,12 +95,13 @@ class nsHTTPSOnlyUtils { /** * Determines if the request was previously upgraded with HTTPS-First, creates * a downgraded URI and logs to console. - * @param aStatus Status code - * @param aChannel Failed channel - * @return URI with http-scheme or nullptr + * @param aStatus Status code + * @param aDocumentLoadListener Failed document load listener + * @return URI with http-scheme or nullptr */ static already_AddRefed<nsIURI> PotentiallyDowngradeHttpsFirstRequest( - nsIChannel* aChannel, nsresult aStatus); + mozilla::net::DocumentLoadListener* aDocumentLoadListener, + nsresult aStatus); /** * Checks if the error code is on a block-list of codes that are probably diff --git a/dom/security/test/csp/file_csp_error_messages.html b/dom/security/test/csp/file_csp_error_messages.html new file mode 100644 index 0000000000..65d26ac57e --- /dev/null +++ b/dom/security/test/csp/file_csp_error_messages.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <meta http-equiv="Content-Security-Policy" content="default-src 'nonce-abc';"> + <title></title> +</head> + +<!-- event handler --> +<body onload="alert('onload');"> + + <!-- img-src --> + <img src="image.png"> + + <!-- external script --> + <script src=script.js></script> + + <!-- inline script --> + <script> + alert("failure"); + </script> + + <script nonce="abc"> + /* worker-src */ + new Worker("/worker.js") + </script> + + <script nonce="abc"> + // eslint-disable-next-line no-eval + eval("hello world"); + </script> +</body> +</html>
\ No newline at end of file diff --git a/dom/security/test/csp/mochitest.toml b/dom/security/test/csp/mochitest.toml index 8d8c6c31f5..5dd9a14222 100644 --- a/dom/security/test/csp/mochitest.toml +++ b/dom/security/test/csp/mochitest.toml @@ -433,6 +433,9 @@ skip-if = [ ["test_connect-src.html"] +["test_csp_error_messages.html"] +support-files = ["file_csp_error_messages.html"] + ["test_csp_frame_ancestors_about_blank.html"] support-files = [ "file_csp_frame_ancestors_about_blank.html", diff --git a/dom/security/test/csp/test_csp_error_messages.html b/dom/security/test/csp/test_csp_error_messages.html new file mode 100644 index 0000000000..51be37e7c0 --- /dev/null +++ b/dom/security/test/csp/test_csp_error_messages.html @@ -0,0 +1,75 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>Test some specialized CSP errors</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<iframe id="cspframe"></iframe> + +<script class="testbody" type="text/javascript"> +SimpleTest.waitForExplicitFinish(); + +function cleanup() { + SpecialPowers.postConsoleSentinel(); + SimpleTest.finish(); +}; + +let errors = []; +function add(name) { + ok(!errors.includes(name), `duplicate error for ${name}`); + errors.push(name); +} + +SpecialPowers.registerConsoleListener(msg => { + if (!msg.errorMessage) { + return; + } + + let {errorMessage} = msg; + function contains(str) { + ok(errorMessage.includes(str), `error message contains "${str}"`); + } + + if (errorMessage.includes("(script-src-attr)")) { + contains("blocked an event handler"); + contains("from being executed"); + contains("Source: alert('onload');"); + add("event handler"); + } else if (errorMessage.includes("(img-src)")) { + contains("blocked the loading of a resource"); + contains("/image.png"); + add("image"); + } else if (errorMessage.includes("an inline script")) { + contains("(script-src-elem)"); + contains("from being executed"); + add("inline script"); + } else if (errorMessage.includes("a script")) { + contains("(script-src-elem)"); + contains("from being executed"); + contains("/script.js"); + add("script"); + } else if (errorMessage.includes("(worker-src)")) { + contains("(worker-src)"); + contains("from being executed"); + contains("/worker.js"); + add("worker"); + } else if (errorMessage.includes("a JavaScript eval")) { + contains("(script-src)"); + contains("from being executed"); + contains("Missing 'unsafe-eval'") + add("eval"); + } + + if (errors.length == 6) { + SimpleTest.executeSoon(cleanup); + } +}); + +document.getElementById('cspframe').src = 'file_csp_error_messages.html'; +</script> +</body> +</html> diff --git a/dom/security/test/general/browser.toml b/dom/security/test/general/browser.toml index 0f4ec5b224..c6d6b4bf79 100644 --- a/dom/security/test/general/browser.toml +++ b/dom/security/test/general/browser.toml @@ -48,6 +48,16 @@ support-files = [ "file_gpc_server.sjs", ] +["browser_test_http_download.js"] +skip-if = [ + "win11_2009", # Bug 1784764 + "os == 'linux' && !debug", +] +support-files = [ + "http_download_page.html", + "http_download_server.sjs" +] + ["browser_test_referrer_loadInOtherProcess.js"] ["browser_test_report_blocking.js"] diff --git a/dom/security/test/general/browser_restrict_privileged_about_script.js b/dom/security/test/general/browser_restrict_privileged_about_script.js index 0baa6e3d4d..7dfb6d691a 100644 --- a/dom/security/test/general/browser_restrict_privileged_about_script.js +++ b/dom/security/test/general/browser_restrict_privileged_about_script.js @@ -20,7 +20,7 @@ add_task(async function test_principal_click() { }); await BrowserTestUtils.withNewTab( "about:test-about-privileged-with-scripts", - async function (browser) { + async function () { // Wait for page to fully load info("Waiting for tab to be loaded.."); // let's look into the fully loaded about page diff --git a/dom/security/test/general/browser_test_data_download.js b/dom/security/test/general/browser_test_data_download.js index df5a8aeac4..9cebb97b30 100644 --- a/dom/security/test/general/browser_test_data_download.js +++ b/dom/security/test/general/browser_test_data_download.js @@ -22,13 +22,13 @@ function addWindowListener(aURL) { resolve(domwindow); }, domwindow); }, - onCloseWindow(aXULWindow) {}, + onCloseWindow() {}, }); }); } function waitDelay(delay) { - return new Promise((resolve, reject) => { + return new Promise(resolve => { /* eslint-disable mozilla/no-arbitrary-setTimeout */ window.setTimeout(resolve, delay); }); diff --git a/dom/security/test/general/browser_test_data_text_csv.js b/dom/security/test/general/browser_test_data_text_csv.js index 9855ddce46..b6c9f46336 100644 --- a/dom/security/test/general/browser_test_data_text_csv.js +++ b/dom/security/test/general/browser_test_data_text_csv.js @@ -6,7 +6,7 @@ const kTestPath = getRootDirectory(gTestPath).replace( ); const kTestURI = kTestPath + "file_data_text_csv.html"; -function addWindowListener(aURL, aCallback) { +function addWindowListener(aURL) { return new Promise(resolve => { Services.wm.addListener({ onOpenWindow(aXULWindow) { @@ -22,7 +22,7 @@ function addWindowListener(aURL, aCallback) { resolve(domwindow); }, domwindow); }, - onCloseWindow(aXULWindow) {}, + onCloseWindow() {}, }); }); } diff --git a/dom/security/test/general/browser_test_http_download.js b/dom/security/test/general/browser_test_http_download.js new file mode 100644 index 0000000000..35e3fdfc4b --- /dev/null +++ b/dom/security/test/general/browser_test_http_download.js @@ -0,0 +1,275 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +ChromeUtils.defineESModuleGetters(this, { + Downloads: "resource://gre/modules/Downloads.sys.mjs", + DownloadsCommon: "resource:///modules/DownloadsCommon.sys.mjs", +}); + +const HandlerService = Cc[ + "@mozilla.org/uriloader/handler-service;1" +].getService(Ci.nsIHandlerService); + +const MIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + +// Using insecure HTTP URL for a test cases around HTTP downloads +let INSECURE_BASE_URL = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/" + ) + "http_download_page.html"; + +function promiseFocus() { + return new Promise(resolve => { + waitForFocus(resolve); + }); +} + +async function task_openPanel() { + await promiseFocus(); + + let promise = BrowserTestUtils.waitForPopupEvent( + DownloadsPanel.panel, + "shown" + ); + DownloadsPanel.showPanel(); + await promise; +} + +const downloadMonitoringView = { + _listeners: [], + onDownloadAdded(download) { + for (let listener of this._listeners) { + listener(download); + } + this._listeners = []; + }, + waitForDownload(listener) { + this._listeners.push(listener); + }, +}; + +/** + * Waits until a download is triggered. + * Unless the always_ask_before_handling_new_types pref is true, the download + * will simply be saved, so resolve when the view is notified of the new + * download. Otherwise, it waits until a prompt is shown, selects the choosen + * <action>, then accepts the dialog + * @param [action] Which action to select, either: + * "handleInternally", "save" or "open". + * @returns {Promise} Resolved once done. + */ + +function shouldTriggerDownload(action = "save") { + if ( + Services.prefs.getBoolPref( + "browser.download.always_ask_before_handling_new_types" + ) + ) { + return new Promise((resolve, reject) => { + Services.wm.addListener({ + onOpenWindow(xulWin) { + Services.wm.removeListener(this); + let win = xulWin.docShell.domWindow; + waitForFocus(() => { + if ( + win.location == + "chrome://mozapps/content/downloads/unknownContentType.xhtml" + ) { + let dialog = win.document.getElementById("unknownContentType"); + let button = dialog.getButton("accept"); + let actionRadio = win.document.getElementById(action); + actionRadio.click(); + button.disabled = false; + dialog.acceptDialog(); + resolve(); + } else { + reject(); + } + }, win); + }, + }); + }); + } + return new Promise(res => { + downloadMonitoringView.waitForDownload(res); + }); +} + +const CONSOLE_ERROR_MESSAGE = "We blocked a download that’s not secure"; + +function shouldConsoleError() { + // Waits until CONSOLE_ERROR_MESSAGE was logged + return new Promise((resolve, reject) => { + function listener(msgObj) { + let text = msgObj.message; + if (text.includes(CONSOLE_ERROR_MESSAGE)) { + Services.console.unregisterListener(listener); + resolve(); + } + } + Services.console.registerListener(listener); + }); +} + +async function resetDownloads() { + // Removes all downloads from the download List + const types = new Set(); + let publicList = await Downloads.getList(Downloads.PUBLIC); + let downloads = await publicList.getAll(); + for (let download of downloads) { + if (download.contentType) { + types.add(download.contentType); + } + publicList.remove(download); + await download.finalize(true); + } + + if (types.size) { + // reset handlers for the contentTypes of any files previously downloaded + for (let type of types) { + const mimeInfo = MIMEService.getFromTypeAndExtension(type, ""); + info("resetting handler for type: " + type); + HandlerService.remove(mimeInfo); + } + } +} + +function shouldNotifyDownloadUI() { + return new Promise(res => { + downloadMonitoringView.waitForDownload(async aDownload => { + let { error } = aDownload; + if ( + error.becauseBlockedByReputationCheck && + error.reputationCheckVerdict == Downloads.Error.BLOCK_VERDICT_INSECURE + ) { + // It's an insecure Download, now Check that it has been cleaned up properly + if ((await IOUtils.stat(aDownload.target.path)).size != 0) { + throw new Error(`Download target is not empty!`); + } + if ((await IOUtils.stat(aDownload.target.path)).size != 0) { + throw new Error(`Download partFile was not cleaned up properly`); + } + // Assert that the Referrer is presnt + if (!aDownload.source.referrerInfo) { + throw new Error("The Blocked download is missing the ReferrerInfo"); + } + + res(aDownload); + } else { + ok(false, "No error for download that was expected to error!"); + } + }); + }); +} + +async function runTest(url, link, checkFunction, description) { + await SpecialPowers.pushPrefEnv({ + set: [["dom.block_download_insecure", true]], + }); + await resetDownloads(); + + let tab = BrowserTestUtils.addTab(gBrowser, url); + gBrowser.selectedTab = tab; + + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + info("Checking: " + description); + + let checkPromise = checkFunction(); + // Click the Link to trigger the download + SpecialPowers.spawn(gBrowser.selectedBrowser, [link], contentLink => { + content.document.getElementById(contentLink).click(); + }); + + await checkPromise; + + ok(true, description); + BrowserTestUtils.removeTab(tab); + + await SpecialPowers.popPrefEnv(); +} + +add_setup(async () => { + let list = await Downloads.getList(Downloads.ALL); + list.addView(downloadMonitoringView); + registerCleanupFunction(() => list.removeView(downloadMonitoringView)); +}); + +// Test Blocking +add_task(async function test_blocking() { + for (let prefVal of [true, false]) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.download.always_ask_before_handling_new_types", prefVal]], + }); + await runTest( + INSECURE_BASE_URL, + "http-link", + () => + Promise.all([ + shouldTriggerDownload(), + shouldNotifyDownloadUI(), + shouldConsoleError(), + ]), + "Insecure (HTTP) toplevel -> Insecure (HTTP) download should Error" + ); + await SpecialPowers.popPrefEnv(); + } +}); + +// Test Manual Unblocking +add_task(async function test_manual_unblocking() { + for (let prefVal of [true, false]) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.download.always_ask_before_handling_new_types", prefVal]], + }); + await runTest( + INSECURE_BASE_URL, + "http-link", + async () => { + let [, download] = await Promise.all([ + shouldTriggerDownload(), + shouldNotifyDownloadUI(), + ]); + await download.unblock(); + Assert.equal( + download.error, + null, + "There should be no error after unblocking" + ); + }, + "A blocked download should succeed to download after a manual unblock" + ); + await SpecialPowers.popPrefEnv(); + } +}); + +// Test Unblock Download Visible +add_task(async function test_unblock_download_visible() { + for (let prefVal of [true, false]) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.download.always_ask_before_handling_new_types", prefVal]], + }); + await promiseFocus(); + await runTest( + INSECURE_BASE_URL, + "http-link", + async () => { + let panelHasOpened = BrowserTestUtils.waitForPopupEvent( + DownloadsPanel.panel, + "shown" + ); + info("awaiting that the download is triggered and added to the list"); + await Promise.all([shouldTriggerDownload(), shouldNotifyDownloadUI()]); + info("awaiting that the Download list shows itself"); + await panelHasOpened; + DownloadsPanel.hidePanel(); + ok(true, "The Download Panel should have opened on blocked download"); + }, + "A blocked download should open the download panel" + ); + await SpecialPowers.popPrefEnv(); + } +}); diff --git a/dom/security/test/general/browser_test_report_blocking.js b/dom/security/test/general/browser_test_report_blocking.js index ebd7514097..ab66f1d836 100644 --- a/dom/security/test/general/browser_test_report_blocking.js +++ b/dom/security/test/general/browser_test_report_blocking.js @@ -108,7 +108,7 @@ async function testReporting(test) { return iframe.browsingContext; }); - await SpecialPowers.spawn(frameBC, [type], async obj => { + await SpecialPowers.spawn(frameBC, [type], async () => { // Wait until the reporting UI is visible. await ContentTaskUtils.waitForCondition(() => { let reportUI = content.document.getElementById("blockingErrorReporting"); diff --git a/dom/security/test/general/browser_test_toplevel_data_navigations.js b/dom/security/test/general/browser_test_toplevel_data_navigations.js index 0e006f1fd2..cf7c116eba 100644 --- a/dom/security/test/general/browser_test_toplevel_data_navigations.js +++ b/dom/security/test/general/browser_test_toplevel_data_navigations.js @@ -15,7 +15,7 @@ add_task(async function test_nav_data_uri() { await SpecialPowers.pushPrefEnv({ set: [["security.data_uri.block_toplevel_data_uri_navigations", true]], }); - await BrowserTestUtils.withNewTab(kDataURI, async function (browser) { + await BrowserTestUtils.withNewTab(kDataURI, async function () { await SpecialPowers.spawn( gBrowser.selectedBrowser, [{ kDataBody }], diff --git a/dom/security/test/general/browser_test_view_image_data_navigation.js b/dom/security/test/general/browser_test_view_image_data_navigation.js index 90aace1e3e..6e4173e343 100644 --- a/dom/security/test/general/browser_test_view_image_data_navigation.js +++ b/dom/security/test/general/browser_test_view_image_data_navigation.js @@ -8,7 +8,7 @@ add_task(async function test_principal_right_click_open_link_in_new_tab() { const TEST_PAGE = getRootDirectory(gTestPath) + "file_view_image_data_navigation.html"; - await BrowserTestUtils.withNewTab(TEST_PAGE, async function (browser) { + await BrowserTestUtils.withNewTab(TEST_PAGE, async function () { let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); // simulate right-click->view-image @@ -43,7 +43,7 @@ add_task(async function test_right_click_open_bg_image() { const TEST_PAGE = getRootDirectory(gTestPath) + "file_view_bg_image_data_navigation.html"; - await BrowserTestUtils.withNewTab(TEST_PAGE, async function (browser) { + await BrowserTestUtils.withNewTab(TEST_PAGE, async function () { let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); // simulate right-click->view-image diff --git a/dom/security/test/general/http_download_page.html b/dom/security/test/general/http_download_page.html new file mode 100644 index 0000000000..c5461eaed3 --- /dev/null +++ b/dom/security/test/general/http_download_page.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Test for the download attribute</title> + </head> + <body> + hi + + <script> + const host = window.location.host; + const path = location.pathname.replace("http_download_page.html","http_download_server.sjs"); + + const insecureLink = document.createElement("a"); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + insecureLink.href=`http://${host}/${path}`; + insecureLink.download="true"; + insecureLink.id="http-link"; + insecureLink.textContent="Not secure Link"; + + document.body.append(insecureLink); + </script> + </body> +</html> diff --git a/dom/security/test/general/http_download_server.sjs b/dom/security/test/general/http_download_server.sjs new file mode 100644 index 0000000000..e659df2f40 --- /dev/null +++ b/dom/security/test/general/http_download_server.sjs @@ -0,0 +1,20 @@ +// force the Browser to Show a Download Prompt + +function handleRequest(request, response) { + let type = "image/png"; + let filename = "hello.png"; + request.queryString.split("&").forEach(val => { + var [key, value] = val.split("="); + if (key == "type") { + type = value; + } + if (key == "name") { + filename = value; + } + }); + + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Disposition", `attachment; filename=${filename}`); + response.setHeader("Content-Type", type); + response.write("🙈🙊🐵🙊"); +} diff --git a/dom/security/test/general/test_block_script_wrong_mime.html b/dom/security/test/general/test_block_script_wrong_mime.html index 93a4b9d220..7122363dfc 100644 --- a/dom/security/test/general/test_block_script_wrong_mime.html +++ b/dom/security/test/general/test_block_script_wrong_mime.html @@ -25,7 +25,7 @@ const MIMETypes = [ // <script src=""> function testScript([mime, shouldLoad]) { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { let script = document.createElement("script"); script.onload = () => { document.body.removeChild(script); @@ -44,7 +44,7 @@ function testScript([mime, shouldLoad]) { // new Worker() function testWorker([mime, shouldLoad]) { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { let worker = new Worker("file_block_script_wrong_mime_server.sjs?type=worker&mime="+mime); worker.onmessage = (event) => { ok(shouldLoad, `worker with mime '${mime}' should load`) @@ -62,7 +62,7 @@ function testWorker([mime, shouldLoad]) { // new Worker() with importScripts() function testWorkerImportScripts([mime, shouldLoad]) { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { let worker = new Worker("file_block_script_wrong_mime_server.sjs?type=worker-import&mime="+mime); worker.onmessage = (event) => { ok(shouldLoad, `worker/importScripts with mime '${mime}' should load`) diff --git a/dom/security/test/general/test_block_toplevel_data_navigation.html b/dom/security/test/general/test_block_toplevel_data_navigation.html index bbadacb218..1a1e6e8f8a 100644 --- a/dom/security/test/general/test_block_toplevel_data_navigation.html +++ b/dom/security/test/general/test_block_toplevel_data_navigation.html @@ -45,7 +45,7 @@ async function expectBlockedToplevelData() { } }; - function observer(subject, topic) { + function observer(subject) { if (!bcs.includes(subject.webProgress)) { bcs.push(subject.webProgress); subject.webProgress.addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_ALL); diff --git a/dom/security/test/general/test_bug1277803.xhtml b/dom/security/test/general/test_bug1277803.xhtml index 30cc82310b..8987219ed1 100644 --- a/dom/security/test/general/test_bug1277803.xhtml +++ b/dom/security/test/general/test_bug1277803.xhtml @@ -27,7 +27,7 @@ function runTest() { // Register our observer to intercept favicon requests. - function observer(aSubject, aTopic, aData) { + function observer(aSubject, aTopic) { // Make sure this is a favicon request. let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel); if (FAVICON_URI != httpChannel.URI.spec) { diff --git a/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html index 24ec5dbdd9..d0d702d606 100644 --- a/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html +++ b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html @@ -51,7 +51,7 @@ function createChromeScript() { return Ci.nsIContentPolicy.ACCEPT; }, - shouldProcess(contentLocation, loadInfo) { + shouldProcess() { return Ci.nsIContentPolicy.ACCEPT; } }; diff --git a/dom/security/test/general/test_meta_referrer.html b/dom/security/test/general/test_meta_referrer.html index f5e8b649f4..2871028869 100644 --- a/dom/security/test/general/test_meta_referrer.html +++ b/dom/security/test/general/test_meta_referrer.html @@ -24,7 +24,7 @@ function checkTestsDone() { var script = SpecialPowers.loadChromeScript(() => { /* eslint-env mozilla/chrome-script */ let counter = 0; - Services.obs.addObserver(function onExamResp(subject, topic, data) { + Services.obs.addObserver(function onExamResp(subject) { let channel = subject.QueryInterface(Ci.nsIHttpChannel); if (!channel.URI.spec.startsWith("https://example.com") || counter >= 2) { return; diff --git a/dom/security/test/general/test_same_site_cookies_subrequest.html b/dom/security/test/general/test_same_site_cookies_subrequest.html index 304dbafa9a..0975e49663 100644 --- a/dom/security/test/general/test_same_site_cookies_subrequest.html +++ b/dom/security/test/general/test_same_site_cookies_subrequest.html @@ -78,7 +78,7 @@ function checkResult(aCookieVal) { function setupQueryResultAndRunTest() { var myXHR = new XMLHttpRequest(); myXHR.open("GET", "file_same_site_cookies_subrequest.sjs?queryresult" + curTest); - myXHR.onload = function(e) { + myXHR.onload = function() { checkResult(myXHR.responseText); } myXHR.onerror = function(e) { diff --git a/dom/security/test/general/test_same_site_cookies_toplevel_nav.html b/dom/security/test/general/test_same_site_cookies_toplevel_nav.html index aba825916b..9ba625e4a3 100644 --- a/dom/security/test/general/test_same_site_cookies_toplevel_nav.html +++ b/dom/security/test/general/test_same_site_cookies_toplevel_nav.html @@ -83,7 +83,7 @@ function checkResult(aCookieVal) { function setupQueryResultAndRunTest() { var myXHR = new XMLHttpRequest(); myXHR.open("GET", "file_same_site_cookies_toplevel_nav.sjs?queryresult" + curTest); - myXHR.onload = function(e) { + myXHR.onload = function() { checkResult( myXHR.responseText); } myXHR.onerror = function(e) { diff --git a/dom/security/test/https-first/browser_download_attribute.js b/dom/security/test/https-first/browser_download_attribute.js index 8165add998..ea4558cc84 100644 --- a/dom/security/test/https-first/browser_download_attribute.js +++ b/dom/security/test/https-first/browser_download_attribute.js @@ -31,7 +31,7 @@ let msgCounter = 0; function shouldConsoleTryUpgradeAndError() { // Waits until CONSOLE_ERROR_MESSAGE was logged. // Checks if download was tried via http:// - return new Promise((resolve, reject) => { + return new Promise(resolve => { function listener(msgObj) { let text = msgObj.message; // Verify upgrade messages diff --git a/dom/security/test/https-first/browser_httpsfirst.js b/dom/security/test/https-first/browser_httpsfirst.js index 733474dcc1..c4437f6051 100644 --- a/dom/security/test/https-first/browser_httpsfirst.js +++ b/dom/security/test/https-first/browser_httpsfirst.js @@ -31,6 +31,7 @@ add_task(async function () { await SpecialPowers.pushPrefEnv({ set: [["dom.security.https_first", false]], }); + Services.fog.testResetFOG(); await runPrefTest( "http://example.com", @@ -42,6 +43,23 @@ add_task(async function () { set: [["dom.security.https_first", true]], }); + for (const key of [ + "upgraded", + "upgradedSchemeless", + "downgraded", + "downgradedSchemeless", + "downgradedOnTimer", + "downgradedOnTimerSchemeless", + "downgradeTime", + "downgradeTimeSchemeless", + ]) { + is( + Glean.httpsfirst[key].testGetValue(), + null, + `No telemetry should have been recorded yet for ${key}` + ); + } + await runPrefTest( "http://example.com", "Should upgrade upgradeable website", @@ -71,4 +89,21 @@ add_task(async function () { "Should downgrade after timeout.", "http://" ); + + info("Checking expected telemetry"); + is(Glean.httpsfirst.upgraded.testGetValue(), 5); + is(Glean.httpsfirst.upgradedSchemeless.testGetValue(), null); + is(Glean.httpsfirst.downgraded.testGetValue(), 3); + is(Glean.httpsfirst.downgradedSchemeless.testGetValue(), null); + is(Glean.httpsfirst.downgradedOnTimer.testGetValue().numerator, 1); + is(Glean.httpsfirst.downgradedOnTimerSchemeless.testGetValue(), null); + const downgradeSeconds = + Glean.httpsfirst.downgradeTime.testGetValue().sum / 1_000_000_000; + ok( + downgradeSeconds > 2 && downgradeSeconds < 30, + `Summed downgrade time should be above 2 and below 30 seconds (is ${downgradeSeconds.toFixed( + 2 + )}s)` + ); + is(null, Glean.httpsfirst.downgradeTimeSchemeless.testGetValue()); }); diff --git a/dom/security/test/https-first/browser_mixed_content_download.js b/dom/security/test/https-first/browser_mixed_content_download.js index 09ea64cea8..919470a78e 100644 --- a/dom/security/test/https-first/browser_mixed_content_download.js +++ b/dom/security/test/https-first/browser_mixed_content_download.js @@ -57,7 +57,7 @@ const DOWNLOAD_URL = // falls back since download is not available via https let msgCounter = 0; function shouldConsoleError() { - return new Promise((resolve, reject) => { + return new Promise(resolve => { function listener(msgObj) { let text = msgObj.message; if (text.includes(CONSOLE_UPGRADE_MESSAGE) && msgCounter == 0) { diff --git a/dom/security/test/https-first/browser_schemeless.js b/dom/security/test/https-first/browser_schemeless.js index 9687f15072..64b078983d 100644 --- a/dom/security/test/https-first/browser_schemeless.js +++ b/dom/security/test/https-first/browser_schemeless.js @@ -153,6 +153,7 @@ async function runTest(aInput, aDesc, aExpectedScheme) { add_task(async function () { requestLongerTimeout(10); + Services.fog.testResetFOG(); await SpecialPowers.pushPrefEnv({ set: [ @@ -183,9 +184,36 @@ add_task(async function () { "http" ); + for (const key of [ + "upgraded", + "upgradedSchemeless", + "downgraded", + "downgradedSchemeless", + "downgradedOnTimer", + "downgradedOnTimerSchemeless", + "downgradeTime", + "downgradeTimeSchemeless", + ]) { + is( + Glean.httpsfirst[key].testGetValue(), + null, + `No telemetry should have been recorded yet for ${key}` + ); + } + await runTest( "example.com", "Should upgrade upgradeable website without explicit scheme", "https" ); + + info("Checking expected telemetry"); + is(Glean.httpsfirst.upgraded.testGetValue(), null); + is(Glean.httpsfirst.upgradedSchemeless.testGetValue(), 5); + is(Glean.httpsfirst.downgraded.testGetValue(), null); + is(Glean.httpsfirst.downgradedSchemeless.testGetValue(), null); + is(Glean.httpsfirst.downgradedOnTimer.testGetValue(), null); + is(Glean.httpsfirst.downgradedOnTimerSchemeless.testGetValue(), null); + is(Glean.httpsfirst.downgradeTime.testGetValue(), null); + is(Glean.httpsfirst.downgradeTimeSchemeless.testGetValue(), null); }); diff --git a/dom/security/test/https-first/file_data_uri.html b/dom/security/test/https-first/file_data_uri.html index 69133e5079..e6d5744802 100644 --- a/dom/security/test/https-first/file_data_uri.html +++ b/dom/security/test/https-first/file_data_uri.html @@ -6,7 +6,7 @@ </head> <body> <script class="testbody" type="text/javascript"> - window.onload = (event) => { + window.onload = () => { let myLoc = window.location.href; window.opener.parent.postMessage({location: myLoc}, "*"); window.close(); diff --git a/dom/security/test/https-first/test_resource_upgrade.html b/dom/security/test/https-first/test_resource_upgrade.html index 66f65d9a04..275a6a23af 100644 --- a/dom/security/test/https-first/test_resource_upgrade.html +++ b/dom/security/test/https-first/test_resource_upgrade.html @@ -44,7 +44,7 @@ // returns after the server has received all the expected requests. var myXHR = new XMLHttpRequest(); myXHR.open("GET", "file_upgrade_insecure_server.sjs?queryresult"); - myXHR.onload = function (e) { + myXHR.onload = function () { var results = myXHR.responseText.split(","); for (var index in results) { checkResult(results[index]); diff --git a/dom/security/test/https-only/browser_hsts_host.js b/dom/security/test/https-only/browser_hsts_host.js index 858c19865c..c612606f72 100644 --- a/dom/security/test/https-only/browser_hsts_host.js +++ b/dom/security/test/https-only/browser_hsts_host.js @@ -146,7 +146,7 @@ add_task(async function () { Services.obs.removeObserver(observer, "http-on-examine-response"); }); -function observer(subject, topic, state) { +function observer(subject, topic) { info("observer called with " + topic); if (topic == "http-on-examine-response") { onExamineResponse(subject); diff --git a/dom/security/test/https-only/browser_save_as.js b/dom/security/test/https-only/browser_save_as.js index 309dd69c79..fbfdf276a8 100644 --- a/dom/security/test/https-only/browser_save_as.js +++ b/dom/security/test/https-only/browser_save_as.js @@ -14,7 +14,7 @@ const TEST_PATH = "http://example.com/browser/dom/security/test/https-only/file_save_as.html"; let MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(window.browsingContext); const tempDir = createTemporarySaveDirectory(); MockFilePicker.displayDirectory = tempDir; @@ -78,7 +78,7 @@ function createPromiseForTransferComplete() { } function createPromiseForConsoleError(message) { - return new Promise((resolve, reject) => { + return new Promise(resolve => { function listener(msgObj) { let text = msgObj.message; if (text.includes(message)) { @@ -155,7 +155,11 @@ async function setHttpsFirstAndOnlyPrefs(httpsFirst, httpsOnly) { add_task(async function testBaseline() { // Run with HTTPS-First and HTTPS-Only disabled await setHttpsFirstAndOnlyPrefs(false, false); - await runTest("#insecure-link", HTTP_LINK, undefined); + await runTest( + "#insecure-link", + HTTP_LINK, + "We blocked a download that’s not secure: “http://example.org/”." + ); await runTest("#secure-link", HTTPS_LINK, undefined); }); @@ -169,7 +173,7 @@ add_task(async function testHttpsFirst() { await runTest( "#insecure-link", HTTP_LINK, - "Blocked downloading insecure content “http://example.org/”." + "We blocked a download that’s not secure: “http://example.org/”." ); await runTest("#secure-link", HTTPS_LINK, undefined); }); @@ -181,7 +185,7 @@ add_task(async function testHttpsOnly() { await runTest( "#insecure-link", HTTP_LINK, - "Blocked downloading insecure content “http://example.org/”." + "We blocked a download that’s not secure: “http://example.org/”." ); await runTest("#secure-link", HTTPS_LINK, undefined); }); diff --git a/dom/security/test/https-only/browser_user_gesture.js b/dom/security/test/https-only/browser_user_gesture.js index e7d6a20318..9eec10bd88 100644 --- a/dom/security/test/https-only/browser_user_gesture.js +++ b/dom/security/test/https-only/browser_user_gesture.js @@ -23,7 +23,7 @@ add_task(async function () { // 1. Upgrade a page to https:// BrowserTestUtils.startLoadingURIString(browser, kTestURI); await loaded; - await ContentTask.spawn(browser, {}, async args => { + await ContentTask.spawn(browser, {}, async () => { ok( content.document.location.href.startsWith("https://"), "Should be https" diff --git a/dom/security/test/https-only/file_upgrade_insecure.html b/dom/security/test/https-only/file_upgrade_insecure.html index 346cfbeb9c..b2d3b89e3d 100644 --- a/dom/security/test/https-only/file_upgrade_insecure.html +++ b/dom/security/test/https-only/file_upgrade_insecure.html @@ -55,7 +55,7 @@ ); if (AppConstants.platform !== "android") { var mySocket = new WebSocket("ws://example.com/tests/dom/security/test/https-only/file_upgrade_insecure"); - mySocket.onopen = function(e) { + mySocket.onopen = function() { if (mySocket.url.includes("wss://")) { window.parent.postMessage({result: "websocket-ok"}, "*"); } diff --git a/dom/security/test/https-only/file_websocket_exceptions_iframe.html b/dom/security/test/https-only/file_websocket_exceptions_iframe.html index 23c6af2d45..d8d0046119 100644 --- a/dom/security/test/https-only/file_websocket_exceptions_iframe.html +++ b/dom/security/test/https-only/file_websocket_exceptions_iframe.html @@ -5,17 +5,17 @@ <script> window.addEventListener("message", receiveMessage); -function receiveMessage(event) { +function receiveMessage() { window.removeEventListener("message", receiveMessage); var mySocket = new WebSocket("ws://example.com/tests/dom/security/test/https-only/file_upgrade_insecure"); - mySocket.onopen = function(e) { + mySocket.onopen = function() { parent.dispatchEvent(new CustomEvent("WebSocketEnded", { detail: { url: mySocket.url, state: "onopen" } })); mySocket.close(); }; - mySocket.onerror = function(e) { + mySocket.onerror = function() { parent.dispatchEvent(new CustomEvent("WebSocketEnded", { detail: { url: mySocket.url, state: "onerror" } })); diff --git a/dom/security/test/https-only/test_redirect_upgrade.html b/dom/security/test/https-only/test_redirect_upgrade.html index 59f02f96d0..6cf96bd494 100644 --- a/dom/security/test/https-only/test_redirect_upgrade.html +++ b/dom/security/test/https-only/test_redirect_upgrade.html @@ -28,12 +28,12 @@ Test that 302 redirect requests get upgraded to https:// with HTTPS-Only Mode en // Make a request to a site (eg. https://file_redirect.sjs?301), which will redirect to http://file_redirect.sjs?check. // The response will either be secure-ok, if the request has been upgraded to https:// or secure-error if it didn't. myXHR.open("GET", `https://example.com/tests/dom/security/test/https-only/file_redirect.sjs?${currentCode}`); - myXHR.onload = (e) => { + myXHR.onload = () => { is(myXHR.responseText, "secure-ok", `a ${currentCode} redirect when posting violation report should be blocked`) testDone(); } // This should not happen - myXHR.onerror = (e) => { + myXHR.onerror = () => { ok(false, `Could not query results from server for ${currentCode}-redirect test (" + e.message + ")`); testDone(); } diff --git a/dom/security/test/https-only/test_resource_upgrade.html b/dom/security/test/https-only/test_resource_upgrade.html index 6584bad020..1be7fcc3dd 100644 --- a/dom/security/test/https-only/test_resource_upgrade.html +++ b/dom/security/test/https-only/test_resource_upgrade.html @@ -54,7 +54,7 @@ // returns after the server has received all the expected requests. var myXHR = new XMLHttpRequest(); myXHR.open("GET", "file_upgrade_insecure_server.sjs?queryresult"); - myXHR.onload = function (e) { + myXHR.onload = function () { var results = myXHR.responseText.split(","); for (var index in results) { checkResult(results[index]); diff --git a/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js b/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js index ee350008aa..b103d83cd7 100644 --- a/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js +++ b/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js @@ -101,7 +101,7 @@ function shouldTriggerDownload(action = "save") { }); } -const CONSOLE_ERROR_MESSAGE = "Blocked downloading insecure content"; +const CONSOLE_ERROR_MESSAGE = "We blocked a download that’s not secure"; function shouldConsoleError() { // Waits until CONSOLE_ERROR_MESSAGE was logged diff --git a/dom/security/test/referrer-policy/browser.toml b/dom/security/test/referrer-policy/browser.toml index 325b6a3f49..a77046c85b 100644 --- a/dom/security/test/referrer-policy/browser.toml +++ b/dom/security/test/referrer-policy/browser.toml @@ -1,8 +1,8 @@ [DEFAULT] support-files = ["referrer_page.sjs"] -["browser_fragment_navigation.js"] -support-files = ["file_fragment_navigation.sjs"] +["browser_session_history.js"] +support-files = ["file_session_history.sjs"] ["browser_referrer_disallow_cross_site_relaxing.js"] diff --git a/dom/security/test/referrer-policy/browser_fragment_navigation.js b/dom/security/test/referrer-policy/browser_session_history.js index c3d5e62854..b480ce4ff0 100644 --- a/dom/security/test/referrer-policy/browser_fragment_navigation.js +++ b/dom/security/test/referrer-policy/browser_session_history.js @@ -4,7 +4,7 @@ "use strict"; const TEST_FILE = - "https://example.com/browser/dom/security/test/referrer-policy/file_fragment_navigation.sjs"; + "https://example.com/browser/dom/security/test/referrer-policy/file_session_history.sjs"; add_task(async function test_browser_navigation() { await BrowserTestUtils.withNewTab(TEST_FILE, async browser => { @@ -37,6 +37,21 @@ add_task(async function test_browser_navigation() { content.document.getElementById("ok"), "Page should load when checking referrer after fragment navigation and reload" ); + + info("Clicking on push_state button"); + content.document.getElementById("push_state").click(); + }); + + info("Reloading tab"); + loadPromise = BrowserTestUtils.browserLoaded(browser); + await BrowserTestUtils.reloadTab(gBrowser.selectedTab); + await loadPromise; + + await SpecialPowers.spawn(browser, [], () => { + ok( + content.document.getElementById("ok"), + "Page should load when checking referrer after history.pushState and reload" + ); }); }); }); diff --git a/dom/security/test/referrer-policy/file_fragment_navigation.sjs b/dom/security/test/referrer-policy/file_session_history.sjs index 5fb6f0d826..3d5f06b026 100644 --- a/dom/security/test/referrer-policy/file_fragment_navigation.sjs +++ b/dom/security/test/referrer-policy/file_session_history.sjs @@ -6,16 +6,22 @@ function handleRequest(request, response) { request.queryString === "check_referrer" && (!request.hasHeader("referer") || request.getHeader("referer") !== - "https://example.com/browser/dom/security/test/referrer-policy/file_fragment_navigation.sjs") + "https://example.com/browser/dom/security/test/referrer-policy/file_session_history.sjs") ) { response.setStatusLine(request.httpVersion, 400, "Bad Request"); response.write("Did not receive referrer"); } else { response.setHeader("Content-Type", "text/html"); response.write( - `<span id="ok">OK</span> -<a id="check_referrer" href="?check_referrer">check_referrer</a> -<a id="fragment" href="#fragment">fragment</a>` + `<span id="ok">OK</span> + <a id="check_referrer" href="?check_referrer">check_referrer</a> + <a id="fragment" href="#fragment">fragment</a> + <script> + function pushState(){ + history.pushState({}, "", location); + } + </script> + <button id="push_state" onclick="pushState();" >push_state</button>` ); } } diff --git a/dom/security/test/referrer-policy/referrer_helper.js b/dom/security/test/referrer-policy/referrer_helper.js index b892017eef..92593fa907 100644 --- a/dom/security/test/referrer-policy/referrer_helper.js +++ b/dom/security/test/referrer-policy/referrer_helper.js @@ -61,7 +61,7 @@ function checkIndividualResults(aTestname, aExpectedReferrer, aName) { ); advance(); }; - var onerror = xhr => { + var onerror = () => { ok(false, "Can't get results from the counter server."); SimpleTest.finish(); }; @@ -69,7 +69,7 @@ function checkIndividualResults(aTestname, aExpectedReferrer, aName) { } function resetState() { - doXHR(RESET_STATE, advance, function (xhr) { + doXHR(RESET_STATE, advance, function () { ok(false, "error in reset state"); SimpleTest.finish(); }); diff --git a/dom/security/test/referrer-policy/test_img_referrer.html b/dom/security/test/referrer-policy/test_img_referrer.html index fcc80929d2..5e3a2e6ddf 100644 --- a/dom/security/test/referrer-policy/test_img_referrer.html +++ b/dom/security/test/referrer-policy/test_img_referrer.html @@ -64,7 +64,7 @@ function checkIndividualResults(aTestname, aExpectedImg, aName) { advance(); }, - function(xhr) { + function() { ok(false, "Can't get results from the counter server."); SimpleTest.finish(); }); @@ -73,7 +73,7 @@ function checkIndividualResults(aTestname, aExpectedImg, aName) { function resetState() { doXHR('/tests/dom/security/test/referrer-policy/img_referrer_testserver.sjs?action=resetState', advance, - function(xhr) { + function() { ok(false, "error in reset state"); SimpleTest.finish(); }); diff --git a/dom/security/test/sec-fetch/browser_external_loads.js b/dom/security/test/sec-fetch/browser_external_loads.js index 0340b46899..070342e800 100644 --- a/dom/security/test/sec-fetch/browser_external_loads.js +++ b/dom/security/test/sec-fetch/browser_external_loads.js @@ -7,7 +7,7 @@ const TEST_PATH = getRootDirectory(gTestPath).replace( var gExpectedHeader = {}; -function checkSecFetchUser(subject, topic, data) { +function checkSecFetchUser(subject) { let channel = subject.QueryInterface(Ci.nsIHttpChannel); if (!channel.URI.spec.startsWith("https://example.com")) { return; @@ -45,7 +45,7 @@ add_task(async function external_load() { Services.obs.addObserver(checkSecFetchUser, "http-on-stop-request"); let headersChecked = new Promise(resolve => { - let reqStopped = async (subject, topic, data) => { + let reqStopped = async () => { Services.obs.removeObserver(reqStopped, "http-on-stop-request"); resolve(); }; diff --git a/dom/security/test/sec-fetch/browser_navigation.js b/dom/security/test/sec-fetch/browser_navigation.js index d203391356..2d51a7d1f5 100644 --- a/dom/security/test/sec-fetch/browser_navigation.js +++ b/dom/security/test/sec-fetch/browser_navigation.js @@ -10,7 +10,7 @@ async function setup() { waitForExplicitFinish(); } -function checkSecFetchUser(subject, topic, data) { +function checkSecFetchUser(subject) { let channel = subject.QueryInterface(Ci.nsIHttpChannel); if (!channel.URI.spec.startsWith("https://example.com/")) { return; diff --git a/dom/security/test/sec-fetch/test_iframe_history_manipulation.html b/dom/security/test/sec-fetch/test_iframe_history_manipulation.html index 5ec749bf4d..65d79fa530 100644 --- a/dom/security/test/sec-fetch/test_iframe_history_manipulation.html +++ b/dom/security/test/sec-fetch/test_iframe_history_manipulation.html @@ -18,7 +18,7 @@ let testFrame; var script = SpecialPowers.loadChromeScript(() => { /* eslint-env mozilla/chrome-script */ - Services.obs.addObserver(function onExamResp(subject, topic, data) { + Services.obs.addObserver(function onExamResp(subject) { let channel = subject.QueryInterface(Ci.nsIHttpChannel); info("request observed: " + channel.URI.spec); if (!channel.URI.spec.startsWith("https://example.org")) { diff --git a/dom/security/test/sec-fetch/test_iframe_src_metaRedirect.html b/dom/security/test/sec-fetch/test_iframe_src_metaRedirect.html index 28eae80226..d05ae4df70 100644 --- a/dom/security/test/sec-fetch/test_iframe_src_metaRedirect.html +++ b/dom/security/test/sec-fetch/test_iframe_src_metaRedirect.html @@ -25,7 +25,7 @@ let testPassCounter = 0; var script = SpecialPowers.loadChromeScript(() => { /* eslint-env mozilla/chrome-script */ - Services.obs.addObserver(function onExamResp(subject, topic, data) { + Services.obs.addObserver(function onExamResp(subject) { let channel = subject.QueryInterface(Ci.nsIHttpChannel); if (!channel.URI.spec.startsWith("https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs")) { return; diff --git a/dom/security/test/sec-fetch/test_iframe_srcdoc_metaRedirect.html b/dom/security/test/sec-fetch/test_iframe_srcdoc_metaRedirect.html index adee5afe84..1a3fa85603 100644 --- a/dom/security/test/sec-fetch/test_iframe_srcdoc_metaRedirect.html +++ b/dom/security/test/sec-fetch/test_iframe_srcdoc_metaRedirect.html @@ -25,7 +25,7 @@ let testPassCounter = 0; var script = SpecialPowers.loadChromeScript(() => { /* eslint-env mozilla/chrome-script */ - Services.obs.addObserver(function onExamResp(subject, topic, data) { + Services.obs.addObserver(function onExamResp(subject) { let channel = subject.QueryInterface(Ci.nsIHttpChannel); if (!channel.URI.spec.startsWith("https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs")) { return; diff --git a/dom/security/test/sec-fetch/test_iframe_window_open_metaRedirect.html b/dom/security/test/sec-fetch/test_iframe_window_open_metaRedirect.html index b532baeb5e..1dd7f8864e 100644 --- a/dom/security/test/sec-fetch/test_iframe_window_open_metaRedirect.html +++ b/dom/security/test/sec-fetch/test_iframe_window_open_metaRedirect.html @@ -26,7 +26,7 @@ let testWindow; var script = SpecialPowers.loadChromeScript(() => { /* eslint-env mozilla/chrome-script */ - Services.obs.addObserver(function onExamResp(subject, topic, data) { + Services.obs.addObserver(function onExamResp(subject) { let channel = subject.QueryInterface(Ci.nsIHttpChannel); if (!channel.URI.spec.startsWith("https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs")) { return; diff --git a/dom/security/test/sec-fetch/test_trustworthy_loopback.html b/dom/security/test/sec-fetch/test_trustworthy_loopback.html index 95ecac17ed..3b44895e77 100644 --- a/dom/security/test/sec-fetch/test_trustworthy_loopback.html +++ b/dom/security/test/sec-fetch/test_trustworthy_loopback.html @@ -23,7 +23,7 @@ function checkTestsDone() { var script = SpecialPowers.loadChromeScript(() => { /* eslint-env mozilla/chrome-script */ - Services.obs.addObserver(function onExamResp(subject, topic, data) { + Services.obs.addObserver(function onExamResp(subject) { let channel = subject.QueryInterface(Ci.nsIHttpChannel); if (!channel.URI.spec.includes("localhost") || channel.URI.spec.startsWith("http://localhost:9898/tests/dom/security/test/sec-fetch/file_trustworthy_loopback.html")) { diff --git a/dom/security/test/sec-fetch/test_websocket.html b/dom/security/test/sec-fetch/test_websocket.html index 5df0553a4f..4613419040 100644 --- a/dom/security/test/sec-fetch/test_websocket.html +++ b/dom/security/test/sec-fetch/test_websocket.html @@ -21,7 +21,7 @@ function checkTestsDone() { var script = SpecialPowers.loadChromeScript(() => { /* eslint-env mozilla/chrome-script */ - Services.obs.addObserver(function onExamResp(subject, topic, data) { + Services.obs.addObserver(function onExamResp(subject) { let channel = subject.QueryInterface(Ci.nsIHttpChannel); if (!channel.URI.spec.startsWith("https://example.com/tests/dom/security/test/sec-fetch/file_websocket")) { return; @@ -61,11 +61,11 @@ script.addMessageListener("test-end", () => { }); var wssSocket = new WebSocket("wss://example.com/tests/dom/security/test/sec-fetch/file_websocket"); -wssSocket.onopen = function(e) { +wssSocket.onopen = function() { ok(true, "sanity: wssSocket onopen"); checkTestsDone(); }; -wssSocket.onerror = function(e) { +wssSocket.onerror = function() { ok(false, "sanity: wssSocket onerror"); }; diff --git a/dom/security/test/unit/test_csp_reports.js b/dom/security/test/unit/test_csp_reports.js index 36da1a13e5..3d5a00b0f6 100644 --- a/dom/security/test/unit/test_csp_reports.js +++ b/dom/security/test/unit/test_csp_reports.js @@ -23,7 +23,7 @@ const REPORT_SERVER_URI = "http://localhost"; * or fails a test based on what it gets. */ function makeReportHandler(testpath, message, expectedJSON) { - return function (request, response) { + return function (request) { // we only like "POST" submissions for reports! if (request.method !== "POST") { do_throw("violation report should be a POST request"); diff --git a/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js b/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js index 26758d261d..1c5fdabf31 100644 --- a/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js +++ b/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js @@ -45,11 +45,11 @@ var tests = [ function ChannelListener() {} ChannelListener.prototype = { - onStartRequest(request) {}, - onDataAvailable(request, stream, offset, count) { + onStartRequest() {}, + onDataAvailable() { do_throw("Should not get any data!"); }, - onStopRequest(request, status) { + onStopRequest(request) { var upgrade_insecure_header = false; try { if (request.getRequestHeader("Upgrade-Insecure-Requests")) { @@ -76,7 +76,7 @@ function setupChannel(aContentType) { return chan; } -function serverHandler(metadata, response) { +function serverHandler() { // no need to perform anything here } diff --git a/dom/security/test/unit/test_https_only_https_first_default_port.js b/dom/security/test/unit/test_https_only_https_first_default_port.js index bd4d6717eb..06ffb80eee 100644 --- a/dom/security/test/unit/test_https_only_https_first_default_port.js +++ b/dom/security/test/unit/test_https_only_https_first_default_port.js @@ -41,13 +41,13 @@ const TESTS = [ function ChannelListener() {} ChannelListener.prototype = { - onStartRequest(request) { + onStartRequest() { // dummy implementation }, - onDataAvailable(request, stream, offset, count) { + onDataAvailable() { do_throw("Should not get any data!"); }, - onStopRequest(request, status) { + onStopRequest(request) { var chan = request.QueryInterface(Ci.nsIChannel); let requestURL = chan.URI; Assert.equal( @@ -79,7 +79,7 @@ function setUpChannel() { return chan; } -function serverHandler(metadata, response) { +function serverHandler() { // dummy implementation } diff --git a/dom/security/test/unit/test_https_only_https_first_prefs.js b/dom/security/test/unit/test_https_only_https_first_prefs.js index 9c6ced1fcb..6c7e112d9b 100644 --- a/dom/security/test/unit/test_https_only_https_first_prefs.js +++ b/dom/security/test/unit/test_https_only_https_first_prefs.js @@ -272,10 +272,10 @@ ChannelListener.prototype = { var authHeader = httpChan.getRequestHeader("Authorization"); Assert.equal(authHeader, "Basic user:pass", curTest.description); }, - onDataAvailable(request, stream, offset, count) { + onDataAvailable() { do_throw("Should not get any data!"); }, - onStopRequest(request, status) { + onStopRequest(request) { var chan = request.QueryInterface(Ci.nsIChannel); let requestURL = chan.URI; Assert.equal( @@ -331,7 +331,7 @@ function setUpChannel() { return chan; } -function serverHandler(metadata, response) { +function serverHandler() { // dummy implementation } diff --git a/dom/security/trusted-types/TrustedHTML.cpp b/dom/security/trusted-types/TrustedHTML.cpp new file mode 100644 index 0000000000..005cab8c63 --- /dev/null +++ b/dom/security/trusted-types/TrustedHTML.cpp @@ -0,0 +1,13 @@ +/* -*- 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 "mozilla/dom/TrustedHTML.h" + +namespace mozilla::dom { + +IMPL_TRUSTED_TYPE_CLASS(TrustedHTML) + +} // namespace mozilla::dom diff --git a/dom/security/trusted-types/TrustedHTML.h b/dom/security/trusted-types/TrustedHTML.h new file mode 100644 index 0000000000..68a642c83a --- /dev/null +++ b/dom/security/trusted-types/TrustedHTML.h @@ -0,0 +1,19 @@ +/* -*- 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 DOM_SECURITY_TRUSTED_TYPES_TRUSTEDHTML_H_ +#define DOM_SECURITY_TRUSTED_TYPES_TRUSTEDHTML_H_ + +#include "mozilla/dom/TrustedTypeUtils.h" + +namespace mozilla::dom { + +// https://w3c.github.io/trusted-types/dist/spec/#trusted-html +DECL_TRUSTED_TYPE_CLASS(TrustedHTML) + +} // namespace mozilla::dom + +#endif // DOM_SECURITY_TRUSTED_TYPES_TRUSTEDHTML_H_ diff --git a/dom/security/trusted-types/TrustedScript.cpp b/dom/security/trusted-types/TrustedScript.cpp new file mode 100644 index 0000000000..496c53278a --- /dev/null +++ b/dom/security/trusted-types/TrustedScript.cpp @@ -0,0 +1,13 @@ +/* -*- 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 "mozilla/dom/TrustedScript.h" + +namespace mozilla::dom { + +IMPL_TRUSTED_TYPE_CLASS(TrustedScript) + +} // namespace mozilla::dom diff --git a/dom/security/trusted-types/TrustedScript.h b/dom/security/trusted-types/TrustedScript.h new file mode 100644 index 0000000000..0b6e0965ed --- /dev/null +++ b/dom/security/trusted-types/TrustedScript.h @@ -0,0 +1,19 @@ +/* -*- 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 DOM_SECURITY_TRUSTED_TYPES_TRUSTEDSCRIPT_H_ +#define DOM_SECURITY_TRUSTED_TYPES_TRUSTEDSCRIPT_H_ + +#include "mozilla/dom/TrustedTypeUtils.h" + +namespace mozilla::dom { + +// https://w3c.github.io/trusted-types/dist/spec/#trusted-script +DECL_TRUSTED_TYPE_CLASS(TrustedScript) + +} // namespace mozilla::dom + +#endif // DOM_SECURITY_TRUSTED_TYPES_TRUSTEDSCRIPT_H_ diff --git a/dom/security/trusted-types/TrustedScriptURL.cpp b/dom/security/trusted-types/TrustedScriptURL.cpp new file mode 100644 index 0000000000..8de2448f91 --- /dev/null +++ b/dom/security/trusted-types/TrustedScriptURL.cpp @@ -0,0 +1,14 @@ +/* -*- 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 "mozilla/dom/TrustedScriptURL.h" + +namespace mozilla::dom { + +// https://w3c.github.io/trusted-types/dist/spec/#trused-script-url +IMPL_TRUSTED_TYPE_CLASS(TrustedScriptURL) + +} // namespace mozilla::dom diff --git a/dom/security/trusted-types/TrustedScriptURL.h b/dom/security/trusted-types/TrustedScriptURL.h new file mode 100644 index 0000000000..6e71235cb9 --- /dev/null +++ b/dom/security/trusted-types/TrustedScriptURL.h @@ -0,0 +1,18 @@ +/* -*- 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 DOM_SECURITY_TRUSTED_TYPES_TRUSTEDSCRIPTURL_H_ +#define DOM_SECURITY_TRUSTED_TYPES_TRUSTEDSCRIPTURL_H_ + +#include "mozilla/dom/TrustedTypeUtils.h" + +namespace mozilla::dom { + +DECL_TRUSTED_TYPE_CLASS(TrustedScriptURL) + +} // namespace mozilla::dom + +#endif // DOM_SECURITY_TRUSTED_TYPES_TRUSTEDSCRIPTURL_H_ diff --git a/dom/security/trusted-types/TrustedTypePolicy.cpp b/dom/security/trusted-types/TrustedTypePolicy.cpp new file mode 100644 index 0000000000..3c4e758ed0 --- /dev/null +++ b/dom/security/trusted-types/TrustedTypePolicy.cpp @@ -0,0 +1,43 @@ +/* -*- 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 "mozilla/dom/TrustedTypePolicy.h" + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/dom/TrustedTypePolicyFactory.h" +#include "mozilla/dom/TrustedTypesBinding.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TrustedTypePolicy, mParentObject) + +JSObject* TrustedTypePolicy::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return TrustedTypePolicy_Binding::Wrap(aCx, this, aGivenProto); +} + +UniquePtr<TrustedHTML> TrustedTypePolicy::CreateHTML( + JSContext* aJSContext, const nsAString& aInput, + const Sequence<JS::Value>& aArguments) const { + // TODO: implement the spec. + return MakeUnique<TrustedHTML>(); +} + +UniquePtr<TrustedScript> TrustedTypePolicy::CreateScript( + JSContext* aJSContext, const nsAString& aInput, + const Sequence<JS::Value>& aArguments) const { + // TODO: implement the spec. + return MakeUnique<TrustedScript>(); +} + +UniquePtr<TrustedScriptURL> TrustedTypePolicy::CreateScriptURL( + JSContext* aJSContext, const nsAString& aInput, + const Sequence<JS::Value>& aArguments) const { + // TODO: implement the spec. + return MakeUnique<TrustedScriptURL>(); +} + +} // namespace mozilla::dom diff --git a/dom/security/trusted-types/TrustedTypePolicy.h b/dom/security/trusted-types/TrustedTypePolicy.h new file mode 100644 index 0000000000..22d99947b3 --- /dev/null +++ b/dom/security/trusted-types/TrustedTypePolicy.h @@ -0,0 +1,72 @@ +/* -*- 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 DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEPOLICY_H_ +#define DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEPOLICY_H_ + +#include "js/TypeDecls.h" +#include "js/Value.h" +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/DOMString.h" +#include "mozilla/dom/TrustedHTML.h" +#include "mozilla/dom/TrustedScript.h" +#include "mozilla/dom/TrustedScriptURL.h" +#include "nsISupportsImpl.h" +#include "nsStringFwd.h" +#include "nsWrapperCache.h" + +namespace mozilla::dom { + +class TrustedTypePolicyFactory; + +// https://w3c.github.io/trusted-types/dist/spec/#trusted-type-policy +class TrustedTypePolicy : public nsWrapperCache { + public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(TrustedTypePolicy) + NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(TrustedTypePolicy) + + explicit TrustedTypePolicy(TrustedTypePolicyFactory* aParentObject) + : mParentObject{aParentObject} {} + + // Required for Web IDL binding. + TrustedTypePolicyFactory* GetParentObject() const { return mParentObject; } + + // Required for Web IDL binding. + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + + // https://w3c.github.io/trusted-types/dist/spec/#trustedtypepolicy-name + void GetName(DOMString& aResult) const { + // TODO: impl. + } + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicy-createhtml + UniquePtr<TrustedHTML> CreateHTML( + JSContext* aJSContext, const nsAString& aInput, + const Sequence<JS::Value>& aArguments) const; + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicy-createscript + UniquePtr<TrustedScript> CreateScript( + JSContext* aJSContext, const nsAString& aInput, + const Sequence<JS::Value>& aArguments) const; + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicy-createscripturl + UniquePtr<TrustedScriptURL> CreateScriptURL( + JSContext* aJSContext, const nsAString& aInput, + const Sequence<JS::Value>& aArguments) const; + + private: + // Required because this class is ref-counted. + virtual ~TrustedTypePolicy() = default; + + RefPtr<TrustedTypePolicyFactory> mParentObject; +}; + +} // namespace mozilla::dom + +#endif // DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEPOLICY_H_ diff --git a/dom/security/trusted-types/TrustedTypePolicyFactory.cpp b/dom/security/trusted-types/TrustedTypePolicyFactory.cpp new file mode 100644 index 0000000000..448c51eb3b --- /dev/null +++ b/dom/security/trusted-types/TrustedTypePolicyFactory.cpp @@ -0,0 +1,50 @@ +/* -*- 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 "mozilla/dom/TrustedTypePolicyFactory.h" + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/RefPtr.h" +#include "mozilla/dom/TrustedTypePolicy.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TrustedTypePolicyFactory, mGlobalObject) + +JSObject* TrustedTypePolicyFactory::WrapObject( + JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { + return TrustedTypePolicyFactory_Binding::Wrap(aCx, this, aGivenProto); +} + +already_AddRefed<TrustedTypePolicy> TrustedTypePolicyFactory::CreatePolicy( + const nsAString& aPolicyName, + const TrustedTypePolicyOptions& aPolicyOptions) { + // TODO: implement the spec. + return MakeRefPtr<TrustedTypePolicy>(this).forget(); +} + +UniquePtr<TrustedHTML> TrustedTypePolicyFactory::EmptyHTML() { + // Preserving the wrapper ensures: + // ``` + // const e = trustedTypes.emptyHTML; + // e === trustedTypes.emptyHTML; + // ``` + // which comes with the cost of keeping the factory, one per global, alive. + // An additional benefit is it saves the cost of re-instantiating potentially + // multiple emptyHML objects. Both, the JS- and the C++-objects. + dom::PreserveWrapper(this); + + return MakeUnique<TrustedHTML>(); +} + +UniquePtr<TrustedScript> TrustedTypePolicyFactory::EmptyScript() { + // See the explanation in `EmptyHTML()`. + dom::PreserveWrapper(this); + + return MakeUnique<TrustedScript>(); +} + +} // namespace mozilla::dom diff --git a/dom/security/trusted-types/TrustedTypePolicyFactory.h b/dom/security/trusted-types/TrustedTypePolicyFactory.h new file mode 100644 index 0000000000..fea5312cf8 --- /dev/null +++ b/dom/security/trusted-types/TrustedTypePolicyFactory.h @@ -0,0 +1,105 @@ +/* -*- 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 DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEPOLICYFACTORY_H_ +#define DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEPOLICYFACTORY_H_ + +#include "js/TypeDecls.h" +#include "mozilla/dom/TrustedHTML.h" +#include "mozilla/dom/TrustedScript.h" +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "nsIGlobalObject.h" +#include "nsISupportsImpl.h" +#include "nsStringFwd.h" +#include "nsWrapperCache.h" + +template <typename T> +struct already_AddRefed; + +class DOMString; + +namespace mozilla::dom { + +class TrustedTypePolicy; + +// https://w3c.github.io/trusted-types/dist/spec/#trusted-type-policy-factory +class TrustedTypePolicyFactory : public nsWrapperCache { + public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(TrustedTypePolicyFactory) + NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(TrustedTypePolicyFactory) + + explicit TrustedTypePolicyFactory(nsIGlobalObject* aGlobalObject) + : mGlobalObject{aGlobalObject} {} + + // Required for Web IDL binding. + nsIGlobalObject* GetParentObject() const { return mGlobalObject; } + + // Required for Web IDL binding. + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-createpolicy + already_AddRefed<TrustedTypePolicy> CreatePolicy( + const nsAString& aPolicyName, + const TrustedTypePolicyOptions& aPolicyOptions); + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-ishtml + bool IsHTML(JSContext* aJSContext, + const JS::Handle<JS::Value>& aValue) const { + // TODO: impl. + return false; + } + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-isscript + bool IsScript(JSContext* aJSContext, + const JS::Handle<JS::Value>& aValue) const { + // TODO: impl. + return false; + } + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-isscripturl + bool IsScriptURL(JSContext* aJSContext, + const JS::Handle<JS::Value>& aValue) const { + // TODO: impl. + return false; + } + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-emptyhtml + UniquePtr<TrustedHTML> EmptyHTML(); + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-emptyscript + UniquePtr<TrustedScript> EmptyScript(); + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-getattributetype + void GetAttributeType(const nsAString& aTagName, const nsAString& aAttribute, + const nsAString& aElementNs, const nsAString& aAttrNs, + DOMString& aResult) { + // TODO: impl. + } + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-getpropertytype + void GetPropertyType(const nsAString& aTagName, const nsAString& aProperty, + const nsAString& aElementNs, DOMString& aResult) { + // TODO: impl + } + + // https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-defaultpolicy + TrustedTypePolicy* GetDefaultPolicy() const { + // TODO: impl + return nullptr; + } + + private: + // Required because this class is ref-counted. + virtual ~TrustedTypePolicyFactory() = default; + + RefPtr<nsIGlobalObject> mGlobalObject; +}; + +} // namespace mozilla::dom + +#endif // DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEPOLICYFACTORY_H_ diff --git a/dom/security/trusted-types/TrustedTypeUtils.h b/dom/security/trusted-types/TrustedTypeUtils.h new file mode 100644 index 0000000000..90ffc50c38 --- /dev/null +++ b/dom/security/trusted-types/TrustedTypeUtils.h @@ -0,0 +1,35 @@ +/* -*- 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 DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEUTILS_H_ +#define DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEUTILS_H_ + +#include "mozilla/dom/DOMString.h" +#include "mozilla/dom/NonRefcountedDOMObject.h" +#include "mozilla/dom/TrustedTypesBinding.h" +#include "nsStringFwd.h" + +#define DECL_TRUSTED_TYPE_CLASS(_class) \ + class _class : public mozilla::dom::NonRefcountedDOMObject { \ + public: \ + /* Required for Web IDL binding. */ \ + bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, \ + JS::MutableHandle<JSObject*> aObject); \ + \ + void Stringify(nsAString& aResult) const { /* TODO: impl. */ \ + } \ + \ + void ToJSON(DOMString& aResult) const { /* TODO: impl. */ \ + } \ + }; + +#define IMPL_TRUSTED_TYPE_CLASS(_class) \ + bool _class::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, \ + JS::MutableHandle<JSObject*> aObject) { \ + return _class##_Binding::Wrap(aCx, this, aGivenProto, aObject); \ + } + +#endif // DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEUTILS_H_ diff --git a/dom/security/trusted-types/moz.build b/dom/security/trusted-types/moz.build new file mode 100644 index 0000000000..159a54ff02 --- /dev/null +++ b/dom/security/trusted-types/moz.build @@ -0,0 +1,27 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Core", "DOM: Security") + +EXPORTS.mozilla.dom += [ + "TrustedHTML.h", + "TrustedScript.h", + "TrustedScriptURL.h", + "TrustedTypePolicy.h", + "TrustedTypePolicyFactory.h", + "TrustedTypeUtils.h", +] + +UNIFIED_SOURCES += [ + "TrustedHTML.cpp", + "TrustedScript.cpp", + "TrustedScriptURL.cpp", + "TrustedTypePolicy.cpp", + "TrustedTypePolicyFactory.cpp", +] + +FINAL_LIBRARY = "xul" |