diff --git a/tools/toolkit-qa-check.sh b/tools/toolkit-qa-check.sh index 56d361c..c2c2312 100755 --- a/tools/toolkit-qa-check.sh +++ b/tools/toolkit-qa-check.sh @@ -1602,17 +1602,22 @@ while IFS=: read -r file line_num line_content; do # Skip if already suppressed is_suppressed "$file" "$line_num" "source-check" && continue - # Extract sourced file path - sourced_file=$(echo "$line_content" | grep -oE 'source\s+[^ ]+|\\.\s+[^ ]+' | awk '{print $2}') + # Skip if line has error handling (|| or 2>/dev/null) + if echo "$line_content" | grep -qE '\|\||2>/dev/null'; then + continue + fi - # Skip if it's a variable or absolute path starting with / - if [[ "$sourced_file" =~ ^\$ ]] || [[ "$sourced_file" =~ ^/ ]]; then + # Extract sourced file path + sourced_file=$(echo "$line_content" | grep -oE 'source\s+[^ ]+' | awk '{print $2}' | tr -d '"') + + # Skip if empty, variable, absolute path, or contains special chars + if [ -z "$sourced_file" ] || [[ "$sourced_file" =~ ^\$ ]] || [[ "$sourced_file" =~ ^/ ]] || [[ "$sourced_file" =~ [{}()] ]]; then continue fi # Check if there's a validation within 3 lines before context=$(sed -n "$((line_num - 3)),$line_num p" "$file" 2>/dev/null) - if ! echo "$context" | grep -qE '\[\s+-f\s+.*'"$sourced_file"'|\[\s+-e\s+.*'"$sourced_file"; then + if ! echo "$context" | grep -qE '\[\s+-[fe]\s+.*'"$sourced_file"'|&&\s+source|if.*source'; then echo "HIGH|$file|$line_num|[SOURCE] Sourcing without existence check: $sourced_file" echo " Risk: Script fails with 'file not found' if path is wrong" echo " Fix: Add '[ -f \"$sourced_file\" ] && source \"$sourced_file\" || { echo \"Error\"; exit 1; }'" @@ -1620,7 +1625,7 @@ while IFS=: read -r file line_num line_content; do ((count++)) [ "$count" -ge 15 ] && break fi -done < <(grep -rnE '(source|\.)\s+[a-zA-Z]' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null) +done < <(grep -rnE '^\s*source\s+[a-zA-Z_]' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null) echo "Found: $count unvalidated source commands" echo "" @@ -1859,8 +1864,8 @@ echo "" echo "[51/60] Checking: Word splitting bugs in loops..." { echo "## CHECK 51: Unquoted variables/arrays in for loops" -echo "Severity: HIGH" -echo "Pattern: for x in \$var (splits on spaces, not newlines)" +echo "Severity: MEDIUM" +echo "Pattern: for x in \$var (splits on spaces - intentional or bug?)" echo "" count=0 @@ -1870,15 +1875,27 @@ while IFS=: read -r file line_num line_content; do # Extract the unquoted variable var=$(echo "$line_content" | grep -oE 'for\s+\w+\s+in\s+\$[A-Za-z_][A-Za-z0-9_]*' | grep -oE '\$[A-Za-z_]+') - echo "HIGH|$file|$line_num|[WORDSPLIT] Unquoted variable in for loop: $var" + # Skip common intentional patterns + var_name="${var#\$}" + if [[ "$var_name" =~ (disks|ips|users|dbs|files|dirs|items|list|args|@|\*|REPLY) ]]; then + continue # These are typically intentional word-splitting lists + fi + + # Skip if variable name suggests it's a list/array + if [[ "$var_name" =~ _(list|array|items)$ ]]; then + continue + fi + + echo "MEDIUM|$file|$line_num|[WORDSPLIT] Unquoted variable in for loop: $var" echo " Risk: Splits on spaces, not lines - 'file name.txt' becomes 2 items" - echo " Fix: Use quotes: for x in \"\$var\" or use array with \"\${arr[@]}\"" - count_issue "HIGH" + echo " Fix: If intentional (space-separated list), add # qa-suppress:word-split" + echo " If bug (filenames), use: for x in \"\$var\" or \"\${arr[@]}\"" + count_issue "MEDIUM" ((count++)) [ "$count" -ge 10 ] && break done < <(grep -rnE 'for\s+\w+\s+in\s+\$[A-Za-z_]' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null | grep -v '"\$') -echo "Found: $count word splitting risks" +echo "Found: $count potential word splitting issues" echo "" } >> "$REPORT" @@ -2410,11 +2427,20 @@ count=0 while IFS=: read -r file line_num line_content; do is_suppressed "$file" "$line_num" "ifs" && continue - # Look for IFS= assignment - if echo "$line_content" | grep -qE 'IFS='; then - # Check if there's a reset within next 20 lines or in same command - if ! echo "$line_content" | grep -qE ';.*IFS=' && \ - ! sed -n "$((line_num + 1)),$((line_num + 20))p" "$file" 2>/dev/null | grep -qE 'IFS='; then + # Skip safe patterns: IFS= read (only affects read command) + if echo "$line_content" | grep -qE 'IFS=.*read'; then + continue + fi + + # Skip if IFS is in a while/for loop condition (scoped) + if echo "$line_content" | grep -qE '(while|for).*IFS='; then + continue + fi + + # Look for standalone IFS= assignment + if echo "$line_content" | grep -qE '^\s*IFS='; then + # Check if there's a reset within next 20 lines + if ! sed -n "$((line_num + 1)),$((line_num + 20))p" "$file" 2>/dev/null | grep -qE '^\s*IFS=\$'; then echo "HIGH|$file|$line_num|[IFS] IFS modified without reset" echo " Risk: Affects all subsequent word splitting in script" echo " Fix: OLD_IFS=\$IFS; IFS=:; ...; IFS=\$OLD_IFS or use in subshell" @@ -2423,7 +2449,7 @@ while IFS=: read -r file line_num line_content; do [ "$count" -ge 10 ] && break fi fi -done < <(grep -rn 'IFS=' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null | head -50) +done < <(grep -rn '^\s*IFS=' "$TOOLKIT_PATH" --include="*.sh" 2>/dev/null | head -50) echo "Found: $count IFS modifications without reset" echo ""