# Custom Rules

[Escape's built-in tests](<https://docs.escape.tech/documentation/reference/vulnerabilities/>) cover application and API vulnerabilities. **Custom rules** let you encode the security checks specific to **your** product: business-logic violations, internal naming conventions, deprecated routes, environment isolation: using a deterministic YAML DSL.

There are two flavors:

- **API custom rules** (`type: API`) target REST and GraphQL APIs. They can seed requests, replay them through transformations, run matchers on the request and response, and extract values for re-injection.
- **WebApp custom rules** (`type: WEBAPP`) drive a real Playwright-controlled browser against your web application. They can perform browser actions (navigate, fill, click, complete TOTP / magic-link MFA), then assert against the rendered DOM, cookies, storage, and dialog popups.

## What You Can Express on Each Surface

| Block | API (`type: API`) | WebApp (`type: WEBAPP`) |
| --- | --- | --- |
| `alert` | yes | yes |
| `seed` | REST, HTTP raw, curl | browser actions |
| `transform` | yes | no |
| `detect` | HTTP and schema data | browser state |
| `extractors` | transformed rules without `seed` | no |

## Where Custom Rules Run

Custom rules are attached to a scan through the Escape platform and then picked up by whichever scanner executes your scan. Which rules actually fire depends on three things:

1. The **scan kind** (DAST, AI Pentesting, ASM: and what target type it runs against: API vs WebApp).
2. The **rule type** you authored (`type: API` or `type: WEBAPP`).
3. For API rules, the **shape of the rule**: rules are classified at runtime into one of three execution subtypes:

   | API rule shape | Subtype | Behavior |
   | --- | --- | --- |
   | Has `seed:` | **SEED** | Sends the seeded request, then runs `detect` |
   | Has `transform:` | **COMPLEX** | Replays mutated requests, then runs `detect` |
   | Neither (detector-only) | **FILTER\_ONLY** | Matches passively on observed traffic |

If an API rule has both `seed:` and `transform:`, the seeded path takes precedence: transformations and extractors don't run. Extractors and `use_extraction` variables work only in the COMPLEX path (`transform:` without `seed:`). Extraction runs on the original exchange before mutation, then on each replayed exchange before detection.

WebApp rules (`type: WEBAPP`) always run the same way: the browser executes the `seed:` actions and then evaluates `detect`.

The matrix below summarises, per scan kind, which rule types and subtypes actually execute today.

| Scan kind | API custom rules | WebApp custom rules |
| --- | --- | --- |
| **DAST: API** | Yes: SEED + FILTER\_ONLY + COMPLEX | No |
| **DAST: WebApp** | Yes: SEED + FILTER\_ONLY + COMPLEX | Yes |
| **AI Pentesting: API** | Yes: SEED + FILTER\_ONLY + COMPLEX | No |
| **AI Pentesting: WebApp** | Yes: SEED + FILTER\_ONLY + COMPLEX | Yes |
| **ASM: API surface** | Partial: SEED only | No |
| **ASM: WebApp surface** | Partial: SEED only | Yes |
| **All other ASM kinds** | No | No |

> [!WARNING]
>
> **Custom asset discovery**
>
> Custom asset discovery from custom rules isn't supported today.

## How a Rule Flows

```
flowchart LR
  Author[YAML rule] --> Surface{type}
  Surface -->|API: seeded| ApiSeed["seed -> detect -> alert"]
  Surface -->|API: transformed| ApiPipeline["extract -> transform -> replay -> extract -> detect -> alert"]
  Surface -->|API: detector-only| ApiFilter["observed traffic -> detect -> alert"]
  Surface -->|WebApp| WebPipeline["seed (browser actions) -> detect -> alert"]
  ApiPipeline --> Alert[Find vulnerability]
  ApiSeed --> Alert
  ApiFilter --> Alert
  WebPipeline --> Alert
```

## Where to Look Next

Reference, in the order you usually need it when authoring a rule:

1. [Seeders](<https://docs.escape.tech/documentation/dast/custom-rules/seeders/index.md>): inject requests (API) or drive the browser (WebApp).
2. [Mutators](<https://docs.escape.tech/documentation/dast/custom-rules/mutators/index.md>): replay matched API requests with field-level changes.
3. [Extractors](<https://docs.escape.tech/documentation/dast/custom-rules/extractors/index.md>): capture values from API responses for later re-injection.
4. [Detectors](<https://docs.escape.tech/documentation/dast/custom-rules/detectors/index.md>): match conditions on requests, responses, or the rendered page.
5. [Alerting](<https://docs.escape.tech/documentation/dast/custom-rules/alerting/index.md>): name, severity, context, category.

For real-world examples:

- [Examples for APIs](<https://docs.escape.tech/documentation/dast/custom-rules/examples-api/index.md>): every shipped API example, grouped by vulnerability.
- [Examples for WebApps](<https://docs.escape.tech/documentation/dast/custom-rules/examples-webapp/index.md>): every shipped WebApp example, grouped by vulnerability.

For the JSON Schema [Custom Rules Reference](<https://docs.escape.tech/documentation/reference/custom-rules-reference/index.md>) (consumed by IDE tooling and the Escape platform).

## Featured Examples

A short tour to get you started. Each link jumps to the full prose + YAML on the matching examples page.

### API

- [Anonymous mutation succeeded](<https://docs.escape.tech/documentation/dast/custom-rules/examples-api/#example-api-access-control-unauthenticated-mutation>)
- [BOLA via user swap with fingerprint comparison](<https://docs.escape.tech/documentation/dast/custom-rules/examples-api/#example-api-access-control-bola-user-swap-fingerprint>)
- [Mass assignment via privileged JSON field injection](<https://docs.escape.tech/documentation/dast/custom-rules/examples-api/#example-api-access-control-mass-assignment-role-injection>)
- [Header reinjection with extractor](<https://docs.escape.tech/documentation/dast/custom-rules/examples-api/#example-api-information-disclosure-debug-headers-reinjected>)

### WebApp

- [Reflected XSS](<https://docs.escape.tech/documentation/dast/custom-rules/examples-webapp/#example-webapp-injection-reflected-xss-page-text>)
- [Stored XSS surviving a form submit + reload](<https://docs.escape.tech/documentation/dast/custom-rules/examples-webapp/#example-webapp-injection-stored-xss-form-roundtrip>)
- [Subdomain takeover marker](<https://docs.escape.tech/documentation/dast/custom-rules/examples-webapp/#example-webapp-configuration-subdomain-takeover-marker>)
- [Sensitive file reachable in browser](<https://docs.escape.tech/documentation/dast/custom-rules/examples-webapp/#example-webapp-access-control-sensitive-file-env-exposure>)
