mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 04:09:50 +00:00
Bug 1872179 - Part 3: File picker file uploads consult content analysis r=rkraesig,win-reviewers
File picker upload operations check with any connected content analysis tool before giving access to the web page. Differential Revision: https://phabricator.services.mozilla.com/D198456
This commit is contained in:
parent
a2c4862570
commit
0e786b6070
@ -818,7 +818,8 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
|
||||
mode = nsIFilePicker::modeOpen;
|
||||
}
|
||||
|
||||
nsresult rv = filePicker->Init(win, title, mode);
|
||||
nsresult rv =
|
||||
filePicker->Init(win, title, mode, OwnerDoc()->GetBrowsingContext());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!okButtonLabel.IsEmpty()) {
|
||||
|
@ -11,11 +11,12 @@
|
||||
#include "nsIFile.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/FileBlobImpl.h"
|
||||
#include "mozilla/dom/FileSystemSecurity.h"
|
||||
#include "mozilla/dom/BrowserParent.h"
|
||||
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/BrowserParent.h"
|
||||
#include "mozilla/dom/FileBlobImpl.h"
|
||||
#include "mozilla/dom/FileSystemSecurity.h"
|
||||
#include "mozilla/dom/IPCBlobUtils.h"
|
||||
|
||||
using mozilla::Unused;
|
||||
@ -223,7 +224,9 @@ bool FilePickerParent::CreateFilePicker() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Element* element = BrowserParent::GetFrom(Manager())->GetOwnerElement();
|
||||
auto* browserParent = BrowserParent::GetFrom(Manager());
|
||||
auto* browsingContext = browserParent->GetBrowsingContext();
|
||||
Element* element = browserParent->GetOwnerElement();
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
@ -233,7 +236,8 @@ bool FilePickerParent::CreateFilePicker() {
|
||||
return false;
|
||||
}
|
||||
|
||||
return NS_SUCCEEDED(mFilePicker->Init(window, mTitle, mMode));
|
||||
return NS_SUCCEEDED(
|
||||
mFilePicker->Init(window, mTitle, mMode, browsingContext));
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult FilePickerParent::RecvOpen(
|
||||
|
@ -244,7 +244,7 @@ nsresult ContentAnalysisRequest::GetFileDigest(const nsAString& aFilePath,
|
||||
PRInt32 bytesRead = PR_Read(fd, buffer.get(), kBufferSize);
|
||||
while (bytesRead != 0) {
|
||||
if (bytesRead == -1) {
|
||||
return NS_ERROR_DOM_FILE_NOT_READABLE_ERR;
|
||||
return NS_ErrorAccordingToNSPR();
|
||||
}
|
||||
digest.Update(mozilla::Span<const uint8_t>(buffer.get(), bytesRead));
|
||||
bytesRead = PR_Read(fd, buffer.get(), kBufferSize);
|
||||
|
@ -153,9 +153,10 @@ nsBaseFilePicker::nsBaseFilePicker()
|
||||
|
||||
nsBaseFilePicker::~nsBaseFilePicker() = default;
|
||||
|
||||
NS_IMETHODIMP nsBaseFilePicker::Init(mozIDOMWindowProxy* aParent,
|
||||
const nsAString& aTitle,
|
||||
nsIFilePicker::Mode aMode) {
|
||||
NS_IMETHODIMP nsBaseFilePicker::Init(
|
||||
mozIDOMWindowProxy* aParent, const nsAString& aTitle,
|
||||
nsIFilePicker::Mode aMode,
|
||||
mozilla::dom::BrowsingContext* aBrowsingContext) {
|
||||
MOZ_ASSERT(aParent,
|
||||
"Null parent passed to filepicker, no file "
|
||||
"picker for you!");
|
||||
@ -165,6 +166,7 @@ NS_IMETHODIMP nsBaseFilePicker::Init(mozIDOMWindowProxy* aParent,
|
||||
nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(mParent);
|
||||
NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
|
||||
|
||||
mBrowsingContext = aBrowsingContext;
|
||||
mMode = aMode;
|
||||
InitNative(widget, aTitle);
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifndef nsBaseFilePicker_h__
|
||||
#define nsBaseFilePicker_h__
|
||||
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsIFilePicker.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
@ -28,7 +29,8 @@ class nsBaseFilePicker : public nsIFilePicker {
|
||||
virtual ~nsBaseFilePicker();
|
||||
|
||||
NS_IMETHOD Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle,
|
||||
nsIFilePicker::Mode aMode) override;
|
||||
nsIFilePicker::Mode aMode,
|
||||
mozilla::dom::BrowsingContext* aBrowsingContext) override;
|
||||
NS_IMETHOD IsModeSupported(nsIFilePicker::Mode aMode, JSContext* aCx,
|
||||
mozilla::dom::Promise** aPromise) override;
|
||||
#ifndef XP_WIN
|
||||
@ -68,6 +70,9 @@ class nsBaseFilePicker : public nsIFilePicker {
|
||||
nsString mDisplaySpecialDirectory;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> mParent;
|
||||
// The BrowsingContext from which the file picker is being opened.
|
||||
// Used for content analysis.
|
||||
RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
|
||||
nsIFilePicker::Mode mMode;
|
||||
nsString mOkButtonLabel;
|
||||
nsTArray<nsString> mRawFilters;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "mozilla/dom/Directory.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/BrowserChild.h"
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "mozilla/dom/IPCBlobUtils.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
@ -25,7 +26,8 @@ nsFilePickerProxy::~nsFilePickerProxy() = default;
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFilePickerProxy::Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle,
|
||||
nsIFilePicker::Mode aMode) {
|
||||
nsIFilePicker::Mode aMode,
|
||||
BrowsingContext* aBrowsingContext) {
|
||||
BrowserChild* browserChild = BrowserChild::GetFrom(aParent);
|
||||
if (!browserChild) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMArray.h"
|
||||
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "mozilla/dom/PFilePickerChild.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
||||
@ -34,7 +35,8 @@ class nsFilePickerProxy : public nsBaseFilePicker,
|
||||
|
||||
// nsIFilePicker (less what's in nsBaseFilePicker)
|
||||
NS_IMETHOD Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle,
|
||||
nsIFilePicker::Mode aMode) override;
|
||||
nsIFilePicker::Mode aMode,
|
||||
mozilla::dom::BrowsingContext* aBrowsingContext) override;
|
||||
NS_IMETHOD AppendFilter(const nsAString& aTitle,
|
||||
const nsAString& aFilter) override;
|
||||
NS_IMETHOD GetCapture(nsIFilePicker::CaptureTarget* aCapture) override;
|
||||
|
@ -10,6 +10,7 @@ interface nsIFile;
|
||||
interface nsIURI;
|
||||
interface mozIDOMWindowProxy;
|
||||
interface nsISimpleEnumerator;
|
||||
webidl BrowsingContext;
|
||||
|
||||
// Declared in this file, below.
|
||||
interface nsIFilePickerShownCallback;
|
||||
@ -69,9 +70,15 @@ interface nsIFilePicker : nsISupports
|
||||
* on this parent. parent must be non-null.
|
||||
* @param title The title for the file widget
|
||||
* @param mode load, save, or get folder
|
||||
*
|
||||
* @param browsingContext [optional]
|
||||
* The context in which the file picker is being shown. This is
|
||||
* used for content analysis and can be omitted if chrome is
|
||||
* showing the file picker.
|
||||
*/
|
||||
void init(in mozIDOMWindowProxy parent, in AString title, in nsIFilePicker_Mode mode);
|
||||
void init(in mozIDOMWindowProxy parent,
|
||||
in AString title,
|
||||
in nsIFilePicker_Mode mode,
|
||||
[optional] in BrowsingContext browsingContext);
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves to true if the passed nsIFilePicker mode
|
||||
|
@ -14,8 +14,11 @@
|
||||
#include <winuser.h>
|
||||
#include <utility>
|
||||
|
||||
#include "ContentAnalysis.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/BackgroundHangMonitor.h"
|
||||
#include "mozilla/Components.h"
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/ipc/UtilityProcessManager.h"
|
||||
#include "mozilla/ProfilerLabels.h"
|
||||
@ -24,6 +27,7 @@
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsEnumeratorUtils.h"
|
||||
#include "nsIContentAnalysis.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsPrintfCString.h"
|
||||
@ -84,9 +88,10 @@ nsFilePicker::nsFilePicker() : mSelectedType(1) {}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker)
|
||||
|
||||
NS_IMETHODIMP nsFilePicker::Init(mozIDOMWindowProxy* aParent,
|
||||
const nsAString& aTitle,
|
||||
nsIFilePicker::Mode aMode) {
|
||||
NS_IMETHODIMP nsFilePicker::Init(
|
||||
mozIDOMWindowProxy* aParent, const nsAString& aTitle,
|
||||
nsIFilePicker::Mode aMode,
|
||||
mozilla::dom::BrowsingContext* aBrowsingContext) {
|
||||
// Don't attempt to open a real file-picker in headless mode.
|
||||
if (gfxPlatform::IsHeadless()) {
|
||||
return nsresult::NS_ERROR_NOT_AVAILABLE;
|
||||
@ -96,7 +101,7 @@ NS_IMETHODIMP nsFilePicker::Init(mozIDOMWindowProxy* aParent,
|
||||
nsIDocShell* docShell = window ? window->GetDocShell() : nullptr;
|
||||
mLoadContext = do_QueryInterface(docShell);
|
||||
|
||||
return nsBaseFilePicker::Init(aParent, aTitle, aMode);
|
||||
return nsBaseFilePicker::Init(aParent, aTitle, aMode, aBrowsingContext);
|
||||
}
|
||||
|
||||
namespace mozilla::detail {
|
||||
@ -191,10 +196,11 @@ static auto ShowRemote(HWND parent, ActionType&& action)
|
||||
|
||||
// fd_async
|
||||
//
|
||||
// Wrapper-namespace for the AsyncExecute() function.
|
||||
// Wrapper-namespace for the AsyncExecute() and AsyncAll() functions.
|
||||
namespace fd_async {
|
||||
|
||||
// Implementation details of, specifically, the AsyncExecute() function.
|
||||
// Implementation details of, specifically, the AsyncExecute() and AsyncAll()
|
||||
// functions.
|
||||
namespace details {
|
||||
// Helper for generically copying ordinary types and nsTArray (which lacks a
|
||||
// copy constructor) in the same breath.
|
||||
@ -234,6 +240,49 @@ static Strategy GetStrategy() {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class AsyncAllIterator final {
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(AsyncAllIterator)
|
||||
AsyncAllIterator(
|
||||
nsTArray<T> aItems,
|
||||
std::function<RefPtr<mozilla::GenericPromise>(const T& item)> aPredicate,
|
||||
RefPtr<mozilla::GenericPromise::Private> aPromise)
|
||||
: mItems(std::move(aItems)),
|
||||
mNextIndex(0),
|
||||
mPredicate(std::move(aPredicate)),
|
||||
mPromise(std::move(aPromise)) {}
|
||||
|
||||
void StartIterating() { ContinueIterating(); }
|
||||
|
||||
private:
|
||||
~AsyncAllIterator() = default;
|
||||
void ContinueIterating() {
|
||||
if (mNextIndex >= mItems.Length()) {
|
||||
mPromise->Resolve(true, __func__);
|
||||
return;
|
||||
}
|
||||
mPredicate(mItems.ElementAt(mNextIndex))
|
||||
->Then(
|
||||
mozilla::GetMainThreadSerialEventTarget(), __func__,
|
||||
[self = RefPtr{this}](bool aResult) {
|
||||
if (!aResult) {
|
||||
self->mPromise->Resolve(false, __func__);
|
||||
return;
|
||||
}
|
||||
++self->mNextIndex;
|
||||
self->ContinueIterating();
|
||||
},
|
||||
[self = RefPtr{this}](nsresult aError) {
|
||||
self->mPromise->Reject(aError, __func__);
|
||||
});
|
||||
}
|
||||
nsTArray<T> mItems;
|
||||
uint32_t mNextIndex;
|
||||
std::function<RefPtr<mozilla::GenericPromise>(const T& item)> mPredicate;
|
||||
RefPtr<mozilla::GenericPromise::Private> mPromise;
|
||||
};
|
||||
|
||||
namespace telemetry {
|
||||
static uint32_t Delta(uint64_t tb, uint64_t ta) {
|
||||
// FILETIMEs are 100ns intervals; we reduce that to 1ms.
|
||||
@ -348,8 +397,24 @@ static auto AsyncExecute(Fn1 local, Fn2 remote, Args const&... args)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Asynchronously invokes `aPredicate` on each member of `aItems`.
|
||||
// Yields `false` (and stops immediately) if any invocation of
|
||||
// `predicate` yielded `false`; otherwise yields `true`.
|
||||
template <typename T>
|
||||
static RefPtr<mozilla::GenericPromise> AsyncAll(
|
||||
nsTArray<T> aItems,
|
||||
std::function<RefPtr<mozilla::GenericPromise>(const T& item)> aPredicate) {
|
||||
auto promise =
|
||||
mozilla::MakeRefPtr<mozilla::GenericPromise::Private>(__func__);
|
||||
auto iterator = mozilla::MakeRefPtr<details::AsyncAllIterator<T>>(
|
||||
std::move(aItems), aPredicate, promise);
|
||||
iterator->StartIterating();
|
||||
return promise;
|
||||
}
|
||||
} // namespace fd_async
|
||||
|
||||
using fd_async::AsyncAll;
|
||||
using fd_async::AsyncExecute;
|
||||
|
||||
} // namespace mozilla::detail
|
||||
@ -614,6 +679,77 @@ void nsFilePicker::ClearFiles() {
|
||||
mFiles.Clear();
|
||||
}
|
||||
|
||||
RefPtr<mozilla::GenericPromise> nsFilePicker::CheckContentAnalysisService() {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIContentAnalysis> contentAnalysis =
|
||||
mozilla::components::nsIContentAnalysis::Service(&rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return mozilla::GenericPromise::CreateAndReject(rv, __func__);
|
||||
}
|
||||
bool contentAnalysisIsActive = false;
|
||||
rv = contentAnalysis->GetIsActive(&contentAnalysisIsActive);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return mozilla::GenericPromise::CreateAndReject(rv, __func__);
|
||||
}
|
||||
if (!contentAnalysisIsActive) {
|
||||
return mozilla::GenericPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
nsCOMArray<nsIFile> files;
|
||||
if (!mUnicodeFile.IsEmpty()) {
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = GetFile(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return mozilla::GenericPromise::CreateAndReject(rv, __func__);
|
||||
}
|
||||
files.AppendElement(file);
|
||||
} else {
|
||||
files.AppendElements(mFiles);
|
||||
}
|
||||
nsTArray<mozilla::PathString> paths(files.Length());
|
||||
std::transform(files.begin(), files.end(), MakeBackInserter(paths),
|
||||
[](auto* entry) { return entry->NativePath(); });
|
||||
|
||||
RefPtr<nsIURI> uri = mBrowsingContext->Canonical()->GetCurrentURI();
|
||||
nsCString uriCString;
|
||||
rv = uri->GetSpec(uriCString);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return mozilla::GenericPromise::CreateAndReject(rv, __func__);
|
||||
}
|
||||
nsString uriString = NS_ConvertUTF8toUTF16(uriCString);
|
||||
|
||||
auto promise = mozilla::detail::AsyncAll<mozilla::PathString>(
|
||||
std::move(paths),
|
||||
[self = RefPtr{this}, contentAnalysis = std::move(contentAnalysis),
|
||||
uriString = std::move(uriString)](const mozilla::PathString& aItem) {
|
||||
nsCString emptyDigestString;
|
||||
auto* windowGlobal =
|
||||
self->mBrowsingContext->Canonical()->GetCurrentWindowGlobal();
|
||||
nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest(
|
||||
new mozilla::contentanalysis::ContentAnalysisRequest(
|
||||
nsIContentAnalysisRequest::AnalysisType::eFileAttached, aItem,
|
||||
true, std::move(emptyDigestString), uriString,
|
||||
nsIContentAnalysisRequest::OperationType::eCustomDisplayString,
|
||||
windowGlobal));
|
||||
|
||||
auto promise =
|
||||
mozilla::MakeRefPtr<mozilla::GenericPromise::Private>(__func__);
|
||||
auto contentAnalysisCallback = mozilla::MakeRefPtr<
|
||||
mozilla::contentanalysis::ContentAnalysisCallback>(
|
||||
[promise](nsIContentAnalysisResponse* aResponse) {
|
||||
promise->Resolve(aResponse->GetShouldAllowContent(), __func__);
|
||||
},
|
||||
[promise](nsresult aError) { promise->Reject(aError, __func__); });
|
||||
|
||||
nsresult rv = contentAnalysis->AnalyzeContentRequestCallback(
|
||||
contentAnalysisRequest, true, contentAnalysisCallback);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
promise->Reject(rv, __func__);
|
||||
}
|
||||
return promise;
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsIFilePicker impl.
|
||||
|
||||
@ -666,6 +802,25 @@ nsresult nsFilePicker::Open(nsIFilePickerShownCallback* aCallback) {
|
||||
}
|
||||
}
|
||||
|
||||
if (self->mBrowsingContext && !self->mBrowsingContext->IsChrome() &&
|
||||
self->mMode != modeSave && retValue != ResultCode::returnCancel) {
|
||||
self->CheckContentAnalysisService()->Then(
|
||||
mozilla::GetMainThreadSerialEventTarget(), __func__,
|
||||
[retValue, callback, self = RefPtr{self}](bool aAllowContent) {
|
||||
if (aAllowContent) {
|
||||
callback->Done(retValue);
|
||||
} else {
|
||||
self->ClearFiles();
|
||||
callback->Done(ResultCode::returnCancel);
|
||||
}
|
||||
},
|
||||
[callback, self = RefPtr{self}](nsresult aError) {
|
||||
self->ClearFiles();
|
||||
callback->Done(ResultCode::returnCancel);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback->Done(retValue);
|
||||
},
|
||||
[callback = RefPtr(aCallback)](HRESULT err) {
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "nsIContentAnalysis.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsCOMArray.h"
|
||||
@ -63,7 +65,8 @@ class nsFilePicker final : public nsBaseWinFilePicker {
|
||||
nsFilePicker();
|
||||
|
||||
NS_IMETHOD Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle,
|
||||
nsIFilePicker::Mode aMode) override;
|
||||
nsIFilePicker::Mode aMode,
|
||||
mozilla::dom::BrowsingContext* aBrowsingContext) override;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
@ -103,6 +106,7 @@ class nsFilePicker final : public nsBaseWinFilePicker {
|
||||
HWND aParent, nsTArray<Command> const& commands);
|
||||
|
||||
void ClearFiles();
|
||||
RefPtr<mozilla::GenericPromise> CheckContentAnalysisService();
|
||||
|
||||
protected:
|
||||
void RememberLastUsedDirectory();
|
||||
|
Loading…
x
Reference in New Issue
Block a user