Commit message: None
CVE: CVE-2018-18347
CWE: CWE-20
Vulnerability description: Incorrect handling of failed navigations with invalid URLs in Navigation in Google Chrome prior to 71.0.3578.80 allowed a remote attacker to trick a user into executing javascript in an arbitrary origin via a crafted HTML page.

Project: Chrome

  ChromeNavigationPortMappedBrowserTest() {}

Project: Chrome

  SignInIsolationBrowserTest()
      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}

Project: Chrome

  content::WebContents* SimulateCtrlClick(content::WebContents* main_contents,
                                          const char* id_of_anchor_to_click) {
    content::WebContents* new_contents = nullptr;
    {
      content::WebContentsAddedObserver new_tab_observer;
#if defined(OS_MACOSX)
      const char* new_tab_click_script_template =
          "simulateClick(\"%s\", { metaKey: true });";
#else
      const char* new_tab_click_script_template =
          "simulateClick(\"%s\", { ctrlKey: true });";
#endif
      std::string new_tab_click_script = base::StringPrintf(
          new_tab_click_script_template, id_of_anchor_to_click);
      EXPECT_TRUE(ExecuteScript(main_contents, new_tab_click_script));

      new_contents = new_tab_observer.GetWebContents();
    }

    EXPECT_TRUE(WaitForLoadStop(new_contents));
    EXPECT_LT(1, browser()->tab_strip_model()->count());  // More than 1 tab?
    CHECK_NE(TabStripModel::kNoTab,
             browser()->tab_strip_model()->GetIndexOfWebContents(new_contents));
    GURL expected_url(embedded_test_server()->GetURL("/title1.html"));
    EXPECT_EQ(expected_url, new_contents->GetLastCommittedURL());

    VerifyProcessExpectations(main_contents, new_contents);

    {
      std::string name_of_main_contents_window;
      EXPECT_TRUE(ExecuteScriptAndExtractString(
          main_contents, "window.domAutomationController.send(window.name)",
          &name_of_main_contents_window));
      EXPECT_EQ("main_contents", name_of_main_contents_window);

      bool window_opener_cast_to_bool = true;
      EXPECT_TRUE(ExecuteScriptAndExtractBool(
          new_contents, "window.domAutomationController.send(!!window.opener)",
          &window_opener_cast_to_bool));
      EXPECT_FALSE(window_opener_cast_to_bool);

      VerifyBrowsingInstanceExpectations(main_contents, new_contents);
    }

    return new_contents;
  }

Project: Chrome

  void VerifyBrowsingInstanceExpectations(content::WebContents* main_contents,
                                          content::WebContents* new_contents) {
    std::string location_of_opened_window;
    EXPECT_TRUE(ExecuteScriptAndExtractString(
        new_contents,
        "w = window.open('', 'main_contents');"
        "window.domAutomationController.send(w.location.href);",
        &location_of_opened_window));
    EXPECT_EQ(url::kAboutBlankURL, location_of_opened_window);
  }

Project: Chrome

  bool WillProcessResponseCalled() { return will_process_response_called_; }

Project: Chrome

  explicit WillProcessResponseObserver(content::WebContents* web_contents,
                                       const GURL& url)
      : content::WebContentsObserver(web_contents), url_(url) {}

Project: Chrome

    WillProcessResponseObserverThrottle(content::NavigationHandle* handle,
                                        bool* will_process_response_called)
        : NavigationThrottle(handle),
          will_process_response_called_(will_process_response_called) {}

Project: Chrome

  net::EmbeddedTestServer* https_server() { return &https_server_; }

Project: Chrome

void NavigatorImpl::CancelNavigation(FrameTreeNode* frame_tree_node,
                                     bool inform_renderer) {
  if (frame_tree_node->navigation_request() &&
      frame_tree_node->navigation_request()->navigation_handle()) {
    frame_tree_node->navigation_request()
        ->navigation_handle()
        ->set_net_error_code(net::ERR_ABORTED);
  }
  frame_tree_node->ResetNavigationRequest(false, inform_renderer);
  if (frame_tree_node->IsMainFrame())
    navigation_data_.reset();
}

Project: Chrome

void NavigatorImpl::CheckWebUIRendererDoesNotDisplayNormalURL(
    RenderFrameHostImpl* render_frame_host,
    const GURL& url) {
  int enabled_bindings = render_frame_host->GetEnabledBindings();
  bool is_allowed_in_web_ui_renderer =
      WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
          render_frame_host->frame_tree_node()
              ->navigator()
              ->GetController()
              ->GetBrowserContext(),
          url);
  if ((enabled_bindings & kWebUIBindingsPolicyMask) &&
      !is_allowed_in_web_ui_renderer) {
    FrameTreeNode* root_node =
        render_frame_host->frame_tree_node()->frame_tree()->root();
    GetContentClient()->SetActiveURL(
        url, root_node->current_url().possibly_invalid_spec());
    CHECK(0);
  }
}

