chromium/chrome/browser/ui/webui/dlp_internals/dlp_internals_page_handler.cc

// Copyright 2023 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 "chrome/browser/ui/webui/dlp_internals/dlp_internals_page_handler.h"

#include <sys/stat.h>

#include "base/check.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/dlp_internals/dlp_internals.mojom.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/dbus/dlp/dlp_client.h"
#include "components/enterprise/data_controls/core/browser/dlp_policy_event.pb.h"
#include "components/enterprise/data_controls/core/browser/rule.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "ui/base/clipboard/clipboard.h"

namespace policy {

namespace {
dlp_internals::mojom::Level DlpLevelToMojo(data_controls::Rule::Level level) {
  switch (level) {
    case data_controls::Rule::Level::kNotSet:
      return dlp_internals::mojom::Level::kNotSet;
    case data_controls::Rule::Level::kReport:
      return dlp_internals::mojom::Level::kReport;
    case data_controls::Rule::Level::kWarn:
      return dlp_internals::mojom::Level::kWarn;
    case data_controls::Rule::Level::kBlock:
      return dlp_internals::mojom::Level::kBlock;
    case data_controls::Rule::Level::kAllow:
      return dlp_internals::mojom::Level::kAllow;
  }
}

dlp_internals::mojom::ContentRestriction ContentRestrictionToMojo(
    DlpContentRestriction restriction) {
  switch (restriction) {
    case DlpContentRestriction::kScreenshot:
      return dlp_internals::mojom::ContentRestriction::kScreenshot;
    case DlpContentRestriction::kPrivacyScreen:
      return dlp_internals::mojom::ContentRestriction::kPrivacyScreen;
    case DlpContentRestriction::kPrint:
      return dlp_internals::mojom::ContentRestriction::kPrint;
    case DlpContentRestriction::kScreenShare:
      return dlp_internals::mojom::ContentRestriction::kScreenShare;
  }
}

std::vector<dlp_internals::mojom::ContentRestrictionInfoPtr>
RestrictionSetToMojo(DlpContentRestrictionSet restriction_set) {
  std::vector<dlp_internals::mojom::ContentRestrictionInfoPtr>
      restrictions_mojo_arr;

  // An array of all content restrictions. Please keep it up to date with
  // DlpContentRestriction enum.
  std::array restrictions = {
      DlpContentRestriction::kScreenshot, DlpContentRestriction::kPrivacyScreen,
      DlpContentRestriction::kPrint, DlpContentRestriction::kScreenShare};
  for (const auto& restriction : restrictions) {
    auto lvl_and_url = restriction_set.GetRestrictionLevelAndUrl(restriction);
    restrictions_mojo_arr.push_back(
        dlp_internals::mojom::ContentRestrictionInfo::New(
            ContentRestrictionToMojo(restriction),
            DlpLevelToMojo(lvl_and_url.level), lvl_and_url.url));
  }
  return restrictions_mojo_arr;
}

std::vector<dlp_internals::mojom::RenderFrameHostInfoPtr> RfhInfoToMojo(
    std::vector<DlpContentTabHelper::RfhInfo> rfh_info_vector) {
  std::vector<dlp_internals::mojom::RenderFrameHostInfoPtr> rfh_info_mojo_arr;
  for (const auto& rfh_info : rfh_info_vector) {
    DCHECK(rfh_info.first);
    rfh_info_mojo_arr.push_back(dlp_internals::mojom::RenderFrameHostInfo::New(
        rfh_info.first->GetLastCommittedURL(),
        RestrictionSetToMojo(rfh_info.second)));
  }
  return rfh_info_mojo_arr;
}

dlp_internals::mojom::DlpEvent::Restriction EventRestrictionToMojo(
    DlpPolicyEvent::Restriction restriction) {
  switch (restriction) {
    case DlpPolicyEvent_Restriction_UNDEFINED_RESTRICTION:
      return dlp_internals::mojom::DlpEvent::Restriction::kUndefinedRestriction;
    case DlpPolicyEvent_Restriction_CLIPBOARD:
      return dlp_internals::mojom::DlpEvent::Restriction::kClipboard;
    case DlpPolicyEvent_Restriction_SCREENSHOT:
      return dlp_internals::mojom::DlpEvent::Restriction::kScreenshot;
    case DlpPolicyEvent_Restriction_SCREENCAST:
      return dlp_internals::mojom::DlpEvent::Restriction::kScreencast;
    case DlpPolicyEvent_Restriction_PRINTING:
      return dlp_internals::mojom::DlpEvent::Restriction::kPrinting;
    case DlpPolicyEvent_Restriction_EPRIVACY:
      return dlp_internals::mojom::DlpEvent::Restriction::kEprivacy;
    case DlpPolicyEvent_Restriction_FILES:
      return dlp_internals::mojom::DlpEvent::Restriction::kFiles;
  }
}

dlp_internals::mojom::DlpEvent::UserType EventUserTypeToMojo(
    DlpPolicyEvent::UserType restriction) {
  switch (restriction) {
    case DlpPolicyEvent_UserType_REGULAR:
      return dlp_internals::mojom::DlpEvent::UserType::kRegular;
    case DlpPolicyEvent_UserType_MANAGED_GUEST:
      return dlp_internals::mojom::DlpEvent::UserType::kManagedGuest;
    case DlpPolicyEvent_UserType_KIOSK:
      return dlp_internals::mojom::DlpEvent::UserType::kKiosk;
    case DlpPolicyEvent_UserType_UNDEFINED_USER_TYPE:
      return dlp_internals::mojom::DlpEvent::UserType::kUndefinedUserType;
  }
}

dlp_internals::mojom::DlpEvent::Mode EventModeToMojo(
    DlpPolicyEvent::Mode mode) {
  switch (mode) {
    case DlpPolicyEvent_Mode_UNDEFINED_MODE:
      return dlp_internals::mojom::DlpEvent::Mode::kUndefinedMode;
    case DlpPolicyEvent_Mode_BLOCK:
      return dlp_internals::mojom::DlpEvent::Mode::kBlock;
    case DlpPolicyEvent_Mode_REPORT:
      return dlp_internals::mojom::DlpEvent::Mode::kReport;
    case DlpPolicyEvent_Mode_WARN:
      return dlp_internals::mojom::DlpEvent::Mode::kWarn;
    case DlpPolicyEvent_Mode_WARN_PROCEED:
      return dlp_internals::mojom::DlpEvent::Mode::kWarnProceed;
  }
}

dlp_internals::mojom::EventDestination::Component
EventDestinationComponentToMojo(
    DlpPolicyEventDestination::Component component) {
  switch (component) {
    case DlpPolicyEventDestination_Component_UNDEFINED_COMPONENT:
      return dlp_internals::mojom::EventDestination::Component::
          kUndefinedComponent;
    case DlpPolicyEventDestination_Component_ARC:
      return dlp_internals::mojom::EventDestination::Component::kArc;
    case DlpPolicyEventDestination_Component_CROSTINI:
      return dlp_internals::mojom::EventDestination::Component::kCrostini;
    case DlpPolicyEventDestination_Component_PLUGIN_VM:
      return dlp_internals::mojom::EventDestination::Component::kPluginVm;
    case DlpPolicyEventDestination_Component_USB:
      return dlp_internals::mojom::EventDestination::Component::kUsb;
    case DlpPolicyEventDestination_Component_DRIVE:
      return dlp_internals::mojom::EventDestination::Component::kDrive;
    case DlpPolicyEventDestination_Component_ONEDRIVE:
      return dlp_internals::mojom::EventDestination::Component::kOnedrive;
  }
}

dlp_internals::mojom::EventDestinationPtr EventDestinationToMojo(
    DlpPolicyEventDestination destination) {
  auto destination_mojo = dlp_internals::mojom::EventDestination::New();

  if (destination.has_component()) {
    destination_mojo->component =
        EventDestinationComponentToMojo(destination.component());
  }

  if (destination.has_url()) {
    destination_mojo->url_pattern = destination.url();
  }
  return destination_mojo;
}

}  // namespace

DlpInternalsPageHandler::DlpInternalsPageHandler(
    mojo::PendingReceiver<dlp_internals::mojom::PageHandler> receiver,
    Profile* profile)
    : receiver_(this, std::move(receiver)), profile_(profile) {
  DCHECK(profile_);

  auto* rules_manager = DlpRulesManagerFactory::GetForPrimaryProfile();
  auto* reporting_manager =
      rules_manager ? rules_manager->GetReportingManager() : nullptr;
  if (reporting_manager) {
    reporting_observation_.Observe(reporting_manager);
  }
}

DlpInternalsPageHandler::~DlpInternalsPageHandler() = default;

void DlpInternalsPageHandler::GetClipboardDataSource(
    GetClipboardDataSourceCallback callback) {
  auto source = ui::Clipboard::GetForCurrentThread()->GetSource(
      ui::ClipboardBuffer::kCopyPaste);
  if (!source) {
    std::move(callback).Run(std::move(nullptr));
    return;
  }

  auto mojom_source = dlp_internals::mojom::DataTransferEndpoint::New();
  switch (source->type()) {
    case ui::EndpointType::kDefault:
      mojom_source->type = dlp_internals::mojom::EndpointType::kDefault;
      break;

    case ui::EndpointType::kUrl:
      mojom_source->type = dlp_internals::mojom::EndpointType::kUrl;
      break;

    case ui::EndpointType::kClipboardHistory:
      mojom_source->type =
          dlp_internals::mojom::EndpointType::kClipboardHistory;
      break;

    case ui::EndpointType::kUnknownVm:
      mojom_source->type = dlp_internals::mojom::EndpointType::kUnknownVm;
      break;

    case ui::EndpointType::kArc:
      mojom_source->type = dlp_internals::mojom::EndpointType::kArc;
      break;

    case ui::EndpointType::kBorealis:
      mojom_source->type = dlp_internals::mojom::EndpointType::kBorealis;
      break;

    case ui::EndpointType::kCrostini:
      mojom_source->type = dlp_internals::mojom::EndpointType::kCrostini;
      break;

    case ui::EndpointType::kPluginVm:
      mojom_source->type = dlp_internals::mojom::EndpointType::kPluginVm;
      break;

    case ui::EndpointType::kLacros:
      mojom_source->type = dlp_internals::mojom::EndpointType::kLacros;
      break;
  }

  if (source->IsUrlType()) {
    mojom_source->url = *source->GetURL();
  }

  std::move(callback).Run(std::move(mojom_source));
}

void DlpInternalsPageHandler::GetContentRestrictionsInfo(
    GetContentRestrictionsInfoCallback callback) {
  auto* content_manager = DlpContentManager::Get();
  if (!content_manager) {
    std::move(callback).Run({});
  }

  auto info_vector = content_manager->GetWebContentsInfo();
  std::vector<dlp_internals::mojom::WebContentsInfoPtr> info_mojo_array;

  for (const auto& web_contents_info : info_vector) {
    DCHECK(web_contents_info.web_contents);
    info_mojo_array.push_back(dlp_internals::mojom::WebContentsInfo::New(
        web_contents_info.web_contents->GetLastCommittedURL(),
        RestrictionSetToMojo(web_contents_info.restriction_set),
        RfhInfoToMojo(web_contents_info.rfh_info_vector)));
  }
  std::move(callback).Run(std::move(info_mojo_array));
}

void DlpInternalsPageHandler::ObserveReporting(
    mojo::PendingRemote<dlp_internals::mojom::ReportingObserver> observer) {
  reporting_observers_.Add(std::move(observer));
}

void DlpInternalsPageHandler::GetFilesDatabaseEntries(
    GetFilesDatabaseEntriesCallback callback) {
  if (!chromeos::DlpClient::Get() || !chromeos::DlpClient::Get()->IsAlive()) {
    std::move(callback).Run({});
    return;
  }
  chromeos::DlpClient::Get()->GetDatabaseEntries(
      base::BindOnce(&DlpInternalsPageHandler::ProcessDatabaseEntries,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void DlpInternalsPageHandler::GetFileInode(const std::string& file_name,
                                           GetFileInodeCallback callback) {
  base::FilePath downloads_path;
  base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &downloads_path);
  auto file_path = downloads_path.Append(file_name);

  struct stat file_stats;
  if (stat(file_path.value().c_str(), &file_stats) != 0) {
    std::move(callback).Run(0);
    return;
  }

  std::move(callback).Run(file_stats.st_ino);
}

void DlpInternalsPageHandler::OnReportEvent(DlpPolicyEvent event) {
  dlp_internals::mojom::DlpEventPtr event_mojo =
      dlp_internals::mojom::DlpEvent::New();
  if (event.has_source() && event.source().has_url()) {
    event_mojo->source_pattern = event.source().url();
  }

  if (event.has_destination()) {
    event_mojo->destination = EventDestinationToMojo(event.destination());
  }

  if (event.has_restriction()) {
    event_mojo->restriction = EventRestrictionToMojo(event.restriction());
  }

  if (event.mode()) {
    event_mojo->mode = EventModeToMojo(event.mode());
  }

  if (event.has_timestamp_micro()) {
    event_mojo->timestamp_micro = event.timestamp_micro();
  }

  if (event.has_user_type()) {
    event_mojo->user_type = EventUserTypeToMojo(event.user_type());
  }

  if (event.has_content_name()) {
    event_mojo->content_name = event.content_name();
  }

  if (event.has_triggered_rule_name()) {
    event_mojo->triggered_rule_name = event.triggered_rule_name();
  }

  if (event.has_triggered_rule_id()) {
    event_mojo->triggered_rule_id = event.triggered_rule_id();
  }

  for (auto& observer : reporting_observers_) {
    observer->OnReportEvent(event_mojo.Clone());
  }
}

void DlpInternalsPageHandler::ProcessDatabaseEntries(
    GetFilesDatabaseEntriesCallback callback,
    ::dlp::GetDatabaseEntriesResponse response_proto) {
  std::vector<dlp_internals::mojom::FileDatabaseEntryPtr> database_entries;
  for (const auto& file_entry : response_proto.files_entries()) {
    database_entries.push_back(dlp_internals::mojom::FileDatabaseEntry::New(
        file_entry.inode(), file_entry.crtime(), file_entry.source_url(),
        file_entry.referrer_url()));
  }
  std::move(callback).Run(std::move(database_entries));
}

}  // namespace policy