39948-vm/backend/src/utils/sqlValidator.js
2026-04-05 18:46:16 +04:00

54 lines
1.5 KiB
JavaScript

/**
* SQL Validator
*
* Shared validation for read-only SQL queries (widgets, admin SQL executor).
* Ensures queries are SELECT-only and don't contain dangerous patterns.
*/
const DEFAULT_MAX_LENGTH = 5000;
const RESTRICTED_FUNCTIONS = /\b(pg_sleep|set_config|copy)\b/i;
/**
* Validate a SQL query for read-only execution
* @param {string} sql - The SQL query to validate
* @param {object} options - Validation options
* @param {number} [options.maxLength=5000] - Maximum allowed query length
* @returns {{ valid: boolean, error?: string, normalized?: string }}
*/
function validateReadOnlySql(sql, options = {}) {
const maxLength = options.maxLength || DEFAULT_MAX_LENGTH;
if (typeof sql !== 'string' || !sql.trim()) {
return { valid: false, error: 'SQL query must be a non-empty string' };
}
if (sql.length > maxLength) {
return {
valid: false,
error: `SQL query is too long (max ${maxLength} characters)`,
};
}
const normalized = sql.trim().replace(/;+\s*$/, '');
if (!/^(select|with)\b/i.test(normalized)) {
return { valid: false, error: 'Only SELECT statements are allowed' };
}
if (normalized.includes(';')) {
return { valid: false, error: 'Only a single statement is allowed' };
}
if (/--|\/\*/.test(normalized)) {
return { valid: false, error: 'SQL comments are not allowed' };
}
if (RESTRICTED_FUNCTIONS.test(normalized)) {
return { valid: false, error: 'Restricted SQL function detected' };
}
return { valid: true, normalized };
}
module.exports = { validateReadOnlySql };