sql-query-safety-checker
Version:
🛡️ Comprehensive TypeScript library for SQL query security analysis with injection detection, risk assessment, and Express middleware integration
422 lines (421 loc) • 13.4 kB
JavaScript
// 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;
};
export {
validateQueryAgainstPolicy,
normalizeQuery,
needsConfirmation,
isReadOnlyQuery,
isQuerySafe,
getSecurityLevelColor,
getQuerySecuritySummary,
generateRecommendations,
createSafetyChecker,
analyzeQuerySecurity,
SecurityLevel,
SQLQuerySafetyChecker,
SECURITY_THREATS,
SAFE_OPERATIONS
};