Getting a Browser Extension Through Review

16 June 2020

Browser extensions can be confusing at the best of times, but recently we've seen that the current state of affairs is causing web store reviews to take weeks and there is evidence to show that nobody is safe from the wrath of the review teams.

Needless to say, whether you're publishing your first browser extension or already have one listed, it's worth making sure your submissions are prepared for a rigorous review.

Here's a quick run-through for how you can lock down your browser extension and increase your chance of getting your extension approved swiftly.

Timing your permissions

The fewer permissions your extension requires to run, the faster it's likely going to get through review. One of the ways you can trim down permissions without changing functionality is by only requesting additional permissions on an ad-hoc basis.

👎 Install-time permissions

The simplest approach, called "install-time permissions", involves an extension requesting, and being granted, permissions upon installation. Once installed, this allows the given extension to make use of its privileges at any time.

As you can imagine, this is a pattern that does little to restrict extensions functioning outside of their advertised feature-set. Most other ecosystems (see Android, IOS and W3) have tried to avoid or phase out this approach in favor of runtime permissions.

Consider an extension that granted install-time permissions for accessing all webpages and the microphone. Following installation, such an extension has the potential to do the following without requiring further approval:

  • Read any webpage content — bank details, messages, network requests
  • Manipulate any webpage content — inject adverts, political propaganda, Rick Astley videos
  • Listen to any user events — see key presses (including passwords), clicks, network traffic
  • Record audio at any time

Chrome install prompt for all urls

Chrome install prompt for <all_urls>

Because permissions are granted in advance and the user has no way to regulate these permissions following installation, be prepared for meticulous reviews and publishing delays.

👍 Runtime permissions

Runtime permissions are a safer option for both users and extension developers. They work in a similar fashion to most other platforms by requesting no special permissions at install time and instead, prompt the user for additional permissions as-and-when needed.

Chrome install prompt for optional permissions

Chrome install prompt for optional permissions

While this can take more time to implement, by using this method users can have a better idea as to when and why an extension is using any privileges. This also provides a means for the user to use a given extension with limited functionality, as opposed to the all-or-nothing case that install-time permissions offer.

The major caveat that comes with runtime permissions is the requirement that a permission request must be triggered inside of a user event handler. In other words, there is no way to programmatically trigger a permission request unless it is immediately following a user gesture.

Requesting webpage permissions

There are a few different ways a webpage can request permission to access a webpage — with some being better than others.

🥇 Active tab

For cases where a content script is required to run on the current active tab and only following a user invocation:

"permissions": ["activeTab"],

Example use of active tab permissions in an extension manifest.

Because this permission is technically always granted at runtime (by user invocation), declaring it as an install-time permission is common practice and will not result in any warnings to the user.

You might want to use this if:

  • your extension can be invoked by a user action
  • your extension only needs permission for the current tab

🥈 Match patterns

This is a host permission, intended for extensions where a content script is required to run on specific domains:

"permissions": [
  "https://my.domain.com",
	"https://*.domain.com",   // Subdomain wildcard
],
// or
"optional_permissions": [
  "*://my.domain.com",      // Scheme wildcard (https|http|ws)
  "https://my.domain.com/*" // Path wildcard"
],

Example use of scoped host permissions in an extension manifest.

You might want to use this if:

  • your extension needs permission for specific known URLs
  • your extension can't use activeTab

🥉 All URLs

This is the broadest host permission scope intended for extensions where a content script is required to run on all webpages without user invocation.

"permissions": ["<all_urls>"],
// or
"optional_permissions": ["<all_urls>"],

Example use of <all_urls> in an extension manifest.

You might want to use this if:

  • your extension needs permission for all URLs
  • your extension can't use activeTab

Preventing unsafe code

While reviewers will be spending a fair amount of time reviewing the code you submitted, that's not all that needs to be considered. There is likely a fair amount of code that is also included with your extension and has not been explicitly written by you (such as third-party dependencies).

Not only does your code need to be assessed for malicious content, dependencies are also an attack vector.

🛡 Content Security Policies (CSP)

Just like websites, browser extensions can make use of a Content Security Policy (CSP) to limit how code can be sourced and run in the browser.

"content_security_policy": "script-src 'self';",

Example use of a content security policy in an extension manifest.

While we haven't yet found an automated solution for injecting safe checksums into the extension manifest at build time, there is a webpack plugin to do this for webpages — an additional layer of security if your extension includes any HTML pages (such as a devtools panel).

🕵️‍♀️ Linting your extension

The team over at Mozilla have created a great CLI for browser extensions called web-ext. It includes a bunch of neat features for running, building, and testing your extension; and most importantly, it can lint extensions to flag vulnerabilities.

Output from running web-ext lint

Output from running web-ext lint

From issues in the manifest to unsafe practices in your JS, this one catches a myriad of issues and is a must for anyone building a browser extension.

There's a chance that this will be used by store reviewers when assessing your extension so be sure to address any flagged issues before publishing.

Sealing the deal

For the time being, browser extension reviews are being done by humans, not an automated program. Reviewers are the number one people to please as they have the power to accept or deny your listing onto the web store, so be sure to make their life as easy as possible and get them on your side.

📘 Document the purpose

Try to document what the purpose of the extension is, and make it clear why the extension requests the permissions it does.

For example, a "dark mode" extension that accesses your webcam without explanation could trigger alarm bells. If it's explained that the webcam is used to detect ambient light, this is less likely to be the case.

🗺 How did you get there

Nobody likes reading minified code. If there is a build process to publish your extension, make sure to provide the source code and instructions on how to reproduce the built files.

Linking to the open source repo is another great way to gain trust.

🦺 Be conservative

A secure extension which uses CSPs, minimal permissions, and minimal dependencies should take less time to review.

This process isn't perfect

Even if you follow all these guidelines, there's no guarantee your project will be reviewed in a short amount of time. Manual reviews naturally take a long time and there are plenty of good reasons why some of the prior advice can't be followed.

Our very own urql Devtools is a great example of this. Like many other devtools extensions, anyone with our extension installed will have a content script run on every page they visit (i.e. install-time permissions for <all_urls> ).

Frankly, we don't want this to be the case, but the current state of browser extension permissions means it's just not possible to request permission to access a page at runtime when a user opens the devtools panel (as this does not constitute a "user gesture").

Can browser extensions be even safer in the future?

Making suggestions for browser standards is well outside of my skill-set and I'm sure there are some very smart people actively working on this.

One thing I would like to see is a continued movement towards reducing the number of browser extensions requesting permissions at install-time. The limitation in which extensions can only request permissions following a user gesture was clearly to prevent permission spam. I agree with the incentive but I suspect this decision has led to many extensions (including devtools extensions) being cornered into requesting more than permissions than they actually need.

Permission request prompt on Android 10

Prevention of permission request spam in Android 10

Hopefully, in the future, we'll see a process that follows in the footsteps of other platforms that have managed to give users dynamic permission control while also preventing permission request spam.

Additional resources

Credits

Special thanks to Umar Hansa (@umaar) for the peer review!

Join our team — we're hiring!

Related Posts

Distributed Locking in DynamoDB

This article will discuss transactions in DynamoDB, their limitations, and a proposed solution involving distributing locking, replete with details.

The Many Benefits of Good Pull Request Description ...

Submitting pull requests (PR) is a key part of our jobs as software engineers. When we take time to write a good PR description we're improving our co ...

Fixture-first Development

When you hear the word "Storybook", you probably think UI libraries. Tools like Storybook and Cosmos have been around for a few years now and do a pre ...

Check out more of Andy's blog posts