Extensions in Chromium and where to find them

Michael Maltsev

Extensions in Chromium and where to find them

Chromium extensions provide a way of customizing the web-browsing experience by adding extra functionality to the browser. The straightforward way of loading an extension in Chromium is by installing it via a supported extension store. But that’s not the only way. An extension can also be loaded from a folder on the computer, via the registry, or via admin policies. Some extensions, called component extensions, are an integral part of the browser which happen to be implemented as extensions.

For each loaded extension, Chromium keeps track of its source location type, which affects the way the extension is treated. For example, component extensions are not displayed on the extensions page (chrome://extensions) and have some extra privileges.

In this post, we’ll take a closer look at where inside Chromium extensions are found, and why their location matters.

The ManifestLocation enum

The ManifestLocation enum is defined in the manifest.mojom file in the Chromium source code, and at the time of writing, it has 10 valid values. The enum is preceded by a short explanation comment:

Historically, where an extension was loaded from, and whether an extension's files were inside or outside of the profile's directory. In modern usage, a Location can be thought of as the installation source: whether an extension was explicitly installed by the user (through the UI), or implicitly installed by other means. For example, enterprise policy, being part of Chrome per se (but implemented as an extension), or installed as a side effect of installing third party software.

Here are the 10 values and comments from the source code which provide a short explanation:

The location rank

The values in the ManifestLocation enum are ordered chronologically, with each newly added value added to the end of the list. I found it more convenient to order the values by their rank. The GetLocationRank function, implemented in manifest.cc, assigns a rank to each ManifestLocation value in order to be able to decide which extension to load if there’s an extension of the same id in different locations. The rank has a good correlation with the privileges that are given to the extensions from the corresponding location.

Here are the ranks along with short comments which can be found in the source code:

In addition to the rank values, the ranking function divides the 10 ManifestLocation values to 5 groups, which helps get some extra intuition about the values and the way the browser treats them.

Listing installed extensions

Before we begin looking at the different extension types, let’s tackle another basic question - how do we list all of the installed extensions and their location values? There’s the extensions page (chrome://extensions), but as we mentioned, not all extensions are displayed on it, and it also doesn’t show the location values.

One way to see all enabled extensions, including component extensions, is to navigate to chrome://system and look at the extensions row. Another way to see component extensions is to run Chromium with the --show-component-extension-options command line switch which will show them on the familiar extensions page (chrome://extensions). But those two methods still don’t give us enough information. Specifically, we still can’t see the location value of each extension.

To get full visibility, we can look directly at the information in the user profile folder. The “Secure Preferences” file contains the information we need. For Chrome on Windows, the file for the default profile is located in the following folder:

%localappdata%\Google\Chrome\User Data\Default

The file is a JSON file containing, among other details, the list of installed extensions for the profile which can be found under extensions.settings. For each extension, the key of the entry is the extension ID, and one of the properties is “location”. The location value is a number corresponding to the ManifestLocation enum defined in the manifest.mojom file.

For example, Google Chrome comes bundled with a component extension called Google Hangouts, and we can see the following entry for it in the Secure Preferences file:

{
  "extensions": {
    "settings": {
      // …
      "nkeimhogjdpnpccoofpliimaahmaaome": {
        // …
        "location": 5,
        // …
        "path": "C:\\Program Files\\Google\\Chrome\\Application\\97.0.4692.71\\resources\\hangout_services",
        // …
      },
      // …
    }
  }
}

This tells us that the location value of Google Hangouts is kComponent. The path parameter is also interesting - we can see that the extension is loaded from the readonly installation folder of Chrome, not from the profile folder.

After installing vanilla Google Chrome (version 97.0.4692.71) on Windows 10, here’s what I got under my profile:

Note that 4 extensions are visible on the extensions page, but in fact 14 extensions are installed.

Extension types

The post wouldn’t be complete without mentioning extension types. Chromium defines another enum called Type, defined in manifest.h, which contains the following values at the time of writing:

  • TYPE_UNKNOWN
  • TYPE_EXTENSION
  • TYPE_THEME
  • TYPE_USER_SCRIPT
  • TYPE_HOSTED_APP
  • TYPE_LEGACY_PACKAGED_APP
  • TYPE_PLATFORM_APP
  • TYPE_SHARED_MODULE
  • TYPE_LOGIN_SCREEN_EXTENSION
  • TYPE_CHROMEOS_SYSTEM_EXTENSION

The logic that determines the extension type is implemented in the GetTypeFromManifestValue function in manifest.cc. That’s the reason why, for example, the Slides extension was visible on the extensions page for me, but the YouTube extension wasn’t - the former is of type TYPE_EXTENSION, while the latter is of type TYPE_HOSTED_APP (and is visible on chrome://apps).

In this post, we’ll be focusing on extensions of type TYPE_EXTENSION.

kComponent

As a reminder, here’s what the comment in the ManifestLocation enum says about kComponent:

An integral component of Chrome itself, which happens to be implemented as an extension. We don't show these in the management UI.

Component extensions are registered to be loaded by the AddDefaultComponentExtensions function in component_loader.cc, and loaded by ​​the AddComponentExtension function in extension_service.cc. The list of component extensions is predefined in the code, and can’t be changed without changing the code and recompiling the browser. The extensions themselves are loaded from the browser installation folder, not from the profile folder, and don’t change unless the browser changes, e.g. on a browser update. That means that component extensions don’t update independently like regular extensions do.

As an example, here’s the commit that adds the Google Network Speech component extension.

It can be useful as a reference for adding your own component extension to Chromium.

Regarding special treatment of the browser for component extensions, you can find several such code snippets in the source code by looking for “kComponent” and “IsComponentLocation” around the code. Here are a couple of examples:

Note: Confusingly, in addition to component extensions, Chromium has something completely unrelated called components. Components are listed in chrome://components, and are bundles of files, usually dynamic libraries or data files, which are updated separately from the browser itself. To add to the confusion, components are distributed in .crx files, but they have nothing to do with extensions.

kExternal*

Before proceeding to kExternalComponent, there are details that are common to all kExternal* values. Extensions for all 6 kExternal* values (kExternalComponent, kExternalPolicy, kExternalPolicyDownload, kExternalRegistry, kExternalPref, kExternalPrefDownload) are loaded by loaders which are specialization classes of the ExternalLoader class. Those loaders are used by instances of ExternalProviderImpl that pass the loaded extensions to an installation service. The extensions are eventually loaded by the CheckForExternalUpdates function in extension_service.cc.

Each external provider can provide extensions in two ways: Extensions originating from .crx files, and extensions originating from update URLs. The external provider is initialized with a location type for each of the two ways, which the installed extension will end up being marked with.

Here is the rough list of extension loader specializations and their location values. ChromeOS-specific and other OS-specific cases are not included.


It’s interesting to note that kExternalPolicy is not present in the table. It’s only being used in ChromeOS.

kExternalComponent

The comment from the ManifestLocation enum:

Similar to kComponent in that it's considered an internal implementation detail of chrome, but installed from an update URL like the *kDownload ones.

External component extensions are registered in the StartLoading function in external_component_loader.cc. The registration sets the extension IDs and the extension store URL to be used for installing the extensions.

Like with component extensions, the browser has special treatment for external component extensions. You can find relevant snippets in the source code by looking for “kExternalComponent” and “IsComponentLocation”.

kExternalPolicy

The comment from the ManifestLocation enum:

A crx file from an external directory (via admin policies), cached locally and installed from the cache.

As was already mentioned, the kExternalPolicy location is only used in ChromeOS.

kExternalPolicyDownload

The comment from the ManifestLocation enum:

A crx file from an external directory (via admin policies), installed from an update URL.

kExternalPolicyDownload extensions are registered in the StartLoading function in external_policy_loader.cc. Two instances of ExternalPolicyLoader are created, one for forced extensions (that can’t be disabled) and one for recommended extensions.

Like with other location types, the browser has special treatment for policy extensions. You can find relevant snippets in the source code by looking for “kExternalPolicyDownload” and “IsPolicyLocation”.

kCommandLine

The comment from the ManifestLocation enum:

--load-extension.

Extensions that are loaded by using the --load-extension command line switch are marked with the kCommandLine location. They are loaded by ​​the LoadExtensionsFromCommandLineFlag function in extension_service.cc, which delegates the loading to UnpackedInstaller which loads the extensions from their target folders.

The browser has special treatment for extensions which are loaded unpacked. You can find relevant snippets in the source code by looking for “IsUnpackedLocation”. There are also a few places with special treatment specifically for “kCommandLine”.

kUnpacked

The comment from the ManifestLocation enum:

From loading an unpacked extension from the extensions settings page.

Extensions that are manually loaded from a folder for development are marked with the kUnpacked location. They are loaded by ​​the FileSelected function in developer_private_api.cc, which delegates the loading to UnpackedInstaller which loads the extensions from their target folders. They are also reloaded by ​​the LoadExtensionForReload function in extension_service.cc on browser launch.

The browser has special treatment for extensions which are loaded unpacked. You can find relevant snippets in the source code by looking for “IsUnpackedLocation”.

kExternalRegistry

The comment from the ManifestLocation enum:

A crx file from an external directory (via eg the registry on Windows).

kExternalRegistry is a Windows-specific location for extensions that were loaded from a local .crx file via the registry as specified here: Pre-installed Extensions (Pre-installing via the Registry). The extensions are registered in the StartLoading function in external_registry_loader_win.cc.

Note: Extensions that were loaded from a URL (and not a .crx file) via the registry as specified here: Alternative extension distribution options (Using the Windows registry) are registered with the kExternalPrefDownload location, not kExternalRegistry.

kExternalPref

The comment from the ManifestLocation enum:

A crx file from an external directory (via prefs).

kExternalPref is a location for extensions that were loaded from a local .crx file via the browser preferences as specified here: Alternative extension distribution options (Using a preferences file). The extensions are registered in the StartLoading function in external_pref_loader.cc. Two instances of ExternalPrefLoader are created, one for the system-wide preferences and one for the per-user preferences. kExternalPref is not used on Windows (except for ExtensionMigrator which is a specific migration case).

Note: Extensions that were loaded from a URL (and not a .crx file) via the browser preferences as specified here: Alternative extension distribution options (Using a preferences file) are registered with the kExternalPrefDownload location, not kExternalRegistry.

kExternalPrefDownload

The comment from the ManifestLocation enum:

A crx file from an external directory (via prefs), installed from an update URL.

kExternalPrefDownload is a location for extensions which were loaded from an update URL via the registry (Windows) or via the browser preferences (non-Window). See kExternalRegistry and kExternalPref for more information.

kInternal

The comment from the ManifestLocation enum:

A crx file from the internal Extensions directory. This includes extensions explicitly installed by the user. It also includes installed-by-default extensions that are not part of Chrome itself (and thus not a kComponent), but are part of a larger system (such as Chrome OS).

Except for a couple of specific cases, kInternal extensions are the common, regular extensions that are installed by the user from the extension store.

Summary

In this post, we went over the extension locations that Chromium defines and uses. We looked at when and where they’re used, and how they affect the way the browser treats the extensions.

Michael Maltsev

Michael Maltsev is a senior software developer at Island. As part of the core team, he is responsible for developing the Island browser as well as leading research in the Chromium and security fields. Before Island, Michael was a vulnerability researcher in ZecOps, where he focused on Windows security and exploitation.

You might also like