# Security-gate report

## Scope (files reviewed)
- `kb.ts` — target

## kb.ts
**Oracle (complete-mediation):** SILENT

**LLM leg (v2@sonnet/MED, 3-roll union):**
- [critical] SQL Injection via `sql.raw()` in `listArticlesBySpaceIds`  _(found by 3/3 rolls)_
- [high] Portal Queries Missing `isNull(deletedAt)` — Soft-Deleted PUBLISHED Articles Exposed to Customers  _(found by 3/3 rolls)_
- [high] `moveArticle` Depth Check Non-Atomic With Write — Concurrent Moves Can Exceed `MAX_TREE_DEPTH`  _(found by 3/3 rolls)_
- [high] `softDeleteArticle` subtree deletion is non-atomic — partial deletion on interruption  _(found by 2/3 rolls)_
- [high] Cycle creation in `moveArticle` causes infinite loop / stack overflow in `treeDepth` and `subtreeHeight`  _(found by 2/3 rolls)_
- [high] Self-Parenting Creates Permanent Cycle — Infinite Loops in `treeDepth`, `subtreeHeight`, and `softDeleteArticle` BFS  _(found by 1/3 rolls)_
- [high] `updateArticleMeta` Accepts `parentId` Without Depth Validation or Space Ownership Check  _(found by 1/3 rolls)_
- [high] `softDeleteArticle` subtree deletion is not atomic — partial deletes on Worker eviction  _(found by 1/3 rolls)_
- [medium] TOCTOU slug uniqueness race in `duplicateArticle`  _(found by 2/3 rolls)_
- [medium] `NaN` can be written to the position column via `parseFloat` on corrupt data  _(found by 2/3 rolls)_
- [medium] Slug uniqueness check in `duplicateArticle` omits `tenantId` filter  _(found by 2/3 rolls)_
- [medium] `updateArticle` and `incrementViewCount` Operate on Soft-Deleted Articles  _(found by 1/3 rolls)_
- [medium] `moveArticle` Does Not Validate That `parentId` Belongs to the Same Space — Cross-Space Reparenting Corrupts Tree  _(found by 1/3 rolls)_
- [medium] `getLastSiblingPosition` counts soft-deleted siblings, skewing new article positions  _(found by 1/3 rolls)_
- [medium] `kbPortalQuery` trusts `customerId` without verifying it belongs to `tenantId` (cross-file assumption / IDOR)  _(found by 1/3 rolls)_
- [low] Hard `deleteArticle` does not cascade to children — orphans become stale roots  _(found by 1/3 rolls)_
- [low] Published children of DRAFT/non-published parents surface as root nodes in portal tree  _(found by 1/3 rolls)_
- [low] `deleteArticle` hard-deletes without orphan handling, coexisting with `softDeleteArticle`  _(found by 1/3 rolls)_
- [low] `subtreeHeight` loads entire space into Worker memory — unbounded result set  _(found by 1/3 rolls)_
