Methodology

Scoping a penetration test without guessing

Nearly every unprofitable engagement was mis-scoped rather than mis-delivered. The questions that move the estimate, the answers that are usually wrong, and the exclusions worth writing down.

Pental8 min read

Nearly every unprofitable engagement was mis-scoped rather than mis-delivered. The pattern is consistent: an estimate built from a description of the system rather than its actual surface, then a kick-off call where the surface turns out to be larger, and a fixed price that now has to absorb the difference.

Count what drives effort

Host counts and page counts are proxies, and poor ones. What drives effort is distinct functionality and distinct trust boundaries: how many roles exist, how many authentication paths, how many places user input crosses into another system. An application with forty pages and one role is smaller than one with eight pages and five roles, because the second one has ten role-pair combinations to test authorisation across.

Ask for the role matrix early

Roles multiply. Two roles is one boundary to test; five roles is ten. Asking "how many user types are there, and can you provide credentials for each" changes more estimates than any other single question.

The questions that move the number

  • How many user roles, and can you provide working credentials for every one?
  • Is there an API separate from the interface? Is it documented, and is the documentation current?
  • Which third-party integrations sit inside the boundary, and who owns them contractually?
  • Is the test environment a true copy of production, and if not, precisely where does it differ?
  • Are there rate limits, WAFs, bot protection or alerting that will interfere, and can they be relaxed for tester source addresses?
  • Is there anything in scope that cannot be reset if it breaks?
  • Who can authorise access if something blocks us on day two, and are they available that week?

The last one is not a formality. Waiting for credentials is the single most common way a fixed-price engagement loses a day, and it is entirely preventable at scoping.

Get answers in a structured form, not an email thread

Scoping conducted over email produces answers scattered across replies, and the version that reaches the estimate is whichever one the estimator happened to read last. A structured request the client completes, attached to the engagement, means the scope everyone agreed is the scope everyone can still see six weeks later when the disagreement happens.

Write the exclusions as carefully as the inclusions

Denial of service, social engineering, physical access, third-party hosted components, destructive testing, and anything touching production data should each be explicitly in or out. A scope listing only inclusions leaves every ambiguity to be resolved mid-test, under time pressure, by whoever is holding it.

Third-party components need permission you do not have

If the application depends on a hosted service your client does not own, testing it is your client's contractual problem and potentially your legal one. Name those components at scoping and get written confirmation either way.

Time-box the unknowns explicitly

Where scope genuinely cannot be pinned down, say what you will do with the time rather than pretending precision. "Two days on the API, prioritised by the role matrix, with a note on anything we could not reach" is an honest scope. A number invented to look confident is not, and it will be quoted back to you.

Quote the retest at the same time

Pricing it later, once findings exist, makes it look like an upsell triggered by bad news. Including it in the original proposal makes verification a normal part of the engagement, which is better for the client and considerably better for the proportion of your findings that actually get fixed.

A scope that cannot be read aloud on the kick-off call without someone saying "wait, does that include..." is not finished.

Pental is built by the people writing this

Engagement management for testing firms, on a database you own, under your brand, with the AI running on your key.