# WebApp Testing: Scope Configuration

## Overview

The scope configuration controls which pages are crawled, which elements are interacted with, and which API traffic is analyzed during WebApp Testing scans. The configuration is organized into four main areas:

- **Exploration Scope**: High-level domain boundaries for the entire scan: this is what you edit to pick up APIs that live on a domain different from the base URL
- **Crawling Scope**: Controls page crawling, element interactions, and URL visit patterns
- **API Testing Scope**: Controls which API traffic is captured, analyzed, and tested
- **Crawling Tuning**: Optimization settings for the crawling process (visit limits, logging)

This separation enables precise control over scan coverage while optimizing scan duration and resource utilization.

## Exploration Scope

The **exploration scope** is the outermost boundary of the scan: the set of domains the scanner is allowed to reach at all. Everything else (crawling rules, API testing rules) operates *within* this boundary.

By default, the exploration scope is derived automatically from the profile's base URL (`https://app.example.com` → `app.example.com` is in scope). `scope.use_defaults` defaults to `true`: it injects the organization's ASM scope into scanner configuration. For applications spanning multiple domains, include each domain you want to test:

- A frontend on `app.example.com` calls `api.example.com`: add the API domain to include captured calls in the security-testing scope.
- A customer portal spans `portal.example.com` and `auth.example.com`.
- A SaaS application serves static assets from `cdn.example.net` and issues XHR requests to `backend.example.com`.

Extend the exploration scope to include traffic to these additional domains in security testing.

### Where It Lives in the Configuration

The exploration scope is expressed as `type: domain` entries in the **top-level global `scope.allowlist`**. It's shared across WebApp, REST and GraphQL scanners in the profile, so use it to declare "these are the domains this application is made of".

```yaml
# Top-level global scope: this IS the exploration scope.
scope:
  allowlist:
    - type: domain
      operation: equals
      value: app.example.com       # the frontend (base URL host)
    - type: domain
      operation: equals
      value: api.example.com       # backend API called by the frontend
    - type: domain
      operation: ends_with
      value: .example.com          # any subdomain of example.com
```

Once this is set, the WebApp scanner:

