Chrome · Actor › Glic
CVE-2026-106191
Authorization Error in Actor (Glic)
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/glic/actor/glic_actor_task_manager.cc |
modified | |
switchchrome/browser/glic/actor/glic_actor_task_manager.cc |
modified | |
BindRepeatingchrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.cc |
modified |
Files Changed
chrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.ccchrome/browser/glic/actor/glic_actor_action_execution_browsertest.ccchrome/browser/glic/actor/glic_actor_task_manager.ccchrome/browser/glic/actor/glic_actor_task_manager.h
Patch
From c083e5ef61b0b64e767ad8580327041525d53e04 Mon Sep 17 00:00:00 2001
From: mark a. foltz <mfoltz@chromium.org>
Date: Fri, 04 Sep 2026 11:25:31 -0700
Subject: [PATCH] [glic] Validate task ID matches current task in GlicActorClientSession.
- Adds ValidateTaskIdMatchesCurrent in GlicActorClientSession to ensure
that glic calls that take a task ID only operate on the session's
active current_task_id_.
- Adds browser tests in GlicActorActionExecutionFunctionalBrowserTest
verifying that mismatched task IDs are rejected for PerformActions
and CancelActions.
Fixed: 502282293
Change-Id: I05bc20a54de9ba85a83d9854ee01f792f6e2f4d9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8333560
Commit-Queue: Mark Foltz <mfoltz@chromium.org>
Reviewed-by: Justin DeWitt <dewittj@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1692616}
---
diff --git a/chrome/browser/glic/actor/glic_actor_task_manager.cc b/chrome/browser/glic/actor/glic_actor_task_manager.cc
index 452e131..710796bb 100644
--- a/chrome/browser/glic/actor/glic_actor_task_manager.cc
+++ b/chrome/browser/glic/actor/glic_actor_task_manager.cc
@@ -462,7 +462,8 @@
}
actor::TaskId task_id(actions.task_id());
- if (!actor_keyed_service().GetTask(task_id)) {
+ if (!ValidateTaskIdMatchesCurrent(task_id, "GlicPerformActions") ||
+ !actor_keyed_service().GetTask(task_id)) {
actor_keyed_service().GetJournal().Log(GURL::EmptyGURL(), task_id,
"Act Failed",
actor::JournalDetailsBuilder()
@@ -511,6 +512,10 @@
void GlicActorClientSession::CancelActions(int32_t task_id,
CancelActionsCallback callback) {
auto actor_task_id = actor::TaskId(task_id);
+ if (!ValidateTaskIdMatchesCurrent(actor_task_id, "CancelActions")) {
+ std::move(callback).Run(mojom::CancelActionsResult::kTaskNotFound);
+ return;
+ }
actor::ActorTask* task = actor_keyed_service().GetTask(actor_task_id);
if (!task) {
std::move(callback).Run(mojom::CancelActionsResult::kTaskNotFound);
@@ -529,6 +534,9 @@
int32_t task_id,
mojom::ActorTaskStopReason stop_reason) {
auto actor_task_id = actor::TaskId(task_id);
+ if (!ValidateTaskIdMatchesCurrent(actor_task_id, "StopActorTask")) {
+ return;
+ }
instance_metrics().OnStopActorTask();
actor::ActorTask::StoppedReason reason;
switch (stop_reason) {
@@ -572,6 +580,9 @@
mojom::ActorTaskPauseReason pause_reason,
std::optional<int32_t> tab_handle) {
auto actor_task_id = actor::TaskId(task_id);
+ if (!ValidateTaskIdMatchesCurrent(actor_task_id, "PauseActorTask")) {
+ return;
+ }
tabs::TabInterface::Handle handle;
if (tab_handle.has_value()) {
handle = tabs::TabInterface::Handle(*tab_handle);
@@ -604,6 +615,12 @@
mojom::TabContextOptionsPtr context_options,
ResumeActorTaskCallback callback) {
auto actor_task_id = actor::TaskId(task_id);
+ if (!ValidateTaskIdMatchesCurrent(actor_task_id, "ResumeActorTask")) {
+ std::string error_message = "No such task";
+ std::move(callback).Run(mojom::GetContextResultWithActionResultCode::New(
+ mojom::GetContextResult::NewErrorReason(error_message), std::nullopt));
+ return;
+ }
instance_metrics().OnResumeActorTask();
actor::ActorTask* task = actor_keyed_service().GetTask(actor_task_id);
if (!task || !task->IsUnderUserControl()) {
@@ -769,6 +786,9 @@
int32_t task_id,
std::optional<mojom::ActorTaskInterruptReason> interrupt_reason) {
auto actor_task_id = actor::TaskId(task_id);
+ if (!ValidateTaskIdMatchesCurrent(actor_task_id, "InterruptActorTask")) {
+ return;
+ }
instance_metrics().InterruptActorTask();
actor::ActorTask* task = actor_keyed_service().GetTask(actor_task_id);
@@ -789,6 +809,9 @@
void GlicActorClientSession::UninterruptActorTask(int32_t task_id) {
auto actor_task_id = actor::TaskId(task_id);
+ if (!ValidateTaskIdMatchesCurrent(actor_task_id, "UninterruptActorTask")) {
+ return;
+ }
instance_metrics().UninterruptActorTask();
actor::ActorTask* task = actor_keyed_service().GetTask(actor_task_id);
if (!task) {
@@ -811,6 +834,10 @@
int32_t task_id,
const std::string& step_progress) {
auto actor_task_id = actor::TaskId(task_id);
+ if (!ValidateTaskIdMatchesCurrent(actor_task_id,
+ "UpdateActorTaskStepProgress")) {
+ return;
+ }
actor::ActorTask* task = actor_keyed_service().GetTask(actor_task_id);
if (!task) {
actor_keyed_service().GetJournal().Log(
@@ -830,6 +857,10 @@
mojom::CreateActorTabOptionsPtr options,
CreateActorTabCallback callback) {
auto actor_task_id = actor::TaskId(task_id);
+ if (!ValidateTaskIdMatchesCurrent(actor_task_id, "CreateActorTab")) {
+ std::move(callback).Run(nullptr);
+ return;
+ }
tabs::TabHandle initiator_tab_handle =
options->initiator_tab_id.has_value()
? tabs::TabHandle(*options->initiator_tab_id)
@@ -936,6 +967,22 @@
actor_keyed_service().StopTask(task->id(), reason);
}
+bool GlicActorClientSession::ValidateTaskIdMatchesCurrent(
+ actor::TaskId task_id,
+ std::string_view method_name) {
+ if (current_task_id_.is_null() || task_id != current_task_id_) {
+ actor_keyed_service().GetJournal().Log(
+ GURL::EmptyGURL(), task_id, method_name,
+ actor::JournalDetailsBuilder()
+ .AddError("Task ID does not match current task")
+ .Add("expected_task_id", current_task_id_.value())
+ .Add("provided_task_id", task_id.value())
+ .Build());
+ return false;
+ }
+ return true;
+}
+
actor::ActorKeyedService& GlicActorClientSession::actor_keyed_service() const {
return *manager_->actor_keyed_service_;
}
diff --git a/chrome/browser/glic/actor/glic_actor_task_manager.h b/chrome/browser/glic/actor/glic_actor_task_manager.h
index a5e97d4..0e86137 100644
--- a/chrome/browser/glic/actor/glic_actor_task_manager.h
+++ b/chrome/browser/glic/actor/glic_actor_task_manager.h
@@ -256,6 +256,8 @@
void NotifyActorTaskStateChanged(actor::ActorTask& task);
void StopTaskImpl(actor::TaskId task_id,
actor::ActorTask::StoppedReason reason);
+ bool ValidateTaskIdMatchesCurrent(actor::TaskId task_id,
+ std::string_view method_name);
actor::ActorKeyedService& actor_keyed_service() const;
GlicActorPolicyChecker& actor_policy_checker() const;
GlicInstanceMetrics& instance_metrics() const;
diff --git a/chrome/browser/glic/actor/glic_actor_action_execution_browsertest.cc b/chrome/browser/glic/actor/glic_actor_action_execution_browsertest.cc
index 909fe2a7..3f8990d 100644
--- a/chrome/browser/glic/actor/glic_actor_action_execution_browsertest.cc
+++ b/chrome/browser/glic/actor/glic_actor_action_execution_browsertest.cc
@@ -258,47 +258,47 @@
IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
PerformConcurrentAsyncWaitActions) {
- // Manually create tasks via ActorKeyedService.
- TaskId task_id_1 = actor_keyed_service()->CreateTask(
- MockGlicTaskSourceInfo(), ::actor::NoEnterprisePolicyChecker());
- TaskId task_id_2 = actor_keyed_service()->CreateTask(
- MockGlicTaskSourceInfo(), ::actor::NoEnterprisePolicyChecker());
+ ASSERT_OK_AND_ASSIGN(TaskId task_id, CreateTask());
+ ASSERT_NE(task_id, TaskId());
- // Create tabs for each task using CreateActorTab API to ensure a
+ // Create tabs for the task using CreateActorTab API to ensure a
// TabObservation is included in its result.
ASSERT_OK_AND_ASSIGN(
tabs::TabHandle tab_1,
- CreateActorTab(task_id_1, /*open_in_background=*/false,
+ CreateActorTab(task_id, /*open_in_background=*/false,
base::ToString(active_tab()->GetHandle().raw_value()),
base::ToString(browser()->GetSessionID().id())));
ASSERT_OK_AND_ASSIGN(
tabs::TabHandle tab_2,
- CreateActorTab(task_id_2, /*open_in_background=*/false,
+ CreateActorTab(task_id, /*open_in_background=*/false,
base::ToString(active_tab()->GetHandle().raw_value()),
base::ToString(browser()->GetSessionID().id())));
- // Perform two WaitActions where the first resolves after the second
- Actions action_1 = ::actor::MakeWait(kShortWaitTime * 2, tab_1, task_id_1);
- Actions action_2 = ::actor::MakeWait(kShortWaitTime, tab_2, task_id_2);
-
+ // Perform two WaitActions
+ Actions action_1 = ::actor::MakeWait(kShortWaitTime, tab_1, task_id);
std::unique_ptr<AsyncActionWaiter> waiter_1 = PerformActionsAsync(action_1);
- std::unique_ptr<AsyncActionWaiter> waiter_2 = PerformActionsAsync(action_2);
-
- // We should still be able to wait for result_2 after result_1 despite
- // action_2 resolving first.
ASSERT_OK_AND_ASSIGN(ActionsResult result_1, waiter_1->Wait());
+
+ Actions action_2 = ::actor::MakeWait(kShortWaitTime, tab_2, task_id);
+ std::unique_ptr<AsyncActionWaiter> waiter_2 = PerformActionsAsync(action_2);
ASSERT_OK_AND_ASSIGN(ActionsResult result_2, waiter_2->Wait());
- // Verify a tab observation was included in the results.
+ // Verify tab observations were included in the results.
EXPECT_THAT(result_1, HasResultCode(::actor::mojom::ActionResultCode::kOk));
- EXPECT_THAT(result_1.tabs(), testing::SizeIs(1));
+ EXPECT_THAT(result_1.tabs(), testing::SizeIs(2));
EXPECT_THAT(result_1.tabs().at(0).result(),
TabObservation::TAB_OBSERVATION_OK);
+ EXPECT_THAT(result_1.tabs().at(1).result(),
+ TabObservation::TAB_OBSERVATION_OK);
EXPECT_THAT(result_2, HasResultCode(::actor::mojom::ActionResultCode::kOk));
- EXPECT_THAT(result_2.tabs(), testing::SizeIs(1));
+ EXPECT_THAT(result_2.tabs(), testing::SizeIs(2));
EXPECT_THAT(result_2.tabs().at(0).result(),
TabObservation::TAB_OBSERVATION_OK);
+ EXPECT_THAT(result_2.tabs().at(1).result(),
+ TabObservation::TAB_OBSERVATION_OK);
+
+ StopActorTask(task_id, glic::mojom::ActorTaskStopReason::kTaskComplete);
}
IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
@@ -411,5 +411,68 @@
ActorTask::State::kCreated);
}
+IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
+ PerformActionsRejectsMismatchedTaskId) {
+ ASSERT_OK_AND_ASSIGN(TaskId task_id, CreateTask());
+ EXPECT_NE(task_id, TaskId());
+
+ const GURL target_url = embedded_https_test_server().GetURL(
+ "example.com", "/actor/blank.html?target");
+ // Construct actions targeting a different/foreign task ID.
+ TaskId foreign_task_id(task_id.value() + 999);
+ Actions action = ::actor::MakeNavigate(active_tab()->GetHandle(),
+ target_url.spec(), foreign_task_id);
+
+ ASSERT_OK_AND_ASSIGN(ActionsResult result, PerformActions(action));
+ EXPECT_THAT(result,
+ HasResultCode(::actor::mojom::ActionResultCode::kTaskWentAway));
+
+ StopActorTask(task_id, glic::mojom::ActorTaskStopReason::kTaskComplete);
+}
+
+IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
+ CancelActionsRejectsMismatchedTaskId) {
+ ASSERT_OK_AND_ASSIGN(TaskId task_id, CreateTask());
+ EXPECT_NE(task_id, TaskId());
+
+ TaskId foreign_task_id(task_id.value() + 999);
+ EXPECT_THAT(
+ CancelActions(foreign_task_id),
+ base::test::ValueIs(glic::mojom::CancelActionsResult::kTaskNotFound));
+
+ StopActorTask(task_id, glic::mojom::ActorTaskStopReason::kTaskComplete);
+}
+
+// Security regression test for bug 5:
+// Verifies that a Glic guest cannot hijack a foreign task in the profile
+// (e.g. created by experimental_actor) when the Glic session has no active
+// task (current_task_id_ is null).
+IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
+ PerformActionsRejectsForeignTaskWhenNoCurrentTask) {
+ const TaskId victim_task_id = actor_keyed_service()->CreateTask(
+ ::actor::TaskSourceInfo(
+ ::actor::TaskSourceInfo::Client::kExperimentalActor,
+ /*id=*/std::nullopt),
+ ::actor::NoEnterprisePolicyChecker());
+ ASSERT_FALSE(victim_task_id.is_null());
+ ActorTask* victim_task = actor_keyed_service()->GetTask(victim_task_id);
+ ASSERT_NE(victim_task, nullptr);
+
+ const GURL initial_url = web_contents()->GetURL();
+ const GURL target_url = embedded_https_test_server().GetURL(
+ "example.com", "/actor/blank.html?hijacked-by-glic-guest");
+ ASSERT_NE(target_url, initial_url);
+
+ Actions hijack = ::actor::MakeNavigate(active_tab()->GetHandle(),
+ target_url.spec(), victim_task_id);
+
+ ASSERT_OK_AND_ASSIGN(ActionsResult result, PerformActions(hijack));
+ EXPECT_THAT(result,
+ HasResultCode(::actor::mojom::ActionResultCode::kTaskWentAway));
+
+ EXPECT_EQ(initial_url, web_contents()->GetURL());
+ EXPECT_EQ(ActorTask::State::kCreated, victim_task->GetState());
+}
+
} // namespace
} // namespace glic::actor
diff --git a/chrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.cc b/chrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.cc
index e6c5b88..3ba7feb 100644
--- a/chrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.cc
+++ b/chrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.cc
@@ -472,8 +472,9 @@
// Pausing an invalid task should be a no-op and log an error.
observer.WaitUntil(
base::BindRepeating([](const ::actor::mojom::JournalEntry& entry) {
- return entry.event == "Failed to pause task" &&
- JournalEntryHasError(entry, "No such task");
+ return entry.event == "PauseActorTask" &&
+ JournalEntryHasError(entry,
+ "Task ID does not match current task");
}));
ContinueJsTest();
@@ -500,8 +501,9 @@
// Pausing an inactive task should be a no-op and log an error.
observer.WaitUntil(
base::BindRepeating([](const ::actor::mojom::JournalEntry& entry) {
- return entry.event == "Failed to pause task" &&
- JournalEntryHasError(entry, "No such task");
+ return entry.event == "PauseActorTask" &&
+ JournalEntryHasError(entry,
+ "Task ID does not match current task");
}));
ContinueJsTest();
@@ -551,8 +553,9 @@
// Interrupting an invalid task should be a no-op and log an error.
observer.WaitUntil(
base::BindRepeating([](const ::actor::mojom::JournalEntry& entry) {
- return entry.event == "Failed to interrupt task" &&
- JournalEntryHasError(entry, "No such task");
+ return entry.event == "InterruptActorTask" &&
+ JournalEntryHasError(entry,
+ "Task ID does not match current task");
}));
ContinueJsTest();
@@ -560,8 +563,9 @@
// Uninterrupting an invalid task should be a no-op and log an error.
observer.WaitUntil(
base::BindRepeating([](const ::actor::mojom::JournalEntry& entry) {
- return entry.event == "Failed to uninterrupt task" &&
- JournalEntryHasError(entry, "No such task");
+ return entry.event == "UninterruptActorTask" &&
+ JournalEntryHasError(entry,
+ "Task ID does not match current task");
}));
}
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/glic/actor/glic_actor_action_execution_browsertest.cc b/chrome/browser/glic/actor/glic_actor_action_execution_browsertest.cc
index 909fe2a7..3f8990d 100644
--- a/chrome/browser/glic/actor/glic_actor_action_execution_browsertest.cc
+++ b/chrome/browser/glic/actor/glic_actor_action_execution_browsertest.cc
@@ -258,47 +258,47 @@
IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
PerformConcurrentAsyncWaitActions) {
- // Manually create tasks via ActorKeyedService.
- TaskId task_id_1 = actor_keyed_service()->CreateTask(
- MockGlicTaskSourceInfo(), ::actor::NoEnterprisePolicyChecker());
- TaskId task_id_2 = actor_keyed_service()->CreateTask(
- MockGlicTaskSourceInfo(), ::actor::NoEnterprisePolicyChecker());
+ ASSERT_OK_AND_ASSIGN(TaskId task_id, CreateTask());
+ ASSERT_NE(task_id, TaskId());
- // Create tabs for each task using CreateActorTab API to ensure a
+ // Create tabs for the task using CreateActorTab API to ensure a
// TabObservation is included in its result.
ASSERT_OK_AND_ASSIGN(
tabs::TabHandle tab_1,
- CreateActorTab(task_id_1, /*open_in_background=*/false,
+ CreateActorTab(task_id, /*open_in_background=*/false,
base::ToString(active_tab()->GetHandle().raw_value()),
base::ToString(browser()->GetSessionID().id())));
ASSERT_OK_AND_ASSIGN(
tabs::TabHandle tab_2,
- CreateActorTab(task_id_2, /*open_in_background=*/false,
+ CreateActorTab(task_id, /*open_in_background=*/false,
base::ToString(active_tab()->GetHandle().raw_value()),
base::ToString(browser()->GetSessionID().id())));
- // Perform two WaitActions where the first resolves after the second
- Actions action_1 = ::actor::MakeWait(kShortWaitTime * 2, tab_1, task_id_1);
- Actions action_2 = ::actor::MakeWait(kShortWaitTime, tab_2, task_id_2);
-
+ // Perform two WaitActions
+ Actions action_1 = ::actor::MakeWait(kShortWaitTime, tab_1, task_id);
std::unique_ptr<AsyncActionWaiter> waiter_1 = PerformActionsAsync(action_1);
- std::unique_ptr<AsyncActionWaiter> waiter_2 = PerformActionsAsync(action_2);
-
- // We should still be able to wait for result_2 after result_1 despite
- // action_2 resolving first.
ASSERT_OK_AND_ASSIGN(ActionsResult result_1, waiter_1->Wait());
+
+ Actions action_2 = ::actor::MakeWait(kShortWaitTime, tab_2, task_id);
+ std::unique_ptr<AsyncActionWaiter> waiter_2 = PerformActionsAsync(action_2);
ASSERT_OK_AND_ASSIGN(ActionsResult result_2, waiter_2->Wait());
- // Verify a tab observation was included in the results.
+ // Verify tab observations were included in the results.
EXPECT_THAT(result_1, HasResultCode(::actor::mojom::ActionResultCode::kOk));
- EXPECT_THAT(result_1.tabs(), testing::SizeIs(1));
+ EXPECT_THAT(result_1.tabs(), testing::SizeIs(2));
EXPECT_THAT(result_1.tabs().at(0).result(),
TabObservation::TAB_OBSERVATION_OK);
+ EXPECT_THAT(result_1.tabs().at(1).result(),
+ TabObservation::TAB_OBSERVATION_OK);
EXPECT_THAT(result_2, HasResultCode(::actor::mojom::ActionResultCode::kOk));
- EXPECT_THAT(result_2.tabs(), testing::SizeIs(1));
+ EXPECT_THAT(result_2.tabs(), testing::SizeIs(2));
EXPECT_THAT(result_2.tabs().at(0).result(),
TabObservation::TAB_OBSERVATION_OK);
+ EXPECT_THAT(result_2.tabs().at(1).result(),
+ TabObservation::TAB_OBSERVATION_OK);
+
+ StopActorTask(task_id, glic::mojom::ActorTaskStopReason::kTaskComplete);
}
IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
@@ -411,5 +411,68 @@
ActorTask::State::kCreated);
}
+IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
+ PerformActionsRejectsMismatchedTaskId) {
+ ASSERT_OK_AND_ASSIGN(TaskId task_id, CreateTask());
+ EXPECT_NE(task_id, TaskId());
+
+ const GURL target_url = embedded_https_test_server().GetURL(
+ "example.com", "/actor/blank.html?target");
+ // Construct actions targeting a different/foreign task ID.
+ TaskId foreign_task_id(task_id.value() + 999);
+ Actions action = ::actor::MakeNavigate(active_tab()->GetHandle(),
+ target_url.spec(), foreign_task_id);
+
+ ASSERT_OK_AND_ASSIGN(ActionsResult result, PerformActions(action));
+ EXPECT_THAT(result,
+ HasResultCode(::actor::mojom::ActionResultCode::kTaskWentAway));
+
+ StopActorTask(task_id, glic::mojom::ActorTaskStopReason::kTaskComplete);
+}
+
+IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
+ CancelActionsRejectsMismatchedTaskId) {
+ ASSERT_OK_AND_ASSIGN(TaskId task_id, CreateTask());
+ EXPECT_NE(task_id, TaskId());
+
+ TaskId foreign_task_id(task_id.value() + 999);
+ EXPECT_THAT(
+ CancelActions(foreign_task_id),
+ base::test::ValueIs(glic::mojom::CancelActionsResult::kTaskNotFound));
+
+ StopActorTask(task_id, glic::mojom::ActorTaskStopReason::kTaskComplete);
+}
+
+// Security regression test for bug 5:
+// Verifies that a Glic guest cannot hijack a foreign task in the profile
+// (e.g. created by experimental_actor) when the Glic session has no active
+// task (current_task_id_ is null).
+IN_PROC_BROWSER_TEST_F(GlicActorActionExecutionFunctionalBrowserTest,
+ PerformActionsRejectsForeignTaskWhenNoCurrentTask) {
+ const TaskId victim_task_id = actor_keyed_service()->CreateTask(
+ ::actor::TaskSourceInfo(
+ ::actor::TaskSourceInfo::Client::kExperimentalActor,
+ /*id=*/std::nullopt),
+ ::actor::NoEnterprisePolicyChecker());
+ ASSERT_FALSE(victim_task_id.is_null());
+ ActorTask* victim_task = actor_keyed_service()->GetTask(victim_task_id);
+ ASSERT_NE(victim_task, nullptr);
+
+ const GURL initial_url = web_contents()->GetURL();
+ const GURL target_url = embedded_https_test_server().GetURL(
+ "example.com", "/actor/blank.html?hijacked-by-glic-guest");
+ ASSERT_NE(target_url, initial_url);
+
+ Actions hijack = ::actor::MakeNavigate(active_tab()->GetHandle(),
+ target_url.spec(), victim_task_id);
+
+ ASSERT_OK_AND_ASSIGN(ActionsResult result, PerformActions(hijack));
+ EXPECT_THAT(result,
+ HasResultCode(::actor::mojom::ActionResultCode::kTaskWentAway));
+
+ EXPECT_EQ(initial_url, web_contents()->GetURL());
+ EXPECT_EQ(ActorTask::State::kCreated, victim_task->GetState());
+}
+
} // namespace
} // namespace glic::actor
diff --git a/chrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.cc b/chrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.cc
index e6c5b88..3ba7feb 100644
--- a/chrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.cc
+++ b/chrome/browser/glic/actor/glic_actor_task_lifecycle_browsertest.cc
@@ -472,8 +472,9 @@
// Pausing an invalid task should be a no-op and log an error.
observer.WaitUntil(
base::BindRepeating([](const ::actor::mojom::JournalEntry& entry) {
- return entry.event == "Failed to pause task" &&
- JournalEntryHasError(entry, "No such task");
+ return entry.event == "PauseActorTask" &&
+ JournalEntryHasError(entry,
+ "Task ID does not match current task");
}));
ContinueJsTest();
@@ -500,8 +501,9 @@
// Pausing an inactive task should be a no-op and log an error.
observer.WaitUntil(
base::BindRepeating([](const ::actor::mojom::JournalEntry& entry) {
- return entry.event == "Failed to pause task" &&
- JournalEntryHasError(entry, "No such task");
+ return entry.event == "PauseActorTask" &&
+ JournalEntryHasError(entry,
+ "Task ID does not match current task");
}));
ContinueJsTest();
@@ -551,8 +553,9 @@
// Interrupting an invalid task should be a no-op and log an error.
observer.WaitUntil(
base::BindRepeating([](const ::actor::mojom::JournalEntry& entry) {
- return entry.event == "Failed to interrupt task" &&
- JournalEntryHasError(entry, "No such task");
+ return entry.event == "InterruptActorTask" &&
+ JournalEntryHasError(entry,
+ "Task ID does not match current task");
}));
ContinueJsTest();
@@ -560,8 +563,9 @@
// Uninterrupting an invalid task should be a no-op and log an error.
observer.WaitUntil(
base::BindRepeating([](const ::actor::mojom::JournalEntry& entry) {
- return entry.event == "Failed to uninterrupt task" &&
- JournalEntryHasError(entry, "No such task");
+ return entry.event == "UninterruptActorTask" &&
+ JournalEntryHasError(entry,
+ "Task ID does not match current task");
}));
}
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page