@nuxt/content
Version:
Write your content inside your Nuxt app
92 lines (91 loc) • 2.85 kB
JavaScript
const SQL_COMMANDS = /SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|\$/i;
const SQL_COUNT_REGEX = /COUNT\((DISTINCT )?([a-z_]\w+|\*)\)/i;
const SQL_SELECT_REGEX = /^SELECT (.*) FROM (\w+)( WHERE .*)? ORDER BY (["\w,\s]+) (ASC|DESC)( LIMIT \d+)?( OFFSET \d+)?$/;
export function assertSafeQuery(sql, collection) {
if (!sql) {
throw new Error("Invalid query");
}
const cleanedupQuery = cleanupQuery(sql);
if (cleanedupQuery !== sql) {
throw new Error("Invalid query");
}
const match = sql.match(SQL_SELECT_REGEX);
if (!match) {
throw new Error("Invalid query");
}
const [_, select, from, where, orderBy, order, limit, offset] = match;
const columns = select.trim().split(", ");
if (columns.length === 1) {
if (columns[0] !== "*" && !columns[0].match(SQL_COUNT_REGEX) && !columns[0].match(/^"[a-z_]\w+"$/i)) {
throw new Error("Invalid query");
}
} else if (!columns.every((column) => column.match(/^"[a-z_]\w+"$/i))) {
throw new Error("Invalid query");
}
if (from !== `_content_${collection}`) {
throw new Error("Invalid query");
}
if (where) {
if (!where.startsWith(" WHERE (") || !where.endsWith(")")) {
throw new Error("Invalid query");
}
const noString = cleanupQuery(where, { removeString: true });
if (noString.match(SQL_COMMANDS)) {
throw new Error("Invalid query");
}
}
const _order = (orderBy + " " + order).split(", ");
if (!_order.every((column) => column.match(/^("[a-zA-Z_]+"|[a-zA-Z_]+) (ASC|DESC)$/))) {
throw new Error("Invalid query");
}
if (limit !== void 0 && !limit.match(/^ LIMIT \d+$/)) {
throw new Error("Invalid query");
}
if (offset !== void 0 && !offset.match(/^ OFFSET \d+$/)) {
throw new Error("Invalid query");
}
return true;
}
function cleanupQuery(query, options = { removeString: false }) {
let inString = false;
let stringFence = "";
let result = "";
for (let i = 0; i < query.length; i++) {
const char = query[i];
const prevChar = query[i - 1];
const nextChar = query[i + 1];
if (char === "'" || char === '"') {
if (!options?.removeString) {
result += char;
continue;
}
if (inString) {
if (char !== stringFence || nextChar === stringFence || prevChar === stringFence) {
continue;
}
inString = false;
stringFence = "";
continue;
} else {
inString = true;
stringFence = char;
continue;
}
}
if (!inString) {
if (char === "-" && nextChar === "-") {
return result;
}
if (char === "/" && nextChar === "*") {
i += 2;
while (i < query.length && !(query[i] === "*" && query[i + 1] === "/")) {
i += 1;
}
i += 2;
continue;
}
result += char;
}
}
return result;
}