- TypeScript 62.1%
- Edge 13.9%
- HTML 8.7%
- Shell 7.9%
- Python 7.2%
- Other 0.2%
Manuale: sezione «Siti non nostri» — cosa chiedere al cliente (password applicazione di un amministratore, HTTPS, revocabile da lui) e cosa ARGO fa e non fa. Documentazione tecnica: contratto, le tre garanzie e come sono imposte, limiti verificati sul codice di WordPress, cosa serve prima di esporre l API davvero. |
||
|---|---|---|
| app | ||
| bin | ||
| collectors | ||
| commands | ||
| config | ||
| database/migrations | ||
| docs | ||
| public | ||
| resources | ||
| start | ||
| tests | ||
| .editorconfig | ||
| .env.example | ||
| .gitignore | ||
| ace.js | ||
| adonisrc.ts | ||
| eslint.config.js | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
ARGO
Sorveglianza e hardening di installazioni WordPress su parco proprio.
ARGO monitora decine di WordPress, incrocia le versioni installate con le vulnerabilità note (feed Wordfence Intelligence), esegue controlli di configurazione propri (xmlrpc, enumerazione utenti, permessi, PHP fuori supporto, password deboli, credenziali nei log e nel database) e applica difese reversibili con un click.
Ogni installazione si può scansionare a richiesta dal cruscotto, con un riquadro di avanzamento che mostra tempo trascorso e le quattro fasi (audit → inventario → vulnerabilità → configurazione) mentre si completano.
Gli aggiornamenti dei componenti vulnerabili (plugin, temi, core) sono
reversibili e a livello file: ARGO scarica il pacchetto ufficiale — per il
core verificando i checksum MD5 per ogni file — lo scambia con un backup
pronto al rollback, e lascia che sia WordPress a fare le proprie migrazioni al
primo caricamento (non esegue PHP). Anche l'update ha il suo riquadro a fasi. I
plugin/temi premium (non sul repo pubblico) restano un intervento manuale,
con il link diretto a wp-admin. Un vecchio WordPress esposto e non manutenuto si
può negare via HTTP in modo reversibile (.htaccess), senza cancellarlo.
Non tutto quello che ARGO trova è qualcosa che qualcuno mantiene: su un server
convivono i siti veri, i backup/, gli old/, le copie tenute come prova di
un'intrusione. L'inventario li trova tutti — deve trovarli, sono superficie
d'attacco vera — ma il cruscotto elenca solo ciò che si mantiene: le altre
installazioni si ignorano (restano scansionate, i loro problemi restano
aperti, e una riga in fondo alla lista dichiara sempre quante sono e con quante
criticità). Su un sito ignorato ARGO si rifiuta di scrivere.
Nasce dall'incidente Mandrill dell'agosto 2026: una chiave API esfiltrata da un log di plugin. Il problema non era la singola credenziale — era che nessuno stava guardando il parco nel suo insieme.
Principi (perché è fatto così)
- Agente esterno, nessun plugin sui siti. Un plugin gira dentro il sito che dovrebbe proteggere: se è compromesso lo è anche lui. ARGO agisce da fuori, via SSH.
- Nessun PHP eseguito sui target. I collector leggono file da disco e
interrogano MySQL; l'integrità si verifica con
md5sumcontro i checksum ufficiali di wordpress.org. Zero software da installare o mantenere sui siti. - Ogni modifica è reversibile. Nessuna azione che scrive viene eseguita senza la sua inversa già pronta e un backup. Un click di Ripristina riporta il sito byte per byte allo stato precedente.
- Un fix si verifica contro l'attacco reale, non contro la sua descrizione. Dopo ogni correzione ARGO rigioca l'attacco (es. POST a xmlrpc): se non è chiuso davvero, annulla da solo.
- "Non ho guardato" non è "a posto". Uno scan fallito, un host non raggiungibile, un formato non verificabile: si dichiarano, non si nascondono dietro un verde.
- Non si entra dove non si è stati invitati.
pveshelenca tutti i guest di un cluster, anche di clienti che non abbiamo in inventario: ARGO li interroga dal lato hypervisor e non esegue nulla dentro i container. Ciò che non riesce a ricavare così, lo dichiara come non mappato. - Un sito senza dominio non è un sito «senza vhost». Se WordPress gira, il suo
indirizzo è in
wp_options.siteurl. Se non c'è, quel WordPress non funziona — e il perché (database irraggiungibile, credenziali rifiutate, nessunwp-config) è esso stesso un fatto di sicurezza: si mostra, non si sostituisce con una supposizione. - I segreti non lasciano il target. Password e credenziali trovate nei log vengono ridotte a lunghezza + hash sul posto: ad ARGO arriva solo l'impronta.
Il registro completo delle scelte è in docs/DECISIONI.md.
ARGO sa anche analizzare un WordPress che non è nostro, senza SSH: gli si passa
l'indirizzo e una password applicazione via API, lui interroga il sito in sola
lettura, restituisce il referto in JSON e dimentica la credenziale — che non
viene salvata da nessuna parte. Nessuna modifica al sito: solo GET.
Stack
AdonisJS v6 (TypeScript strict, Lucid, Edge, Vite) su Node 24 · PostgreSQL 15 · Redis + BullMQ · Ansible · collector in Python/bash inviati via SSH.
Il pannello ascolta su :8307, accessibile solo da VPN, dietro autenticazione
locale con secondo fattore TOTP.
Comandi
# scansione e inventario
node ace scan:host <indirizzo> --glob=<pattern> # audit read-only di un host
node ace audit:import --file=audit.json # importa un audit nell'inventario
node ace url:resolve # ricava i domini da wp_options.siteurl
# vulnerabilità
node ace feed:ingest # scarica e ingerisce il feed Wordfence
node ace findings:recompute # incrocia inventario e feed
# controlli propri e sicurezza
node ace checks:run # controlli di configurazione + password deboli
node ace os:assess [host] # postura di hardening del SO (host)
node ace weakpass:site <url> # password deboli di un sito (read-only)
# remediation (reversibile)
node ace fix:site <url> [--undo] # applica/annulla i rimedi di un sito
node ace wp:blocca-http <siteId> [path] # nega l'accesso HTTP a un WordPress obsoleto (reversibile)
# aggiunta target e utenti
node ace target:proxmox <indirizzo> # enumera un Proxmox e ne registra i guest
node ace proxmox:mappa [--prova] # associa ogni host al nodo che lo ospita (per IP, sola lettura)
node ace user:create <email> # crea un operatore
# worker (feed ogni 6h, scansioni ogni 12h, ricalcolo sui due inneschi)
node ace worker:start
API di analisi remota
curl -s -X POST https://<argo>:8307/api/wordpress/analizza \
-H "Authorization: Bearer $ARGO_API_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"url":"https://sito.it","utente":"admin","password":"xxxx xxxx xxxx xxxx"}'
curl -s "https://<argo>:8307/api/wordpress/storico?url=https://sito.it" \
-H "Authorization: Bearer $ARGO_API_TOKEN"
La password applicazione è usata per l'analisi e mai conservata. Senza
ARGO_API_TOKEN configurato l'API resta chiusa (503). Se la credenziale non è
valida ARGO si ferma alla prima richiesta e non restituisce nulla: senza quella
barriera, chi ottenesse il token potrebbe usarci come scanner verso terzi.
Struttura
app/
controllers/ dashboard, auth (TOTP), rimedi, target, utenti
services/ feed, collector, inventory, findings, checks (+catalogo),
os_checks, remediation, provisioning, version_compare, totp,
scan (scansione osservabile), update (aggiornamenti a livello
file, con loader), shell_quote (quoting SSH sicuro)
id_param (validazione degli :id in arrivo dall'URL)
workers/ i worker BullMQ
collectors/ sonde inviate via SSH (read-only o remediation reversibile)
wp_probe.sh inventario WordPress
wp_leak_probe.sh credenziali nei log di file (redazione sul target)
wp_dbleak_probe.py credenziali nelle tabelle di log del database
wp_weakpass_probe.py password deboli via hash, sul target
os_probe.sh postura di hardening del SO
wp_fix.sh motore di remediation reversibile (config + deny-http)
wp_update.sh aggiornamenti reversibili a livello file (plugin/tema/core)
database/migrations/ schema (solo via migration)
docs/DECISIONI.md il perché di ogni scelta
Sicurezza operativa
- Accesso: utenti locali + TOTP. Le operazioni distruttive ("correggi tutto", "annulla tutto", rollback) richiedono la riconferma del secondo fattore al momento.
- Le password di root dei target non vengono mai conservate: si usano in memoria per installare la chiave di ARGO e si scartano. Dopo il bootstrap, tutto è a chiave.
- La chiave di ARGO è vincolabile a un comando forzato lato server per limitare cosa può fare (fase successiva).
- Il servizio gira con
NODE_ENV=production: le pagine d'errore non espongono query, sorgenti o percorsi. Conseguenza: Edge tiene le viste in cache, quindi ogni modifica a un.edgerichiedesystemctl restart argo.service. - Un sito ignorato è escluso dalle scritture: correzioni e aggiornamenti vengono rifiutati dal server, non solo nascosti dall'interfaccia. Gli annullamenti restano invece sempre possibili: tornare indietro non si blocca mai.