UNPKG

sql-query-safety-checker

Version:

🛡️ Comprehensive TypeScript library for SQL query security analysis with injection detection, risk assessment, and Express middleware integration

455 lines (452 loc) 15 kB
var import_node_module = require("node:module"); var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __hasOwnProp = Object.prototype.hasOwnProperty; var __moduleCache = /* @__PURE__ */ new WeakMap; var __toCommonJS = (from) => { var entry = __moduleCache.get(from), desc; if (entry) return entry; entry = __defProp({}, "__esModule", { value: true }); if (from && typeof from === "object" || typeof from === "function") __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable })); __moduleCache.set(from, entry); return entry; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true, configurable: true, set: (newValue) => all[name] = () => newValue }); }; // index.ts var exports_sql_query_safety_checker = {}; __export(exports_sql_query_safety_checker, { validateQueryAgainstPolicy: () => validateQueryAgainstPolicy, normalizeQuery: () => normalizeQuery, needsConfirmation: () => needsConfirmation, isReadOnlyQuery: () => isReadOnlyQuery, isQuerySafe: () => isQuerySafe, getSecurityLevelColor: () => getSecurityLevelColor, getQuerySecuritySummary: () => getQuerySecuritySummary, generateRecommendations: () => generateRecommendations, createSafetyChecker: () => createSafetyChecker, analyzeQuerySecurity: () => analyzeQuerySecurity, SecurityLevel: () => SecurityLevel, SQLQuerySafetyChecker: () => SQLQuerySafetyChecker, SECURITY_THREATS: () => SECURITY_THREATS, SAFE_OPERATIONS: () => SAFE_OPERATIONS }); module.exports = __toCommonJS(exports_sql_query_safety_checker); // src/types.ts var SecurityLevel; ((SecurityLevel2) => { SecurityLevel2["SAFE"] = "safe"; SecurityLevel2["LOW_RISK"] = "low_risk"; SecurityLevel2["MEDIUM_RISK"] = "medium_risk"; SecurityLevel2["HIGH_RISK"] = "high_risk"; SecurityLevel2["CRITICAL"] = "critical"; })(SecurityLevel ||= {}); // src/constants.ts var SECURITY_THREATS = [ { pattern: /\b(DELETE|delete)\s+FROM\b/gi, name: "DELETE", description: "Data deletion operation", level: "high_risk" /* HIGH_RISK */, category: "DML" }, { pattern: /\b(UPDATE|update)\s+\w+\s+SET\b/gi, name: "UPDATE", description: "Data update operation", level: "medium_risk" /* MEDIUM_RISK */, category: "DML" }, { pattern: /\b(INSERT|insert)\s+INTO\b/gi, name: "INSERT", description: "Data insertion operation", level: "medium_risk" /* MEDIUM_RISK */, category: "DML" }, { pattern: /\b(REPLACE|replace)\s+INTO\b/gi, name: "REPLACE", description: "Data replacement operation", level: "medium_risk" /* MEDIUM_RISK */, category: "DML" }, { pattern: /\b(MERGE|merge)\s+INTO\b/gi, name: "MERGE", description: "Data merge operation", level: "medium_risk" /* MEDIUM_RISK */, category: "DML" }, { pattern: /\b(UPSERT|upsert)\b/gi, name: "UPSERT", description: "Data insert/update operation", level: "medium_risk" /* MEDIUM_RISK */, category: "DML" }, { pattern: /\b(DROP|drop)\s+(TABLE|DATABASE|SCHEMA|INDEX|VIEW|TRIGGER|FUNCTION|PROCEDURE)\b/gi, name: "DROP", description: "Structure deletion operation", level: "critical" /* CRITICAL */, category: "DDL" }, { pattern: /\b(CREATE|create)\s+(TABLE|DATABASE|SCHEMA|INDEX|VIEW|TRIGGER|FUNCTION|PROCEDURE)\b/gi, name: "CREATE", description: "Structure creation operation", level: "high_risk" /* HIGH_RISK */, category: "DDL" }, { pattern: /\b(ALTER|alter)\s+(TABLE|DATABASE|SCHEMA|INDEX|VIEW)\b/gi, name: "ALTER", description: "Structure modification operation", level: "high_risk" /* HIGH_RISK */, category: "DDL" }, { pattern: /\b(TRUNCATE|truncate)\s+(TABLE)?\b/gi, name: "TRUNCATE", description: "Table content clearing operation", level: "high_risk" /* HIGH_RISK */, category: "DDL" }, { pattern: /\b(GRANT|grant|REVOKE|revoke)\b/gi, name: "PERMISSIONS", description: "Permission modification operation", level: "critical" /* CRITICAL */, category: "DCL" }, { pattern: /\b(SHUTDOWN|shutdown|RESTART|restart)\b/gi, name: "SERVER_CONTROL", description: "Server control operation", level: "critical" /* CRITICAL */, category: "ADMIN" }, { pattern: /\b(BACKUP|backup|RESTORE|restore)\b/gi, name: "BACKUP_RESTORE", description: "Backup/restore operation", level: "critical" /* CRITICAL */, category: "ADMIN" }, { pattern: /\b(LOAD\s+DATA|load\s+data)\b/gi, name: "LOAD_DATA", description: "Data loading operation", level: "high_risk" /* HIGH_RISK */, category: "ADMIN" }, { pattern: /\b(EXEC|exec|EXECUTE|execute)\s+(xp_|sp_cmdshell)/gi, name: "SYSTEM_EXEC", description: "System command execution", level: "critical" /* CRITICAL */, category: "SYSTEM" }, { pattern: /\b(OPENROWSET|openrowset|OPENDATASOURCE|opendatasource)\b/gi, name: "EXTERNAL_ACCESS", description: "External data source access", level: "critical" /* CRITICAL */, category: "SYSTEM" }, { pattern: /(;|\s)(DROP|drop)\s+(TABLE|DATABASE)/gi, name: "INJECTION_DROP", description: "Potential SQL injection with DROP command", level: "critical" /* CRITICAL */, category: "INJECTION" }, { pattern: /(\s|^)(UNION|union)\s+(ALL\s+)?(SELECT|select)/gi, name: "UNION_INJECTION", description: "Potential UNION-based SQL injection", level: "critical" /* CRITICAL */, category: "INJECTION" }, { pattern: /'(\s*)(OR|or|AND|and)(\s*)(\d+\s*=\s*\d+|'\s*=\s*')/gi, name: "BOOLEAN_INJECTION", description: "Potential boolean-based SQL injection", level: "critical" /* CRITICAL */, category: "INJECTION" }, { pattern: /(\/\*|\*\/|--|\||&)/g, name: "COMMENT_INJECTION", description: "Potential comment-based injection or evasion", level: "high_risk" /* HIGH_RISK */, category: "INJECTION" }, { pattern: /\b(WAITFOR|waitfor)\s+(DELAY|delay|TIME|time)/gi, name: "TIME_INJECTION", description: "Potential time-based SQL injection", level: "critical" /* CRITICAL */, category: "INJECTION" } ]; var SAFE_OPERATIONS = [ /^\s*(SELECT|select)\b/, /^\s*(WITH|with)\b/, /^\s*(EXPLAIN|explain)\b/, /^\s*(DESCRIBE|describe)\b/, /^\s*(DESC|desc)\b/, /^\s*(SHOW|show)\b/, /^\s*(PRAGMA|pragma)\b/ ]; // src/utils.ts var normalizeQuery = (query) => { if (!query || typeof query !== "string") { return ""; } return query.replace(/--[^\r\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").trim(); }; var isReadOnlyQuery = (query) => { if (!query) return true; return SAFE_OPERATIONS.some((pattern) => pattern.test(query)); }; // src/recommendations.ts var generateRecommendations = (threats, isSelectOnly) => { const recommendations = []; if (isSelectOnly) { recommendations.push("✅ Query appears to be read-only and safe to execute"); return recommendations; } if (threats.length === 0) { recommendations.push("✅ No obvious security threats detected"); return recommendations; } const categories = [...new Set(threats.map((t) => t.category))]; categories.forEach((category) => { switch (category) { case "DML": recommendations.push("⚠️ Data modification detected - verify query intent and backup data if necessary"); break; case "DDL": recommendations.push("\uD83D\uDEA8 Structure modification detected - this could permanently alter your database schema"); break; case "DCL": recommendations.push("\uD83D\uDEA8 Permission changes detected - this could affect database security"); break; case "INJECTION": recommendations.push("\uD83D\uDEA8 CRITICAL: Potential SQL injection pattern detected - DO NOT EXECUTE"); break; case "ADMIN": recommendations.push("\uD83D\uDEA8 Administrative operation detected - requires elevated privileges"); break; case "SYSTEM": recommendations.push("\uD83D\uDEA8 CRITICAL: System-level operation detected - could compromise server security"); break; } }); const criticalThreats = threats.filter((t) => t.level === "critical" /* CRITICAL */); if (criticalThreats.length > 0) { recommendations.push("\uD83D\uDED1 STOP: Critical security threats detected - manual review required"); recommendations.push("\uD83D\uDCA1 Consider using parameterized queries or stored procedures instead"); } const highRiskThreats = threats.filter((t) => t.level === "high_risk" /* HIGH_RISK */); if (highRiskThreats.length > 0) { recommendations.push("⚠️ High-risk operations detected - ensure you have proper backups"); recommendations.push("\uD83D\uDCA1 Test on a development environment first"); } return recommendations; }; // src/analyzer.ts var analyzeQuerySecurity = (query) => { const cleanQuery = normalizeQuery(query); if (!cleanQuery) { return { securityLevel: "safe" /* SAFE */, isDangerous: false, isSelectOnly: true, threats: [], recommendations: ["Query is empty or contains only comments"], allowExecution: true }; } const threats = []; let highestSecurityLevel = "safe" /* SAFE */; SECURITY_THREATS.forEach((threat) => { const matches = cleanQuery.match(threat.pattern); if (matches) { threats.push({ name: threat.name, description: threat.description, level: threat.level, category: threat.category }); const levels = [ "safe" /* SAFE */, "low_risk" /* LOW_RISK */, "medium_risk" /* MEDIUM_RISK */, "high_risk" /* HIGH_RISK */, "critical" /* CRITICAL */ ]; const currentIndex = levels.indexOf(threat.level); const highestIndex = levels.indexOf(highestSecurityLevel); if (currentIndex > highestIndex) { highestSecurityLevel = threat.level; } } threat.pattern.lastIndex = 0; }); const isSelectOnly = isReadOnlyQuery(cleanQuery) && threats.length === 0; const isDangerous = threats.length > 0; const recommendations = generateRecommendations(threats, isSelectOnly); const allowExecution = highestSecurityLevel === "safe" /* SAFE */ || highestSecurityLevel === "low_risk" /* LOW_RISK */ || isSelectOnly; return { securityLevel: highestSecurityLevel, isDangerous, isSelectOnly, threats, recommendations, allowExecution }; }; var needsConfirmation = (query) => { const analysis = analyzeQuerySecurity(query); if (analysis.securityLevel === "safe" /* SAFE */ || analysis.isSelectOnly) { return { required: false, level: "safe" /* SAFE */, reason: "Query is safe to execute" }; } const levelMessages = { ["low_risk" /* LOW_RISK */]: "Query contains low-risk operations", ["medium_risk" /* MEDIUM_RISK */]: "Query contains data modification operations", ["high_risk" /* HIGH_RISK */]: "Query contains high-risk operations that could affect database structure", ["critical" /* CRITICAL */]: "Query contains critical security threats - execution not recommended" }; return { required: true, level: analysis.securityLevel, reason: levelMessages[analysis.securityLevel] || "Unknown security level" }; }; var validateQueryAgainstPolicy = (query, allowedOperations = [ "SELECT", "WITH", "EXPLAIN", "DESCRIBE", "SHOW" ]) => { const analysis = analyzeQuerySecurity(query); const violations = []; analysis.threats.forEach((threat) => { if (!allowedOperations.includes(threat.name)) { violations.push(`Operation '${threat.name}' is not allowed by current security policy`); } }); if (analysis.securityLevel === "critical" /* CRITICAL */) { violations.push("Critical security threats are not allowed"); } const injectionThreats = analysis.threats.filter((t) => t.category === "INJECTION"); if (injectionThreats.length > 0) { violations.push("SQL injection patterns are strictly forbidden"); } return { isValid: violations.length === 0, violations, analysis }; }; var isQuerySafe = (query) => { const analysis = analyzeQuerySecurity(query); return analysis.allowExecution; }; var getQuerySecuritySummary = (query) => { const analysis = analyzeQuerySecurity(query); if (analysis.isSelectOnly) { return "Safe read-only query"; } if (analysis.threats.length === 0) { return "No security threats detected"; } const threatCounts = analysis.threats.reduce((acc, threat) => { acc[threat.level] = (acc[threat.level] || 0) + 1; return acc; }, {}); const summaryParts = []; if (threatCounts["critical" /* CRITICAL */]) { summaryParts.push(`${threatCounts["critical" /* CRITICAL */]} critical threat(s)`); } if (threatCounts["high_risk" /* HIGH_RISK */]) { summaryParts.push(`${threatCounts["high_risk" /* HIGH_RISK */]} high-risk operation(s)`); } if (threatCounts["medium_risk" /* MEDIUM_RISK */]) { summaryParts.push(`${threatCounts["medium_risk" /* MEDIUM_RISK */]} medium-risk operation(s)`); } return summaryParts.join(", ") || "Low security risk"; }; // src/ui-helpers.ts var getSecurityLevelColor = (level) => { switch (level) { case "safe" /* SAFE */: return "#4caf50"; case "low_risk" /* LOW_RISK */: return "#8bc34a"; case "medium_risk" /* MEDIUM_RISK */: return "#ff9800"; case "high_risk" /* HIGH_RISK */: return "#f44336"; case "critical" /* CRITICAL */: return "#9c27b0"; default: return "#757575"; } }; // src/checker.ts class SQLQuerySafetyChecker { analyzeQuery(query) { return analyzeQuerySecurity(query); } checkQuerySafety(query) { const analysis = this.analyzeQuery(query); const dangerousOperations = analysis.threats.filter((threat) => threat.level === "high_risk" /* HIGH_RISK */ || threat.level === "critical" /* CRITICAL */).map((threat) => threat.name); return { isDangerous: analysis.isDangerous, dangerousOperations, isSelectOnly: analysis.isSelectOnly }; } isSelectOnlyQuery(query) { const analysis = this.analyzeQuery(query); return analysis.isSelectOnly; } requiresConfirmation(query) { return needsConfirmation(query); } validateAgainstPolicy(query, policy) { return validateQueryAgainstPolicy(query, policy.allowedOperations); } getSecuritySummary(query) { return getQuerySecuritySummary(query); } isSafe(query) { return isQuerySafe(query); } } var createSafetyChecker = () => { return new SQLQuerySafetyChecker; };