Skip to main content

Manage allowed browser extensions

Maintain the list of browser extensions a security rule allows, adding or removing extension IDs from automation. Extensions are configured inside the rule's allowedOrBlockedExtensions control, so the workflow is different from list fields elsewhere in the API: there is no per-element delta, you read the control, change the array, and write it back whole.

Use this when:

  • You curate a list of approved Chrome extensions and want a script to keep it current.

Prerequisites: a Super User service account and the environment variables from Getting started. Read Policy overview first.

export RULE_ID='0RLEXAMPLEEXTENSIONSXXXXXXXX' # a security rule
caution

Pair an allow list with a block-all baseline. An allow list only matters if everything else is blocked. Make sure a baseline rule (or any lower-priority rule that matches the same users) sets the extensions policy to block all, and let this higher-priority rule carry the allow list. Without a block-all baseline, extensions not on your list are still allowed by default.

caution

Gotcha: controls have no delta. Unlike a rule's scope or an access-and-data application list, controls are replaced whole. There is no addExtension / removeExtension. To change the list you must GET the rule, edit the extensions array yourself, and PATCH the entire control back. Read-modify-write is required here, so guard against concurrent editors (read immediately before you write).


1. Read the current control​

curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID" -H "Authorization: Bearer $PB_TOKEN"

The relevant slice of the response:

{
"controls": {
"allowedOrBlockedExtensions": {
"mode": "allowByList",
"extensions": [
{ "id": "aapbdbdomjkkjkaonfhkkikfgjllcleb" }
]
}
}
}
FieldNotes
modeallowAll, blockAll, allowByList, or blockByListOrRisk. Use allowByList to allow only the listed extensions.
extensionsArray of { id }. Each id is a 32-character Chrome extension ID (a-p only). Up to 1000.

2. Compute the new array​

In your code, append the extension IDs to add and drop the ones to remove. The result is the complete new list.


3. PATCH the whole control​

Send the full control body with the updated extensions array. Include mode so the control stays in allow-by-list mode:

curl -sS -X PATCH "$PB_API_BASE/policy/security/rules/$RULE_ID" \
-H "Authorization: Bearer $PB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"controls": {
"allowedOrBlockedExtensions": {
"mode": "allowByList",
"extensions": [
{ "id": "aapbdbdomjkkjkaonfhkkikfgjllcleb" },
{ "id": "bfnaelmomeimhlpmgjnjophhpkkoljpa" }
]
}
}
}'

Response (200):

{ "id": "0RLEXAMPLEEXTENSIONSXXXXXXXX" }
note

Other controls on the rule are untouched. In a PATCH, controls you do not mention are preserved; only allowedOrBlockedExtensions is replaced because that is the only key you sent.


Verify and publish​

curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID" -H "Authorization: Bearer $PB_TOKEN"

curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \
-H "Authorization: Bearer $PB_TOKEN" \
-H "Content-Type: application/json" \
-d '{"description": "Update allowed extensions"}'

The GET returns the full rule (confirm the extensions array), and the publish returns 201 when a new active version is created.


Full script (Python)​

import os, requests

base = os.environ["PB_API_BASE"]
headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"}
rule_id = os.environ["RULE_ID"]

def sync_extensions(to_add, to_remove):
# 1. Read immediately before writing (controls have no delta)
rule = requests.get(f"{base}/policy/security/rules/{rule_id}", headers=headers, timeout=30).json()
control = rule.get("controls", {}).get("allowedOrBlockedExtensions", {"mode": "allowByList", "extensions": []})

# 2. Compute the new array
current = {e["id"] for e in control.get("extensions", [])}
current |= set(to_add)
current -= set(to_remove)

# 3. PATCH the whole control back
body = {"controls": {"allowedOrBlockedExtensions": {
"mode": control.get("mode", "allowByList"),
"extensions": [{"id": i} for i in sorted(current)],
}}}
resp = requests.patch(f"{base}/policy/security/rules/{rule_id}", headers=headers, json=body, timeout=30)
resp.raise_for_status()
return resp.json()["id"]

sync_extensions(
to_add=["bfnaelmomeimhlpmgjnjophhpkkoljpa"],
to_remove=[],
)

# Publish (full, or partial-publish is not yet available for policy objects)
requests.post(f"{base}/configuration-management/draft/publish", headers=headers,
json={"description": "Update allowed extensions"}, timeout=30)
note

partial publish does not yet support policy objects, so publish the whole draft (or stage extension changes in their own publish cycle).