#!/usr/bin/env bash set -Eeuo pipefail umask 022 # STEP029 - SERVER ATTESO: 215.5 / ax-presenze # # Audit della struttura DVM-SUITE per integrare un pannello "Genera portale". # # Target esclusivo in sola lettura: # /var/www/html/dvm-suite # # Analizza: # - stack e struttura; # - menu/sidebar/navigation; # - route/controller/API; # - autenticazione, ruoli e permessi; # - accesso database, schema e migrazioni; # - eventuali meccanismi di job/comandi remoti; # - candidati concreti per l'integrazione. # # Nessuna modifica al progetto. # Nessuna modifica al 215.6. # Nessun altro portale toccato. START_DIR="$PWD" STAMP="$(date '+%Y%m%d_%H%M%S')" BASE="STEP029_2155_DVM_SUITE_PORTAL_GENERATOR_INTEGRATION_AUDIT_ONLY_${STAMP}" WORKDIR="${START_DIR}/.${BASE}_work_$$" OUTZIP="${START_DIR}/${BASE}.zip" LOGFILE="${WORKDIR}/run.log" REPORT="${WORKDIR}/report.txt" PACKAGED=0 FAILURES=0 WARNINGS=0 EXPECTED_HOST="${STEP_EXPECTED_HOST:-ax-presenze}" EXPECTED_IP="${STEP_EXPECTED_IP:-192.168.215.5}" SKIP_SERVER_CHECK="${STEP_SKIP_SERVER_CHECK:-0}" PROJECT_DIR="${STEP_PROJECT_DIR:-/var/www/html/dvm-suite}" mkdir -p "$WORKDIR" : >"$LOGFILE" : >"$REPORT" log() { printf '%s\n' "$*" | tee -a "$LOGFILE"; } report() { printf '%s\n' "$*" >>"$REPORT"; } fail() { FAILURES=$((FAILURES + 1)); log "ERRORE: $*"; report "ERRORE: $*"; } warn() { WARNINGS=$((WARNINGS + 1)); log "ATTENZIONE: $*"; report "ATTENZIONE: $*"; } package_results() { local rc="${1:-0}" local zip_rc=0 [[ "$PACKAGED" -eq 1 ]] && return 0 PACKAGED=1 report "" report "Esito finale: errori=${FAILURES}, avvisi=${WARNINGS}, exit_code=${rc}" rm -f -- "$OUTZIP" if ! command -v zip >/dev/null 2>&1; then rm -rf -- "$WORKDIR" printf '%s\n' "ERRORE: comando zip non disponibile" >&2 return 127 fi (cd "$WORKDIR" && zip -qr "$OUTZIP" .) || zip_rc=$? if [[ "$zip_rc" -eq 0 ]]; then zip -T "$OUTZIP" >>"$LOGFILE" 2>&1 || zip_rc=$? fi rm -rf -- "$WORKDIR" if [[ "$zip_rc" -ne 0 ]]; then rm -f -- "$OUTZIP" printf '%s\n' "ERRORE: creazione/verifica ZIP fallita (rc=${zip_rc})" >&2 return "$zip_rc" fi printf '%s\n' "ZIP CREATO E VERIFICATO: $OUTZIP" } on_exit() { local rc=$? trap - EXIT INT TERM HUP package_results "$rc" || rc=$? exit "$rc" } trap on_exit EXIT trap 'exit 130' INT trap 'exit 143' TERM HUP sanitize_excerpt() { sed -E \ -e 's/([Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd]|[Ss][Ee][Cc][Rr][Ee][Tt]|[Tt][Oo][Kk][Ee][Nn]|[Aa][Pp][Ii][_-]?[Kk][Ee][Yy])([[:space:]]*[:=][[:space:]]*)[^,;[:space:]]+/\1\2[REDACTED]/g' \ -e 's/[[:space:]]+/ /g' \ | cut -c1-500 } log "$BASE" log "SERVER ATTESO: 215.5 / ax-presenze" log "PROGETTO IN SOLA LETTURA: $PROJECT_DIR" log "215.6: NON TOCCATO" log "MODALITA': AUDIT ONLY - NESSUNA MODIFICA" log "Avvio: $(date --iso-8601=seconds)" ACTUAL_HOST="$(hostname -s 2>/dev/null || hostname)" ACTUAL_IPS="$(hostname -I 2>/dev/null | xargs || true)" { echo "STEP: $BASE" echo "Server atteso: 215.5 / ax-presenze" echo "Progetto in sola lettura: $PROJECT_DIR" echo "215.6: NON TOCCATO" echo "Modalita': AUDIT ONLY" echo "Data: $(date --iso-8601=seconds)" echo "Hostname: $ACTUAL_HOST" echo "IP: $ACTUAL_IPS" echo } >>"$REPORT" for cmd in grep awk wc sort zip hostname xargs tr find du numfmt basename dirname stat cut sed head tail php; do command -v "$cmd" >/dev/null 2>&1 || fail "comando richiesto non disponibile: $cmd" done [[ "$FAILURES" -eq 0 ]] || exit 20 if [[ "$SKIP_SERVER_CHECK" != "1" ]]; then [[ "$ACTUAL_HOST" == "$EXPECTED_HOST" ]] \ || fail "hostname ${ACTUAL_HOST}; previsto ${EXPECTED_HOST}" if [[ -n "$EXPECTED_IP" ]] && ! grep -qw -- "$EXPECTED_IP" <<<"$ACTUAL_IPS"; then fail "IP ${EXPECTED_IP} non presente tra: ${ACTUAL_IPS}" fi fi [[ "$PROJECT_DIR" == "/var/www/html/dvm-suite" ]] \ || fail "progetto inatteso: $PROJECT_DIR" [[ -d "$PROJECT_DIR" ]] || fail "progetto assente: $PROJECT_DIR" [[ ! -L "$PROJECT_DIR" ]] || fail "root progetto non deve essere symlink" [[ "$FAILURES" -eq 0 ]] || exit 21 log "[1/9] Ambiente e struttura generale" PROJECT_BYTES="$(du -sbx -- "$PROJECT_DIR" 2>/dev/null | awk '{print $1}' || printf '0')" PROJECT_HUMAN="$(numfmt --to=iec-i --suffix=B "$PROJECT_BYTES" 2>/dev/null || printf '%sB' "$PROJECT_BYTES")" PHP_VERSION="$(php -r 'echo PHP_VERSION;' 2>/dev/null || true)" GIT_BRANCH="" GIT_COMMIT="" if [[ -d "${PROJECT_DIR}/.git" ]] && command -v git >/dev/null 2>&1; then GIT_BRANCH="$(git -C "$PROJECT_DIR" branch --show-current 2>/dev/null || true)" GIT_COMMIT="$(git -C "$PROJECT_DIR" rev-parse --short HEAD 2>/dev/null || true)" fi { echo "Hostname: $ACTUAL_HOST" echo "IP: $ACTUAL_IPS" echo "PHP CLI: ${PHP_VERSION:-NON DISPONIBILE}" echo "Project root: $PROJECT_DIR" echo "Project size: $PROJECT_HUMAN" echo "Git branch: ${GIT_BRANCH:-NON RILEVATA}" echo "Git commit: ${GIT_COMMIT:-NON RILEVATO}" } >"${WORKDIR}/01_ENVIRONMENT.txt" TOP_RAW="${WORKDIR}/.top_raw.tsv" : >"$TOP_RAW" while IFS= read -r -d '' item; do name="$(basename "$item")" if [[ -L "$item" ]]; then type="symlink" bytes="$(stat -c '%s' "$item" 2>/dev/null || printf '0')" elif [[ -d "$item" ]]; then type="directory" bytes="$(du -sbx -- "$item" 2>/dev/null | awk '{print $1}' || printf '0')" elif [[ -f "$item" ]]; then type="file" bytes="$(stat -c '%s' "$item" 2>/dev/null || printf '0')" else continue fi human="$(numfmt --to=iec-i --suffix=B "$bytes" 2>/dev/null || printf '%sB' "$bytes")" printf '%s\t%s\t%s\t%s\t%s\n' "$bytes" "$human" "$type" "$name" "$item" >>"$TOP_RAW" done < <( find "$PROJECT_DIR" -xdev -mindepth 1 -maxdepth 1 \ \( -type f -o -type d -o -type l \) -print0 2>>"$LOGFILE" ) { printf 'byte\tspazio\ttipo\tnome\tpercorso\n' sort -t $'\t' -rn -k1,1 "$TOP_RAW" } >"${WORKDIR}/02_TOP_LEVEL.tsv" log "[2/9] File stack e configurazione" STACK="${WORKDIR}/03_STACK_FILES.tsv" printf 'percorso\ttipo\tbyte\tspazio\n' >"$STACK" while IFS= read -r -d '' file; do rel="${file#${PROJECT_DIR}/}" type="file" [[ -L "$file" ]] && type="symlink" bytes="$(stat -c '%s' "$file" 2>/dev/null || printf '0')" human="$(numfmt --to=iec-i --suffix=B "$bytes" 2>/dev/null || printf '%sB' "$bytes")" printf '%s\t%s\t%s\t%s\n' "$rel" "$type" "$bytes" "$human" >>"$STACK" done < <( find "$PROJECT_DIR" -xdev -maxdepth 4 \ \( \ -name 'composer.json' \ -o -name 'composer.lock' \ -o -name 'package.json' \ -o -name 'package-lock.json' \ -o -name 'yarn.lock' \ -o -name 'vite.config.*' \ -o -name 'webpack.config.*' \ -o -name 'artisan' \ -o -name 'index.php' \ -o -name '.env.example' \ -o -name 'docker-compose.yml' \ -o -name 'docker-compose.yaml' \ \) \ \( -type f -o -type l \) \ -print0 2>>"$LOGFILE" ) log "[3/9] Preparazione inventario codice" CODE_FILES="${WORKDIR}/04_CODE_FILES.list" : >"$CODE_FILES" find "$PROJECT_DIR" \ -xdev \ \( \ -path "${PROJECT_DIR}/.git" \ -o -path "${PROJECT_DIR}/vendor" \ -o -path "${PROJECT_DIR}/node_modules" \ -o -path "${PROJECT_DIR}/bower_components" \ -o -path "${PROJECT_DIR}/storage" \ -o -path "${PROJECT_DIR}/cache" \ -o -path "${PROJECT_DIR}/tmp" \ -o -path "${PROJECT_DIR}/logs" \ -o -path "${PROJECT_DIR}/cesare" \ \) -prune \ -o -type f \ \( \ -name '*.php' \ -o -name '*.js' \ -o -name '*.ts' \ -o -name '*.tsx' \ -o -name '*.jsx' \ -o -name '*.vue' \ -o -name '*.html' \ -o -name '*.htm' \ -o -name '*.json' \ -o -name '*.xml' \ -o -name '*.sql' \ -o -name '*.tpl' \ -o -name '*.ini' \ -o -name '*.conf' \ \) \ -print0 >"$CODE_FILES" CODE_FILE_COUNT="$(tr -cd '\0' <"$CODE_FILES" | wc -c | tr -d ' ')" scan_category() { local category="$1" local regex="$2" local output="$3" local file local rel local match local lineno local content local clean printf 'categoria\tfile\triga\ttesto\n' >"$output" while IFS= read -r -d '' file; do rel="${file#${PROJECT_DIR}/}" while IFS= read -r match; do [[ -n "$match" ]] || continue lineno="${match%%:*}" content="${match#*:}" clean="$(printf '%s' "$content" | sanitize_excerpt)" printf '%s\t%s\t%s\t%s\n' \ "$category" "$rel" "$lineno" "$clean" \ >>"$output" done < <(grep -nI -E "$regex" "$file" 2>/dev/null | head -n 30 || true) done <"$CODE_FILES" } log "[4/9] Menu, sidebar e navigazione" scan_category \ "MENU_NAVIGATION" \ '(sidebar|side-menu|sidemenu|navigation|navbar|nav-item|menu-item|main-menu|menu[[:space:]]*=|addMenu|registerMenu|routeName)' \ "${WORKDIR}/05_MENU_NAVIGATION.tsv" log "[5/9] Route, controller e API" scan_category \ "ROUTES_CONTROLLERS_API" \ '(Route::|router\.|routes?[/_.-]|controller|/api/|api[/_.-]|endpoint|REQUEST_METHOD|php://input|json_encode|json_decode)' \ "${WORKDIR}/06_ROUTES_CONTROLLERS_API.tsv" log "[6/9] Autenticazione, ruoli e permessi" scan_category \ "AUTH_ROLES_PERMISSIONS" \ '(auth|authenticate|authorization|permission|permissions|role|roles|acl|session_start|session_id|currentUser|loggedIn|isAdmin|can\(|authorize)' \ "${WORKDIR}/07_AUTH_ROLES_PERMISSIONS.tsv" log "[7/9] Database, schema e migrazioni" scan_category \ "DATABASE_SCHEMA" \ '(CREATE[[:space:]]+TABLE|ALTER[[:space:]]+TABLE|INSERT[[:space:]]+INTO|migration|migrations|schema|PDO|mysqli|database|DB::|table\(|SELECT[[:space:]].*FROM)' \ "${WORKDIR}/08_DATABASE_SCHEMA.tsv" log "[8/9] Job, processi e comunicazione remota" scan_category \ "JOBS_REMOTE_EXECUTION" \ '(shell_exec|proc_open|popen|passthru|system\(|exec\(|ssh2_|curl_|curl_exec|Guzzle|Http::|file_get_contents\([[:space:]]*["'\'']https?://|queue|job|worker|cron)' \ "${WORKDIR}/09_JOBS_REMOTE_EXECUTION.tsv" log "[9/9] Candidati file e PHP lint" CANDIDATES="${WORKDIR}/10_CANDIDATE_FILES.tsv" printf 'categoria\tfile\toccorrenze\tphp_lint\n' >"$CANDIDATES" build_candidate_summary() { local category="$1" local source="$2" local rel local count local lint awk -F '\t' 'NR>1 {count[$2]++} END {for (f in count) print count[f] "\t" f}' "$source" \ | sort -t $'\t' -rn -k1,1 \ | head -n 30 \ | while IFS=$'\t' read -r count rel; do lint="N/A" if [[ "$rel" == *.php ]]; then if php -l "${PROJECT_DIR}/${rel}" >/dev/null 2>&1; then lint="OK" else lint="ERRORE" warn "PHP lint fallito sul candidato: $rel" fi fi printf '%s\t%s\t%s\t%s\n' \ "$category" "$rel" "$count" "$lint" \ >>"$CANDIDATES" done } build_candidate_summary "MENU_NAVIGATION" "${WORKDIR}/05_MENU_NAVIGATION.tsv" build_candidate_summary "ROUTES_CONTROLLERS_API" "${WORKDIR}/06_ROUTES_CONTROLLERS_API.tsv" build_candidate_summary "AUTH_ROLES_PERMISSIONS" "${WORKDIR}/07_AUTH_ROLES_PERMISSIONS.tsv" build_candidate_summary "DATABASE_SCHEMA" "${WORKDIR}/08_DATABASE_SCHEMA.tsv" build_candidate_summary "JOBS_REMOTE_EXECUTION" "${WORKDIR}/09_JOBS_REMOTE_EXECUTION.tsv" MENU_MATCHES=$(( $(wc -l <"${WORKDIR}/05_MENU_NAVIGATION.tsv") - 1 )) ROUTE_MATCHES=$(( $(wc -l <"${WORKDIR}/06_ROUTES_CONTROLLERS_API.tsv") - 1 )) AUTH_MATCHES=$(( $(wc -l <"${WORKDIR}/07_AUTH_ROLES_PERMISSIONS.tsv") - 1 )) DB_MATCHES=$(( $(wc -l <"${WORKDIR}/08_DATABASE_SCHEMA.tsv") - 1 )) JOB_MATCHES=$(( $(wc -l <"${WORKDIR}/09_JOBS_REMOTE_EXECUTION.tsv") - 1 )) CANDIDATE_FILES=$(( $(wc -l <"$CANDIDATES") - 1 )) PHP_LINT_ERRORS="$(awk -F '\t' 'NR>1 && $4=="ERRORE" {n++} END {print n+0}' "$CANDIDATES")" { echo "PUNTI DI INTEGRAZIONE DA CONFERMARE NEL PROSSIMO STEP" echo echo "1. MENU / NAVIGAZIONE" awk -F '\t' 'NR>1 && $1=="MENU_NAVIGATION" {print " - " $2 " (" $3 " occorrenze, lint " $4 ")"}' "$CANDIDATES" | head -n 8 echo echo "2. ROUTE / CONTROLLER / API" awk -F '\t' 'NR>1 && $1=="ROUTES_CONTROLLERS_API" {print " - " $2 " (" $3 " occorrenze, lint " $4 ")"}' "$CANDIDATES" | head -n 8 echo echo "3. AUTENTICAZIONE / RUOLI" awk -F '\t' 'NR>1 && $1=="AUTH_ROLES_PERMISSIONS" {print " - " $2 " (" $3 " occorrenze, lint " $4 ")"}' "$CANDIDATES" | head -n 8 echo echo "4. DATABASE / SCHEMA" awk -F '\t' 'NR>1 && $1=="DATABASE_SCHEMA" {print " - " $2 " (" $3 " occorrenze, lint " $4 ")"}' "$CANDIDATES" | head -n 8 echo echo "5. JOB / COMUNICAZIONE REMOTA" awk -F '\t' 'NR>1 && $1=="JOBS_REMOTE_EXECUTION" {print " - " $2 " (" $3 " occorrenze, lint " $4 ")"}' "$CANDIDATES" | head -n 8 echo echo "Nessuna modifica eseguita." } >"${WORKDIR}/11_RECOMMENDED_INSERTION_POINTS.txt" { printf 'voce\tvalore\n' printf 'spazio_progetto\t%s\n' "$PROJECT_HUMAN" printf 'file_codice_analizzati\t%s\n' "$CODE_FILE_COUNT" printf 'occorrenze_menu\t%s\n' "$MENU_MATCHES" printf 'occorrenze_route_api\t%s\n' "$ROUTE_MATCHES" printf 'occorrenze_auth_permessi\t%s\n' "$AUTH_MATCHES" printf 'occorrenze_database\t%s\n' "$DB_MATCHES" printf 'occorrenze_job_remoti\t%s\n' "$JOB_MATCHES" printf 'file_candidati\t%s\n' "$CANDIDATE_FILES" printf 'errori_php_lint_candidati\t%s\n' "$PHP_LINT_ERRORS" } >"${WORKDIR}/12_SUMMARY.tsv" { echo "AUDIT COMPLETATO" echo "Server: 215.5 / ax-presenze" echo "Progetto: $PROJECT_DIR" echo "Spazio progetto: $PROJECT_HUMAN" echo "File codice analizzati: $CODE_FILE_COUNT" echo "Occorrenze menu/navigation: $MENU_MATCHES" echo "Occorrenze route/controller/API: $ROUTE_MATCHES" echo "Occorrenze auth/ruoli/permessi: $AUTH_MATCHES" echo "Occorrenze database/schema: $DB_MATCHES" echo "Occorrenze job/comunicazione remota: $JOB_MATCHES" echo "File candidati classificati: $CANDIDATE_FILES" echo "Errori PHP lint sui candidati: $PHP_LINT_ERRORS" echo "215.6 modificato: NO" echo "DVM-SUITE modificato: NO" echo "Altri portali toccati: NO" } >"${WORKDIR}/99_SUMMARY.txt" report "Audit integrazione pannello generatore completato." report "File codice analizzati: $CODE_FILE_COUNT." report "File candidati: $CANDIDATE_FILES." report "Errori PHP lint candidati: $PHP_LINT_ERRORS." report "Nessuna modifica eseguita." log "AUDIT COMPLETATO: errori=${FAILURES}, avvisi=${WARNINGS}"