Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Greaselion dependency from Rewards service (v2) #25949

Merged
merged 1 commit into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions browser/about_flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,14 @@
FEATURE_VALUE_TYPE( \
brave_rewards::features::kAnimatedBackgroundFeature), \
}, \
{ \
"brave-rewards-platform-creator-detection", \
"Detect Brave Creators on media platform sites", \
"Enables detection of Brave Creator pages on media platform sites.", \
kOsDesktop | kOsAndroid, \
FEATURE_VALUE_TYPE( \
brave_rewards::features::kPlatformCreatorDetectionFeature), \
}, \
{ \
"brave-ads-should-launch-brave-ads-as-an-in-process-service", \
"Launch Brave Ads as an in-process service", \
Expand Down
11 changes: 5 additions & 6 deletions browser/brave_rewards/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. */

import("//brave/components/greaselion/browser/buildflags/buildflags.gni")
import("//extensions/buildflags/buildflags.gni")
import("//testing/test.gni")

source_set("brave_rewards") {
sources = [
"creator_detection_script_injector.h",
"rewards_service_factory.h",
"rewards_tab_helper.h",
]

deps = [
"//base",
"//brave/components/brave_rewards/browser",
"//brave/components/script_injector/common/mojom",
"//chrome/browser/profiles",
"//components/keyed_service/content",
"//components/sessions",
Expand All @@ -25,6 +26,7 @@ source_set("brave_rewards") {

source_set("brave_rewards_impl") {
sources = [
"creator_detection_script_injector.cc",
"rewards_prefs_util.cc",
"rewards_prefs_util.h",
"rewards_service_factory.cc",
Expand All @@ -40,7 +42,8 @@ source_set("brave_rewards_impl") {
"//brave/browser/ui:brave_rewards_source",
"//brave/components/brave_rewards/browser",
"//brave/components/brave_rewards/common",
"//brave/components/greaselion/browser/buildflags",
"//brave/components/brave_rewards/common:features",
"//brave/components/brave_rewards/resources/creator_detection:creator_detection_generated_grit",
"//chrome/browser/bitmap_fetcher",
"//chrome/browser/favicon",
"//chrome/browser/profiles",
Expand All @@ -65,10 +68,6 @@ source_set("brave_rewards_impl") {

deps += [ "//extensions/browser" ]
}

if (enable_greaselion) {
deps += [ "//brave/browser/greaselion" ]
}
}

source_set("util") {
Expand Down
173 changes: 173 additions & 0 deletions browser/brave_rewards/creator_detection_script_injector.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

#include "brave/browser/brave_rewards/creator_detection_script_injector.h"

#include <utility>

#include "base/containers/fixed_flat_map.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "brave/browser/brave_rewards/rewards_util.h"
#include "brave/components/brave_rewards/common/features.h"
#include "brave/components/brave_rewards/common/pref_names.h"
#include "brave/components/brave_rewards/common/publisher_utils.h"
#include "brave/components/brave_rewards/resources/grit/creator_detection_generated.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_isolated_world_ids.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_client.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "ui/base/resource/resource_bundle.h"

namespace brave_rewards {

namespace {

constexpr auto kScriptMap = base::MakeFixedFlatMap<std::string_view, int>(
{{"github.com", IDR_CREATOR_DETECTION_GITHUB_BUNDLE_JS},
{"www.github.com", IDR_CREATOR_DETECTION_GITHUB_BUNDLE_JS},
{"gist.github.com", IDR_CREATOR_DETECTION_GITHUB_BUNDLE_JS},
{"reddit.com", IDR_CREATOR_DETECTION_REDDIT_BUNDLE_JS},
{"www.reddit.com", IDR_CREATOR_DETECTION_REDDIT_BUNDLE_JS},
{"twitch.tv", IDR_CREATOR_DETECTION_TWITCH_BUNDLE_JS},
{"www.twitch.tv", IDR_CREATOR_DETECTION_TWITCH_BUNDLE_JS},
{"twitter.com", IDR_CREATOR_DETECTION_TWITTER_BUNDLE_JS},
{"x.com", IDR_CREATOR_DETECTION_TWITTER_BUNDLE_JS},
{"vimeo.com", IDR_CREATOR_DETECTION_VIMEO_BUNDLE_JS},
{"www.youtube.com", IDR_CREATOR_DETECTION_YOUTUBE_BUNDLE_JS},
{"m.youtube.com", IDR_CREATOR_DETECTION_YOUTUBE_BUNDLE_JS}});

std::string LoadScriptResource(int id) {
auto& bundle = ui::ResourceBundle::GetSharedInstance();
if (bundle.IsGzipped(id)) {
return bundle.LoadDataResourceString(id);
}
return std::string(bundle.GetRawDataResource(id));
}

std::optional<std::string> GetDetectionScript(content::RenderFrameHost* rfh) {
// Only run scripts for the main frame.
if (!rfh || !rfh->IsInPrimaryMainFrame()) {
return std::nullopt;
}

// Only run scripts if the creator detection feature is enabled.
if (!base::FeatureList::IsEnabled(
features::kPlatformCreatorDetectionFeature)) {
return std::nullopt;
}

auto* profile = Profile::FromBrowserContext(rfh->GetBrowserContext());

// Only run scripts if the Rewards service is available for this profile.
if (!IsSupportedForProfile(profile)) {
return std::nullopt;
}

// Only run scripts if the user has enabled Brave Rewards.
if (!profile || !profile->GetPrefs()->GetBoolean(prefs::kEnabled)) {
return std::nullopt;
}

// Only run scripts for known "media platform" sites.
GURL url = rfh->GetLastCommittedURL();
if (!IsMediaPlatformURL(url)) {
return std::nullopt;
}

// Only run scripts when there is an exact hostname match.
auto iter = kScriptMap.find(url.host_piece());
if (iter == kScriptMap.end()) {
return std::nullopt;
}

return LoadScriptResource(iter->second);
}

} // namespace

CreatorDetectionScriptInjector::CreatorDetectionScriptInjector() = default;
CreatorDetectionScriptInjector::~CreatorDetectionScriptInjector() = default;

void CreatorDetectionScriptInjector::MaybeInjectScript(
content::RenderFrameHost* rfh) {
injector_.reset();
injector_host_token_ = content::GlobalRenderFrameHostToken();

if (!rfh) {
return;
}

auto script_source = GetDetectionScript(rfh);
if (!script_source) {
return;
}

injector_host_token_ = rfh->GetGlobalFrameToken();
rfh->GetRemoteAssociatedInterfaces()->GetInterface(&injector_);

// Execute the detection script. It must set `braveRewards.detectCreator` to a
// function. That function will be called by `DetectCreator`.
ExecuteScript(script_source.value(), base::DoNothing());
}

void CreatorDetectionScriptInjector::DetectCreator(
content::RenderFrameHost* rfh,
DetectCreatorCallback callback) {
if (!rfh || rfh->GetGlobalFrameToken() != injector_host_token_ ||
!injector_.is_bound()) {
std::move(callback).Run(std::nullopt);
return;
}

// Call the detection function set up by the detection script.
ExecuteScript(
"braveRewards.detectCreator()",
base::BindOnce(&CreatorDetectionScriptInjector::OnCreatorDetected,
weak_factory_.GetWeakPtr(), std::move(callback)));
}

void CreatorDetectionScriptInjector::ExecuteScript(
std::string_view script,
ExecuteScriptCallback callback) {
CHECK(injector_.is_bound());
injector_->RequestAsyncExecuteScript(
ISOLATED_WORLD_ID_BRAVE_INTERNAL, base::UTF8ToUTF16(script),
diracdeltas marked this conversation as resolved.
Show resolved Hide resolved
diracdeltas marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reported by reviewdog 🐶
[semgrep] Security hotspot found (ISOLATED_WORLD). A security-team member should analyze the code security for possible vulnerabilities.

Source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/brave-isolated-world.yaml


Cc @thypon @diracdeltas @bridiver

blink::mojom::UserActivationOption::kDoNotActivate,
blink::mojom::PromiseResultOption::kAwait, std::move(callback));
diracdeltas marked this conversation as resolved.
Show resolved Hide resolved
diracdeltas marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reported by reviewdog 🐶
[semgrep] RequestAsyncExecuteScript usages should be vet by the security-team.

References:
- https://github.com/brave/brave-browser/wiki/Security-reviews (point 13)


Source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/client/brave-execute-script.yaml


Cc @thypon @diracdeltas @bridiver

}

void CreatorDetectionScriptInjector::OnCreatorDetected(
DetectCreatorCallback callback,
base::Value value) {
Result result;
if (auto* dict = value.GetIfDict()) {
if (auto* id = dict->FindString("id")) {
bridiver marked this conversation as resolved.
Show resolved Hide resolved
result.id = *id;
}
if (auto* name = dict->FindString("name")) {
result.name = *name;
}
if (auto* url = dict->FindString("url")) {
result.url = *url;
}
if (auto* image_url = dict->FindString("imageURL")) {
result.image_url = *image_url;
}
}
std::move(callback).Run(std::move(result));
}

CreatorDetectionScriptInjector::Result::Result() = default;
CreatorDetectionScriptInjector::Result::~Result() = default;
CreatorDetectionScriptInjector::Result::Result(const Result&) = default;
CreatorDetectionScriptInjector::Result&
CreatorDetectionScriptInjector::Result::operator=(const Result&) = default;

} // namespace brave_rewards
81 changes: 81 additions & 0 deletions browser/brave_rewards/creator_detection_script_injector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

#ifndef BRAVE_BROWSER_BRAVE_REWARDS_CREATOR_DETECTION_SCRIPT_INJECTOR_H_
#define BRAVE_BROWSER_BRAVE_REWARDS_CREATOR_DETECTION_SCRIPT_INJECTOR_H_

#include <optional>
#include <string>
#include <string_view>

#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "brave/components/script_injector/common/mojom/script_injector.mojom.h"
#include "content/public/browser/global_routing_id.h"
#include "mojo/public/cpp/bindings/associated_remote.h"

namespace content {
class RenderFrameHost;
}

namespace brave_rewards {

// Responsible for detecting Brave creator information associated with media
// platform pages, using JS scripts that are injected into an isolated world.
class CreatorDetectionScriptInjector {
public:
CreatorDetectionScriptInjector();
~CreatorDetectionScriptInjector();

CreatorDetectionScriptInjector(const CreatorDetectionScriptInjector&) =
delete;
CreatorDetectionScriptInjector& operator=(
const CreatorDetectionScriptInjector&) = delete;

// Injects creator detection scripts (if appropriate) into an isolated world
// associated with the specified render frame host. The scripts are expected
// to set up a JS function that will later be called by `DetectCreator`.
void MaybeInjectScript(content::RenderFrameHost* rfh);

struct Result {
Result();
~Result();
Result(const Result&);
Result& operator=(const Result&);

std::string id;
std::string name;
std::string url;
std::string image_url;
};

using DetectCreatorCallback = base::OnceCallback<void(std::optional<Result>)>;

// Runs the creator detection routine initialized by `MaybeInjectScript` and
// asynchrounously returns the detection result. Return `nullopt` if the
// detection routine was not invoked (e.g. because Rewards is not enabled or
// because there is no script for this page). Returns a `Result` with empty
// fields if there is not creator associated with the current page. Note that
// any of the `Result` fields may be empty if the detection script was unable
// to gather that information from the page.
void DetectCreator(content::RenderFrameHost* rfh,
DetectCreatorCallback callback);

private:
using ExecuteScriptCallback = base::OnceCallback<void(base::Value)>;

void ExecuteScript(std::string_view script, ExecuteScriptCallback callback);

void OnCreatorDetected(DetectCreatorCallback callback, base::Value value);

mojo::AssociatedRemote<script_injector::mojom::ScriptInjector> injector_;
content::GlobalRenderFrameHostToken injector_host_token_;
base::WeakPtrFactory<CreatorDetectionScriptInjector> weak_factory_{this};
};

} // namespace brave_rewards

#endif // BRAVE_BROWSER_BRAVE_REWARDS_CREATOR_DETECTION_SCRIPT_INJECTOR_H_
11 changes: 0 additions & 11 deletions browser/brave_rewards/rewards_service_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include "brave/components/brave_rewards/browser/rewards_service.h"
#include "brave/components/brave_rewards/browser/rewards_service_impl.h"
#include "brave/components/brave_rewards/browser/rewards_service_observer.h"
#include "brave/components/greaselion/browser/buildflags/buildflags.h"
#include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service_factory.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/profiles/incognito_helpers.h"
Expand All @@ -31,10 +30,6 @@
#include "extensions/browser/event_router_factory.h"
#endif

#if BUILDFLAG(ENABLE_GREASELION)
#include "brave/browser/greaselion/greaselion_service_factory.h"
#endif

namespace brave_rewards {

RewardsService* testing_service_ = nullptr;
Expand Down Expand Up @@ -65,9 +60,6 @@ RewardsServiceFactory::RewardsServiceFactory()
BrowserContextDependencyManager::GetInstance()) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
DependsOn(extensions::EventRouterFactory::GetInstance());
#endif
#if BUILDFLAG(ENABLE_GREASELION)
DependsOn(greaselion::GreaselionServiceFactory::GetInstance());
#endif
DependsOn(brave_wallet::BraveWalletServiceFactory::GetInstance());
}
Expand Down Expand Up @@ -123,9 +115,6 @@ RewardsServiceFactory::BuildServiceInstanceForBrowserContext(
profile, ServiceAccessType::EXPLICIT_ACCESS),
request_image_callback, cancel_request_image_callback,
profile->GetDefaultStoragePartition(),
#if BUILDFLAG(ENABLE_GREASELION)
greaselion::GreaselionServiceFactory::GetForBrowserContext(context),
#endif
brave_wallet::BraveWalletServiceFactory::GetServiceForContext(
context));
rewards_service->Init(std::move(extension_observer),
Expand Down
Loading
Loading