Only report what you can exploit
Every finding needs a concrete attack scenario. "Send this request, get this result" — never "an attacker could theoretically…".
The philosophy that separates a useful audit from a checklist dump — plus how severity is actually decided.
Every finding needs a concrete attack scenario. "Send this request, get this result" — never "an attacker could theoretically…".
The agent that checks a finding is never the agent that found it. Hunters find; a separate validator tries to disprove.
Severity = likelihood × impact, on both axes. If you can't describe the concrete damage, the severity is lower than you think.
If Layer A already prevents the attack, the absence of Layer B is a hardening suggestion — report it separately, don't inflate it.
Identify comparable software and calibrate. Same pattern exploited elsewhere? Stronger finding. Never exploited in 20 years? Understand why first.
A single run finds roughly half. Re-running with prior findings in hand keeps surfacing new ground.
The HIGH-vs-MEDIUM line for business logic: does the finding defeat an explicit security boundary?
Unauthenticated RCE, full database dump, admin account takeover without credentials.
Authenticated RCE, SQLi with exfiltration, stored XSS firing for all users, auth bypass, or an RBAC model completely defeated for a consequential action.
Targeted XSS needing specific conditions, CSRF with meaningful state change, secrets disclosure, or business-logic bypass with real but limited consequences.
Non-secret information disclosure, DoS requiring sustained effort, hardening gaps.
The mistakes that make security audits useless.
Listing everything that deviates from OWASP. It's a checklist, not a bug list; every real app makes tradeoffs.
Rating a missing redundant check as HIGH/CRITICAL when the query builder already quotes identifiers.
Rate limiting at the CDN is valid architecture; not every app needs app-level rate limiting.
If the trust model says admins are fully trusted, admins doing admin things is not a finding.
Ten LOWs don't make a useful report. Three real MEDIUMs do.
Either you can exploit it or you can't. If you need "potentially", you haven't done enough research.
Saying auth is solid builds trust in the findings you do report and helps prioritization.
The most convincing false positives reason "the runtime will interpret this as…" without verifying. Cite the spec or test it.
Scanners check SQLi/XSS/SSRF. A manual audit's value is logic errors, state-machine violations, and chained attacks.
"It uses parameterized queries" is lazy. Check every sql.raw(), dynamic identifiers, search/FTS, and bypass paths. Push.