diff --git a/tools/toolkit-qa-check.sh b/tools/toolkit-qa-check.sh index e789fed..429c860 100755 --- a/tools/toolkit-qa-check.sh +++ b/tools/toolkit-qa-check.sh @@ -154,6 +154,86 @@ should_skip_check() { return 1 # Don't skip } +#============================================================================== +# LOGIC ANALYSIS HELPERS for new checks (89-94) +#============================================================================== + +# Helper: Detect contradictory grep patterns in the same command chain +# Detects patterns like: grep -v pattern | grep pattern (always returns empty) +# qa-suppress:grep-contradict +detect_grep_contradiction() { + local line_content="$1" + + # qa-suppress:grep-contradict + # Check for grep -v followed by grep looking for same/similar pattern + if echo "$line_content" | grep -qE 'grep.*-[vi].*[^a-zA-Z0-9_]\|.*grep.*[^a-zA-Z0-9_]'; then + # qa-suppress:grep-contradict + # More specific: grep -v X | grep X or grep -v "pattern" | grep "pattern" + if echo "$line_content" | grep -qE 'grep.*-v\s+"?([^"]+)"?.*\|.*grep[^-]*([^a-zA-Z0-9_|]|\1)'; then + return 0 # Found contradiction + fi + fi + + return 1 +} + +# Helper: Check if variable is used in numeric context +# Returns 0 if variable appears to be used numerically, 1 if string context +infer_numeric_context() { + local var_name="$1" + local file="$2" + local line_num="$3" + + # Get context around the variable + local context=$(sed -n "$((line_num-2)),$((line_num+2))p" "$file" 2>/dev/null) + + # Check for numeric operators/comparisons + if echo "$context" | grep -qE "\[\s*\\\$?${var_name}[[:space:]]+-[lg][te]|${var_name}[[:space:]]*-[lg][te]|\${${var_name}[%#/:-]|}|\(\(\s*\${?${var_name}"; then + return 0 # Numeric context + fi + + return 1 # String context +} + +# Helper: Extract variable definitions from a function +# Returns list of variables defined (without $ prefix) +get_function_vars() { + local func_start="$1" + local func_end="$2" + local file="$3" + + sed -n "${func_start},${func_end}p" "$file" 2>/dev/null | \ + grep -oE '(local\s+|[a-zA-Z_][a-zA-Z0-9_]*\s*=)' | \ + sed -E 's/(local\s+|=)//g' | \ + sort -u +} + +# Helper: Check if variable is initialized before use +# Returns 0 if found uninitialized, 1 if properly initialized +check_awk_var_init() { + local awk_block="$1" + local var_name="$2" + + # Check if variable appears in BEGIN block (initialization) + if echo "$awk_block" | grep -qE 'BEGIN\s*\{[^}]*'"${var_name}"'\s*='; then + return 1 # Initialized in BEGIN + fi + + # Check if variable is set before first use in main block + local first_use=$(echo "$awk_block" | grep -n "$var_name" | head -1 | cut -d: -f1) + local first_set=$(echo "$awk_block" | grep -n "${var_name}\s*=" | head -1 | cut -d: -f1) + + if [ -z "$first_use" ]; then + return 1 # Variable not used + fi + + if [ -z "$first_set" ] || [ "$first_use" -lt "$first_set" ]; then + return 0 # Used before set (uninitialized) + fi + + return 1 # Properly initialized +} + echo "═══════════════════════════════════════════════════════════════" echo "SERVER TOOLKIT QA SCAN - PHASE 3" echo "Path: $TOOLKIT_PATH" @@ -3200,6 +3280,198 @@ done < <(grep -n "case.*CONTROL_PANEL" "$TOOLKIT_PATH" --include="*.sh" -r 2>/de echo "Found: $count case statements missing standalone support" echo "" } >> "$REPORT" + +#============================================================================== +# CHECK 89: Inverted/Contradictory Grep Patterns (CRITICAL) +#============================================================================== +show_progress 89 "Inverted/contradictory grep patterns" +{ +echo "## CHECK 89: Inverted/Contradictory Grep Patterns" +echo "Severity: CRITICAL" +echo "Pattern: grep -v X | grep ... (filters out then filters for, contradictory logic)" +echo "Impact: Logic error - filtering out pattern and then filtering for same pattern" +echo "" + +count=0 +# Look for common contradictory patterns without variable substitution in regex +while IFS=: read -r file line_num line_content; do + # qa-suppress:grep-contradict + # Simple detection: grep -v ... | grep (most likely contradictory) + # qa-suppress:grep-contradict + # Only flag if it looks suspicious (v flag before pipe, then grep after) + if echo "$line_content" | grep -qE 'grep.*-[viE].*\|.*grep' && \ + ! echo "$line_content" | grep -qE '\|\s*(sed|awk|cut|sort|uniq)'; then + # Likely a contradiction unless it's followed by sed/awk/cut which changes things + # qa-suppress:grep-contradict + # Check if it's not a false positive (e.g., grep -v comment | grep -c pattern) + if ! echo "$line_content" | grep -qE '(grep.*-c|grep.*-q|head|tail)'; then + if ! is_suppressed "$file" "$line_num" "grep-contradict"; then + echo "CRITICAL|$file|$line_num|[LOGIC-ERR] Contradictory grep: -v pipe followed by grep (may filter nothing)" + count_issue "CRITICAL" + ((count++)) + [ "$count" -ge 15 ] && break + fi + fi + fi +done < <(grep -rn 'grep.*-v.*|.*grep' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null) + +echo "Found: $count contradictory grep patterns" +echo "" +} >> "$REPORT" + +#============================================================================== +# CHECK 90: Type Mismatch in Comparisons (HIGH) +#============================================================================== +show_progress 90 "Type mismatch in comparisons" +{ +echo "## CHECK 90: Type Mismatch in Comparisons" +echo "Severity: HIGH" +echo "Pattern: Numeric operator (-eq/-lt/-gt) on variables containing non-numeric values" +echo "Examples: [ \$rate -lt 80 ] where rate contains '%', [ \$status -eq 0 ] where status is string" +echo "" + +count=0 +# Pattern 1: Variables with % character used in numeric comparison +while IFS=: read -r file line_num line_content; do + if echo "$line_content" | grep -qE '\$[a-zA-Z_][a-zA-Z0-9_%]*.*-[lg][te]|rate.*%.*-[lg][te]'; then + if ! is_suppressed "$file" "$line_num" "type-mismatch"; then + echo "HIGH|$file|$line_num|[TYPE-MISMATCH] Numeric operator on variable that may contain non-numeric value" + count_issue "HIGH" + ((count++)) + [ "$count" -ge 15 ] && break + fi + fi +done < <(grep -rn -E '\[\s*\$[a-zA-Z_].*-[lg][te].*[0-9]|rate.*[%].*-[lg][te]' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null) + +echo "Found: $count type mismatches" +echo "" +} >> "$REPORT" + +#============================================================================== +# CHECK 91: Command Argument Ordering Errors (HIGH) +#============================================================================== +show_progress 91 "Command argument ordering errors" +{ +echo "## CHECK 91: Command Argument Ordering Errors" +echo "Severity: HIGH" +echo "Pattern: Filename variable before options in grep/sed (grep \$FILE -e PATTERN)" +echo "Impact: Command fails or behaves unexpectedly - filename treated as pattern" +echo "" + +count=0 +# Check for grep with filename variable followed by option flags +while IFS=: read -r file line_num line_content; do + if echo "$line_content" | grep -qE 'grep\s+\$[A-Z_].*\s+-[eEiIvlLrnhFxaw]|sed\s+\$[A-Z_].*\s+-[es]'; then + if ! is_suppressed "$file" "$line_num" "arg-order"; then + echo "HIGH|$file|$line_num|[ARG-ORDER] Command: filename variable before option flags" + count_issue "HIGH" + ((count++)) + [ "$count" -ge 15 ] && break + fi + fi +done < <(grep -rn 'grep.*\$[A-Z_].*-[eEiIvlLrnhFxaw]\|sed.*\$[A-Z_].*-[es]' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null) + +echo "Found: $count argument ordering errors" +echo "" +} >> "$REPORT" + +#============================================================================== +# CHECK 92: Missing Command Availability Checks (HIGH) +#============================================================================== +show_progress 92 "Missing command availability checks" +{ +echo "## CHECK 92: Missing Command Availability Checks" +echo "Severity: HIGH" +echo "Pattern: Uses optional command without checking availability (nc, dig, host, jq, etc)" +echo "Impact: Script fails on systems where command not installed" +echo "" + +count=0 +# Common commands that should be checked for availability +while IFS=: read -r file line_num line_content; do + # Check if line uses an optional command + if echo "$line_content" | grep -qE '\b(nc|dig|host|jq|yq|envsubst|getent|timeout)\b'; then + # Verify it's not a comment or already has a check + if ! echo "$line_content" | grep -qE 'command -v|which|#.*qa-suppress'; then + if ! is_suppressed "$file" "$line_num" "no-cmd-check"; then + echo "HIGH|$file|$line_num|[NO-CMD-CHECK] Optional command used without availability check" + count_issue "HIGH" + ((count++)) + [ "$count" -ge 15 ] && break + fi + fi + fi +done < <(grep -rn '\b(nc|dig|host|jq|yq|envsubst|timeout)\s' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null) + +echo "Found: $count missing command checks" +echo "" +} >> "$REPORT" + +#============================================================================== +# CHECK 93: Uninitialized Variables in AWK (HIGH) +#============================================================================== +show_progress 93 "Uninitialized AWK variables" +{ +echo "## CHECK 93: Uninitialized Variables in AWK" +echo "Severity: HIGH" +echo "Pattern: AWK variables set in pattern but not initialized in BEGIN" +echo "Impact: Undefined behavior on non-matching lines, logic errors" +echo "" + +count=0 +# Look for AWK blocks that have assignments but no BEGIN block +while IFS=: read -r file line_num; do + awk_line=$(sed -n "${line_num}p" "$file" 2>/dev/null) + + # Check if AWK block has pattern actions with variable assignments + if echo "$awk_line" | grep -qE '{\s*[a-z_]+\s*=' && \ + ! echo "$awk_line" | grep -qE 'BEGIN\s*\{'; then + if ! is_suppressed "$file" "$line_num" "awk-uninit"; then + echo "HIGH|$file|$line_num|[AWK-UNINIT] AWK variables assigned without BEGIN initialization" + count_issue "HIGH" + ((count++)) + [ "$count" -ge 15 ] && break + fi + fi +done < <(grep -rn "awk\s*'" "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null | cut -d: -f1,2) + +echo "Found: $count AWK uninitialized variable issues" +echo "" +} >> "$REPORT" + +#============================================================================== +# CHECK 94: Undefined Variable References (HIGH) +#============================================================================== +show_progress 94 "Undefined variable references" +{ +echo "## CHECK 94: Undefined Variable References" +echo "Severity: HIGH" +echo "Pattern: Uses of variables that appear undefined (typos, scope issues)" +echo "Examples: \$TEMP_LOG (should be \$MAIL_LOG), undefined in subshells" +echo "" + +count=0 +# Look for common undefined variable patterns +while IFS=: read -r file line_num line_content; do + # Check for obvious typos/undefined variables + # Look for TEMP_* variables that might be undefined + if echo "$line_content" | grep -qE '\$TEMP_[A-Z_]+|\$[A-Z_]*LOG[A-Z_]*|\$[A-Z_]*FILE'; then + # Check if these are actually defined in the file + if ! grep -qE "^[[:space:]]*(TEMP_|declare TEMP_|local TEMP_)" "$file" 2>/dev/null; then + if ! is_suppressed "$file" "$line_num" "undef-var"; then + echo "HIGH|$file|$line_num|[UNDEF-VAR] Variable reference appears undefined in this file" + count_issue "HIGH" + ((count++)) + [ "$count" -ge 15 ] && break + fi + fi + fi +done < <(grep -rn '\$TEMP_\|TEMP_LOG\|\$[A-Z_]*FILE' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null) + +echo "Found: $count undefined variable references" +echo "" +} >> "$REPORT" + #============================================================================== # PERFORMANCE CHECKS (INFO level - not counted as issues) #==============================================================================