chromium/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/safe_browsing/download_protection/file_analyzer.h"

#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/safe_browsing/archive_analyzer_results.h"
#include "chrome/common/safe_browsing/mock_binary_feature_extractor.h"
#include "components/safe_browsing/content/common/file_type_policies_test_util.h"
#include "components/safe_browsing/core/common/features.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/zlib/google/zip.h"

namespace safe_browsing {

_;
DoAll;
IsEmpty;
Return;
SetArgPointee;
SizeIs;
StrEq;

class FileAnalyzerTest : public testing::Test {};

TEST_F(FileAnalyzerTest, TypeWinExecutable) {}

TEST_F(FileAnalyzerTest, TypeChromeExtension) {}

TEST_F(FileAnalyzerTest, TypeAndroidApk) {}

TEST_F(FileAnalyzerTest, TypeZippedExecutable) {}

TEST_F(FileAnalyzerTest, TypeMacExecutable) {}

TEST_F(FileAnalyzerTest, TypeZippedArchive) {}

TEST_F(FileAnalyzerTest, TypeInvalidZip) {}

// Since we only inspect contents of DMGs on OS X, we only get
// MAC_ARCHIVE_FAILED_PARSING on OS X.
#if BUILDFLAG(IS_MAC)
TEST_F(FileAnalyzerTest, TypeInvalidDmg) {
  scoped_refptr<MockBinaryFeatureExtractor> extractor =
      new testing::StrictMock<MockBinaryFeatureExtractor>();
  FileAnalyzer analyzer(extractor);
  base::RunLoop run_loop;

  base::FilePath target_path(FILE_PATH_LITERAL("target.dmg"));
  base::FilePath tmp_path =
      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("tmp.crdownload"));

  std::string file_contents = "invalid contents";
  ASSERT_TRUE(base::WriteFile(tmp_path, file_contents));

  analyzer.Start(
      target_path, tmp_path, /*password=*/std::nullopt,
      base::BindOnce(&FileAnalyzerTest::DoneCallback, base::Unretained(this),
                     run_loop.QuitClosure()));
  run_loop.Run();

  ASSERT_TRUE(has_result_);
  EXPECT_EQ(result_.type, ClientDownloadRequest::MAC_ARCHIVE_FAILED_PARSING);
  EXPECT_EQ(result_.archive_summary.parser_status(),
            ClientDownloadRequest::ArchiveSummary::UNKNOWN);
}
#endif

// TODO(drubery): Add tests verifying Rar inspection

TEST_F(FileAnalyzerTest, ArchiveIsValidSetForValidArchive) {}

TEST_F(FileAnalyzerTest, ArchiveIsValidSetForInvalidArchive) {}

TEST_F(FileAnalyzerTest, ArchivedExecutableSetForZipWithExecutable) {}

TEST_F(FileAnalyzerTest, ArchivedExecutableFalseForZipNoExecutable) {}

TEST_F(FileAnalyzerTest, ArchivedArchiveSetForZipWithArchive) {}

TEST_F(FileAnalyzerTest, ArchivedArchiveSetForZipNoArchive) {}

TEST_F(FileAnalyzerTest, ArchivedBinariesHasArchiveAndExecutable) {}

TEST_F(FileAnalyzerTest, ArchivedBinariesSkipsSafeFiles) {}

TEST_F(FileAnalyzerTest, ArchivedBinariesRespectsPolicyMaximum) {}

TEST_F(FileAnalyzerTest, ExtractsFileSignatureForExe) {}

TEST_F(FileAnalyzerTest, ExtractsImageHeadersForExe) {}

#if BUILDFLAG(IS_MAC)

TEST_F(FileAnalyzerTest, ExtractsSignatureForDmg) {
  scoped_refptr<MockBinaryFeatureExtractor> extractor =
      new testing::StrictMock<MockBinaryFeatureExtractor>();
  FileAnalyzer analyzer(extractor);
  base::RunLoop run_loop;

  base::FilePath target_path(FILE_PATH_LITERAL("target.dmg"));
  base::FilePath signed_dmg;
  EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &signed_dmg));
  signed_dmg = signed_dmg.AppendASCII("safe_browsing")
                   .AppendASCII("mach_o")
                   .AppendASCII("signed-archive.dmg");

  analyzer.Start(
      target_path, signed_dmg, /*password=*/std::nullopt,
      base::BindOnce(&FileAnalyzerTest::DoneCallback, base::Unretained(this),
                     run_loop.QuitClosure()));
  run_loop.Run();

  ASSERT_TRUE(has_result_);
  EXPECT_EQ(2215u, result_.disk_image_signature.size());

  base::FilePath signed_dmg_signature;
  EXPECT_TRUE(
      base::PathService::Get(chrome::DIR_TEST_DATA, &signed_dmg_signature));
  signed_dmg_signature = signed_dmg_signature.AppendASCII("safe_browsing")
                             .AppendASCII("mach_o")
                             .AppendASCII("signed-archive-signature.data");

  std::string signature;
  base::ReadFileToString(signed_dmg_signature, &signature);
  EXPECT_EQ(2215u, signature.length());
  std::vector<uint8_t> signature_vector(signature.begin(), signature.end());
  EXPECT_EQ(signature_vector, result_.disk_image_signature);
}

