Added comprehensive automation for file permissions management to prevent HTTP 403 errors caused by incorrect permissions (600 instead of 644). 🆕 New Tool: fix-permissions.sh ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Automated script to fix and verify file permissions: Features: - Fixes local source permissions (--local) - Fixes remote router permissions (--remote) - Default: fixes both local and remote - Auto-verifies RPCD scripts (755) - Auto-verifies CSS files (644) - Auto-verifies JS files (644) - Clears cache and restarts services (remote) - Reports all changes made Usage: ./secubox-tools/fix-permissions.sh --local # Before commit ./secubox-tools/fix-permissions.sh --remote # After deploy ./secubox-tools/fix-permissions.sh # Both ✨ Enhanced: validate-modules.sh - Check 7 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Added comprehensive permission validation: Check 7: htdocs file permissions - Validates all CSS files have 644 permissions - Validates all JS files have 644 permissions - Reports files with wrong permissions - Suggests fix-permissions.sh for auto-correction - Counts permission errors in summary Total validation checks: 7 1. RPCD script names vs ubus objects 2. Menu paths vs view file locations 3. View files have menu entries 4. RPCD script permissions (755) 5. JSON syntax validation 6. ubus object naming convention 7. htdocs file permissions (644) ← NEW 📚 Documentation Updates ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ DEVELOPMENT-GUIDELINES.md: - Added "Correction Automatique" section with fix-permissions.sh - Added "Validation Automatique des Permissions" section - Added recommended workflow: fix → validate → commit → deploy → fix remote QUICK-START.md: - Updated Validation section with fix-permissions.sh - Updated Common Errors Quick Fix with automated script - Updated Pre-Commit Checklist with automated tools - Marked permissions as "auto-verified" in checklist CLAUDE.md: - Updated critical rules with auto-fix commands - Added 7 validation checks list - Enhanced Validation section with detailed check descriptions - Added fix-permissions.sh to workflow 🔧 Files Modified ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ New: + secubox-tools/fix-permissions.sh (executable) Modified: * secubox-tools/validate-modules.sh (Check 7 added) * DEVELOPMENT-GUIDELINES.md (~50 lines added) * QUICK-START.md (~15 lines added) * CLAUDE.md (~25 lines added) 🎯 Problem Solved ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Root cause: Files created/deployed with umask 0077 result in 600 permissions Symptom: HTTP 403 Forbidden errors on CSS/JS resources Impact: Modules fail to load in browser Recent examples: - secubox: 10 files with 600 permissions (monitoring.js, theme.js, etc.) - netdata-dashboard: 3 files with 600 permissions Solution: Automated detection and correction tools now prevent this issue Workflow integration: ✅ Pre-commit: fix-permissions.sh --local ✅ Validation: validate-modules.sh (Check 7) ✅ Post-deploy: fix-permissions.sh --remote 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
317 lines
11 KiB
Bash
Executable File
317 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# SecuBox Module Validation Script
|
|
# Validates RPCD naming, menu paths, and module structure
|
|
#
|
|
|
|
set -e
|
|
set -o pipefail
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
ERRORS=0
|
|
WARNINGS=0
|
|
|
|
echo "========================================"
|
|
echo "SecuBox Module Validation"
|
|
echo "========================================"
|
|
echo ""
|
|
|
|
# Function to print error
|
|
error() {
|
|
echo -e "${RED}❌ ERROR: $1${NC}"
|
|
((ERRORS++))
|
|
}
|
|
|
|
# Function to print warning
|
|
warn() {
|
|
echo -e "${YELLOW}⚠️ WARNING: $1${NC}"
|
|
((WARNINGS++))
|
|
}
|
|
|
|
# Function to print success
|
|
success() {
|
|
echo -e "${GREEN}✓ $1${NC}"
|
|
}
|
|
|
|
# Check 1: RPCD script names must match ubus object names
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "1. Validating RPCD script names vs ubus objects"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
module_name=$(basename "$module_dir")
|
|
echo "Checking $module_name..."
|
|
|
|
# Find RPCD script
|
|
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 names from JavaScript files
|
|
js_objects=$(find "$module_dir/htdocs" -name "*.js" -type f 2>/dev/null | \
|
|
xargs grep -h "object:" 2>/dev/null | \
|
|
grep -o "'[^']*'" | sort -u | tr -d "'")
|
|
|
|
if [ -n "$js_objects" ]; then
|
|
# Check if RPCD script name matches any ubus object
|
|
match_found=false
|
|
for obj in $js_objects; do
|
|
if [ "$rpcd_name" = "$obj" ]; then
|
|
match_found=true
|
|
success "$module_name: RPCD script '$rpcd_name' matches ubus object '$obj'"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "$match_found" = false ]; then
|
|
error "$module_name: RPCD script '$rpcd_name' does not match any ubus object(s): $js_objects"
|
|
echo " → Rename $rpcd_script to one of: $js_objects"
|
|
fi
|
|
else
|
|
warn "$module_name: No ubus object declarations found in JavaScript files"
|
|
fi
|
|
else
|
|
warn "$module_name: No RPCD script found in $rpcd_dir"
|
|
fi
|
|
else
|
|
warn "$module_name: No RPCD directory found"
|
|
fi
|
|
echo ""
|
|
done
|
|
|
|
# Check 2: Menu paths must match actual view file locations
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "2. Validating menu paths vs view file locations"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
module_name=$(basename "$module_dir")
|
|
menu_file="$module_dir/root/usr/share/luci/menu.d/${module_name}.json"
|
|
|
|
if [ -f "$menu_file" ]; then
|
|
echo "Checking $module_name menu paths..."
|
|
|
|
# Extract all view paths from menu JSON
|
|
menu_paths=$(grep -o '"path":\s*"[^"]*"' "$menu_file" | cut -d'"' -f4)
|
|
|
|
for path in $menu_paths; do
|
|
# Convert menu path to file path
|
|
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 at $view_file"
|
|
|
|
# Suggest possible matches
|
|
view_dir=$(dirname "$view_file")
|
|
if [ -d "$view_dir" ]; then
|
|
echo " → Possible files in $(dirname $path):"
|
|
find "$view_dir" -name "*.js" -type f | while read -r f; do
|
|
echo " - $(basename $f)"
|
|
done
|
|
fi
|
|
fi
|
|
done
|
|
echo ""
|
|
fi
|
|
done
|
|
|
|
# Check 3: View files must have corresponding menu entries
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "3. Validating view files have menu entries"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
module_name=$(basename "$module_dir")
|
|
view_dir="$module_dir/htdocs/luci-static/resources/view"
|
|
menu_file="$module_dir/root/usr/share/luci/menu.d/${module_name}.json"
|
|
|
|
if [ -d "$view_dir" ] && [ -f "$menu_file" ]; then
|
|
echo "Checking $module_name view files..."
|
|
|
|
# Temporarily disable exit on error for grep checks in loops
|
|
set +e
|
|
# Find all .js view files
|
|
find "$view_dir" -name "*.js" -type f 2>/dev/null | while read -r view_file; do
|
|
# Convert file path to menu path
|
|
rel_path=$(echo "$view_file" | sed "s|$module_dir/htdocs/luci-static/resources/view/||" | sed 's|.js$||')
|
|
|
|
# Check if path exists in menu
|
|
if grep -q "\"path\":\s*\"$rel_path\"" "$menu_file" 2>/dev/null; then
|
|
success "$module_name: View '$rel_path.js' has menu entry"
|
|
else
|
|
warn "$module_name: View file '$rel_path.js' exists but has no menu entry"
|
|
fi
|
|
done
|
|
set -e
|
|
echo ""
|
|
fi
|
|
done
|
|
|
|
# Check 4: RPCD scripts must be executable
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "4. Validating RPCD script permissions"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
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: RPCD script $(basename $script) is executable"
|
|
else
|
|
error "$module_name: RPCD script $(basename $script) is NOT executable"
|
|
echo " → Run: chmod +x $script"
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Check 5: JSON files must be valid
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "5. Validating JSON syntax"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
module_name=$(basename "$module_dir")
|
|
|
|
# Check menu JSON
|
|
menu_file="$module_dir/root/usr/share/luci/menu.d/${module_name}.json"
|
|
if [ -f "$menu_file" ]; then
|
|
if python3 -m json.tool "$menu_file" > /dev/null 2>&1; then
|
|
success "$module_name: menu.d JSON is valid"
|
|
else
|
|
error "$module_name: menu.d JSON is INVALID"
|
|
fi
|
|
fi
|
|
|
|
# Check ACL JSON
|
|
acl_file="$module_dir/root/usr/share/rpcd/acl.d/${module_name}.json"
|
|
if [ -f "$acl_file" ]; then
|
|
if python3 -m json.tool "$acl_file" > /dev/null 2>&1; then
|
|
success "$module_name: acl.d JSON is valid"
|
|
else
|
|
error "$module_name: acl.d JSON is INVALID"
|
|
fi
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Check 6: Verify ubus object naming convention
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "6. Validating ubus object naming convention"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
for module_dir in luci-app-*/; do
|
|
module_name=$(basename "$module_dir")
|
|
|
|
# Extract ubus object names from JavaScript
|
|
js_objects=$(find "$module_dir/htdocs" -name "*.js" -type f 2>/dev/null | \
|
|
xargs grep -h "object:" 2>/dev/null | \
|
|
grep -o "'[^']*'" | sort -u | tr -d "'")
|
|
|
|
if [ -n "$js_objects" ]; then
|
|
for obj in $js_objects; do
|
|
# Check if object starts with 'luci.'
|
|
if [[ $obj == luci.* ]]; then
|
|
success "$module_name: ubus object '$obj' follows naming convention (luci.* prefix)"
|
|
else
|
|
error "$module_name: ubus object '$obj' does NOT follow naming convention (missing luci. prefix)"
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Check 7: htdocs files must have correct permissions (644 for web server)
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "7. Validating htdocs file permissions (CSS/JS must be 644)"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
PERMISSION_ERRORS=0
|
|
|
|
for module_dir in luci-app-*/; do
|
|
module_name=$(basename "$module_dir")
|
|
htdocs_dir="$module_dir/htdocs"
|
|
|
|
if [ -d "$htdocs_dir" ]; then
|
|
# Check CSS files
|
|
while IFS= read -r css_file; do
|
|
if [ -n "$css_file" ]; then
|
|
perms=$(stat -c "%a" "$css_file" 2>/dev/null)
|
|
if [ "$perms" != "644" ]; then
|
|
error "$module_name: CSS file has wrong permissions: $css_file ($perms, should be 644)"
|
|
echo " → Run: chmod 644 $css_file"
|
|
((PERMISSION_ERRORS++))
|
|
else
|
|
success "$module_name: CSS file has correct permissions (644): $(basename $css_file)"
|
|
fi
|
|
fi
|
|
done < <(find "$htdocs_dir" -name "*.css" -type f 2>/dev/null)
|
|
|
|
# Check JS files
|
|
while IFS= read -r js_file; do
|
|
if [ -n "$js_file" ]; then
|
|
perms=$(stat -c "%a" "$js_file" 2>/dev/null)
|
|
if [ "$perms" != "644" ]; then
|
|
error "$module_name: JS file has wrong permissions: $js_file ($perms, should be 644)"
|
|
echo " → Run: chmod 644 $js_file"
|
|
((PERMISSION_ERRORS++))
|
|
else
|
|
success "$module_name: JS file has correct permissions (644): $(basename $js_file)"
|
|
fi
|
|
fi
|
|
done < <(find "$htdocs_dir" -name "*.js" -type f 2>/dev/null)
|
|
fi
|
|
done
|
|
|
|
if [ $PERMISSION_ERRORS -gt 0 ]; then
|
|
echo ""
|
|
echo -e "${YELLOW}⚠️ To fix all permission errors automatically, run:${NC}"
|
|
echo " ./secubox-tools/fix-permissions.sh --local"
|
|
fi
|
|
echo ""
|
|
|
|
# Add permission errors to total error count
|
|
TOTAL_ERRORS=$((ERRORS + PERMISSION_ERRORS))
|
|
|
|
# Summary
|
|
echo "========================================"
|
|
echo "Validation Summary"
|
|
echo "========================================"
|
|
echo ""
|
|
if [ $TOTAL_ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
|
|
echo -e "${GREEN}✓ All checks passed!${NC}"
|
|
exit 0
|
|
elif [ $TOTAL_ERRORS -eq 0 ]; then
|
|
echo -e "${YELLOW}✓ All critical checks passed with $WARNINGS warning(s)${NC}"
|
|
exit 0
|
|
else
|
|
echo -e "${RED}✗ Found $TOTAL_ERRORS error(s) and $WARNINGS warning(s)${NC}"
|
|
if [ $PERMISSION_ERRORS -gt 0 ]; then
|
|
echo -e "${YELLOW} ($PERMISSION_ERRORS permission error(s))${NC}"
|
|
fi
|
|
echo ""
|
|
echo "Please fix the errors listed above before deploying."
|
|
echo "Run: ./secubox-tools/fix-permissions.sh --local"
|
|
exit 1
|
|
fi
|