Project: Chrome

void NavigatorImpl::DidFailLoadWithError(
    RenderFrameHostImpl* render_frame_host,
    const GURL& url,
    int error_code,
    const base::string16& error_description) {
  if (delegate_) {
    delegate_->DidFailLoadWithError(render_frame_host, url, error_code,
                                    error_description);
  }
}

Project: Chrome

void NavigatorImpl::DidNavigate(
    RenderFrameHostImpl* render_frame_host,
    const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
    std::unique_ptr<NavigationHandleImpl> navigation_handle,
    bool was_within_same_document) {
  FrameTreeNode* frame_tree_node = render_frame_host->frame_tree_node();
  FrameTree* frame_tree = frame_tree_node->frame_tree();

  bool is_same_document_navigation = controller_->IsURLSameDocumentNavigation(
      params.url, params.origin, was_within_same_document, render_frame_host);

  if (is_same_document_navigation &&
      render_frame_host !=
          frame_tree_node->render_manager()->current_frame_host()) {
    bad_message::ReceivedBadMessage(render_frame_host->GetProcess(),
                                    bad_message::NI_IN_PAGE_NAVIGATION);
    is_same_document_navigation = false;
  }

  if (ui::PageTransitionIsMainFrame(params.transition)) {
    if (delegate_) {
      if (delegate_->CanOverscrollContent()) {
        if (!was_within_same_document)
          controller_->TakeScreenshot();
      }

      delegate_->DidNavigateMainFramePreCommit(is_same_document_navigation);
    }
  }

  frame_tree_node->render_manager()->DidNavigateFrame(
      render_frame_host, params.gesture == NavigationGestureUser,
      is_same_document_navigation);

  frame_tree_node->SetCurrentOrigin(
      params.origin, params.has_potentially_trustworthy_unique_origin);
  frame_tree_node->SetInsecureRequestPolicy(params.insecure_request_policy);
  frame_tree_node->SetInsecureNavigationsSet(params.insecure_navigations_set);

  if (!is_same_document_navigation) {
    render_frame_host->ResetContentSecurityPolicies();
    frame_tree_node->ResetForNavigation();
  }

  SiteInstanceImpl* site_instance = render_frame_host->GetSiteInstance();
  if (!site_instance->HasSite() &&
      SiteInstanceImpl::ShouldAssignSiteForURL(params.url)) {
    site_instance->SetSite(params.url);
  }

  if (ui::PageTransitionIsMainFrame(params.transition) && delegate_)
    delegate_->SetMainFrameMimeType(params.contents_mime_type);

  int old_entry_count = controller_->GetEntryCount();
  LoadCommittedDetails details;
  bool did_navigate = controller_->RendererDidNavigate(
      render_frame_host, params, &details, is_same_document_navigation,
      navigation_handle.get());

  if (old_entry_count != controller_->GetEntryCount() ||
      details.previous_entry_index !=
          controller_->GetLastCommittedEntryIndex()) {
    frame_tree->root()->render_manager()->SendPageMessage(
        new PageMsg_SetHistoryOffsetAndLength(
            MSG_ROUTING_NONE, controller_->GetLastCommittedEntryIndex(),
            controller_->GetEntryCount()),
        site_instance);
  }

  render_frame_host->DidNavigate(params, is_same_document_navigation);

  if (details.type != NAVIGATION_TYPE_NAV_IGNORE && delegate_) {
    DCHECK_EQ(!render_frame_host->GetParent(),
              did_navigate ? details.is_main_frame : false);
    navigation_handle->DidCommitNavigation(
        params, did_navigate, details.did_replace_entry, details.previous_url,
        details.type, render_frame_host);
    navigation_handle.reset();
  }

  if (!did_navigate)
    return;  // No navigation happened.


  RecordNavigationMetrics(details, params, site_instance);

  if (delegate_) {
    if (details.is_main_frame) {
      delegate_->DidNavigateMainFramePostCommit(render_frame_host, details,
                                                params);
    }

    delegate_->DidNavigateAnyFramePostCommit(render_frame_host, details,
                                             params);
  }
}

Project: Chrome

