# Seeders

Seeders inject fresh starting points into the scan. They run before detection. The two surfaces use different seeders because they speak to the target differently.

## API Seeders

Available shapes for `seed:` entries on `type: API` rules:

### Curl Seeder

`protocol: curl`

Inject a request specified as a `curl` command. Useful when you already have a working curl one-liner from browser DevTools (Network tab → Right-click → Copy as cURL) or Postman, and you want to drop it in as-is.

```yaml
seed:
  - protocol: curl
    user: admin
    curl: >
      curl -X POST 'https://example.com/api/users'
        -H 'Content-Type: application/json'
        -d '{"name": "tester"}'
```

For long-lived rules prefer the `rest` seeder (clearer diff, auto-filled host).

#### Properties

- `user`: The user to use for the request. If not provided, the request is sent as the scan main user.
- `curl`: The curl command to use for the request.

---

### REST Seeder

`protocol: rest`

Inject a REST request at scan start. Host and scheme are automatically filled from the scan target so you only specify the path, method, and any headers / body / query parameters.

```yaml
seed:
  - protocol: rest
    path: /api/users
    method: GET
    user: admin
    headers:
      Content-Type: application/json
    body: '{"name": "tester"}'
    params:
      limit: '100'
```

In context: Spring Boot actuator probing:

#### Spring Boot Actuator /Env Endpoint Exposed

Probe the Spring Boot actuator `/env` and `/beans` endpoints (with and without the `/actuator` prefix) and alert if the body leaks `JAVA_HOME`: proof the actuator is reachable.

Spring Boot's actuator exposes deep introspection endpoints (`/env`, `/beans`, `/heapdump`, `/configprops`, `/mappings`) that in default 1.x configurations were public. They leak environment variables, database connection strings, JWT secrets, S3 credentials, anything passed via `-D` flags or env vars.

The rule probes the four most common path layouts and asserts a successful response containing `JAVA_HOME`, which is virtually certain to be present in `/env` output.

**When to use:** Any backend potentially using Spring Boot. False-positive rate is very low because the `JAVA_HOME` marker rarely appears in legitimate API responses.

