> 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/preparing-for-an-engagement.md).

# Preparing for an Engagement

Everything the pentester needs before a run, and why each item matters. Gathering these first is the difference between a two-hour engagement and a two-week one.

## Checklist

|   | Item                                     | Required             |
| - | ---------------------------------------- | -------------------- |
| 1 | Target hostname and port                 | Yes                  |
| 2 | API specification, or a discoverable one | Strongly recommended |
| 3 | One credential set per role              | Strongly recommended |
| 4 | Nullify source IP allowlisted            | Yes                  |
| 5 | Rate limits relaxed for that IP          | Recommended          |
| 6 | Repository name                          | Recommended          |
| 7 | Agreed test window and scope             | Yes                  |

## 1. Target

A fully-qualified hostname and port, for example `api.staging.example.com:443`.

Test against a staging or QA environment where you can. The pentester sends real attack traffic and may create or modify data.

If the service is not reachable from the internet, see [Running in Private Networks](/capabilities/pentests/private-networks.md).

## 2. API specification

This matters more than anything else on the list.

Nullify looks for a specification in this order:

1. A file you supply with `--spec-path`.
2. GraphQL introspection, for GraphQL APIs.
3. Common paths on your running service — `/openapi.json`, `/swagger.json`, `/api-docs`, `/graphql`. If your service already exposes one of these, you need to do nothing.
4. A specification reverse-engineered from your source code.
5. A minimal generated fallback.

Testing works without a specification, and the pentester can still send live requests and attempt exploitation. Coverage is much shallower, however, because it lacks the schema browsing and endpoint context used to map the API systematically.

Send OpenAPI or Swagger as either JSON or YAML.

## 3. Credentials

The pentester logs in as each role you give it and replays requests across identities. That is how it finds broken object-level authorization, privilege escalation, and authorization bypass — the flaws that scanners miss and that matter most in an API.

For each role, provide:

* A role name, such as `admin`, `member`, or `guest`
* Username and password, or a token
* The login URL, if login is not a simple token
* The TOTP secret, if multi-factor authentication is enforced on the account

Use dedicated test accounts rather than real user accounts.

Nullify supports form logins with optional TOTP, API keys (in a header, bearer token, query parameter, or JSON body), HTTP basic auth, OpenID Connect, and OAuth 2.0 client-credentials and authorization-code flows.

Secrets are encrypted at rest and are never returned by the API — they read back masked.

**Every credential must authenticate.** A pentest stops at the login stage if any attached credential fails, rather than testing a partial view of your application. Validate each credential in the dashboard, or with `POST /dast/credentials/{credentialId}/validate`, before starting a run.

Validation reports one of three outcomes. `valid` means the login returned real evidence of a session. `invalid` means it was rejected — fix the credential. `unknown` (shown as "Not Tested") means the check could not get an answer: a WAF or bot-mitigation product answered on your application's behalf, the endpoint rate-limited the check, or the login returned no evidence either way. `unknown` is not a failure and does not block a run — the pentest agent drives the full login flow at scan time — but it does mean this check cannot confirm the credential for you. Allowlisting the Nullify source IPs (below) removes the most common cause.

## 4. Allowlist the Nullify source IP

Pentest traffic originates from a small, static set of IPs specific to your tenant's region. Retrieve them with:

```
GET /dast/sourceips
```

They are also shown on the Pentest page in the dashboard.

Allowlist those addresses on any WAF, API gateway, or secure web gateway in front of the target. Skipping this step is the most common reason an engagement stalls.

Use the allowlisted source IPs to identify and filter pentest traffic. Request headers are not a stable traffic boundary: different pentest tools use different `User-Agent` values, and `X-Nullify-ID` is not added to every request. Do not build WAF rules or log filters that depend on either header.

## 5. Rate limits

The pentester backs off when it receives a 429, but aggressive rate limiting still reduces coverage. Relax or exempt limits for the Nullify source IPs during the test window.

## 6. Repository

The GitHub organisation and repository name for the service. This lets Nullify attach findings to the right repository, route them to code owners, and generate fix pull requests.

## 7. Test window and scope

Agree before the run:

* Start and end of the test window
* Whether destructive testing is acceptable, and whether test data is disposable
* Endpoints that are explicitly out of scope — anything moving money, exporting regulated data, or calling a third-party partner API
* An escalation contact if the environment degrades

You can stop a run at any time from the dashboard or with `POST /dast/pentest/scans/{scanId}/stop`.

## Preflight

Once the application, target, and credentials are in place, run a preflight check. It confirms the target is reachable, the credentials authenticate, and a specification is available. It also estimates cost and duration.

```
POST /dast/pentest/applications/{applicationId}/preflight
```

You get five preflight runs per application per day. Check remaining quota with `GET /dast/pentest/applications/{applicationId}/preflight/quota`.
