Comprehensive validation tooling for SecuBox module generation and git workflow. New Tools: ----------- 1. validate-module-generation.sh - Deep validation of single module during/after generation - Checks 9 categories: Makefile, RPCD, ACL, Menu, JS Views, UCI, Permissions, Security, Docs - Validates RPCD naming (luci.* prefix) vs JavaScript ubus objects - Validates menu paths vs actual view file locations - Cross-checks RPC methods between JavaScript and RPCD - Security scans for hardcoded credentials and dangerous commands - Exit codes: 0=pass, 1=critical errors 2. pre-push-validation.sh - Git pre-push hook that blocks push if critical errors found - Validates all modules before allowing remote push - Detects modified modules and runs comprehensive checks - Prevents deployment of broken modules - Can be bypassed with --no-verify (not recommended) 3. install-git-hooks.sh - One-command installation of git hooks - Creates symlink from .git/hooks/pre-push to pre-push-validation.sh - Enables automatic validation before every push Documentation: -------------- 4. VALIDATION-GUIDE.md - Complete guide to validation workflow - Critical naming convention rules with examples - Module generation checklist (5 phases) - Common validation errors and fixes - Best practices and troubleshooting - CI/CD integration examples Updated: -------- 5. secubox-tools/README.md - Added descriptions for new validation tools - Added recommended workflows for module generation and modification - Organized tools into categories (Validation, Maintenance) Key Validation Rules Enforced: ------------------------------- ✓ RPCD script name MUST match ubus object name (exact match with luci. prefix) Example: object: 'luci.cdn-cache' → file: luci.cdn-cache ✓ Menu paths MUST match view file locations (prevent HTTP 404) Example: "path": "cdn-cache/overview" → view/cdn-cache/overview.js ✓ All ubus objects MUST use luci.* prefix ✅ 'luci.cdn-cache' ❌ 'cdn-cache' ✓ ACL permissions MUST cover all RPCD methods ✓ JavaScript RPC method calls MUST exist in RPCD implementation ✓ RPCD scripts MUST be executable (chmod +x) ✓ All JSON files MUST have valid syntax ✓ Security: No hardcoded credentials or dangerous commands Benefits: --------- - Prevents RPC errors (-32000: Object not found) - Prevents HTTP 404 errors (view files not found) - Catches naming mismatches before deployment - Ensures ACL permissions are complete - Enforces consistent naming conventions - Blocks broken modules from being pushed - Provides detailed error messages with fix suggestions Usage: ------ # Validate new/modified module: ./secubox-tools/validate-module-generation.sh luci-app-cdn-cache # Install git hooks (one-time): ./secubox-tools/install-git-hooks.sh # After installation, validation runs automatically: git push # Pre-push validation blocks if errors found # Manual pre-push validation: ./secubox-tools/pre-push-validation.sh See VALIDATION-GUIDE.md for complete documentation and workflows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
380 lines
13 KiB
Bash
Executable File
380 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# pre-push-validation.sh
|
|
# ======================
|
|
# Git pre-push hook for SecuBox repository
|
|
# Validates all modules before allowing push to remote
|
|
#
|
|
# Installation:
|
|
# ln -s ../../secubox-tools/pre-push-validation.sh .git/hooks/pre-push
|
|
# chmod +x .git/hooks/pre-push
|
|
#
|
|
# Or run manually:
|
|
# ./secubox-tools/pre-push-validation.sh
|
|
#
|
|
|
|
set -e
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m'
|
|
|
|
ERRORS=0
|
|
WARNINGS=0
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
echo ""
|
|
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${CYAN}║${NC} ${BOLD}SecuBox Pre-Push Validation${NC}"
|
|
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
|
|
error() {
|
|
echo -e "${RED}✗ $1${NC}"
|
|
((ERRORS++))
|
|
}
|
|
|
|
warn() {
|
|
echo -e "${YELLOW}⚠ $1${NC}"
|
|
((WARNINGS++))
|
|
}
|
|
|
|
success() {
|
|
echo -e "${GREEN}✓ $1${NC}"
|
|
}
|
|
|
|
info() {
|
|
echo -e "${CYAN}→ $1${NC}"
|
|
}
|
|
|
|
section() {
|
|
echo ""
|
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo -e "${BLUE} $1${NC}"
|
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
}
|
|
|
|
cd "$REPO_ROOT"
|
|
|
|
# ============================================
|
|
# 1. Check Git Status
|
|
# ============================================
|
|
section "1. Git Status Check"
|
|
|
|
if git diff --cached --quiet; then
|
|
warn "No staged changes to commit"
|
|
else
|
|
success "Staged changes detected"
|
|
fi
|
|
|
|
# Get list of modified module directories
|
|
MODIFIED_MODULES=$(git diff --cached --name-only | grep '^luci-app-' | cut -d'/' -f1 | sort -u)
|
|
|
|
if [ -z "$MODIFIED_MODULES" ]; then
|
|
info "No module files modified in this commit"
|
|
else
|
|
info "Modified modules in this commit:"
|
|
echo "$MODIFIED_MODULES" | while read -r mod; do
|
|
echo " - $mod"
|
|
done
|
|
fi
|
|
|
|
# ============================================
|
|
# 2. Critical Naming Convention Checks
|
|
# ============================================
|
|
section "2. Critical Naming Conventions"
|
|
|
|
info "Checking RPCD script names match ubus object names..."
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
[ -d "$module_dir" ] || continue
|
|
|
|
module_name=$(basename "$module_dir")
|
|
rpcd_dir="$module_dir/root/usr/libexec/rpcd"
|
|
|
|
if [ -d "$rpcd_dir" ]; then
|
|
rpcd_script=$(find "$rpcd_dir" -type f ! -name "*.md" 2>/dev/null | head -1)
|
|
|
|
if [ -n "$rpcd_script" ]; then
|
|
rpcd_name=$(basename "$rpcd_script")
|
|
|
|
# Extract ubus object from JavaScript
|
|
js_objects=$(find "$module_dir/htdocs" -name "*.js" -type f 2>/dev/null | \
|
|
xargs grep -h "object:" 2>/dev/null | \
|
|
grep -o "'[^']*'" | grep "luci\." | tr -d "'" | sort -u)
|
|
|
|
if [ -n "$js_objects" ]; then
|
|
match_found=false
|
|
|
|
for obj in $js_objects; do
|
|
if [ "$rpcd_name" = "$obj" ]; then
|
|
success "$module_name: RPCD '$rpcd_name' matches ubus object '$obj'"
|
|
match_found=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "$match_found" = false ]; then
|
|
error "$module_name: RPCD '$rpcd_name' does NOT match ubus objects: $js_objects"
|
|
info " Fix: Rename $rpcd_script to one of: $js_objects"
|
|
fi
|
|
fi
|
|
|
|
# Check naming convention
|
|
if [[ ! $rpcd_name == luci.* ]]; then
|
|
error "$module_name: RPCD script '$rpcd_name' missing 'luci.' prefix"
|
|
info " All RPCD scripts MUST start with 'luci.'"
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# ============================================
|
|
# 3. Menu Path Validation
|
|
# ============================================
|
|
section "3. Menu Path vs View File Validation"
|
|
|
|
info "Checking menu paths match view file locations..."
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
[ -d "$module_dir" ] || continue
|
|
|
|
module_name=$(basename "$module_dir")
|
|
menu_file="$module_dir/root/usr/share/luci/menu.d/${module_name}.json"
|
|
|
|
if [ -f "$menu_file" ]; then
|
|
# Extract paths from menu
|
|
menu_paths=$(grep -o '"path":\s*"[^"]*"' "$menu_file" | cut -d'"' -f4)
|
|
|
|
for path in $menu_paths; do
|
|
view_file="$module_dir/htdocs/luci-static/resources/view/${path}.js"
|
|
|
|
if [ -f "$view_file" ]; then
|
|
success "$module_name: Menu path '$path' → file exists"
|
|
else
|
|
error "$module_name: Menu path '$path' → file NOT FOUND"
|
|
info " Expected: $view_file"
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
|
|
# ============================================
|
|
# 4. JSON Validation
|
|
# ============================================
|
|
section "4. JSON Syntax Validation"
|
|
|
|
info "Validating all JSON files..."
|
|
echo ""
|
|
|
|
find . -name "*.json" -path "*/luci-app-*" ! -path "*/.git/*" | while read -r json_file; do
|
|
if python3 -m json.tool "$json_file" > /dev/null 2>&1; then
|
|
success "$(echo "$json_file" | cut -d'/' -f1-2): $(basename "$json_file") is valid"
|
|
else
|
|
error "Invalid JSON: $json_file"
|
|
fi
|
|
done
|
|
|
|
# ============================================
|
|
# 5. RPCD Executable Check
|
|
# ============================================
|
|
section "5. RPCD Script Permissions"
|
|
|
|
info "Checking RPCD scripts are executable..."
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
[ -d "$module_dir" ] || continue
|
|
|
|
module_name=$(basename "$module_dir")
|
|
rpcd_dir="$module_dir/root/usr/libexec/rpcd"
|
|
|
|
if [ -d "$rpcd_dir" ]; then
|
|
find "$rpcd_dir" -type f ! -name "*.md" 2>/dev/null | while read -r script; do
|
|
if [ -x "$script" ]; then
|
|
success "$module_name: $(basename "$script") is executable"
|
|
else
|
|
error "$module_name: $(basename "$script") is NOT executable"
|
|
info " Fix: chmod +x $script"
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
|
|
# ============================================
|
|
# 6. ACL Method Coverage
|
|
# ============================================
|
|
section "6. ACL Method Coverage"
|
|
|
|
info "Checking ACL permissions cover RPCD methods..."
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
[ -d "$module_dir" ] || continue
|
|
|
|
module_name=$(basename "$module_dir")
|
|
rpcd_dir="$module_dir/root/usr/libexec/rpcd"
|
|
acl_file="$module_dir/root/usr/share/rpcd/acl.d/${module_name}.json"
|
|
|
|
if [ -d "$rpcd_dir" ] && [ -f "$acl_file" ]; then
|
|
rpcd_script=$(find "$rpcd_dir" -type f ! -name "*.md" 2>/dev/null | head -1)
|
|
|
|
if [ -n "$rpcd_script" ]; then
|
|
# Extract methods from RPCD
|
|
rpcd_methods=$(grep -A 50 'case "\$2" in' "$rpcd_script" 2>/dev/null | \
|
|
grep -E '^\s+[a-z_]+\)' | \
|
|
sed 's/[[:space:]]*\(.*\))/\1/' | \
|
|
grep -v '^\*')
|
|
|
|
# Check each method is in ACL
|
|
for method in $rpcd_methods; do
|
|
if grep -q "\"$method\"" "$acl_file"; then
|
|
success "$module_name: Method '$method' is in ACL"
|
|
else
|
|
warn "$module_name: Method '$method' from RPCD not in ACL"
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# ============================================
|
|
# 7. Makefile Validation
|
|
# ============================================
|
|
section "7. Makefile Validation"
|
|
|
|
info "Checking Makefiles have required fields..."
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
[ -d "$module_dir" ] || continue
|
|
|
|
module_name=$(basename "$module_dir")
|
|
makefile="$module_dir/Makefile"
|
|
|
|
if [ ! -f "$makefile" ]; then
|
|
error "$module_name: Makefile missing"
|
|
continue
|
|
fi
|
|
|
|
# Check required fields
|
|
required_fields=("PKG_NAME" "PKG_VERSION" "PKG_RELEASE" "PKG_LICENSE" "LUCI_TITLE")
|
|
missing=()
|
|
|
|
for field in "${required_fields[@]}"; do
|
|
if ! grep -q "^${field}" "$makefile"; then
|
|
missing+=("$field")
|
|
fi
|
|
done
|
|
|
|
if [ ${#missing[@]} -eq 0 ]; then
|
|
success "$module_name: Makefile has all required fields"
|
|
else
|
|
error "$module_name: Makefile missing fields: ${missing[*]}"
|
|
fi
|
|
|
|
# Check PKG_NAME matches directory
|
|
if grep -q "^PKG_NAME.*${module_name}" "$makefile"; then
|
|
success "$module_name: PKG_NAME matches directory"
|
|
else
|
|
warn "$module_name: PKG_NAME might not match directory"
|
|
fi
|
|
done
|
|
|
|
# ============================================
|
|
# 8. Security Checks
|
|
# ============================================
|
|
section "8. Security Checks"
|
|
|
|
info "Scanning for potential security issues..."
|
|
echo ""
|
|
|
|
# Check for hardcoded secrets
|
|
SECRET_PATTERNS="password\s*=\s*['\"][^'\"]*['\"]|api_key\s*=|secret\s*=\s*['\"][^'\"]*['\"]|token\s*=\s*['\"][^'\"]*['\"]"
|
|
|
|
for module_dir in luci-app-*/; do
|
|
[ -d "$module_dir" ] || continue
|
|
|
|
module_name=$(basename "$module_dir")
|
|
|
|
# Check RPCD scripts
|
|
if [ -d "$module_dir/root/usr/libexec/rpcd" ]; then
|
|
if find "$module_dir/root/usr/libexec/rpcd" -type f -exec grep -iE "$SECRET_PATTERNS" {} \; 2>/dev/null | grep -v '^#' | head -1 > /dev/null; then
|
|
warn "$module_name: Possible hardcoded credentials in RPCD scripts"
|
|
fi
|
|
fi
|
|
|
|
# Check JavaScript files
|
|
if [ -d "$module_dir/htdocs" ]; then
|
|
if find "$module_dir/htdocs" -name "*.js" -type f -exec grep -iE "$SECRET_PATTERNS" {} \; 2>/dev/null | grep -v '^//' | grep -v '^\*' | head -1 > /dev/null; then
|
|
warn "$module_name: Possible hardcoded credentials in JS files"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
success "Security scan complete"
|
|
|
|
# ============================================
|
|
# 9. Run Full Module Validation (if modified)
|
|
# ============================================
|
|
if [ -n "$MODIFIED_MODULES" ] && [ -f "$SCRIPT_DIR/validate-modules.sh" ]; then
|
|
section "9. Full Module Validation"
|
|
|
|
info "Running comprehensive validation on modified modules..."
|
|
echo ""
|
|
|
|
if "$SCRIPT_DIR/validate-modules.sh" 2>&1 | tail -20; then
|
|
success "Full validation passed"
|
|
else
|
|
error "Full validation found issues"
|
|
fi
|
|
fi
|
|
|
|
# ============================================
|
|
# Summary
|
|
# ============================================
|
|
section "Validation Summary"
|
|
|
|
echo ""
|
|
if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
|
|
echo -e "${GREEN}╔══════════════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${GREEN}║ ✓ ALL CHECKS PASSED - Push is allowed ║${NC}"
|
|
echo -e "${GREEN}╚══════════════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
exit 0
|
|
elif [ $ERRORS -eq 0 ]; then
|
|
echo -e "${YELLOW}╔══════════════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${YELLOW}║ ⚠ Passed with $WARNINGS warning(s) - Push is allowed ║${NC}"
|
|
echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
echo -e "${CYAN}Consider fixing warnings before pushing.${NC}"
|
|
echo ""
|
|
exit 0
|
|
else
|
|
echo -e "${RED}╔══════════════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${RED}║ ✗ VALIDATION FAILED - Push is BLOCKED ║${NC}"
|
|
echo -e "${RED}║ Found $ERRORS error(s) and $WARNINGS warning(s) ║${NC}"
|
|
echo -e "${RED}╚══════════════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
echo -e "${CYAN}Fix the errors above before pushing:${NC}"
|
|
echo ""
|
|
echo " 1. Rename RPCD scripts to match ubus objects (use luci. prefix)"
|
|
echo " 2. Update menu paths to match view file locations"
|
|
echo " 3. Add missing RPCD methods to ACL permissions"
|
|
echo " 4. Fix Makefile missing fields"
|
|
echo " 5. Make RPCD scripts executable: chmod +x"
|
|
echo ""
|
|
echo "To bypass this check (NOT RECOMMENDED):"
|
|
echo " git push --no-verify"
|
|
echo ""
|
|
exit 1
|
|
fi
|