Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -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.
10 changes: 10 additions & 0 deletions src/api/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,11 @@ async function handleUpdateRow(
return;
}

if (!(await assertTableExists(runtime, tableName))) {
sendJsonError(res, `Table "${tableName}" not found`, 404);
return;
}
Comment on lines +973 to +976

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While adding the assertTableExists check correctly addresses the authorization bypass, its placement after readJsonBody is less than optimal. Moving this check to the very beginning of the function (before parsing the request body) would follow the 'fail-fast' principle, avoiding unnecessary resource consumption (CPU/memory for JSON parsing) when the target table does not exist. This would also align with the implementation in handleGetRows.


const setClauses = Object.entries(body.data).map(([col, val]) =>
sqlAssign(col, val),
);
Comment on lines 970 to 980

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security: Risk of SQL Injection in Update Handler

The code constructs SQL SET and WHERE clauses for the UPDATE statement by directly interpolating user-supplied data using custom helpers (sqlAssign, sqlPredicate). Unless these helpers are proven to be robust against all forms of SQL injection, this approach is highly risky. Attackers could potentially craft input that bypasses escaping and executes arbitrary SQL.

Recommendation:
Use parameterized queries provided by your database library or ORM instead of manual string interpolation. For example, with Drizzle or pg, use placeholders and pass values as parameters to ensure safe query execution.

Example (conceptual):

await db.execute('UPDATE table SET col1 = $1 WHERE col2 = $2', [val1, val2]);

Expand Down Expand Up @@ -1016,6 +1021,11 @@ async function handleDeleteRow(
return;
}

if (!(await assertTableExists(runtime, tableName))) {
sendJsonError(res, `Table "${tableName}" not found`, 404);
return;
}
Comment on lines +1024 to +1027

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to handleUpdateRow, the assertTableExists check should ideally be performed at the start of the function, before readJsonBody. This prevents the server from processing potentially large or malformed request bodies for non-existent or unauthorized tables, improving efficiency and DoS resilience.


const whereClauses = Object.entries(body.where).map(([col, val]) =>
sqlPredicate(col, val),
);
Comment on lines 1021 to 1031

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security: Risk of SQL Injection in Delete Handler

The construction of the WHERE clause for the DELETE statement uses direct mapping of user input via sqlPredicate, which may not be sufficient to prevent SQL injection. This is a critical security vulnerability if the helper does not fully sanitize all possible input cases.

Recommendation:
Switch to parameterized queries for all user-supplied data in SQL statements. Avoid manual string construction for SQL commands. Use the parameterization features of your database adapter to ensure all values are safely escaped and injected.

Expand Down
Loading