void NavigatorImpl::DidStartMainFrameNavigation(
    const GURL& url,
    SiteInstanceImpl* site_instance,
    NavigationHandleImpl* navigation_handle) {
  NavigationEntryImpl* pending_entry = controller_->GetPendingEntry();
  bool has_browser_initiated_pending_entry =
      pending_entry && !pending_entry->is_renderer_initiated();

  bool renderer_provisional_load_to_pending_url =
      pending_entry && pending_entry->is_renderer_initiated() &&
      (pending_entry->GetURL() == url);

  bool has_transient_entry = !!controller_->GetTransientEntry();

  if (!has_browser_initiated_pending_entry && !has_transient_entry &&
      !renderer_provisional_load_to_pending_url) {
    std::unique_ptr<NavigationEntryImpl> entry =
        NavigationEntryImpl::FromNavigationEntry(
            NavigationController::CreateNavigationEntry(
                url, content::Referrer(), ui::PAGE_TRANSITION_LINK,
                true /* is_renderer_initiated */, std::string(),
                controller_->GetBrowserContext(),
                nullptr /* blob_url_loader_factory */));
    if (pending_entry) {
      entry->set_should_replace_entry(pending_entry->should_replace_entry());
      entry->SetRedirectChain(pending_entry->GetRedirectChain());
    }

    controller_->SetPendingEntry(std::move(entry));
    if (delegate_)
      delegate_->NotifyChangedNavigationState(content::INVALIDATE_TYPE_URL);
  }
}

Project: Chrome

void NavigatorImpl::NavigateFromFrameProxy(
    RenderFrameHostImpl* render_frame_host,
    const GURL& url,
    SiteInstance* source_site_instance,
    const Referrer& referrer,
    ui::PageTransition page_transition,
    bool should_replace_current_entry,
    const std::string& method,
    scoped_refptr<network::ResourceRequestBody> post_body,
    const std::string& extra_headers,
    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
  if (method != "POST" && post_body) {
    NOTREACHED();
    post_body = nullptr;
  }

  if (!delegate_->ShouldTransferNavigation(
          render_frame_host->frame_tree_node()->IsMainFrame()))
    return;

  Referrer referrer_to_use(referrer);
  SiteInstance* current_site_instance = render_frame_host->GetSiteInstance();
  if (!GetContentClient()->browser()->ShouldAllowOpenURL(
          source_site_instance ? source_site_instance : current_site_instance,
          url)) {
    return;
  }

  bool is_renderer_initiated = true;
  if (render_frame_host->web_ui()) {
    referrer_to_use = Referrer();

    is_renderer_initiated = false;
  }

  GetContentClient()->browser()->OverrideNavigationParams(
      current_site_instance, &page_transition, &is_renderer_initiated,
      &referrer_to_use);

  controller_->NavigateFromFrameProxy(
      render_frame_host, url, is_renderer_initiated, source_site_instance,
      referrer_to_use, page_transition, should_replace_current_entry, method,
      post_body, extra_headers, std::move(blob_url_loader_factory));
}

Project: Chrome

void NavigatorImpl::OnAbortNavigation(FrameTreeNode* frame_tree_node) {
  DCHECK(!IsPerNavigationMojoInterfaceEnabled());
  NavigationRequest* ongoing_navigation_request =
      frame_tree_node->navigation_request();
  if (!ongoing_navigation_request ||
      ongoing_navigation_request->browser_initiated()) {
    return;
  }

  CancelNavigation(frame_tree_node, false);
}

Project: Chrome

void NavigatorImpl::OnBeforeUnloadACK(FrameTreeNode* frame_tree_node,
                                      bool proceed,
                                      const base::TimeTicks& proceed_time) {
  DCHECK(frame_tree_node);

  NavigationRequest* navigation_request = frame_tree_node->navigation_request();

  if (!navigation_request)
    return;

  if (!proceed) {
    CancelNavigation(frame_tree_node, true);
    return;
  }

  if (navigation_request->state() >
      NavigationRequest::WAITING_FOR_RENDERER_RESPONSE) {
    DCHECK(navigation_request->from_begin_navigation());
    return;
  }

  navigation_request->set_navigation_start_time(proceed_time);

  DCHECK_EQ(NavigationRequest::WAITING_FOR_RENDERER_RESPONSE,
            navigation_request->state());

  navigation_request->BeginNavigation();
}

Project: Chrome

