diff --git a/.github/workflows/shell-validation.yml b/.github/workflows/shell-validation.yml new file mode 100644 index 0000000..3214ad0 --- /dev/null +++ b/.github/workflows/shell-validation.yml @@ -0,0 +1,154 @@ +name: Shell Script Validation + +on: + push: + branches: [ main ] + pull_request: + +jobs: + validate-shell-scripts: + runs-on: ubuntu-latest + name: Validate Shell Scripts + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install shellcheck + run: | + sudo apt-get update + sudo apt-get install -y shellcheck + + - name: Install shfmt + run: | + curl -L -o shfmt https://github.com/mvdan/sh/releases/latest/download/shfmt_v3.7.0_linux_amd64 + chmod +x shfmt + sudo mv shfmt /usr/local/bin/ + + - name: Find shell scripts + id: find-scripts + run: | + echo "Found shell scripts:" + find scripts/ -type f -executable -o -name "*.sh" | tee scripts-list.txt + # Also check files with shell shebangs + find scripts/ -type f -exec grep -l '^#!/bin/sh\|^#!/bin/bash\|^#!/usr/bin/env sh\|^#!/usr/bin/env bash' {} \; | tee -a scripts-list.txt + sort -u scripts-list.txt > unique-scripts.txt + mv unique-scripts.txt scripts-list.txt + cat scripts-list.txt + + - name: POSIX Compliance Check (shellcheck) + run: | + echo "Running shellcheck for POSIX compliance..." + exit_code=0 + while IFS= read -r script; do + if [ -f "$script" ]; then + echo "Checking $script..." + if ! shellcheck -s sh -e SC1091 -e SC2039 "$script"; then + echo "❌ $script failed POSIX compliance check" + exit_code=1 + else + echo "✅ $script passed POSIX compliance check" + fi + echo "---" + fi + done < scripts-list.txt + exit $exit_code + + - name: Shell Script Linting (shellcheck extended) + run: | + echo "Running extended shellcheck analysis..." + exit_code=0 + while IFS= read -r script; do + if [ -f "$script" ]; then + echo "Linting $script..." + if ! shellcheck -f gcc "$script"; then + echo "❌ $script failed extended linting" + exit_code=1 + else + echo "✅ $script passed extended linting" + fi + echo "---" + fi + done < scripts-list.txt + exit $exit_code + + - name: Executable Permissions Check + run: | + echo "Checking executable permissions..." + exit_code=0 + while IFS= read -r script; do + if [ -f "$script" ]; then + if [ ! -x "$script" ]; then + echo "⚠️ $script is not executable" + exit_code=1 + else + echo "✅ $script has correct executable permissions" + fi + fi + done < scripts-list.txt + exit $exit_code + + - name: Shebang Validation + run: | + echo "Validating shebangs..." + exit_code=0 + while IFS= read -r script; do + if [ -f "$script" ]; then + first_line=$(head -n1 "$script") + case "$first_line" in + "#!/bin/sh"|"#!/usr/bin/env sh") + echo "✅ $script has valid POSIX shebang: $first_line" + ;; + "#!/bin/bash"|"#!/usr/bin/env bash") + echo "⚠️ $script uses bash shebang (not POSIX): $first_line" + ;; + "#!"*) + echo "❌ $script has non-standard shebang: $first_line" + exit_code=1 + ;; + *) + echo "❌ $script missing shebang" + exit_code=1 + ;; + esac + fi + done < scripts-list.txt + exit $exit_code + + - name: Security Scan (basic) + run: | + echo "Running basic security checks..." + exit_code=0 + while IFS= read -r script; do + if [ -f "$script" ]; then + echo "Security scanning $script..." + + # Check for potentially dangerous patterns + if grep -n "eval\|exec\|system\|curl.*|.*sh\|wget.*|.*sh" "$script" | grep -v "^#"; then + echo "⚠️ $script contains potentially dangerous patterns" + fi + + # Check for hardcoded credentials patterns + if grep -ni "password\|secret\|token\|key" "$script" | grep -v "^#" | grep "="; then + echo "⚠️ $script may contain hardcoded credentials" + fi + + # Check for unquoted variables + if grep -n '\$[A-Za-z_][A-Za-z0-9_]*[^A-Za-z0-9_"]' "$script" | grep -v "^#"; then + echo "⚠️ $script has potentially unquoted variables" + fi + + echo "✅ Basic security scan completed for $script" + echo "---" + fi + done < scripts-list.txt + exit $exit_code + + - name: Summary Report + if: always() + run: | + echo "=== Shell Script Validation Summary ===" + echo "Scripts validated:" + cat scripts-list.txt | wc -l + echo "" + echo "Validation completed. Check individual step results above for details." \ No newline at end of file diff --git a/scripts/help b/scripts/help old mode 100644 new mode 100755 diff --git a/scripts/install b/scripts/install old mode 100644 new mode 100755 index fc5292c..fab0db9 --- a/scripts/install +++ b/scripts/install @@ -80,6 +80,7 @@ headline() { check_for_user_confirmation() { if [ "$INTERACTIVE" = "true" ]; then echo "Press Enter to continue, or Ctrl+C to cancel..." + # shellcheck disable=SC2034 read -r answer /dev/null | LC_ALL=C tr -dc '[:alnum:]' | cut -c -"${1:-16}" + dd if=/dev/urandom bs=1 count=512 2>/dev/null | LC_ALL=C tr -dc '[:alnum:]' | cut -c -16 } generate_random_hex() { @@ -174,8 +175,10 @@ install_docker_debian() { echo "Installing Docker on Debian-like..." sudo apt-get update -y sudo apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release + # shellcheck disable=SC2046,SC1091 curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg \ | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + # shellcheck disable=SC1091 echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \ https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \ $(lsb_release -cs) stable" \ diff --git a/scripts/share-logs b/scripts/share-logs old mode 100644 new mode 100755 index 4464488..c3febe5 --- a/scripts/share-logs +++ b/scripts/share-logs @@ -18,7 +18,7 @@ if [ -f docker-compose.yml ]; then echo "Found docker-compose.yml" else echo "Could not find docker-compose.yml, trying $INSTALL_DIR" - cd $INSTALL_DIR || exit 1 + cd "$INSTALL_DIR" || exit 1 if [ -f docker-compose.yml ]; then echo "Found docker-compose.yml" else @@ -28,10 +28,11 @@ else fi echo "Writing logs to a file..." +# shellcheck disable=SC2024 sudo docker compose logs --timestamps --no-color > logs.txt echo "Uploading logs to S3..." LOGS_FILE=$(date +%Y%m%d_%H%M%S).log -curl -fLX PUT --upload-file logs.txt https://openops-client-logs.s3.amazonaws.com/$LOGS_FILE +curl -fLX PUT --upload-file logs.txt "https://openops-client-logs.s3.amazonaws.com/$LOGS_FILE" echo "Done. File uploaded to https://openops-client-logs.s3.amazonaws.com/$LOGS_FILE" echo "Please share the following file name with OpenOps: $LOGS_FILE" diff --git a/scripts/tls b/scripts/tls old mode 100644 new mode 100755 index c42d3b1..4a18602 --- a/scripts/tls +++ b/scripts/tls @@ -70,6 +70,7 @@ headline() { check_for_user_confirmation() { if [ "$INTERACTIVE" = "true" ]; then echo "Press Enter to continue, or Ctrl+C to cancel..." + # shellcheck disable=SC2034 read -r answer