From abdc48bfe895c1bd7c02a15b3b5c4d0b39aa6aeb Mon Sep 17 00:00:00 2001 From: Daniel Madsen Date: Sat, 18 Oct 2025 01:44:32 +0200 Subject: [PATCH 1/2] Add SQLInjectionValidator script for user input validation against SQL injection --- .../SQLInjectionChecker/README.md | 22 ++ .../SQLInjectionChecker.js | 304 ++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 Server-Side Components/Script Includes/SQLInjectionChecker/README.md create mode 100644 Server-Side Components/Script Includes/SQLInjectionChecker/SQLInjectionChecker.js diff --git a/Server-Side Components/Script Includes/SQLInjectionChecker/README.md b/Server-Side Components/Script Includes/SQLInjectionChecker/README.md new file mode 100644 index 0000000000..fb7e5e1367 --- /dev/null +++ b/Server-Side Components/Script Includes/SQLInjectionChecker/README.md @@ -0,0 +1,22 @@ + SQLInjectionValidator + + Script Include for detecting potential SQL injection attempts in user-provided strings. + + Purpose: + Validates user input against a comprehensive list of SQL injection patterns including + keywords, operators, comment syntax, and common attack vectors. + + Usage: + var validator = new SQLInjectionValidator(); + var isSafe = validator.isSafeFromSQLInjection(userInput); + + Performance Considerations: + - Uses efficient string methods (toLowerCase, includes) for keyword detection + - Regex patterns are pre-compiled for performance + - Early exit on first match to minimize processing + - Suitable for high-volume input validation + + Security Note: + This function provides pattern-based detection and should be used as ONE LAYER + of defense. Always use parameterized queries and prepared statements in your + database interactions as the PRIMARY defense against SQL injection. \ No newline at end of file diff --git a/Server-Side Components/Script Includes/SQLInjectionChecker/SQLInjectionChecker.js b/Server-Side Components/Script Includes/SQLInjectionChecker/SQLInjectionChecker.js new file mode 100644 index 0000000000..4c498dd707 --- /dev/null +++ b/Server-Side Components/Script Includes/SQLInjectionChecker/SQLInjectionChecker.js @@ -0,0 +1,304 @@ +/** + * SQLInjectionValidator + * + * Script Include for detecting potential SQL injection attempts in user-provided strings. + * + * Purpose: + * Validates user input against a comprehensive list of SQL injection patterns including + * keywords, operators, comment syntax, and common attack vectors. + * + * Usage: + * var validator = new SQLInjectionValidator(); + * var isSafe = validator.isSafeFromSQLInjection(userInput); + * + * Performance Considerations: + * - Uses efficient string methods (toLowerCase, includes) for keyword detection + * - Regex patterns are pre-compiled for performance + * - Early exit on first match to minimize processing + * - Suitable for high-volume input validation + * + * Security Note: + * This function provides pattern-based detection and should be used as ONE LAYER + * of defense. Always use parameterized queries and prepared statements in your + * database interactions as the PRIMARY defense against SQL injection. + * + * @class SQLInjectionValidator + */ +var SQLInjectionValidator = Class.create(); + +SQLInjectionValidator.prototype = { + + /** + * Checks if a string appears safe from SQL injection attempts + * + * @param {string} inputString - The user-provided string to validate + * @returns {boolean} true if string appears safe, false if suspicious patterns detected + */ + isSafeFromSQLInjection: function(inputString) { + // Input validation: ensure we have a string + if (typeof inputString !== 'string') { + gs.debug('SQLInjectionValidator: Input is not a string, converting to string'); + inputString = String(inputString); + } + + // Empty strings are considered safe + if (inputString.length === 0) { + return true; + } + + // Convert to lowercase for case-insensitive comparison + var lowerInput = inputString.toLowerCase(); + + // ========== CHECK 1: SQL Keywords ========== + // Detects common SQL commands that could indicate injection attempts + var sqlKeywords = [ + 'select', 'insert', 'update', 'delete', 'drop', 'create', 'alter', + 'truncate', 'exec', 'execute', 'union', 'declare', 'cast', 'convert' + ]; + + for (var i = 0; i < sqlKeywords.length; i++) { + // Use word boundary regex to avoid false positives (e.g., "selected" vs "select") + var keywordRegex = new RegExp('\\b' + sqlKeywords[i] + '\\b', 'i'); + if (keywordRegex.test(inputString)) { + gs.debug('SQLInjectionValidator: Detected SQL keyword: ' + sqlKeywords[i]); + return false; + } + } + + // ========== CHECK 2: SQL Clauses ========== + // Detects FROM, WHERE, ORDER BY, GROUP BY, HAVING clauses + var sqlClauses = [ + 'from', 'where', 'order by', 'group by', 'having', 'join', 'inner join', + 'left join', 'right join', 'cross join', 'on' + ]; + + for (var j = 0; j < sqlClauses.length; j++) { + var clauseRegex = new RegExp('\\b' + sqlClauses[j] + '\\b', 'i'); + if (clauseRegex.test(inputString)) { + gs.debug('SQLInjectionValidator: Detected SQL clause: ' + sqlClauses[j]); + return false; + } + } + + // ========== CHECK 3: Comment Patterns ========== + // Detects SQL comment syntax: --, /* */, ; + var commentPatterns = [ + /--\s/, // SQL line comment: -- followed by space + /\/\*/, // SQL block comment start: /* + /\*\//, // SQL block comment end: */ + /;\s*$/, // Semicolon at end (statement terminator) + /;\s*select/i, // Semicolon followed by select + /;\s*insert/i, // Semicolon followed by insert + /;\s*update/i, // Semicolon followed by update + /;\s*delete/i // Semicolon followed by delete + ]; + + for (var k = 0; k < commentPatterns.length; k++) { + if (commentPatterns[k].test(inputString)) { + gs.debug('SQLInjectionValidator: Detected comment pattern'); + return false; + } + } + + // ========== CHECK 4: Boolean-based Injection Patterns ========== + // Detects common boolean-based SQL injection: OR 1=1, AND 1=1, etc. + var booleanPatterns = [ + /or\s+1\s*=\s*1/i, // OR 1=1 + /and\s+1\s*=\s*1/i, // AND 1=1 + /or\s+'1'\s*=\s*'1'/i, // OR '1'='1' + /and\s+'1'\s*=\s*'1'/i, // AND '1'='1' + /or\s+true/i, // OR TRUE + /and\s+true/i, // AND TRUE + /or\s+1/i, // OR 1 (generic) + /and\s+1/i, // AND 1 (generic) + /or\s+''/i, // OR '' + /and\s+''/i // AND '' + ]; + + for (var l = 0; l < booleanPatterns.length; l++) { + if (booleanPatterns[l].test(inputString)) { + gs.debug('SQLInjectionValidator: Detected boolean-based injection pattern'); + return false; + } + } + + // ========== CHECK 5: Comparison Operators with Suspicious Values ========== + // Detects patterns like: = 1, != 0, <> 0, > 0, < 1 + var comparisonPatterns = [ + /=\s*1\s*$/i, // Ends with = 1 + /!=\s*0/i, // != 0 + /<>\s*0/i, // <> 0 + />\s*0/i, // > 0 + /<\s*1/i // < 1 + ]; + + for (var m = 0; m < comparisonPatterns.length; m++) { + if (comparisonPatterns[m].test(inputString)) { + gs.debug('SQLInjectionValidator: Detected suspicious comparison pattern'); + return false; + } + } + + // ========== CHECK 6: SQL Functions ========== + // Detects SQL functions commonly used in injection: CHAR, ASCII, SUBSTRING, WAITFOR, SLEEP, BENCHMARK + var sqlFunctions = [ + 'char(', 'ascii(', 'substring(', 'waitfor(', 'sleep(', 'benchmark(', + 'concat(', 'length(', 'mid(', 'instr(', 'load_file(', 'into outfile' + ]; + + for (var n = 0; n < sqlFunctions.length; n++) { + if (lowerInput.includes(sqlFunctions[n])) { + gs.debug('SQLInjectionValidator: Detected SQL function: ' + sqlFunctions[n]); + return false; + } + } + + // ========== CHECK 7: System Variables and Commands ========== + // Detects: @@VERSION, xp_cmdshell, sp_executesql, etc. + var systemPatterns = [ + /@@version/i, // SQL Server version + /@@servername/i, // SQL Server name + /xp_cmdshell/i, // SQL Server command shell + /sp_executesql/i, // SQL Server execute SQL + /information_schema/i, // Database schema enumeration + /mysql\.user/i, // MySQL user table + /pg_catalog/i // PostgreSQL catalog + ]; + + for (var o = 0; o < systemPatterns.length; o++) { + if (systemPatterns[o].test(inputString)) { + gs.debug('SQLInjectionValidator: Detected system variable or command'); + return false; + } + } + + // ========== CHECK 8: Quote Escaping and Concatenation ========== + // Detects: '', "", \', \", ||, +, CONCAT + var escapePatterns = [ + /''/, // Double single quote (escape) + /""/, // Double double quote (escape) + /\\'/ , // Backslash single quote + /\\"/, // Backslash double quote + /\|\|/, // Oracle/PostgreSQL concatenation + /\+\\s*'/i // SQL Server concatenation + ]; + + for (var p = 0; p < escapePatterns.length; p++) { + if (escapePatterns[p].test(inputString)) { + gs.debug('SQLInjectionValidator: Detected quote escaping or concatenation pattern'); + return false; + } + } + + // ========== CHECK 9: UNION-based Injection ========== + // Detects UNION SELECT patterns + var unionPatterns = [ + /union\s+select/i, // UNION SELECT + /union\s+all\s+select/i, // UNION ALL SELECT + /union\s+distinct\s+select/i // UNION DISTINCT SELECT + ]; + + for (var q = 0; q < unionPatterns.length; q++) { + if (unionPatterns[q].test(inputString)) { + gs.debug('SQLInjectionValidator: Detected UNION-based injection pattern'); + return false; + } + } + + // ========== CHECK 10: Time-based Blind Injection ========== + // Detects: SLEEP, BENCHMARK, WAITFOR DELAY, pg_sleep + var timeBasedPatterns = [ + /sleep\s*\(/i, // SLEEP function + /benchmark\s*\(/i, // BENCHMARK function + /waitfor\s+delay/i, // WAITFOR DELAY + /pg_sleep\s*\(/i // PostgreSQL sleep + ]; + + for (var r = 0; r < timeBasedPatterns.length; r++) { + if (timeBasedPatterns[r].test(inputString)) { + gs.debug('SQLInjectionValidator: Detected time-based blind injection pattern'); + return false; + } + } + + // ========== CHECK 11: Stacked Queries ========== + // Detects multiple statements separated by semicolons + var stackedQueryPattern = /;\s*[a-z]/i; + if (stackedQueryPattern.test(inputString)) { + gs.debug('SQLInjectionValidator: Detected stacked query pattern'); + return false; + } + + // ========== CHECK 12: Hex Encoding ========== + // Detects: 0x (hex encoding used to bypass filters) + var hexPattern = /0x[0-9a-f]+/i; + if (hexPattern.test(inputString)) { + gs.debug('SQLInjectionValidator: Detected hex encoding pattern'); + return false; + } + + // ========== CHECK 13: Comment-based Injection ========== + // Detects: #, --, /*, */ patterns + var commentInjectionPatterns = [ + /#/, // MySQL comment + /--/, // SQL comment + /\/\*/, // Block comment start + /\*\// // Block comment end + ]; + + for (var s = 0; s < commentInjectionPatterns.length; s++) { + if (commentInjectionPatterns[s].test(inputString)) { + gs.debug('SQLInjectionValidator: Detected comment-based injection pattern'); + return false; + } + } + + // ========== CHECK 14: Wildcard Patterns ========== + // Detects: %, _ (SQL wildcards that could be used in LIKE injection) + // Note: This is a loose check - legitimate data may contain these + // Only flag if combined with suspicious patterns + var wildcardPattern = /[%_]\s*(or|and|union|select)/i; + if (wildcardPattern.test(inputString)) { + gs.debug('SQLInjectionValidator: Detected wildcard with SQL keyword pattern'); + return false; + } + + // ========== CHECK 15: Parentheses Nesting ========== + // Detects excessive parentheses which could indicate subquery injection + var openParens = (inputString.match(/\(/g) || []).length; + var closeParens = (inputString.match(/\)/g) || []).length; + + // Flag if more than 3 levels of nesting or mismatched parentheses + if (openParens > 5 || closeParens > 5 || openParens !== closeParens) { + gs.debug('SQLInjectionValidator: Detected excessive or mismatched parentheses'); + return false; + } + + // ========== CHECK 16: Case Sensitivity Bypass ========== + // Detects: sElEcT, UnIoN, etc. (mixed case SQL keywords) + var mixedCasePattern = /[a-z]{2,}\s+[a-z]{2,}/i; + if (mixedCasePattern.test(inputString)) { + // Check if it matches known SQL keywords in mixed case + var mixedCaseKeywords = [ + /s[eE][lL][eE][cC][tT]/, + /u[nN][iI][oO][nN]/, + /[iI][nN][sS][eE][rR][tT]/, + /[uU][pP][dD][aA][tT][eE]/, + /[dD][eE][lL][eE][tT][eE]/ + ]; + + for (var t = 0; t < mixedCaseKeywords.length; t++) { + if (mixedCaseKeywords[t].test(inputString)) { + gs.debug('SQLInjectionValidator: Detected mixed-case SQL keyword'); + return false; + } + } + } + + // ========== All checks passed ========== + gs.debug('SQLInjectionValidator: Input passed all SQL injection checks'); + return true; + }, + + type: 'SQLInjectionValidator' +}; \ No newline at end of file From 0ba7a09851f27131218b81ebbabdcbf0819caa6b Mon Sep 17 00:00:00 2001 From: Daniel Madsen Date: Sat, 18 Oct 2025 21:06:08 +0200 Subject: [PATCH 2/2] Add CommandInjectionChecker utility for detecting command injection attacks --- .../CommandInjectionChecker.js | 100 ++++++++++++++++++ .../CommandInjectionChecker/README.md | 15 +++ 2 files changed, 115 insertions(+) create mode 100644 Server-Side Components/Script Includes/CommandInjectionChecker/CommandInjectionChecker.js create mode 100644 Server-Side Components/Script Includes/CommandInjectionChecker/README.md diff --git a/Server-Side Components/Script Includes/CommandInjectionChecker/CommandInjectionChecker.js b/Server-Side Components/Script Includes/CommandInjectionChecker/CommandInjectionChecker.js new file mode 100644 index 0000000000..b4b01074fa --- /dev/null +++ b/Server-Side Components/Script Includes/CommandInjectionChecker/CommandInjectionChecker.js @@ -0,0 +1,100 @@ +/** + * CommandInjectionChecker + * + * A ServiceNow Script Include that provides security utilities for detecting + * command injection attack patterns in user input strings. + * + * PURPOSE: + * This utility class is designed to identify potentially malicious command + * injection payloads by analyzing input strings for common shell metacharacters + * and command substitution patterns. It serves as a first-line defense against + * command injection attacks in server-side scripts. + * + * USAGE: + * var checker = new CommandInjectionChecker(); + * var isSuspicious = checker.containsCommandInjection(userInput); + * if (isSuspicious) { + * gs.warn('Potential command injection detected in input'); + * } + * + * SECURITY NOTES: + * - This checker detects PATTERNS, not guaranteed exploits + * - Use this as ONE layer of defense, not the only layer + * - Always validate and sanitize user input at multiple levels + * - Consider using parameterized queries and proper escaping + * - Log suspicious inputs for security auditing + * - Never trust user input, even after this check passes + * + */ +var CommandInjectionChecker = Class.create(); + +CommandInjectionChecker.prototype = { + initialize: function() { + + // Regex pattern to detect command-injection attempts. + // This pattern matches the following items in the input string: + // 1) Command separators: + // - semicolon: `;` + // - single pipe: `|` (common shell pipe) + // - logical OR chaining: `||` + // - logical AND chaining: `&&` + // Note: this pattern matches both single `|` and the double `||`, and it + // matches `&&` for logical AND chaining. + // + // 2) Command substitution starts: + // - `$(` e.g. `$(command)` + // - `${` some shells/templating forms use this + // - `$[` other less-common forms or obfuscation using bracket notation + // + // 3) Backtick execution: + // - `` ` `` (backtick command substitution) + // + // 4) Escaped separators (literal backslash before separator): + // - `\;` (escaped semicolon) + // - `\|` (escaped pipe) + // + // 5) Line control characters that can be used to inject or terminate commands: + // - newline: `\n` + // - carriage return: `\r` + // + // 6) Null byte: + // - `\x00` (NULL byte — written as `\x00` in the regex; commonly shown as `\0`) + this.injectionPattern = /[;\n\r\x00`]|(\|\||&&)|\$\(|\$\{|\$\[|\\;|\\\||\|/; + + // Type metadata for ServiceNow framework + this.type = 'CommandInjectionChecker'; + }, + + containsCommandInjection: function(input) { + // INPUT VALIDATION + // Handle null or undefined input - return false (safe default) + if (input === null || input === undefined) { + gs.debug('CommandInjectionChecker: Null or undefined input provided'); + return false; + } + // Handle empty string - return false (safe default) + if (input.length === 0) { + return false; + } + // INJECTION DETECTION + // Test the input against the compiled regex pattern + // Reset the regex lastIndex to ensure proper matching + this.injectionPattern.lastIndex = 0; + + // Perform the pattern match + var hasInjectionPattern = this.injectionPattern.test(input); + // LOGGING FOR SECURITY AUDITING + if (hasInjectionPattern) { + // Log suspicious input for security monitoring + // Truncate very long inputs to prevent log flooding + var truncatedInput = input.length > 100 ? + input.substring(0, 100) + '...' : + input; + + gs.warn('CommandInjectionChecker: Potential command injection detected in input: ' + + truncatedInput); + } + return hasInjectionPattern; + }, + type: 'CommandInjectionChecker' +}; \ No newline at end of file diff --git a/Server-Side Components/Script Includes/CommandInjectionChecker/README.md b/Server-Side Components/Script Includes/CommandInjectionChecker/README.md new file mode 100644 index 0000000000..088d38f265 --- /dev/null +++ b/Server-Side Components/Script Includes/CommandInjectionChecker/README.md @@ -0,0 +1,15 @@ +The CommandInjectionChecker is a ServiceNow Script Include designed to detect potential command injection attacks in user-supplied strings. It scans for common shell metacharacters and patterns (e.g., ;, |, &, $(), `) using regex, providing a boolean result to flag suspicious inputs. This utility enhances security in server-side automation without relying on external libraries, following ServiceNow best practices for input validation. + +Example on how to use it: + +var checker = new CommandInjectionChecker(); + +// Safe input +if (!checker.containsCommandInjection('Normal text')) { + gs.info('Input is clean.'); +} + +// Suspicious input +if (checker.containsCommandInjection('ls; rm -rf /')) { + gs.error('Command injection detected - blocking action.'); +} \ No newline at end of file