Commit a838aec4 authored by harkness's avatar harkness Committed by Commit bot

Budget API calls should only succeed on secure origins

BUG=617971

Review-Url: https://codereview.chromium.org/2366533002
Cr-Commit-Position: refs/heads/master@{#420630}
parent 7d1b9476
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "components/pref_registry/pref_registry_syncable.h" #include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/common/origin_util.h"
#include "third_party/WebKit/public/platform/modules/budget_service/budget_service.mojom.h" #include "third_party/WebKit/public/platform/modules/budget_service/budget_service.mojom.h"
#include "url/origin.h" #include "url/origin.h"
...@@ -68,12 +69,22 @@ double BudgetManager::GetCost(blink::mojom::BudgetOperationType type) { ...@@ -68,12 +69,22 @@ double BudgetManager::GetCost(blink::mojom::BudgetOperationType type) {
void BudgetManager::GetBudget(const url::Origin& origin, void BudgetManager::GetBudget(const url::Origin& origin,
const GetBudgetCallback& callback) { const GetBudgetCallback& callback) {
if (origin.unique() || !content::IsOriginSecure(GURL(origin.Serialize()))) {
callback.Run(blink::mojom::BudgetServiceErrorType::NOT_SUPPORTED,
mojo::Array<blink::mojom::BudgetStatePtr>());
return;
}
db_.GetBudgetDetails(origin, callback); db_.GetBudgetDetails(origin, callback);
} }
void BudgetManager::Reserve(const url::Origin& origin, void BudgetManager::Reserve(const url::Origin& origin,
blink::mojom::BudgetOperationType type, blink::mojom::BudgetOperationType type,
const ReserveCallback& callback) { const ReserveCallback& callback) {
if (origin.unique() || !content::IsOriginSecure(GURL(origin.Serialize()))) {
callback.Run(blink::mojom::BudgetServiceErrorType::NOT_SUPPORTED,
false /* success */);
return;
}
db_.SpendBudget(origin, GetCost(type), db_.SpendBudget(origin, GetCost(type),
base::Bind(&BudgetManager::DidReserve, base::Bind(&BudgetManager::DidReserve,
weak_ptr_factory_.GetWeakPtr(), origin, callback)); weak_ptr_factory_.GetWeakPtr(), origin, callback));
...@@ -82,6 +93,11 @@ void BudgetManager::Reserve(const url::Origin& origin, ...@@ -82,6 +93,11 @@ void BudgetManager::Reserve(const url::Origin& origin,
void BudgetManager::Consume(const url::Origin& origin, void BudgetManager::Consume(const url::Origin& origin,
blink::mojom::BudgetOperationType type, blink::mojom::BudgetOperationType type,
const ConsumeCallback& callback) { const ConsumeCallback& callback) {
if (origin.unique() || !content::IsOriginSecure(GURL(origin.Serialize()))) {
callback.Run(false /* success */);
return;
}
bool found_reservation = false; bool found_reservation = false;
// First, see if there is a reservation already. // First, see if there is a reservation already.
......
...@@ -37,16 +37,18 @@ class BudgetManagerTest : public testing::Test { ...@@ -37,16 +37,18 @@ class BudgetManagerTest : public testing::Test {
void SetSiteEngagementScore(double score) { void SetSiteEngagementScore(double score) {
SiteEngagementService* service = SiteEngagementService::Get(&profile_); SiteEngagementService* service = SiteEngagementService::Get(&profile_);
service->ResetScoreForURL(GURL(kTestOrigin), score); service->ResetScoreForURL(GURL(origin().Serialize()), score);
} }
Profile* profile() { return &profile_; } Profile* profile() { return &profile_; }
const url::Origin origin() const { return origin_; } const url::Origin origin() const { return origin_; }
void SetOrigin(const url::Origin& origin) { origin_ = origin; }
void ReserveCallback(base::Closure run_loop_closure, void ReserveCallback(base::Closure run_loop_closure,
blink::mojom::BudgetServiceErrorType error, blink::mojom::BudgetServiceErrorType error,
bool success) { bool success) {
success_ = (error == blink::mojom::BudgetServiceErrorType::NONE) && success; success_ = (error == blink::mojom::BudgetServiceErrorType::NONE) && success;
error_ = error;
run_loop_closure.Run(); run_loop_closure.Run();
} }
...@@ -77,11 +79,12 @@ class BudgetManagerTest : public testing::Test { ...@@ -77,11 +79,12 @@ class BudgetManagerTest : public testing::Test {
// Members for callbacks to set. // Members for callbacks to set.
bool success_; bool success_;
blink::mojom::BudgetServiceErrorType error_;
private: private:
content::TestBrowserThreadBundle thread_bundle_; content::TestBrowserThreadBundle thread_bundle_;
TestingProfile profile_; TestingProfile profile_;
const url::Origin origin_; url::Origin origin_;
}; };
TEST_F(BudgetManagerTest, GetBudgetConsumedOverTime) { TEST_F(BudgetManagerTest, GetBudgetConsumedOverTime) {
...@@ -107,3 +110,26 @@ TEST_F(BudgetManagerTest, GetBudgetConsumedOverTime) { ...@@ -107,3 +110,26 @@ TEST_F(BudgetManagerTest, GetBudgetConsumedOverTime) {
// available. // available.
ASSERT_FALSE(ConsumeBudget(type)); ASSERT_FALSE(ConsumeBudget(type));
} }
TEST_F(BudgetManagerTest, TestInsecureOrigin) {
const blink::mojom::BudgetOperationType type =
blink::mojom::BudgetOperationType::SILENT_PUSH;
SetOrigin(url::Origin(GURL("http://example.com")));
SetSiteEngagementScore(kTestSES);
// Methods on the BudgetManager should only be allowed for secure origins.
ASSERT_FALSE(ReserveBudget(type));
ASSERT_EQ(blink::mojom::BudgetServiceErrorType::NOT_SUPPORTED, error_);
ASSERT_FALSE(ConsumeBudget(type));
}
TEST_F(BudgetManagerTest, TestUniqueOrigin) {
const blink::mojom::BudgetOperationType type =
blink::mojom::BudgetOperationType::SILENT_PUSH;
SetOrigin(url::Origin(GURL("file://example.com:443/etc/passwd")));
// Methods on the BudgetManager should not be allowed for unique origins.
ASSERT_FALSE(ReserveBudget(type));
ASSERT_EQ(blink::mojom::BudgetServiceErrorType::NOT_SUPPORTED, error_);
ASSERT_FALSE(ConsumeBudget(type));
}
...@@ -59,6 +59,10 @@ ScriptPromise BudgetService::getCost(ScriptState* scriptState, const AtomicStrin ...@@ -59,6 +59,10 @@ ScriptPromise BudgetService::getCost(ScriptState* scriptState, const AtomicStrin
{ {
DCHECK(m_service); DCHECK(m_service);
String errorMessage;
if (!scriptState->getExecutionContext()->isSecureContext(errorMessage))
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, errorMessage));
mojom::blink::BudgetOperationType type = stringToOperationType(operation); mojom::blink::BudgetOperationType type = stringToOperationType(operation);
if (type == mojom::blink::BudgetOperationType::INVALID_OPERATION) if (type == mojom::blink::BudgetOperationType::INVALID_OPERATION)
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, "Invalid operation type specified")); return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, "Invalid operation type specified"));
...@@ -80,12 +84,15 @@ ScriptPromise BudgetService::getBudget(ScriptState* scriptState) ...@@ -80,12 +84,15 @@ ScriptPromise BudgetService::getBudget(ScriptState* scriptState)
{ {
DCHECK(m_service); DCHECK(m_service);
String errorMessage;
if (!scriptState->getExecutionContext()->isSecureContext(errorMessage))
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, errorMessage));
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise(); ScriptPromise promise = resolver->promise();
// Get the budget from the browser BudgetService. // Get the budget from the browser BudgetService.
RefPtr<SecurityOrigin> origin(scriptState->getExecutionContext()->getSecurityOrigin()); RefPtr<SecurityOrigin> origin(scriptState->getExecutionContext()->getSecurityOrigin());
// TODO(harkness): Check that this is a valid secure origin.
m_service->GetBudget(origin, convertToBaseCallback(WTF::bind(&BudgetService::gotBudget, wrapPersistent(this), wrapPersistent(resolver)))); m_service->GetBudget(origin, convertToBaseCallback(WTF::bind(&BudgetService::gotBudget, wrapPersistent(this), wrapPersistent(resolver))));
return promise; return promise;
} }
...@@ -113,12 +120,15 @@ ScriptPromise BudgetService::reserve(ScriptState* scriptState, const AtomicStrin ...@@ -113,12 +120,15 @@ ScriptPromise BudgetService::reserve(ScriptState* scriptState, const AtomicStrin
if (type == mojom::blink::BudgetOperationType::INVALID_OPERATION) if (type == mojom::blink::BudgetOperationType::INVALID_OPERATION)
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, "Invalid operation type specified")); return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, "Invalid operation type specified"));
String errorMessage;
if (!scriptState->getExecutionContext()->isSecureContext(errorMessage))
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, errorMessage));
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise(); ScriptPromise promise = resolver->promise();
// Call to the BudgetService to place the reservation. // Call to the BudgetService to place the reservation.
RefPtr<SecurityOrigin> origin(scriptState->getExecutionContext()->getSecurityOrigin()); RefPtr<SecurityOrigin> origin(scriptState->getExecutionContext()->getSecurityOrigin());
// TODO(harkness): Check that this is a valid secure origin.
m_service->Reserve(origin, type, convertToBaseCallback(WTF::bind(&BudgetService::gotReservation, wrapPersistent(this), wrapPersistent(resolver)))); m_service->Reserve(origin, type, convertToBaseCallback(WTF::bind(&BudgetService::gotReservation, wrapPersistent(this), wrapPersistent(resolver))));
return promise; return promise;
} }
......
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