Skip to content

Commit

Permalink
Disable content analysis within a tab.
Browse files Browse the repository at this point in the history
If the use copies or cuts data from a web page and pastes it back into
the same web page, there is no need to perform content scanning since
the page already has access to the data.

Bug: b:256818217
Change-Id: Id1a4fdf6d4e757ff75145f256135f6d964b022e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4219234
Commit-Queue: Roger Tawa <rogerta@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Dominique Fauteux-Chapleau <domfc@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1118326}
  • Loading branch information
Roger Tawa authored and Chromium LUCI CQ committed Mar 16, 2023
1 parent 9d94ef9 commit c774694
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 2 deletions.
39 changes: 38 additions & 1 deletion content/browser/renderer_host/clipboard_host_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,26 @@

namespace content {

namespace {

// Used to skip content analysis checks when the source and destination of the
// clipboard data are the same.
struct LastWriterInfo {
// A pointer to the last ClipboardHostImpl that committed data to the
// clipboard.
ClipboardHostImpl* writer = nullptr;

// The sequence number of the last commit made by `writer`.
ui::ClipboardSequenceNumberToken seqno;
};

LastWriterInfo& GetLastWriterInfo() {
static LastWriterInfo info;
return info;
}

} // namespace

// The amount of time that the result of a content allow request is cached
// and reused for the same clipboard `seqno`.
constexpr base::TimeDelta
Expand Down Expand Up @@ -124,6 +144,7 @@ void ClipboardHostImpl::Create(
}

ClipboardHostImpl::~ClipboardHostImpl() {
GetLastWriterInfo() = {};
clipboard_writer_->Reset();
}

Expand Down Expand Up @@ -494,6 +515,13 @@ void ClipboardHostImpl::WriteImage(const SkBitmap& bitmap) {
void ClipboardHostImpl::CommitWrite() {
clipboard_writer_ = std::make_unique<ui::ScopedClipboardWriter>(
ui::ClipboardBuffer::kCopyPaste, CreateDataEndpoint());

// Remember the ClipboardHostImpl and associated seqno of the last write
// made to the clipboard by any ClipboardHostImpl.
GetLastWriterInfo() = {
.writer = this,
.seqno = ui::Clipboard::GetForCurrentThread()->GetSequenceNumber(
ui::ClipboardBuffer::kCopyPaste)};
}

bool ClipboardHostImpl::IsRendererPasteAllowed(
Expand Down Expand Up @@ -630,11 +658,20 @@ void ClipboardHostImpl::PerformPasteIfContentAllowed(
std::string data,
IsClipboardPasteContentAllowedCallback callback) {
CleanupObsoleteRequests();

// Always allow if the source of the last clipboard commit was this host.
const LastWriterInfo& info = GetLastWriterInfo();
if (info.writer == this && info.seqno == seqno) {
std::move(callback).Run(data);
return;
}

// Add |callback| to the callbacks associated to the sequence number, adding
// an entry to the map if one does not exist.
auto& request = is_allowed_requests_[seqno];
if (request.AddCallback(std::move(callback)))
if (request.AddCallback(std::move(callback))) {
StartIsPasteContentAllowedRequest(seqno, data_type, std::move(data));
}
}

void ClipboardHostImpl::StartIsPasteContentAllowedRequest(
Expand Down
4 changes: 4 additions & 0 deletions content/browser/renderer_host/clipboard_host_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ class CONTENT_EXPORT ClipboardHostImpl
PerformPasteIfContentAllowed_EmptyData);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest,
PerformPasteIfContentAllowed);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest,
PerformPasteIfContentAllowed_SameHost_NotStarted);
FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest,
PerformPasteIfContentAllowed_External_Started);

// mojom::ClipboardHost
void GetSequenceNumber(ui::ClipboardBuffer clipboard_buffer,
Expand Down
60 changes: 59 additions & 1 deletion content/browser/renderer_host/clipboard_host_impl_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,26 @@ class FakeClipboardHostImpl : public ClipboardHostImpl {
void StartIsPasteContentAllowedRequest(
const ui::ClipboardSequenceNumberToken& seqno,
const ui::ClipboardFormatType& data_type,
std::string data) override {}
std::string data) override {
++start_count_;
}

void CompleteRequest(const ui::ClipboardSequenceNumberToken& seqno,
const std::string& data) {
FinishPasteIfContentAllowed(seqno, data);
}

size_t start_count() const { return start_count_; }

using ClipboardHostImpl::CleanupObsoleteRequests;
using ClipboardHostImpl::is_paste_allowed_requests_for_testing;
using ClipboardHostImpl::kIsPasteContentAllowedRequestTooOld;
using ClipboardHostImpl::PasteIfPolicyAllowed;
using ClipboardHostImpl::PerformPasteIfContentAllowed;

private:
// number of times StartIsPasteContentAllowedRequest() is called.
size_t start_count_ = 0;
};

class PolicyControllerTest : public ui::DataTransferPolicyController {
Expand Down Expand Up @@ -305,6 +313,56 @@ class ClipboardHostImplScanTest : public RenderViewHostTestHarness {
raw_ptr<FakeClipboardHostImpl> fake_clipboard_host_impl_;
};

TEST_F(ClipboardHostImplScanTest,
PerformPasteIfContentAllowed_SameHost_NotStarted) {
const std::u16string kText = u"text";
clipboard_host_impl()->WriteText(kText);
clipboard_host_impl()->CommitWrite();

std::u16string read_text;
clipboard_host_impl()->ReadText(
ui::ClipboardBuffer::kCopyPaste,
base::BindLambdaForTesting(
[&read_text](const std::u16string& value) { read_text = value; }));

// When the same document writes and then reads from the clipboard, content
// checks should be skipped.
EXPECT_EQ(0u, clipboard_host_impl()->start_count());
EXPECT_EQ(kText, read_text);
}

TEST_F(ClipboardHostImplScanTest,
PerformPasteIfContentAllowed_External_Started) {
const std::u16string kText = u"text";

// Write directly to clipboard.
{
ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste);
writer.WriteText(kText);
}

std::u16string read_text;
clipboard_host_impl()->ReadText(
ui::ClipboardBuffer::kCopyPaste,
base::BindLambdaForTesting(
[&read_text](const std::u16string& value) { read_text = value; }));

// Completing the request invokes the callback. The request will
// remain pending until it is cleaned up.
clipboard_host_impl()->CompleteRequest(
ui::Clipboard::GetForCurrentThread()->GetSequenceNumber(
ui::ClipboardBuffer::kCopyPaste),
base::UTF16ToUTF8(kText));
EXPECT_EQ(
1u,
clipboard_host_impl()->is_paste_allowed_requests_for_testing().size());

// When a document reads from the clipboard, but the clipboard was written
// from an unknown source, content checks should not be skipped.
EXPECT_EQ(1u, clipboard_host_impl()->start_count());
EXPECT_EQ(kText, read_text);
}

TEST_F(ClipboardHostImplScanTest, PasteIfPolicyAllowed_EmptyData) {
int count = 0;

Expand Down

0 comments on commit c774694

Please sign in to comment.