Why a vulnerability scanner will not find your worst bug
Scanners are good at recognising patterns they were taught. The findings that actually cost money are the ones that look like normal application behaviour.
Automated scanners earn their place. They crawl quickly, they catch the obvious misconfigurations, and they give you a cheap baseline you can run every week. What they cannot do is understand what your application is for.
A scanner recognises patterns it was taught: a reflected parameter, a missing header, an outdated library version, an error message that looks like SQL. It has no opinion about whether a discount code should be applicable twice, whether a trial account should be able to invite users into a paid workspace, or whether the price submitted at checkout should be trusted at all.
The findings that actually cost money
Almost every high-impact issue I report to clients falls into one of four groups, and none of them look like an attack to a scanner:
Business logic. The request is well-formed, authenticated and completely valid. It just does something the business never intended: applying a coupon after the total is calculated, changing a quantity to a negative number, or completing a multi-step flow out of order so a payment step is skipped.
Authorization. The application checks who you are but forgets to check what you own. Change an ID in a URL, an API path or a JSON body and you are reading another tenant's data. A scanner sees a 200 response and moves on; it has no idea the record belonged to someone else.
Account lifecycle. Registration, password reset, email change, OTP verification, invitation and session termination are usually built at different times by different people. The gaps between them are where accounts get taken over.
Chained low-severity issues. An information disclosure that is genuinely low risk on its own becomes critical when it leaks the identifier needed to exploit a weak authorization check somewhere else. Chaining requires understanding the application as a system, not a list of URLs.
Why manual testing finds these
The difference is not tooling. I use plenty of tools. The difference is that a human tester builds a model of the application first: what the roles are, what each role is supposed to be able to do, where money and trust change hands, and which workflows have state that can be manipulated.
That model is what turns "this endpoint responded 200" into "this endpoint let a viewer-role user approve their own invoice."
A practical way to combine both
You do not have to choose. The pattern that works well for growing teams:
- Run automated scanning continuously in CI. It is cheap and it catches regressions in the boring categories.
- Book manual testing around meaningful change: before launch, before onboarding a larger customer, after a permissions model changes, after a payment or checkout rewrite.
- Retest the fixes. A finding is not closed because a ticket was closed; it is closed because someone tried the attack again and it no longer works.
Scanners tell you whether you have the common problems. Manual testing tells you whether your application can be abused. Growing platforms need both, and they need to be honest about which question each one answers.