0

Use request_initiator in quarantine

When source_url cannot be used (e.g. data:// URLs), try using the
request_initiator as HostUrl instead.

Bug: 351165321
Change-Id: I0c481a2da634891abaf18d7795cd911d04aec1ff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5824218
Commit-Queue: Daniel Rubery <drubery@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Reviewed-by: Will Harris <wfh@chromium.org>
Reviewed-by: Derek Schuff <dschuff@chromium.org>
Reviewed-by: Min Qin <qinmin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1352856}
This commit is contained in:
Daniel Rubery
2024-09-09 17:27:07 +00:00
committed by Chromium LUCI CQ
parent abefe04fdc
commit 4fbe682556
21 changed files with 220 additions and 58 deletions

@ -42,7 +42,8 @@ void ScanFile(
if (quarantine_remote) { if (quarantine_remote) {
quarantine_remote->QuarantineFile( quarantine_remote->QuarantineFile(
dest_platform_path, GURL(), GURL(), std::string(), dest_platform_path, GURL(), GURL(), /*request_initiator=*/std::nullopt,
std::string(),
base::BindOnce(&OnFileQuarantined, std::move(result_callback))); base::BindOnce(&OnFileQuarantined, std::move(result_callback)));
} else { } else {
std::move(result_callback).Run(base::File::FILE_OK); std::move(result_callback).Run(base::File::FILE_OK);

@ -8,6 +8,7 @@ include_rules = [
"+components/safe_browsing/buildflags.h", "+components/safe_browsing/buildflags.h",
"+components/safe_browsing/content/common", "+components/safe_browsing/content/common",
"+components/services/quarantine/quarantine.h", "+components/services/quarantine/quarantine.h",
"+components/services/quarantine/public",
"+components/ukm/test_ukm_recorder.h", "+components/ukm/test_ukm_recorder.h",
"+crypto", "+crypto",
"+mojo/public/c/system", "+mojo/public/c/system",

@ -650,7 +650,7 @@ void BaseFile::AnnotateWithSourceInformation(
authority_url, referrer_url)); authority_url, referrer_url));
quarantine_service_->QuarantineFile( quarantine_service_->QuarantineFile(
full_path_, authority_url, referrer_url, client_guid, full_path_, authority_url, referrer_url, request_initiator, client_guid,
base::BindOnce(&BaseFile::OnFileQuarantined, base::BindOnce(&BaseFile::OnFileQuarantined,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }

@ -33,6 +33,8 @@
#include "components/download/public/common/download_file_impl.h" #include "components/download/public/common/download_file_impl.h"
#include "components/download/public/common/download_interrupt_reasons.h" #include "components/download/public/common/download_interrupt_reasons.h"
#include "components/download/public/common/mock_input_stream.h" #include "components/download/public/common/mock_input_stream.h"
#include "components/services/quarantine/public/mojom/quarantine.mojom.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
@ -44,11 +46,13 @@
using ::testing::_; using ::testing::_;
using ::testing::AnyNumber; using ::testing::AnyNumber;
using ::testing::DoAll; using ::testing::DoAll;
using ::testing::Eq;
using ::testing::InSequence; using ::testing::InSequence;
using ::testing::Return; using ::testing::Return;
using ::testing::Sequence; using ::testing::Sequence;
using ::testing::SetArgPointee; using ::testing::SetArgPointee;
using ::testing::StrictMock; using ::testing::StrictMock;
using ::testing::WithArg;
namespace download { namespace download {
namespace { namespace {
@ -139,6 +143,18 @@ class TestDownloadFileImpl : public DownloadFileImpl {
#endif #endif
}; };
class MockQuarantine : public quarantine::mojom::Quarantine {
public:
MOCK_METHOD(void,
QuarantineFile,
(const base::FilePath& full_path,
const GURL& source_url,
const GURL& referrer_url,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid,
quarantine::mojom::Quarantine::QuarantineFileCallback callback));
};
} // namespace } // namespace
class DownloadFileTest : public testing::Test { class DownloadFileTest : public testing::Test {
@ -164,7 +180,8 @@ class DownloadFileTest : public testing::Test {
additional_streams_(std::vector<raw_ptr<StrictMock<MockInputStream>>>{ additional_streams_(std::vector<raw_ptr<StrictMock<MockInputStream>>>{
nullptr, nullptr}), nullptr, nullptr}),
bytes_(-1), bytes_(-1),
bytes_per_sec_(-1) {} bytes_per_sec_(-1),
quarantine_remote_(&quarantine_) {}
~DownloadFileTest() override {} ~DownloadFileTest() override {}
@ -187,6 +204,12 @@ class DownloadFileTest : public testing::Test {
EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _)) EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _))
.Times(AnyNumber()) .Times(AnyNumber())
.WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo)); .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo));
ON_CALL(quarantine_, QuarantineFile(_, _, _, _, _, _))
.WillByDefault(WithArg<5>(
[](quarantine::mojom::Quarantine::QuarantineFileCallback callback) {
std::move(callback).Run(
quarantine::mojom::QuarantineFileResult::OK);
}));
bool result = download_dir_.CreateUniqueTempDir(); bool result = download_dir_.CreateUniqueTempDir();
CHECK(result); CHECK(result);
} }
@ -425,9 +448,18 @@ class DownloadFileTest : public testing::Test {
break; break;
case RENAME_AND_ANNOTATE: case RENAME_AND_ANNOTATE:
// We cannot rebind a mojo::Remote without resetting it. The
// real implementation binds a new Remote on every call to
// RenameAndAnnotate, but it's simpler to reuse
// `quarantine_remote_` in tests.
quarantine_remote_.reset();
download_file_->RenameAndAnnotate( download_file_->RenameAndAnnotate(
full_path, "12345678-ABCD-1234-DCBA-123456789ABC", GURL(), GURL(), full_path, "12345678-ABCD-1234-DCBA-123456789ABC",
/*request_initiator=*/std::nullopt, mojo::NullRemote(), GURL("https://source.example.com/"),
GURL("https://referrer.example.com/"),
/*request_initiator=*/
url::Origin::Create(GURL("https://initiator.example.com/")),
quarantine_remote_.BindNewPipeAndPassRemote(),
std::move(completion_callback)); std::move(completion_callback));
break; break;
} }
@ -527,6 +559,8 @@ class DownloadFileTest : public testing::Test {
// Keep track of what data should be saved to the disk file. // Keep track of what data should be saved to the disk file.
std::string expected_data_; std::string expected_data_;
MockQuarantine quarantine_;
private: private:
void SetRenameResult(base::OnceClosure closure, void SetRenameResult(base::OnceClosure closure,
DownloadInterruptReason* reason_p, DownloadInterruptReason* reason_p,
@ -540,6 +574,8 @@ class DownloadFileTest : public testing::Test {
std::move(closure).Run(); std::move(closure).Run();
} }
mojo::Receiver<quarantine::mojom::Quarantine> quarantine_remote_;
base::test::TaskEnvironment task_environment_; base::test::TaskEnvironment task_environment_;
}; };
@ -1274,4 +1310,31 @@ TEST_F(DownloadFileTest, SecondStreamReadsOffsetWrittenByFirst) {
DestroyDownloadFile(0, false); DestroyDownloadFile(0, false);
} }
TEST_F(DownloadFileTest, PropagatesUrlAndInitiatorToQuarantine) {
ASSERT_TRUE(CreateDownloadFile(true));
base::FilePath initial_path(download_file_->FullPath());
base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
EXPECT_CALL(
quarantine_,
QuarantineFile(
_, GURL("https://source.example.com/"),
GURL("https://referrer.example.com"),
Eq(url::Origin::Create(GURL("https://initiator.example.com/"))), _,
_))
.WillOnce(WithArg<5>(
[](quarantine::mojom::Quarantine::QuarantineFileCallback callback) {
std::move(callback).Run(
quarantine::mojom::QuarantineFileResult::OK);
}));
base::FilePath new_path;
EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
RenameAndAnnotate(path_1, &new_path));
EXPECT_EQ(path_1.value(), new_path.value());
FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true, kEmptyHash);
base::RunLoop().RunUntilIdle();
DestroyDownloadFile(0);
}
} // namespace download } // namespace download

