Rework npm run network-audit into a browser test suite (#10389)
* Rework npm run network-audit into a browser test suite This change adds a new browser test to brave_browser_tests that runs similar tests to what Brave used to do when running the network-audit npm script, via the --network_log command line parameter. As with that previous script, this test makes sure Brave is launched with --net-log-capture-mode=Everything and --log-net-log, and that the network_log.json and network-audit-results.json files are written to disk after the network audit process is completed. However, in this case we're also prepending the name of the browser test to the names of such files, so that we can have more than one browser test without overriding each others' results. Last, this tests adds two browser tests to check both the case of loading the simple.html file via the embedded test server and the more real world scenario of loading brave://welcome, in a final attempt to replicate what npm run network-audit did for me on a recent build (and also as a way to validate that having multiple browser tests will work fine). Resolves https://github.com/brave/brave-browser/issues/7207 Resolves https://github.com/brave/brave-browser/issues/7281 * Remove all trace of npm run network-audit and replace it where needed Drop code related to the npm run network-audit command, including the command itself, and then make sure that the newly added browser tests are run as part of the npm run test-security command. Also, this change moves the definitions of the three whitelists used by the network audit process (i.e. protocols, prefixes and patterns) to a separate header file so that we can adapt .github/CODEOWNERS to only cover those lists and not the browser tests themselves. Last, also adapt Jenkinsfile to remove all references to the former npm run network-audit command. * Remove Google-owned prefixes and patterns from network audit whitelist As noted by @diracdeltas on Slack, these entries should be removed as they correspond with internal 307 responses that should not be going out to the network, so we're dropping them as part of this work. * Extract brave_network_audit_browsertest.cc to a separate binary A proper re-implementation of these tests will require to "leave the browser open" for at least 2 minutes, for which we'd need to pass a big timeout parameters when running the browser tests, delaying the execution of the brave_browser_tests for no good reason. Instead, we create a new test suite 'brave_network_audit_tests' that will exclusively perform the network audit process, so that we can specify a different timeout only for it (in a follow-up patch). * Force BraveNetworkAuditTest tests to keep the browser open after load Similarly to what was done via the npm run network-audit script, we make sure that we wait ~2 minutes after loading an URL to make sure we gather enough information (i.e. network requests) before verifying that no allowed URL requests are made during that time. In one hand, this means explicitly waiting test until such amount of time has passed in the tests themselves. In the other hand, this also means passing specific timeout values to the brave_network_audit_tests when running it via the npm run test-security script. * Simplify BraveNetworkAuditTest test suite by having one browser test Having separate tests was nice in order to be able to run them in parallel but, at the same time, was probably not necessary because we want to test the behaviour of firing up one browser and monitoring network requests after loading some URLs, so probably better to go back to having one test only. Additionally, this allows simplifying a bit the creation of the json files with the log of the requests and the result of the audit, which don't need having a test-based prefix anymore. * Whitelist https://laptop-updates-staging.brave.com for network audit This staging-related URL might be present on devs' machines if they have it set on their .npmrc file, for instance, so let's add it to the whitelisted entries similarly to how other staging-related URLs are already present there. * Don't whitelist http://componentupdater.brave.com/service/update2 As suggested by @diracdeltas, we can remove the HTTP version of this URL from the whitelist for the network audit (see [1]). [1] github.com/brave/brave-core/pull/10389#discussion_r723587821 * Increase timeout for network-audit browser tests to 5min after loads It seems some network requests might take longer than 2 minutes to happen, so let's increase the timeout to 5 minutes after loading the URLs, and adapt the caller script to account for that. * Also check network requests on brave://rewards with Brave Rewards enabled Add one more case to the network audit process to double check whether only allowed network requests happen when Brave Rewards is enabled. * Whitelist Brave Rewards-related URL prefixes for the network audit This means whitelisting the following prefixes, found to be hit in the test after enabling Rewards and waiting for ~5 minutes: On production environments (e.g. Relese builds on CI): - https://api.rewards.brave.com/v1/parameters - https://rewards.brave.com/publishers/prefix-list - https://grant.rewards.brave.com/v1/promotions On development environments: - https://api.rewards.bravesoftware.com/v1/parameters", - https://rewards-stg.bravesoftware.com/publishers/prefix-list", - https://grant.rewards.bravesoftware.com/v1/promotions", * Restore network-audit and define test-security in terms of it * Disable the ThinLTO cache for the brave_network_audit_tests GN target We need to disable the ThinLTO cache or the linker will die with a "Resource temporarily unavailable" error due to the linking reaching the vm.max_map_count limit of 65530 memory mappings. * Use JSONFileValueSerializer instead of base::JSONWriter * Replaced use of deprecated APIs with the correct ones
This commit is contained in:
+1
-2
@@ -103,8 +103,7 @@ patches/*.java.patch @samartnik
|
||||
third_party/bitcoin-core/BUILD.gn @orspetol
|
||||
|
||||
# Network auditor
|
||||
build/commands/lib/whitelistedUrlPatterns.js @brave/sec-team
|
||||
build/commands/lib/whitelistedUrlPrefixes.js @brave/sec-team
|
||||
browser/net/brave_network_audit_whitelists.h @brave/sec-team
|
||||
|
||||
# iOS
|
||||
ios/ @brave/ios
|
||||
|
||||
@@ -159,7 +159,10 @@ if (!is_ios) {
|
||||
deps = [ "test:brave_unit_tests" ]
|
||||
|
||||
if (!is_android) {
|
||||
deps += [ "test:brave_browser_tests" ]
|
||||
deps += [
|
||||
"test:brave_browser_tests",
|
||||
"test:brave_network_audit_tests",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/base_paths.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/files/scoped_temp_dir.h"
|
||||
#include "base/json/json_file_value_serializer.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/test/bind.h"
|
||||
#include "base/test/test_timeouts.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "base/time/time.h"
|
||||
#include "brave/browser/brave_rewards/rewards_service_factory.h"
|
||||
#include "brave/browser/net/brave_network_audit_whitelists.h"
|
||||
#include "brave/components/brave_rewards/browser/rewards_service_impl.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/ui/browser.h"
|
||||
#include "chrome/test/base/in_process_browser_test.h"
|
||||
#include "chrome/test/base/ui_test_utils.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "content/public/test/browser_test.h"
|
||||
#include "services/network/public/cpp/network_switches.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "third_party/re2/src/re2/re2.h"
|
||||
|
||||
namespace brave {
|
||||
namespace {
|
||||
|
||||
// Max amount of time to wait after getting an URL loaded, in milliseconds. Note
|
||||
// that the value passed to --ui-test-action-timeout in //brave/package.json, as
|
||||
// part of the 'network-audit' script, must be big enough to accomodate this.
|
||||
//
|
||||
// In particular:
|
||||
// --ui-test-action-timeout: should be greater than |kMaxTimeoutPerLoadedURL|.
|
||||
// --test-launcher-timeout: should be able to fit the total sum of timeouts.
|
||||
const int kMaxTimeoutPerLoadedURL = 300000;
|
||||
|
||||
// Based on the implementation of isPrivateIP() from NPM's "ip" module.
|
||||
// See https://github.com/indutny/node-ip/blob/master/lib/ip.js
|
||||
constexpr const char* kPrivateIPRegexps[] = {
|
||||
"(::f{4}:)?10\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})",
|
||||
"(::f{4}:)?192\\.168\\.([0-9]{1,3})\\.([0-9]{1,3})",
|
||||
"(::f{4}:)?172\\.(1[6-9]|2\\d|30|31)\\.([0-9]{1,3})\\.([0-9]{1,3})",
|
||||
"(::f{4}:)?127\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})",
|
||||
"(::f{4}:)?169\\.254\\.([0-9]{1,3})\\.([0-9]{1,3})",
|
||||
"f[cd][0-9a-f]{2}:.*",
|
||||
"fe80:.*",
|
||||
"::1",
|
||||
"::"};
|
||||
|
||||
void WaitForTimeout(int timeout) {
|
||||
base::RunLoop run_loop;
|
||||
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
||||
FROM_HERE, run_loop.QuitClosure(),
|
||||
base::TimeDelta::FromMilliseconds(timeout));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
bool isPrivateURL(const GURL& url) {
|
||||
for (const char* regexp : kPrivateIPRegexps) {
|
||||
if (RE2::FullMatch(url.host(), regexp)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PerformNetworkAuditProcess(base::Value* events) {
|
||||
DCHECK(events && events->is_list());
|
||||
|
||||
bool failed = false;
|
||||
events->EraseListValueIf([&failed](base::Value& event_value) {
|
||||
base::DictionaryValue* event_dict;
|
||||
EXPECT_TRUE(event_value.GetAsDictionary(&event_dict));
|
||||
|
||||
absl::optional<int> event_type = event_dict->FindIntPath("type");
|
||||
EXPECT_TRUE(event_type.has_value());
|
||||
|
||||
// Showing these helps determine which URL requests which don't
|
||||
// actually hit the network.
|
||||
if (static_cast<net::NetLogEventType>(event_type.value()) ==
|
||||
net::NetLogEventType::URL_REQUEST_FAKE_RESPONSE_HEADERS_CREATED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const base::Value* source_dict = event_dict->FindDictPath("source");
|
||||
EXPECT_TRUE(source_dict);
|
||||
|
||||
// Consider URL requests only.
|
||||
absl::optional<int> source_type = source_dict->FindIntPath("type");
|
||||
EXPECT_TRUE(source_type.has_value());
|
||||
|
||||
if (static_cast<net::NetLogSourceType>(source_type.value()) !=
|
||||
net::NetLogSourceType::URL_REQUEST) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Discard events without URLs in the parameters.
|
||||
if (!event_dict->FindKey("params"))
|
||||
return true;
|
||||
|
||||
const base::Value* params_dict2 = event_dict->FindDictPath("params");
|
||||
EXPECT_TRUE(params_dict2);
|
||||
|
||||
if (!params_dict2->FindKey("url"))
|
||||
return true;
|
||||
|
||||
const std::string* url_str = params_dict2->FindStringPath("url");
|
||||
EXPECT_TRUE(url_str);
|
||||
|
||||
GURL url(*url_str);
|
||||
EXPECT_TRUE(url.is_valid());
|
||||
|
||||
if (RE2::FullMatch(url.host(), "[a-z]+")) {
|
||||
// Chromium sometimes sends requests to random non-resolvable hosts.
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const char* protocol : kWhitelistedUrlProtocols) {
|
||||
if (protocol == url.scheme()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool found_prefix = false;
|
||||
for (const char* prefix : kWhitelistedUrlPrefixes) {
|
||||
if (!url.spec().rfind(prefix, 0)) {
|
||||
found_prefix = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool found_pattern = false;
|
||||
for (const char* pattern : kWhitelistedUrlPatterns) {
|
||||
if (RE2::FullMatch(url.spec(), pattern)) {
|
||||
found_pattern = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_prefix && !found_pattern) {
|
||||
// Check if the URL is a private IP.
|
||||
if (isPrivateURL(url)) {
|
||||
// Warn but don't fail the audit.
|
||||
LOG(WARNING) << "NETWORK AUDIT WARNING:" << url.spec() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "NETWORK AUDIT FAIL:" << url.spec() << std::endl;
|
||||
failed = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return !failed;
|
||||
}
|
||||
|
||||
void WriteNetworkAuditResultsToDisk(const base::DictionaryValue& results_dic,
|
||||
const base::FilePath& path) {
|
||||
std::string results;
|
||||
JSONFileValueSerializer serializer(path);
|
||||
serializer.Serialize(results_dic);
|
||||
|
||||
LOG(INFO) << "Network audit results stored in " << path << std::endl;
|
||||
}
|
||||
|
||||
class BraveNetworkAuditTest : public InProcessBrowserTest {
|
||||
public:
|
||||
BraveNetworkAuditTest() = default;
|
||||
|
||||
void SetUpOnMainThread() override {
|
||||
InProcessBrowserTest::SetUpOnMainThread();
|
||||
|
||||
ASSERT_TRUE(embedded_test_server()->Start());
|
||||
|
||||
// Create and start the Rewards service
|
||||
rewards_service_ = static_cast<brave_rewards::RewardsServiceImpl*>(
|
||||
brave_rewards::RewardsServiceFactory::GetForProfile(profile()));
|
||||
base::RunLoop run_loop;
|
||||
rewards_service_->StartProcess(run_loop.QuitClosure());
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
void TearDownOnMainThread() override {
|
||||
rewards_service_->Shutdown();
|
||||
InProcessBrowserTest::TearDownOnMainThread();
|
||||
}
|
||||
|
||||
void SetUpCommandLine(base::CommandLine* command_line) override {
|
||||
base::FilePath source_root_path;
|
||||
base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_path);
|
||||
|
||||
// Full log containing all the network requests.
|
||||
net_log_path_ = source_root_path.AppendASCII("network_log.json");
|
||||
|
||||
// Log containing the results of the audit only.
|
||||
audit_results_path_ =
|
||||
source_root_path.AppendASCII("network_audit_results.json");
|
||||
|
||||
command_line->AppendSwitchPath(network::switches::kLogNetLog,
|
||||
net_log_path_);
|
||||
command_line->AppendSwitchASCII(network::switches::kNetLogCaptureMode,
|
||||
"Everything");
|
||||
}
|
||||
|
||||
void TearDownInProcessBrowserTestFixture() override {
|
||||
VerifyNetworkAuditLog();
|
||||
}
|
||||
|
||||
bool EnableBraveRewards() {
|
||||
PrefService* pref_service = profile()->GetPrefs();
|
||||
pref_service->SetInteger("brave.rewards.version", 7);
|
||||
pref_service->SetBoolean("brave.rewards.enabled", true);
|
||||
return pref_service->GetBoolean("brave.rewards.enabled");
|
||||
}
|
||||
|
||||
Profile* profile() { return browser()->profile(); }
|
||||
|
||||
private:
|
||||
// Verify that the netlog file was written, appears to be well formed, and
|
||||
// includes the requested level of data.
|
||||
void VerifyNetworkAuditLog() {
|
||||
// Read the netlog from disk.
|
||||
std::string file_contents;
|
||||
ASSERT_TRUE(base::ReadFileToString(net_log_path_, &file_contents))
|
||||
<< "Could not read: " << net_log_path_;
|
||||
|
||||
// Parse it as JSON.
|
||||
auto parsed = base::JSONReader::Read(file_contents);
|
||||
ASSERT_TRUE(parsed.has_value());
|
||||
|
||||
// Ensure the root value is a dictionary.
|
||||
base::DictionaryValue* main;
|
||||
ASSERT_TRUE(parsed->GetAsDictionary(&main));
|
||||
|
||||
// Ensure it has a "constants" property.
|
||||
base::Value* constants = main->FindDictPath("constants");
|
||||
ASSERT_TRUE(constants && constants->is_dict());
|
||||
ASSERT_FALSE(constants->DictEmpty());
|
||||
|
||||
// Ensure it has an "events" property.
|
||||
base::Value* events = main->FindListPath("events");
|
||||
ASSERT_TRUE(events && events->is_list());
|
||||
ASSERT_FALSE(events->GetList().empty());
|
||||
|
||||
EXPECT_TRUE(PerformNetworkAuditProcess(events))
|
||||
<< "network-audit FAILED. Import " << net_log_path_.AsUTF8Unsafe()
|
||||
<< " in chrome://net-internals for more details.";
|
||||
|
||||
// Write results of the audit to disk, useful for further debugging.
|
||||
WriteNetworkAuditResultsToDisk(*main, audit_results_path_);
|
||||
ASSERT_TRUE(base::PathExists(audit_results_path_));
|
||||
}
|
||||
|
||||
brave_rewards::RewardsServiceImpl* rewards_service_;
|
||||
base::FilePath net_log_path_;
|
||||
base::FilePath audit_results_path_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BraveNetworkAuditTest);
|
||||
};
|
||||
|
||||
// Loads brave://welcome first to simulate a first run and then loads another
|
||||
// URL, and finally enables brave rewards, waiting some time after each load to
|
||||
// allow gathering network requests.
|
||||
IN_PROC_BROWSER_TEST_F(BraveNetworkAuditTest, BasicTests) {
|
||||
// Load the Welcome page.
|
||||
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("brave://welcome")));
|
||||
WaitForTimeout(kMaxTimeoutPerLoadedURL);
|
||||
|
||||
// Load a simple HTML page from the test server.
|
||||
GURL simple_url(embedded_test_server()->GetURL("/simple.html"));
|
||||
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), simple_url));
|
||||
WaitForTimeout(kMaxTimeoutPerLoadedURL);
|
||||
|
||||
// Finally, load brave://rewards and enable Brave Rewards.
|
||||
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("brave://rewards")));
|
||||
ASSERT_TRUE(EnableBraveRewards());
|
||||
WaitForTimeout(kMaxTimeoutPerLoadedURL);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace brave
|
||||
@@ -0,0 +1,88 @@
|
||||
/* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_BROWSER_NET_BRAVE_NETWORK_AUDIT_WHITELISTS_H_
|
||||
#define BRAVE_BROWSER_NET_BRAVE_NETWORK_AUDIT_WHITELISTS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace brave {
|
||||
|
||||
// Before adding to this list, get approval from the security team.
|
||||
constexpr const char* kWhitelistedUrlProtocols[] = {
|
||||
"chrome-extension", "chrome", "brave", "file", "data", "blob",
|
||||
};
|
||||
|
||||
// Before adding to this list, get approval from the security team.
|
||||
constexpr const char* kWhitelistedUrlPrefixes[] = {
|
||||
// allowed because it 307's to https://componentupdater.brave.com
|
||||
"https://componentupdater.brave.com/service/update2",
|
||||
"https://crlsets.brave.com/",
|
||||
"https://crxdownload.brave.com/crx/blobs/",
|
||||
|
||||
// Omaha/Sparkle
|
||||
"https://updates.bravesoftware.com/",
|
||||
|
||||
// stats/referrals
|
||||
"https://laptop-updates.brave.com/",
|
||||
"https://laptop-updates-staging.brave.com/",
|
||||
|
||||
// needed for DoH on Mac build machines
|
||||
"https://dns.google/dns-query",
|
||||
|
||||
// needed for DoH on Mac build machines
|
||||
"https://chrome.cloudflare-dns.com/dns-query",
|
||||
|
||||
// for fetching tor client updater component
|
||||
"https://tor.bravesoftware.com/",
|
||||
|
||||
// brave sync v2 production
|
||||
"https://sync-v2.brave.com/v2",
|
||||
|
||||
// brave sync v2 staging
|
||||
"https://sync-v2.bravesoftware.com/v2",
|
||||
|
||||
// brave sync v2 dev
|
||||
"https://sync-v2.brave.software/v2",
|
||||
|
||||
// brave A/B testing
|
||||
"https://variations.brave.com/seed",
|
||||
|
||||
// Brave Today (production)
|
||||
"https://brave-today-cdn.brave.com/",
|
||||
|
||||
// Brave's Privacy-focused CDN
|
||||
"https://pcdn.brave.com/",
|
||||
|
||||
// Brave Rewards production
|
||||
"https://api.rewards.brave.com/v1/parameters",
|
||||
"https://rewards.brave.com/publishers/prefix-list",
|
||||
"https://grant.rewards.brave.com/v1/promotions",
|
||||
|
||||
// Brave Rewards staging & dev
|
||||
"https://api.rewards.bravesoftware.com/v1/parameters",
|
||||
"https://rewards-stg.bravesoftware.com/publishers/prefix-list",
|
||||
"https://grant.rewards.bravesoftware.com/v1/promotions",
|
||||
|
||||
// Other
|
||||
"https://brave-core-ext.s3.brave.com/",
|
||||
"https://go-updater.brave.com/",
|
||||
"https://p3a.brave.com/",
|
||||
"https://redirector.brave.com/",
|
||||
"https://safebrowsing.brave.com/",
|
||||
"https://static.brave.com/",
|
||||
"https://static1.brave.com/",
|
||||
};
|
||||
|
||||
// Before adding to this list, get approval from the security team.
|
||||
constexpr const char* kWhitelistedUrlPatterns[] = {
|
||||
// allowed because it's url for fetching super referral's mapping table
|
||||
"https://mobile-data.s3.brave.com/superreferrer/map-table.json",
|
||||
"https://mobile-data-dev.s3.brave.software/superreferrer/map-table.json",
|
||||
};
|
||||
|
||||
} // namespace brave
|
||||
|
||||
#endif // BRAVE_BROWSER_NET_BRAVE_NETWORK_AUDIT_WHITELISTS_H_
|
||||
+3
-101
@@ -4,16 +4,6 @@ const ip = require('ip')
|
||||
const URL = require('url').URL
|
||||
const config = require('../lib/config')
|
||||
const util = require('../lib/util')
|
||||
const whitelistedUrlPrefixes = require('./whitelistedUrlPrefixes')
|
||||
const whitelistedUrlPatterns = require('./whitelistedUrlPatterns')
|
||||
const whitelistedUrlProtocols = [
|
||||
'chrome-extension:',
|
||||
'chrome:',
|
||||
'brave:',
|
||||
'file:',
|
||||
'data:',
|
||||
'blob:'
|
||||
]
|
||||
|
||||
const start = (passthroughArgs, buildConfig = config.defaultBuildConfig, options) => {
|
||||
config.buildConfig = buildConfig
|
||||
@@ -85,32 +75,13 @@ const start = (passthroughArgs, buildConfig = config.defaultBuildConfig, options
|
||||
}
|
||||
braveArgs.push('--user-data-dir=' + user_data_dir);
|
||||
}
|
||||
const networkLogFile = path.resolve(path.join(config.rootDir, 'network_log.json'))
|
||||
if (options.network_log) {
|
||||
braveArgs.push(`--log-net-log=${networkLogFile}`)
|
||||
braveArgs.push(`--net-log-capture-mode=Everything`)
|
||||
if (user_data_dir) {
|
||||
// clear the data directory before doing a network test
|
||||
fs.removeSync(user_data_dir.replace('\\', ''))
|
||||
if (fs.existsSync(networkLogFile)) {
|
||||
fs.unlinkSync(networkLogFile)
|
||||
}
|
||||
if (fs.existsSync('network-audit-results.json')) {
|
||||
fs.unlinkSync('network-audit-results.json')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cmdOptions = {
|
||||
stdio: 'inherit',
|
||||
timeout: options.network_log ? 120000 : undefined,
|
||||
continueOnFail: options.network_log ? true : false,
|
||||
timeout: undefined,
|
||||
continueOnFail: false,
|
||||
shell: process.platform === 'darwin' ? true : false,
|
||||
killSignal: options.network_log && process.env.RELEASE_TYPE ? 'SIGKILL' : 'SIGTERM'
|
||||
}
|
||||
|
||||
if (options.network_log) {
|
||||
console.log('Network audit started. Logging requests for the next 2min or until you quit Brave...')
|
||||
killSignal: 'SIGTERM'
|
||||
}
|
||||
|
||||
let outputPath = options.output_path
|
||||
@@ -123,75 +94,6 @@ const start = (passthroughArgs, buildConfig = config.defaultBuildConfig, options
|
||||
}
|
||||
}
|
||||
util.run(outputPath, braveArgs, cmdOptions)
|
||||
|
||||
if (options.network_log) {
|
||||
let exitCode = 0
|
||||
let jsonOutput = {}
|
||||
// Read the network log
|
||||
let jsonContent = fs.readFileSync(networkLogFile, 'utf8').trim()
|
||||
// On windows netlog ends abruptly causing JSON parsing errors
|
||||
if (!jsonContent.endsWith('}]}')) {
|
||||
const n = jsonContent.lastIndexOf('},')
|
||||
jsonContent = jsonContent.substring(0, n) + '}]}'
|
||||
}
|
||||
jsonOutput = JSON.parse(jsonContent)
|
||||
|
||||
const URL_REQUEST_TYPE = jsonOutput.constants.logSourceType.URL_REQUEST
|
||||
const URL_REQUEST_FAKE_RESPONSE_HEADERS_CREATED = jsonOutput.constants.logEventTypes.URL_REQUEST_FAKE_RESPONSE_HEADERS_CREATED
|
||||
const urlRequests = jsonOutput.events.filter((event) => {
|
||||
if (event.type === URL_REQUEST_FAKE_RESPONSE_HEADERS_CREATED) {
|
||||
// showing these helps determine which URL requests which don't
|
||||
// actually hit the network
|
||||
return true
|
||||
}
|
||||
if (event.source.type === URL_REQUEST_TYPE) {
|
||||
if (!event.params) {
|
||||
return false
|
||||
}
|
||||
const url = event.params.url
|
||||
if (!url) {
|
||||
return false
|
||||
}
|
||||
const urlParsed = new URL(url)
|
||||
const hostname = urlParsed.hostname
|
||||
if (/^[a-z]+$/.test(hostname)) {
|
||||
// Chromium sometimes sends requests to random non-resolvable hosts
|
||||
return false
|
||||
}
|
||||
if (whitelistedUrlProtocols.includes(urlParsed.protocol)) {
|
||||
return false
|
||||
}
|
||||
const foundPrefix = whitelistedUrlPrefixes.find((prefix) => {
|
||||
return url.startsWith(prefix)
|
||||
})
|
||||
const foundPattern = whitelistedUrlPatterns.find((pattern) => {
|
||||
return RegExp('^' + pattern).test(url)
|
||||
})
|
||||
if (!foundPrefix && !foundPattern) {
|
||||
// Check if the URL is a private IP
|
||||
try {
|
||||
if (ip.isPrivate(hostname)) {
|
||||
// Warn but don't fail the audit
|
||||
console.log('NETWORK AUDIT WARN:', url)
|
||||
return true
|
||||
}
|
||||
} catch (e) {}
|
||||
// This is not a whitelisted URL! log it and exit with non-zero
|
||||
console.log('NETWORK AUDIT FAIL:', url)
|
||||
exitCode = 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
fs.writeJsonSync('network-audit-results.json', urlRequests)
|
||||
if (exitCode > 0) {
|
||||
console.log(`network-audit failed. import ${networkLogFile} in chrome://net-internals for more details.`)
|
||||
} else {
|
||||
console.log('network audit passed.')
|
||||
}
|
||||
process.exit(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = start
|
||||
|
||||
@@ -74,7 +74,12 @@ const test = (passthroughArgs, suite, buildConfig = config.defaultBuildConfig, o
|
||||
braveArgs = braveArgs.concat(passthroughArgs)
|
||||
|
||||
// Build the tests
|
||||
if (suite === 'brave_unit_tests' || suite === 'brave_browser_tests') {
|
||||
let testSuites = [
|
||||
'brave_unit_tests',
|
||||
'brave_browser_tests',
|
||||
'brave_network_audit_tests',
|
||||
]
|
||||
if (testSuites.includes(suite)) {
|
||||
util.run('ninja', ['-C', config.outputDir, "brave/test:" + suite], config.defaultOptions)
|
||||
} else {
|
||||
util.run('ninja', ['-C', config.outputDir, suite], config.defaultOptions)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Before adding to this list, get approval from the security team
|
||||
module.exports = [
|
||||
'http://[A-Za-z0-9-\.]+\.gvt1\.com/edgedl/release2/.+', // allowed because it 307's to redirector.brave.com
|
||||
'https://[A-Za-z0-9-\.]+\.gvt1\.com/edgedl/release2/.+', // allowed because it 307's to redirector.brave.com
|
||||
'http://www.google.com/dl/release2/chrome_component/.+crl-set.+', // allowed because it 307's to crlsets.brave.com
|
||||
'https://www.google.com/dl/release2/chrome_component/.+crl-set.+', // allowed because it 307's to crlsets.brave.com
|
||||
'http://storage.googleapis.com/update-delta/hfnkpimlhhgieaddgfemjhofmfblmnib/.+crxd', // allowed because it 307's to crlsets.brave.com,
|
||||
'https://storage.googleapis.com/update-delta/hfnkpimlhhgieaddgfemjhofmfblmnib/.+crxd', // allowed because it 307's to crlsets.brave.com
|
||||
'https://mobile-data.s3.brave.com/superreferrer/map-table.json', // allowed because it's url for fetching super referral's mapping table
|
||||
'https://mobile-data-dev.s3.brave.software/superreferrer/map-table.json' // allowed because it's url for fetching super referral's mapping table
|
||||
]
|
||||
@@ -1,32 +0,0 @@
|
||||
// Before adding to this list, get approval from the security team
|
||||
module.exports = [
|
||||
'http://update.googleapis.com/service/update2', // allowed because it 307's to go-updater.brave.com. should never actually connect to googleapis.com.
|
||||
'https://update.googleapis.com/service/update2', // allowed because it 307's to go-updater.brave.com. should never actually connect to googleapis.com.
|
||||
'https://safebrowsing.googleapis.com/v4/threatListUpdates', // allowed because it 307's to safebrowsing.brave.com
|
||||
'https://clients2.googleusercontent.com/crx/blobs/',
|
||||
'http://dl.google.com/', // allowed because it 307's to redirector.brave.com
|
||||
'https://dl.google.com/', // allowed because it 307's to redirector.brave.com
|
||||
'https://no-thanks.invalid/', // fake gaia URL
|
||||
'https://go-updater.brave.com/',
|
||||
'https://safebrowsing.brave.com/',
|
||||
'https://brave-core-ext.s3.brave.com/',
|
||||
'https://laptop-updates.brave.com/', // stats/referrals
|
||||
'https://static.brave.com/',
|
||||
'https://static1.brave.com/',
|
||||
'http://componentupdater.brave.com/service/update2', // allowed because it 307's to https://componentupdater.brave.com
|
||||
'https://componentupdater.brave.com/service/update2',
|
||||
'https://crlsets.brave.com/',
|
||||
'https://crxdownload.brave.com/crx/blobs/',
|
||||
'https://updates.bravesoftware.com/', // Omaha/Sparkle
|
||||
'https://p3a.brave.com/',
|
||||
'https://dns.google/dns-query', // needed for DoH on Mac build machines
|
||||
'https://chrome.cloudflare-dns.com/dns-query', // needed for DoH on Mac build machines
|
||||
'https://tor.bravesoftware.com/', // for fetching tor client updater component
|
||||
'https://redirector.brave.com/',
|
||||
'https://sync-v2.brave.com/v2', // brave sync v2 production
|
||||
'https://sync-v2.bravesoftware.com/v2', // brave sync v2 staging
|
||||
'https://sync-v2.brave.software/v2', // brave sync v2 dev
|
||||
'https://variations.brave.com/seed', // brave A/B testing
|
||||
'https://brave-today-cdn.brave.com/', // Brave Today (production)
|
||||
'https://pcdn.brave.com/', // Brave's Privacy-focused CDN
|
||||
]
|
||||
@@ -176,7 +176,6 @@ program
|
||||
.option('--brave_ads_staging', 'ads staging')
|
||||
.option('--brave_ads_debug', 'ads debug')
|
||||
.option('--single_process', 'use a single process')
|
||||
.option('--network_log', 'log network activity to network_log.json')
|
||||
.option('--output_path [pathname]', 'use the Brave binary located at [pathname]')
|
||||
.arguments('[build_config]')
|
||||
.action(start.bind(null, parsedArgs.unknown))
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
"update_patches": "node ./build/commands/scripts/commands.js update_patches",
|
||||
"apply_patches": "node ./build/commands/scripts/commands.js apply_patches",
|
||||
"start": "node ./build/commands/scripts/commands.js start",
|
||||
"network-audit": "node ./build/commands/scripts/commands.js start --enable_brave_update --network_log --user_data_dir_name=brave-network-test --disable-doh",
|
||||
"network-audit": "npm run test brave_network_audit_tests -- --ui-test-action-timeout=330000 --test-launcher-timeout=1000000",
|
||||
"push_l10n": "node ./build/commands/scripts/commands.js push_l10n",
|
||||
"pull_l10n": "node ./build/commands/scripts/commands.js pull_l10n",
|
||||
"chromium_rebase_l10n": "node ./build/commands/scripts/commands.js chromium_rebase_l10n",
|
||||
@@ -27,7 +27,7 @@
|
||||
"format": "node ./build/commands/scripts/commands.js format",
|
||||
"test": "node ./build/commands/scripts/commands.js test",
|
||||
"test:scripts": "jest build/commands/lib build/commands/scripts",
|
||||
"test-security": "npm run check_security && npm run audit_deps && node ./build/commands/scripts/commands.js start --enable_brave_update --network_log --user_data_dir_name=brave-network-test",
|
||||
"test-security": "npm run check_security && npm run audit_deps && npm run network-audit",
|
||||
"tslint": "tslint --project tsconfig-lint.json \"components/**/*.{ts,tsx}\"",
|
||||
"pep8": "pycodestyle --max-line-length 120 -r script",
|
||||
"pylint": "node ./build/commands/scripts/commands.js pylint",
|
||||
|
||||
@@ -952,6 +952,39 @@ if (!is_android) {
|
||||
}
|
||||
public_deps = [ ":browser_tests_runner" ]
|
||||
}
|
||||
|
||||
test("brave_network_audit_tests") {
|
||||
testonly = true
|
||||
|
||||
# We need to disable the ThinLTO cache or the linker will die with
|
||||
# a "Resource temporarily unavailable" error due to the linking
|
||||
# reaching the vm.max_map_count limit of 65530 memory mappings.
|
||||
configs += [ "//chrome/test:disable_thinlto_cache_flags" ]
|
||||
|
||||
sources = [
|
||||
"//brave/browser/net/brave_network_audit_browsertest.cc",
|
||||
"//brave/browser/net/brave_network_audit_whitelists.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//base",
|
||||
"//brave/components/brave_rewards/browser",
|
||||
"//chrome/browser",
|
||||
"//chrome/browser/profiles:profile",
|
||||
"//chrome/browser/ui",
|
||||
"//chrome/test:test_support_ui",
|
||||
"//components/prefs",
|
||||
"//content/public/browser",
|
||||
"//content/test:test_support",
|
||||
"//services/network/public/cpp",
|
||||
"//testing/gmock",
|
||||
"//third_party/re2",
|
||||
]
|
||||
|
||||
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
|
||||
|
||||
public_deps = [ ":browser_tests_runner" ]
|
||||
}
|
||||
} else { # if (!is_android) {
|
||||
test("brave_browser_tests") {
|
||||
configs += [ "//build/config:precompiled_headers" ]
|
||||
|
||||
+24
-7
@@ -11,11 +11,8 @@ template("fix_testing_install_name_impl") {
|
||||
executable_path,
|
||||
]
|
||||
|
||||
args = [
|
||||
"/usr/bin/install_name_tool",
|
||||
] + command_args + [
|
||||
rebase_path(executable_path, root_build_dir),
|
||||
]
|
||||
args = [ "/usr/bin/install_name_tool" ] + command_args +
|
||||
[ rebase_path(executable_path, root_build_dir) ]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,18 +20,27 @@ template("fix_testing_install_name") {
|
||||
change_args = []
|
||||
if (defined(invoker.changes)) {
|
||||
foreach(change, invoker.changes) {
|
||||
change_args += ["-change", change[0], change[1]]
|
||||
change_args += [
|
||||
"-change",
|
||||
change[0],
|
||||
change[1],
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (defined(invoker.new_path) && defined(invoker.current_path)) {
|
||||
change_args += ["-change", invoker.current_path, invoker.new_path]
|
||||
change_args += [
|
||||
"-change",
|
||||
invoker.current_path,
|
||||
invoker.new_path,
|
||||
]
|
||||
}
|
||||
|
||||
fix_testing_install_name_impl(target_name + "_unit_tests") {
|
||||
forward_variables_from(invoker, "*")
|
||||
executable_path = "$root_build_dir/brave_unit_tests"
|
||||
command_args = change_args
|
||||
|
||||
# input is the same as the output so just fake it
|
||||
outputs = [ "$root_build_dir/alwaysrununittests/$target_name" ]
|
||||
deps = [ "//brave/test:brave_unit_tests" ]
|
||||
@@ -44,8 +50,19 @@ template("fix_testing_install_name") {
|
||||
forward_variables_from(invoker, "*")
|
||||
executable_path = "$root_build_dir/brave_browser_tests"
|
||||
command_args = change_args
|
||||
|
||||
# input is the same as the output so just fake it
|
||||
outputs = [ "$root_build_dir/alwaysrunbrowsertests/$target_name" ]
|
||||
deps = [ "//brave/test:brave_browser_tests" ]
|
||||
}
|
||||
|
||||
fix_testing_install_name_impl(target_name + "_network_audit_tests") {
|
||||
forward_variables_from(invoker, "*")
|
||||
executable_path = "$root_build_dir/brave_network_audit_tests"
|
||||
command_args = change_args
|
||||
|
||||
# input is the same as the output so just fake it
|
||||
outputs = [ "$root_build_dir/alwaysrunbrowsertests/$target_name" ]
|
||||
deps = [ "//brave/test:brave_network_audit_tests" ]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user