#!/usr/bin/env bash # STEP042 - Porting Presenze 215.6 # Audit mirato: calendario Silvelox bloccato dal messaggio # "verificare se tutti i dati sono inseriti" # Server atteso: 215.6 / ax-accessi # Modalita': AUDIT ONLY - nessuna modifica a file applicativi, database o servizi set -Eeuo pipefail umask 077 STEP_NAME="STEP042_2156_SILVELOX_CALENDAR_REQUIRED_FIELDS_VALIDATION_AUDIT_ONLY_VERIFIED_20260730" EXPECTED_HOST="${EXPECTED_HOST:-ax-accessi}" EXPECTED_IP_FRAGMENT="${EXPECTED_IP_FRAGMENT:-192.168.215.6}" TARGET_ROOT="${TARGET_ROOT:-/var/www/html/presenze.silvelox}" TARGET_HOST="${TARGET_HOST:-presenze.silvelox.axuno.it}" TARGET_DB="${TARGET_DB:-presenze_silvelox}" ALLOW_TEST="${ALLOW_TEST:-0}" START_TS="$(date +%Y%m%d_%H%M%S)" OUTPUT_ZIP="$PWD/${STEP_NAME}_${START_TS}.zip" WORKDIR="$(mktemp -d "$PWD/.${STEP_NAME}.XXXXXX")" REPORT="$WORKDIR/${STEP_NAME}_REPORT.txt" cleanup() { rm -rf -- "$WORKDIR" } trap cleanup EXIT INT TERM on_error() { local rc=$? local line="${1:-?}" printf '\nERRORE linea %s, codice %s\n' "$line" "$rc" >&2 exit "$rc" } trap 'on_error "$LINENO"' ERR section() { printf '\n\n================================================================\n%s\n================================================================\n' "$1" >> "$REPORT" } append_cmd() { local title="$1" shift section "$title" printf '$' >> "$REPORT" printf ' %q' "$@" >> "$REPORT" printf '\n' >> "$REPORT" "$@" >> "$REPORT" 2>&1 || printf '[comando terminato con codice %s]\n' "$?" >> "$REPORT" } context_for_match() { local file="$1" local line="$2" local before="${3:-100}" local after="${4:-140}" local start end total [[ -f "$file" ]] || return 0 [[ "$line" =~ ^[0-9]+$ ]] || return 0 total="$(wc -l < "$file" 2>/dev/null || echo 0)" start=$((line - before)) (( start < 1 )) && start=1 end=$((line + after)) (( end > total )) && end="$total" printf '\n--- FILE: %s | RIGHE %s-%s | MATCH %s ---\n' "$file" "$start" "$end" "$line" sed -n "${start},${end}p" "$file" 2>/dev/null | nl -ba -v "$start" || true } extract_php_endpoints() { local src="$1" grep -Eio \ '([A-Za-z0-9_./-]+[.]php)([?][^"'\''[:space:]]*)?|url[[:space:]]*:[[:space:]]*["'\''][^"'\'']+|action[[:space:]]*=[[:space:]]*["'\''][^"'\'']+' \ "$src" 2>/dev/null \ | sed -E 's/^[Uu][Rr][Ll][[:space:]]*:[[:space:]]*["'\'']//; s/^[Aa][Cc][Tt][Ii][Oo][Nn][[:space:]]*=[[:space:]]*["'\'']//; s/["'\'']$//' \ | sed 's/[?#].*$//' \ | grep -E '[.]php$' \ | sort -u || true } HOSTNAME_NOW="$(hostname 2>/dev/null || true)" IPS_NOW="$(hostname -I 2>/dev/null || true)" if [[ "$ALLOW_TEST" != "1" ]]; then if [[ "$HOSTNAME_NOW" != "$EXPECTED_HOST" && "$IPS_NOW" != *"$EXPECTED_IP_FRAGMENT"* ]]; then echo "ERRORE: server errato. Rilevato host='$HOSTNAME_NOW' IP='$IPS_NOW'; atteso 215.6 / ax-accessi." >&2 exit 10 fi if [[ ! -d "$TARGET_ROOT" ]]; then echo "ERRORE: portale Silvelox non trovato: $TARGET_ROOT" >&2 exit 11 fi fi { echo "$STEP_NAME" echo "DATA: $(date --iso-8601=seconds 2>/dev/null || date)" echo "SERVER ATTESO: 215.6 / ax-accessi" echo "PORTALE: $TARGET_ROOT" echo "HOST: $TARGET_HOST" echo "DATABASE ATTESO: $TARGET_DB" echo "SCOPO: individuare il campo o la condizione che genera il messaggio di dati mancanti" echo "MODALITA': AUDIT ONLY" echo "NESSUNA MODIFICA APPLICATIVA, NESSUNA QUERY DI SCRITTURA, NESSUN RELOAD" } > "$REPORT" append_cmd "Identita' server" bash -lc 'hostname; hostname -f 2>/dev/null || true; hostname -I 2>/dev/null || true; id' append_cmd "Runtime attivi" bash -lc 'php5.6 -v 2>/dev/null || php -v 2>/dev/null || true; apache2ctl -v 2>/dev/null || true; mysql --version 2>/dev/null || mariadb --version 2>/dev/null || true' MATCHES="$WORKDIR/message_matches.txt" RELATED_FILES="$WORKDIR/related_files.txt" ENDPOINT_NAMES="$WORKDIR/endpoint_names.txt" ENDPOINT_FILES="$WORKDIR/endpoint_files.txt" ALL_FILES="$WORKDIR/all_relevant_files.txt" MODEL_TOKENS="$WORKDIR/model_tokens.txt" TABLE_NAMES="$WORKDIR/table_names.txt" : > "$MATCHES" : > "$RELATED_FILES" : > "$ENDPOINT_NAMES" : > "$ENDPOINT_FILES" : > "$ALL_FILES" : > "$MODEL_TOKENS" : > "$TABLE_NAMES" : > "$WORKDIR/php_lint.txt" section "Ricerca della frase esatta e delle varianti" if command -v rg >/dev/null 2>&1; then rg -n -i --hidden \ --glob '!bower_components/**' \ --glob '!node_modules/**' \ --glob '!vendor/**' \ --glob '!templates_c/**' \ --glob '!cache/**' \ --glob '!*.min.js' \ -e 'verificare[[:space:]]+se[[:space:]]+tutti[[:space:]]+i[[:space:]]+dati[[:space:]]+sono[[:space:]]+inseriti' \ -e 'tutti[[:space:]]+i[[:space:]]+dati.*inserit' \ -e 'verificare.*dati.*inserit' \ -e 'check.*all.*data' \ -e 'all.*fields.*required' \ "$TARGET_ROOT" 2>/dev/null | head -n 3000 > "$MATCHES" || true else grep -RniIE \ --exclude-dir=bower_components \ --exclude-dir=node_modules \ --exclude-dir=vendor \ --exclude-dir=templates_c \ --exclude-dir=cache \ --exclude='*.min.js' \ 'verificare[[:space:]]+se[[:space:]]+tutti[[:space:]]+i[[:space:]]+dati[[:space:]]+sono[[:space:]]+inseriti|tutti[[:space:]]+i[[:space:]]+dati.*inserit|verificare.*dati.*inserit|check.*all.*data|all.*fields.*required' \ "$TARGET_ROOT" 2>/dev/null | head -n 3000 > "$MATCHES" || true fi cat "$MATCHES" >> "$REPORT" MATCH_COUNT="$(grep -c . "$MATCHES" 2>/dev/null || true)" if [[ "$MATCH_COUNT" -eq 0 ]]; then echo "NESSUNA OCCORRENZA TROVATA: la frase potrebbe provenire da traduzioni compilate, concatenazione o risposta server." >> "$REPORT" fi cut -d: -f1 "$MATCHES" 2>/dev/null | sort -u > "$RELATED_FILES" || true section "Contesto completo attorno a ogni messaggio" while IFS=: read -r file line rest; do [[ -n "${file:-}" && -n "${line:-}" ]] || continue context_for_match "$file" "$line" 120 180 done < "$MATCHES" >> "$REPORT" 2>&1 section "Metadati e hash dei file contenenti il messaggio" while IFS= read -r file; do [[ -f "$file" ]] || continue stat -c '%A %a %U:%G %s %y %n' "$file" 2>/dev/null || true sha256sum "$file" 2>/dev/null || true done < "$RELATED_FILES" >> "$REPORT" 2>&1 section "Condizioni di validazione nei file del messaggio" while IFS= read -r file; do [[ -f "$file" ]] || continue echo "--- $file ---" grep -nEi \ 'required|ng-required|[.]required|[$]invalid|[$]valid|[$]error|undefined|null|empty[[:space:]]*[(]|isset[[:space:]]*[(]|trim[[:space:]]*[(]|length|moment|isValid|Date|time|ora|data|dal|al|inizio|fine|badge|utente|employee|dipendente|causale|turno|reparto|azienda|confirm|alert|sweetalert|toastr|notify|message' \ "$file" 2>/dev/null | head -n 2500 || true done < "$RELATED_FILES" >> "$REPORT" 2>&1 section "Endpoint PHP richiamati dai file del messaggio" while IFS= read -r file; do [[ -f "$file" ]] || continue extract_php_endpoints "$file" done < "$RELATED_FILES" | sort -u > "$ENDPOINT_NAMES" || true cat "$ENDPOINT_NAMES" >> "$REPORT" section "Risoluzione degli endpoint nel portale" while IFS= read -r endpoint; do [[ -n "$endpoint" ]] || continue base="$(basename "$endpoint")" find "$TARGET_ROOT" -xdev -type f -name "$base" -print 2>/dev/null done < "$ENDPOINT_NAMES" | sort -u > "$ENDPOINT_FILES" || true cat "$ENDPOINT_FILES" >> "$REPORT" cat "$RELATED_FILES" "$ENDPOINT_FILES" 2>/dev/null | grep -v '^$' | sort -u > "$ALL_FILES" || true section "Form, campi e payload nei file correlati" while IFS= read -r file; do [[ -f "$file" ]] || continue echo "--- $file ---" grep -nEi \ '|/dev/null | head -n 3500 || true done < "$ALL_FILES" >> "$REPORT" 2>&1 section "Contenuto completo degli endpoint PHP individuati" while IFS= read -r file; do [[ -f "$file" ]] || continue case "$file" in *.php) lines="$(wc -l < "$file" 2>/dev/null || echo 0)" echo "--- $file | $lines righe ---" if [[ "$lines" -le 1800 ]]; then nl -ba "$file" 2>/dev/null || true else grep -nEi \ 'REQUEST_METHOD|php://input|json_decode|[$]_POST|filter_input|isset|empty|trim|required|INSERT|UPDATE|DELETE|SELECT|mysqli|PDO|error|message|success|status|calendar|event|turn|schedule' \ "$file" 2>/dev/null | head -n 4000 || true fi ;; esac done < "$ENDPOINT_FILES" >> "$REPORT" 2>&1 section "Ricerca globale dei modelli e dei nomi campo correlati" while IFS= read -r file; do [[ -f "$file" ]] || continue grep -Eio \ 'ng-model[[:space:]]*=[[:space:]]*["'\''][^"'\'']+|name[[:space:]]*=[[:space:]]*["'\''][^"'\'']+|[$]scope[.][A-Za-z_][A-Za-z0-9_.]*' \ "$file" 2>/dev/null \ | sed -E 's/^[^="'\'']*[="'\'']//' \ | sed -E 's/["'\'']$//' \ | sed -E 's/^[$]scope[.]//' \ | sed -E 's/.*[.]//' \ | grep -E '^[A-Za-z_][A-Za-z0-9_]{2,}$' || true done < "$RELATED_FILES" | sort -u | head -n 300 > "$MODEL_TOKENS" || true cat "$MODEL_TOKENS" >> "$REPORT" while IFS= read -r token; do [[ -n "$token" ]] || continue echo "--- TOKEN: $token ---" grep -RniIE \ --exclude-dir=bower_components \ --exclude-dir=node_modules \ --exclude-dir=vendor \ --exclude-dir=templates_c \ --exclude-dir=cache \ --exclude='*.min.js' \ "\b${token}\b" "$TARGET_ROOT" 2>/dev/null | head -n 250 || true done < "$MODEL_TOKENS" >> "$REPORT" 2>&1 section "Sintassi PHP dei file correlati" PHP_BIN="$(command -v php5.6 || command -v php || true)" LINT_TOTAL=0 LINT_ERRORS=0 if [[ -n "$PHP_BIN" ]]; then while IFS= read -r file; do [[ "$file" == *.php && -f "$file" ]] || continue LINT_TOTAL=$((LINT_TOTAL + 1)) if ! "$PHP_BIN" -l "$file" >> "$WORKDIR/php_lint.txt" 2>&1; then LINT_ERRORS=$((LINT_ERRORS + 1)) fi done < "$ALL_FILES" else echo "PHP CLI non disponibile." > "$WORKDIR/php_lint.txt" fi { echo "PHP usato: ${PHP_BIN:-NON DISPONIBILE}" echo "File controllati: $LINT_TOTAL" echo "Errori sintassi: $LINT_ERRORS" cat "$WORKDIR/php_lint.txt" 2>/dev/null || true } >> "$REPORT" section "Richieste recenti verso calendario e relativi codici HTTP" for log in /var/log/apache2/*silvelox*access*.log /var/log/apache2/access.log; do [[ -r "$log" ]] || continue echo "--- $log ---" tail -n 60000 "$log" 2>/dev/null \ | grep -Ei \ '"(POST|PUT|PATCH|DELETE) |calendar|calendario|event|planning|schedule|turni|controllerCtrl|components/' \ | tail -n 5000 || true done >> "$REPORT" 2>&1 section "Errori applicativi recenti successivi alla correzione STEP041" for log in /var/log/apache2/*silvelox*error*.log /var/log/apache2/error.log /var/log/php5.6-fpm.log /var/log/php*-fpm.log; do [[ -r "$log" ]] || continue echo "--- $log ---" tail -n 30000 "$log" 2>/dev/null \ | grep -Ei \ 'silvelox|calendar|calendario|event|planning|schedule|turni|PHP (Fatal|Warning|Notice)|Uncaught|Exception|SQLSTATE|mysqli|PDO|undefined|invalid|missing|required|empty|json|session|403|404|500' \ | tail -n 5000 || true done >> "$REPORT" 2>&1 section "Schema DB delle tabelle nominate negli endpoint" MYSQL_BIN="$(command -v mariadb || command -v mysql || true)" while IFS= read -r file; do [[ -f "$file" ]] || continue grep -Eio \ '(FROM|JOIN|INTO|UPDATE|TABLE)[[:space:]]+[`]?[A-Za-z_][A-Za-z0-9_]*[`]?' \ "$file" 2>/dev/null \ | awk '{print $2}' \ | tr -d '`' || true done < "$ENDPOINT_FILES" | sort -u | head -n 300 > "$TABLE_NAMES" || true cat "$TABLE_NAMES" >> "$REPORT" if [[ -n "$MYSQL_BIN" ]]; then while IFS= read -r table; do [[ "$table" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || continue echo "--- $TARGET_DB.$table ---" "$MYSQL_BIN" --batch --raw --skip-column-names -e \ "SELECT COLUMN_NAME,COLUMN_TYPE,IS_NULLABLE,COLUMN_DEFAULT,EXTRA FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='${TARGET_DB//\'/\'\'}' AND TABLE_NAME='${table//\'/\'\'}' ORDER BY ORDINAL_POSITION;" 2>&1 || true done < "$TABLE_NAMES" else echo "Client MySQL/MariaDB non disponibile." fi >> "$REPORT" 2>&1 section "Riepilogo automatico" { echo "Occorrenze messaggio/varianti: $MATCH_COUNT" echo "File contenenti il messaggio: $(grep -c . "$RELATED_FILES" 2>/dev/null || true)" echo "Endpoint PHP individuati: $(grep -c . "$ENDPOINT_FILES" 2>/dev/null || true)" echo "Modelli/campi estratti: $(grep -c . "$MODEL_TOKENS" 2>/dev/null || true)" echo "File PHP controllati: $LINT_TOTAL" echo "Errori PHP lint: $LINT_ERRORS" echo echo "ESITO: AUDIT COMPLETATO" echo "NESSUNA MODIFICA ESEGUITA" } >> "$REPORT" ( cd "$WORKDIR" zip -q -9 "$OUTPUT_ZIP" ./* ) zip -T "$OUTPUT_ZIP" >/dev/null printf 'AUDIT COMPLETATO\nZIP: %s\n' "$OUTPUT_ZIP"