New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 714251 link

Starred by 1 user

Issue metadata

Status: Assigned
Owner: ----
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: ----
Pri: 3
Type: Bug



Sign in to add a comment

Incorrect treatment of "background.allow_js_access" permission during window.open(..., ..., "background")

Project Member Reported by nick@chromium.org, Apr 21 2017

Issue description

This is a low severity bug, found by inspection, and confirmed with a real extension.

Chrome Apps and extensions with the "background" permission in their manifest are able to call window.open(url, "_blank", "background") to open a background window.

If the manifest additionally specifies "background.allow_js_access = false", then this operation is supposed to disallow the caller from getting a WindowProxy reference to the opened window.

However, the filtering we do is insufficient:

  // If the opener is trying to create a background window but doesn't have
  // the appropriate permission, fail the attempt.
  if (container_type == content::mojom::WindowContainerType::BACKGROUND) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
    ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
    InfoMap* map = io_data->GetExtensionInfoMap();
    if (!map->SecurityOriginHasAPIPermission(source_origin,
                                             opener_render_process_id,
                                             APIPermission::kBackground)) {
      return false;
    }

    // Note: this use of GetExtensionOrAppByURL is safe but imperfect.  It may
    // return a recently installed Extension even if this CanCreateWindow call
    // was made by an old copy of the page in a normal web process.  That's ok,
    // because the permission check above would have caused an early return
    // already. We must use the full URL to find hosted apps, though, and not
    // just the origin.
    const Extension* extension =
        map->extensions().GetExtensionOrAppByURL(opener_url);
    if (extension && !extensions::BackgroundInfo::AllowJSAccess(extension))
      *no_javascript_access = true;
#endif

    return true;
  }

The problem arises because, in the above code, the security check is done using |source_origin|, but the no_js_access lookup is done using |opener_url|.  But if opener_url is about:blank, |extension| is null, even though we looked up a valid extension inside of SecurityOriginHasAPIPermission.

To repro this, you can create an empty extension like:

{
   "manifest_version": 2,
   "minimum_chrome_version": "23",
   "name": "Crosh Window extension (ncarter hacked)",
   "update_url": "http://clients2.google.com/service/update2/crx",
   "version": "1",
   "permissions": ["background"],
   "background": {
      "allow_js_access": false
   }
}

Then, load a page from that that extension (maybe navigate to its manifest.json, and execute JS via devtools). Executing the following JS snippet doesn't work:

  w = window.open("", "", "background")  // is blocked

BUT, the following equivalent operation does work:

 w = document.body.appendChild(document.createElement('iframe')).contentWindow.open("", "", "background")  // opens a window

To fix this, we should only do one extension lookup and use it for both checks -- probably based on the SiteInstance SiteURL of the opener frame.
 

Comment 1 by nick@chromium.org, Apr 21 2017

Description: Show this description

Comment 2 by nick@chromium.org, Apr 21 2017

Description: Show this description
Cc: lazyboy@chromium.org
Status: Assigned (was: Untriaged)
Marking as assigned.
Owner: ----

Sign in to add a comment