@ -11,5 +11,6 @@ mojom("mojom") {
"//mojo/public/mojom/base", "//mojo/public/mojom/base",
"//sandbox/policy/mojom", "//sandbox/policy/mojom",
"//url/mojom:url_mojom_gurl", "//url/mojom:url_mojom_gurl",
"//url/mojom:url_mojom_origin",
] ]
} }

@ -7,6 +7,7 @@ module quarantine.mojom;
import "mojo/public/mojom/base/file_path.mojom"; import "mojo/public/mojom/base/file_path.mojom";
import "sandbox/policy/mojom/sandbox.mojom"; import "sandbox/policy/mojom/sandbox.mojom";
import "url/mojom/url.mojom"; import "url/mojom/url.mojom";
import "url/mojom/origin.mojom";
enum QuarantineFileResult { enum QuarantineFileResult {
OK, // Success. OK, // Success.
@ -29,9 +30,15 @@ enum QuarantineFileResult {
[ServiceSandbox=sandbox.mojom.Sandbox.kNoSandbox] [ServiceSandbox=sandbox.mojom.Sandbox.kNoSandbox]
interface Quarantine { interface Quarantine {
// Quarantine a file that was downloaded from the internet. This method
// will apply platform-dependent annotations to the file to indicate that
// it came from the internet, and what URL(s) led to the file. For more
// details, see the documentation in
// //components/services/quarantine/quarantine.h.
QuarantineFile(mojo_base.mojom.FilePath full_path, QuarantineFile(mojo_base.mojom.FilePath full_path,
url.mojom.Url source_url, url.mojom.Url source_url,
url.mojom.Url referrer_url, url.mojom.Url referrer_url,
url.mojom.Origin? request_initiator,
string client_guid) string client_guid)
=> (QuarantineFileResult result); => (QuarantineFileResult result);
}; };

@ -13,6 +13,7 @@ namespace quarantine {
void QuarantineFile(const base::FilePath& file, void QuarantineFile(const base::FilePath& file,
const GURL& source_url, const GURL& source_url,
const GURL& referrer_url, const GURL& referrer_url,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid, const std::string& client_guid,
mojom::Quarantine::QuarantineFileCallback callback) { mojom::Quarantine::QuarantineFileCallback callback) {
std::move(callback).Run(QuarantineFileResult::OK); std::move(callback).Run(QuarantineFileResult::OK);

@ -58,6 +58,9 @@ using mojom::QuarantineFileResult;
// |source_url|: URL from which the file content was downloaded. This is empty // |source_url|: URL from which the file content was downloaded. This is empty
// for off-the-record download. // for off-the-record download.
// |referrer_url|: Referring URL. This is empty for off-the-record download. // |referrer_url|: Referring URL. This is empty for off-the-record download.
// `request_initiator`: Origin initiating the request. This is meant to
// replace the source URL when the source URL is not suitable for use in an
// annotation (e.g. a data URL).
// |client_guid|: Only used on Windows. Identifies the client application // |client_guid|: Only used on Windows. Identifies the client application
// that downloaded the file. // that downloaded the file.
// |callback|: Will be called with the quarantine result on completion. // |callback|: Will be called with the quarantine result on completion.
@ -70,6 +73,7 @@ using mojom::QuarantineFileResult;
void QuarantineFile(const base::FilePath& file, void QuarantineFile(const base::FilePath& file,
const GURL& source_url, const GURL& source_url,
const GURL& referrer_url, const GURL& referrer_url,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid, const std::string& client_guid,
mojom::Quarantine::QuarantineFileCallback callback); mojom::Quarantine::QuarantineFileCallback callback);

@ -27,6 +27,7 @@ void OnFileAdded(mojom::Quarantine::QuarantineFileCallback callback,
void QuarantineFile(const base::FilePath& file, void QuarantineFile(const base::FilePath& file,
const GURL& source_url_unsafe, const GURL& source_url_unsafe,
const GURL& referrer_url_unsafe, const GURL& referrer_url_unsafe,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid, const std::string& client_guid,
mojom::Quarantine::QuarantineFileCallback callback) { mojom::Quarantine::QuarantineFileCallback callback) {
if (!chromeos::DlpClient::Get() || !chromeos::DlpClient::Get()->IsAlive()) { if (!chromeos::DlpClient::Get() || !chromeos::DlpClient::Get()->IsAlive()) {

@ -34,6 +34,7 @@ void QuarantineImpl::QuarantineFile(
const base::FilePath& full_path, const base::FilePath& full_path,
const GURL& source_url, const GURL& source_url,
const GURL& referrer_url, const GURL& referrer_url,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid, const std::string& client_guid,
mojom::Quarantine::QuarantineFileCallback callback) { mojom::Quarantine::QuarantineFileCallback callback) {
#if BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_MAC)
@ -50,7 +51,7 @@ void QuarantineImpl::QuarantineFile(
FROM_HERE, FROM_HERE,
base::BindOnce( base::BindOnce(
&quarantine::QuarantineFile, full_path, source_url, referrer_url, &quarantine::QuarantineFile, full_path, source_url, referrer_url,
client_guid, request_initiator, client_guid,
base::BindOnce(&ReplyToCallback, base::BindOnce(&ReplyToCallback,
base::SingleThreadTaskRunner::GetCurrentDefault(), base::SingleThreadTaskRunner::GetCurrentDefault(),
std::move(callback)))); std::move(callback))));

@ -33,6 +33,7 @@ class QuarantineImpl : public mojom::Quarantine {
const base::FilePath& full_path, const base::FilePath& full_path,
const GURL& source_url, const GURL& source_url,
const GURL& referrer_url, const GURL& referrer_url,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid, const std::string& client_guid,
mojom::Quarantine::QuarantineFileCallback callback) override; mojom::Quarantine::QuarantineFileCallback callback) override;

@ -96,6 +96,7 @@ bool AddOriginMetadataToFile(const base::FilePath& file,
void QuarantineFile(const base::FilePath& file, void QuarantineFile(const base::FilePath& file,
const GURL& source_url_unsafe, const GURL& source_url_unsafe,
const GURL& referrer_url_unsafe, const GURL& referrer_url_unsafe,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid, const std::string& client_guid,
mojom::Quarantine::QuarantineFileCallback callback) { mojom::Quarantine::QuarantineFileCallback callback) {
if (!base::PathExists(file)) { if (!base::PathExists(file)) {
@ -111,6 +112,9 @@ void QuarantineFile(const base::FilePath& file,
GURL source_url = SanitizeUrlForQuarantine(source_url_unsafe); GURL source_url = SanitizeUrlForQuarantine(source_url_unsafe);
GURL referrer_url = SanitizeUrlForQuarantine(referrer_url_unsafe); GURL referrer_url = SanitizeUrlForQuarantine(referrer_url_unsafe);
if (source_url.is_empty() && request_initiator.has_value()) {
source_url = SanitizeUrlForQuarantine(request_initiator->GetURL());
}
// Don't consider it an error if we fail to add origin metadata. // Don't consider it an error if we fail to add origin metadata.
AddOriginMetadataToFile(file, source_url, referrer_url); AddOriginMetadataToFile(file, source_url, referrer_url);

@ -26,9 +26,11 @@
namespace quarantine { namespace quarantine {
namespace { namespace {
void CheckQuarantineResult(QuarantineFileResult result, void CheckQuarantineResult(base::OnceClosure quit_closure,
QuarantineFileResult result,
QuarantineFileResult expected_result) { QuarantineFileResult expected_result) {
EXPECT_EQ(expected_result, result); EXPECT_EQ(expected_result, result);
std::move(quit_closure).Run();
} }
class QuarantineMacTest : public testing::Test { class QuarantineMacTest : public testing::Test {
@ -69,23 +71,37 @@ class QuarantineMacTest : public testing::Test {
}; };
TEST_F(QuarantineMacTest, CheckMetadataSetCorrectly) { TEST_F(QuarantineMacTest, CheckMetadataSetCorrectly) {
QuarantineFile( base::RunLoop run_loop;
test_file_, source_url_, referrer_url_, "", QuarantineFile(test_file_, source_url_, referrer_url_,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); /*request_initiator=*/std::nullopt, "",
base::RunLoop().RunUntilIdle(); base::BindOnce(&CheckQuarantineResult, run_loop.QuitClosure(),
QuarantineFileResult::OK));
run_loop.Run();
EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_)); EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_));
} }
TEST_F(QuarantineMacTest, SetMetadataMultipleTimes) { TEST_F(QuarantineMacTest, SetMetadataMultipleTimes) {
GURL dummy_url("http://www.dummy.example.com"); {
QuarantineFile( base::RunLoop run_loop;
test_file_, source_url_, referrer_url_, "", QuarantineFile(
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); test_file_, source_url_, referrer_url_,
QuarantineFile( /*request_initiator=*/std::nullopt, "",
test_file_, dummy_url, dummy_url, "", base::BindOnce(&CheckQuarantineResult, run_loop.QuitClosure(),
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); run_loop.Run();
EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_)); }
{
base::RunLoop run_loop;
GURL dummy_url("http://www.dummy.example.com");
QuarantineFile(
test_file_, dummy_url, dummy_url,
/*request_initiator=*/std::nullopt, "",
base::BindOnce(&CheckQuarantineResult, run_loop.QuitClosure(),
QuarantineFileResult::OK));
run_loop.Run();
EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_));
}
} }
TEST_F(QuarantineMacTest, IsFileQuarantined_NoFile) { TEST_F(QuarantineMacTest, IsFileQuarantined_NoFile) {
@ -99,20 +115,24 @@ TEST_F(QuarantineMacTest, IsFileQuarantined_NoAnnotationsOnFile) {
} }
TEST_F(QuarantineMacTest, IsFileQuarantined_SourceUrlOnly) { TEST_F(QuarantineMacTest, IsFileQuarantined_SourceUrlOnly) {
QuarantineFile( base::RunLoop run_loop;
test_file_, source_url_, GURL(), std::string(), QuarantineFile(test_file_, source_url_, GURL(),
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); /*request_initiator=*/std::nullopt, std::string(),
base::RunLoop().RunUntilIdle(); base::BindOnce(&CheckQuarantineResult, run_loop.QuitClosure(),
QuarantineFileResult::OK));
run_loop.Run();
EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, GURL())); EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, GURL()));
EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), GURL())); EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), GURL()));
EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), referrer_url_)); EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), referrer_url_));
} }
TEST_F(QuarantineMacTest, IsFileQuarantined_FullMetadata) { TEST_F(QuarantineMacTest, IsFileQuarantined_FullMetadata) {
QuarantineFile( base::RunLoop run_loop;
test_file_, source_url_, referrer_url_, std::string(), QuarantineFile(test_file_, source_url_, referrer_url_,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); /*request_initiator=*/std::nullopt, std::string(),
base::RunLoop().RunUntilIdle(); base::BindOnce(&CheckQuarantineResult, run_loop.QuitClosure(),
QuarantineFileResult::OK));
run_loop.Run();
EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), GURL())); EXPECT_TRUE(IsFileQuarantined(test_file_, GURL(), GURL()));
EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, GURL())); EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, GURL()));
EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_)); EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_));
@ -120,24 +140,28 @@ TEST_F(QuarantineMacTest, IsFileQuarantined_FullMetadata) {
} }
TEST_F(QuarantineMacTest, IsFileQuarantined_Sanitize) { TEST_F(QuarantineMacTest, IsFileQuarantined_Sanitize) {
base::RunLoop run_loop;
GURL host_url{"https://user:pass@example.com/foo/bar?x#y"}; GURL host_url{"https://user:pass@example.com/foo/bar?x#y"};
GURL host_url_clean{"https://example.com/foo/bar?x#y"}; GURL host_url_clean{"https://example.com/foo/bar?x#y"};
GURL referrer_url{"https://user:pass@example.com/foo/index?x#y"}; GURL referrer_url{"https://user:pass@example.com/foo/index?x#y"};
GURL referrer_url_clean{"https://example.com/foo/index?x#y"}; GURL referrer_url_clean{"https://example.com/foo/index?x#y"};
QuarantineFile( QuarantineFile(test_file_, host_url, referrer_url,
test_file_, host_url, referrer_url, std::string(), /*request_initiator=*/std::nullopt, std::string(),
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, run_loop.QuitClosure(),
base::RunLoop().RunUntilIdle(); QuarantineFileResult::OK));
run_loop.Run();
EXPECT_TRUE( EXPECT_TRUE(
IsFileQuarantined(test_file_, host_url_clean, referrer_url_clean)); IsFileQuarantined(test_file_, host_url_clean, referrer_url_clean));
} }
TEST_F(QuarantineMacTest, IsFileQuarantined_AgentBundleIdentifier) { TEST_F(QuarantineMacTest, IsFileQuarantined_AgentBundleIdentifier) {
QuarantineFile( base::RunLoop run_loop;
test_file_, source_url_, referrer_url_, "", QuarantineFile(test_file_, source_url_, referrer_url_,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); /*request_initiator=*/std::nullopt, "",
base::RunLoop().RunUntilIdle(); base::BindOnce(&CheckQuarantineResult, run_loop.QuitClosure(),
QuarantineFileResult::OK));
run_loop.Run();
NSDictionary* properties = GetQuarantineProperties(test_file_); NSDictionary* properties = GetQuarantineProperties(test_file_);
ASSERT_TRUE(properties); ASSERT_TRUE(properties);
@ -157,10 +181,12 @@ TEST_F(QuarantineMacTest, IsFileQuarantined_AgentBundleIdentifier) {
} }
TEST_F(QuarantineMacTest, NoWhereFromsKeyIfNoURLs) { TEST_F(QuarantineMacTest, NoWhereFromsKeyIfNoURLs) {
QuarantineFile( base::RunLoop run_loop;
test_file_, GURL(), GURL(), std::string(), QuarantineFile(test_file_, GURL(), GURL(), /*request_initiator=*/std::nullopt,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); std::string(),
base::RunLoop().RunUntilIdle(); base::BindOnce(&CheckQuarantineResult, run_loop.QuitClosure(),
QuarantineFileResult::OK));
run_loop.Run();
NSString* file_path = base::apple::FilePathToNSString(test_file_); NSString* file_path = base::apple::FilePathToNSString(test_file_);
ASSERT_NE(nullptr, file_path); ASSERT_NE(nullptr, file_path);
@ -178,5 +204,18 @@ TEST_F(QuarantineMacTest, NoWhereFromsKeyIfNoURLs) {
EXPECT_FALSE(attr); EXPECT_FALSE(attr);
} }
TEST_F(QuarantineMacTest, RequestInitiatorReplacesSourceUrl) {
base::RunLoop run_loop;
QuarantineFile(test_file_, GURL("data://text/html,payload"), referrer_url_,
/*request_initiator=*/
url::Origin::Create(GURL("http://www.source.example.com/")),
"",
base::BindOnce(&CheckQuarantineResult, run_loop.QuitClosure(),
QuarantineFileResult::OK));
run_loop.Run();
EXPECT_TRUE(IsFileQuarantined(
test_file_, GURL("http://www.source.example.com/"), referrer_url_));
}
} // namespace } // namespace
} // namespace quarantine } // namespace quarantine

