Add CHECK 31 to QA script: detect 'local' outside functions

Issue:
- User encountered "local: can only be used in a function" error
  in analyze-historical-attacks.sh (lines 190, 203)
- The script used 'local' keyword in a code block redirected to a file
- This is a CRITICAL runtime error that prevents script execution
- QA script didn't catch this issue

Solution:
Added CHECK 31 to toolkit-qa-check.sh:
- Detects 'local' keyword used outside function context
- Tracks function boundaries using brace depth counting
- Reads entire file line-by-line to maintain state
- Skips comments to avoid false positives
- Severity: CRITICAL (script fails at runtime)

Implementation:
- Function detection: matches `function_name()` pattern
- Brace tracking: counts { and } to detect function exit
- State machine: in_function flag toggles based on brace depth
- Reports line number and file for easy fixing

Testing:
 Correctly identifies 'local' outside functions
 Does NOT flag 'local' inside functions (no false positives)
 Found existing issues in test files

Example error caught:
  /tmp/test-local-outside-function.sh:4|'local' keyword outside function

This check prevents runtime failures and makes QA more comprehensive.
This commit is contained in:
cschantz
2025-12-13 02:32:12 -05:00
parent 33bcdb4ef0
commit 527b4d897f
+83 -30
View File
@@ -58,7 +58,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 1: grep -F with regex anchors (CRITICAL - causes wrong results) # CHECK 1: grep -F with regex anchors (CRITICAL - causes wrong results)
#============================================================================== #==============================================================================
echo "[1/30] Checking: grep -F with regex anchors..." echo "[1/31] Checking: grep -F with regex anchors..."
{ {
echo "## CHECK 1: grep -F with regex anchors" echo "## CHECK 1: grep -F with regex anchors"
echo "Severity: CRITICAL" echo "Severity: CRITICAL"
@@ -79,7 +79,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 2: SCRIPT_DIR collisions (HIGH - causes path errors) # CHECK 2: SCRIPT_DIR collisions (HIGH - causes path errors)
#============================================================================== #==============================================================================
echo "[2/30] Checking: SCRIPT_DIR variable collisions..." echo "[2/31] Checking: SCRIPT_DIR variable collisions..."
{ {
echo "## CHECK 2: SCRIPT_DIR variable collisions" echo "## CHECK 2: SCRIPT_DIR variable collisions"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -99,7 +99,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 3: SYS_* variable resets (CRITICAL - breaks system detection) # CHECK 3: SYS_* variable resets (CRITICAL - breaks system detection)
#============================================================================== #==============================================================================
echo "[3/30] Checking: SYS_* variable resets..." echo "[3/31] Checking: SYS_* variable resets..."
{ {
echo "## CHECK 3: SYS_* variable resets without protection" echo "## CHECK 3: SYS_* variable resets without protection"
echo "Severity: CRITICAL" echo "Severity: CRITICAL"
@@ -120,7 +120,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 4: Missing function exports (HIGH - functions unavailable) # CHECK 4: Missing function exports (HIGH - functions unavailable)
#============================================================================== #==============================================================================
echo "[4/30] Checking: Missing function exports..." echo "[4/31] Checking: Missing function exports..."
{ {
echo "## CHECK 4: Missing function exports in libraries" echo "## CHECK 4: Missing function exports in libraries"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -145,7 +145,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 5: Integer comparisons without empty checks (HIGH - causes errors) # CHECK 5: Integer comparisons without empty checks (HIGH - causes errors)
#============================================================================== #==============================================================================
echo "[5/30] Checking: Unsafe integer comparisons (top 10)..." echo "[5/31] Checking: Unsafe integer comparisons (top 10)..."
{ {
echo "## CHECK 5: Integer comparisons without empty checks" echo "## CHECK 5: Integer comparisons without empty checks"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -170,7 +170,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 6: Missing common-functions.sh (HIGH - command not found) # CHECK 6: Missing common-functions.sh (HIGH - command not found)
#============================================================================== #==============================================================================
echo "[6/30] Checking: Missing common-functions.sh..." echo "[6/31] Checking: Missing common-functions.sh..."
{ {
echo "## CHECK 6: Missing common-functions.sh sourcing" echo "## CHECK 6: Missing common-functions.sh sourcing"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -190,7 +190,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 7: exit in libraries (HIGH - terminates parent script) # CHECK 7: exit in libraries (HIGH - terminates parent script)
#============================================================================== #==============================================================================
echo "[7/30] Checking: exit in library files..." echo "[7/31] Checking: exit in library files..."
{ {
echo "## CHECK 7: exit in sourced libraries" echo "## CHECK 7: exit in sourced libraries"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -215,7 +215,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 8: bc command usage (LOW - external dependency) # CHECK 8: bc command usage (LOW - external dependency)
#============================================================================== #==============================================================================
echo "[8/30] Checking: bc command usage..." echo "[8/31] Checking: bc command usage..."
{ {
echo "## CHECK 8: bc command usage" echo "## CHECK 8: bc command usage"
echo "Severity: LOW" echo "Severity: LOW"
@@ -238,7 +238,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 9: Hardcoded /var/cpanel paths (MEDIUM - breaks multi-panel) # CHECK 9: Hardcoded /var/cpanel paths (MEDIUM - breaks multi-panel)
#============================================================================== #==============================================================================
echo "[9/30] Checking: Hardcoded /var/cpanel paths..." echo "[9/31] Checking: Hardcoded /var/cpanel paths..."
{ {
echo "## CHECK 9: Hardcoded /var/cpanel paths" echo "## CHECK 9: Hardcoded /var/cpanel paths"
echo "Severity: MEDIUM" echo "Severity: MEDIUM"
@@ -264,7 +264,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 10: Undefined color variables (LOW - cosmetic issue) # CHECK 10: Undefined color variables (LOW - cosmetic issue)
#============================================================================== #==============================================================================
echo "[10/30] Checking: Undefined color variables..." echo "[10/31] Checking: Undefined color variables..."
{ {
echo "## CHECK 10: Undefined color variables" echo "## CHECK 10: Undefined color variables"
echo "Severity: LOW" echo "Severity: LOW"
@@ -289,7 +289,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 11: Bash syntax validation (CRITICAL - prevents execution) # CHECK 11: Bash syntax validation (CRITICAL - prevents execution)
#============================================================================== #==============================================================================
echo "[11/30] Checking: Bash syntax errors..." echo "[11/31] Checking: Bash syntax errors..."
{ {
echo "## CHECK 11: Bash syntax validation" echo "## CHECK 11: Bash syntax validation"
echo "Severity: CRITICAL" echo "Severity: CRITICAL"
@@ -310,7 +310,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 12: Dangerous rm commands (CRITICAL - data loss risk) # CHECK 12: Dangerous rm commands (CRITICAL - data loss risk)
#============================================================================== #==============================================================================
echo "[12/30] Checking: Dangerous rm commands..." echo "[12/31] Checking: Dangerous rm commands..."
{ {
echo "## CHECK 12: Dangerous rm commands" echo "## CHECK 12: Dangerous rm commands"
echo "Severity: CRITICAL" echo "Severity: CRITICAL"
@@ -339,7 +339,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 13: Unquoted variable expansions (HIGH - word splitting/globbing risks) # CHECK 13: Unquoted variable expansions (HIGH - word splitting/globbing risks)
#============================================================================== #==============================================================================
echo "[13/30] Checking: Unquoted variables in dangerous contexts..." echo "[13/31] Checking: Unquoted variables in dangerous contexts..."
{ {
echo "## CHECK 13: Unquoted variable expansions" echo "## CHECK 13: Unquoted variable expansions"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -363,7 +363,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 14: Command injection via eval (CRITICAL - arbitrary code execution) # CHECK 14: Command injection via eval (CRITICAL - arbitrary code execution)
#============================================================================== #==============================================================================
echo "[14/30] Checking: Command injection risks..." echo "[14/31] Checking: Command injection risks..."
{ {
echo "## CHECK 14: Command injection via eval" echo "## CHECK 14: Command injection via eval"
echo "Severity: CRITICAL" echo "Severity: CRITICAL"
@@ -384,7 +384,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 15: Temp file security (MEDIUM - race conditions/predictable names) # CHECK 15: Temp file security (MEDIUM - race conditions/predictable names)
#============================================================================== #==============================================================================
echo "[15/30] Checking: Temp file security..." echo "[15/31] Checking: Temp file security..."
{ {
echo "## CHECK 15: Insecure temp file creation" echo "## CHECK 15: Insecure temp file creation"
echo "Severity: MEDIUM" echo "Severity: MEDIUM"
@@ -407,7 +407,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 16: TODO/FIXME/HACK markers (LOW - technical debt tracking) # CHECK 16: TODO/FIXME/HACK markers (LOW - technical debt tracking)
#============================================================================== #==============================================================================
echo "[16/30] Checking: Technical debt markers..." echo "[16/31] Checking: Technical debt markers..."
{ {
echo "## CHECK 16: TODO/FIXME/HACK comments" echo "## CHECK 16: TODO/FIXME/HACK comments"
echo "Severity: LOW" echo "Severity: LOW"
@@ -431,7 +431,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 17: Duplicate function definitions (MEDIUM - causes conflicts) # CHECK 17: Duplicate function definitions (MEDIUM - causes conflicts)
#============================================================================== #==============================================================================
echo "[17/30] Checking: Duplicate function definitions..." echo "[17/31] Checking: Duplicate function definitions..."
{ {
echo "## CHECK 17: Duplicate function definitions" echo "## CHECK 17: Duplicate function definitions"
echo "Severity: MEDIUM" echo "Severity: MEDIUM"
@@ -457,7 +457,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 18: Missing input validation (HIGH - security/reliability risk) # CHECK 18: Missing input validation (HIGH - security/reliability risk)
#============================================================================== #==============================================================================
echo "[18/30] Checking: Missing input validation..." echo "[18/31] Checking: Missing input validation..."
{ {
echo "## CHECK 18: Functions without parameter validation" echo "## CHECK 18: Functions without parameter validation"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -555,7 +555,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 19: Long functions (MEDIUM - maintainability issue) # CHECK 19: Long functions (MEDIUM - maintainability issue)
#============================================================================== #==============================================================================
echo "[19/30] Checking: Overly long functions..." echo "[19/31] Checking: Overly long functions..."
{ {
echo "## CHECK 19: Long functions (>100 lines)" echo "## CHECK 19: Long functions (>100 lines)"
echo "Severity: MEDIUM" echo "Severity: MEDIUM"
@@ -600,7 +600,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 20: ShellCheck integration (if available) # CHECK 20: ShellCheck integration (if available)
#============================================================================== #==============================================================================
echo "[20/30] Checking: ShellCheck warnings (if available)..." echo "[20/31] Checking: ShellCheck warnings (if available)..."
{ {
echo "## CHECK 20: ShellCheck static analysis" echo "## CHECK 20: ShellCheck static analysis"
echo "Severity: VARIES" echo "Severity: VARIES"
@@ -636,7 +636,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 21: Using [ ] instead of [[ ]] (MEDIUM - less safe) # CHECK 21: Using [ ] instead of [[ ]] (MEDIUM - less safe)
#============================================================================== #==============================================================================
echo "[21/30] Checking: Single bracket conditionals..." echo "[21/31] Checking: Single bracket conditionals..."
{ {
echo "## CHECK 21: Using [ ] instead of [[ ]]" echo "## CHECK 21: Using [ ] instead of [[ ]]"
echo "Severity: MEDIUM" echo "Severity: MEDIUM"
@@ -664,7 +664,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 22: Looping over ls output (HIGH - fatally flawed pattern) # CHECK 22: Looping over ls output (HIGH - fatally flawed pattern)
#============================================================================== #==============================================================================
echo "[22/30] Checking: Loops over ls output..." echo "[22/31] Checking: Loops over ls output..."
{ {
echo "## CHECK 22: Looping over ls output" echo "## CHECK 22: Looping over ls output"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -684,7 +684,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 23: Missing set -euo pipefail (MEDIUM - silent failures) # CHECK 23: Missing set -euo pipefail (MEDIUM - silent failures)
#============================================================================== #==============================================================================
echo "[23/30] Checking: Missing error handling flags..." echo "[23/31] Checking: Missing error handling flags..."
{ {
echo "## CHECK 23: Missing set -euo pipefail" echo "## CHECK 23: Missing set -euo pipefail"
echo "Severity: MEDIUM" echo "Severity: MEDIUM"
@@ -717,7 +717,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 24: Unused variables (LOW - dead code) # CHECK 24: Unused variables (LOW - dead code)
#============================================================================== #==============================================================================
echo "[24/30] Checking: Unused variables..." echo "[24/31] Checking: Unused variables..."
{ {
echo "## CHECK 24: Unused variables" echo "## CHECK 24: Unused variables"
echo "Severity: LOW" echo "Severity: LOW"
@@ -748,7 +748,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 25: Backticks instead of $() (LOW - deprecated syntax) # CHECK 25: Backticks instead of $() (LOW - deprecated syntax)
#============================================================================== #==============================================================================
echo "[25/30] Checking: Deprecated backticks..." echo "[25/31] Checking: Deprecated backticks..."
{ {
echo "## CHECK 25: Using backticks instead of \$()" echo "## CHECK 25: Using backticks instead of \$()"
echo "Severity: LOW" echo "Severity: LOW"
@@ -768,7 +768,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 26: Missing or wrong shebang (HIGH - execution issues) # CHECK 26: Missing or wrong shebang (HIGH - execution issues)
#============================================================================== #==============================================================================
echo "[26/30] Checking: Shebang issues..." echo "[26/31] Checking: Shebang issues..."
{ {
echo "## CHECK 26: Missing or incorrect shebang" echo "## CHECK 26: Missing or incorrect shebang"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -801,7 +801,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 27: Not checking command exit status (MEDIUM - silent failures) # CHECK 27: Not checking command exit status (MEDIUM - silent failures)
#============================================================================== #==============================================================================
echo "[27/30] Checking: Unchecked critical commands..." echo "[27/31] Checking: Unchecked critical commands..."
{ {
echo "## CHECK 27: Critical commands without exit status checks" echo "## CHECK 27: Critical commands without exit status checks"
echo "Severity: MEDIUM" echo "Severity: MEDIUM"
@@ -831,7 +831,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 28: Incorrect string/number comparison (HIGH - type confusion) # CHECK 28: Incorrect string/number comparison (HIGH - type confusion)
#============================================================================== #==============================================================================
echo "[28/30] Checking: Type confusion in comparisons..." echo "[28/31] Checking: Type confusion in comparisons..."
{ {
echo "## CHECK 28: Using wrong comparison operators" echo "## CHECK 28: Using wrong comparison operators"
echo "Severity: HIGH" echo "Severity: HIGH"
@@ -856,7 +856,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 29: Unsafe array iteration (MEDIUM - word splitting) # CHECK 29: Unsafe array iteration (MEDIUM - word splitting)
#============================================================================== #==============================================================================
echo "[29/30] Checking: Unsafe array expansions..." echo "[29/31] Checking: Unsafe array expansions..."
{ {
echo "## CHECK 29: Array iteration without quotes" echo "## CHECK 29: Array iteration without quotes"
echo "Severity: MEDIUM" echo "Severity: MEDIUM"
@@ -882,7 +882,7 @@ echo ""
#============================================================================== #==============================================================================
# CHECK 30: Hardcoded credentials or secrets (CRITICAL - security) # CHECK 30: Hardcoded credentials or secrets (CRITICAL - security)
#============================================================================== #==============================================================================
echo "[30/30] Checking: Hardcoded credentials..." echo "[30/31] Checking: Hardcoded credentials..."
{ {
echo "## CHECK 30: Hardcoded passwords/API keys" echo "## CHECK 30: Hardcoded passwords/API keys"
echo "Severity: CRITICAL" echo "Severity: CRITICAL"
@@ -901,6 +901,59 @@ done < <(grep -rniE '(password|passwd|api_key|apikey|secret|token)=' "$TOOLKIT_P
echo "" echo ""
} >> "$REPORT" } >> "$REPORT"
#==============================================================================
# CHECK 31: local keyword outside functions (CRITICAL - script fails)
#==============================================================================
echo "[31/31] Checking: 'local' outside functions..."
{
echo "## CHECK 31: 'local' keyword outside function context"
echo "Severity: CRITICAL"
echo "Issue: 'local' can only be used inside functions, causes runtime error"
echo ""
while read -r file; do
# Read entire file and track function boundaries
in_function=0
brace_depth=0
line_num=0
while IFS= read -r line_content; do
line_num=$((line_num + 1))
# Skip comments
if echo "$line_content" | grep -q '^\s*#'; then
continue
fi
# Detect function start
if echo "$line_content" | grep -qE '^\s*[a-zA-Z_][a-zA-Z0-9_]*\s*\(\)'; then
in_function=1
brace_depth=0
fi
# Track braces
open_braces=$(echo "$line_content" | grep -o '{' | wc -l)
close_braces=$(echo "$line_content" | grep -o '}' | wc -l)
brace_depth=$((brace_depth + open_braces - close_braces))
# If we were in a function and braces are now balanced back to 0, we've exited
if [ "$in_function" -eq 1 ] && [ "$brace_depth" -le 0 ]; then
in_function=0
fi
# Check for 'local' keyword outside function
if [ "$in_function" -eq 0 ]; then
if echo "$line_content" | grep -qE '^\s*local\s+[a-zA-Z_]'; then
echo "CRITICAL|$file|$line_num|'local' keyword outside function (runtime error)"
count_issue "CRITICAL"
fi
fi
done < "$file"
done < <(find "$TOOLKIT_PATH" -name "*.sh" -type f 2>/dev/null)
echo ""
} >> "$REPORT"
#============================================================================== #==============================================================================
# PERFORMANCE CHECKS (INFO level - not counted as issues) # PERFORMANCE CHECKS (INFO level - not counted as issues)
#============================================================================== #==============================================================================