September 12, 2026
PREPARE, RECEIVE FINDINGS, FIX, AND PROVE THE FIX

Lesson 49.1 taught you what a penetration test is and how to scope one. Now the operational half: what to clean up before testers arrive, how to read what they send back without defensiveness, and how to turn each finding into a verified closure — not a quiet code edit you hope worked.
Pre-pentest hygiene: be testable before you are tested
Testers charge for their window. Every hour they spend learning your staging URL is down, test accounts do not work, or logs record nothing is money spent on your disorganization instead of your security. A week before the window, confirm:
- Deployed URLs and environments — staging reachable at a stable URL, version pinned, differences from production written in the brief.
- Route and integration inventory — every in-scope route, job, and third-party integration listed with owners. Surprise endpoints either expand the scope or go untested.
- Scoped test accounts — owner, other-user, anonymous, and limited-admin accounts with realistic fake data, credentials in the test vault.
- Dummy credentials removed — no
test123, no shared admin password, no leftover seed keys with production privileges. Testers will find the credential you forgot; so would attackers. - Logs working — structured events from Class 51 firing on in-scope routes: sign-in, save, denied edit, refresh request. If you cannot see what the tester did, you cannot reproduce or verify anything after.
- Safe incident channel — a defined way for testers to report something urgent mid-test (active exploitation, a data-exposure chain) and a named recipient who watches it.
- Rollback owner — one person empowered to pause testing, roll back a deployment, or rotate a credential, with steps written down before the window opens.
Run a read-only AI check first: "Given this route inventory and checklist, list which items lack evidence and draft the missing setup tasks." Then do the tasks. The brief plus this checklist is what makes test week about findings instead of logistics.
Severity is triage, not ego
Reports rank findings — typically Critical, High, Medium, Low, Informational. Read ranks as scheduling instructions, not grades on your character:
| Severity | Meaning | Response |
|----------|---------|----------|
| Critical | Directly compromisable now: takeover, mass exposure, auth bypass | Stop affected work, restrict or disable the path if needed, emergency fix-and-verify track |
| High | Serious but narrower: one user's data via ID swap, authenticated abuse of a paid endpoint | Task card immediately, bounded fix, regression test, prioritized deploy and retest |
| Medium | Real weakness needing conditions or chaining: verbose errors aiding enumeration, missing limit on a cheap route | Scheduled fix with owner and due date in normal flow |
| Low / Informational | Hardening and hygiene: headers, version disclosure, best-practice gaps | Batch deliberately; never let them crowd out Highs |
Capture the same record per finding: evidence, affected asset, reproduction conditions, impact, remediation owner, due date, retest method, and communication plan (who must be told, including users if data was demonstrably exposed). A finding without an owner and due date is a rumor.
Two beginner mistakes: arguing severity instead of reproducing first — reproduce, then discuss. And fixing easy Lows while an uncomfortable High stays open. Triage means the High gets its owner and date before anything else moves.
The remediation loop
Every finding travels the same eight stations — post this where the team can see it:
understand finding → reproduce safely → write task card
→ fix smallest safe boundary → add a regression test
→ deploy through review → independent retest/verification
→ record remaining risk
Walk it with the borrowed-ID finding from 49.1 (GET /api/watchlist/:id exposing User B's item to User A):
1. Understand. Read the finding end to end. Which route, which accounts, what exactly was exposed? Quote the evidence back before theorizing. 2. Reproduce safely. Replay the exact steps with test accounts on staging: sign in as User A, request User B's item ID, observe the response. Capture request ID, time, log lines. Never reproduce against real customer data. 3. Write a task card. Bounded scope, allowed files, the abuse story as acceptance check ("User B's item requested by User A is denied; owner still allowed; anonymous denied"), risk tier, reviewer. 4. Fix at the smallest safe boundary. The server-side ownership check (plus database policy where supported) — not a frontend hiding maneuver. One boundary, one reviewable diff. 5. Add a regression test. The two-identity test from Class 47: owner allowed, different user denied, anonymous denied. This test is the finding's permanent memorial. 6. Deploy through review. Branch, reviewed diff, preview check, then production. No direct-to-production edits. 7. Independent retest. The original tester (or a qualified second party) verifies the fix on the live relevant path, not just "code looks right." For smaller findings, an independent replay of the evidence steps suffices — but record which level of independence each finding received. 8. Record. Close the register entry with fix commit, test name, deploy version, retest evidence, date, and remaining risk.
"Fixed in code" is not "fixed"
A fix merged to a branch is not a fix experienced by users. Proof must cover the live relevant path — the deployed environment, configuration, and policy where the weakness lived. Teams declare victory too early when: the fix is merged but never deployed; it reaches staging while production runs the old build; the code is correct but the database policy still permits the old access; an equivalent mobile or legacy route still exposes the same data; or flags and environment values keep the vulnerable path active. The verification question is literal: on production build X, replaying the reporter's steps, is the attack now denied — and does the log prove it?
That is why step 8 records remaining risk honestly: logs that cannot prove the past, tokens issued before the fix that stay valid until expiry, cached responses, third-party data already sent. Write it down, assign it, name the monitoring. A register claiming every finding is perfectly erased is less trustworthy than one closed finding with a named caveat.
Outside reports get the same system
Bug bounties and direct user reports need brief etiquette, because first contact sets the tone: accept respectfully — thank the reporter, acknowledge receipt promptly, route into the same evidence → card → fix → retest pipeline. Do not ask reporters to keep attacking ("can you escalate this further?") — that turns a helpful outsider into an unauthorized tester; reproduce it yourself. Acknowledge and credit per your published policy, say what happens next, then follow up. Never punish good-faith reporting with threats or silence; if behavior exceeded your policy's scope, say so plainly and redirect. Close the loop — report the outcome (fixed at version X, bounty awarded) without disclosing other customers' data. And keep a monitored contact path in SECURITY.md (Class 48.2): a bounty program without a watched inbox is a promise without a mailbox.
Exercise: the findings register with one verified close
Create SECURITY-FINDINGS.md and take one sample finding all the way to close:
| ID | Finding | Severity | Asset | Owner | Due | Status | Retest | Remaining risk |
|----|---------|----------|-------|-------|-----|--------|--------|----------------|
| F-001 | Borrowed watchlist ID readable by other user | High | GET /api/watchlist/:id | you | +14d | verified closed | independent replay on prod v1.4, denied + logged | exposure before v1.4 estimated |
| F-002 | … | … | … | … | … | open | … | … |
For F-001 attach the full trail using the loop's eight stations as headings: reporter evidence, safe reproduction (request IDs, times, log lines), task card, fix commit, regression test name, deploy version, retest evidence, remaining-risk note — so a stranger can audit the journey.
Finish line: a findings register with one sample finding taken to verified close.
Verify: replay the F-001 steps against the current production build. Denied plus logged means closed; anything else reopens it.
Common failure: closing when code is merged but deploy, policy, or retest is still pending. Merged is a station, not the destination.
Check your understanding
1. Which seven hygiene items must hold before the testing window, and why does each save money? 2. Walk the eight-station loop for a High finding without skipping or reordering a station. 3. Why can "fixed in code" leave users exposed? Name three concrete gaps.
Class 50 turns this fix-and-prove instinct into everyday engineering: testing that makes the product prove itself on every change, not just under adversarial review.
ARTICLE DISCUSSION
JOIN THE
CONVERSATION.
Got a question, a take, or a better way to do this? Log in and leave a comment.