@ -59,7 +59,8 @@ TEST_F(QuarantineServiceTest, QuarantineFile) {
base::RunLoop run_loop; base::RunLoop run_loop;
quarantine_->QuarantineFile( quarantine_->QuarantineFile(
test_file, GURL(kInternetURL), GURL(kInternetReferrerURL), std::string(), test_file, GURL(kInternetURL), GURL(kInternetReferrerURL),
/*request_initiator=*/std::nullopt, std::string(),
base::BindOnce(&QuarantineServiceTest::OnFileQuarantined, base::BindOnce(&QuarantineServiceTest::OnFileQuarantined,
base::Unretained(this), test_file, base::Unretained(this), test_file,
run_loop.QuitClosure())); run_loop.QuitClosure()));

@ -64,7 +64,8 @@ class QuarantineTest : public testing::Test {
TEST_F(QuarantineTest, FileCanBeOpenedForReadAfterAnnotation) { TEST_F(QuarantineTest, FileCanBeOpenedForReadAfterAnnotation) {
base::FilePath test_file = GetTestFilePath(); base::FilePath test_file = GetTestFilePath();
QuarantineFile( QuarantineFile(
test_file, GURL(kInternetURL), GURL(kInternetReferrerURL), kTestGUID, test_file, GURL(kInternetURL), GURL(kInternetReferrerURL),
/*request_initiator=*/std::nullopt, kTestGUID,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -76,7 +77,7 @@ TEST_F(QuarantineTest, FileCanBeOpenedForReadAfterAnnotation) {
TEST_F(QuarantineTest, FileCanBeAnnotatedWithNoGUID) { TEST_F(QuarantineTest, FileCanBeAnnotatedWithNoGUID) {
QuarantineFile( QuarantineFile(
GetTestFilePath(), GURL(kInternetURL), GURL(kInternetReferrerURL), GetTestFilePath(), GURL(kInternetURL), GURL(kInternetReferrerURL),
std::string(), /*request_initiator=*/std::nullopt, std::string(),
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }

@ -225,6 +225,7 @@ QuarantineFileResult SetInternetZoneIdentifierDirectly(
void QuarantineFile(const base::FilePath& file, void QuarantineFile(const base::FilePath& file,
const GURL& source_url_unsafe, const GURL& source_url_unsafe,
const GURL& referrer_url_unsafe, const GURL& referrer_url_unsafe,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid, const std::string& client_guid,
mojom::Quarantine::QuarantineFileCallback callback) { mojom::Quarantine::QuarantineFileCallback callback) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
@ -245,6 +246,10 @@ void QuarantineFile(const base::FilePath& file,
} }
GURL source_url = SanitizeUrlForQuarantine(source_url_unsafe); GURL source_url = SanitizeUrlForQuarantine(source_url_unsafe);
if (source_url.is_empty() && request_initiator.has_value()) {
source_url = SanitizeUrlForQuarantine(request_initiator->GetURL());
}
GURL referrer_url = SanitizeUrlForQuarantine(referrer_url_unsafe); GURL referrer_url = SanitizeUrlForQuarantine(referrer_url_unsafe);
if (file_size == 0 || IsEqualGUID(guid, GUID_NULL)) { if (file_size == 0 || IsEqualGUID(guid, GUID_NULL)) {

@ -187,7 +187,7 @@ class QuarantineWinTest : public ::testing::Test {
TEST_F(QuarantineWinTest, MissingFile) { TEST_F(QuarantineWinTest, MissingFile) {
QuarantineFile(GetTempDir().AppendASCII("does-not-exist.exe"), QuarantineFile(GetTempDir().AppendASCII("does-not-exist.exe"),
GURL(kDummySourceUrl), GURL(kDummyReferrerUrl), GURL(kDummySourceUrl), GURL(kDummyReferrerUrl),
kDummyClientGuid, /*request_initiator=*/std::nullopt, kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, base::BindOnce(&CheckQuarantineResult,
QuarantineFileResult::FILE_MISSING)); QuarantineFileResult::FILE_MISSING));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -210,7 +210,8 @@ TEST_F(QuarantineWinTest, LocalFile_DependsOnLocalConfig) {
ASSERT_TRUE(CreateFile(test_file)); ASSERT_TRUE(CreateFile(test_file));
QuarantineFile( QuarantineFile(
test_file, GURL(source_url), GURL(), kDummyClientGuid, test_file, GURL(source_url), GURL(), /*request_initiator=*/std::nullopt,
kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -237,7 +238,8 @@ TEST_F(QuarantineWinTest, DownloadedFile_DependsOnLocalConfig) {
ASSERT_TRUE(CreateFile(test_file)); ASSERT_TRUE(CreateFile(test_file));
QuarantineFile( QuarantineFile(
test_file, GURL(source_url), GURL(), kDummyClientGuid, test_file, GURL(source_url), GURL(), /*request_initiator=*/std::nullopt,
kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -270,7 +272,7 @@ TEST_F(QuarantineWinTest, UnsafeReferrer_DependsOnLocalConfig) {
ASSERT_TRUE(CreateFile(test_file)); ASSERT_TRUE(CreateFile(test_file));
QuarantineFile( QuarantineFile(
test_file, GURL("http://example.com/good"), GURL(referrer_url), test_file, GURL("http://example.com/good"), GURL(referrer_url),
kDummyClientGuid, /*request_initiator=*/std::nullopt, kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -293,7 +295,8 @@ TEST_F(QuarantineWinTest, EmptySource_DependsOnLocalConfig) {
ASSERT_TRUE(CreateFile(test_file)); ASSERT_TRUE(CreateFile(test_file));
QuarantineFile( QuarantineFile(
test_file, GURL(), GURL(), kDummyClientGuid, test_file, GURL(), GURL(), /*request_initiator=*/std::nullopt,
kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -314,7 +317,8 @@ TEST_F(QuarantineWinTest, EmptyFile) {
ASSERT_TRUE(base::WriteFile(test_file, "")); ASSERT_TRUE(base::WriteFile(test_file, ""));
QuarantineFile( QuarantineFile(
test_file, net::FilePathToFileURL(test_file), GURL(), kDummyClientGuid, test_file, net::FilePathToFileURL(test_file), GURL(),
/*request_initiator=*/std::nullopt, kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -336,7 +340,8 @@ TEST_F(QuarantineWinTest, NoClientGuid) {
ASSERT_TRUE(CreateFile(test_file)); ASSERT_TRUE(CreateFile(test_file));
QuarantineFile( QuarantineFile(
test_file, net::FilePathToFileURL(test_file), GURL(), std::string(), test_file, net::FilePathToFileURL(test_file), GURL(),
/*request_initiator=*/std::nullopt, std::string(),
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -359,7 +364,8 @@ TEST_F(QuarantineWinTest, SuperLongURL) {
std::string source_url("http://example.com/"); std::string source_url("http://example.com/");
source_url.append(INTERNET_MAX_URL_LENGTH * 2, 'a'); source_url.append(INTERNET_MAX_URL_LENGTH * 2, 'a');
QuarantineFile( QuarantineFile(
test_file, GURL(source_url), GURL(), std::string(), test_file, GURL(source_url), GURL(), /*request_initiator=*/std::nullopt,
std::string(),
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -380,7 +386,8 @@ TEST_F(QuarantineWinTest, TrustedSite) {
ASSERT_TRUE(CreateFile(test_file)); ASSERT_TRUE(CreateFile(test_file));
QuarantineFile( QuarantineFile(
test_file, source_url, GURL(), kDummyClientGuid, test_file, source_url, GURL(), /*request_initiator=*/std::nullopt,
kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -398,7 +405,8 @@ TEST_F(QuarantineWinTest, RestrictedSite) {
ASSERT_TRUE(CreateFile(test_file)); ASSERT_TRUE(CreateFile(test_file));
// Files from a restricted site are deleted. // Files from a restricted site are deleted.
QuarantineFile(test_file, source_url, GURL(), kDummyClientGuid, QuarantineFile(test_file, source_url, GURL(),
/*request_initiator=*/std::nullopt, kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, base::BindOnce(&CheckQuarantineResult,
QuarantineFileResult::BLOCKED_BY_POLICY)); QuarantineFileResult::BLOCKED_BY_POLICY));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -417,7 +425,8 @@ TEST_F(QuarantineWinTest, TrustedSite_AlreadyQuarantined) {
// Ensure the file already contains a zone identifier. // Ensure the file already contains a zone identifier.
ASSERT_TRUE(AddInternetZoneIdentifierDirectly(test_file)); ASSERT_TRUE(AddInternetZoneIdentifierDirectly(test_file));
QuarantineFile( QuarantineFile(
test_file, source_url, GURL(), kDummyClientGuid, test_file, source_url, GURL(), /*request_initiator=*/std::nullopt,
kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -439,7 +448,8 @@ TEST_F(QuarantineWinTest, RestrictedSite_AlreadyQuarantined) {
ASSERT_TRUE(AddInternetZoneIdentifierDirectly(test_file)); ASSERT_TRUE(AddInternetZoneIdentifierDirectly(test_file));
// Files from a restricted site are deleted. // Files from a restricted site are deleted.
QuarantineFile(test_file, source_url, GURL(), kDummyClientGuid, QuarantineFile(test_file, source_url, GURL(),
/*request_initiator=*/std::nullopt, kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, base::BindOnce(&CheckQuarantineResult,
QuarantineFileResult::BLOCKED_BY_POLICY)); QuarantineFileResult::BLOCKED_BY_POLICY));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -463,7 +473,8 @@ TEST_F(QuarantineWinTest, MetaData_ApplyMOTW_Directly) {
// An invalid GUID will cause QuarantineFile() to apply the MOTW directly. // An invalid GUID will cause QuarantineFile() to apply the MOTW directly.
QuarantineFile( QuarantineFile(
test_file, host_url, referrer_url, std::string(), test_file, host_url, referrer_url, /*request_initiator=*/std::nullopt,
std::string(),
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
@ -484,11 +495,26 @@ TEST_F(QuarantineWinTest, MetaData_InvokeAS) {
base::StrCat({"https://", GetInternetSite(), "/folder/index?x#y"})); base::StrCat({"https://", GetInternetSite(), "/folder/index?x#y"}));
QuarantineFile( QuarantineFile(
test_file, host_url, referrer_url, kDummyClientGuid, test_file, host_url, referrer_url, /*request_initiator=*/std::nullopt,
kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK)); base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_TRUE(IsFileQuarantined(test_file, host_url_clean, referrer_url_clean)); EXPECT_TRUE(IsFileQuarantined(test_file, host_url_clean, referrer_url_clean));
} }
TEST_F(QuarantineWinTest, RequestInitiatorReplacesSourceUrl) {
base::FilePath test_file = GetTempDir().AppendASCII("foo.exe");
ASSERT_TRUE(CreateFile(test_file));
GURL host_url(base::StrCat({"https://", GetInternetSite(), "/"}));
QuarantineFile(
test_file, GURL("data://text/html,payload"), GURL(),
url::Origin::Create(host_url), kDummyClientGuid,
base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(IsFileQuarantined(test_file, host_url, GURL()));
}
} // namespace quarantine } // namespace quarantine

@ -67,6 +67,7 @@ class MockQuarantine : public quarantine::mojom::Quarantine {
void QuarantineFile(const base::FilePath& full_path, void QuarantineFile(const base::FilePath& full_path,
const GURL& source_url, const GURL& source_url,
const GURL& referrer_url, const GURL& referrer_url,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid, const std::string& client_guid,
QuarantineFileCallback callback) override { QuarantineFileCallback callback) override {
paths.push_back(full_path); paths.push_back(full_path);

@ -344,6 +344,9 @@ void FileSystemAccessSafeMoveHelper::DidFileDoQuarantine(
quarantine::mojom::Quarantine* raw_quarantine = quarantine_remote.get(); quarantine::mojom::Quarantine* raw_quarantine = quarantine_remote.get();
raw_quarantine->QuarantineFile( raw_quarantine->QuarantineFile(
target_url.path(), authority_url, referrer_url, target_url.path(), authority_url, referrer_url,
// TODO(crbug.com/351165321): Consider propagating request_initiator
// information here.
/*request_initiator=*/std::nullopt,
GetContentClient() GetContentClient()
->browser() ->browser()
->GetApplicationClientGUIDForQuarantineCheck(), ->GetApplicationClientGUIDForQuarantineCheck(),

@ -54,6 +54,7 @@ class MockQuarantine : public quarantine::mojom::Quarantine {
void QuarantineFile(const base::FilePath& full_path, void QuarantineFile(const base::FilePath& full_path,
const GURL& source_url, const GURL& source_url,
const GURL& referrer_url, const GURL& referrer_url,
const std::optional<url::Origin>& request_initiator,
const std::string& client_guid, const std::string& client_guid,
QuarantineFileCallback callback) override { QuarantineFileCallback callback) override {
paths.push_back(full_path); paths.push_back(full_path);

@ -482,7 +482,7 @@ void PepperFileIOHost::OnLocalFileOpened(
quarantine::mojom::Quarantine* raw_quarantine = quarantine_remote.get(); quarantine::mojom::Quarantine* raw_quarantine = quarantine_remote.get();
raw_quarantine->QuarantineFile( raw_quarantine->QuarantineFile(
path, browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()), path, browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()),
GURL(), std::string(), GURL(), /*request_initiator=*/std::nullopt, std::string(),
mojo::WrapCallbackWithDefaultInvokeIfNotRun( mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(&PepperFileIOHost::OnLocalFileQuarantined, base::BindOnce(&PepperFileIOHost::OnLocalFileQuarantined,
weak_ptr_factory_.GetWeakPtr(), reply_context, path, weak_ptr_factory_.GetWeakPtr(), reply_context, path,