A Chrome extension declares what it wants in the permissions array of manifest.json, and
that array is most of what a user is shown before installing. So any checklist, audit sheet or risk table that reasons
about extension permissions is only as good as its list of names. A row naming something Chrome does not implement
cannot be wrong in an interesting way. It is not describing anything.
I had a 68-row permission table to audit and decided to check the names before the judgements, because the names are the part with an authority behind them. Chrome publishes the complete list at developer.chrome.com/docs/extensions/reference/permissions-list. Extracting every quoted permission name from that page on 8 August 2026 gives 78 entries. Comparing against it is a set membership test; there is nothing clever to get right.
Ten of the 68 names were not on the list. They are not typos. They fail in four distinct ways, and each way is a mistake that can be made again.
The tempting shortcut is to treat a dotted name as suspect: storage.session has a dot in it, so it
must be an API path rather than a permission. That heuristic is wrong often enough to be dangerous. Thirteen of the 78
official names contain a dot:
accessibilityFeatures.modify enterprise.networkingAttributes
accessibilityFeatures.read enterprise.platformKeys
downloads.open identity.email
downloads.ui system.cpu
enterprise.deviceAttributes system.display
enterprise.hardwarePlatform system.memory
system.storage
downloads.open is a real permission and downloads.shelf is not, and nothing in the shape
of either string tells you which is which. That is the actual finding: no syntactic rule separates a permission from a
plausible non-permission, which is exactly why the check has to be mechanical rather than by eye.
| Name in the table | What it actually is |
|---|---|
storage.session | a StorageArea exposed as a property of chrome.storage |
storage.managed | the same, for values pushed by enterprise policy |
storage.accessed | not a documented storage area either |
windows | an API namespace, chrome.windows, gated by a differently-named permission |
The storage areas are real and they behave differently from one another, so a document describing them describes
something true. But the manifest permission that unlocks all of them is the single string storage, which
is what the official list carries. Splitting one permission into three rows makes an extension look like it is asking
for more than it is, and it makes any total over the table meaningless.
windows is the more instructive case, because the official list answers it directly in the entry for
tabs:
"tabs" — Gives access to privileged fields of the Tab objects used by several APIs, includingchrome.tabsandchrome.windows. You usually don't need to declare this permission to use those APIs.
So window management is not gated by a windows permission; the privileged fields are gated by
tabs, and the ordinary parts of the API need no declaration at all. A table with a windows
row has invented a permission boundary that does not exist, and by doing so it hides the one that does.
| Name in the table | The real siblings on the official list |
|---|---|
downloads.shelf | downloads, downloads.open, downloads.ui |
system.network | system.cpu, system.display, system.memory, system.storage |
wallpaper.legacy | wallpaper |
This is the mode that survives review, because each invented name sits inside a family that genuinely has members.
system.cpu and system.memory exist, so system.network reads as the obvious
fourth. It is not there. Neither is downloads.shelf, in a family where two dotted siblings do exist.
commands and host_permissions are both top-level keys in manifest.json.
Neither appears anywhere on the permissions list.
host_permissions is the consequential one, because it is where the broad access actually lives. A
manifest asking for <all_urls> asks in host_permissions, not in permissions.
A table that files host_permissions as one permission among 68 has flattened the most important axis of
extension risk — which sites the extension may touch — into a single row sitting next to idle and
alarms. That is not a naming slip. It means the model of the manifest is wrong.
fileSystem is real, and it is not an extension permission. It belongs to the Chrome Apps platform,
which is retired. The extensions list carries no fileSystem entry at all; the nearest names on it are
fileSystemProvider, for implementing a virtual file system, and fileBrowserHandler, for
handling files opened from the ChromeOS file browser. Both are narrower things wearing a similar name.
This one is worth separating from the invented names because the failure is different in kind. The name has a real referent and correct documentation somewhere, so every check short of "is it on this specific list" passes it. Searching for it finds a genuine API reference. Only the set comparison catches it.
Extract the quoted names from the official list, extract the names from whatever you are auditing, take the difference. On 8 August 2026 the first half returned 78 names:
curl -s https://developer.chrome.com/docs/extensions/reference/permissions-list \ | grep -oE '"[a-zA-Z][a-zA-Z0-9._]*"</code>' \ | sort -u
The table I was auditing had already been reviewed by hand, and that review reported eight bad names. Running the
set difference returned ten. The two it had missed were windows and fileSystem — precisely
the two that do not look wrong, because both name real things. Every name that was caught by eye was caught because it
looked odd, and both names that looked reasonable survived. An eyeball pass over a list of 68 plausible identifiers
finds the ugly errors and leaves the well-dressed ones in place.
Two caveats on the result. The official list changes as permissions are added and removed across Chrome releases,
so a count is a snapshot with a date on it, not a constant. And the list deliberately excludes host permissions,
because those live under a separate manifest key — an audit that wants to describe what an extension can actually
reach has to read host_permissions and the content_scripts match patterns as their own
question.
Permissions are the only pre-install signal most people get. The install dialog paraphrases them into sentences like "Read your browsing history", and that paraphrase is generated from what the manifest declares — the official list prints the exact warning string next to the permissions that trigger one. A reviewer working from a checklist containing names Chrome does not implement will watch for warnings that can never fire, while the field that decides the blast radius has been mis-filed as an ordinary row.
The defensible version of a permission audit is boring. Take the manifest, intersect its permissions
array with the official list, report anything outside the intersection as undocumented rather than as safe, and treat
host_permissions as a separate question with its own answer. Any risk weighting layered on top is
editorial judgement and should be labelled as such, because there is no measurement behind scoring tabs
at 72 out of 100.