Custom detections let admins write their own detection rules. Use them to identify browser-based threats specific to your organisation.
Rules are written in YAML. A configuration file may contain one or more rules, separated by --- (standard YAML multi-document syntax). Push validates the configuration against this specification when you save a custom detection.
Each rule is a YAML document with the following top-level keys:
| Key | Required | Description |
|---|---|---|
input | Yes | The data source the rule operates on. |
metadata | Yes | Indicator configuration for the rule. |
conditions | Yes | Logic that determines when the rule fires. |
description | No | A note for your own reference. Not used at runtime. |
min_spec | No | Minimum specification version required to evaluate this rule. |
extension_version_constraints | No | Restrict the rule to specific extension versions. |
One of:
dom_content— Match against the page DOM (CSS selectors, HTML comments, page URL, document title, JavaScript-set cookies).web_request— Match against outgoing HTTP requests.web_response— Match against incoming HTTP responses.
| Key | Required | Description |
|---|---|---|
indicator | Yes | An ENUM_STYLE string labelling the specific indicator matched by this rule. Must match ^[A-Z0-9_]+$. You may use any value, e.g. TORRENT_MAGNET_LINK. |
Conditions define when the rule fires. The structure determines how they are combined:
- A map (key-value pairs at the same level) combines conditions with logical AND — all must match.
- A list combines conditions with logical OR — any must match.
These can be nested to express more complex logic. The following example requires method AND request_url, with request_url being evaluated as (path endswith /submit.php):
conditions:
method: POST
request_url:
path|endswith: /submit.php| Key | Description |
|---|---|
css_selectors | Match elements in the page DOM. |
html_comments | Match HTML comments in the document. |
url | Match the page URL (window.location.href). See URL conditions. |
document_title | Match against document.title. |
document_cookies | Match cookies accessible via document.cookie (key-value test). |
A selector entry can be a plain string (used directly with querySelector()), or a map with the following keys:
| Key | Description |
|---|---|
selector | CSS selector using querySelector() — matches the first element. |
selector_all | CSS selector using querySelectorAll() — matches all elements. |
text_content | Test the element's textContent. |
text_nodes | Test the concatenated value of child text nodes. |
inner_html | Test the element's innerHTML. |
outer_html | Test the element's outerHTML. |
condition | For selector_all only. Use all to require every matched element to pass the test. Default is any. |
# Match any <a> inside a <p> that contains "Create one!"
css_selectors:
selector: p > a
text_content|includes: Create one!# All <script> elements must contain "atob("
css_selectors:
selector_all: script
text_content|includes: atob(
condition: allMatch against HTML comment content. A plain string checks for exact equality. Use a named key with a modifier for other tests:
# Match either comment (OR)
html_comments:
- ex1|includes: example text
- ex2|startswith: another example
# Both comments must match (AND)
html_comments:
ex1|includes: example text
ex2|startswith: another example| Key | Description |
|---|---|
method | HTTP method, e.g. GET, POST. |
request_url | Match the request URL. See URL conditions. |
tab_url | Match the URL of the browser tab at the time of the request. See URL conditions. |
type | Resource type, e.g. script, xmlhttprequest, main_frame. |
body | Raw request body. |
form_data | Parsed form data for multipart/form-data and application/x-www-form-urlencoded requests (key-value test). |
request_headers | HTTP request headers (key-value test). |
Note: A rule that tests
request_headerscannot also testbody. These come from different browser extension events.
input: web_request
metadata:
indicator: SUSPICIOUS_FORM_SUBMISSION
conditions:
method: POST
request_url:
path|endswith: /collect
form_data:
- name: email
value|exists: true| Key | Description |
|---|---|
method | HTTP method. |
request_url | Match the request URL. See URL conditions. |
tab_url | Match the URL of the browser tab. See URL conditions. |
type | Resource type. |
status_code | HTTP response status code. |
response_headers | HTTP response headers (key-value test). |
cookies | Cookies from set-cookie response headers (key-value test). |
input: web_response
metadata:
indicator: TRACKING_COOKIE_SET
conditions:
cookies:
- name: tracking_id
value|exists: trueA URL condition can be:
- A plain string — tested against the full URL (exact equality after trimming, or with a modifier).
- A map targeting specific URL components.
| Component | Description | Example value |
|---|---|---|
href | Full URL | https://a.b.example.co.uk:8080/login?x=y#h |
scheme | Protocol, without : | https |
origin | Scheme + host + port | https://a.b.example.co.uk:8080 |
host | Hostname + port | a.b.example.co.uk:8080 |
hostname | Full hostname | a.b.example.co.uk |
subdomain | Subdomain parts only | a.b |
sld | Second-level domain | example |
tld | Top-level domain | co.uk |
root | Hostname without subdomain | example.co.uk |
path | URL path | /login |
params | Full query string | ?x=y |
hash | Fragment | #h |
port | Port number (blank if the default for the protocol) | 8080 |
search_params | Parsed query parameters (key-value test) | — |
hostname_parts | Array of hostname parts split on . — matches if any part matches | — |
# Path ends with /login
request_url:
path|endswith: /login
# Any part of the hostname equals "ipfs"
request_url:
hostname_parts: ipfs
# Query parameter "token" exists
request_url:
search_params:
- name: token
value|exists: trueConditions for search_params, form_data, request_headers, response_headers, cookies, and document_cookies are key-value tests. Use name and value sub-keys to match against entries:
# Any query parameter starting "user_" with a numeric value
request_url:
search_params:
- name|startswith: user_
value|re: ^[\d-]+$# Match a form submission containing an email at a specific domain
form_data:
- name: email
value|endswith: @example.com
- name: password
value|exists: trueAppend modifiers to a condition key with |:
| Modifier | Description |
|---|---|
| (none) | Trim the value and check exact equality. |
|includes | String contains the match. |
|startswith | String starts with the match. |
|endswith | String ends with the match. |
|re | String matches the regular expression. |
|normalize | Collapse whitespace, remove zero-width characters, and lowercase before testing. |
|exists | Boolean — whether the named property exists (true) or does not exist (false). |
|length | Length of a string, or count of items in cookies, form_data, or search_params. |
Modifiers can be stacked. Unknown modifiers are ignored, which allows applying multiple tests of the same type to a single key — all must match:
# Normalize then check equality
text_content|normalize: example text
# All three includes tests must match
css_selectors:
selector_all: script
text_content|includes|a: fetch(
text_content|includes|b: atob(
text_content|includes|c: document.write(Note: Wildcard tests (
includesandre) are performed on values truncated to 500,000 characters.
Use extension_version_constraints to restrict a rule to specific extension versions — for example, to require a minimum version for a feature the rule depends on, or to avoid a version with a known bug.
Specify a list of version ranges. Items in the list are combined with OR. Multiple constraints within a single item are combined with AND.
extension_version_constraints:
- ">=2.5.0"
- ">=1.9.0 <2.0.0"Supported operators: <, <=, >, >=, !=.
---
description: Detect requests using the magnet protocol
input: web_request
metadata:
indicator: TORRENT_MAGNET_LINK_DETECTED
conditions:
request_url:
scheme: magnet
---
description: Detect form submissions to a known data collection endpoint
input: web_request
metadata:
indicator: SUSPICIOUS_FORM_SUBMISSION
conditions:
method: POST
request_url:
path|endswith: /collect
form_data:
- name: email
value|exists: true