> For the complete documentation index, see [llms.txt](https://docs.xygeni.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.xygeni.io/xygeni-products/dast-security/dast-scanner/dast-scanner-configuration.md).

# DAST Scanner Configuration

### DAST Scanner Configuration

The [**DAST Scanner**](/xygeni-products/dast-security/dast-scanner.md) is configured through **YAML scan profiles** that control how the scanner behaves for different types of applications.

Profiles have **two independent axes**: a **tech base** (`--profile`) that describes *what the target is*, and a **scan intensity** (`--intensity`) that describes *how hard to scan*. They compose:

```bash
# tech base × intensity
xy-dast scan -u https://example.com --profile spa --intensity deep -o report.json
```

`--profile` selects the tech base (`traditional`, `spa`, `openapi`, `graphql`, `soap`, `cms`, or a custom profile); `--intensity` selects `quick`, `balanced` (default), `deep`, or `passive`. The intensity overlays attack strength, thresholds, phase durations, and CVE / deep-crawl enablement on top of the tech base, leaving its crawl and policy intact.

### Profile Locations

Custom profiles can be placed in any of these directories:

| Directory                    | Description                                |
| ---------------------------- | ------------------------------------------ |
| `$XYGENI_DAST_DIR/profiles/` | Installation-level profiles                |
| `./profiles/`                | Project-level profiles (current directory) |
| `./conf/profiles/`           | Alternative project-level location         |

List all available profiles (built-in and custom):

```bash
xy-dast scan --list-profiles
```

### Tech Base × Intensity

Built-in profiles split along two axes you combine with `--profile` and `--intensity`:

| Axis          | Option        | Values                                                    | Controls                                                            |
| ------------- | ------------- | --------------------------------------------------------- | ------------------------------------------------------------------- |
| **Tech base** | `--profile`   | `traditional`, `spa`, `openapi`, `graphql`, `soap`, `cms` | Scope, crawl, endpoint import, scan policy                          |
| **Intensity** | `--intensity` | `quick`, `balanced` (default), `deep`, `passive`          | Attack strength, thresholds, durations, CVE / deep-crawl enablement |

Any tech base combines with any intensity:

```bash
xy-dast scan -u https://api.example.com --profile openapi --intensity deep    # deep API scan (CVE + deep crawl on)
xy-dast scan -u https://app.example.com --profile spa      --intensity quick   # fast SPA smoke test
xy-dast scan -u https://prod.example.com --profile traditional --intensity passive  # production-safe: no active payloads
```

* `passive` skips the active scan entirely (crawl + passive analysis only) — safe for production/staging.
* `deep` turns on CVE checking and the deep crawler and raises strength/durations. CVE and deep crawl can also be toggled independently on any intensity with `--vuln-check` and `--deep-crawl`.
* When `--intensity` is omitted, `balanced` (the tech base's own settings) is used. `--auto-profile` picks the tech base; `--intensity` still applies on top.

### Profile Schema

A profile is a YAML file with the following sections:

```yaml
# Identity
name: my-profile
description: "Description of the profile"
extends: openapi  # Optional: inherit from a base profile

# Scope - URL patterns to include/exclude
scope:
  includePatterns:
    - "https://api.example.com/v2/.*"
  excludePatterns:
    - ".*logout.*"
    - ".*\\.js$"
    - ".*\\.css$"

# Authentication (see "Authentication Configuration" below for every method)
authentication:
  method: BEARER            # NONE, FORM, BEARER, HEADER, BASIC, JSON, OAUTH2, SCRIPT
  loginUrl: ""              # Login form URL (for FORM method)
  usernameField: "username" # Form field name for username
  passwordField: "password" # Form field name for password
  headerName: "Authorization"  # Header name (for BEARER/HEADER)
  headerValue: "${env:API_TOKEN}"  # Header value
  headerPrefix: "Bearer "   # Header value prefix

# Users (for form-based authentication)
users:
  - name: "test-user"
    username: "admin"
    password: "admin123"
    default: true

# Session management
session:
  method: COOKIE  # COOKIE, HEADER, or SCRIPT
  # import:       # Pre-authenticated session import (SSO) — see below
  #   cookies: [...]
  #   headers: [...]

# Technology context (helps optimize scanning)
technology:
  language: javascript
  database: mysql
  framework: express
  include:
    - "Db / MySQL"
    - "Language / JavaScript"

# Spider configuration
# Durations: a positive value caps the phase; 0 means UNLIMITED (no timeout), never "skip".
# To disable a phase, set its `skip: true` (or use the matching --skip-* CLI flag).
spider:
  duration: 10   # Maximum duration in minutes (0 = unlimited)
  depth: 5       # Maximum crawl depth
  children: 10   # Maximum children per node
  skip: false    # Set to true to disable the spider

# AJAX Spider configuration (for SPAs)
ajaxSpider:
  duration: 15   # Maximum duration in minutes (0 = unlimited)
  depth: 5       # Maximum crawl depth
  browsers: 4    # Number of browser instances
  skip: false    # Set to true to disable AJAX spider

# Active scan configuration
activeScan:
  duration: 20       # Maximum duration in minutes (0 = unlimited)
  ruleDuration: 5    # Maximum duration per rule in minutes (0 = unlimited)
  policy: ""         # Scan policy (e.g., "API-Scan")
  strength: "MEDIUM" # Attack strength: LOW, MEDIUM, HIGH, INSANE
  threshold: "MEDIUM" # Alert threshold: LOW, MEDIUM, HIGH
  skip: false        # Set to true to disable active scan (= --passive-only / --skip-active-scan)
  rules: []          # Per-rule overrides (see below)

# Passive scan configuration
passiveScan:
  waitDuration: 5  # Maximum wait time in minutes

# Passive WebSocket scanning — optional; see "WebSocket Scanning" below.
# Equivalent to --websocket / --websocket-scripts on the CLI. Captured only
# during AJAX spidering, so most useful for SPA targets (on by default for `spa`).
webSocket:
  enabled: true
  # passiveScripts:        # optional; omit to use the bundled disclosure scripts
  #   - pii-disclosure
  #   - email-disclosure

# Deep crawl (headless browser-based pre-scan crawling)
deepCrawl:
  enabled: false    # Enable deep crawl before the scan (the `deep` intensity / --deep-crawl sets this)
  depth: 3          # Maximum crawl depth
  duration: "5m"    # Crawl timeout (accepts: 30s, 5m, 1h)

# Vulnerability check (template-based CVE/misconfiguration detection)
vulnCheck:
  enabled: false    # Enable post-scan vulnerability check (the `deep` intensity / --vuln-check sets this)
  severities:       # Severity filter
    - critical
    - high
    - medium
  excludeTags:      # Template tags to exclude (default: dos, fuzz)
    - dos
    - fuzz
  rateLimit: 50     # Requests per second
  timeout: "15m"    # Scan timeout
  # templates:      # Advanced: override template directories. The defaults
  #   - ...         # cover CVEs, exposures, misconfigurations, and known
  #                 # vulnerabilities and rarely need to be customised.

# Client certificate (mTLS) — optional
# Equivalent to --client-cert / --client-cert-password on the CLI.
# Orthogonal to other authentication methods (combine as needed).
clientCertificate:
  path: /path/to/client.p12        # PKCS#12 (.p12 / .pfx) certificate
  password: "${env:CERT_PASSWORD}" # Read from env to keep secrets out of YAML

# Out-of-band detection (OAST) — optional; see "Out-of-Band Detection" below.
# Equivalent to --oast-service / --oast / --oast-token on the CLI. Off by default.
oast:
  service: interactsh              # interactsh | boast | callback | none
  server: https://oast.internal.example.com  # required for interactsh (no built-in default)
  token: "${env:OAST_TOKEN}"       # self-hosted server auth token (env-resolved, never logged)
  # pollSeconds: 60                # poll frequency for interactsh/boast
  # port: 0                        # advertised callback port (callback service only)

# Result filtering
filtering:
  excludeRules:       # Rule IDs to exclude from results
    - "10094"
  riskThreshold: ""   # Minimum risk: INFO, LOW, MEDIUM, HIGH
```

### Profile Inheritance

Use `extends` to inherit settings from a built-in or custom profile. Only the fields you specify are overridden; all other settings come from the parent.

```yaml
name: my-api
description: "Custom API profile with stricter scanning"
extends: openapi

activeScan:
  duration: 30
  strength: HIGH
```

This profile inherits all settings from `openapi` (minimal spidering, no AJAX spider, API-Scan policy) but overrides the active scan duration and strength.

For the intensity axis you rarely need `extends` at all: `--intensity {quick|balanced|deep|passive}` composes an intensity onto any tech base (built-in or custom) at scan time, so `--profile openapi --intensity deep` gives a deep API scan without a custom profile.

### Authentication Configuration

#### FORM - HTML Form Login

```yaml
authentication:
  method: FORM
  loginUrl: "https://app.example.com/login"
  usernameField: "username"
  passwordField: "password"

users:
  - name: "test-user"
    username: "admin"
    password: "${env:APP_PASSWORD}"
    default: true
```

#### JSON - Login Returning a Token

For a login endpoint that accepts JSON and answers with a token in the **response body** — the usual single-page-app shape. The scanner logs in, reads the token out of the reply, and sends it as the header you name on every subsequent request:

```yaml
authentication:
  method: JSON
  jsonAuthUrl: "https://app.example.com/rest/user/login"
  jsonBody: '{"email":"{username}","password":"{password}"}'
  tokenJsonPath: "authentication.token"   # dotted path into the login response
  headerName: "Authorization"
  headerPrefix: "Bearer "

users:
  - name: "test-user"
    username: "${env:APP_USER}"
    password: "${env:APP_PASSWORD}"
    default: true
```

| Field           | Meaning                                                                                           |
| --------------- | ------------------------------------------------------------------------------------------------- |
| `jsonAuthUrl`   | Login endpoint the credentials are posted to                                                      |
| `jsonBody`      | Request body; `{username}` and `{password}` are filled from the `users` entry                     |
| `tokenJsonPath` | Dotted path to the token in the login response, e.g. `authentication.token` or `data.accessToken` |
| `headerName`    | Header carrying the token on later requests (default `Authorization`)                             |
| `headerPrefix`  | Text placed before the token — usually `"Bearer "`, including the trailing space                  |

The token is re-read from the login response whenever the scanner re-authenticates, so a long scan does not quietly continue as an anonymous user once the first token expires.

{% hint style="info" %}
Omit `tokenJsonPath` when the login sets a session **cookie** instead of returning a token: cookie session management is then used and no header is injected.
{% endhint %}

{% hint style="warning" %}
`FORM` and `JSON` cannot log in without credentials to log in with. A profile declaring either without a `users:` block is refused before the scan starts, rather than running an unauthenticated scan that reports success.
{% endhint %}

#### BEARER - Bearer Token

```yaml
authentication:
  method: BEARER
  headerName: "Authorization"
  headerValue: "${env:API_TOKEN}"
  headerPrefix: "Bearer "
```

#### HEADER - Custom Header

```yaml
authentication:
  method: HEADER
  headerName: "X-API-Key"
  headerValue: "${env:API_KEY}"
  headerPrefix: ""
```

#### BASIC - HTTP Basic Authentication

```yaml
authentication:
  method: BASIC

users:
  - name: "test-user"
    username: "admin"
    password: "${env:BASIC_PASSWORD}"
    default: true
```

Credentials are Base64-encoded and sent as `Authorization: Basic <encoded>` on every request (RFC 7617).

#### OAUTH2 - OAuth2

The scanner obtains an access token from the OAuth2 token endpoint **before** the scan and sends it as `Authorization: Bearer <token>` on every request. Three non-interactive grants are supported.

```yaml
authentication:
  method: OAUTH2
  grantType: CLIENT_CREDENTIALS      # CLIENT_CREDENTIALS | PASSWORD | REFRESH_TOKEN
  tokenUrl: "https://idp.example.com/oauth/token"
  clientId: "${env:OAUTH_CLIENT_ID}"
  clientSecret: "${env:OAUTH_CLIENT_SECRET}"
  scope: "api.read"                  # optional
```

Additional optional fields cover provider differences and the other grants:

```yaml
authentication:
  method: OAUTH2
  grantType: PASSWORD
  tokenUrl: "https://idp.example.com/oauth/token"
  clientId: "${env:OAUTH_CLIENT_ID}"
  clientSecret: "${env:OAUTH_CLIENT_SECRET}"
  clientAuthMethod: "post"           # "post" (default) sends credentials in the body; "basic" uses an HTTP Basic header
  audience: "https://api.example.com" # optional (e.g. Auth0)
  username: "${env:OAUTH_USERNAME}"   # PASSWORD grant
  password: "${env:OAUTH_PASSWORD}"   # PASSWORD grant
  refreshToken: "${env:OAUTH_REFRESH}"   # REFRESH_TOKEN grant
  extraParams:                       # provider-specific token-request parameters
    resource: "urn:example:api"
  tokenLifecycle:                    # renew the token mid-scan on long runs (see below)
    expiryStatus: 401
```

| Grant                | Use case                                                                                                                                                                                  |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CLIENT_CREDENTIALS` | Machine-to-machine API access (no user).                                                                                                                                                  |
| `PASSWORD`           | Scan as a user, using resource-owner credentials.                                                                                                                                         |
| `REFRESH_TOKEN`      | Scan an API behind an interactive (browser) login: perform the login once out-of-band, capture the **refresh token**, store it as a secret, and let the scanner mint access tokens in CI. |

**Token renewal for long scans.** By default the token is fetched once. If it might expire before the scan finishes, add a `tokenLifecycle` block so the scanner renews it during the scan and keeps authenticated coverage:

```yaml
authentication:
  method: OAUTH2
  grantType: REFRESH_TOKEN
  tokenUrl: "https://idp.example.com/oauth/token"
  clientId: "${env:OAUTH_CLIENT_ID}"
  clientSecret: "${env:OAUTH_CLIENT_SECRET}"
  refreshToken: "${env:OAUTH_REFRESH}"
  tokenLifecycle:
    expiryStatus: 401                # response status that signals expiry (default: 401)
    expiryPattern: "token.*expired"  # optional response-body regex
```

On the CLI, `--token-refresh` enables this for the client-credentials flow (with `--token-expiry-status` to change the status code). Without it, the token is not renewed mid-scan — use token lifecycle, a longer-lived token, or a shorter scan.

If a client certificate (`clientCertificate`) is configured, it is reused for the token endpoint when the endpoint requires mTLS (RFC 8705).

{% hint style="info" %}
Environment variables can be referenced with `${env:VARNAME}` syntax anywhere in profile values. This is the recommended approach for sensitive values like tokens and passwords.
{% endhint %}

#### SCRIPT - Scripted Browser Login (multi-step / advanced)

For logins the declarative methods above cannot express — identifier-first flows, multi-page forms, JavaScript-gated logins, or apps that hand back the session token in a JS variable — provide a recorded **browser-automation script**. The scanner drives a real browser through the login, captures the resulting session (cookies, and tokens from response headers or browser storage), and injects it into the scan (active/passive scanning, the deep crawler, and the vulnerability check) — all authenticated.

The script format is **Selenium IDE (`.side`)** today; the configuration is engine-neutral so other automation frameworks (e.g. Playwright) can be added in future.

{% hint style="info" %}
If the login is a single request that answers with the token in a JSON body, use [`JSON`](#json-login-returning-a-token) instead — it needs no browser and no recording, and re-reads the token whenever it re-authenticates.
{% endhint %}

```yaml
authentication:
  method: SCRIPT
  script:
    engine: selenium                 # selenium today; more frameworks planned
    file: auth/login.side            # recorded login flow
    # seedNavigation: true           # also use the login replay's traffic to seed the scan
    #                                # (default: on for the `spa` tech base, off otherwise)
    vars:                            # substituted into the script's ${VAR} placeholders
      USERNAME: "${env:DAST_USER}"
      PASSWORD: "${env:DAST_PASS}"
    extract:                         # optional: pin which value to reuse and how
      - name: bearer
        as: bearer                   # bearer | header:<Name> | cookie
        from: sessionStorage         # sessionStorage | localStorage | cookie | responseHeader | responseBody | var
        key: currentUser
        jsonPath: $.token            # optional, to dig into a JSON value
```

How the session is captured, layered (lowest-config first): (1) extraction the script itself declares (`store` / `executeScript` into a variable); (2) explicit `extract:` rules above; (3) otherwise a best-effort scan of cookies and browser storage (a captured JWT becomes a bearer header).

{% hint style="info" %}
Record the `.side` with the Selenium IDE browser extension. The **actuation** steps are replayed — navigation, field entry, clicks, checkboxes, waits, frame and window switches (`selectFrame` / `selectWindow`, so logins inside an iframe or a popup work), special keys recorded as `${KEY_ENTER}`, `${KEY_TAB}`, … and control flow (`if` / `else if` / `else` / `end`, `while`, `times`, `do` / `repeat if`) whose conditions are evaluated as JavaScript in the page. `assert`/`verify` test steps are ignored — the scanner authenticates, it does not run UI tests. Keep credentials out of the committed `.side` by using `${VAR}` placeholders supplied via `vars` (with `${env:...}`).
{% endhint %}

By default the login script is used **only to authenticate**. Set `script.seedNavigation: true` to also feed the pages it visits into the scan as navigation seeds — useful when the login flow already walks through gated areas you want covered. It defaults **on for the `spa` tech base** (whose JS-driven flows benefit most from seeds) and **off** otherwise; an explicit value always wins. This is separate from the standalone `--navigation` input, which seeds the scan from a recording independent of login.

**When a recorded login does not complete**

A scripted login that fails stops the scan, rather than continuing unauthenticated and reporting a misleadingly clean result for pages it never reached. The error names the step that failed, where the browser actually was, and two artifacts written next to the report:

```
Selenium .side step 10 of 10, command 'waitForElementVisible' on 'css=.app-header__profile' failed.
  Browser was at: https://app.example.com/login  (page title: "Sign in")
  Screenshot: /path/to/xy-dast-side-failure-step10-waitForElementVisible.png
  Page snapshot: /path/to/xy-dast-side-failure-step10-waitForElementVisible.html
  Cause: Expected condition failed: waiting for visibility of element located by ...
```

The URL is usually the answer on its own — still on the login page means the flow never submitted, and the screenshot normally shows why.

| What you see                                                | Likely cause                                                                                                                                          |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| Still on the login page, nothing visibly wrong              | The submit click had no effect. A warning naming the element is logged when it never became clickable.                                                |
| Still on the login page, an error visible in the screenshot | Credentials rejected, or the app requires MFA / a captcha.                                                                                            |
| A `Skipping .side step N/M '<command>'` **warning**         | The recording needs a step the scanner does not implement, so what followed ran against the wrong state.                                              |
| The step after a `selectFrame` cannot find its element      | The frame locator did not match — check it against the recording.                                                                                     |
| Timeout on the *first* step                                 | The start URL is not reachable from the scanner.                                                                                                      |
| `unusable control flow` before any step runs                | The recording's `if`/`while`/`do` blocks do not balance, or it uses `forEach` (unsupported).                                                          |
| `condition … could not be evaluated in the page`            | A recorded `if`/`while`/`repeat if` condition threw. It is never assumed true or false, since either guess would run the wrong half of the recording. |

{% hint style="warning" %}
The failure artifacts are written **only when a step fails**, to the report's output directory. Password fields are replaced with `***redacted***` in the page snapshot, but **the screenshot is not redacted** — it shows whatever was typed into visible fields, including the username. Treat both as sensitive.
{% endhint %}

#### Pre-authenticated Session Import (SSO / federated)

For OIDC/SAML SSO targets (Okta, Microsoft Entra ID, …), authenticate **out-of-band** and hand the scanner the resulting session artifacts — no scripted login required. Orthogonal to the header-based methods (combinable with bearer / API-key / mTLS); mutually exclusive with form login.

```bash
# CLI
xy-dast scan -u https://app.example.com \
  --session-cookie "SESSION=${SESSION_ID}" --session-cookie "XSRF-TOKEN=${XSRF}" \
  --session-file session.json
```

```yaml
# Profile
session:
  import:
    cookies:
      - name: SESSION
        value: "${env:SESSION_ID}"
    headers:
      - name: Authorization
        value: "Bearer ${env:SSO_TOKEN}"
```

Imported cookies are sent as a single `Cookie` request header and the headers verbatim, on every scan request.

`--session-file` also accepts a **Playwright `storageState.json`** (the artifact from `context.storage_state()`): its cookies are imported and a JWT found in local storage becomes an `Authorization: Bearer` header, so a session captured by an existing Playwright login script needs no conversion.

### Authentication Methods Reference

| Method   | Description                                                              | Required Fields                                                  |
| -------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| `NONE`   | No authentication                                                        | --                                                               |
| `FORM`   | HTML form login with username/password                                   | `loginUrl`, `usernameField`, `passwordField`, `users`            |
| `JSON`   | JSON login; the token is read from the response body                     | `jsonAuthUrl`, `jsonBody`, `tokenJsonPath`, `users`              |
| `BEARER` | Bearer token in Authorization header                                     | `headerName`, `headerValue`, `headerPrefix`                      |
| `HEADER` | Custom header authentication                                             | `headerName`, `headerValue`                                      |
| `BASIC`  | HTTP Basic authentication (RFC 7617)                                     | `users` (username and password)                                  |
| `OAUTH2` | OAuth2 token acquired pre-scan, injected as a bearer token               | `tokenUrl`, `clientId`, `clientSecret` (+ grant-specific fields) |
| `SCRIPT` | Scripted browser login (Selenium `.side`) for multi-step / JS-gated auth | `script.engine`, `script.file`                                   |

Two further mechanisms are orthogonal to the `method` above and combine with it: **session import** (`session.import` / `--session-cookie` / `--session-file`) for pre-authenticated SSO sessions, and **client certificate** (`clientCertificate` / `--client-cert`) for mTLS.

### Out-of-Band Detection (OAST)

Some vulnerabilities only reveal themselves **out-of-band** — the target opens a connection to a server you control rather than showing anything in its HTTP response. The scanner injects a payload and, if the target is vulnerable, it calls back to an **OAST server** (out-of-band application security testing). This is **off by default** (no surprise external callbacks); enable it with `--oast-service` / the `oast:` profile block. A bare `--oast <url>` infers `interactsh`.

```yaml
oast:
  service: interactsh          # interactsh | boast | callback | none
  server: https://oast.internal.example.com
  token: "${env:OAST_TOKEN}"   # self-hosted only; env-resolved, never logged
  # pollSeconds: 60
  # port: 0                    # callback service only
```

CLI `--oast-*` flags take precedence over the profile block.

#### Choosing a service — the reachability constraint

An OAST callback only works if the **target** can reach the server *and* the scanner can observe the hit. Because the scanner runs in a container, pick the service to match how the target can reach back:

| Service      | How a hit is signalled                                                                         | Use when                                                            |
| ------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `callback`   | Target connects back to the scanner's own advertised address (`--oast`/`--oast-callback-port`) | Target shares a network with the scanner (same Docker/host network) |
| `boast`      | Target does OOB to a BOAST server; scanner polls it                                            | Public target with outbound egress (zero-config public server)      |
| `interactsh` | Target does OOB to an Interactsh server; scanner polls it                                      | Public egress, or a **self-hosted** server both reach               |

* **`callback`** is the simplest, most reliable option for internal targets — the scanner is its own OAST server, no external service. It detects the HTTP-based rules (blind SSRF, blind XXE).
* **Public** Interactsh/BOAST are best-effort — they depend on a third-party server being reachable. Prefer `callback` (internal) or a self-hosted server for dependable results.
* **`interactsh` requires an explicit `--oast` URL** — there is no built-in default (a blank server leaves detection inert). Use `boast` for a zero-config public server.

#### Per-rule detection

| Rule                                     | ID    |             Works with `callback`            |
| ---------------------------------------- | ----- | :------------------------------------------: |
| Server Side Request Forgery (blind SSRF) | 40046 |                       ✅                      |
| XML External Entity Attack (blind XXE)   | 90023 |                       ✅                      |
| Out-of-Band XSS                          | 40031 | ✅ (a browser must view the injected payload) |
| Server Side Template Injection (blind)   | 90036 |             detected via timing¹             |
| Log4Shell (CVE-2021-44228)               | 44228 |           ❌ needs an external OAST²          |
| Text4Shell (CVE-2022-42889)              | 40047 |           ❌ needs an external OAST²          |

¹ Blind SSTI is detected by its time-based technique (no OAST server required). ² Log4Shell/Text4Shell payloads are JNDI/interpolation targets (`ldap://…`) the `callback` service can't carry; they need an `interactsh`/`boast` server. Log4Shell out-of-band is also covered independently by the [vulnerability check](/xygeni-products/dast-security/dast-scanner.md#vulnerability-check).

#### Self-hosting an OAST server (internal / air-gapped targets)

For internal targets that cannot route back to the scanner and have no public egress, run your own OAST server that **both the target and the scanner can reach**. The recommended option is [**projectdiscovery/interactsh**](https://github.com/projectdiscovery/interactsh):

1. **Provision** an `interactsh-server` on a host reachable by the target and the scanner (`go install github.com/projectdiscovery/interactsh/cmd/interactsh-server@latest`, or the published container image).
2. **Give it a domain.** Interactsh correlates via a unique DNS subdomain per payload, so its domain (e.g. `oast.internal.example.com`) needs an `NS` record pointing at the server, and the server must be reachable on DNS (53) and HTTP/S (80/443). Protect the poll API with a token (`-token`). This is the server's *own* (local) DNS — **not** public internet DNS exfiltration.
3. **Point the scanner at it:**

   ```bash
   xy-dast scan -u https://app.internal \
     --oast-service=interactsh \
     --oast=https://oast.internal.example.com \
     --oast-token env:OAST_TOKEN
   ```

When `--oast-service=interactsh` is used with a server, the [vulnerability check](/xygeni-products/dast-security/dast-scanner.md#vulnerability-check) phase is pointed at the **same** server, so out-of-band detection is consistent across both engines.

{% hint style="info" %}
Run the self-hosted OAST server alongside the target, **not** inside the scanner container. Ensure the `interactsh-server` version is protocol-compatible with the scanner's OAST client.
{% endhint %}

### WebSocket Scanning

Applications that push data over **WebSocket** channels (`ws://` / `wss://`) can leak information the ordinary HTTP scan never sees. When enabled, the scanner runs **passive** checks over every WebSocket message exchanged during the scan — no messages are injected or replayed, so it is safe against production-like targets.

```yaml
webSocket:
  enabled: true
  # passiveScripts:        # optional; omit to use the bundled disclosure scripts
  #   - pii-disclosure
  #   - email-disclosure
```

Enable it with the `webSocket:` profile block or `--websocket`; `--websocket-scripts name1,name2` overrides the profile's `passiveScripts`. It is **on by default for the `spa` profile** and off elsewhere.

{% hint style="info" %}
WebSocket channels are captured only while the **AJAX spider** drives a real browser through the scanner — so this applies to SPA/AJAX scans, not the plain spider or an API-only (OpenAPI/Postman) import.
{% endhint %}

When no scripts are selected, a bundled set of disclosure checks runs: `pii-disclosure`, `email-disclosure`, `base64-disclosure`, `application-error`, `debug-error-disclosure`, and `xml-comments-disclosure`. A script named in the selection but not found is skipped with a warning — it never fails the scan.

### Per-Rule Policy Overrides

The `activeScan.rules` list allows you to override threshold and strength for individual scan rules, or disable rules entirely:

```yaml
activeScan:
  duration: 20
  strength: MEDIUM
  rules:
    # Enable specific rules with higher strength
    - id: 40018
      name: "SQL Injection"
      threshold: "Medium"
      strength: "High"
    - id: 40012
      name: "Cross Site Scripting (Reflected)"
      threshold: "Medium"
      strength: "High"
    # Disable a rule
    - id: 30001
      name: "Buffer Overflow"
      threshold: "Off"
```

{% hint style="info" %}
Setting `threshold: "Off"` disables a rule entirely. Valid threshold values are `Off`, `Low`, `Medium`, and `High`. Valid strength values are `Low`, `Medium`, `High`, and `Insane`.
{% endhint %}

### Example: OWASP Juice Shop Profile

This complete example shows a custom profile for scanning the OWASP Juice Shop application:

```yaml
name: example-juiceshop
description: "Profile for OWASP Juice Shop application"
extends: spa

scope:
  includePatterns:
    - "http://localhost:3000/.*"
  excludePatterns:
    - ".*\\.js$"
    - ".*\\.css$"
    - ".*\\.png$"
    - ".*socket\\.io.*"
    - ".*logout.*"

technology:
  language: javascript
  database: mysql
  framework: express
  include:
    - "Db / MySQL"
    - "Language / JavaScript"

ajaxSpider:
  duration: 10
  depth: 8
  browsers: 3
  skip: false

activeScan:
  duration: 20
  ruleDuration: 5
  strength: "HIGH"
  threshold: "MEDIUM"
  rules:
    - id: 40018
      name: "SQL Injection"
      threshold: "Medium"
      strength: "High"
    - id: 40012
      name: "Cross Site Scripting (Reflected)"
      threshold: "Medium"
      strength: "High"
    - id: 40014
      name: "Cross Site Scripting (Persistent)"
      threshold: "Medium"
      strength: "High"
    - id: 40026
      name: "Cross Site Scripting (DOM Based)"
      threshold: "Medium"
      strength: "High"
    - id: 6
      name: "Path Traversal"
      threshold: "Medium"
      strength: "High"
    - id: 90023
      name: "XML External Entity Attack"
      threshold: "Medium"
      strength: "High"
    # Disable low-value rules for this app
    - id: 30001
      name: "Buffer Overflow"
      threshold: "Off"
    - id: 40003
      name: "CRLF Injection"
      threshold: "Off"
```

Run with:

```bash
xy-dast scan -u http://localhost:3000 --profile example-juiceshop -o report.json
```

### Profile Schema Reference

| Section               | Fields                                                                                                                                                                                                                                                           | Description                                                                                                        |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `name`, `description` | string                                                                                                                                                                                                                                                           | Profile identity                                                                                                   |
| `extends`             | string                                                                                                                                                                                                                                                           | Parent profile to inherit from                                                                                     |
| `scope`               | `includePatterns`, `excludePatterns`                                                                                                                                                                                                                             | Regex lists for URL filtering                                                                                      |
| `authentication`      | `method`, `loginUrl`, `usernameField`, `passwordField`, `headerName`, `headerValue`, `headerPrefix`; OAuth2: `grantType`, `tokenUrl`, `clientId`, `clientSecret`, `scope`, `audience`, `username`, `password`, `refreshToken`, `clientAuthMethod`, `extraParams` | Authentication settings                                                                                            |
| `users`               | List of `{name, username, password, default}`                                                                                                                                                                                                                    | Credentials for form-based auth                                                                                    |
| `session`             | `method`                                                                                                                                                                                                                                                         | Session management: `COOKIE`, `HEADER`, or `SCRIPT`                                                                |
| `technology`          | `language`, `database`, `framework`, `include`                                                                                                                                                                                                                   | Technology context for scan optimization                                                                           |
| `spider`              | `duration`, `depth`, `children`                                                                                                                                                                                                                                  | Traditional spider settings                                                                                        |
| `ajaxSpider`          | `duration`, `depth`, `browsers`, `skip`                                                                                                                                                                                                                          | AJAX spider settings (for SPAs)                                                                                    |
| `activeScan`          | `duration`, `ruleDuration`, `policy`, `strength`, `threshold`, `rules`                                                                                                                                                                                           | Active scan settings                                                                                               |
| `passiveScan`         | `waitDuration`                                                                                                                                                                                                                                                   | Passive scan wait time                                                                                             |
| `webSocket`           | `enabled`, `passiveScripts`                                                                                                                                                                                                                                      | Passive WebSocket scanning (SPA/AJAX only); on by default for `spa`. See [WebSocket Scanning](#websocket-scanning) |
| `deepCrawl`           | `enabled`, `depth`, `duration`                                                                                                                                                                                                                                   | Pre-scan deep crawling                                                                                             |
| `vulnCheck`           | `enabled`, `templates`, `severities`, `excludeTags`, `rateLimit`, `timeout`                                                                                                                                                                                      | Post-scan vulnerability checking                                                                                   |
| `clientCertificate`   | `path`, `password`                                                                                                                                                                                                                                               | PKCS#12 client certificate for mTLS targets (orthogonal to other auth methods)                                     |
| `oast`                | `service`, `server`, `token`, `pollSeconds`, `port`                                                                                                                                                                                                              | Out-of-band detection (OAST); off by default. See [Out-of-Band Detection](#out-of-band-detection-oast)             |
| `filtering`           | `excludeRules`, `riskThreshold`                                                                                                                                                                                                                                  | Result filtering                                                                                                   |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.xygeni.io/xygeni-products/dast-security/dast-scanner/dast-scanner-configuration.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
