Commit 434a6e8d authored by mfoltz@chromium.org's avatar mfoltz@chromium.org

Implement argument validation for chrome.cast.channel.{open,send}

Cleanup namespace usage in cast_channel_api.h.

TESTED=Browser test.  Manually with Cast extension
BUG=331165

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=266804

Review URL: https://codereview.chromium.org/255443002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284200 0039d316-1c4b-4281-b951-d872f2087c98
parent 8c72dfe7
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "chrome/browser/extensions/api/cast_channel/cast_channel_api.h" #include "chrome/browser/extensions/api/cast_channel/cast_channel_api.h"
#include <limits> #include <limits>
#include <string>
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
...@@ -60,6 +61,21 @@ void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) { ...@@ -60,6 +61,21 @@ void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) {
channel_info->error_state = socket.error_state(); channel_info->error_state = socket.error_state();
} }
bool IsValidConnectInfoPort(const ConnectInfo& connect_info) {
return connect_info.port > 0 && connect_info.port <
std::numeric_limits<unsigned short>::max();
}
bool IsValidConnectInfoAuth(const ConnectInfo& connect_info) {
return connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED ||
connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL;
}
bool IsValidConnectInfoIpAddress(const ConnectInfo& connect_info) {
net::IPAddressNumber ip_address;
return net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address);
}
} // namespace } // namespace
CastChannelAPI::CastChannelAPI(content::BrowserContext* context) CastChannelAPI::CastChannelAPI(content::BrowserContext* context)
...@@ -244,22 +260,12 @@ bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url, ...@@ -244,22 +260,12 @@ bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url,
cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED : cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED :
cast_channel::CHANNEL_AUTH_TYPE_SSL; cast_channel::CHANNEL_AUTH_TYPE_SSL;
return true; return true;
}; }
net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo( net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo(
const ConnectInfo& connect_info) { const ConnectInfo& connect_info) {
net::IPAddressNumber ip_address; net::IPAddressNumber ip_address;
if (!net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address)) { CHECK(net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address));
return NULL;
}
if (connect_info.port < 0 || connect_info.port >
std::numeric_limits<unsigned short>::max()) {
return NULL;
}
if (connect_info.auth != cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED &&
connect_info.auth != cast_channel::CHANNEL_AUTH_TYPE_SSL) {
return NULL;
}
return new net::IPEndPoint(ip_address, connect_info.port); return new net::IPEndPoint(ip_address, connect_info.port);
} }
...@@ -280,20 +286,35 @@ bool CastChannelOpenFunction::Prepare() { ...@@ -280,20 +286,35 @@ bool CastChannelOpenFunction::Prepare() {
connect_info_.reset(new ConnectInfo); connect_info_.reset(new ConnectInfo);
if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) { if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) {
connect_info_.reset(); connect_info_.reset();
SetError("Invalid connect_info (invalid Cast URL " + cast_url + ")");
} }
break; break;
case base::Value::TYPE_DICTIONARY: case base::Value::TYPE_DICTIONARY:
connect_info_ = ConnectInfo::FromValue(*(params_->connect_info)); connect_info_ = ConnectInfo::FromValue(*(params_->connect_info));
if (!connect_info_.get()) {
SetError("connect_info.auth is required");
}
break; break;
default: default:
SetError("Invalid connect_info (unknown type)");
break; break;
} }
if (connect_info_.get()) { if (!connect_info_.get()) {
channel_auth_ = connect_info_->auth; return false;
ip_endpoint_.reset(ParseConnectInfo(*connect_info_)); }
return ip_endpoint_.get() != NULL; if (!IsValidConnectInfoPort(*connect_info_)) {
SetError("Invalid connect_info (invalid port)");
} else if (!IsValidConnectInfoAuth(*connect_info_)) {
SetError("Invalid connect_info (invalid auth)");
} else if (!IsValidConnectInfoIpAddress(*connect_info_)) {
SetError("Invalid connect_info (invalid IP address)");
} }
return false; if (!GetError().empty()) {
return false;
}
channel_auth_ = connect_info_->auth;
ip_endpoint_.reset(ParseConnectInfo(*connect_info_));
return true;
} }
void CastChannelOpenFunction::AsyncWorkStart() { void CastChannelOpenFunction::AsyncWorkStart() {
...@@ -319,6 +340,26 @@ CastChannelSendFunction::~CastChannelSendFunction() { } ...@@ -319,6 +340,26 @@ CastChannelSendFunction::~CastChannelSendFunction() { }
bool CastChannelSendFunction::Prepare() { bool CastChannelSendFunction::Prepare() {
params_ = Send::Params::Create(*args_); params_ = Send::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get()); EXTENSION_FUNCTION_VALIDATE(params_.get());
if (params_->message.namespace_.empty()) {
SetError("message_info.namespace_ is required");
return false;
}
if (params_->message.source_id.empty()) {
SetError("message_info.source_id is required");
return false;
}
if (params_->message.destination_id.empty()) {
SetError("message_info.destination_id is required");
return false;
}
switch (params_->message.data->GetType()) {
case base::Value::TYPE_STRING:
case base::Value::TYPE_BINARY:
break;
default:
SetError("Invalid type of message_info.data");
return false;
}
return true; return true;
} }
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_CAST_CHANNEL_CAST_CHANNEL_API_H_ #ifndef CHROME_BROWSER_EXTENSIONS_API_CAST_CHANNEL_CAST_CHANNEL_API_H_
#define CHROME_BROWSER_EXTENSIONS_API_CAST_CHANNEL_CAST_CHANNEL_API_H_ #define CHROME_BROWSER_EXTENSIONS_API_CAST_CHANNEL_CAST_CHANNEL_API_H_
#include <string>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
...@@ -31,7 +33,6 @@ namespace cast_channel = api::cast_channel; ...@@ -31,7 +33,6 @@ namespace cast_channel = api::cast_channel;
class CastChannelAPI : public BrowserContextKeyedAPI, class CastChannelAPI : public BrowserContextKeyedAPI,
public cast_channel::CastSocket::Delegate { public cast_channel::CastSocket::Delegate {
public: public:
explicit CastChannelAPI(content::BrowserContext* context); explicit CastChannelAPI(content::BrowserContext* context);
...@@ -137,12 +138,12 @@ class CastChannelOpenFunction : public CastChannelAsyncApiFunction { ...@@ -137,12 +138,12 @@ class CastChannelOpenFunction : public CastChannelAsyncApiFunction {
// corresponding details, and returns true. Returns false if |url| is not a // corresponding details, and returns true. Returns false if |url| is not a
// valid Cast URL. // valid Cast URL.
static bool ParseChannelUrl(const GURL& url, static bool ParseChannelUrl(const GURL& url,
api::cast_channel::ConnectInfo* connect_info); cast_channel::ConnectInfo* connect_info);
// Validates that |connect_info| represents a valid IP end point and returns a // Validates that |connect_info| represents a valid IP end point and returns a
// new IPEndPoint if so. Otherwise returns NULL. // new IPEndPoint if so. Otherwise returns NULL.
static net::IPEndPoint* ParseConnectInfo( static net::IPEndPoint* ParseConnectInfo(
const api::cast_channel::ConnectInfo& connect_info); const cast_channel::ConnectInfo& connect_info);
void OnOpen(int result); void OnOpen(int result);
...@@ -150,9 +151,9 @@ class CastChannelOpenFunction : public CastChannelAsyncApiFunction { ...@@ -150,9 +151,9 @@ class CastChannelOpenFunction : public CastChannelAsyncApiFunction {
// The id of the newly opened socket. // The id of the newly opened socket.
int new_channel_id_; int new_channel_id_;
CastChannelAPI* api_; CastChannelAPI* api_;
scoped_ptr<api::cast_channel::ConnectInfo> connect_info_; scoped_ptr<cast_channel::ConnectInfo> connect_info_;
scoped_ptr<net::IPEndPoint> ip_endpoint_; scoped_ptr<net::IPEndPoint> ip_endpoint_;
api::cast_channel::ChannelAuthType channel_auth_; cast_channel::ChannelAuthType channel_auth_;
FRIEND_TEST_ALL_PREFIXES(CastChannelOpenFunctionTest, TestParseChannelUrl); FRIEND_TEST_ALL_PREFIXES(CastChannelOpenFunctionTest, TestParseChannelUrl);
FRIEND_TEST_ALL_PREFIXES(CastChannelOpenFunctionTest, TestParseConnectInfo); FRIEND_TEST_ALL_PREFIXES(CastChannelOpenFunctionTest, TestParseConnectInfo);
......
...@@ -46,7 +46,7 @@ TEST(CastChannelOpenFunctionTest, TestParseChannelUrl) { ...@@ -46,7 +46,7 @@ TEST(CastChannelOpenFunctionTest, TestParseChannelUrl) {
EXPECT_FALSE(ccof::ParseChannelUrl(GURL("cast://192.0.0.1:"), &connect_info)); EXPECT_FALSE(ccof::ParseChannelUrl(GURL("cast://192.0.0.1:"), &connect_info));
} }
// Tests validation of ConnectInfo. // Tests parsing of ConnectInfo.
TEST(CastChannelOpenFunctionTest, TestParseConnectInfo) { TEST(CastChannelOpenFunctionTest, TestParseConnectInfo) {
typedef CastChannelOpenFunction ccof; typedef CastChannelOpenFunction ccof;
scoped_ptr<net::IPEndPoint> ip_endpoint; scoped_ptr<net::IPEndPoint> ip_endpoint;
...@@ -60,30 +60,6 @@ TEST(CastChannelOpenFunctionTest, TestParseConnectInfo) { ...@@ -60,30 +60,6 @@ TEST(CastChannelOpenFunctionTest, TestParseConnectInfo) {
ip_endpoint.reset(ccof::ParseConnectInfo(connect_info)); ip_endpoint.reset(ccof::ParseConnectInfo(connect_info));
EXPECT_TRUE(ip_endpoint.get() != NULL); EXPECT_TRUE(ip_endpoint.get() != NULL);
EXPECT_EQ(ip_endpoint->ToString(), "192.0.0.1:8009"); EXPECT_EQ(ip_endpoint->ToString(), "192.0.0.1:8009");
// Invalid IP
ConnectInfo invalid_ip_connect_info;
invalid_ip_connect_info.ip_address = "blargh";
invalid_ip_connect_info.port = 8009;
invalid_ip_connect_info.auth = CHANNEL_AUTH_TYPE_SSL;
ip_endpoint.reset(ccof::ParseConnectInfo(invalid_ip_connect_info));
EXPECT_TRUE(ip_endpoint.get() == NULL);
// Invalid port
ConnectInfo invalid_port_connect_info;
invalid_port_connect_info.ip_address = "192.0.0.1";
invalid_port_connect_info.port = -1;
invalid_port_connect_info.auth = CHANNEL_AUTH_TYPE_SSL;
ip_endpoint.reset(ccof::ParseConnectInfo(invalid_port_connect_info));
EXPECT_TRUE(ip_endpoint.get() == NULL);
// Invalid auth
ConnectInfo invalid_auth_connect_info;
invalid_auth_connect_info.ip_address = "192.0.0.1";
invalid_auth_connect_info.port = 8009;
invalid_auth_connect_info.auth = CHANNEL_AUTH_TYPE_NONE;
ip_endpoint.reset(ccof::ParseConnectInfo(invalid_auth_connect_info));
EXPECT_TRUE(ip_endpoint.get() == NULL);
} }
} // namespace cast_channel } // namespace cast_channel
......
...@@ -8,7 +8,9 @@ ...@@ -8,7 +8,9 @@
#include "chrome/browser/extensions/api/cast_channel/cast_channel_api.h" #include "chrome/browser/extensions/api/cast_channel/cast_channel_api.h"
#include "chrome/browser/extensions/api/cast_channel/cast_socket.h" #include "chrome/browser/extensions/api/cast_channel/cast_socket.h"
#include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_function_test_utils.h"
#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/extensions/api/cast_channel.h" #include "chrome/common/extensions/api/cast_channel.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "extensions/common/switches.h" #include "extensions/common/switches.h"
...@@ -23,6 +25,9 @@ using cast_channel::CastSocket; ...@@ -23,6 +25,9 @@ using cast_channel::CastSocket;
using cast_channel::ChannelError; using cast_channel::ChannelError;
using cast_channel::MessageInfo; using cast_channel::MessageInfo;
using cast_channel::ReadyState; using cast_channel::ReadyState;
using extensions::Extension;
namespace utils = extension_function_test_utils;
using ::testing::_; using ::testing::_;
using ::testing::A; using ::testing::A;
...@@ -130,6 +135,22 @@ class CastChannelAPITest : public ExtensionApiTest { ...@@ -130,6 +135,22 @@ class CastChannelAPITest : public ExtensionApiTest {
api->OnMessage(cast_socket, message_info); api->OnMessage(cast_socket, message_info);
} }
extensions::CastChannelOpenFunction* CreateOpenFunction(
scoped_refptr<Extension> extension) {
extensions::CastChannelOpenFunction* cast_channel_open_function =
new extensions::CastChannelOpenFunction;
cast_channel_open_function->set_extension(extension.get());
return cast_channel_open_function;
}
extensions::CastChannelSendFunction* CreateSendFunction(
scoped_refptr<Extension> extension) {
extensions::CastChannelSendFunction* cast_channel_send_function =
new extensions::CastChannelSendFunction;
cast_channel_send_function->set_extension(extension.get());
return cast_channel_send_function;
}
MockCastSocket* mock_cast_socket_; MockCastSocket* mock_cast_socket_;
net::CapturingNetLog capturing_net_log_; net::CapturingNetLog capturing_net_log_;
}; };
...@@ -259,3 +280,103 @@ IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenError) { ...@@ -259,3 +280,103 @@ IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenError) {
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
} }
IN_PROC_BROWSER_TEST_F(CastChannelAPITest, TestOpenInvalidConnectInfo) {
scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension());
scoped_refptr<extensions::CastChannelOpenFunction> cast_channel_open_function;
// Invalid URL
// TODO(mfoltz): Remove this test case when fixing crbug.com/331905
cast_channel_open_function = CreateOpenFunction(empty_extension);
std::string error(utils::RunFunctionAndReturnError(
cast_channel_open_function.get(), "[\"blargh\"]", browser()));
EXPECT_EQ(error, "Invalid connect_info (invalid Cast URL blargh)");
// Wrong type
// TODO(mfoltz): Remove this test case when fixing crbug.com/331905
cast_channel_open_function = CreateOpenFunction(empty_extension);
error = utils::RunFunctionAndReturnError(
cast_channel_open_function.get(),
"[123]", browser());
EXPECT_EQ(error, "Invalid connect_info (unknown type)");
// Invalid IP address
cast_channel_open_function = CreateOpenFunction(empty_extension);
error = utils::RunFunctionAndReturnError(
cast_channel_open_function.get(),
"[{\"ipAddress\": \"invalid_ip\", \"port\": 8009, \"auth\": \"ssl\"}]",
browser());
EXPECT_EQ(error, "Invalid connect_info (invalid IP address)");
// Invalid port
cast_channel_open_function = CreateOpenFunction(empty_extension);
error = utils::RunFunctionAndReturnError(
cast_channel_open_function.get(),
"[{\"ipAddress\": \"127.0.0.1\", \"port\": -200, \"auth\": \"ssl\"}]",
browser());
EXPECT_EQ(error, "Invalid connect_info (invalid port)");
// Auth not set
cast_channel_open_function = CreateOpenFunction(empty_extension);
error = utils::RunFunctionAndReturnError(
cast_channel_open_function.get(),
"[{\"ipAddress\": \"127.0.0.1\", \"port\": 8009}]",
browser());
EXPECT_EQ(error, "connect_info.auth is required");
}
IN_PROC_BROWSER_TEST_F(CastChannelAPITest, TestSendInvalidMessageInfo) {
scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension());
scoped_refptr<extensions::CastChannelSendFunction> cast_channel_send_function;
// Numbers are not supported
cast_channel_send_function = CreateSendFunction(empty_extension);
std::string error(utils::RunFunctionAndReturnError(
cast_channel_send_function.get(),
"[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", "
"\"connectInfo\": "
"{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, "
"\"auth\": \"ssl\"}, \"readyState\": \"open\"}, "
"{\"namespace_\": \"foo\", \"sourceId\": \"src\", "
"\"destinationId\": \"dest\", \"data\": 1235}]",
browser()));
EXPECT_EQ(error, "Invalid type of message_info.data");
// Missing namespace_
cast_channel_send_function = CreateSendFunction(empty_extension);
error = utils::RunFunctionAndReturnError(
cast_channel_send_function.get(),
"[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", "
"\"connectInfo\": "
"{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, "
"\"auth\": \"ssl\"}, \"readyState\": \"open\"}, "
"{\"namespace_\": \"\", \"sourceId\": \"src\", "
"\"destinationId\": \"dest\", \"data\": \"data\"}]",
browser());
EXPECT_EQ(error, "message_info.namespace_ is required");
// Missing source_id
cast_channel_send_function = CreateSendFunction(empty_extension);
error = utils::RunFunctionAndReturnError(
cast_channel_send_function.get(),
"[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", "
"\"connectInfo\": "
"{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, "
"\"auth\": \"ssl\"}, \"readyState\": \"open\"}, "
"{\"namespace_\": \"foo\", \"sourceId\": \"\", "
"\"destinationId\": \"dest\", \"data\": \"data\"}]",
browser());
EXPECT_EQ(error, "message_info.source_id is required");
// Missing destination_id
cast_channel_send_function = CreateSendFunction(empty_extension);
error = utils::RunFunctionAndReturnError(
cast_channel_send_function.get(),
"[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", "
"\"connectInfo\": "
"{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, "
"\"auth\": \"ssl\"}, \"readyState\": \"open\"}, "
"{\"namespace_\": \"foo\", \"sourceId\": \"src\", "
"\"destinationId\": \"\", \"data\": \"data\"}]",
browser());
EXPECT_EQ(error, "message_info.destination_id is required");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment