Skip to content

Commit

Permalink
Fixes leaking of full version via navigator.userAgentData.getHighEntr…
Browse files Browse the repository at this point in the history
…opyValues

Fixes brave/brave-browser#23491

It seems uaFullVersion was always leaking but the fullVersionList
started leaking because of the change in
#14155 where brand was added
to GetUserAgentBrandList function in
components/embedder_support/user_agent_utils.cc which broke the
BraveContentBrowserClient::GetUserAgentMetadata expectation that the
brand list would only contain 2 items (instead of now 3).

This fix adjusts the BraveContentBrowserClient::GetUserAgentMetadata
expectations and removes adding the Brave brand to the lists because
it's already there. Now we just need to zero out 3 last components of
the full versions list and uaFullVersion string.

Also, adds a browser test to check the sizes of the lists and versions
values.
  • Loading branch information
mkarolin committed Nov 30, 2022
1 parent 9f10690 commit 964ee7b
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 35 deletions.
44 changes: 13 additions & 31 deletions browser/brave_content_browser_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1066,37 +1066,19 @@ void BraveContentBrowserClient::OverrideWebkitPrefs(WebContents* web_contents,
blink::UserAgentMetadata BraveContentBrowserClient::GetUserAgentMetadata() {
blink::UserAgentMetadata metadata =
ChromeContentBrowserClient::GetUserAgentMetadata();
if (metadata.brand_version_list.size() == 2) {
// some logic copied from upstream GetUserAgentBrandList and
// GenerateBrandVersionList
std::string major_version_string = version_info::GetMajorVersionNumber();
int seed = 0;
DCHECK(base::StringToInt(major_version_string, &seed));
DCHECK_GE(seed, 0);
blink::UserAgentBrandVersion greasey_bv =
metadata.brand_version_list[seed % 2];
blink::UserAgentBrandVersion chromium_bv =
metadata.brand_version_list[(seed + 1) % 2];
blink::UserAgentBrandVersion brave_bv = {
l10n_util::GetStringUTF8(IDS_PRODUCT_NAME), chromium_bv.version};
const int npermutations = 6; // 3!
int permutation = seed % npermutations;
const std::vector<std::vector<int>> orders{{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
{1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
const std::vector<int> order = orders[permutation];
blink::UserAgentBrandList greased_brand_version_list(3);
greased_brand_version_list[order[0]] = greasey_bv;
greased_brand_version_list[order[1]] = chromium_bv;
greased_brand_version_list[order[2]] = brave_bv;
metadata.brand_version_list = greased_brand_version_list;
greasey_bv.version = base::StrCat({greasey_bv.version, ".0.0.0"});
chromium_bv.version = base::StrCat({chromium_bv.version, ".0.0.0"});
brave_bv.version = base::StrCat({brave_bv.version, ".0.0.0"});
blink::UserAgentBrandList greased_brand_full_version_list(3);
greased_brand_full_version_list[order[0]] = greasey_bv;
greased_brand_full_version_list[order[1]] = chromium_bv;
greased_brand_full_version_list[order[2]] = brave_bv;
metadata.brand_full_version_list = greased_brand_full_version_list;
// Expect the brand version lists to have brand version, chromium_version, and
// greased version.
DCHECK_EQ(3UL, metadata.brand_version_list.size());
DCHECK_EQ(3UL, metadata.brand_full_version_list.size());
// Zero out the last 3 version components in full version list versions.
for (auto& brand_version : metadata.brand_full_version_list) {
base::Version version(brand_version.version);
brand_version.version =
base::StrCat({base::NumberToString(version.components()[0]), ".0.0.0"});
}
// Zero out the last 3 version components in full version.
base::Version version(metadata.full_version);
metadata.full_version =
base::StrCat({base::NumberToString(version.components()[0]), ".0.0.0"});
return metadata;
}
96 changes: 92 additions & 4 deletions browser/farbling/brave_navigator_useragent_farbling_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
#include <memory>

#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/thread_test_helper.h"
#include "base/version.h"
#include "brave/browser/brave_content_browser_client.h"
#include "brave/browser/extensions/brave_base_local_data_files_browsertest.h"
#include "brave/components/brave_component_updater/browser/local_data_files_service.h"
Expand All @@ -25,6 +26,7 @@
#include "components/embedder_support/user_agent_utils.h"
#include "components/permissions/permission_request.h"
#include "components/prefs/pref_service.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
Expand All @@ -37,11 +39,48 @@ using brave_shields::ControlType;
using content::TitleWatcher;

namespace {
const char kUserAgentScript[] = "navigator.userAgent";
const char kBrandScript[] =

constexpr char kUserAgentScript[] = "navigator.userAgent";

constexpr char kBrandScript[] =
"navigator.userAgentData.brands[0].brand + '|' + "
"navigator.userAgentData.brands[1].brand + '|' + "
"navigator.userAgentData.brands[2].brand";

constexpr char kGetHighEntropyValuesScript[] = R"(
navigator.userAgentData.getHighEntropyValues(
["fullVersionList", "uaFullVersion"]).then(
(values) => {return values;})
)";

void CheckUserAgentMetadataVersionsList(
const base::Value::List& versions_list,
const std::string& expected_version,
std::function<void(const std::string&)> check_greased_version) {
// Expect 3 items in the list: Brave, Chromium, and greased.
EXPECT_EQ(3UL, versions_list.size());

bool has_brave_brand = false;
bool has_chromium_brand = false;
for (auto& brand_version : versions_list) {
const std::string* brand = brand_version.GetDict().FindString("brand");
ASSERT_NE(nullptr, brand);
const std::string* version = brand_version.GetDict().FindString("version");
ASSERT_NE(nullptr, version);
if (*brand == "Brave") {
has_brave_brand = true;
EXPECT_EQ(expected_version, *version);
} else if (*brand == "Chromium") {
has_chromium_brand = true;
EXPECT_EQ(expected_version, *version);
} else {
check_greased_version(*version);
}
}
EXPECT_TRUE(has_brave_brand);
EXPECT_TRUE(has_chromium_brand);
}

} // namespace

class BraveNavigatorUserAgentFarblingBrowserTest : public InProcessBrowserTest {
Expand Down Expand Up @@ -251,10 +290,59 @@ IN_PROC_BROWSER_TEST_F(BraveNavigatorUserAgentFarblingBrowserTest,

// Tests results of user agent metadata brands
IN_PROC_BROWSER_TEST_F(BraveNavigatorUserAgentFarblingBrowserTest,
AddBraveToNavigatorUserAgentBrandList) {
BraveIsInNavigatorUserAgentBrandList) {
GURL url = https_server()->GetURL("a.com", "/simple.html");
NavigateToURLUntilLoadStop(url);
std::string brands = EvalJs(contents(), kBrandScript).ExtractString();
EXPECT_NE(std::string::npos, brands.find("Brave"));
EXPECT_NE(std::string::npos, brands.find("Chromium"));
}

// Tests that user agent metadata versions are as expected.
IN_PROC_BROWSER_TEST_F(BraveNavigatorUserAgentFarblingBrowserTest,
CheckUserAgentMetadataVersions) {
GURL url = https_server()->GetURL("a.com", "/simple.html");
NavigateToURLUntilLoadStop(url);
const content::EvalJsResult result =
EvalJs(contents(), kGetHighEntropyValuesScript);
EXPECT_TRUE(result.error.empty());
const base::Value::Dict* values = result.value.GetIfDict();
ASSERT_NE(nullptr, values);

// Check brands versions
const base::Value::List* brands_list = values->FindList("brands");
ASSERT_NE(nullptr, brands_list);

// Expected major version for Brave and Chromium.
const std::string major_version = version_info::GetMajorVersionNumber();

CheckUserAgentMetadataVersionsList(
*brands_list, major_version, [](const std::string& version) {
EXPECT_EQ(std::string::npos, version.find("."));
});

// Check full versions
const base::Value::List* full_version_list =
values->FindList("fullVersionList");
ASSERT_NE(nullptr, full_version_list);

// Expected version string for Brave and Chromium.
const std::string expected_full_version =
base::StrCat({major_version, ".0.0.0"});

CheckUserAgentMetadataVersionsList(
*full_version_list, expected_full_version,
[](const std::string& version_str) {
base::Version version(version_str);
for (size_t i = 0; i < version.components().size(); i++) {
if (i > 0) {
EXPECT_EQ(0U, version.components()[i]);
}
}
});

// Check auFullVersion
const std::string* ua_full_version = values->FindString("uaFullVersion");
ASSERT_NE(nullptr, ua_full_version);
EXPECT_EQ(expected_full_version, *ua_full_version);
}

0 comments on commit 964ee7b

Please sign in to comment.