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

#include "components/security_state/ios/security_state_utils.h"

#include <memory>

#import "components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h"
#include "components/security_state/core/security_state.h"
#include "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/navigation_manager.h"
#include "ios/web/public/security/security_style.h"
#include "ios/web/public/security/ssl_status.h"
#import "ios/web/public/web_state.h"
#include "url/origin.h"

namespace security_state {

MaliciousContentStatus GetMaliciousContentStatus(
    const web::WebState* web_state) {
  using enum safe_browsing::SBThreatType;

  // There is no known malicious content if there is no allow list or no visible
  // item.
  const SafeBrowsingUrlAllowList* allow_list =
      SafeBrowsingUrlAllowList::FromWebState(web_state);
  web::NavigationItem* visible_item =
      web_state->GetNavigationManager()->GetVisibleItem();
  if (!allow_list || !visible_item)
    return security_state::MALICIOUS_CONTENT_STATUS_NONE;

  // There is no malicious content if there is no allowed unsafe resource and no
  // pending decision.
  const GURL& visible_url = visible_item->GetURL();
  std::set<safe_browsing::SBThreatType> threats;
  bool is_unsafe_resource_allowed_or_pending =
      allow_list->AreUnsafeNavigationsAllowed(visible_url, &threats) ||
      allow_list->IsUnsafeNavigationDecisionPending(visible_url, &threats);
  if (!is_unsafe_resource_allowed_or_pending)
    return security_state::MALICIOUS_CONTENT_STATUS_NONE;

  // Return the appropriate MaliciousContentStatus from the allowed or pending
  // threat type.
  DCHECK(!threats.empty());
  switch (*threats.begin()) {
    case SB_THREAT_TYPE_UNUSED:
    case SB_THREAT_TYPE_SAFE:
    case SB_THREAT_TYPE_URL_PHISHING:
    case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
      return security_state::MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING;
    case SB_THREAT_TYPE_URL_MALWARE:
      return security_state::MALICIOUS_CONTENT_STATUS_MALWARE;
    case SB_THREAT_TYPE_URL_UNWANTED:
      return security_state::MALICIOUS_CONTENT_STATUS_UNWANTED_SOFTWARE;
    case SB_THREAT_TYPE_SAVED_PASSWORD_REUSE:
    case SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE:
    case SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE:
    case SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE:
    case SB_THREAT_TYPE_BILLING:
      return security_state::MALICIOUS_CONTENT_STATUS_BILLING;
    case SB_THREAT_TYPE_MANAGED_POLICY_WARN:
      return security_state::MALICIOUS_CONTENT_STATUS_MANAGED_POLICY_WARN;
    case SB_THREAT_TYPE_MANAGED_POLICY_BLOCK:
      return security_state::MALICIOUS_CONTENT_STATUS_MANAGED_POLICY_BLOCK;
    case DEPRECATED_SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING:
    case DEPRECATED_SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
    case SB_THREAT_TYPE_URL_BINARY_MALWARE:
    case SB_THREAT_TYPE_EXTENSION:
    case SB_THREAT_TYPE_API_ABUSE:
    case SB_THREAT_TYPE_SUBRESOURCE_FILTER:
    case SB_THREAT_TYPE_CSD_ALLOWLIST:
    case SB_THREAT_TYPE_AD_SAMPLE:
    case SB_THREAT_TYPE_BLOCKED_AD_POPUP:
    case SB_THREAT_TYPE_BLOCKED_AD_REDIRECT:
    case SB_THREAT_TYPE_SUSPICIOUS_SITE:
    case SB_THREAT_TYPE_APK_DOWNLOAD:
    case SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST:
    case SB_THREAT_TYPE_CSD_DOWNLOAD_ALLOWLIST:
    case SB_THREAT_TYPE_WARNABLE_SUSPICIOUS_SITE:
      // These threat types are not currently associated with
      // interstitials, and thus resources with these threat types are
      // not ever whitelisted or pending whitelisting.
      NOTREACHED();
  }
  return security_state::MALICIOUS_CONTENT_STATUS_NONE;
}

std::unique_ptr<security_state::VisibleSecurityState>
GetVisibleSecurityStateForWebState(const web::WebState* web_state) {
  auto state = std::make_unique<security_state::VisibleSecurityState>();

  state->malicious_content_status = GetMaliciousContentStatus(web_state);

  const web::NavigationItem* item =
      web_state->GetNavigationManager()->GetVisibleItem();
  if (!item || item->GetSSL().security_style == web::SECURITY_STYLE_UNKNOWN)
    return state;

  state->connection_info_initialized = true;
  state->url = item->GetURL();
  const web::SSLStatus& ssl = item->GetSSL();
  state->certificate = ssl.certificate;
  state->cert_status = ssl.cert_status;
  state->displayed_mixed_content =
      (ssl.content_status & web::SSLStatus::DISPLAYED_INSECURE_CONTENT) ? true
                                                                        : false;

  return state;
}

security_state::SecurityLevel GetSecurityLevelForWebState(
    const web::WebState* web_state) {
  if (!web_state) {
    return security_state::NONE;
  }
  return security_state::GetSecurityLevel(
      *GetVisibleSecurityStateForWebState(web_state));
}

}  // namespace security_state