**OWASP:** API8:2023 Security Misconfiguration · **CWE:** [CWE-200](<https://cwe.mitre.org/data/definitions/200.html>)

**Severity rationale:** HIGH: actuator output usually contains credentials, signing keys, and infrastructure URLs that lead to direct compromise.

**Features used:** `seed`, `detect`, `helpers.response.is_successful`, `response.body.text contains`

```yaml
rule:
  id: example-api-spring-boot-actuator-env
  type: API
  alert:
    name: Spring Boot actuator /env exposed
    context: |
      A request to the Spring Boot actuator `/env` endpoint returned
      a successful response containing `JAVA_HOME`, indicating the
      actuator is publicly reachable and leaking environment data.
    severity: HIGH
    category: INFORMATION_DISCLOSURE
  seed:
  - protocol: rest
    method: GET
    path: /actuator/env
    headers: {}
  - protocol: rest
    method: GET
    path: /actuator/beans
    headers: {}
  - protocol: rest
    method: GET
    path: /env
    headers: {}
  - protocol: rest
    method: GET
    path: /beans
    headers: {}
  detect:
  - if: helpers.response.is_successful
    is: true
  - if: response.body.text
    contains: JAVA_HOME
```

**References:**

- [https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/](<https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/>)
- [https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html](<https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html>)
- [https://cwe.mitre.org/data/definitions/200.html](<https://cwe.mitre.org/data/definitions/200.html>)

In context: exposed SQL dump probing with `Range` header:

#### Exposed SQL Dumps Reachable Over HTTP

Probe a curated list of common SQL dump filenames at the root of the target and alert if the server returns the file with a SQL DDL/DML signature in the body.

Backups left in the web root are one of the highest-impact and cheapest-to-find exposures in API testing. This rule probes a curated list of common backup filenames (`backup.sql`, `dump.sql`, `db.sql`, `mysqldump.sql`, ...) using a `Range: bytes=0-3000` header so the download stays small even if the file is multi-gigabyte. Extend the `seed:` list with additional paths to suit your target.

Detection requires both a 200/206 status and a body containing typical SQL DDL/DML keywords (`DROP TABLE`, `CREATE TABLE`, `INSERT INTO`, `LOCK TABLE`). The status code and body regex are AND-combined so a generic 200 page with no SQL content doesn't fire.

**When to use:** Run against every public host. Especially useful immediately after deployments, when build artifacts are sometimes left in `/public` by accident.

**OWASP:** A05:2021 Security Misconfiguration · **CWE:** [CWE-540](<https://cwe.mitre.org/data/definitions/540.html>)

**Severity rationale:** HIGH: exposed dumps regularly contain credentials, password hashes, and full PII.

**Features used:** `seed`, `detect`, `rest seeder with custom Range header`, `response.body.text regex`, `response.status_code in`

```yaml
rule:
  id: example-api-exposed-sql-dumps
  type: API
  alert:
    name: Exposed SQL dump
    context: |
      A request to a common SQL dump filename returned a 200/206 with
      DDL/DML keywords in the body, indicating a database backup
      reachable over HTTP.
    severity: HIGH
    category: INFORMATION_DISCLOSURE
  seed:
  - protocol: rest
    method: GET
    path: /backup.sql
    headers:
      Range: bytes=0-3000
  - protocol: rest
    method: GET
    path: /database.sql
    headers:
      Range: bytes=0-3000
  - protocol: rest
    method: GET
    path: /dump.sql
    headers:
      Range: bytes=0-3000
  - protocol: rest
    method: GET
    path: /db.sql
    headers:
      Range: bytes=0-3000
  - protocol: rest
    method: GET
    path: /mysqldump.sql
    headers:
      Range: bytes=0-3000
  - protocol: rest
    method: GET
    path: /db_backup.sql
    headers:
      Range: bytes=0-3000
  - protocol: rest
    method: GET
    path: /wp-content/uploads/dump.sql
    headers:
      Range: bytes=0-3000
  detect:
  - if: response.body.text
    regex: .*((DROP|CREATE|(?:UN)?LOCK) TABLE|INSERT INTO).*
  - if: response.status_code
    in:
    - 200
    - 206
```

**References:**

- [https://owasp.org/Top10/A05\_2021-Security\_Misconfiguration/](<https://owasp.org/Top10/A05_2021-Security_Misconfiguration/>)
- [https://cwe.mitre.org/data/definitions/540.html](<https://cwe.mitre.org/data/definitions/540.html>)

#### Properties

- `user`: The user to use for the request. If not provided, the request is sent as the scan main user.
- `path`: The path to use for the request.
- `method`: The method to use for the request.
- `headers`: The headers to use for the request. The key is the header name and the value is the header value.
- `body`: The body to use for the request.
- `params`: parameters to use for the request. The key is the parameter name and the value is the parameter value.

---

### RAW HTTP Seeder

`protocol: http`

Inject a request at scan start, formatted as a raw HTTP message with a `@Host` directive. Use when you need full control over the wire format, want to call a host outside the scan target, or need non-standard headers / methods.

```yaml
seed:
  - protocol: http
    raw: |
      @Host: https://example.com
      GET /debug HTTP/1.1
      Host: example.com
      Content-Type: application/json
```

In context: environment isolation check that hits an internal-only host:

#### Environment Isolation for Private Hosts

Use the raw HTTP seeder to call a private host (`internal.example.com`) and alert on a successful response when the production environment isn't isolated from internal services.

Multi-environment platforms expect production runtime to be network-isolated from internal/back-office services. When a production-runtime container can resolve and reach `internal.example.com`, a single SSRF or admin-route mistake becomes a path into the internal network.

The seeder uses the raw HTTP seeder (with the `@Host` directive) so the request actually leaves the application's normal `base_url` and goes to the internal hostname.

**When to use:** Run this from the same scanning environment you want to validate. Replace `internal.example.com` with the actual hostname you want to assert is unreachable.

**OWASP:** A05:2021 Security Misconfiguration · **CWE:** [CWE-918](<https://cwe.mitre.org/data/definitions/918.html>)

**Severity rationale:** HIGH when paired with any user-controlled URL fetch in the API; MEDIUM as a standalone configuration finding.

**Features used:** `seed`, `detect`, `http raw seeder with @Host directive`

```yaml
rule:
  id: example-api-environment-isolation-internal-host
  type: API
  alert:
    name: Production environment can reach internal host
    context: |
      A request issued to `internal.example.com` from the production
      scanner returned a successful response. The runtime
      environment is not isolated from internal services.
    severity: HIGH
    category: REQUEST_FORGERY
  seed:
  - protocol: http
    raw: |
      @Host: https://internal.example.com
      GET /api/health HTTP/1.1
      Host: internal.example.com
  detect:
  - if: schema.url
    contains: internal.example.com
  - if: helpers.response.is_successful
    is: true
```

**References:**

- [https://owasp.org/www-community/attacks/Server\_Side\_Request\_Forgery](<https://owasp.org/www-community/attacks/Server_Side_Request_Forgery>)
- [https://cwe.mitre.org/data/definitions/918.html](<https://cwe.mitre.org/data/definitions/918.html>)

For standard REST endpoints prefer the `rest` seeder (auto-fills host and scheme from the scan target).

#### Properties

- `user`: The user to use for the request. If not provided, the request is sent as the scan main user.
- `raw`: The raw HTTP request in Nuclei format.

---

## WebApp Seeders

On `type: WEBAPP` rules, the `seed:` block is a list of browser actions executed by a real Playwright-controlled browser before detection runs.

### Browser Action Seeds

The Action Seeder runs a sequence of browser actions at the start of the scan.

Actions execute against a real Playwright-driven browser before the detection phase. Use them to authenticate, navigate to a target page, fill forms, or solve TOTP / magic-link challenges that the application requires before the rule's `detect` block can apply.

Actions bound to a custom rule are executed only when that rule is enabled. Disable the rule and the seed sequence is skipped.

#### Authenticated Session Bootstrap Via Form

A seed-only template that performs a full email + password sign-in via the browser form. Subsequent rules can stack on top with their own detect and additional seed actions.

The recurring scaffolding for any authenticated WebApp rule is the same: navigate to the login page, fill the form, submit, take a screenshot for evidence. This rule documents the canonical shape so other rules can reuse the same `seed:` block as a starting point.

The detector here intentionally only checks that the page rendered "Sign out": it's the cheapest signal that the session was established.

**When to use:** As a starting template when authoring a new authenticated WebApp rule. Copy the seed block, append your own seed actions and detector.

**OWASP:** N/A: building block, not a vulnerability rule · **CWE:** N/A

**Severity rationale:** INFO: this is a building block, not a vulnerability rule.

**Features used:** `seed`, `browser actions (goto, fill, click, wait_text)`, `page_text detector`

```yaml
rule:
  id: example-webapp-seeder-auth-form
  type: WEBAPP
  alert:
    name: Authenticated session bootstrap (template)
    context: |
      Template seeder demonstrating an email + password sign-in
      via the browser form. Stack additional seed actions and a
      real detector on top to author a complete rule.
    severity: INFO
    category: CUSTOM
  seed:
  - action: goto
    url: https://example.com/login
  - action: fill
    locator: input[name="email"]
    value: tester@example.com
  - action: fill
    locator: input[name="password"]
    value: tester-password
  - action: click
    locator: button[type="submit"]
  - action: wait_text
    value: Sign out
    timeout: 10
  detect:
  - if: page_text
    contains: Sign out
```

**References:**

- [WebApp Seeders](<https://docs.escape.tech/documentation/dast/custom-rules/seeders/#webapp-seeders>)

#### SSO Bootstrap Via Emailed Magic Link

Navigate to the login page, request a magic link, follow the link from the test inbox, and assert the dashboard rendered.

Magic-link auth is increasingly common (Notion, Slack invites, most B2B tools). The custom-rule action set supports this natively via `click_mail_magic_link`, which polls the configured test inbox and follows the most recent magic link.

**When to use:** Apps using passwordless / magic-link sign-in. Pair with a dedicated test mailbox configured in your scan authentication settings.

**OWASP:** A07:2021 Identification and Authentication Failures · **CWE:** [CWE-1390](<https://cwe.mitre.org/data/definitions/1390.html>)

**Severity rationale:** INFO: building block, not a vulnerability rule.

**Features used:** `seed`, `browser actions (goto, fill, click, click_mail_magic_link, wait_text)`

```yaml
rule:
  id: example-webapp-seeder-magic-link
  type: WEBAPP
  alert:
    name: SSO magic-link bootstrap (template)
    context: |
      Template seeder demonstrating passwordless sign-in via an
      emailed magic link.
    severity: INFO
    category: CUSTOM
  seed:
  - action: goto
    url: https://example.com/login
  - action: fill
    locator: input[name="email"]
    value: tester@example.com
  - action: click
    locator: button[type="submit"]
  - action: click_mail_magic_link
    email_address: tester.escape@scan.escape.tech
    timeout: 60
  - action: wait_text
    value: Sign out
    timeout: 10
  detect:
  - if: page_text
    contains: Sign out
```

**References:**

- [https://owasp.org/Top10/A07\_2021-Identification\_and\_Authentication\_Failures/](<https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/>)

#### Two-Factor (TOTP) Bootstrap During Sign-in

Sign in with email + password, then complete the TOTP challenge using `fill_totp` against the configured authenticator secret.

Apps that gate sign-in behind TOTP multi-factor frequently break headless scans. The action set ships `fill_totp` (HOTP-secret based) and `fill_mail_totp` (mailbox-delivered code) so the seeder can complete the challenge without human intervention.

**When to use:** Apps with TOTP MFA enabled for the test account. Configure the HOTP secret in your scan authentication settings; the seeder will compute the current 6-digit code automatically.

**OWASP:** A07:2021 Identification and Authentication Failures · **CWE:** [CWE-308](<https://cwe.mitre.org/data/definitions/308.html>)

**Severity rationale:** INFO: building block, not a vulnerability rule.

**Features used:** `seed`, `browser actions (goto, fill, click, fill_totp, wait_text)`

```yaml
rule:
  id: example-webapp-seeder-totp
  type: WEBAPP
  alert:
    name: TOTP MFA bootstrap (template)
    context: |
      Template seeder demonstrating sign-in through a TOTP MFA
      challenge using the `fill_totp` action.
    severity: INFO
    category: CUSTOM
  seed:
  - action: goto
    url: https://example.com/login
  - action: fill
    locator: input[name="email"]
    value: tester@example.com
  - action: fill
    locator: input[name="password"]
    value: tester-password
  - action: click
    locator: button[type="submit"]
  - action: fill_totp
    locator: input[name="otp"]
    secret: JBSWY3DPEHPK3PXP
  - action: click
    locator: button[type="submit"]
  - action: wait_text
    value: Sign out
    timeout: 10
  detect:
  - if: page_text
    contains: Sign out
```

**References:**

- [https://owasp.org/Top10/A07\_2021-Identification\_and\_Authentication\_Failures/](<https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/>)

### Available Browser Actions

#### `click`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[click]` | `click` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `locator`\* | `string` |  | The [Playwright Locator](<https://playwright.dev/docs/api/class-page#page-locator>) to select the element to click on |
| `select_first_if_multiple` | `boolean` | `false` | Whether to select the first element if multiple elements are found. If false, an error will be raised. |
| `timeout` | `integer` | `30` | Timeout in seconds for executing the click action |

#### `fill`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[fill]` | `fill` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `auto_submit` | `boolean` | `false` | Whether the form should be automatically submitted after the fill action |
| `locator`\* | `string` |  | The [Playwright Locator](<https://playwright.dev/docs/api/class-page#page-locator>) to select the field to fill |
| `one_by_one` | `boolean` | `false` | Whether the field should be typed one character by one character, like a TOTP code |
| `select_first_if_multiple` | `boolean` | `false` | Whether to select the first element if multiple elements are found. If false, an error will be raised. |
| `timeout` | `integer` | `30` | Timeout in seconds for executing the input filling action |
| `value`\* | `string` |  | The value to fill in the field |

#### `fill_totp`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[fill_totp]` | `fill_totp` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `auto_submit` | `boolean` | `false` | Whether the form should be automatically submitted after the TOTP code is filled |
| `locator`\* | `string` |  | The [Playwright Locator](<https://playwright.dev/docs/api/class-page#page-locator>) to select the field to fill the TOTP code in |
| `secret`\* | `string` |  | The secret to generate the TOTP code from |

#### `goto`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[goto]` | `goto` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `new_page` | `boolean` | `false` | Whether to create a new page for the navigation or remain on the current page |
| `timeout` | `integer` | `60` | The timeout (seconds) to wait for the page to load |
| `url`\* | `string` |  | The URL to navigate to |
| `verify_scope` | `boolean` | `true` | When true, verify that the URL is within the crawling scope. When false, allow navigation to out-of-scope URLs. |

#### `send_request`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[send_request]` | `send_request` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `body` | `string` |  | The body to use for the request. |
| `headers` | `Dict[string, string]` |  | The headers to use for the request. |
| `max_redirects` | `integer` |  | The maximum number of redirections to follow. |
| `method` | `HTTPMethod` | `GET` | The HTTP method to use for the request. |
| `timeout` | `integer` | `60` | The timeout (seconds) to wait for the page to load |
| `url`\* | `string` |  | The URL to navigate to |

#### `check`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[check]` | `check` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `locator`\* | `string` |  | The [Playwright Locator](<https://playwright.dev/docs/api/class-page#page-locator>) to select the checkbox to check |
| `select_first_if_multiple` | `boolean` | `false` | Whether to select the first element if multiple elements are found. If false, an error will be raised. |
| `timeout` | `integer` | `30` | Timeout in seconds for executing the check action |

#### `select`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[select]` | `select` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `locator`\* | `string` |  | The [Playwright Locator](<https://playwright.dev/docs/api/class-page#page-locator>) to select the dropdown to select from |
| `select_first_if_multiple` | `boolean` | `false` | Whether to select the first element if multiple elements are found. If false, an error will be raised. |
| `timeout` | `integer` | `30` | Timeout in seconds for executing the click action |
| `value`\* | `string` |  | The value to select |

#### `wait_element`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[wait_element]` | `wait_element` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `locator`\* | `string` |  | The selector to wait for |
| `timeout` | `number` | `10` | The timeout (seconds) to wait for the element to be visible |

#### `wait_text`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[wait_text]` | `wait_text` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `timeout` | `number` | `10` | The timeout (seconds) to wait for the text to be visible |
| `value`\* | `string` |  | The text to wait for until visible, case-insensitive |

#### `focus_page`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[focus_page]` | `focus_page` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `url_pattern`\* | `string` |  | The pattern to find in the URL to focus on a page. Should be a regex allowing to match the page using Python `re.findall()`. |

#### `sleep`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[sleep]` | `sleep` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `seconds`\* | `number` |  | The time to sleep in seconds, between 0 and 40 seconds |

#### `fill_email_totp`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Literal[fill_mail_totp, fill_email_totp]` | `fill_email_totp` | Use `fill_email_totp`. `fill_mail_totp` is deprecated and can be removed in the future. |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `auto_submit` | `boolean` | `false` | Whether the form should be automatically submitted after the fill action |
| `email_address`\* | `string` |  | The email address where the TOTP code will be sent.\\nMust be a valid scan inbox address matching {alias}.{mailboxId}[@scan](<https://github.com/scan> "GitHub User: scan").escape.tech. The full organization UUID suffix is still accepted for existing configurations. Alias: 1-27 ASCII letters, digits, and hyphens; dots allowed only between characters. |
| `locator`\* | `string` |  | The [Playwright Locator](<https://playwright.dev/docs/api/class-page#page-locator>) to select the field to fill |
| `one_by_one` | `boolean` | `false` | Whether the field should be typed one character by one character, like a TOTP code |
| `select_first_if_multiple` | `boolean` | `false` | Whether to select the first element if multiple elements are found. If false, an error will be raised. |
| `timeout` | `integer` | `30` | Timeout in seconds for executing the input filling action |

#### `click_mail_magic_link`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[click_mail_magic_link]` | `click_mail_magic_link` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `email_address`\* | `string` |  | The email address where the magic link will be sent.\\nMust be a valid scan inbox address matching {alias}.{mailboxId}[@scan](<https://github.com/scan> "GitHub User: scan").escape.tech. The full organization UUID suffix is still accepted for existing configurations. Alias: 1-27 ASCII letters, digits, and hyphens; dots allowed only between characters. |
| `new_page` | `boolean` | `false` | Whether to create a new page for the navigation or remain on the current page |
| `timeout` | `integer` | `60` | The timeout (seconds) to wait for the page to load |

#### `solve_captcha`

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | `Const[solve_captcha]` | `solve_captcha` |  |
| `allow_failure` | `boolean` | `false` | Allow this action to fail without breaking authentication, defaults to False. |
| `auto_submit` | `boolean` | `false` | Whether the form should be automatically submitted after the captcha is filled |
| `locator`\* | `string` |  | The [Playwright Locator](<https://playwright.dev/docs/api/class-page#page-locator>) to select the captcha field to fill |
