Implement a more-specific AppAgent::IsConnectionAllowed() policy.
Prevents connections if an app is not launched. Or, if an app is
launched, connections must match the "transportId" that was in the last
RECEIVER_STATUS update.
Bug: b/162542369
Change-Id: I99afcf8a4c80d015505fa688b7372b83ae650578
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2552433
Commit-Queue: Jordan Bayles <jophba@chromium.org>
Reviewed-by: Jordan Bayles <jophba@chromium.org>
diff --git a/cast/receiver/application_agent.cc b/cast/receiver/application_agent.cc
index 97f2da2..ec8a44a 100644
--- a/cast/receiver/application_agent.cc
+++ b/cast/receiver/application_agent.cc
@@ -127,6 +127,7 @@
if (message_port_.GetSocketId() == ToCastSocketId(socket) &&
!message_port_.client_sender_id().empty() &&
message_port_.client_sender_id() == message.destination_id()) {
+ OSP_DCHECK(message_port_.client_sender_id() != kPlatformReceiverId);
message_port_.OnMessage(router, socket, std::move(message));
return;
}
@@ -179,7 +180,15 @@
bool ApplicationAgent::IsConnectionAllowed(
const VirtualConnection& virtual_conn) const {
- return true;
+ if (virtual_conn.local_id == kPlatformReceiverId) {
+ return true;
+ }
+ if (!launched_app_ || message_port_.client_sender_id().empty()) {
+ // No app currently launched. Or, there is a launched app, but it did not
+ // call MessagePort::SetClient() to indicate it wants messages routed to it.
+ return false;
+ }
+ return virtual_conn.local_id == message_port_.client_sender_id();
}
void ApplicationAgent::OnClose(CastSocket* socket) {
diff --git a/cast/receiver/application_agent_unittest.cc b/cast/receiver/application_agent_unittest.cc
index 8a02707..90fe705 100644
--- a/cast/receiver/application_agent_unittest.cc
+++ b/cast/receiver/application_agent_unittest.cc
@@ -33,6 +33,7 @@
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::Ne;
+using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Sequence;
using ::testing::StrEq;
@@ -585,6 +586,71 @@
agent()->UnregisterApplication(&some_app);
}
+TEST_F(ApplicationAgentTest, AllowsVirtualConnectionsToApp) {
+ NiceMock<FakeApplication> some_app("1A2B3C4D", "Something Doer");
+ agent()->RegisterApplication(&some_app);
+
+ // Launch the app, using gMock to simulate an app that calls
+ // MessagePort::SetClient() (to permit messaging) and to get the transport ID
+ // of the app.
+ EXPECT_CALL(*idle_app(), DidStop());
+ EXPECT_CALL(some_app, DidLaunch(_, NotNull()))
+ .WillOnce(Invoke([&](Json::Value params, MessagePort* port) {
+ port->SetClient(&some_app, some_app.GetSessionId());
+ }));
+ std::string transport_id;
+ EXPECT_CALL(*sender_inbound(), OnMessage(_, _))
+ .WillRepeatedly(Invoke([&](CastSocket*, CastMessage message) {
+ const Json::Value payload = ValidateAndParseMessage(
+ message, kPlatformReceiverId, kBroadcastId, kReceiverNamespace);
+ if (payload["type"].asString() == "RECEIVER_STATUS") {
+ transport_id =
+ payload["status"]["applications"][0]["transportId"].asString();
+ }
+ }));
+ auto launch_result = sender_outbound()->Send(MakeCastMessage(
+ kPlatformSenderId, kPlatformReceiverId, kReceiverNamespace, R"({
+ "requestId":1,
+ "type":"LAUNCH",
+ "appId":"1A2B3C4D",
+ "appParams":{},
+ "language":"en-US",
+ "supportedAppTypes":["WEB"]
+ })"));
+ ASSERT_TRUE(launch_result.ok()) << launch_result;
+ Mock::VerifyAndClearExpectations(idle_app());
+ Mock::VerifyAndClearExpectations(&some_app);
+ Mock::VerifyAndClearExpectations(sender_inbound());
+
+ // Now that the application has launched, check that the policy allows
+ // connections to both the ApplicationAgent and the running application.
+ auto* const policy =
+ static_cast<ConnectionNamespaceHandler::VirtualConnectionPolicy*>(
+ agent());
+ EXPECT_TRUE(policy->IsConnectionAllowed(
+ VirtualConnection{kPlatformReceiverId, "any-sender-12345", 0}));
+ ASSERT_FALSE(transport_id.empty());
+ EXPECT_TRUE(policy->IsConnectionAllowed(
+ VirtualConnection{transport_id, "any-sender-12345", 0}));
+ EXPECT_FALSE(policy->IsConnectionAllowed(
+ VirtualConnection{"wherever i likes", "any-sender-12345", 0}));
+
+ // Unregister the app, which will automatically stop it too.
+ EXPECT_CALL(some_app, DidStop());
+ EXPECT_CALL(*idle_app(), DidLaunch(_, NotNull()));
+ EXPECT_CALL(*sender_inbound(), OnMessage(_, _)); // RECEIVER_STATUS update.
+ agent()->UnregisterApplication(&some_app);
+
+ // With the app stopped, check that the policy no longer allows connections to
+ // the now-stale |transport_id|.
+ EXPECT_TRUE(policy->IsConnectionAllowed(
+ VirtualConnection{kPlatformReceiverId, "any-sender-12345", 0}));
+ EXPECT_FALSE(policy->IsConnectionAllowed(
+ VirtualConnection{transport_id, "any-sender-12345", 0}));
+ EXPECT_FALSE(policy->IsConnectionAllowed(
+ VirtualConnection{"wherever i likes", "any-sender-12345", 0}));
+}
+
} // namespace
} // namespace cast
} // namespace openscreen