void NavigatorImpl::OnBeginNavigation(
    FrameTreeNode* frame_tree_node,
    const CommonNavigationParams& common_params,
    mojom::BeginNavigationParamsPtr begin_params,
    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
    mojom::NavigationClientAssociatedPtrInfo navigation_client) {
  DCHECK(frame_tree_node);

  NavigationRequest* ongoing_navigation_request =
      frame_tree_node->navigation_request();

  if (ongoing_navigation_request && ongoing_navigation_request->request_params()
                                        .is_history_navigation_in_new_child) {
    ongoing_navigation_request = nullptr;
    frame_tree_node->ResetNavigationRequest(false, true);
  }

  if (ongoing_navigation_request &&
      ongoing_navigation_request->browser_initiated() &&
      !common_params.has_user_gesture) {
    if (!IsPerNavigationMojoInterfaceEnabled()) {
      RenderFrameHost* current_frame_host =
          frame_tree_node->render_manager()->current_frame_host();
      current_frame_host->Send(
          new FrameMsg_DroppedNavigation(current_frame_host->GetRoutingID()));
    }
    return;
  }

  if (frame_tree_node->IsMainFrame()) {
    DidStartMainFrameNavigation(
        common_params.url,
        frame_tree_node->current_frame_host()->GetSiteInstance(), nullptr);
    navigation_data_.reset();
  }
  NavigationEntryImpl* pending_entry = controller_->GetPendingEntry();
  NavigationEntryImpl* current_entry = controller_->GetLastCommittedEntry();
  bool override_user_agent =
      current_entry
          ? current_entry->GetIsOverridingUserAgent()
          : delegate_ && delegate_->ShouldOverrideUserAgentInNewTabs();
  frame_tree_node->CreatedNavigationRequest(
      NavigationRequest::CreateRendererInitiated(
          frame_tree_node, pending_entry, common_params,
          std::move(begin_params), controller_->GetLastCommittedEntryIndex(),
          controller_->GetEntryCount(), override_user_agent,
          std::move(blob_url_loader_factory), std::move(navigation_client)));
  NavigationRequest* navigation_request = frame_tree_node->navigation_request();

  DCHECK(!FrameMsg_Navigate_Type::IsSameDocument(
      navigation_request->common_params().navigation_type));
  bool should_dispatch_beforeunload =
      frame_tree_node->current_frame_host()->ShouldDispatchBeforeUnload(
          true /* check_subframes_only */);
  if (should_dispatch_beforeunload) {
    frame_tree_node->navigation_request()->SetWaitingForRendererResponse();
    frame_tree_node->current_frame_host()->DispatchBeforeUnload(
        RenderFrameHostImpl::BeforeUnloadType::RENDERER_INITIATED_NAVIGATION,
        FrameMsg_Navigate_Type::IsReload(common_params.navigation_type));
    return;
  }

  navigation_request->BeginNavigation();
}

Project: Chrome

void NavigatorImpl::RequestOpenURL(
    RenderFrameHostImpl* render_frame_host,
    const GURL& url,
    bool uses_post,
    const scoped_refptr<network::ResourceRequestBody>& body,
    const std::string& extra_headers,
    const Referrer& referrer,
    WindowOpenDisposition disposition,
    bool should_replace_current_entry,
    bool user_gesture,
    blink::WebTriggeringEventInfo triggering_event_info,
    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {

  if (render_frame_host !=
      render_frame_host->frame_tree_node()->current_frame_host()) {
    return;
  }

  SiteInstance* current_site_instance = render_frame_host->GetSiteInstance();

  std::vector<GURL> redirect_chain;

  GURL dest_url(url);
  if (!GetContentClient()->browser()->ShouldAllowOpenURL(current_site_instance,
                                                         url)) {
    dest_url = GURL(url::kAboutBlankURL);
  }

  int frame_tree_node_id = -1;

  if (disposition == WindowOpenDisposition::CURRENT_TAB &&
      render_frame_host->GetParent()) {
    frame_tree_node_id =
        render_frame_host->frame_tree_node()->frame_tree_node_id();
  }

  OpenURLParams params(dest_url, referrer, frame_tree_node_id, disposition,
                       ui::PAGE_TRANSITION_LINK,
                       true /* is_renderer_initiated */);
  params.uses_post = uses_post;
  params.post_data = body;
  params.extra_headers = extra_headers;
  if (redirect_chain.size() > 0)
    params.redirect_chain = redirect_chain;
  params.should_replace_current_entry = should_replace_current_entry;
  params.user_gesture = user_gesture;
  params.triggering_event_info = triggering_event_info;

  params.source_site_instance = current_site_instance;

  params.source_render_frame_id = render_frame_host->GetRoutingID();
  params.source_render_process_id = render_frame_host->GetProcess()->GetID();

  if (render_frame_host->web_ui()) {
    params.referrer = Referrer();

    params.is_renderer_initiated = false;
  }

  params.blob_url_loader_factory = std::move(blob_url_loader_factory);

  GetContentClient()->browser()->OverrideNavigationParams(
      current_site_instance, &params.transition, &params.is_renderer_initiated,
      &params.referrer);

  if (delegate_)
    delegate_->OpenURL(params);
}

Project: Chrome

bool NavigatorImpl::StartHistoryNavigationInNewSubframe(
    RenderFrameHostImpl* render_frame_host,
    const GURL& default_url) {
  return controller_->StartHistoryNavigationInNewSubframe(render_frame_host,
                                                          default_url);
}