Bug 1621987: Implement Sec-Fetch-User. r=baku,edgar,Gijs

Differential Revision: https://phabricator.services.mozilla.com/D69392

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Christoph Kerschbaumer 2020-04-16 08:04:26 +00:00
parent 533b57acda
commit 7e43ad336f
20 changed files with 156 additions and 49 deletions

View File

@ -1484,6 +1484,8 @@ function _loadURI(browser, uri, params = {}) {
params || {};
let loadFlags =
params.loadFlags || params.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
let hasValidUserGestureActivation =
document.hasValidTransientUserGestureActivation;
if (!triggeringPrincipal) {
throw new Error("Must load with a triggering Principal");
@ -1525,6 +1527,7 @@ function _loadURI(browser, uri, params = {}) {
loadFlags,
referrerInfo,
postData,
hasValidUserGestureActivation,
};
try {
if (!mustChangeProcess) {

View File

@ -3937,6 +3937,9 @@ nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI,
loadState->SetLoadType(LOAD_ERROR_PAGE);
loadState->SetFirstParty(true);
loadState->SetSourceBrowsingContext(mBrowsingContext);
loadState->SetHasValidUserGestureActivation(
mBrowsingContext &&
mBrowsingContext->HasValidTransientUserGestureActivation());
return InternalLoad(loadState, nullptr, nullptr);
}
@ -4041,6 +4044,9 @@ nsDocShell::Reload(uint32_t aReloadFlags) {
loadState->SetSrcdocData(srcdoc);
loadState->SetSourceBrowsingContext(mBrowsingContext);
loadState->SetBaseURI(baseURI);
loadState->SetHasValidUserGestureActivation(
mBrowsingContext &&
mBrowsingContext->HasValidTransientUserGestureActivation());
rv = InternalLoad(loadState, nullptr, nullptr);
}
@ -5115,6 +5121,8 @@ nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
loadState->SetTriggeringPrincipal(principal);
if (doc) {
loadState->SetCsp(doc->GetCsp());
loadState->SetHasValidUserGestureActivation(
doc->HasValidTransientUserGestureActivation());
}
loadState->SetPrincipalIsExplicit(true);
@ -8273,6 +8281,9 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState,
loadState->SetForceAllowDataURI(
aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
loadState->SetHasValidUserGestureActivation(
aLoadState->HasValidUserGestureActivation());
rv = win->Open(NS_ConvertUTF8toUTF16(spec),
aLoadState->Target(), // window name
EmptyString(), // Features
@ -9758,6 +9769,15 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
Maybe<mozilla::dom::ServiceWorkerDescriptor>(),
sandboxFlags);
// in case this docshell load was triggered by a valid transient user gesture,
// or also the load originates from external, then we pass that information on
// to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
if (mBrowsingContext->HasValidTransientUserGestureActivation() ||
aLoadState->HasValidUserGestureActivation() ||
aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
loadInfo->SetHasValidUserGestureActivation(true);
}
/* Get the cache Key from SH */
uint32_t cacheKey = 0;
if (mLSHE) {
@ -12203,6 +12223,9 @@ nsresult nsDocShell::OnLinkClickSync(
loadState->SetFirstParty(true);
loadState->SetSourceBrowsingContext(mBrowsingContext);
loadState->SetIsFormSubmission(aContent->IsHTMLElement(nsGkAtoms::form));
loadState->SetHasValidUserGestureActivation(
mBrowsingContext &&
mBrowsingContext->HasValidTransientUserGestureActivation());
nsresult rv = InternalLoad(loadState, aDocShell, aRequest);
if (NS_SUCCEEDED(rv)) {

View File

@ -42,6 +42,7 @@ nsDocShellLoadState::nsDocShellLoadState(nsIURI* aURI)
mSrcdocData(VoidString()),
mLoadFlags(0),
mFirstParty(false),
mHasValidUserGestureActivation(false),
mTypeHint(VoidCString()),
mFileName(VoidString()),
mIsFromProcessingFrameAttributes(false) {
@ -64,6 +65,7 @@ nsDocShellLoadState::nsDocShellLoadState(
mTarget = aLoadState.Target();
mLoadFlags = aLoadState.LoadFlags();
mFirstParty = aLoadState.FirstParty();
mHasValidUserGestureActivation = aLoadState.HasValidUserGestureActivation();
mTypeHint = aLoadState.TypeHint();
mFileName = aLoadState.FileName();
mIsFromProcessingFrameAttributes =
@ -265,6 +267,8 @@ nsresult nsDocShellLoadState::CreateFromLoadURIOptions(
loadState->SetLoadFlags(extraFlags);
loadState->SetFirstParty(true);
loadState->SetHasValidUserGestureActivation(
aLoadURIOptions.mHasValidUserGestureActivation);
loadState->SetPostDataStream(postData);
loadState->SetHeadersStream(aLoadURIOptions.mHeaders);
loadState->SetBaseURI(aLoadURIOptions.mBaseURI);
@ -492,6 +496,15 @@ void nsDocShellLoadState::SetFirstParty(bool aFirstParty) {
mFirstParty = aFirstParty;
}
bool nsDocShellLoadState::HasValidUserGestureActivation() const {
return mHasValidUserGestureActivation;
}
void nsDocShellLoadState::SetHasValidUserGestureActivation(
bool aHasValidUserGestureActivation) {
mHasValidUserGestureActivation = aHasValidUserGestureActivation;
}
const nsCString& nsDocShellLoadState::TypeHint() const { return mTypeHint; }
void nsDocShellLoadState::SetTypeHint(const nsCString& aTypeHint) {
@ -662,6 +675,7 @@ DocShellLoadStateInit nsDocShellLoadState::Serialize() {
loadState.Target() = mTarget;
loadState.LoadFlags() = mLoadFlags;
loadState.FirstParty() = mFirstParty;
loadState.HasValidUserGestureActivation() = mHasValidUserGestureActivation;
loadState.TypeHint() = mTypeHint;
loadState.FileName() = mFileName;
loadState.IsFromProcessingFrameAttributes() =

View File

@ -177,6 +177,10 @@ class nsDocShellLoadState final {
void SetFirstParty(bool aFirstParty);
bool HasValidUserGestureActivation() const;
void SetHasValidUserGestureActivation(bool HasValidUserGestureActivation);
const nsCString& TypeHint() const;
void SetTypeHint(const nsCString& aTypeHint);
@ -355,6 +359,9 @@ class nsDocShellLoadState final {
// Is this a First Party Load?
bool mFirstParty;
// Is this load triggered by a user gesture?
bool mHasValidUserGestureActivation;
// A hint as to the content-type of the resulting data. If no hint, IsVoid()
// should return true.
nsCString mTypeHint;

View File

@ -103,6 +103,8 @@ already_AddRefed<nsDocShellLoadState> LocationBase::CheckURL(
if (referrerInfo) {
loadState->SetReferrerInfo(referrerInfo);
}
loadState->SetHasValidUserGestureActivation(
doc->HasValidTransientUserGestureActivation());
return loadState.forget();
}
@ -130,7 +132,10 @@ void LocationBase::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
nsContentUtils::CallerInnerWindow();
if (sourceWindow) {
loadState->SetSourceBrowsingContext(sourceWindow->GetBrowsingContext());
RefPtr<BrowsingContext> sourceBC = sourceWindow->GetBrowsingContext();
loadState->SetSourceBrowsingContext(sourceBC);
loadState->SetHasValidUserGestureActivation(
sourceBC && sourceBC->HasValidTransientUserGestureActivation());
}
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);

View File

@ -258,6 +258,8 @@ RefPtr<ClientOpPromise> ClientNavigateOpChild::DoNavigate(
loadState->SetSourceBrowsingContext(docShell->GetBrowsingContext());
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
loadState->SetFirstParty(true);
loadState->SetHasValidUserGestureActivation(
doc->HasValidTransientUserGestureActivation());
rv = docShell->LoadURI(loadState, false);
if (NS_FAILED(rv)) {
/// There are tests that try sending file:/// and mixed-content URLs

View File

@ -255,6 +255,7 @@ struct DocShellLoadStateInit
nsIURI BaseURI;
uint32_t LoadFlags;
bool FirstParty;
bool HasValidUserGestureActivation;
nsCString TypeHint;
nsString FileName;
bool IsFromProcessingFrameAttributes;

View File

@ -288,12 +288,24 @@ void SecFetch::AddSecFetchSite(nsIHttpChannel* aHTTPChannel) {
}
void SecFetch::AddSecFetchUser(nsIHttpChannel* aHTTPChannel) {
// TODO: Bug 1621987: Implement Sec-Fetch-User
nsCOMPtr<nsILoadInfo> loadInfo = aHTTPChannel->LoadInfo();
nsContentPolicyType externalType = loadInfo->GetExternalContentPolicyType();
// nsAutoCString user("?1");
// nsresult rv = aHTTPChannel->SetRequestHeader(
// NS_LITERAL_CSTRING("Sec-Fetch-User"), user, false);
// Unused << NS_WARN_IF(NS_FAILED(rv));
// sec-fetch-user only applies to loads of type document or subdocument
if (externalType != nsIContentPolicy::TYPE_DOCUMENT &&
externalType != nsIContentPolicy::TYPE_SUBDOCUMENT) {
return;
}
// sec-fetch-user only applies if the request is user triggered
if (!loadInfo->GetHasValidUserGestureActivation()) {
return;
}
nsAutoCString user("?1");
nsresult rv = aHTTPChannel->SetRequestHeader(
NS_LITERAL_CSTRING("Sec-Fetch-User"), user, false);
Unused << NS_WARN_IF(NS_FAILED(rv));
}
void SecFetch::AddSecFetchHeader(nsIHttpChannel* aHTTPChannel) {

View File

@ -62,6 +62,12 @@ dictionary LoadURIOptions {
*/
URI? baseURI = null;
/**
* Set to indicate that the URI to be loaded was triggered by a user
* action. (Mostly used in the context of Sec-Fetch-User).
*/
boolean hasValidUserGestureActivation = false;
/**
* If non-0, a value to pass to nsIDocShell::setCancelContentJSEpoch
* when initiating the load.

View File

@ -546,6 +546,7 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadInfo* aLoadInfo,
aLoadInfo->GetAllowListFutureDocumentsCreatedFromThisRedirectChain(),
cspNonce, aLoadInfo->GetSkipContentSniffing(),
aLoadInfo->GetHttpsOnlyStatus(),
aLoadInfo->GetHasValidUserGestureActivation(),
aLoadInfo->GetAllowDeprecatedSystemRequests(),
aLoadInfo->GetParserCreatedScript(),
aLoadInfo->GetIsFromProcessingFrameAttributes(), cookieJarSettingsArgs,
@ -747,6 +748,7 @@ nsresult LoadInfoArgsToLoadInfo(
loadInfoArgs.allowListFutureDocumentsCreatedFromThisRedirectChain(),
loadInfoArgs.cspNonce(), loadInfoArgs.skipContentSniffing(),
loadInfoArgs.httpsOnlyStatus(),
loadInfoArgs.hasValidUserGestureActivation(),
loadInfoArgs.allowDeprecatedSystemRequests(),
loadInfoArgs.parserCreatedScript(), loadInfoArgs.hasStoragePermission(),
loadInfoArgs.requestBlockingReason(), loadingContext);
@ -787,6 +789,7 @@ void LoadInfoToParentLoadInfoForwarder(
aLoadInfo->GetAllowInsecureRedirectToDataURI(),
aLoadInfo->GetBypassCORSChecks(), ipcController, tainting,
aLoadInfo->GetSkipContentSniffing(), aLoadInfo->GetHttpsOnlyStatus(),
aLoadInfo->GetHasValidUserGestureActivation(),
aLoadInfo->GetAllowDeprecatedSystemRequests(),
aLoadInfo->GetParserCreatedScript(),
aLoadInfo->GetServiceWorkerTaintingSynthesized(),
@ -827,6 +830,10 @@ nsresult MergeParentLoadInfoForwarder(
rv = aLoadInfo->SetHttpsOnlyStatus(aForwarderArgs.httpsOnlyStatus());
NS_ENSURE_SUCCESS(rv, rv);
rv = aLoadInfo->SetHasValidUserGestureActivation(
aForwarderArgs.hasValidUserGestureActivation());
NS_ENSURE_SUCCESS(rv, rv);
rv = aLoadInfo->SetAllowDeprecatedSystemRequests(
aForwarderArgs.allowDeprecatedSystemRequests());
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -106,6 +106,7 @@ LoadInfo::LoadInfo(
mAllowListFutureDocumentsCreatedFromThisRedirectChain(false),
mSkipContentSniffing(false),
mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED),
mHasValidUserGestureActivation(false),
mAllowDeprecatedSystemRequests(false),
mParserCreatedScript(false),
mHasStoragePermission(false),
@ -380,6 +381,7 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
mAllowListFutureDocumentsCreatedFromThisRedirectChain(false),
mSkipContentSniffing(false),
mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED),
mHasValidUserGestureActivation(false),
mAllowDeprecatedSystemRequests(false),
mParserCreatedScript(false),
mHasStoragePermission(false),
@ -484,6 +486,7 @@ LoadInfo::LoadInfo(dom::CanonicalBrowsingContext* aBrowsingContext,
mAllowListFutureDocumentsCreatedFromThisRedirectChain(false),
mSkipContentSniffing(false),
mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED),
mHasValidUserGestureActivation(false),
mAllowDeprecatedSystemRequests(false),
mParserCreatedScript(false),
mHasStoragePermission(false),
@ -777,6 +780,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
mCspNonce(rhs.mCspNonce),
mSkipContentSniffing(rhs.mSkipContentSniffing),
mHttpsOnlyStatus(rhs.mHttpsOnlyStatus),
mHasValidUserGestureActivation(rhs.mHasValidUserGestureActivation),
mAllowDeprecatedSystemRequests(rhs.mAllowDeprecatedSystemRequests),
mParserCreatedScript(rhs.mParserCreatedScript),
mHasStoragePermission(rhs.mHasStoragePermission),
@ -817,9 +821,10 @@ LoadInfo::LoadInfo(
bool aDocumentHasLoaded,
bool aAllowListFutureDocumentsCreatedFromThisRedirectChain,
const nsAString& aCspNonce, bool aSkipContentSniffing,
uint32_t aHttpsOnlyStatus, bool aAllowDeprecatedSystemRequests,
bool aParserCreatedScript, bool aHasStoragePermission,
uint32_t aRequestBlockingReason, nsINode* aLoadingContext)
uint32_t aHttpsOnlyStatus, bool aHasValidUserGestureActivation,
bool aAllowDeprecatedSystemRequests, bool aParserCreatedScript,
bool aHasStoragePermission, uint32_t aRequestBlockingReason,
nsINode* aLoadingContext)
: mLoadingPrincipal(aLoadingPrincipal),
mTriggeringPrincipal(aTriggeringPrincipal),
mPrincipalToInherit(aPrincipalToInherit),
@ -876,6 +881,7 @@ LoadInfo::LoadInfo(
mCspNonce(aCspNonce),
mSkipContentSniffing(aSkipContentSniffing),
mHttpsOnlyStatus(aHttpsOnlyStatus),
mHasValidUserGestureActivation(aHasValidUserGestureActivation),
mAllowDeprecatedSystemRequests(aAllowDeprecatedSystemRequests),
mParserCreatedScript(aParserCreatedScript),
mHasStoragePermission(aHasStoragePermission),
@ -1725,6 +1731,20 @@ LoadInfo::SetHttpsOnlyStatus(uint32_t aHttpsOnlyStatus) {
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetHasValidUserGestureActivation(
bool* aHasValidUserGestureActivation) {
*aHasValidUserGestureActivation = mHasValidUserGestureActivation;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetHasValidUserGestureActivation(
bool aHasValidUserGestureActivation) {
mHasValidUserGestureActivation = aHasValidUserGestureActivation;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetAllowDeprecatedSystemRequests(
bool* aAllowDeprecatedSystemRequests) {

View File

@ -169,9 +169,10 @@ class LoadInfo final : public nsILoadInfo {
bool aDocumentHasUserInteracted, bool aDocumentHasLoaded,
bool aAllowListFutureDocumentsCreatedFromThisRedirectChain,
const nsAString& aCspNonce, bool aSkipContentSniffing,
uint32_t aHttpsOnlyStatus, bool aAllowDeprecatedSystemRequests,
bool aParserCreatedScript, bool aHasStoragePermission,
uint32_t aRequestBlockingReason, nsINode* aLoadingContext);
uint32_t aHttpsOnlyStatus, bool aHasValidUserGestureActivation,
bool aAllowDeprecatedSystemRequests, bool aParserCreatedScript,
bool aHasStoragePermission, uint32_t aRequestBlockingReason,
nsINode* aLoadingContext);
LoadInfo(const LoadInfo& rhs);
NS_IMETHOD GetRedirects(JSContext* aCx,
@ -268,6 +269,7 @@ class LoadInfo final : public nsILoadInfo {
nsString mCspNonce;
bool mSkipContentSniffing;
uint32_t mHttpsOnlyStatus;
bool mHasValidUserGestureActivation;
bool mAllowDeprecatedSystemRequests;
bool mParserCreatedScript;
bool mHasStoragePermission;

View File

@ -606,6 +606,18 @@ TRRLoadInfo::SetHttpsOnlyStatus(uint32_t aHttpsOnlyStatus) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
TRRLoadInfo::GetHasValidUserGestureActivation(
bool* aHasValidUserGestureActivation) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
TRRLoadInfo::SetHasValidUserGestureActivation(
bool aHasValidUserGestureActivation) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
TRRLoadInfo::GetInternalContentPolicyType(nsContentPolicyType* aResult) {
*aResult = mInternalContentPolicyType;

View File

@ -457,6 +457,14 @@ interface nsILoadInfo : nsISupports
*/
[infallible] attribute unsigned long httpsOnlyStatus;
/**
* Returns true if at the time of the loadinfo construction the document
* that triggered this load has the bit hasValidTransientUserGestureActivation
* set or the load was triggered from External. (Mostly this bool is used
* in the context of Sec-Fetch-User.)
*/
[infallible] attribute boolean hasValidUserGestureActivation;
/**
* We disallow the SystemPrincipal to initiate requests to
* the public web. This flag is to allow exceptions.

View File

@ -289,19 +289,22 @@ already_AddRefed<LoadInfo> DocumentLoadListener::CreateLoadInfo(
securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
}
RefPtr<LoadInfo> loadInfo;
if (aBrowsingContext->GetParent()) {
// Build LoadInfo for TYPE_SUBDOCUMENT
RefPtr<LoadInfo> loadInfo =
new LoadInfo(aBrowsingContext, aLoadState->TriggeringPrincipal(),
aOuterWindowId, securityFlags, sandboxFlags);
return loadInfo.forget();
loadInfo = new LoadInfo(aBrowsingContext, aLoadState->TriggeringPrincipal(),
aOuterWindowId, securityFlags, sandboxFlags);
} else {
// Build LoadInfo for TYPE_DOCUMENT
OriginAttributes attrs;
mLoadContext->GetOriginAttributes(attrs);
loadInfo = new LoadInfo(aBrowsingContext, aLoadState->TriggeringPrincipal(),
attrs, aOuterWindowId, securityFlags, sandboxFlags);
}
// Build LoadInfo for TYPE_DOCUMENT
OriginAttributes attrs;
mLoadContext->GetOriginAttributes(attrs);
RefPtr<LoadInfo> loadInfo =
new LoadInfo(aBrowsingContext, aLoadState->TriggeringPrincipal(), attrs,
aOuterWindowId, securityFlags, sandboxFlags);
loadInfo->SetHasValidUserGestureActivation(
aLoadState->HasValidUserGestureActivation());
return loadInfo.forget();
}

View File

@ -146,6 +146,7 @@ struct LoadInfoArgs
nsString cspNonce;
bool skipContentSniffing;
uint32_t httpsOnlyStatus;
bool hasValidUserGestureActivation;
bool allowDeprecatedSystemRequests;
bool parserCreatedScript;
bool isFromProcessingFrameAttributes;
@ -189,6 +190,12 @@ struct ParentLoadInfoForwarderArgs
uint32_t httpsOnlyStatus;
// Returns true if at the time of the loadinfo construction the document
// that triggered this load has the bit hasValidTransientUserGestureActivation
// set or the load was triggered from External. (Mostly this bool is used
// in the context of Sec-Fetch-User.)
bool hasValidUserGestureActivation;
// The SystemPrincipal is disallowed to make requests to the public web
// and all requests will be cancelled. Setting this flag to true prevents
// the request from being cancelled.

View File

@ -1,9 +0,0 @@
[form.https.sub.html]
[web-platform.test -> web-platform.test:8443 iframe: user-activated: sec-fetch-user]
expected: FAIL
[web-platform.test -> www.web-platform.test:8443 iframe: user-activated: sec-fetch-user]
expected: FAIL
[web-platform.test -> www.not-web-platform.test:8443 iframe: user-activated: sec-fetch-user]
expected: FAIL

View File

@ -1,9 +0,0 @@
[iframe.https.sub.html]
[web-platform.test -> web-platform.test:8443 iframe: user-activated: sec-fetch-user]
expected: FAIL
[web-platform.test -> www.web-platform.test:8443 iframe: user-activated: sec-fetch-user]
expected: FAIL
[web-platform.test -> www.not-web-platform.test:8443 iframe: user-activated: sec-fetch-user]
expected: FAIL

View File

@ -1,16 +1,7 @@
[window-open.https.sub.html]
[Cross-site window, user-activated: sec-fetch-user]
expected: FAIL
[Cross-site window, forced, reloaded]
expected: [PASS, FAIL]
[Same-origin window, user-activated: sec-fetch-user]
expected: FAIL
[Same-site window, user-activated: sec-fetch-user]
expected: FAIL
[Same-site window, forced, reloaded]
expected:
if os == "android": ["PASS", "FAIL"]

View File

@ -1147,6 +1147,8 @@ nsresult nsWindowWatcher::OpenWindowInternal(
loadState = new nsDocShellLoadState(uriToLoad);
loadState->SetSourceBrowsingContext(parentBC);
loadState->SetHasValidUserGestureActivation(
parentBC && parentBC->HasValidTransientUserGestureActivation());
if (subjectPrincipal) {
loadState->SetTriggeringPrincipal(subjectPrincipal);