> For the complete documentation index, see [llms.txt](https://docs.nullify.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nullify.ai/capabilities/pentests/engagement-workflow.md).

# Engagement Workflow

You can launch a pentest from the Nullify dashboard or with the Nullify CLI.

Before you start, work through [Preparing for an Engagement](/capabilities/pentests/preparing-for-an-engagement.md).

## Dashboard

1. Go to **Applications**, select **Add application**, and enter its name and description.
2. On **Targets**, select **Add target**, enter the host, port and protocol, and select the application. Give any API path prefix to your Nullify contact. Each application resolves to one target URL.
3. Attach one credential per identity, then validate each one before starting.
4. If prompted, select **Run readiness check** before starting.
5. Start the run. Review progress and results in the dashboard. Configure ticketing and notifications as described in [Configuration – General](/configuration/configuration-general.md).

![Current Start assessment dialog](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-cd11ef526067563797d18321aaf5585c2be561d9%2Fstart-assessment.png?alt=media)

The current dashboard calls this action **Start assessment**. Select the registered application; readiness checks can hand the request to Nullify for manual handling. The screenshot shows the dialog before selection.

Creating applications and credentials requires the **Admin** role. Starting and stopping scans requires **Editor**. Contact your Nullify contact to arrange dashboard access.

Nullify discovers your API specification automatically where it can (see [Supported Targets](/capabilities/pentests/supported-targets.md)). To supply a specification file directly, use the CLI or send it to your Nullify contact.

## CLI

Use the CLI to run a pentest from your own environment, to wire one into a pipeline, or to test a service that is not reachable from the internet.

Install the CLI and authenticate:

```bash
curl -sSfL https://raw.githubusercontent.com/Nullify-Platform/cli/main/install.sh | sh -s -- --host 'api.<TENANT>.nullify.ai'
nullify auth login --host 'api.<TENANT>.nullify.ai'
```

In CI, set `NULLIFY_TOKEN` to a service-account token instead of logging in. See [API Configuration](/configuration/configuration-api.md) for how to generate one. Inside GitHub Actions you can pass the workflow's GitHub token with `--github-token` and the CLI exchanges it for a Nullify token — see [CLI Authentication](/cli/cli/authentication.md).

Run a pentest:

```bash
nullify pentest \
  --app-name      "Payments API" \
  --spec-path     "./openapi.json" \
  --target-host   "https://api.example.dev" \
  --github-owner  "my-org" \
  --github-repo   "payments-service" \
  --header        "Authorization: Bearer 1234" \
  --header        "X-Custom-Header: abcxyz"
```

`--spec-path` is required. Repeat `--header` once per header you want the pentester to send.

### Testing more than one identity

Static headers test one identity. For cross-identity authorization testing, provide credentials as described in [Preparing for an Engagement](/capabilities/pentests/preparing-for-an-engagement.md). Pass a config file with `--auth-config`:

```bash
nullify pentest \
  --app-name    "Payments API" \
  --spec-path   "./openapi.json" \
  --target-host "https://api.example.dev" \
  --auth-config "./pentest-auth.json"
```

`--auth-config` takes a JSON file — unlike `--spec-path`, this file is not parsed as YAML. The top-level `users` array holds one entry per identity; give each entry a distinct `roleName`:

```json
{
  "users": [
    {
      "roleName": "admin",
      "roleDescription": "Administrator with full access",
      "authConfig": {
        "method": "bearer",
        "token": "<admin token>"
      }
    },
    {
      "roleName": "user",
      "roleDescription": "Standard user",
      "authConfig": {
        "method": "bearer",
        "token": "<user token>"
      }
    },
    {
      "roleName": "guest",
      "roleDescription": "Unauthenticated",
      "authConfig": {
        "method": "none"
      }
    }
  ]
}
```

The CLI does not expand `${VAR}` or template this file — whatever you write into `token` or `password` is read and sent as-is. Do not commit a populated auth-config file to source control: generate it at run time from your secret store (for example, write it to a temporary path in your CI job just before the `nullify pentest` step, then delete it) or keep it in a path covered by `.gitignore`.

### Running against a private network

Add `--local` to run the pentester inside your own network against hosts only you can reach. See [Running in Private Networks](/capabilities/pentests/private-networks.md).

## Continuous Integration

Run the CLI in your pipeline. In GitHub Actions, pass the workflow token and the CLI handles the rest:

```yaml
- name: Nullify pentest
  run: |
    nullify pentest \
      --github-token "${{ secrets.GITHUB_TOKEN }}" \
      --app-name     "Payments API" \
      --spec-path    "./openapi.json" \
      --target-host  "https://api.staging.example.dev"
```

`nullify ci gate` checks existing open findings; it does not wait for this scan or restrict results to new findings. `nullify ci report` produces Markdown which your pipeline can publish. See [CI gate limitations](/cli/cli.md#ci-gate-and-report).

Run pentests on release branches or a schedule.

## What to expect

Each confirmed finding carries the request and response that proved it, plus a reproduction script. Download the PDF report from the run page once the engagement completes.

## Readiness, budgets, and scan states

For hosted runs, review readiness and the application estimate before submitting a scan. The [Pentests API](/api-reference/api-reference/pentester.md) exposes application readiness, `GET /dast/pentest/applications/{applicationId}/estimate`, and `GET /dast/pentest/budget`. An estimate is a planning input, not a promise of complete coverage or a fixed invoice.

| API status             | Meaning for the operator                                                                                                                  |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `queued`               | Accepted for later execution.                                                                                                             |
| `in progress`          | Execution is underway. The serialized value contains a space.                                                                             |
| `stopping`             | A stop request is being processed.                                                                                                        |
| `completed`            | The workflow completed; inspect findings, stages, and coverage.                                                                           |
| `cancelled`            | The run was cancelled.                                                                                                                    |
| `failed`               | The run failed; inspect the recorded error.                                                                                               |
| `aborted_config_error` | Configuration prevented execution. Correct it before retrying.                                                                            |
| `human_fallback`       | Automated execution handed off for operator attention.                                                                                    |
| `awaiting_manual`      | The request awaits manual handling, including deployments with self-service execution disabled. It does not mean an operator has started. |
| `skipped`              | The run was skipped; inspect the reason.                                                                                                  |

A terminal state alone does not establish that every endpoint or identity was tested. Use the run's activity, state, evidence, and report together. DAST autofix uses its own state/activity workflow; do not substitute the SAST/SCA cache routes.
