mirror of
https://github.com/ivuorinen/gibidify.git
synced 2026-01-26 03:24:05 +00:00
chore: modernize workflows, security scanning, and linting configuration (#50)
* build: update Go 1.25, CI workflows, and build tooling - Upgrade to Go 1.25 - Add benchmark targets to Makefile - Implement parallel gosec execution - Lock tool versions for reproducibility - Add shellcheck directives to scripts - Update CI workflows with improved caching * refactor: migrate from golangci-lint to revive - Replace golangci-lint with revive for linting - Configure comprehensive revive rules - Fix all EditorConfig violations - Add yamllint and yamlfmt support - Remove deprecated .golangci.yml * refactor: rename utils to shared and deduplicate code - Rename utils package to shared - Add shared constants package - Deduplicate constants across packages - Address CodeRabbit review feedback * fix: resolve SonarQube issues and add safety guards - Fix all 73 SonarQube OPEN issues - Add nil guards for resourceMonitor, backpressure, metricsCollector - Implement io.Closer for headerFileReader - Propagate errors from processing helpers - Add metrics and templates packages - Improve error handling across codebase * test: improve test infrastructure and coverage - Add benchmarks for cli, fileproc, metrics - Improve test coverage for cli, fileproc, config - Refactor tests with helper functions - Add shared test constants - Fix test function naming conventions - Reduce cognitive complexity in benchmark tests * docs: update documentation and configuration examples - Update CLAUDE.md with current project state - Refresh README with new features - Add usage and configuration examples - Add SonarQube project configuration - Consolidate config.example.yaml * fix: resolve shellcheck warnings in scripts - Use ./*.go instead of *.go to prevent dash-prefixed filenames from being interpreted as options (SC2035) - Remove unreachable return statement after exit (SC2317) - Remove obsolete gibidiutils/ directory reference * chore(deps): upgrade go dependencies * chore(lint): megalinter fixes * fix: improve test coverage and fix file descriptor leaks - Add defer r.Close() to fix pipe file descriptor leaks in benchmark tests - Refactor TestProcessorConfigureFileTypes with helper functions and assertions - Refactor TestProcessorLogFinalStats with output capture and keyword verification - Use shared constants instead of literal strings (TestFilePNG, FormatMarkdown, etc.) - Reduce cognitive complexity by extracting helper functions * fix: align test comments with function names Remove underscores from test comments to match actual function names: - benchmark/benchmark_test.go (2 fixes) - fileproc/filetypes_config_test.go (4 fixes) - fileproc/filetypes_registry_test.go (6 fixes) - fileproc/processor_test.go (6 fixes) - fileproc/resource_monitor_types_test.go (4 fixes) - fileproc/writer_test.go (3 fixes) * fix: various test improvements and bug fixes - Remove duplicate maxCacheSize check in filetypes_registry_test.go - Shorten long comment in processor_test.go to stay under 120 chars - Remove flaky time.Sleep in collector_test.go, use >= 0 assertion - Close pipe reader in benchmark_test.go to fix file descriptor leak - Use ContinueOnError in flags_test.go to match ResetFlags behavior - Add nil check for p.ui in processor_workers.go before UpdateProgress - Fix resource_monitor_validation_test.go by setting hardMemoryLimitBytes directly * chore(yaml): add missing document start markers Add --- document start to YAML files to satisfy yamllint: - .github/workflows/codeql.yml - .github/workflows/build-test-publish.yml - .github/workflows/security.yml - .github/actions/setup/action.yml * fix: guard nil resourceMonitor and fix test deadlock - Guard resourceMonitor before CreateFileProcessingContext call - Add ui.UpdateProgress on emergency stop and path error returns - Fix potential deadlock in TestProcessFile using wg.Go with defer close
This commit is contained in:
145
scripts/gosec.sh
Executable file
145
scripts/gosec.sh
Executable file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Gosec security scanner script for individual Go files
|
||||
# Runs gosec on each Go directory and reports issues per file
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# If NO_COLOR is set, disable colors
|
||||
if [[ -n "${NO_COLOR:-}" ]]; then
|
||||
RED=''
|
||||
GREEN=''
|
||||
YELLOW=''
|
||||
BLUE=''
|
||||
NC=''
|
||||
fi
|
||||
|
||||
# Function to print status
|
||||
print_status() {
|
||||
local msg="$1"
|
||||
echo -e "${BLUE}[INFO]${NC} $msg"
|
||||
return 0
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
local msg="$1"
|
||||
echo -e "${YELLOW}[WARN]${NC} $msg" >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
print_error() {
|
||||
local msg="$1"
|
||||
echo -e "${RED}[ERROR]${NC} $msg" >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
print_success() {
|
||||
local msg="$1"
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $msg"
|
||||
return 0
|
||||
}
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
cd "$PROJECT_ROOT" || {
|
||||
print_error "Failed to change directory to $PROJECT_ROOT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if gosec is available
|
||||
if ! command -v gosec &>/dev/null; then
|
||||
print_error "gosec not found. Please install it first:"
|
||||
print_error "go install github.com/securego/gosec/v2/cmd/gosec@latest"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if jq is available
|
||||
if ! command -v jq &>/dev/null; then
|
||||
print_error "jq not found. Please install it first:"
|
||||
print_error "brew install jq # on macOS"
|
||||
print_error "apt-get install jq # on Ubuntu/Debian"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get all Go files and unique directories
|
||||
GO_FILES=$(find . -name "*.go" -not -path "./.*" | sort)
|
||||
TOTAL_FILES=$(echo "$GO_FILES" | wc -l | tr -d ' ')
|
||||
|
||||
DIRECTORIES=$(echo "$GO_FILES" | xargs -n1 dirname | sort -u)
|
||||
TOTAL_DIRS=$(echo "$DIRECTORIES" | wc -l | tr -d ' ')
|
||||
|
||||
print_status "Found $TOTAL_FILES Go files in $TOTAL_DIRS directories"
|
||||
print_status "Running gosec security scan..."
|
||||
|
||||
ISSUES_FOUND=0
|
||||
FILES_WITH_ISSUES=0
|
||||
CURRENT_DIR=0
|
||||
|
||||
# Create a temporary directory for reports
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
|
||||
# Process each directory
|
||||
while IFS= read -r dir; do
|
||||
CURRENT_DIR=$((CURRENT_DIR + 1))
|
||||
echo -ne "\r${BLUE}[PROGRESS]${NC} Scanning $CURRENT_DIR/$TOTAL_DIRS: $dir "
|
||||
|
||||
# Run gosec on the directory
|
||||
REPORT_FILE="$TEMP_DIR/$(echo "$dir" | tr '/' '_' | tr '.' '_').json"
|
||||
if gosec -fmt=json "$dir" >"$REPORT_FILE" 2>/dev/null; then
|
||||
# Check for issues in all files in this directory
|
||||
ISSUES=$(jq -r '.Issues // [] | length' "$REPORT_FILE" 2>/dev/null || echo "0")
|
||||
|
||||
if [[ "$ISSUES" -gt 0 ]]; then
|
||||
echo # New line after progress
|
||||
print_warning "Found $ISSUES security issue(s) in directory $dir:"
|
||||
|
||||
# Group issues by file and display them
|
||||
jq -r '.Issues[] | "\(.file)|\(.rule_id)|\(.details)|\(.line)"' "$REPORT_FILE" 2>/dev/null | while IFS='|' read -r file rule details line; do
|
||||
if [[ -n "$file" ]]; then
|
||||
# Only count each file once
|
||||
if ! grep -q "$file" "$TEMP_DIR/processed_files.txt" 2>/dev/null; then
|
||||
echo "$file" >>"$TEMP_DIR/processed_files.txt"
|
||||
FILES_WITH_ISSUES=$((FILES_WITH_ISSUES + 1))
|
||||
fi
|
||||
echo " $file:$line → $rule: $details"
|
||||
fi
|
||||
done
|
||||
|
||||
ISSUES_FOUND=$((ISSUES_FOUND + ISSUES))
|
||||
echo
|
||||
fi
|
||||
else
|
||||
echo # New line after progress
|
||||
print_error "Failed to scan directory $dir"
|
||||
fi
|
||||
done <<<"$DIRECTORIES"
|
||||
|
||||
echo # Final new line after progress
|
||||
|
||||
# Count actual files with issues
|
||||
if [[ -f "$TEMP_DIR/processed_files.txt" ]]; then
|
||||
FILES_WITH_ISSUES=$(wc -l <"$TEMP_DIR/processed_files.txt" | tr -d ' ')
|
||||
fi
|
||||
|
||||
# Summary
|
||||
print_status "Gosec scan completed!"
|
||||
print_status "Directories scanned: $TOTAL_DIRS"
|
||||
print_status "Files scanned: $TOTAL_FILES"
|
||||
|
||||
if [[ $ISSUES_FOUND -eq 0 ]]; then
|
||||
print_success "No security issues found! 🎉"
|
||||
exit 0
|
||||
else
|
||||
print_warning "Found $ISSUES_FOUND security issue(s) in $FILES_WITH_ISSUES file(s)"
|
||||
print_status "Review the issues above and fix them before proceeding"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,31 +1,24 @@
|
||||
Available targets:
|
||||
install-tools - Install required linting and development tools
|
||||
lint - Run all linters (Go, EditorConfig, Makefile, shell, YAML)
|
||||
lint-fix - Run linters with auto-fix enabled
|
||||
lint-verbose - Run linters with verbose output
|
||||
test - Run tests
|
||||
test-coverage - Run tests with coverage output
|
||||
coverage - Run tests with coverage and generate HTML report
|
||||
build - Build the application
|
||||
clean - Clean build artifacts
|
||||
all - Run lint, test, and build
|
||||
install-tools - Install required linting and development tools
|
||||
lint - Run all linters (Go, Makefile, shell, YAML)
|
||||
lint-fix - Run linters with auto-fix enabled
|
||||
test - Run tests
|
||||
coverage - Run tests with coverage
|
||||
build - Build the application
|
||||
clean - Clean build artifacts
|
||||
all - Run lint, test, and build
|
||||
|
||||
Security targets:
|
||||
security - Run comprehensive security scan
|
||||
security-full - Run full security analysis with all tools
|
||||
vuln-check - Check for dependency vulnerabilities
|
||||
|
||||
Dependency management:
|
||||
deps-check - Check for available dependency updates
|
||||
deps-update - Update all dependencies to latest versions
|
||||
deps-tidy - Clean up and verify dependencies
|
||||
security - Run comprehensive security scan
|
||||
security-full - Run full security analysis with all tools
|
||||
vuln-check - Check for dependency vulnerabilities
|
||||
|
||||
Benchmark targets:
|
||||
build-benchmark - Build the benchmark binary
|
||||
benchmark - Run all benchmarks
|
||||
benchmark-collection - Run file collection benchmarks
|
||||
benchmark-processing - Run file processing benchmarks
|
||||
benchmark-concurrency - Run concurrency benchmarks
|
||||
benchmark-format - Run format benchmarks
|
||||
build-benchmark - Build the benchmark binary
|
||||
benchmark - Run all benchmarks
|
||||
benchmark-collection - Run file collection benchmarks
|
||||
benchmark-processing - Run file processing benchmarks
|
||||
benchmark-concurrency - Run concurrency benchmarks
|
||||
benchmark-format - Run format benchmarks
|
||||
|
||||
Run 'make <target>' to execute a specific target.
|
||||
|
||||
@@ -1,30 +1,153 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Installing golangci-lint..."
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
echo "Installing gofumpt..."
|
||||
go install mvdan.cc/gofumpt@latest
|
||||
echo "Installing golines..."
|
||||
go install github.com/segmentio/golines@latest
|
||||
echo "Installing goimports..."
|
||||
go install golang.org/x/tools/cmd/goimports@latest
|
||||
echo "Installing staticcheck..."
|
||||
go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
echo "Installing gosec..."
|
||||
go install github.com/securego/gosec/v2/cmd/gosec@latest
|
||||
echo "Installing gocyclo..."
|
||||
go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
|
||||
echo "Installing revive..."
|
||||
go install github.com/mgechev/revive@latest
|
||||
echo "Installing checkmake..."
|
||||
go install github.com/checkmake/checkmake/cmd/checkmake@latest
|
||||
echo "Installing shellcheck..."
|
||||
go install github.com/koalaman/shellcheck/cmd/shellcheck@latest
|
||||
echo "Installing shfmt..."
|
||||
go install mvdan.cc/sh/v3/cmd/shfmt@latest
|
||||
echo "Installing yamllint (Go-based)..."
|
||||
go install github.com/excilsploft/yamllint@latest
|
||||
echo "Installing editorconfig-checker..."
|
||||
go install github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@latest
|
||||
echo "All tools installed successfully!"
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# If NO_COLOR is set, disable colors
|
||||
if [[ -n "${NO_COLOR:-}" ]]; then
|
||||
RED=''
|
||||
GREEN=''
|
||||
YELLOW=''
|
||||
BLUE=''
|
||||
NC=''
|
||||
fi
|
||||
|
||||
# Function to print status
|
||||
print_status() {
|
||||
local msg="$1"
|
||||
echo -e "${BLUE}[INFO]${NC} $msg"
|
||||
return 0
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
local msg="$1"
|
||||
echo -e "${YELLOW}[WARN]${NC} $msg" >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
print_error() {
|
||||
local msg="$1"
|
||||
echo -e "${RED}[ERROR]${NC} $msg" >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
print_success() {
|
||||
local msg="$1"
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $msg"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check if required tools are installed
|
||||
check_dependencies() {
|
||||
print_status "Checking dependencies..."
|
||||
|
||||
local missing_tools=()
|
||||
|
||||
if ! command -v go &>/dev/null; then
|
||||
missing_tools+=("go")
|
||||
fi
|
||||
|
||||
# Check that tools are installed:
|
||||
|
||||
if [[ ${#missing_tools[@]} -ne 0 ]]; then
|
||||
print_error "Missing required tools: ${missing_tools[*]}"
|
||||
print_error "Please install the missing tools and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Security tools
|
||||
|
||||
if ! command -v gosec &>/dev/null; then
|
||||
print_warning "gosec not found, installing..."
|
||||
go install github.com/securego/gosec/v2/cmd/gosec@v2.22.8
|
||||
fi
|
||||
|
||||
if ! command -v govulncheck &>/dev/null; then
|
||||
print_warning "govulncheck not found, installing..."
|
||||
go install golang.org/x/vuln/cmd/govulncheck@v1.1.4
|
||||
fi
|
||||
|
||||
# Linting tools
|
||||
|
||||
if ! command -v revive &>/dev/null; then
|
||||
print_warning "revive not found, installing..."
|
||||
go install github.com/mgechev/revive@v1.11.0
|
||||
fi
|
||||
|
||||
if ! command -v gocyclo &>/dev/null; then
|
||||
print_warning "gocyclo not found, installing..."
|
||||
go install github.com/fzipp/gocyclo/cmd/gocyclo@v0.6.0
|
||||
fi
|
||||
|
||||
if ! command -v checkmake &>/dev/null; then
|
||||
print_warning "checkmake not found, installing..."
|
||||
go install github.com/checkmake/checkmake/cmd/checkmake@v0.2.2
|
||||
fi
|
||||
|
||||
if ! command -v eclint &>/dev/null; then
|
||||
print_warning "eclint not found, installing..."
|
||||
go install gitlab.com/greut/eclint/cmd/eclint@v0.5.1
|
||||
fi
|
||||
|
||||
if ! command -v staticcheck &>/dev/null; then
|
||||
print_warning "staticcheck not found, installing..."
|
||||
go install honnef.co/go/tools/cmd/staticcheck@v0.6.1
|
||||
fi
|
||||
|
||||
if ! command -v yamllint &>/dev/null; then
|
||||
print_warning "yamllint not found, installing..."
|
||||
go install mvdan.cc/yaml/cmd/yaml-lint@v2.4.0
|
||||
fi
|
||||
|
||||
# Formatting tools
|
||||
|
||||
if ! command -v gofumpt &>/dev/null; then
|
||||
print_warning "gofumpt not found, installing..."
|
||||
go install mvdan.cc/gofumpt@v0.8.0
|
||||
fi
|
||||
|
||||
if ! command -v goimports &>/dev/null; then
|
||||
print_warning "goimports not found, installing..."
|
||||
go install golang.org/x/tools/cmd/goimports@v0.36.0
|
||||
fi
|
||||
|
||||
if ! command -v shfmt &>/dev/null; then
|
||||
print_warning "shfmt not found, installing..."
|
||||
go install mvdan.cc/sh/v3/cmd/shfmt@v3.12.0
|
||||
fi
|
||||
|
||||
if ! command -v yamlfmt &>/dev/null; then
|
||||
print_warning "yamlfmt not found, installing..."
|
||||
go install github.com/google/yamlfmt/cmd/yamlfmt@v0.4.0
|
||||
fi
|
||||
|
||||
print_success "All dependencies are available"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---
|
||||
|
||||
# If this file is sourced, export the functions
|
||||
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
|
||||
export -f check_dependencies print_error print_warning print_success print_status
|
||||
fi
|
||||
|
||||
# if this file is executed, execute the function
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
cd "$PROJECT_ROOT" || {
|
||||
echo "Failed to change directory to $PROJECT_ROOT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Installing dev tools for gibidify..."
|
||||
|
||||
check_dependencies
|
||||
fi
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Enable strict error handling
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
shopt -s globstar nullglob
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
cd "$PROJECT_ROOT" || {
|
||||
echo "Failed to change directory to $PROJECT_ROOT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# shellcheck source=scripts/install-tools.sh
|
||||
source "$SCRIPT_DIR/install-tools.sh"
|
||||
# shellcheck source=scripts/security.sh
|
||||
source "$SCRIPT_DIR/security.sh"
|
||||
|
||||
check_dependencies
|
||||
|
||||
echo "Running gofumpt..."
|
||||
gofumpt -l -w .
|
||||
echo "Running golines..."
|
||||
golines -w -m 120 --base-formatter="gofumpt" --shorten-comments .
|
||||
echo "Running goimports..."
|
||||
goimports -w -local github.com/ivuorinen/gibidify .
|
||||
echo "Running go fmt..."
|
||||
@@ -12,14 +29,23 @@ go fmt ./...
|
||||
echo "Running go mod tidy..."
|
||||
go mod tidy
|
||||
echo "Running shfmt formatting..."
|
||||
shfmt -w -i 0 -ci .
|
||||
echo "Running golangci-lint with --fix..."
|
||||
golangci-lint run --fix ./...
|
||||
shfmt -w -i 2 -ci .
|
||||
echo "Running revive linter..."
|
||||
revive -config revive.toml -formatter friendly -set_exit_status ./...
|
||||
echo "Running gosec security linter in parallel..."
|
||||
if ! run_gosec_parallel; then
|
||||
echo "gosec found security issues"
|
||||
exit 1
|
||||
fi
|
||||
echo "Auto-fix completed. Running final lint check..."
|
||||
golangci-lint run ./...
|
||||
echo "Running revive..."
|
||||
revive -config revive.toml -formatter friendly ./...
|
||||
revive -config revive.toml -formatter friendly -set_exit_status ./...
|
||||
if ! run_gosec_parallel; then
|
||||
echo "Final gosec check found security issues"
|
||||
exit 1
|
||||
fi
|
||||
echo "Running checkmake..."
|
||||
checkmake --config=.checkmake Makefile
|
||||
echo "Running yamllint..."
|
||||
yamllint .
|
||||
echo "Running yamlfmt..."
|
||||
yamlfmt -conf .yamlfmt.yml -gitignore_excludes -dstar ./**/*.{yaml,yml}
|
||||
echo "Running eclint fix..."
|
||||
eclint -fix ./*.go ./*.md benchmark/ cli/ cmd/ config/ fileproc/ metrics/ shared/ templates/ testutil/ scripts/ Makefile
|
||||
|
||||
@@ -1,23 +1,65 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Running golangci-lint..."
|
||||
golangci-lint run ./...
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
cd "$PROJECT_ROOT" || {
|
||||
echo "Failed to change directory to $PROJECT_ROOT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# shellcheck source=scripts/install-tools.sh
|
||||
source "$SCRIPT_DIR/install-tools.sh"
|
||||
# shellcheck source=scripts/security.sh
|
||||
source "$SCRIPT_DIR/security.sh"
|
||||
|
||||
check_dependencies
|
||||
|
||||
echo "Linting..."
|
||||
|
||||
# Track overall exit status
|
||||
exit_code=0
|
||||
|
||||
echo "Running revive..."
|
||||
revive -config revive.toml -formatter friendly ./...
|
||||
if ! revive -config revive.toml -formatter friendly -set_exit_status ./...; then
|
||||
exit_code=1
|
||||
fi
|
||||
|
||||
echo "Running gosec in parallel..."
|
||||
|
||||
if ! run_gosec_parallel; then
|
||||
exit_code=1
|
||||
fi
|
||||
|
||||
echo "Running checkmake..."
|
||||
checkmake --config=.checkmake Makefile
|
||||
|
||||
echo "Running editorconfig-checker..."
|
||||
editorconfig-checker
|
||||
|
||||
echo "Running shellcheck..."
|
||||
shellcheck scripts/*.sh
|
||||
if ! checkmake --config=.checkmake Makefile; then
|
||||
exit_code=1
|
||||
fi
|
||||
|
||||
echo "Running shfmt check..."
|
||||
shfmt -d -i 0 -ci .
|
||||
if ! shfmt -d .; then
|
||||
exit_code=1
|
||||
fi
|
||||
|
||||
echo "Running yamllint..."
|
||||
yamllint .
|
||||
if command -v yamllint >/dev/null 2>&1; then
|
||||
# Python yamllint supports the .yamllint config; lint the whole repo
|
||||
if ! yamllint -c .yamllint .; then
|
||||
exit_code=1
|
||||
fi
|
||||
elif command -v yaml-lint >/dev/null 2>&1; then
|
||||
# Go yaml-lint has different flags and no .yamllint support; use its defaults
|
||||
if ! yaml-lint .; then
|
||||
exit_code=1
|
||||
fi
|
||||
else
|
||||
echo "YAML linter not found (yamllint or yaml-lint); skipping."
|
||||
fi
|
||||
|
||||
echo "Running editorconfig-checker..."
|
||||
if ! eclint ./*.go ./*.md benchmark/ cli/ cmd/ config/ fileproc/ metrics/ shared/ templates/ testutil/ scripts/ Makefile; then
|
||||
exit_code=1
|
||||
fi
|
||||
|
||||
# Exit with failure status if any linter failed
|
||||
exit $exit_code
|
||||
|
||||
@@ -1,483 +1,258 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Security Scanning Script for gibidify
|
||||
# This script runs comprehensive security checks locally and in CI
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
cd "$PROJECT_ROOT" || {
|
||||
echo "Failed to change directory to $PROJECT_ROOT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# shellcheck source=scripts/install-tools.sh
|
||||
source "$SCRIPT_DIR/install-tools.sh"
|
||||
|
||||
echo "🔒 Starting comprehensive security scan for gibidify..."
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print status
|
||||
print_status() {
|
||||
printf "${BLUE}[INFO]${NC} %s\n" "$1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
printf "${YELLOW}[WARN]${NC} %s\n" "$1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
printf "${RED}[ERROR]${NC} %s\n" "$1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
printf "${GREEN}[SUCCESS]${NC} %s\n" "$1"
|
||||
}
|
||||
|
||||
# Run command with timeout if available, otherwise run directly
|
||||
# Usage: run_with_timeout DURATION COMMAND [ARGS...]
|
||||
run_with_timeout() {
|
||||
duration="$1"
|
||||
shift
|
||||
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout "$duration" "$@"
|
||||
else
|
||||
# timeout not available, run command directly
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if required tools are installed
|
||||
check_dependencies() {
|
||||
print_status "Checking security scanning dependencies..."
|
||||
|
||||
missing_tools=""
|
||||
|
||||
if ! command -v go >/dev/null 2>&1; then
|
||||
missing_tools="${missing_tools}go "
|
||||
print_error "Go is not installed. Please install Go first."
|
||||
print_error "Visit https://golang.org/doc/install for installation instructions."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v golangci-lint >/dev/null 2>&1; then
|
||||
print_warning "golangci-lint not found, installing..."
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
fi
|
||||
|
||||
if ! command -v gosec >/dev/null 2>&1; then
|
||||
print_warning "gosec not found, installing..."
|
||||
go install github.com/securego/gosec/v2/cmd/gosec@latest
|
||||
fi
|
||||
|
||||
if ! command -v govulncheck >/dev/null 2>&1; then
|
||||
print_warning "govulncheck not found, installing..."
|
||||
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
fi
|
||||
|
||||
if ! command -v checkmake >/dev/null 2>&1; then
|
||||
print_warning "checkmake not found, installing..."
|
||||
go install github.com/checkmake/checkmake/cmd/checkmake@latest
|
||||
fi
|
||||
|
||||
if ! command -v shfmt >/dev/null 2>&1; then
|
||||
print_warning "shfmt not found, installing..."
|
||||
go install mvdan.cc/sh/v3/cmd/shfmt@latest
|
||||
fi
|
||||
|
||||
if ! command -v yamllint >/dev/null 2>&1; then
|
||||
print_warning "yamllint not found, attempting to install..."
|
||||
|
||||
# Update PATH to include common user install directories
|
||||
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
|
||||
|
||||
installed=0
|
||||
|
||||
# Try pipx first
|
||||
if command -v pipx >/dev/null 2>&1; then
|
||||
print_status "Attempting install with pipx..."
|
||||
if pipx install yamllint; then
|
||||
# Update PATH to include pipx bin directory
|
||||
pipx_bin_dir=$(pipx environment --value PIPX_BIN_DIR 2>/dev/null || echo "$HOME/.local/bin")
|
||||
export PATH="$pipx_bin_dir:$PATH"
|
||||
installed=1
|
||||
else
|
||||
print_warning "pipx install yamllint failed, trying next method..."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Try pip3 --user if pipx didn't work
|
||||
if [ "$installed" -eq 0 ] && command -v pip3 >/dev/null 2>&1; then
|
||||
print_status "Attempting install with pip3 --user..."
|
||||
if pip3 install --user yamllint; then
|
||||
installed=1
|
||||
else
|
||||
print_warning "pip3 install yamllint failed, trying next method..."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Try apt-get with smart sudo handling
|
||||
if [ "$installed" -eq 0 ] && command -v apt-get >/dev/null 2>&1; then
|
||||
sudo_cmd=""
|
||||
can_use_apt=false
|
||||
|
||||
# Check if running as root
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
print_status "Running as root, no sudo needed for apt-get..."
|
||||
sudo_cmd=""
|
||||
can_use_apt=true
|
||||
elif command -v sudo >/dev/null 2>&1; then
|
||||
# Try non-interactive sudo first
|
||||
if sudo -n true 2>/dev/null; then
|
||||
print_status "Attempting install with apt-get (sudo cached)..."
|
||||
sudo_cmd="sudo"
|
||||
can_use_apt=true
|
||||
elif [ -t 0 ]; then
|
||||
# TTY available, allow interactive sudo
|
||||
print_status "Attempting install with apt-get (may prompt for sudo)..."
|
||||
sudo_cmd="sudo"
|
||||
can_use_apt=true
|
||||
else
|
||||
print_warning "apt-get available but sudo not accessible (non-interactive, no cache). Skipping apt-get."
|
||||
can_use_apt=false
|
||||
fi
|
||||
else
|
||||
print_warning "apt-get available but sudo not found. Skipping apt-get."
|
||||
can_use_apt=false
|
||||
fi
|
||||
|
||||
# Attempt apt-get only if we have permission to use it
|
||||
if [ "$can_use_apt" = true ]; then
|
||||
if [ -n "$sudo_cmd" ]; then
|
||||
if run_with_timeout 300 ${sudo_cmd:+"$sudo_cmd"} apt-get update; then
|
||||
if run_with_timeout 300 ${sudo_cmd:+"$sudo_cmd"} apt-get install -y yamllint; then
|
||||
installed=1
|
||||
else
|
||||
print_warning "apt-get install yamllint failed or timed out"
|
||||
fi
|
||||
else
|
||||
print_warning "apt-get update failed or timed out"
|
||||
fi
|
||||
else
|
||||
# Running as root without sudo
|
||||
if run_with_timeout 300 apt-get update; then
|
||||
if run_with_timeout 300 apt-get install -y yamllint; then
|
||||
installed=1
|
||||
else
|
||||
print_warning "apt-get install yamllint failed or timed out"
|
||||
fi
|
||||
else
|
||||
print_warning "apt-get update failed or timed out"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Final check with updated PATH
|
||||
if ! command -v yamllint >/dev/null 2>&1; then
|
||||
print_error "yamllint installation failed or yamllint still not found in PATH."
|
||||
print_error "Please install yamllint manually using one of:"
|
||||
print_error " - pipx install yamllint"
|
||||
print_error " - pip3 install --user yamllint"
|
||||
print_error " - sudo apt-get install yamllint (Debian/Ubuntu)"
|
||||
print_error " - brew install yamllint (macOS)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "yamllint successfully installed and found in PATH"
|
||||
fi
|
||||
|
||||
if [ -n "$missing_tools" ]; then
|
||||
print_error "Missing required tools: $missing_tools"
|
||||
print_error "Please install the missing tools and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "All dependencies are available"
|
||||
}
|
||||
check_dependencies
|
||||
|
||||
# Run gosec security scanner
|
||||
run_gosec() {
|
||||
print_status "Running gosec security scanner..."
|
||||
print_status "Running gosec security scanner..."
|
||||
|
||||
if gosec -fmt=json -out=gosec-report.json -stdout -verbose=text ./...; then
|
||||
print_success "gosec scan completed successfully"
|
||||
else
|
||||
print_error "gosec found security issues!"
|
||||
if [ -f "gosec-report.json" ]; then
|
||||
echo "Detailed report saved to gosec-report.json"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
if gosec -fmt=json -out=gosec-report.json -stdout -verbose=text ./...; then
|
||||
print_success "gosec scan completed successfully"
|
||||
else
|
||||
print_error "gosec found security issues!"
|
||||
if [[ -f "gosec-report.json" ]]; then
|
||||
echo "Detailed report saved to gosec-report.json"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Run vulnerability check
|
||||
run_govulncheck() {
|
||||
print_status "Running govulncheck for dependency vulnerabilities..."
|
||||
print_status "Running govulncheck for dependency vulnerabilities..."
|
||||
|
||||
# govulncheck with -json always exits 0, so we need to check the output
|
||||
# Redirect stderr to separate file to avoid corrupting JSON output
|
||||
govulncheck -json ./... >govulncheck-report.json 2>govulncheck-errors.log
|
||||
|
||||
# Check if there were errors during execution
|
||||
if [ -s govulncheck-errors.log ]; then
|
||||
print_warning "govulncheck produced errors (see govulncheck-errors.log)"
|
||||
fi
|
||||
|
||||
# Use jq to detect finding entries in the JSON output
|
||||
# govulncheck emits a stream of Message objects, need to slurp and filter for Finding field
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
# First validate JSON is parseable
|
||||
if ! jq -s '.' govulncheck-report.json >/dev/null 2>&1; then
|
||||
print_error "govulncheck report contains malformed JSON"
|
||||
echo "Unable to parse govulncheck-report.json"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# JSON is valid, now check for findings
|
||||
if jq -s -e 'map(select(.Finding)) | length > 0' govulncheck-report.json >/dev/null 2>&1; then
|
||||
print_error "Vulnerabilities found in dependencies!"
|
||||
echo "Detailed report saved to govulncheck-report.json"
|
||||
return 1
|
||||
else
|
||||
print_success "No known vulnerabilities found in dependencies"
|
||||
fi
|
||||
else
|
||||
# Fallback to grep if jq is not available (case-insensitive to match "Finding")
|
||||
if grep -qi '"finding":' govulncheck-report.json 2>/dev/null; then
|
||||
print_error "Vulnerabilities found in dependencies!"
|
||||
echo "Detailed report saved to govulncheck-report.json"
|
||||
return 1
|
||||
else
|
||||
print_success "No known vulnerabilities found in dependencies"
|
||||
fi
|
||||
fi
|
||||
if govulncheck -json ./... >govulncheck-report.json 2>&1; then
|
||||
print_success "No known vulnerabilities found in dependencies"
|
||||
else
|
||||
if grep -q '"finding"' govulncheck-report.json 2>/dev/null; then
|
||||
print_error "Vulnerabilities found in dependencies!"
|
||||
echo "Detailed report saved to govulncheck-report.json"
|
||||
return 1
|
||||
else
|
||||
print_success "No vulnerabilities found"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Run enhanced golangci-lint with security focus
|
||||
# Run revive with comprehensive linting
|
||||
run_security_lint() {
|
||||
print_status "Running security-focused linting..."
|
||||
print_status "Running comprehensive code quality linting with revive..."
|
||||
|
||||
security_linters="gosec,gocritic,bodyclose,rowserrcheck,misspell,unconvert,unparam,unused,errcheck,ineffassign,staticcheck"
|
||||
|
||||
if golangci-lint run --enable="$security_linters" --timeout=5m; then
|
||||
print_success "Security linting passed"
|
||||
else
|
||||
print_error "Security linting found issues!"
|
||||
return 1
|
||||
fi
|
||||
if revive -config revive.toml -set_exit_status ./...; then
|
||||
print_success "Revive linting passed"
|
||||
else
|
||||
print_error "Revive linting found issues!"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check for potential secrets
|
||||
check_secrets() {
|
||||
print_status "Scanning for potential secrets and sensitive data..."
|
||||
print_status "Scanning for potential secrets and sensitive data..."
|
||||
|
||||
# POSIX-compatible secrets_found flag using a temp file
|
||||
secrets_found_file="$(mktemp)" || {
|
||||
print_error "Failed to create temporary file with mktemp"
|
||||
exit 1
|
||||
}
|
||||
if [ -z "$secrets_found_file" ]; then
|
||||
print_error "mktemp returned empty path"
|
||||
exit 1
|
||||
fi
|
||||
# Clean up temp file on exit and signals (POSIX-portable)
|
||||
trap 'rm -f "$secrets_found_file"' 0 HUP INT TERM
|
||||
local secrets_found=false
|
||||
|
||||
# Common secret patterns (POSIX [[:space:]] and here-doc quoting)
|
||||
cat <<'PATTERNS' | while IFS= read -r pattern; do
|
||||
password[[:space:]]*[:=][[:space:]]*['"][^'"]{3,}['"]
|
||||
secret[[:space:]]*[:=][[:space:]]*['"][^'"]{3,}['"]
|
||||
key[[:space:]]*[:=][[:space:]]*['"][^'"]{8,}['"]
|
||||
token[[:space:]]*[:=][[:space:]]*['"][^'"]{8,}['"]
|
||||
api_?key[[:space:]]*[:=][[:space:]]*['"][^'"]{8,}['"]
|
||||
aws_?access_?key
|
||||
aws_?secret
|
||||
AKIA[0-9A-Z]{16}
|
||||
github_?token
|
||||
private_?key
|
||||
PATTERNS
|
||||
if [ -n "$pattern" ]; then
|
||||
if find . -type f -name "*.go" -exec grep -i -E -H -n -e "$pattern" {} + 2>/dev/null | grep -q .; then
|
||||
print_warning "Potential secret pattern found: $pattern"
|
||||
touch "$secrets_found_file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
# Common secret patterns
|
||||
local patterns=(
|
||||
"password\s*[:=]\s*['\"][^'\"]{3,}['\"]"
|
||||
"secret\s*[:=]\s*['\"][^'\"]{3,}['\"]"
|
||||
"key\s*[:=]\s*['\"][^'\"]{8,}['\"]"
|
||||
"token\s*[:=]\s*['\"][^'\"]{8,}['\"]"
|
||||
"api_?key\s*[:=]\s*['\"][^'\"]{8,}['\"]"
|
||||
"aws_?access_?key"
|
||||
"aws_?secret"
|
||||
"AKIA[0-9A-Z]{16}" # AWS Access Key pattern
|
||||
"github_?token"
|
||||
"private_?key"
|
||||
)
|
||||
|
||||
if [ -f "$secrets_found_file" ]; then
|
||||
secrets_found=true
|
||||
else
|
||||
secrets_found=false
|
||||
fi
|
||||
for pattern in "${patterns[@]}"; do
|
||||
if grep -r -i -E "$pattern" --include="*.go" . 2>/dev/null; then
|
||||
print_warning "Potential secret pattern found: $pattern"
|
||||
secrets_found=true
|
||||
fi
|
||||
done
|
||||
|
||||
# Check git history for secrets (last 10 commits)
|
||||
if git log --oneline -10 2>/dev/null | grep -i -E "(password|secret|key|token)" >/dev/null 2>&1; then
|
||||
print_warning "Potential secrets mentioned in recent commit messages"
|
||||
secrets_found=true
|
||||
fi
|
||||
# Check git history for secrets (last 10 commits)
|
||||
if git log --oneline -10 | grep -i -E "(password|secret|key|token)" >/dev/null 2>&1; then
|
||||
print_warning "Potential secrets mentioned in recent commit messages"
|
||||
secrets_found=true
|
||||
fi
|
||||
|
||||
if [ "$secrets_found" = true ]; then
|
||||
print_warning "Potential secrets detected. Please review manually."
|
||||
return 1
|
||||
else
|
||||
print_success "No obvious secrets detected"
|
||||
fi
|
||||
if [[ "$secrets_found" = true ]]; then
|
||||
print_warning "Potential secrets detected. Please review manually."
|
||||
return 1
|
||||
else
|
||||
print_success "No obvious secrets detected"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check for hardcoded network addresses
|
||||
check_hardcoded_addresses() {
|
||||
print_status "Checking for hardcoded network addresses..."
|
||||
print_status "Checking for hardcoded network addresses..."
|
||||
|
||||
addresses_found=false
|
||||
local addresses_found=false
|
||||
|
||||
# Look for IP addresses (excluding common safe ones)
|
||||
if grep -r -E "([0-9]{1,3}\.){3}[0-9]{1,3}" --include="*.go" . 2>/dev/null |
|
||||
grep -v -E "(127\.0\.0\.1|0\.0\.0\.0|255\.255\.255\.255|localhost)" >/dev/null 2>&1; then
|
||||
print_warning "Hardcoded IP addresses found:"
|
||||
grep -r -E "([0-9]{1,3}\.){3}[0-9]{1,3}" --include="*.go" . 2>/dev/null |
|
||||
grep -v -E "(127\.0\.0\.1|0\.0\.0\.0|255\.255\.255\.255|localhost)" || true
|
||||
addresses_found=true
|
||||
fi
|
||||
# Look for IP addresses (excluding common safe ones)
|
||||
if grep -r -E "([0-9]{1,3}\.){3}[0-9]{1,3}" --include="*.go" . |
|
||||
grep -v -E "(127\.0\.0\.1|0\.0\.0\.0|255\.255\.255\.255|localhost)" >/dev/null 2>&1; then
|
||||
print_warning "Hardcoded IP addresses found:"
|
||||
grep -r -E "([0-9]{1,3}\.){3}[0-9]{1,3}" --include="*.go" . |
|
||||
grep -v -E "(127\.0\.0\.1|0\.0\.0\.0|255\.255\.255\.255|localhost)" || true
|
||||
addresses_found=true
|
||||
fi
|
||||
|
||||
# Look for URLs (excluding documentation examples and comments)
|
||||
if grep -r -E "https?://[^/\s]+" --include="*.go" . 2>/dev/null |
|
||||
grep -v -E "(example\.com|localhost|127\.0\.0\.1|\$\{|//.*https?://)" >/dev/null 2>&1; then
|
||||
print_warning "Hardcoded URLs found:"
|
||||
grep -r -E "https?://[^/\s]+" --include="*.go" . 2>/dev/null |
|
||||
grep -v -E "(example\.com|localhost|127\.0\.0\.1|\$\{|//.*https?://)" || true
|
||||
addresses_found=true
|
||||
fi
|
||||
# Look for URLs (excluding documentation examples)
|
||||
if grep -r -E "https?://[^/\s]+" --include="*.go" . |
|
||||
grep -v -E "(example\.com|no-color\.org|localhost|127\.0\.0\.1|\$\{)" >/dev/null 2>&1; then
|
||||
print_warning "Hardcoded URLs found:"
|
||||
grep -r -E "https?://[^/\s]+" --include="*.go" . |
|
||||
grep -v -E "(example\.com|localhost|127\.0\.0\.1|\$\{)" || true
|
||||
addresses_found=true
|
||||
fi
|
||||
|
||||
if [ "$addresses_found" = true ]; then
|
||||
print_warning "Hardcoded network addresses detected. Please review."
|
||||
return 1
|
||||
else
|
||||
print_success "No hardcoded network addresses found"
|
||||
fi
|
||||
if [[ "$addresses_found" = true ]]; then
|
||||
print_warning "Hardcoded network addresses detected. Please review."
|
||||
return 1
|
||||
else
|
||||
print_success "No hardcoded network addresses found"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check Docker security (if Dockerfile exists)
|
||||
check_docker_security() {
|
||||
if [ -f "Dockerfile" ]; then
|
||||
print_status "Checking Docker security..."
|
||||
if [[ -f "Dockerfile" ]]; then
|
||||
print_status "Checking Docker security..."
|
||||
|
||||
# Basic Dockerfile security checks
|
||||
docker_issues=false
|
||||
# Basic Dockerfile security checks
|
||||
local docker_issues=false
|
||||
|
||||
if grep -q "^USER root" Dockerfile; then
|
||||
print_warning "Dockerfile runs as root user"
|
||||
docker_issues=true
|
||||
fi
|
||||
if grep -q "^USER root" Dockerfile; then
|
||||
print_warning "Dockerfile runs as root user"
|
||||
docker_issues=true
|
||||
fi
|
||||
|
||||
if ! grep -q "^USER " Dockerfile; then
|
||||
print_warning "Dockerfile doesn't specify a non-root user"
|
||||
docker_issues=true
|
||||
fi
|
||||
if ! grep -q "^USER " Dockerfile; then
|
||||
print_warning "Dockerfile doesn't specify a non-root user"
|
||||
docker_issues=true
|
||||
fi
|
||||
|
||||
if grep -Eq 'RUN.*(wget|curl)' Dockerfile && ! grep -Eq 'rm.*(wget|curl)' Dockerfile; then
|
||||
print_warning "Dockerfile may leave curl/wget installed"
|
||||
docker_issues=true
|
||||
fi
|
||||
if grep -q "RUN.*wget\|RUN.*curl" Dockerfile && ! grep -q "rm.*wget\|rm.*curl" Dockerfile; then
|
||||
print_warning "Dockerfile may leave curl/wget installed"
|
||||
docker_issues=true
|
||||
fi
|
||||
|
||||
if [ "$docker_issues" = true ]; then
|
||||
print_warning "Docker security issues detected"
|
||||
return 1
|
||||
else
|
||||
print_success "Docker security check passed"
|
||||
fi
|
||||
else
|
||||
print_status "No Dockerfile found, skipping Docker security check"
|
||||
fi
|
||||
if [[ "$docker_issues" = true ]]; then
|
||||
print_warning "Docker security issues detected"
|
||||
return 1
|
||||
else
|
||||
print_success "Docker security check passed"
|
||||
fi
|
||||
else
|
||||
print_status "No Dockerfile found, skipping Docker security check"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check file permissions
|
||||
check_file_permissions() {
|
||||
print_status "Checking file permissions..."
|
||||
print_status "Checking file permissions..."
|
||||
|
||||
perm_issues=false
|
||||
local perm_issues=false
|
||||
|
||||
# Check for overly permissive files (using octal for cross-platform compatibility)
|
||||
# -perm -002 finds files writable by others (works on both BSD and GNU find)
|
||||
if find . -type f -perm -002 -not -path "./.git/*" 2>/dev/null | grep -q .; then
|
||||
print_warning "World-writable files found:"
|
||||
find . -type f -perm -002 -not -path "./.git/*" 2>/dev/null || true
|
||||
perm_issues=true
|
||||
fi
|
||||
# Check for overly permissive files
|
||||
if find . -type f -perm +002 -not -path "./.git/*" | grep -q .; then
|
||||
print_warning "World-writable files found:"
|
||||
find . -type f -perm +002 -not -path "./.git/*" || true
|
||||
perm_issues=true
|
||||
fi
|
||||
|
||||
# Check for executable files that shouldn't be
|
||||
# -perm -111 finds files executable by anyone (works on both BSD and GNU find)
|
||||
if find . -type f -name "*.go" -perm -111 -not -path "./.git/*" 2>/dev/null | grep -q .; then
|
||||
print_warning "Executable Go files found (should not be executable):"
|
||||
find . -type f -name "*.go" -perm -111 -not -path "./.git/*" 2>/dev/null || true
|
||||
perm_issues=true
|
||||
fi
|
||||
# Check for executable files that shouldn't be
|
||||
if find . -type f -name "*.go" -perm +111 | grep -q .; then
|
||||
print_warning "Executable Go files found (should not be executable):"
|
||||
find . -type f -name "*.go" -perm +111 || true
|
||||
perm_issues=true
|
||||
fi
|
||||
|
||||
if [ "$perm_issues" = true ]; then
|
||||
print_warning "File permission issues detected"
|
||||
return 1
|
||||
else
|
||||
print_success "File permissions check passed"
|
||||
fi
|
||||
if [[ "$perm_issues" = true ]]; then
|
||||
print_warning "File permission issues detected"
|
||||
return 1
|
||||
else
|
||||
print_success "File permissions check passed"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check Makefile with checkmake
|
||||
check_makefile() {
|
||||
if [ -f "Makefile" ]; then
|
||||
print_status "Checking Makefile with checkmake..."
|
||||
if [[ -f "Makefile" ]]; then
|
||||
print_status "Checking Makefile with checkmake..."
|
||||
|
||||
if checkmake --config=.checkmake Makefile; then
|
||||
print_success "Makefile check passed"
|
||||
else
|
||||
print_error "Makefile issues detected!"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
print_status "No Makefile found, skipping checkmake"
|
||||
fi
|
||||
if checkmake --config=.checkmake Makefile; then
|
||||
print_success "Makefile check passed"
|
||||
else
|
||||
print_error "Makefile issues detected!"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
print_status "No Makefile found, skipping checkmake"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check shell scripts with shfmt
|
||||
check_shell_scripts() {
|
||||
print_status "Checking shell script formatting..."
|
||||
print_status "Checking shell script formatting..."
|
||||
|
||||
if find . -name "*.sh" -type f 2>/dev/null | head -1 | grep -q .; then
|
||||
if shfmt -d .; then
|
||||
print_success "Shell script formatting check passed"
|
||||
else
|
||||
print_error "Shell script formatting issues detected!"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
print_status "No shell scripts found, skipping shfmt check"
|
||||
fi
|
||||
if find . -name "*.sh" -type f | head -1 | grep -q .; then
|
||||
if shfmt -d .; then
|
||||
print_success "Shell script formatting check passed"
|
||||
else
|
||||
print_error "Shell script formatting issues detected!"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
print_status "No shell scripts found, skipping shfmt check"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check YAML files
|
||||
check_yaml_files() {
|
||||
print_status "Checking YAML files..."
|
||||
print_status "Checking YAML files..."
|
||||
|
||||
if find . \( -name "*.yml" -o -name "*.yaml" \) -type f 2>/dev/null | head -1 | grep -q .; then
|
||||
if yamllint .; then
|
||||
print_success "YAML files check passed"
|
||||
else
|
||||
print_error "YAML file issues detected!"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
print_status "No YAML files found, skipping yamllint check"
|
||||
fi
|
||||
if find . -name "*.yml" -o -name "*.yaml" -type f | head -1 | grep -q .; then
|
||||
if yamllint -c .yamllint .; then
|
||||
print_success "YAML files check passed"
|
||||
else
|
||||
print_error "YAML file issues detected!"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
print_status "No YAML files found, skipping yamllint check"
|
||||
fi
|
||||
}
|
||||
|
||||
# Generate security report
|
||||
generate_report() {
|
||||
print_status "Generating security scan report..."
|
||||
print_status "Generating security scan report..."
|
||||
|
||||
report_file="security-report.md"
|
||||
local report_file="security-report.md"
|
||||
|
||||
cat >"$report_file" <<EOF
|
||||
cat >"$report_file" <<EOF
|
||||
# Security Scan Report
|
||||
|
||||
**Generated:** $(date)
|
||||
@@ -489,7 +264,7 @@ generate_report() {
|
||||
### Security Tools Used
|
||||
- gosec (Go security analyzer)
|
||||
- govulncheck (Vulnerability database checker)
|
||||
- golangci-lint (Static analysis with security linters)
|
||||
- revive (Code quality and linting)
|
||||
- checkmake (Makefile linting)
|
||||
- shfmt (Shell script formatting)
|
||||
- yamllint (YAML file validation)
|
||||
@@ -519,65 +294,66 @@ generate_report() {
|
||||
*This report was generated automatically by the gibidify security scanning script.*
|
||||
EOF
|
||||
|
||||
print_success "Security report generated: $report_file"
|
||||
print_success "Security report generated: $report_file"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
echo "🔒 gibidify Security Scanner"
|
||||
echo "=========================="
|
||||
echo
|
||||
echo "🔒 gibidify Security Scanner"
|
||||
echo "=========================="
|
||||
echo
|
||||
|
||||
exit_code=0
|
||||
local exit_code=0
|
||||
|
||||
check_dependencies
|
||||
echo
|
||||
check_dependencies
|
||||
echo
|
||||
|
||||
# Run all security checks
|
||||
run_gosec || exit_code=1
|
||||
echo
|
||||
# Run all security checks
|
||||
run_gosec || exit_code=1
|
||||
echo
|
||||
|
||||
run_govulncheck || exit_code=1
|
||||
echo
|
||||
run_govulncheck || exit_code=1
|
||||
echo
|
||||
|
||||
run_security_lint || exit_code=1
|
||||
echo
|
||||
run_security_lint || exit_code=1
|
||||
echo
|
||||
|
||||
check_secrets || exit_code=1
|
||||
echo
|
||||
check_secrets || exit_code=1
|
||||
echo
|
||||
|
||||
check_hardcoded_addresses || exit_code=1
|
||||
echo
|
||||
check_hardcoded_addresses || exit_code=1
|
||||
echo
|
||||
|
||||
check_docker_security || exit_code=1
|
||||
echo
|
||||
check_docker_security || exit_code=1
|
||||
echo
|
||||
|
||||
check_file_permissions || exit_code=1
|
||||
echo
|
||||
check_file_permissions || exit_code=1
|
||||
echo
|
||||
|
||||
check_makefile || exit_code=1
|
||||
echo
|
||||
check_makefile || exit_code=1
|
||||
echo
|
||||
|
||||
check_shell_scripts || exit_code=1
|
||||
echo
|
||||
check_shell_scripts || exit_code=1
|
||||
echo
|
||||
|
||||
check_yaml_files || exit_code=1
|
||||
echo
|
||||
check_yaml_files || exit_code=1
|
||||
echo
|
||||
|
||||
generate_report
|
||||
echo
|
||||
generate_report
|
||||
echo
|
||||
|
||||
if [ "$exit_code" -eq 0 ]; then
|
||||
print_success "🎉 All security checks passed!"
|
||||
else
|
||||
print_error "❌ Security issues detected. Please review the reports and fix identified issues."
|
||||
print_status "Generated reports:"
|
||||
print_status "- gosec-report.json (if exists)"
|
||||
print_status "- govulncheck-report.json (if exists)"
|
||||
print_status "- security-report.md"
|
||||
fi
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
print_success "🎉 All security checks passed!"
|
||||
else
|
||||
print_error "❌ Security issues detected. Please review the reports and fix identified issues."
|
||||
print_status "Generated reports:"
|
||||
print_status "- gosec-report.json (if exists)"
|
||||
print_status "- govulncheck-report.json (if exists)"
|
||||
print_status "- security-report.md"
|
||||
fi
|
||||
|
||||
exit "$exit_code"
|
||||
exit $exit_code
|
||||
}
|
||||
|
||||
# Run main function
|
||||
|
||||
49
scripts/security.sh
Executable file
49
scripts/security.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Shared security scanning functions
|
||||
|
||||
# Run gosec in parallel on all Go directories
|
||||
run_gosec_parallel() {
|
||||
local exit_code=0
|
||||
local pids=()
|
||||
local go_dirs=("./benchmark" "./cli" "./cmd" "./config" "./fileproc" "./metrics" "./shared" "./templates" "./testutil" ".")
|
||||
|
||||
# Start gosec for each directory in background
|
||||
for dir in "${go_dirs[@]}"; do
|
||||
# Skip non-existent directories
|
||||
if [[ ! -d "$dir" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$dir" == "." ]]; then
|
||||
# For root directory, scan only .go files directly (not subdirectories)
|
||||
gosec -fmt=text -quiet -exclude-dir=vendor -exclude-dir=.git -exclude-dir=benchmark -exclude-dir=cli -exclude-dir=cmd -exclude-dir=config -exclude-dir=fileproc -exclude-dir=metrics -exclude-dir=shared -exclude-dir=templates -exclude-dir=testutil . >"gosec_${dir//\//_}.log" 2>&1 &
|
||||
else
|
||||
# For subdirectories, exclude vendor and .git
|
||||
gosec -fmt=text -quiet -exclude-dir=vendor -exclude-dir=.git "$dir" >"gosec_${dir//\//_}.log" 2>&1 &
|
||||
fi
|
||||
pids+=($!)
|
||||
done
|
||||
|
||||
# Wait for all gosec processes to complete and check their exit codes
|
||||
for i in "${!pids[@]}"; do
|
||||
local pid="${pids[$i]}"
|
||||
local dir="${go_dirs[$i]}"
|
||||
if ! wait "$pid"; then
|
||||
echo "gosec failed for directory: $dir"
|
||||
cat "gosec_${dir//\//_}.log"
|
||||
# Keep log for inspection/artifacts on failure
|
||||
exit_code=1
|
||||
else
|
||||
# Clean up log file if successful
|
||||
rm -f "gosec_${dir//\//_}.log"
|
||||
fi
|
||||
done
|
||||
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# If this file is sourced, export the functions
|
||||
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
|
||||
export -f run_gosec_parallel
|
||||
fi
|
||||
115
scripts/update-deps.sh
Executable file
115
scripts/update-deps.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
cd "$PROJECT_ROOT" || {
|
||||
echo "Failed to change directory to $PROJECT_ROOT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# shellcheck source=scripts/install-tools.sh
|
||||
source "$SCRIPT_DIR/install-tools.sh"
|
||||
|
||||
# Track overall exit status
|
||||
exit_code=0
|
||||
|
||||
print_status "=== Updating Go Dependencies ==="
|
||||
|
||||
# Function to handle rollback if needed
|
||||
rollback() {
|
||||
if [[ -f go.mod.backup && -f go.sum.backup ]]; then
|
||||
print_warning "Rolling back changes due to errors..."
|
||||
mv go.mod.backup go.mod
|
||||
mv go.sum.backup go.sum
|
||||
print_success "Rollback completed"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to cleanup backup files
|
||||
cleanup() {
|
||||
if [[ -f go.mod.backup ]]; then
|
||||
rm go.mod.backup
|
||||
fi
|
||||
if [[ -f go.sum.backup ]]; then
|
||||
rm go.sum.backup
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Trap to ensure cleanup on exit
|
||||
trap cleanup EXIT
|
||||
|
||||
print_status "Creating backup of go.mod and go.sum..."
|
||||
cp go.mod go.mod.backup
|
||||
cp go.sum go.sum.backup
|
||||
|
||||
print_status "Checking current module status..."
|
||||
if ! go mod verify; then
|
||||
print_error "Current module verification failed"
|
||||
exit_code=1
|
||||
exit $exit_code
|
||||
fi
|
||||
|
||||
print_status "Updating dependencies with 'go get -u'..."
|
||||
if ! go get -u ./...; then
|
||||
print_error "Failed to update dependencies"
|
||||
rollback
|
||||
exit_code=1
|
||||
exit $exit_code
|
||||
fi
|
||||
|
||||
print_status "Running 'go mod tidy'..."
|
||||
if ! go mod tidy; then
|
||||
print_error "Failed to tidy module dependencies"
|
||||
rollback
|
||||
exit_code=1
|
||||
exit $exit_code
|
||||
fi
|
||||
|
||||
print_status "Verifying updated dependencies..."
|
||||
if ! go mod verify; then
|
||||
print_error "Module verification failed after updates"
|
||||
rollback
|
||||
exit_code=1
|
||||
exit $exit_code
|
||||
fi
|
||||
|
||||
print_status "Running vulnerability check..."
|
||||
if command -v govulncheck >/dev/null 2>&1; then
|
||||
if ! govulncheck ./...; then
|
||||
print_warning "Vulnerability check failed - review output above"
|
||||
print_warning "Consider updating specific vulnerable packages or pinning versions"
|
||||
# Don't fail the script for vulnerabilities, just warn
|
||||
fi
|
||||
else
|
||||
print_warning "govulncheck not found - install with: go install golang.org/x/vuln/cmd/govulncheck@latest"
|
||||
fi
|
||||
|
||||
print_status "Running basic build test..."
|
||||
if ! go build ./...; then
|
||||
print_error "Build failed after dependency updates"
|
||||
rollback
|
||||
exit_code=1
|
||||
exit $exit_code
|
||||
fi
|
||||
|
||||
print_status "Running quick test to ensure functionality..."
|
||||
if ! go test -short ./...; then
|
||||
print_error "Tests failed after dependency updates"
|
||||
rollback
|
||||
exit_code=1
|
||||
exit $exit_code
|
||||
fi
|
||||
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
print_success "Dependencies updated successfully!"
|
||||
print_success "Review the changes with 'git diff' before committing"
|
||||
cleanup
|
||||
else
|
||||
print_error "Dependency update failed"
|
||||
fi
|
||||
|
||||
exit $exit_code
|
||||
Reference in New Issue
Block a user