diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000000..d79a36b640 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2025-04-26 - Missing Table Existence Check in Row Mutations +**Vulnerability:** Authorization bypass. PUT (update) and DELETE endpoints in /api/database/tables/:table/rows did not verify that the target table was a valid user table (using assertTableExists), unlike GET and POST endpoints. +**Learning:** This oversight allowed potential modification/deletion of arbitrary or internal tables if an attacker guessed the table name, bypassing the safety mechanisms intended to restrict queries to user-facing base tables. +**Prevention:** Always apply the exact same authorization and existence checks across all CRUD operations for a given resource. diff --git a/src/api/database.ts b/src/api/database.ts index 3f786fca9f..ef797fa2d1 100644 --- a/src/api/database.ts +++ b/src/api/database.ts @@ -970,6 +970,11 @@ async function handleUpdateRow( return; } + if (!(await assertTableExists(runtime, tableName))) { + sendJsonError(res, `Table "${tableName}" not found`, 404); + return; + } + const setClauses = Object.entries(body.data).map(([col, val]) => sqlAssign(col, val), ); @@ -1016,6 +1021,11 @@ async function handleDeleteRow( return; } + if (!(await assertTableExists(runtime, tableName))) { + sendJsonError(res, `Table "${tableName}" not found`, 404); + return; + } + const whereClauses = Object.entries(body.where).map(([col, val]) => sqlPredicate(col, val), );