Skip to content

Commit 52dbc3c

Browse files
committed
chore(dx): improve query assertion errors
1 parent 67a8477 commit 52dbc3c

File tree

1 file changed

+12
-11
lines changed

1 file changed

+12
-11
lines changed

src/runtime/internal/security.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ const SQL_SELECT_REGEX = /^SELECT (.*) FROM (\w+)( WHERE .*)? ORDER BY (["\w,\s]
1313
*/
1414
export function assertSafeQuery(sql: string, collection: string) {
1515
if (!sql) {
16-
throw new Error('Invalid query')
16+
throw new Error('Invalid query: Query cannot be empty')
1717
}
1818

1919
const cleanedupQuery = cleanupQuery(sql)
2020

2121
// Query is invalid if the cleaned up query is not the same as the original query (it contains comments)
2222
if (cleanedupQuery !== sql) {
23-
throw new Error('Invalid query')
23+
throw new Error('Invalid query: SQL comments are not allowed')
2424
}
2525

2626
const match = sql.match(SQL_SELECT_REGEX)
2727
if (!match) {
28-
throw new Error('Invalid query')
28+
throw new Error('Invalid query: Query must be a valid SELECT statement with proper syntax')
2929
}
3030

3131
const [_, select, from, where, orderBy, order, limit, offset] = match
@@ -38,43 +38,44 @@ export function assertSafeQuery(sql: string, collection: string) {
3838
&& !columns[0]?.match(SQL_COUNT_REGEX)
3939
&& !columns[0]?.match(/^"[a-z_]\w+"$/i)
4040
) {
41-
throw new Error('Invalid query')
41+
throw new Error(`Invalid query: Column '${columns[0]}' has invalid format. Expected *, COUNT(), or a quoted column name`)
4242
}
4343
}
4444
else if (!columns.every(column => column.match(/^"[a-z_]\w+"$/i))) {
45-
throw new Error('Invalid query')
45+
throw new Error('Invalid query: Multiple columns must be properly quoted and alphanumeric')
4646
}
4747

4848
// FROM
4949
if (from !== `_content_${collection}`) {
50-
throw new Error('Invalid query')
50+
const collection = String(from || '').replace(/^_content_/, '')
51+
throw new Error(`Invalid query: Collection '${collection}' does not exist`)
5152
}
5253

5354
// WHERE
5455
if (where) {
5556
if (!where.startsWith(' WHERE (') || !where.endsWith(')')) {
56-
throw new Error('Invalid query')
57+
throw new Error('Invalid query: WHERE clause must be properly enclosed in parentheses')
5758
}
5859
const noString = cleanupQuery(where, { removeString: true })
5960
if (noString.match(SQL_COMMANDS)) {
60-
throw new Error('Invalid query')
61+
throw new Error('Invalid query: WHERE clause contains unsafe SQL commands')
6162
}
6263
}
6364

6465
// ORDER BY
6566
const _order = (orderBy + ' ' + order).split(', ')
6667
if (!_order.every(column => column.match(/^("[a-zA-Z_]+"|[a-zA-Z_]+) (ASC|DESC)$/))) {
67-
throw new Error('Invalid query')
68+
throw new Error('Invalid query: ORDER BY clause must contain valid column names followed by ASC or DESC')
6869
}
6970

7071
// LIMIT
7172
if (limit !== undefined && !limit.match(/^ LIMIT \d+$/)) {
72-
throw new Error('Invalid query')
73+
throw new Error('Invalid query: LIMIT clause must be a positive number')
7374
}
7475

7576
// OFFSET
7677
if (offset !== undefined && !offset.match(/^ OFFSET \d+$/)) {
77-
throw new Error('Invalid query')
78+
throw new Error('Invalid query: OFFSET clause must be a positive number')
7879
}
7980

8081
return true

0 commit comments

Comments
 (0)