{"id":"sec-dev-pentest-finding-ticket","audience":"developers","category":"security","track":"security","technique":"format","title":"Turn a pentest finding into a dev ticket","free":false,"task":"Turn this finding from your authorized pentest into a ticket for the team. Exactly 5 numbered lines: Title, Severity, Affected, Fix, Done when. Titles start with the service in brackets, like '[billing-service]'. Use your team's severity scale, not the tester's rating. 'Done when' must be a check someone can run. Plain text, under 120 words.","materialLabel":"Pentest finding and team scale (attached to your prompt automatically)","material":"Finding PT-07 from the authorized Q4 pentest by an outside firm (report dated Nov 2, 2026)\nTitle: Insecure direct object reference on invoice download\nTester rating: HIGH\nDescription: A logged-in customer can download another customer's invoice PDF by changing the number in GET /api/invoices/{id}/pdf. The server checks that the user is logged in but not that the invoice belongs to them. Invoice IDs are sequential. Tested with two test accounts the firm was given; no real customer data was accessed.\nAffected: billing-service, endpoint GET /api/invoices/{id}/pdf. The JSON endpoint GET /api/invoices/{id} already checks ownership correctly.\nTester suggestion: \"Consider a full rewrite of the authorization layer.\"\n\nTeam severity scale (engineering handbook):\n- S1: any user can see or change another customer's data, OR no login needed\n- S2: needs a special role or internal network access\n- S3: hardening, no direct data exposure","rules":[{"type":"numberedItems","count":5},{"type":"plainText"},{"type":"maxWords","max":120}],"mistakes":[{"title":"Copying the tester's rating","why":"The AI keeps 'HIGH', which doesn't map to your team's scale.","fix":"Say: \"Use our scale: S1, S2 or S3 as defined.\"","checks":["r1"]},{"title":"Over-scoping the fix","why":"The AI repeats the tester's 'rewrite the authorization layer' suggestion.","fix":"Add: \"Fix only the PDF endpoint, matching the JSON endpoint's ownership check.\"","checks":["r2"]},{"title":"Fuzzy scope","why":"The AI lists both invoice endpoints as affected.","fix":"Ask: \"Affected: only the endpoint the finding proves.\"","checks":["r1"]},{"title":"Ignoring the title convention","why":"The AI writes a long descriptive title your board can't sort by service.","fix":"Say: \"Title starts with [billing-service].\"","checks":["r0"]},{"title":"No ticket shape","why":"The AI writes a report with headings instead of ticket fields.","fix":"Give the fields: \"Exactly 5 numbered lines: Title, Severity, Affected, Fix, Done when.\"","checks":["numberedItems","plainText"]},{"title":"Trusting the AI to count words","why":"AI is bad at counting. Ask for \"under 120 words\" and it often lands well over.","fix":"Ask for less than the limit and cap each line: \"each line under 15 words, under 90 words total.\"","checks":["maxWords"]}],"promptHints":[{"label":"Gives the ticket fields","patterns":["title","severity","affected","done when"]},{"label":"Uses your severity scale","patterns":["s1","our scale","team('s)? scale","not the tester"]},{"label":"Asks for a testable done line","patterns":["test","check","verif","403|404"]},{"label":"Says exactly how many","patterns":["\\b5\\b.{0,25}(line|field|item)","exactly (5|five)"]},{"label":"Sets a word limit","patterns":["\\d+\\s*words","word (limit|count)"]}],"hint":"A ticket is something a developer can close with confidence. Give the AI your fields and severity scale, and ask for a 'done when' that can be tested.","rubricLabels":["The 5 numbered lines start with Title:, Severity:, Affected:, Fix:, Done when:, in that order, and the Title starts with '[billing-service]'","Severity is S1 (not HIGH), and Affected names GET /api/invoices/{id}/pdf only, without saying the JSON endpoint is affected","Fix is an ownership check on the PDF endpoint (matching what the JSON endpoint does), not a full rewrite, and Done when is a runnable check (for example a test where one customer requests another's invoice PDF and gets 403 or 404)"]}