feat: update go to 1.25, add permissions and envs (#49)

* chore(ci): update go to 1.25, add permissions and envs
* fix(ci): update pr-lint.yml
* chore: update go, fix linting
* fix: tests and linting
* fix(lint): lint fixes, renovate should now pass
* fix: updates, security upgrades
* chore: workflow updates, lint
* fix: more lint, checkmake, and other fixes
* fix: more lint, convert scripts to POSIX compliant
* fix: simplify codeql workflow
* tests: increase test coverage, fix found issues
* fix(lint): editorconfig checking, add to linters
* fix(lint): shellcheck, add to linters
* fix(lint): apply cr comment suggestions
* fix(ci): remove step-security/harden-runner
* fix(lint): remove duplication, apply cr fixes
* fix(ci): tests in CI/CD pipeline
* chore(lint): deduplication of strings
* fix(lint): apply cr comment suggestions
* fix(ci): actionlint
* fix(lint): apply cr comment suggestions
* chore: lint, add deps management
This commit is contained in:
2025-10-10 12:14:42 +03:00
committed by GitHub
parent 958f5952a0
commit 3f65b813bd
100 changed files with 6997 additions and 1225 deletions

View File

@@ -1,25 +1,31 @@
Available targets:
install-tools - Install required linting and development tools
lint - Run all linters (Go, Makefile, shell, YAML)
lint-fix - Run linters with auto-fix enabled
lint-verbose - Run linters with verbose output
test - Run tests
coverage - Run tests with coverage
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, 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
Security targets:
security - Run comprehensive security scan
security-full - Run full security analysis with all tools
vuln-check - Check for dependency vulnerabilities
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
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.
Run 'make <target>' to execute a specific target.

View File

@@ -1,14 +1,23 @@
#!/bin/bash
set -e
#!/bin/sh
set -eu
echo "Running golangci-lint..."
golangci-lint run ./...
echo "Running revive..."
revive -config revive.toml -formatter friendly ./...
echo "Running checkmake..."
checkmake --config=.checkmake Makefile
echo "Running editorconfig-checker..."
editorconfig-checker
echo "Running shellcheck..."
shellcheck scripts/*.sh
echo "Running shfmt check..."
shfmt -d .
shfmt -d -i 0 -ci .
echo "Running yamllint..."
yamllint -c .yamllint .
yamllint .

View File

@@ -1,10 +1,10 @@
#!/bin/bash
set -euo pipefail
#!/bin/sh
set -eu
# Security Scanning Script for gibidify
# This script runs comprehensive security checks locally and in CI
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$PROJECT_ROOT"
@@ -20,63 +20,177 @@ NC='\033[0m' # No Color
# Function to print status
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
printf "${BLUE}[INFO]${NC} %s\n" "$1"
}
print_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
printf "${YELLOW}[WARN]${NC} %s\n" "$1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
printf "${RED}[ERROR]${NC} %s\n" "$1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
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..."
local missing_tools=()
missing_tools=""
if ! command -v go &>/dev/null; then
missing_tools+=("go")
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; then
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; then
if ! command -v gosec >/dev/null 2>&1; then
print_warning "gosec not found, installing..."
go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest
go install github.com/securego/gosec/v2/cmd/gosec@latest
fi
if ! command -v govulncheck &>/dev/null; then
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; then
if ! command -v checkmake >/dev/null 2>&1; then
print_warning "checkmake not found, installing..."
go install github.com/mrtazz/checkmake/cmd/checkmake@latest
go install github.com/checkmake/checkmake/cmd/checkmake@latest
fi
if ! command -v shfmt &>/dev/null; then
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; then
print_warning "yamllint not found, installing..."
go install github.com/excilsploft/yamllint@latest
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 [ ${#missing_tools[@]} -ne 0 ]; then
print_error "Missing required tools: ${missing_tools[*]}"
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
@@ -103,15 +217,41 @@ run_gosec() {
run_govulncheck() {
print_status "Running govulncheck for dependency vulnerabilities..."
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
# 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 vulnerabilities found"
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
}
@@ -120,7 +260,7 @@ run_govulncheck() {
run_security_lint() {
print_status "Running security-focused linting..."
local security_linters="gosec,gocritic,bodyclose,rowserrcheck,misspell,unconvert,unparam,unused,errcheck,ineffassign,staticcheck"
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"
@@ -134,31 +274,47 @@ run_security_lint() {
check_secrets() {
print_status "Scanning for potential secrets and sensitive data..."
local secrets_found=false
# 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
# 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"
)
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
# 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
if [ -f "$secrets_found_file" ]; then
secrets_found=true
else
secrets_found=false
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
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
@@ -175,23 +331,23 @@ check_secrets() {
check_hardcoded_addresses() {
print_status "Checking for hardcoded network addresses..."
local addresses_found=false
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" . |
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" . |
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 URLs (excluding documentation examples)
if grep -r -E "https?://[^/\s]+" --include="*.go" . |
grep -v -E "(example\.com|localhost|127\.0\.0\.1|\$\{)" >/dev/null 2>&1; then
# 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" . |
grep -v -E "(example\.com|localhost|127\.0\.0\.1|\$\{)" || true
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
@@ -209,7 +365,7 @@ check_docker_security() {
print_status "Checking Docker security..."
# Basic Dockerfile security checks
local docker_issues=false
docker_issues=false
if grep -q "^USER root" Dockerfile; then
print_warning "Dockerfile runs as root user"
@@ -221,7 +377,7 @@ check_docker_security() {
docker_issues=true
fi
if grep -q "RUN.*wget\|RUN.*curl" Dockerfile && ! grep -q "rm.*wget\|rm.*curl" Dockerfile; then
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
@@ -241,19 +397,21 @@ check_docker_security() {
check_file_permissions() {
print_status "Checking file permissions..."
local perm_issues=false
perm_issues=false
# Check for overly permissive files
if find . -type f -perm /o+w -not -path "./.git/*" | grep -q .; then
# 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 /o+w -not -path "./.git/*" || true
find . -type f -perm -002 -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 /a+x | grep -q .; then
# -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 /a+x || true
find . -type f -name "*.go" -perm -111 -not -path "./.git/*" 2>/dev/null || true
perm_issues=true
fi
@@ -285,7 +443,7 @@ check_makefile() {
check_shell_scripts() {
print_status "Checking shell script formatting..."
if find . -name "*.sh" -type f | head -1 | grep -q .; then
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
@@ -301,8 +459,8 @@ check_shell_scripts() {
check_yaml_files() {
print_status "Checking YAML files..."
if find . -name "*.yml" -o -name "*.yaml" -type f | head -1 | grep -q .; then
if yamllint -c .yamllint .; then
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!"
@@ -317,7 +475,7 @@ check_yaml_files() {
generate_report() {
print_status "Generating security scan report..."
local report_file="security-report.md"
report_file="security-report.md"
cat >"$report_file" <<EOF
# Security Scan Report
@@ -370,7 +528,7 @@ main() {
echo "=========================="
echo
local exit_code=0
exit_code=0
check_dependencies
echo
@@ -409,7 +567,7 @@ main() {
generate_report
echo
if [ $exit_code -eq 0 ]; then
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."
@@ -419,7 +577,7 @@ main() {
print_status "- security-report.md"
fi
exit $exit_code
exit "$exit_code"
}
# Run main function