blob: c8c7cc345c48c8e81d34dbffc170d9ddbbc5f9f2 [file] [log] [blame]
// Copyright 2017 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 "tools/traffic_annotation/auditor/traffic_annotation_file_filter.h"
#include <fstream>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "third_party/re2/src/re2/re2.h"
namespace {
// List of keywords that indicate a file may be related to network traffic
// annotations. This list includes all keywords related to defining annotations
// and all functions that need one.
const char* kRelevantKeywords[] = {
"network_traffic_annotation",
"network_traffic_annotation_test_helper",
"NetworkTrafficAnnotationTag",
"PartialNetworkTrafficAnnotationTag",
"DefineNetworkTrafficAnnotation",
"DefinePartialNetworkTrafficAnnotation",
"CompleteNetworkTrafficAnnotation",
"BranchedCompleteNetworkTrafficAnnotation",
"NO_TRAFFIC_ANNOTATION_YET",
"NO_PARTIAL_TRAFFIC_ANNOTATION_YET",
"MISSING_TRAFFIC_ANNOTATION",
"TRAFFIC_ANNOTATION_FOR_TESTS",
"PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS",
"URLFetcher::Create", // This one is used with class as it's too generic.
"CreateRequest", // URLRequestContext::
nullptr // End Marker
};
} // namespace
TrafficAnnotationFileFilter::TrafficAnnotationFileFilter() = default;
TrafficAnnotationFileFilter::~TrafficAnnotationFileFilter() = default;
void TrafficAnnotationFileFilter::GetFilesFromGit(
const base::FilePath& source_path) {
// Change directory to source path to access git and check files.
base::FilePath original_path;
base::GetCurrentDirectory(&original_path);
base::SetCurrentDirectory(source_path);
std::string git_list;
if (git_file_for_test_.empty()) {
const base::CommandLine::CharType* args[] =
#if defined(OS_WIN)
{FILE_PATH_LITERAL("git.bat"), FILE_PATH_LITERAL("ls-files")};
#else
{"git", "ls-files"};
#endif
base::CommandLine cmdline(2, args);
// Get list of files from git.
if (!base::GetAppOutput(cmdline, &git_list)) {
LOG(ERROR) << "Could not get files from git.";
git_list.clear();
}
} else {
if (!base::ReadFileToString(git_file_for_test_, &git_list)) {
LOG(ERROR) << "Could not load mock git list file from "
<< git_file_for_test_.MaybeAsASCII().c_str();
git_list.clear();
}
}
for (const std::string file_path : base::SplitString(
git_list, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
if (IsFileRelevant(file_path))
git_files_.push_back(file_path);
}
base::SetCurrentDirectory(original_path);
}
bool TrafficAnnotationFileFilter::IsFileRelevant(const std::string& file_path) {
// Check file extension.
int pos = file_path.length() - 3;
if (pos < 0 || (strcmp(".mm", file_path.c_str() + pos) &&
strcmp(".cc", file_path.c_str() + pos))) {
return false;
}
// Ignore test files to speed up the tests. They would be only tested when
// filters are disabled.
pos = file_path.length() - 7;
if (pos >= 0 && (!strcmp("test.cc", file_path.c_str() + pos) ||
!strcmp("test.mm", file_path.c_str() + pos))) {
return false;
}
base::FilePath converted_file_path =
#if defined(OS_WIN)
base::FilePath(
base::FilePath::StringPieceType(base::UTF8ToWide(file_path)));
#else
base::FilePath(base::FilePath::StringPieceType(file_path));
#endif
// Check file content.
std::string file_content;
if (!base::ReadFileToString(converted_file_path, &file_content)) {
LOG(ERROR) << "Could not open file: " << file_path;
return false;
}
for (int i = 0; kRelevantKeywords[i]; i++) {
if (file_content.find(kRelevantKeywords[i]) != std::string::npos)
return true;
}
return false;
}
void TrafficAnnotationFileFilter::GetRelevantFiles(
const base::FilePath& source_path,
const std::vector<std::string>& ignore_list,
std::string directory_name,
std::vector<std::string>* file_paths) {
if (!git_files_.size())
GetFilesFromGit(source_path);
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
std::replace(directory_name.begin(), directory_name.end(), L'\\', L'/');
#endif
size_t name_length = directory_name.length();
for (const std::string& file_path : git_files_) {
if (!strncmp(file_path.c_str(), directory_name.c_str(), name_length)) {
bool ignore = false;
for (const std::string& ignore_pattern : ignore_list) {
if (re2::RE2::FullMatch(file_path.c_str(), ignore_pattern)) {
ignore = true;
break;
}
}
if (!ignore)
file_paths->push_back(file_path);
}
}
}