docs(security): record why the three SQL hotspots in admin.ts are safe (#180) #213

Closed
bermudalamb wants to merge 1 commits from feature/180-sql-justification-restore into main
1 Commits
Author SHA1 Message Date
bermudalambandClaude Opus 5 66ff354631 docs(security): record why the three SQL hotspots in admin.ts are safe (#180)
SonarQube Analysis / sonarqube (pull_request) Failing after 17m31s
Linting / lint (pull_request) Successful in 2m4s
The three open hotspots on the dashboard are `typescript:S2077`, "Formatting SQL queries is security-sensitive" — not the upload-path rules #180's review worked from. That review did reach the multer limits hotspot, which is marked Reviewed/Safe, and the refused-upload accumulation it uncovered was a real defect; it simply was not reviewing these three. The issue's scope note is where it went wrong. It says there is no string-built SQL, and that is true of the binding but not of the assembly — S2077 fires on the formatting.

All three are safe, and the reasoning now sits beside the code rather than only in SonarQube's UI, following the S5693 comment already in this file.

Two of them are a module constant plus a literal holding `$1`. `ADMIN_ITEM_SELECT` interpolates nothing of its own and the id is bound rather than formatted in — including on the update route, where the bound value is caller-supplied, which is exactly why it is a parameter.

The third is the one the rule is for, because `WHERE ${clauses.join(' AND ')}` really is assembled at run time. It holds because `buildItemFilterSql` composes only string literals written in `itemFilters.ts`, and the single interpolation inside any of them is `$${next}` — a placeholder index, a number, seeded from the startIndex argument and incremented locally, never derived from a filter value. A caller chooses which of six fixed fragments are joined and supplies every value in `params`, and neither becomes SQL. `parseItemFilters` rejecting malformed input above is defence in depth rather than the reason this holds.

Comments only, so nothing changes at run time. Backend build clean and 278 unit tests pass.

The dashboard half — marking the three Reviewed → Safe so the count reflects decisions made rather than pending — is still outstanding, which is why this refs rather than closes.

Refs #180

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 11:18:39 -05:00