security-scan.sh 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #!/usr/bin/env bash
  2. # Scan source files for security-sensitive grep patterns.
  3. #
  4. # Usage: security-scan.sh [DIRECTORY]
  5. # Input: Optional directory argument; defaults to the current directory.
  6. # Output: Findings as plain file:line:match records on stdout.
  7. # Stderr: Progress banners, check status, summaries, and usage errors.
  8. # Exit: 0 clean, 2 usage error, 10 findings present.
  9. #
  10. # Examples:
  11. # security-scan.sh .
  12. # security-scan.sh src > findings.txt
  13. set -uo pipefail
  14. usage() {
  15. cat <<'EOF'
  16. Usage: security-scan.sh [DIRECTORY]
  17. Scan source files for security-sensitive grep patterns. DIRECTORY defaults to .
  18. Findings are written to stdout; progress and summaries are written to stderr.
  19. Exit codes:
  20. 0 scan completed with no findings
  21. 2 usage error
  22. 10 scan completed with findings
  23. EXAMPLES
  24. security-scan.sh .
  25. security-scan.sh src > findings.txt
  26. EOF
  27. }
  28. case "${1:-}" in
  29. --help|-h) usage; exit 0 ;;
  30. -*) printf 'security-scan.sh: unknown option: %s\n' "$1" >&2; usage >&2; exit 2 ;;
  31. esac
  32. if [[ $# -gt 1 ]]; then
  33. printf 'security-scan.sh: expected at most one directory\n' >&2
  34. usage >&2
  35. exit 2
  36. fi
  37. DIR="${1:-.}"
  38. # rg is the scan engine. A security scanner that silently reports "clean"
  39. # because its engine is missing is worse than useless — refuse loudly (exit 5)
  40. # rather than let a rg-less environment produce a false all-clear.
  41. if ! command -v rg >/dev/null 2>&1; then
  42. printf 'security-scan.sh: ripgrep (rg) not installed — cannot scan. Install rg; refusing to report a false clean.\n' >&2
  43. exit 5
  44. fi
  45. RED='\033[0;31m'
  46. YELLOW='\033[1;33m'
  47. GREEN='\033[0;32m'
  48. NC='\033[0m'
  49. printf '=== Security Scan: %s ===\n\n' "$DIR" >&2
  50. ISSUES=0
  51. check_pattern() {
  52. local name="$1"
  53. local pattern="$2"
  54. local type="$3"
  55. printf 'Checking: %s... ' "$name" >&2
  56. if rg -l "$pattern" "$DIR" --type "$type" 2>/dev/null | head -5 | grep -q .; then
  57. printf '%bFOUND%b\n' "$RED" "$NC" >&2
  58. rg -n "$pattern" "$DIR" --type "$type" 2>/dev/null | head -10
  59. ISSUES=$((ISSUES + 1))
  60. else
  61. printf '%bOK%b\n' "$GREEN" "$NC" >&2
  62. fi
  63. }
  64. printf '%s\n' '--- Python Security Checks ---' >&2
  65. check_pattern "Hardcoded secrets" "(password|secret|api_key|token)\s*=\s*['\"][^'\"]{8,}['\"]" "py"
  66. check_pattern "SQL injection (f-strings)" "execute\(f['\"]" "py"
  67. check_pattern "SQL injection (format)" "execute\(.*\.format\(" "py"
  68. check_pattern "eval() usage" "\beval\s*\(" "py"
  69. check_pattern "exec() usage" "\bexec\s*\(" "py"
  70. check_pattern "pickle.loads" "pickle\.loads?\(" "py"
  71. check_pattern "os.system" "os\.system\(" "py"
  72. check_pattern "shell=True" "subprocess.*shell\s*=\s*True" "py"
  73. check_pattern "MD5 hashing" "hashlib\.md5\(" "py"
  74. check_pattern "SHA1 hashing" "hashlib\.sha1\(" "py"
  75. printf '\n%s\n' '--- JavaScript Security Checks ---' >&2
  76. check_pattern "innerHTML" "\.innerHTML\s*=" "js"
  77. check_pattern "eval() usage" "\beval\s*\(" "js"
  78. check_pattern "document.write" "document\.write\(" "js"
  79. printf '\n%s\n' '--- General Security Checks ---' >&2
  80. printf 'Checking: .env files in git... ' >&2
  81. if git ls-files | grep -E "\.env$|\.env\." | grep -q .; then
  82. printf '%bFOUND%b\n' "$RED" "$NC" >&2
  83. git ls-files | grep -E "\.env$|\.env\."
  84. ISSUES=$((ISSUES + 1))
  85. else
  86. printf '%bOK%b\n' "$GREEN" "$NC" >&2
  87. fi
  88. printf 'Checking: TODO/FIXME security items... ' >&2
  89. if rg -i "TODO.*security|FIXME.*security|HACK.*security" "$DIR" 2>/dev/null | head -5 | grep -q .; then
  90. printf '%bFOUND%b\n' "$YELLOW" "$NC" >&2
  91. rg -i "TODO.*security|FIXME.*security|HACK.*security" "$DIR" 2>/dev/null | head -10
  92. ISSUES=$((ISSUES + 1))
  93. else
  94. printf '%bOK%b\n' "$GREEN" "$NC" >&2
  95. fi
  96. printf '\n%s\n' '=== Summary ===' >&2
  97. if [[ $ISSUES -eq 0 ]]; then
  98. printf '%bNo issues found!%b\n' "$GREEN" "$NC" >&2
  99. exit 0
  100. fi
  101. printf '%bFound %d potential security issues%b\n' "$RED" "$ISSUES" "$NC" >&2
  102. printf '%s\n' 'Review the findings above and address any real vulnerabilities.' >&2
  103. exit 10