TEST_F(FileAnalyzerTest, TypeSniffsDmgWithoutExtension) {
  scoped_refptr<MockBinaryFeatureExtractor> extractor =
      new testing::StrictMock<MockBinaryFeatureExtractor>();
  FileAnalyzer analyzer(extractor);
  base::RunLoop run_loop;

  base::FilePath target_path(FILE_PATH_LITERAL("target.dmg"));
  base::FilePath dmg_no_extension;
  EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &dmg_no_extension));
  dmg_no_extension = dmg_no_extension.AppendASCII("safe_browsing")
                         .AppendASCII("dmg")
                         .AppendASCII("data")
                         .AppendASCII("mach_o_in_dmg.txt");

  analyzer.Start(
      target_path, dmg_no_extension, /*password=*/std::nullopt,
      base::BindOnce(&FileAnalyzerTest::DoneCallback, base::Unretained(this),
                     run_loop.QuitClosure()));
  run_loop.Run();

  ASSERT_TRUE(has_result_);
  EXPECT_EQ(result_.type, ClientDownloadRequest::MAC_EXECUTABLE);
  EXPECT_EQ(result_.archive_summary.parser_status(),
            ClientDownloadRequest::ArchiveSummary::VALID);
}

#endif

TEST_F(FileAnalyzerTest, SmallRarHasContentInspection) {}

// TODO(crbug.com/41451079): The test is flaky (fail, timeout) on all platforms.
TEST_F(FileAnalyzerTest, LargeRarSkipsContentInspection) {}

TEST_F(FileAnalyzerTest, ZipFilesGetFileCount) {}

TEST_F(FileAnalyzerTest, ZipFilesGetDirectoryCount) {}

TEST_F(FileAnalyzerTest, RarFilesGetFileCount) {}

TEST_F(FileAnalyzerTest, RarFilesGetDirectoryCount) {}

TEST_F(FileAnalyzerTest, LargeZipSkipsContentInspection) {}

TEST_F(FileAnalyzerTest, ZipAnalysisResultMetric) {}

TEST_F(FileAnalyzerTest, RarAnalysisResultMetric) {}

#if BUILDFLAG(IS_MAC)
TEST_F(FileAnalyzerTest, DmgAnalysisResultMetric) {
  scoped_refptr<MockBinaryFeatureExtractor> extractor =
      new testing::StrictMock<MockBinaryFeatureExtractor>();
  FileAnalyzer analyzer(extractor);
  base::HistogramTester histogram_tester;
  base::RunLoop run_loop;

  base::FilePath target_path(FILE_PATH_LITERAL("target.dmg"));
  base::FilePath signed_dmg;
  EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &signed_dmg));
  signed_dmg = signed_dmg.AppendASCII("safe_browsing")
                   .AppendASCII("mach_o")
                   .AppendASCII("signed-archive.dmg");

  analyzer.Start(
      target_path, signed_dmg, /*password=*/std::nullopt,
      base::BindOnce(&FileAnalyzerTest::DoneCallback, base::Unretained(this),
                     run_loop.QuitClosure()));

  run_loop.Run();

  ASSERT_TRUE(has_result_);
  histogram_tester.ExpectBucketCount(
      "SBClientDownload.DmgArchiveAnalysisResult",
      ArchiveAnalysisResult::kValid, 1);
}
#endif

TEST_F(FileAnalyzerTest, EncryptedEntriesDoNotHaveHashOrLength) {}

TEST_F(FileAnalyzerTest, RarDirectoriesNotReported) {}

TEST_F(FileAnalyzerTest, ZeroLengthSevenZipEntriesSupported) {}

}  // namespace safe_browsing