- Can crawl pages served from any of the listed domains **when** `frontend_dast.scope.crawling.extend_global_scope` is set to `true` (it's `false` by default).
- Captures and actively tests API traffic going to any of those domains (because `frontend_dast.scope.api_testing.extend_global_scope` defaults to `true`).

### Typical Pattern: "Pick Up APIs Outside of Scope"

If the frontend scan captures calls to `api.example.com` outside the current exploration scope, include that domain using one of the options below.

**Option A: Extend the global `scope.allowlist` (recommended).** Declares the API domain once, and every scanner on the profile picks it up:

```yaml
scope:
  allowlist:
    - type: domain
      operation: equals
      value: app.example.com
    - type: domain
      operation: equals
      value: api.example.com   # <-- makes the backend API part of the scan
```

**Option B: Scope the extension to the WebApp scan only.** Useful when you want the API to be in scope for the WebApp scan but not for a separate REST/GraphQL scanner that shares the same global config:

```yaml
frontend_dast:
  scope:
    api_testing:
      extend_global_scope: true   # default, shown for clarity
      allowlist:
        - type: domain
          value: api.example.com
```

See [Test Selection](<https://docs.escape.tech/documentation/dast/webapp-testing/test-selection/index.md>) for the full list of reasons a captured endpoint may not be tested: exploration scope is the first thing to check, but not the only one.

## Scope Rule System

The scope configuration uses a rule-based system with allowlists and blocklists to precisely define what's in scope for scanning. Each rule has:

- **`type`**: The type of target (for example, `domain`, `web_page_url`, `rest_api_path`)
- **`value`**: The pattern or value to match
- **`operation`**: The matching operation (for example, `equals`, `regex`, `wildcard`)
- **`method`**: For API rules, the optional HTTP method

See [Scope Operations](<https://docs.escape.tech/documentation/dast/api-testing/scope/#scope-operations>) for the available `operation` values. `regex` is a **full match**: the pattern must cover the whole value, so wrap partial patterns in `.*`. `domain` rules match the hostname without the scheme, port or path for page and REST request targets. GraphQL operation checks in WebApp API testing compare the domain against `host:port` when a port is present. Subdomains aren't expanded implicitly: `equals: example.com` doesn't match `www.example.com`, `ends_with: .example.com` does. A rule that sets a `method` matches only that method; a rule that omits it matches every method. On an `ip` rule, `contains` with a CIDR value tests network membership instead of string matching.

**Rule Evaluation**: Each candidate is evaluated on its own, whether it's a page URL, page element, API request, domain, IP or GraphQL operation.

1. **Blocklist first**: if the candidate matches any blocklist rule, it's out of scope, and no allowlist rule can bring it back
2. **Applicable rules only**: only the rule groups listed below for that kind of candidate are considered; if the allowlist holds none of them, the candidate is in scope
3. **One match per group**: within a group a single match is enough, and every group holding at least one rule must produce a match. Empty groups are ignored.

| Candidate | Allowlist rule groups checked |
| --- | --- |
| Page URL | `domain` (against the host) **and** `web_page_url` |
| API request URL | `domain` (against the host) **and** `rest_api_url` / `rest_api_path` (against the path) |
| API path with no host | `rest_api_path` |
| Domain | `domain` |
| IP address | `ip` |
| GraphQL operation | `graphql_operation` |

Page URLs and API request URLs are therefore checked against two groups. Both must match, which means `web_page_url` and `rest_api_*` rules narrow the scope inside the allowed domains: they never widen it:

```yaml
frontend_dast:
  scope:
    crawling:
      allowlist:
        - type: web_page_url
          value: "https://app\\.example\\.com/dashboard/.*"
          operation: regex
        - type: domain
          value: app.example.com
          operation: equals
```

The base URL `https://app.example.com/` matches the `domain` group but not the `web_page_url` group, so it's out of scope. To include it, widen the URL rule to cover it (`"https://app\\.example\\.com/(dashboard/.*)?"`) or remove the `web_page_url` rules and use domain-based scope.

For `regex` operations, the pattern must match the target's complete value. Wrap a partial pattern in `.*` to match a substring. The value depends on the rule type:

- `web_page_url` and `rest_api_url` rules match the complete URL, including the scheme and host.
- `rest_api_path` rules match only the URL path.
- `graphql_operation` rules match the complete operation name.

For example, `/faq/.*` doesn't match the `web_page_url` target `https://app.example.com/faq/1`. Use `https://app\.example\.com/faq/.*` to match that origin, or `.*/faq/.*` to match the path on every origin already allowed by the scan's domain scope.

## Complete Configuration Example

The following configuration demonstrates all available scope options and can be used as a template:

```yaml
# High-level exploration scope - defines the domain boundaries
# This can be defined as a global configuration for the org.
scope:
  allowlist:
    - type: domain
      operation: ends_with
      value: app.example.com
    - type: domain
      operation: equals
      value: dashboard.example.com

frontend_dast:
  scope:
    # Page crawling and interaction configuration
    crawling:
      # Control whether to extend from global scope (default: false for safety)
      extend_global_scope: false

      # Allowlist rules - targets that are allowed to be crawled
      allowlist:
        - type: web_page_url
          value: "https://app.example.com/dashboard/.*"
          operation: regex
        - type: web_page_url
          value: "https://app.example.com/settings/.*"
          operation: regex
        - type: domain
          value: "subdomain.example.com"
        - type: domain
          value: "cdn.example.com"

      # Blocklist rules - targets that should NOT be crawled
      blocklist:
        - type: web_page_url
          value: ".*/faq/.*"
          operation: regex
        - type: web_page_url
          value: ".*/articles/.*"
          operation: regex
        - type: web_page_url
          value: ".*/help/.*"
          operation: regex
        - type: web_page_url
          value: ".*/documentation/.*"
          operation: regex
        - type: web_page_element_selector
          value: "button.logout"
        - type: web_page_element_selector
          value: "a[href='/logout']"
        - type: web_page_element_selector
          value: ".chat-widget"
        - type: web_page_element_selector
          value: "button[data-action='delete-account']"

    # API traffic analysis and testing configuration
    api_testing:
      # Control whether to extend from global scope (default: true)
      extend_global_scope: true

      # Allowlist rules - API targets that are allowed to be tested
      allowlist:
        - type: rest_api_url
          value: "https://api.example.com/v[0-9]+/.*"
          operation: regex
        - type: rest_api_url
          value: "https://backend.example.com/graphql"
        - type: domain
          value: "api.example.com"
        - type: domain
          value: "backend.example.com"

      # Blocklist rules - API targets that should NOT be tested
      blocklist:
        - type: rest_api_path
          value: "/api/auth/.*"
          operation: regex
          method: null  # applies to all methods
        - type: rest_api_path
          value: "/api/logout"
          method: POST
        - type: rest_api_path
          value: "/api/user/delete"
          method: DELETE

  # Crawling tuning configuration - optimization settings for crawling
  crawling_tuning:
    max_unique_values_per_query_param: 10
    max_unique_fragments_per_page: 10
    max_parameterized_url_variations: 10
    only_inscope_crawling_logs: true
```

## Scope Configuration Structure

The scope configuration is organized into two main sections:

### `crawling` - Page Crawling Scope

Controls which pages are crawled and which elements are interacted with. Uses `WebAppCrawlingScopeRule` types:

- `domain` - Domain patterns for crawling
- `web_page_url` - URL patterns for pages
- `web_page_element_selector` - CSS selectors for elements

### `api_testing` - API Testing Scope

Controls which API traffic is analyzed and tested. Uses `APITestingScopeRule` types:

- `domain` - Domain patterns for API traffic
- `rest_api_path` - REST API path patterns (with optional method/domain)
- `rest_api_url` - REST API full URL patterns (with optional method)
- `graphql_operation` - GraphQL operation names

Each section supports:

- **`extend_global_scope`**: Whether to extend from global scope configuration
- **`allowlist`**: Rules defining what is allowed to be scanned/tested
- **`blocklist`**: Rules defining what should NOT be scanned/tested

`extend_global_scope` defaults to `true` for API testing and `false` for crawling. Set it explicitly to choose whether each scope inherits the global allowlist.

### Behavior

- **Domain Matching**: Only URLs matching these domains will be considered for crawling or API analysis
- **Subdomain Handling**: Subdomains must be listed explicitly or matched with an `ends_with` rule such as `.example.com`
- **Default Behavior**: If not specified, the domain is derived from the base URL of the application

### Use Cases

- **Multi-domain Applications**: Applications spanning multiple domains (for example, main app + admin panel)
- **Microservices Architecture**: Frontend and backend services on different domains
- **Third-party Integrations**: Including CDN or asset domains in the scan scope

## Crawling Scope Configuration

The `crawling` section controls which pages are crawled, how elements are interacted with, and how visit patterns are optimized. This uses a rule-based system with allowlists and blocklists to precisely define what's in scope for crawling.

By default, a scan on `app.example.com` crawls URLs on that exact domain. Use the `crawling` section to add domains or focus exploration on specific application sections.

### Domain Configuration

#### Domain Rules

Additional domains that should be allowed for page crawling are configured using `domain` type rules in the allowlist under `crawling`.

```yaml
frontend_dast:
  scope:
    crawling:
      allowlist:
        - type: domain
          value: "admin.example.com"
        - type: domain
          value: "cdn.example.com"
        - type: domain
          value: "static.example.com"
```

**Use Cases**:

- Applications using subdomains for different features
- Content delivery networks (CDNs) hosting application assets
- Multi-tenant applications with dynamic subdomains

**Note**: We will always add a rule to match the exact domain of the app the scan is running on to limit crawling on one domain, for example:

```yaml
- type: domain
  value: app.example.com
  operation: equals
```

### URL Pattern Filtering

#### Page URL Allowlist Rules

Regular expressions defining which page URLs are permitted to be visited. If specified, only URLs matching at least one `web_page_url` rule will be crawled.

```yaml
frontend_dast:
  scope:
    crawling:
      allowlist:
        - type: web_page_url
          value: "https://app\\.example\\.com/dashboard/.*"
          operation: regex
        - type: web_page_url
          value: "https://app\\.example\\.com/settings/.*"
          operation: regex
```

**Use Cases**:

- **Focused Scanning**: Limit scan to specific application sections (for example, admin panel only)
- **Differential Scanning**: Scan only newly deployed features in CI/CD pipelines
- **Compliance Requirements**: Restrict testing to specific modules for regulatory reasons

**Note**: If no allowlist rules are specified, all URLs that don't match any blocklist rule will be allowed.

#### Page URL Blocklist Rules

Regular expressions defining which page URLs should be excluded from crawling. Blocklist rules are applied after allowlist rules.

```yaml
frontend_dast:
  scope:
    crawling:
      blocklist:
        - type: web_page_url
          value: ".*\/faq\/.*"
          operation: regex
        - type: web_page_url
          value: ".*\/articles\/.*"
          operation: regex
        - type: web_page_url
          value: ".*\/help\/.*"
          operation: regex
        - type: web_page_url
          value: ".*\/documentation\/.*"
          operation: regex
```

**Use Cases**:

- **Optimization**: Skip static content pages that don't contain security-relevant functionality
- **Rate Limiting**: Avoid pages that trigger expensive operations or external API calls
- **Stability**: Exclude pages known to cause issues during automated testing

### Element Interaction Filtering

#### Element Selector Allowlist Rules

CSS selectors defining zones within pages where the scanner is permitted to interact with elements. When specified, only elements matching these `web_page_element_selector` rules will be clicked, filled, or otherwise interacted with.

```yaml
frontend_dast:
  scope:
    crawling:
      allowlist:
        - type: web_page_element_selector
          value: "div#main-app"
        - type: web_page_element_selector
          value: "nav.sidebar"
        - type: web_page_element_selector
          value: "section.dashboard"
```

**Use Cases**:

- **Focused Testing**: Restrict interactions to specific UI components
- **Complex Layouts**: Avoid navigation bars, footers, or promotional content
- **Performance**: Reduce scan time by targeting specific application areas

**Example**: Using `div#main-content` will interact only with elements inside `<div id="main-content">...</div>`.

#### Element Selector Blocklist Rules

CSS selectors defining elements that should be excluded from scanner interactions. Blocklist rules are applied after allowlist rules. These selectors are Playwright-based and support Playwright's extended selector syntax, including text-based selectors like `:has-text()`.

```yaml
frontend_dast:
  scope:
    crawling:
      blocklist:
        - type: web_page_element_selector
          value: "button.logout"
        - type: web_page_element_selector
          value: "a[href='/logout']"
        - type: web_page_element_selector
          value: ".chat-widget"
        - type: web_page_element_selector
          value: "button[data-action='delete-account']"
        - type: web_page_element_selector
          value: "iframe[src*='support']"
        - type: web_page_element_selector
          value: "button:has-text('Logout')"
```

**Use Cases**:

- **Prevent Disruption**: Avoid logout buttons, session termination, or destructive actions
- **Third-party Widgets**: Exclude chat widgets, analytics iframes, or external integrations
- **Stability**: Skip elements that trigger modals or redirects outside the application

## Crawling Tuning Configuration

The `crawling_tuning` section controls optimization settings for the crawling process, such as visit limits and logging visibility. These settings are configured at the top level of the frontend DAST configuration, not within the scope section.

### Visit Limits and Optimization

#### `max_unique_values_per_query_param`

The maximum number of different values to test for each query parameter on the same page path. Already tested values can be revisited without counting against the limit.

```yaml
frontend_dast:
  crawling_tuning:
    max_unique_values_per_query_param: 10
```

**Example**:

For the `/search` page with parameter `q`, if set to 5:

- `/search?q=test1` ✓ (allowed)
- `/search?q=test2` ✓ (allowed)
- `/search?q=test3` ✓ (allowed)
- `/search?q=test4` ✓ (allowed)
- `/search?q=test5` ✓ (allowed)
- `/search?q=test6` ✗ (blocked)

**Note**: The limit applies independently to each query parameter (`q`, `filter`, `page` are tracked separately).

#### `max_unique_fragments_per_page`

The maximum number of different fragments (anchors) to visit for the same page path. Single Page Applications with route fragments containing `/` aren't limited by this setting.

```yaml
frontend_dast:
  crawling_tuning:
    max_unique_fragments_per_page: 10
```

**Example**:

For `/page.html`, if set to 5:

- `/page.html#section1` ✓ (allowed)
- `/page.html#section2` ✓ (allowed)
- `/page.html#section3` ✓ (allowed)
- `/page.html#section4` ✓ (allowed)
- `/page.html#section5` ✓ (allowed)
- `/page.html#section6` ✗ (blocked)

#### `max_parameterized_url_variations`

The maximum number of different identifier values to visit for a shared URL pattern. This applies to identifiers in URL paths and fragments, including numbers and UUIDs.

```yaml
frontend_dast:
  crawling_tuning:
    max_parameterized_url_variations: 10
```

**Example**:

URLs `/users/123/profile` and `/users/456/profile` both match pattern `/users/{param}/profile`:

- `/users/123/profile` ✓ (allowed, variation 1)
- `/users/456/profile` ✓ (allowed, variation 2)
- `/users/789/profile` ✓ (allowed, variation 3)
- ... up to 10 variations
- `/users/999/profile` ✗ (blocked if limit reached)

### Crawling Log Visibility

#### `only_inscope_crawling_logs`

Controls whether the crawling logs under the "Crawling" tab display only in-scope URLs or all discovered URLs.

```yaml
frontend_dast:
  crawling_tuning:
    only_inscope_crawling_logs: true
```

- **`true`** (default): Only in-scope URLs are reported
- **`false`**: All discovered URLs are reported, including out-of-scope URLs

**Use Cases**:

- **Debugging**: Set to `false` to see all URLs the scanner encountered
- **Scope Validation**: Verify that scope configuration is correctly filtering URLs

## API Testing Scope Configuration

The `api_testing` section controls which API traffic is captured, analyzed, and tested for security issues. The scanner observes API traffic without interfering with normal application functionality. This uses a rule-based system with allowlists and blocklists to precisely define what's in scope for API testing.

### API Domain Filtering

#### API Domain Rules

Domains that are permitted for API traffic analysis. If specified, only API requests to these domains will be analyzed and tested.

```yaml
frontend_dast:
  scope:
    api_testing:
      allowlist:
        - type: domain
          value: "api.example.com"
        - type: domain
          value: "backend.example.com"
```

**Use Cases**:

- **Backend Separation**: Focus testing on specific backend services
- **Third-party APIs**: Include or exclude external API integrations
- **Microservices**: Target specific microservices in a complex architecture

**Note**: If no allowlist rules are specified, all domains that don't match any blocklist rule will be allowed.

### API URL Filtering

#### API URL Allowlist Rules

Regular expressions defining which API URLs are permitted for analysis and testing.

```yaml
frontend_dast:
  scope:
    api_testing:
      allowlist:
        - type: rest_api_url
          value: "https://api\\.example\\.com/v[0-9]+/.*"
          operation: regex
        - type: rest_api_url
          value: "https://backend.example.com/graphql"
```

**Use Cases**:

- **API Versioning**: Test only specific API versions (for example, v2 endpoints only)
- **Endpoint Selection**: Focus on specific API paths or services
- **GraphQL/REST Separation**: Target specific API types

### Security Check Exclusions

#### API Blocklist Rules

Patterns defining API endpoints where security checks should be skipped. The API traffic is still captured and analyzed, but active security testing is disabled.

```yaml
frontend_dast:
  scope:
    api_testing:
      blocklist:
        - type: rest_api_path
          value: "/api/auth/.*"
          operation: regex
          method: null  # applies to all HTTP methods
        - type: rest_api_path
          value: "/api/logout"
          method: POST
        - type: rest_api_path
          value: "/api/user/delete"
          method: DELETE
```

**Parameters**:

- **`type`**: Rule type (for example, `rest_api_path`, `rest_api_url`)
- **`value`**: The pattern value to match
- **`operation`**: Matching operation (for example, `regex`, `equals`)
- **`method`**: HTTP method to block (optional, `null` applies to all methods)

**Use Cases**:

- **Authentication Endpoints**: Observe login/logout traffic without running active tests
- **Destructive Operations**: Skip security checks on delete/purge endpoints
- **Rate-limited Endpoints**: Avoid triggering rate limits on sensitive APIs
- **Payment Processing**: Exclude financial transaction endpoints from active testing

**Important**: This is more granular than completely excluding URLs from scope: the traffic is still captured for analysis, but security checks aren't executed.

> [!WARNING]
>
> **API Blocklists and Browser Requests**
>
> The `api_testing` blocklist controls scanner actions on matching endpoints:
>
> - The crawling agent will **not** directly navigate to a blocklisted URL.
> - The crawling agent will **not** directly fire a request against a blocklisted endpoint.
> - Captured traffic matching the blocklist is **not** handed to the security-check engine for active fuzzing.
>
> If a page in scope makes an XHR/`fetch` call to a blocklisted endpoint as part of its normal behavior, the **browser still executes that request**. The request reaches your server and appears in the API Coverage logs as captured. No active API security check runs against it.
>
> In practical terms:
>
> - A blocklisted authentication endpoint may still receive real login/refresh calls triggered by the page itself: but the scanner won't inject payloads into them.
> - A blocklisted destructive endpoint (`/delete`, `/purge`, …) is safe from scanner-initiated mutation, but a page that auto-fires such a call on mount will still fire it.
>
> To prevent requests originated by the application, use the `crawling` blocklist to exclude the pages that issue those calls. To block all requests to an endpoint during the scan, enforce that restriction at your WAF/gateway. A network-level proxy that enforces the `api_testing` blocklist on outbound browser traffic is on the roadmap.

## Common Configuration Patterns

### Pattern 1: Production-Safe Scanning

Restrict scanning to safe pages and skip destructive API endpoints.

```yaml
mode: read_only

frontend_dast:
  scope:
    crawling:
      blocklist:
        - type: web_page_url
          value: ".*/admin/delete/.*"
          operation: regex
        - type: web_page_url
          value: ".*/admin/purge/.*"
          operation: regex
        - type: web_page_element_selector
          value: "button[data-action='delete']"
        - type: web_page_element_selector
          value: "button[data-action='purge']"
        - type: web_page_element_selector
          value: "a[href*='/delete']"

    api_testing:
      allowlist:
        - type: domain
          operation: equals
          value: app.example.com # Only allow testing APIs on this exact domain

      blocklist:
        - type: rest_api_path
          value: "/api/.*/(delete|purge|destroy)"
          operation: regex
          method: null
```

### Pattern 2: Focused Feature Testing

Test only a specific feature or module in the application.

```yaml
frontend_dast:
  scope:
    crawling:
      allowlist:
        - type: web_page_url
          value: "https://app\\.example\\.com/dashboard/analytics/.*"
          operation: regex
        - type: web_page_element_selector
          value: "div#analytics-module"

    api_testing:
      allowlist:
        - type: rest_api_url
          value: "https://api\\.example\\.com/v2/analytics/.*"
          operation: regex
```

### Pattern 3: Multi-Domain Application

Scan an application spanning multiple domains with targeted API testing.

```yaml
frontend_dast:
  scope:
    crawling:
      allowlist:
        # We allow crawling of 2 domains
        - type: domain
          value: admin.example.com
          operation: equals
        - type: domain
          value: app.example.com
          operation: equals

      blocklist:
        - type: web_page_element_selector
          value: ".third-party-widget"

    api_testing:
      allowlist:
        # We allow API testing for these 2 domains
        - type: domain
          value: "api.example.com"
        - type: domain
          value: "backend.example.com"
      blocklist:
        - type: rest_api_path
          value: "/api/auth/.*"
          operation: regex
          method: null
```

### Pattern 4: CI/CD Differential Scanning

Scan only newly deployed pages in a CI/CD pipeline.

```yaml
frontend_dast:
  scope:
    crawling:
      allowlist:
        - type: web_page_url
          value: "https://staging\\.example\\.com/new-feature/.*"
          operation: regex
```

## Best Practices

1. **Start Broad, Then Narrow**: Begin with default scope, analyze crawling logs, then refine with allowlists and blocklists
2. **Use Blocklists for Optimization**: Exclude static content, documentation, and help pages to reduce scan duration
3. **Protect Authentication Flows**: Always blocklist logout buttons in `scope.crawling.blocklist` and authentication endpoints in `scope.api_testing.blocklist`
4. **Balance Coverage and Duration**: Adjust visit limits (`max_unique_values_per_query_param`) based on application size and scan time requirements
5. **Test Scope Configuration**: Use `only_inscope_crawling_logs: false` during initial setup to validate scope rules
6. **Document Scope Decisions**: Maintain comments in configuration explaining why specific patterns are included or excluded
