{"id":"sec-dev-pr-review-sql-query","audience":"developers","category":"security","track":"security","technique":"format","title":"Write security review comments on a database PR","free":false,"task":"Review this pull request for security problems and write exactly 3 numbered review comments in your team's format: 'file:line - Blocker|Suggestion|Nit - comment with the fix'. Blocker means it can't merge as is, and your team treats returning password hashes as a Blocker. Don't flag code that is already safe. Plain text, under 120 words.","materialLabel":"Pull request diff (attached to your prompt automatically)","material":"PR #418: Add customer search and sorting to the admin API\n\n--- a/api/customers.py\n+++ b/api/customers.py\n@@ -10,6 +10,30 @@\n 10  ALLOWED_SORT = {\"name\", \"created_at\", \"city\"}\n 11\n 12  @app.get(\"/admin/customers\")\n 13  @require_role(\"admin\")\n 14  def search_customers():\n 15      q = request.args.get(\"q\", \"\")\n 16      sort = request.args.get(\"sort\", \"name\")\n 17      if sort not in ALLOWED_SORT:\n 18          sort = \"name\"\n 19      sql = f\"SELECT id, name, email, city FROM customers WHERE name LIKE '%{q}%' ORDER BY {sort}\"\n 20      rows = db.execute(sql).fetchall()\n 21      return jsonify([dict(r) for r in rows])\n 22\n 23  @app.get(\"/admin/customers/<int:customer_id>\")\n 24  @require_role(\"admin\")\n 25  def get_customer(customer_id):\n 26      row = db.execute(\"SELECT * FROM customers WHERE id = ?\", (customer_id,)).fetchone()\n 27      if row is None:\n 28          abort(404)\n 29      return jsonify(dict(row))\n\nAuthor's note: \"sort is allowlisted so the f-string is fine. get_customer returns all columns, including password_hash and the internal notes field, but only admins can call it.\"","rules":[{"type":"numberedItems","count":3},{"type":"plainText"},{"type":"maxWords","max":120}],"mistakes":[{"title":"No comment format","why":"The AI writes a long review with headings, so comments can't be pasted onto lines.","fix":"Give the shape: \"file:line - Blocker|Suggestion|Nit - comment with the fix.\"","checks":["numberedItems","plainText"]},{"title":"Flagging safe code","why":"The AI sees an f-string and calls the sort column dangerous too, even though it is allowlisted.","fix":"Add: \"Don't flag code that is already safe, like the allowlisted sort.\"","checks":["r1"]},{"title":"Naming the bug without the fix","why":"The AI says 'SQL injection risk' and stops.","fix":"Ask: \"Each comment names the fix, for example a parameterized query.\"","checks":["r0"]},{"title":"Trusting the author's note","why":"The AI accepts 'only admins can call it' and rates the leaked password hashes as minor.","fix":"Say: \"Returning password hashes is always a Blocker, even for admin-only endpoints.\"","checks":["r2"]},{"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 comment format","patterns":["file:line","blocker","suggestion","nit"]},{"label":"Skips safe code","patterns":["already safe","allowlist","don'?t flag","false positive"]},{"label":"Asks for the fix","patterns":["fix","parameteri","placeholder"]},{"label":"Says exactly how many","patterns":["\\b3\\b.{0,25}(comment|line|item)","exactly (3|three)"]},{"label":"Sets a word limit","patterns":["\\d+\\s*words","word (limit|count)"]}],"hint":"Good review comments point at a line, say how bad it is, and give the fix. Give the AI your format and tell it not to flag code that's already safe.","rubricLabels":["Every comment starts with 'api/customers.py:<line> - ' followed by the word Blocker, Suggestion or Nit, and the line 19 comment is labeled Blocker with the fix of a parameterized query","No comment says the sort value (lines 16 to 18) is unsafe or a Blocker, since it is checked against ALLOWED_SORT","A comment on line 26 (or 29) is labeled Blocker, says SELECT * returns password_hash and the internal notes, and suggests selecting only the needed columns"]}