From fced51fd6a9990c96bcac2530815781a9b6126b2 Mon Sep 17 00:00:00 2001 From: ayalaphiscan Date: Wed, 10 Jun 2026 22:12:59 +0200 Subject: [PATCH 1/2] Add web-security-guard plugin (Security, Compliance, & Legal) Co-Authored-By: Claude Fable 5 --- .claude-plugin/marketplace.json | 21 ++- README-zh.md | 1 + README.md | 1 + .../.claude-plugin/plugin.json | 7 + plugins/web-security-guard/LICENSE | 21 +++ plugins/web-security-guard/README.md | 52 ++++++ .../commands/proteggi-sito.md | 16 ++ .../commands/security-audit.md | 20 +++ .../skills/architettura-fortezza/SKILL.md | 53 ++++++ .../references/fortezza/README.md | 68 ++++++++ .../fortezza/cassaforte/backup-cifrato.sh | 37 +++++ .../fortezza/cassaforte/genera-chiavi.sh | 23 +++ .../fortezza/cassaforte/pull-su-cassaforte.sh | 31 ++++ .../fortezza/origine/firewall-origine.sh | 34 ++++ .../references/fortezza/scudo/Caddyfile | 41 +++++ .../fortezza/scudo/docker-compose.yml | 39 +++++ .../fortezza/scudo/firewall-stealth.sh | 38 +++++ .../fortezza/scudo/guardian-proxy/Dockerfile | 10 ++ .../fortezza/scudo/guardian-proxy/guardian.js | 132 +++++++++++++++ .../scudo/guardian-proxy/package.json | 12 ++ .../fortezza/scudo/guardian-proxy/server.js | 65 ++++++++ .../vpn-wireguard/README-wireguard.md | 60 +++++++ .../skills/autenticazione-sicura/SKILL.md | 41 +++++ .../skills/difesa-attacchi/SKILL.md | 38 +++++ .../references/guardian-express.js | 155 ++++++++++++++++++ .../skills/hardening-siti/SKILL.md | 57 +++++++ .../skills/privacy-pagamenti/SKILL.md | 48 ++++++ .../skills/sicurezza-github/SKILL.md | 23 +++ .../references/dependabot.yml | 19 +++ .../references/security-workflow.yml | 62 +++++++ 30 files changed, 1224 insertions(+), 1 deletion(-) create mode 100644 plugins/web-security-guard/.claude-plugin/plugin.json create mode 100644 plugins/web-security-guard/LICENSE create mode 100644 plugins/web-security-guard/README.md create mode 100644 plugins/web-security-guard/commands/proteggi-sito.md create mode 100644 plugins/web-security-guard/commands/security-audit.md create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/SKILL.md create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/README.md create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/backup-cifrato.sh create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/genera-chiavi.sh create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/pull-su-cassaforte.sh create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/origine/firewall-origine.sh create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/Caddyfile create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/docker-compose.yml create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/firewall-stealth.sh create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/Dockerfile create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/guardian.js create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/package.json create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/server.js create mode 100644 plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/vpn-wireguard/README-wireguard.md create mode 100644 plugins/web-security-guard/skills/autenticazione-sicura/SKILL.md create mode 100644 plugins/web-security-guard/skills/difesa-attacchi/SKILL.md create mode 100644 plugins/web-security-guard/skills/difesa-attacchi/references/guardian-express.js create mode 100644 plugins/web-security-guard/skills/hardening-siti/SKILL.md create mode 100644 plugins/web-security-guard/skills/privacy-pagamenti/SKILL.md create mode 100644 plugins/web-security-guard/skills/sicurezza-github/SKILL.md create mode 100644 plugins/web-security-guard/skills/sicurezza-github/references/dependabot.yml create mode 100644 plugins/web-security-guard/skills/sicurezza-github/references/security-workflow.yml diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6f29b6b..5d606a1 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1670,6 +1670,25 @@ "security", "compliance" ] + }, + { + "name": "web-security-guard", + "source": "./plugins/web-security-guard", + "description": "Integrated security for websites and apps: hardening, secure authentication (email verification, 2FA, passkeys), payment privacy, WAF defense agent with lockdown, 4-layer Fortress architecture and automated GitHub security workflows.", + "version": "0.2.1", + "author": { + "name": "Fede", + "url": "https://github.com/ayalaphiscan" + }, + "category": "Security, Compliance, & Legal", + "homepage": "https://github.com/ayalaphiscan/web-security-guard", + "keywords": [ + "security", + "waf", + "hardening", + "2fa", + "payments" + ] } ] -} \ No newline at end of file +} diff --git a/README-zh.md b/README-zh.md index 2c2de0e..f3a06c6 100644 --- a/README-zh.md +++ b/README-zh.md @@ -188,6 +188,7 @@ - [enterprise-security-reviewer](./plugins/enterprise-security-reviewer) - [legal-advisor](./plugins/legal-advisor) - [legal-compliance-checker](./plugins/legal-compliance-checker) +- [web-security-guard](./plugins/web-security-guard) ## 使用教程 diff --git a/README.md b/README.md index e4de615..5220911 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ Install or disable them dynamically with the `/plugin` command — enabling you - [enterprise-security-reviewer](./plugins/enterprise-security-reviewer) - [legal-advisor](./plugins/legal-advisor) - [legal-compliance-checker](./plugins/legal-compliance-checker) +- [web-security-guard](./plugins/web-security-guard) ## Tutorials diff --git a/plugins/web-security-guard/.claude-plugin/plugin.json b/plugins/web-security-guard/.claude-plugin/plugin.json new file mode 100644 index 0000000..aebfd69 --- /dev/null +++ b/plugins/web-security-guard/.claude-plugin/plugin.json @@ -0,0 +1,7 @@ +{ + "name": "web-security-guard", + "version": "0.2.1", + "description": "Sicurezza integrata per siti e app: hardening, autenticazione sicura (verifica email, 2FA, passkey), privacy su pagamenti e abbonamenti, agente di difesa con lockdown, architettura Fortezza a 4 livelli (scudo invisibile, server nascosto, cassaforte offline) e workflow GitHub di scansione automatica.", + "author": { "name": "Fede" }, + "keywords": ["security", "payments", "privacy", "2fa", "github-actions", "waf"] +} diff --git a/plugins/web-security-guard/LICENSE b/plugins/web-security-guard/LICENSE new file mode 100644 index 0000000..3e53ec0 --- /dev/null +++ b/plugins/web-security-guard/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Fede + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/web-security-guard/README.md b/plugins/web-security-guard/README.md new file mode 100644 index 0000000..6777949 --- /dev/null +++ b/plugins/web-security-guard/README.md @@ -0,0 +1,52 @@ +# Web Security Guard + +[![Claude Code Plugin](https://img.shields.io/badge/Claude%20Code-plugin-d97757)](https://docs.claude.com/en/docs/claude-code) +[![Version](https://img.shields.io/badge/version-0.2.1-blue)](https://github.com/ayalaphiscan/web-security-guard/releases) +[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) + +A security plugin for [Claude Code](https://docs.claude.com/en/docs/claude-code) and Claude Cowork that turns Claude into a security-aware engineer. It bundles 6 skills, 2 slash commands and ready-to-deploy infrastructure templates covering the full lifecycle: hardening, authentication, payments privacy, active defense, stealth architecture and CI security. + +``` +/plugin marketplace add ayalaphiscan/web-security-guard +``` + +## What's inside + +| Skill | What it does | +|---|---| +| `hardening-siti` | Applies security hardening whenever a site/app is built or reviewed: security headers, CSP, HTTPS, input validation, cookies, CORS, uploads, OWASP Top 10 | +| `autenticazione-sicura` | Secure auth flows: email verification codes, 2FA/TOTP, passkeys, password hashing, sessions, account recovery | +| `difesa-attacchi` | Installs a defense agent (WAF middleware for Express) that detects SQLi, XSS, path traversal, brute force and bots — with rate limiting, IP blocklist and a data-preserving lockdown mode | +| `privacy-pagamenti` | Protects payment data: secure Stripe/PayPal integration, webhook signature verification, PCI-DSS, GDPR, data minimization | +| `architettura-fortezza` | Designs a 4-layer "Fortress": invisible reverse-proxy shield, hidden origin server, WireGuard mesh, air-gapped offline vault with encrypted backups. Includes all deployment templates in `references/fortezza/` | +| `sicurezza-github` | Adds automated security workflows to your repos: dependency scanning, secret scanning, CodeQL, Dependabot | + +**Commands:** `/proteggi-sito` (install all protections in the current project) · `/security-audit` (full security audit with report) + +**Battle-tested defense agent.** The included `guardian` middleware (plain Node.js, zero runtime dependencies) handles malformed-URI evasion attempts, prunes its memory maps to avoid unbounded growth, and ships in two flavors: standard (with explanatory 403s) and stealth (attackers get their connection dropped, no response at all). + +## Installation + +### Claude Code +``` +/plugin marketplace add ayalaphiscan/web-security-guard +/plugin install web-security-guard@web-security-guard +``` + +### Claude Cowork (desktop app) +Download this repository as a zip, rename it to `web-security-guard.plugin`, then install it from **Settings → Capabilities**. + +## Usage examples + +- *"Build me a login page"* → hardening + secure authentication kick in automatically +- *"Add Stripe subscriptions"* → payment privacy rules are applied +- *"I want my server hidden and my data in a box"* → Fortress architecture, with templates +- `/security-audit` → full report of issues and fixes for the current project + +## Honest limits (by design, stated in the skills) + +The public web port can never be invisible — what can be made invisible are SSH, admin, the real origin server and the vault. Large volumetric DDoS still requires an external CDN/WAF. The skills promise *minimal attack surface, unreachable core, recoverable data* — never "unhackable". + +## License + +[MIT](LICENSE) — © ayalaphiscan (Fede) diff --git a/plugins/web-security-guard/commands/proteggi-sito.md b/plugins/web-security-guard/commands/proteggi-sito.md new file mode 100644 index 0000000..f39f42b --- /dev/null +++ b/plugins/web-security-guard/commands/proteggi-sito.md @@ -0,0 +1,16 @@ +--- +description: Installa nel progetto corrente tutte le protezioni - middleware anti-attacco, security headers, file di sicurezza GitHub e hardening +--- + +Installa le protezioni di sicurezza nel progetto della cartella di lavoro corrente (se non c'è una cartella connessa, chiedila con request_cowork_directory). Prima rileva lo stack (Node/Express, Python, PHP, sito statico) guardando i file del progetto. + +Applica nell'ordine: + +1. **Agente anti-attacco** (skill difesa-attacchi): copia e adatta `guardian-express.js` in `security/guardian.js` e integralo nell'entry point dell'app. Per stack non-Node, riscrivi la stessa logica nel linguaggio del progetto. Configura `skipPaths` per eventuali webhook di pagamento. +2. **Security headers e hardening** (skill hardening-siti): aggiungi helmet/headers, correggi cookie, CORS e gestione errori dove necessario. +3. **File GitHub** (skill sicurezza-github): crea `.github/workflows/security.yml` e `.github/dependabot.yml` adattati al linguaggio del progetto; verifica `.gitignore`. +4. **Verifiche pagamenti e autenticazione**: se il progetto gestisce pagamenti o login, controlla i punti critici delle skill privacy-pagamenti e autenticazione-sicura e correggi ciò che è automatizzabile; segnala il resto. + +Non rimuovere funzionalità esistenti; se una modifica rischia di rompere qualcosa, chiedi prima conferma all'utente con AskUserQuestion. + +Alla fine presenta un riepilogo: cosa è stato installato, cosa deve fare l'utente sul suo hosting/GitHub (attivare secret scanning, push protection, WAF/CDN), e come si attiva/disattiva manualmente il lockdown (file flag LOCKDOWN). diff --git a/plugins/web-security-guard/commands/security-audit.md b/plugins/web-security-guard/commands/security-audit.md new file mode 100644 index 0000000..e249973 --- /dev/null +++ b/plugins/web-security-guard/commands/security-audit.md @@ -0,0 +1,20 @@ +--- +description: Esegue un audit di sicurezza completo del progetto corrente e produce un report con problemi e correzioni +--- + +Esegui un audit di sicurezza del progetto nella cartella di lavoro corrente (se non c'è una cartella connessa, chiedila con request_cowork_directory). + +Analizza il codice cercando, in ordine di gravità: + +1. **Segreti nel codice**: API key, password, token, chiavi private hardcoded o in file committati (controlla anche che `.env` sia in `.gitignore`). +2. **Injection**: query SQL costruite con concatenazione, input utente in comandi shell, `eval`, percorsi file da input. +3. **XSS**: input utente inserito in HTML senza escaping (`innerHTML`, template senza autoescape). +4. **Autenticazione**: password non hashate o con hash deboli (MD5/SHA1), assenza di rate limiting sul login, sessioni/cookie senza flag di sicurezza, mancanza di verifica email o 2FA dove appropriato (confronta con la skill autenticazione-sicura). +5. **Pagamenti**: dati carta salvati o loggati, webhook senza verifica firma, prezzi presi dal client (confronta con la skill privacy-pagamenti). +6. **Configurazione**: security headers mancanti, CORS aperto, errori che espongono stack trace, HTTP senza redirect a HTTPS (confronta con la skill hardening-siti). +7. **Dipendenze**: esegui `npm audit` o `pip-audit` se disponibili nel sandbox. +8. **Repo GitHub**: presenza di workflow di sicurezza e dependabot (skill sicurezza-github). + +Produci un report con: riepilogo (numero problemi per gravità Critica/Alta/Media/Bassa), per ogni problema il file e la riga, perché è pericoloso (in linguaggio semplice), e la correzione concreta. Salva il report come `security-audit.md` negli outputs e presentalo con present_files. + +Alla fine chiedi all'utente se vuole che applichi subito le correzioni (in tal caso applica le skill di questo plugin). diff --git a/plugins/web-security-guard/skills/architettura-fortezza/SKILL.md b/plugins/web-security-guard/skills/architettura-fortezza/SKILL.md new file mode 100644 index 0000000..9c81688 --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/SKILL.md @@ -0,0 +1,53 @@ +--- +name: architettura-fortezza +description: Progetta e installa un'infrastruttura di sicurezza a 4 livelli dove il cuore (dati e chiavi) è offline e staccato, lo scudo è invisibile agli scanner e il server reale è nascosto. Usare quando l'utente vuole un sistema "chiuso in una scatola", un reverse proxy invisibile, un'alternativa self-hosted a Cloudflare, server nascosto, VPN WireGuard, o backup offline irraggiungibili. Trigger - "fortezza", "scatola chiusa", "offline", "invisibile", "nascosto", "mini cloudflare", "reverse proxy", "air gap", "cassaforte", "WireGuard". +--- + +# Architettura Fortezza (4 livelli) + +Quando l'utente vuole un sistema "blindato" dove il cuore è staccato e lo scudo è +invisibile, proporre e installare questa architettura. I file template completi +(proxy guardian, Caddy, docker-compose, firewall, WireGuard, backup cifrati) sono +in `references/fortezza/` dentro questa skill; questa skill spiega come adattarli. + +## Premessa onesta da dare SEMPRE all'utente +La porta web pubblica non può essere invisibile: se i visitatori la raggiungono, +la raggiunge anche chi attacca. Invisibili possono diventare invece SSH, +l'amministrazione, il server reale e la cassaforte. E i DDoS volumetrici enormi +restano gestibili solo con un CDN/WAF esterno (es. Cloudflare free) davanti allo +scudo. Non promettere "non attaccabile": promettere "superficie ridotta al minimo, +cuore irraggiungibile, dati sempre recuperabili". + +## I quattro livelli + +1. **Scudo invisibile** — unica macchina pubblica. Caddy (HTTPS + rimozione + impronte) → proxy `guardian` (WAF, rate limit, blocklist silenziosa, lockdown, + honeypot). Firewall stealth: solo 80/443 visibili, resto in DROP, niente ping. +2. **Server origine nascosto** — sito + DB, nessuna porta pubblica, accetta solo + lo scudo via VPN. L'app ascolta sull'IP VPN, mai esposta dal provider. +3. **WireGuard** — rete privata cifrata tra scudo, origine e admin. Non risponde + senza chiave valida → invisibile agli scanner. Solo gli `AllowedIPs` passano. +4. **Cassaforte offline** — air-gapped, custodisce la chiave privata. Backup + cifrati con chiave pubblica (il server cifra ma non può decifrare). La + cassaforte va a prendere i backup (sola andata); il server non la conosce. + +## Principi di progettazione da rispettare +- **Minima superficie**: ogni livello espone solo ciò che serve al livello accanto. +- **Conoscenza parziale**: ogni macchina conosce solo il vicino, mai l'intera catena. +- **One-way verso il cuore**: nessun percorso che parta dal server e arrivi alla cassaforte. +- **Cifratura asimmetrica**: chi può essere compromesso (il server) ha solo la chiave pubblica. +- **Silenzio**: agli attaccanti non si risponde (stealth), per non dare impronte né feedback. +- **Difesa in profondità**: questa architettura si SOMMA alle altre skill del plugin + (hardening-siti, difesa-attacchi, privacy-pagamenti, autenticazione-sicura), non le sostituisce. + +## Installazione (ordine) +1. Cassaforte: generare le chiavi offline (`genera-chiavi.sh`). +2. WireGuard su scudo, origine, admin. +3. Origine: avviare app su IP VPN, poi `firewall-origine.sh`. +4. Scudo: configurare `ORIGIN_URL` e dominio, `docker compose up -d --build`, `firewall-stealth.sh`. +5. Backup: cron notturno di `backup-cifrato.sh` sull'origine; pull periodico dalla cassaforte. + +## Verifica +Da macchina esterna `nmap -Pn IP_SCUDO`: solo 80/443. SSH/WireGuard invisibili. +Il server origine non deve rispondere. Provare un ripristino di backup sulla +cassaforte per confermare che la catena funziona. diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/README.md b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/README.md new file mode 100644 index 0000000..875b12a --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/README.md @@ -0,0 +1,68 @@ +# Fortezza — architettura a 4 livelli + +Sistema di sicurezza "a scatole chiuse": il cuore (dati e chiavi) è staccato e +offline, lo scudo è invisibile agli scanner, e ogni livello conosce solo quello +immediatamente accanto. Un attaccante che superasse un livello non troverebbe il +successivo, perché non ne conosce nemmeno l'indirizzo. + +## I quattro livelli + +1. **Scudo invisibile** (`scudo/`) — l'unica macchina esposta a internet. Caddy + gestisce HTTPS e nasconde ogni impronta del server; il proxy `guardian` + filtra le richieste malevole, applica rate limiting, blocca gli IP ostili in + silenzio (modalità stealth: niente risposte agli attaccanti) e attiva il + lockdown sotto attacco. Il firewall (`firewall-stealth.sh`) lascia visibili + solo le porte 80/443 e fa sparire tutto il resto. + +2. **Server origine nascosto** (`origine/`) — il sito e il database reali. Non + ha alcuna porta pubblica: accetta connessioni **solo dallo scudo**, e solo + attraverso la VPN. Per il mondo esterno questa macchina non esiste. + +3. **Corridoio invisibile WireGuard** (`vpn-wireguard/`) — la rete privata + cifrata che collega scudo, origine e amministratore. WireGuard non risponde a + chi non ha la chiave: agli scanner appare come nulla. + +4. **Cassaforte offline** (`cassaforte/`) — il cuore. Macchina air-gapped che + custodisce la chiave privata e i backup. È la cassaforte ad andare a prendere + i backup (sola andata): il server non può raggiungerla né sa che esiste. I + backup sono cifrati con la chiave pubblica, quindi il server può crearli ma + non leggerli — solo la cassaforte può. + +``` +Internet ─▶ [Scudo: Caddy + guardian] ──VPN──▶ [Origine: sito + DB] + (l'unico visibile) │ crea backup cifrati + ▼ + [Cassaforte OFFLINE] ◀── va a prenderli (sola andata) + chiave privata + ripristino +``` + +## Ordine di installazione + +1. **Cassaforte** (offline): `bash cassaforte/genera-chiavi.sh` → ottieni chiave + privata (resta qui) e pubblica (copiala sul server origine). +2. **WireGuard** su tutte e tre le macchine: segui `vpn-wireguard/README-wireguard.md`. +3. **Server origine**: avvia l'app sull'IP VPN, poi `sudo bash origine/firewall-origine.sh`. +4. **Scudo**: imposta `ORIGIN_URL` (IP VPN dell'origine) nel `docker-compose.yml`, + il dominio nel `Caddyfile`, poi `docker compose up -d --build` e + `sudo bash scudo/firewall-stealth.sh`. +5. **Backup**: pianifica `origine/../cassaforte/backup-cifrato.sh` (cron notturno + sul server origine). Periodicamente accendi la cassaforte e lancia + `cassaforte/pull-su-cassaforte.sh`. + +## Verifica dell'invisibilità +Da una macchina esterna: `nmap -Pn TUO_IP_SCUDO` deve mostrare solo 80/443. +SSH e WireGuard non devono comparire. Il server origine non deve rispondere affatto. + +## Cosa questa architettura fa e non fa +- **Fa**: rende il cuore (dati/chiavi) irraggiungibile, nasconde la struttura + interna, blocca attacchi applicativi, garantisce backup illeggibili e + ripristinabili anche dopo una compromissione totale del server. +- **Non fa**: rendere invisibile la porta web pubblica (se la trovano i clienti, + la trova chi attacca) né assorbire DDoS volumetrici enormi. Per quelli, metti + il piano gratuito di Cloudflare davanti allo scudo: i due si combinano bene. + +## Sicurezza operativa +Le chiavi private non lasciano mai la loro macchina. Ruota le chiavi se sospetti +una compromissione. Tieni una copia della chiave privata della cassaforte su +supporto fisico in un cassetto: se la perdi, i backup diventano illeggibili anche +per te. diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/backup-cifrato.sh b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/backup-cifrato.sh new file mode 100644 index 0000000..9465c99 --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/backup-cifrato.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# backup-cifrato.sh — gira sul SERVER ORIGINE. +# Crea un backup del database cifrato con la CHIAVE PUBBLICA della cassaforte. +# +# Punto chiave dell'architettura "scatola chiusa": +# il server origine ha SOLO la chiave PUBBLICA. Può cifrare e creare backup, +# ma NON può rileggerli. Solo la cassaforte offline, che custodisce la chiave +# PRIVATA, può decifrarli. Anche se un attaccante prendesse il controllo totale +# del server, otterrebbe archivi illeggibili. +set -euo pipefail + +OUT_DIR="${OUT_DIR:-/var/backups/fortezza}" +PUB_KEY="${PUB_KEY:-/etc/fortezza/cassaforte.pub}" # solo chiave pubblica, mai la privata +STAMP="$(date +%Y%m%d-%H%M%S)" +TMP="$(mktemp -d)" +mkdir -p "$OUT_DIR" + +echo "[*] Dump del database..." +# Adatta al tuo DB. Esempi: +# PostgreSQL: pg_dump "$DATABASE_URL" > "$TMP/db.sql" +# MySQL: mysqldump --single-transaction db > "$TMP/db.sql" +pg_dump "${DATABASE_URL:?DATABASE_URL mancante}" > "$TMP/db.sql" + +echo "[*] Comprimo e cifro con la chiave pubblica della cassaforte (age)..." +# 'age' = strumento di cifratura moderno e semplice (https://age-encryption.org) +tar -czf "$TMP/backup.tar.gz" -C "$TMP" db.sql +age -R "$PUB_KEY" -o "$OUT_DIR/backup-$STAMP.tar.gz.age" "$TMP/backup.tar.gz" + +# Pulizia dei file in chiaro +shred -u "$TMP/db.sql" "$TMP/backup.tar.gz" 2>/dev/null || rm -f "$TMP/db.sql" "$TMP/backup.tar.gz" +rmdir "$TMP" 2>/dev/null || true + +# Conserva solo gli ultimi 14 backup cifrati +ls -1t "$OUT_DIR"/backup-*.tar.gz.age 2>/dev/null | tail -n +15 | xargs -r rm -f + +echo "[✓] Backup cifrato pronto: $OUT_DIR/backup-$STAMP.tar.gz.age" +echo " Questo file è ILLEGGIBILE senza la chiave privata custodita offline." diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/genera-chiavi.sh b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/genera-chiavi.sh new file mode 100644 index 0000000..a0bfaa1 --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/genera-chiavi.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# genera-chiavi.sh — ESEGUIRE UNA SOLA VOLTA, sulla CASSAFORTE OFFLINE. +# Genera la coppia di chiavi della cassaforte. +# - chiave PRIVATA: resta qui, non lascia MAI questa macchina. +# - chiave PUBBLICA: la copi sul server origine, serve solo a cifrare. +set -euo pipefail +DIR="${DIR:-$HOME/.fortezza}" +mkdir -p "$DIR"; chmod 700 "$DIR" + +if [ -f "$DIR/cassaforte.key" ]; then + echo "Esiste già una chiave in $DIR — interrompo per non sovrascriverla." + exit 1 +fi + +age-keygen -o "$DIR/cassaforte.key" +grep 'public key:' "$DIR/cassaforte.key" | sed 's/# public key: //' > "$DIR/cassaforte.pub" +chmod 600 "$DIR/cassaforte.key" + +echo "[✓] Chiavi generate in $DIR" +echo " PRIVATA: $DIR/cassaforte.key -> NON copiarla da nessuna parte. Fanne una copia su carta/USB in cassetto." +echo " PUBBLICA: $DIR/cassaforte.pub -> copiala sul server origine in /etc/fortezza/cassaforte.pub" +echo +echo "Senza la chiave privata, NESSUN backup è leggibile. Se la perdi, perdi i backup: custodiscila." diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/pull-su-cassaforte.sh b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/pull-su-cassaforte.sh new file mode 100644 index 0000000..813648f --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/cassaforte/pull-su-cassaforte.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# pull-su-cassaforte.sh — gira sulla CASSAFORTE (macchina offline/air-gapped, +# es. un mini-PC o un disco che colleghi solo quando serve). +# +# Direzione UNICA: è la cassaforte ad andare a PRENDERE i backup dal server, +# il server non può MAI iniziare una connessione verso la cassaforte e non ne +# conosce nemmeno l'esistenza. Così non c'è un percorso che un attaccante possa +# risalire dal server verso il cuore del sistema. +# +# Uso tipico: accendi la cassaforte, la colleghi alla VPN per pochi minuti, +# lanci lo script, poi la stacchi di nuovo. Niente di permanente. +set -euo pipefail + +ORIGINE_VPN="${ORIGINE_VPN:-10.8.0.2}" # IP VPN del server origine +REMOTE_DIR="${REMOTE_DIR:-/var/backups/fortezza}" +LOCAL_DIR="${LOCAL_DIR:-$HOME/fortezza-backups}" +SSH_KEY="${SSH_KEY:-$HOME/.ssh/cassaforte_ed25519}" +PRIV_KEY="${PRIV_KEY:-$HOME/.fortezza/cassaforte.key}" # chiave PRIVATA: vive SOLO qui + +mkdir -p "$LOCAL_DIR" + +echo "[*] Scarico i backup cifrati dal server origine (sola lettura)..." +rsync -av --ignore-existing -e "ssh -i $SSH_KEY" \ + "fortezza@${ORIGINE_VPN}:${REMOTE_DIR}/backup-*.tar.gz.age" "$LOCAL_DIR/" + +echo "[✓] Backup al sicuro nella cassaforte: $LOCAL_DIR" +echo +echo "Per RIPRISTINARE un backup (solo qui, offline):" +echo " age -d -i $PRIV_KEY backup-XXXX.tar.gz.age | tar -xzf - " +echo +echo "Ora puoi scollegare la cassaforte dalla rete." diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/origine/firewall-origine.sh b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/origine/firewall-origine.sh new file mode 100644 index 0000000..b807fed --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/origine/firewall-origine.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# firewall-origine.sh — il SERVER REALE (sito + database). +# Questo server NON ha un indirizzo pubblico raggiungibile: accetta connessioni +# SOLO dallo scudo, attraverso la rete privata/VPN. Per il resto del mondo +# semplicemente non esiste. +set -euo pipefail + +# IP dello scudo SULLA RETE PRIVATA/WIREGUARD (non il suo IP pubblico) +SCUDO_VPN_IP="${SCUDO_VPN_IP:-10.8.0.1}" +APP_PORT="${APP_PORT:-3000}" + +echo "[*] Blindo il server origine. Accetta solo lo scudo ($SCUDO_VPN_IP)." + +ufw --force reset +ufw default deny incoming +ufw default allow outgoing + +# L'app risponde SOLO allo scudo, e solo via VPN +ufw allow from "$SCUDO_VPN_IP" to any port "$APP_PORT" proto tcp + +# Amministrazione SSH: solo via VPN +ufw allow from 10.8.0.0/24 to any port 22 proto tcp + +# WireGuard +ufw allow 51820/udp + +# Niente ping, DROP silenzioso +sed -i 's/^DEFAULT_INPUT_POLICY=.*/DEFAULT_INPUT_POLICY="DROP"/' /etc/default/ufw || true +grep -q icmp_echo_ignore_all /etc/sysctl.conf || echo "net.ipv4.icmp_echo_ignore_all = 1" >> /etc/sysctl.conf +sysctl -p >/dev/null || true + +ufw --force enable +echo "[✓] Server origine invisibile: nessuna porta pubblica, solo lo scudo via VPN." +echo " IMPORTANTE: l'app deve ascoltare sull'IP VPN o 0.0.0.0, mai esporre porte pubbliche dal provider." diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/Caddyfile b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/Caddyfile new file mode 100644 index 0000000..1f64fd3 --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/Caddyfile @@ -0,0 +1,41 @@ +# Caddy: HTTPS automatico + rimozione impronte del server. +# Sostituisci tuo-dominio.it con il tuo dominio reale. + +{ + # Nessun header informativo a livello globale + servers { + protocols h1 h2 h3 + } + email tuo-indirizzo@email.it # per i certificati Let's Encrypt +} + +tuo-dominio.it, www.tuo-dominio.it { + encode gzip zstd + + # Inoltra allo scudo guardian (stesso host, rete docker interna) + reverse_proxy guardian-proxy:8080 + + # Security headers (allineati alla skill hardening-siti) + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + X-Content-Type-Options "nosniff" + X-Frame-Options "DENY" + Referrer-Policy "strict-origin-when-cross-origin" + Permissions-Policy "camera=(), microphone=(), geolocation=()" + # Rimuove le impronte: Caddy non rivela nome/versione + -Server + -X-Powered-By + } + + # Log in formato strutturato (utile per il guardian/SIEM) + log { + output file /var/log/caddy/access.log + format json + } +} + +# Qualsiasi richiesta che NON usa il dominio giusto (es. scanner che colpiscono +# l'IP nudo) non riceve nulla di utile: connessione chiusa. +:80 { + abort +} diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/docker-compose.yml b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/docker-compose.yml new file mode 100644 index 0000000..0f6ff21 --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/docker-compose.yml @@ -0,0 +1,39 @@ +# Scudo Fortezza — gira sul server pubblico (l'unico esposto a internet). +# Il server reale NON è qui: vive su un'altra macchina con solo IP privato. +services: + caddy: + image: caddy:2-alpine + restart: unless-stopped + ports: + - "80:80" + - "443:443" + - "443:443/udp" # HTTP/3 + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + - ./logs:/var/log/caddy + depends_on: [guardian-proxy] + networks: [interna] + + guardian-proxy: + build: ./guardian-proxy + restart: unless-stopped + environment: + # IP PRIVATO del server reale, raggiungibile solo via rete interna/VPN. + # NON è un indirizzo pubblico. + ORIGIN_URL: "http://10.8.0.2:3000" + PORT: "8080" + volumes: + - ./logs:/app/logs # security-events.log e flag LOCKDOWN qui + networks: [interna] + # Non espone porte verso l'esterno: solo Caddy può raggiungerlo. + expose: ["8080"] + +volumes: + caddy_data: + caddy_config: + +networks: + interna: + driver: bridge diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/firewall-stealth.sh b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/firewall-stealth.sh new file mode 100644 index 0000000..9fbf493 --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/firewall-stealth.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# firewall-stealth.sh — rende il SERVER SCUDO "invisibile" agli scanner. +# Da eseguire come root sul server pubblico (Ubuntu/Debian con ufw). +# Effetto: rispondono solo le porte web (80/443). Tutto il resto è DROP +# (pacchetto scartato senza risposta) -> per nmap il server appare "filtrato/morto". +set -euo pipefail + +echo "[*] Configuro firewall stealth..." + +# Policy di default: nega tutto in ingresso, consenti uscita +ufw --force reset +ufw default deny incoming +ufw default allow outgoing + +# Web pubblico (l'unica superficie visibile) +ufw allow 80/tcp +ufw allow 443/tcp +ufw allow 443/udp # HTTP/3 + +# SSH: NON aperto al mondo. Solo dalla VPN WireGuard (vedi livello 3). +# Sostituisci 10.8.0.0/24 con la tua subnet WireGuard. +ufw allow from 10.8.0.0/24 to any port 22 proto tcp + +# WireGuard stesso (cambia 51820 con la tua porta; tienila non standard) +ufw allow 51820/udp + +# DROP silenzioso invece di REJECT: niente "porta chiusa", solo silenzio. +sed -i 's/^DEFAULT_INPUT_POLICY=.*/DEFAULT_INPUT_POLICY="DROP"/' /etc/default/ufw || true + +# Non rispondere ai ping (riduce la rilevabilità) +if ! grep -q "net.ipv4.icmp_echo_ignore_all" /etc/sysctl.conf; then + echo "net.ipv4.icmp_echo_ignore_all = 1" >> /etc/sysctl.conf +fi +sysctl -p >/dev/null || true + +ufw --force enable +echo "[✓] Scudo in modalità stealth: visibili solo 80/443. SSH solo via VPN." +echo " Verifica da un'altra macchina: nmap -Pn TUO_IP" diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/Dockerfile b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/Dockerfile new file mode 100644 index 0000000..5048bdb --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/Dockerfile @@ -0,0 +1,10 @@ +FROM node:20-alpine +WORKDIR /app +COPY package.json ./ +RUN npm install --omit=dev +COPY . . +# Utente non-root: se lo scudo fosse compromesso, l'attaccante non è amministratore +RUN addgroup -S app && adduser -S app -G app && chown -R app:app /app +USER app +EXPOSE 8080 +CMD ["node", "server.js"] diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/guardian.js b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/guardian.js new file mode 100644 index 0000000..8abbbe3 --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/guardian.js @@ -0,0 +1,132 @@ +/** + * guardian.js — Agente di difesa (versione Fortezza, con modalità silenziosa) + * Rileva richieste malevole, rate limiting, blocklist IP, lockdown. + * silentMode: gli attaccanti non ricevono risposte — connessione chiusa. + */ +const fs = require('fs'); +const path = require('path'); + +const PATTERNS = [ + /(\b(union|select)\b[\s\S]{0,40}\b(from|sleep|benchmark)\b)|('\s*(or|and)\s+['\d])|(--\s*$)|(\bdrop\s+table\b)/i, + / { try { return decodeURIComponent(s); } catch (_) { return null; } }; + +function guardian(userOpts) { + const opts = defaults(userOpts); + const hits = new Map(), offenses = new Map(), blocked = new Map(); + let globalEvents = [], lockdownUntil = 0; + + // Pulizia periodica: senza, le mappe crescono senza limite (esaurimento memoria) + const sweep = setInterval(() => { + const now = Date.now(); + for (const [k, arr] of hits) { + const a = arr.filter(t => now - t < opts.rateLimit.windowMs); + a.length ? hits.set(k, a) : hits.delete(k); + } + for (const [k, until] of blocked) if (now >= until) { blocked.delete(k); offenses.delete(k); } + for (const [k, o] of offenses) if (o.last && now - o.last > opts.blockMs) offenses.delete(k); + }, 60_000); + if (sweep.unref) sweep.unref(); + + const log = (e) => + fs.appendFile(opts.logFile, JSON.stringify({ ts: new Date().toISOString(), ...e }) + '\n', () => {}); + const inLockdown = () => Date.now() < lockdownUntil || fs.existsSync(opts.lockdownFlagFile); + const silence = (req, res) => { try { req.socket.destroy(); } catch (_) { res.end(); } }; + + const triggerLockdown = async (reason) => { + lockdownUntil = Date.now() + opts.lockdown.unlockAfterMs; + try { fs.writeFileSync(opts.lockdownFlagFile, reason); } catch (_) {} + log({ type: 'LOCKDOWN_ATTIVATO', reason }); + if (opts.onLockdown) { try { await opts.onLockdown(reason); } catch (_) {} } + }; + + const registerOffense = async (ip, req, matched) => { + const ev = { type: 'RICHIESTA_MALEVOLA', ip, method: req.method, url: req.originalUrl, matched }; + log(ev); + if (opts.onAlert) { try { await opts.onAlert(ev); } catch (_) {} } + const o = offenses.get(ip) || { count: 0 }; + o.count += 1; o.last = Date.now(); offenses.set(ip, o); + if (o.count >= opts.blockAfter) { blocked.set(ip, Date.now() + opts.blockMs); log({ type: 'IP_BLOCCATO', ip }); } + const now = Date.now(); + globalEvents = globalEvents.filter(t => now - t < opts.lockdown.windowMs); + globalEvents.push(now); + if (globalEvents.length >= opts.lockdown.threshold && !inLockdown()) + await triggerLockdown(`soglia eventi superata (${globalEvents.length})`); + }; + + const isMalicious = (req) => { + if (HONEYPOT.test(req.path)) return 'honeypot'; + const decoded = safeDecode(req.originalUrl || ''); + if (decoded === null) return 'uri-malformata'; // encoding rotto = tecnica di evasione + const hay = [ + decoded, + JSON.stringify(req.query || {}), + typeof req.body === 'object' ? JSON.stringify(req.body || {}) : String(req.body || ''), + ]; + for (const p of PATTERNS) for (const h of hay) if (p.test(h)) return p.source.slice(0, 40); + if (SCANNER_UA.test(req.headers['user-agent'] || '')) return 'scanner-ua'; + return null; + }; + + return async function guardianMiddleware(req, res, next) { + const ip = req.ip || req.socket.remoteAddress || 'unknown'; + + if (inLockdown()) { + res.set('Retry-After', '900'); + return res.status(503).send('

Manutenzione

Il servizio tornerà disponibile a breve. I dati sono al sicuro.

'); + } + + const until = blocked.get(ip); + if (until) { + if (Date.now() < until) return opts.silentMode ? silence(req, res) : res.status(403).end(); + blocked.delete(ip); offenses.delete(ip); + } + + if (opts.skipPaths.some(p => req.path.startsWith(p))) return next(); + + const now = Date.now(); + const arr = (hits.get(ip) || []).filter(t => now - t < opts.rateLimit.windowMs); + arr.push(now); hits.set(ip, arr); + const strict = opts.strictPaths.some(p => req.path.startsWith(p)); + if (arr.length > (strict ? opts.strictMax : opts.rateLimit.max)) { + log({ type: 'RATE_LIMIT', ip, path: req.path }); + res.set('Retry-After', '60'); + return res.status(429).send('Troppe richieste.'); + } + + const matched = isMalicious(req); + if (matched) { + await registerOffense(ip, req, matched); + return opts.silentMode ? silence(req, res) : res.status(403).end(); + } + next(); + }; +} + +module.exports = { guardian }; diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/package.json b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/package.json new file mode 100644 index 0000000..a77f891 --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/package.json @@ -0,0 +1,12 @@ +{ + "name": "fortezza-scudo", + "version": "1.0.0", + "private": true, + "description": "Scudo Fortezza: reverse proxy invisibile con agente guardian", + "main": "server.js", + "scripts": { "start": "node server.js" }, + "dependencies": { + "express": "^4.19.0", + "http-proxy-middleware": "^3.0.0" + } +} diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/server.js b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/server.js new file mode 100644 index 0000000..a461756 --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/scudo/guardian-proxy/server.js @@ -0,0 +1,65 @@ +/** + * Scudo Fortezza — reverse proxy invisibile con agente guardian. + * Tutto ciò che non è traffico web legittimo viene scartato in silenzio. + * + * Env richieste: + * ORIGIN_URL -> es. http://10.0.0.2:3000 (server nascosto, IP privato) + * PORT -> default 8080 (dietro Caddy) + */ +const express = require('express'); +const { createProxyMiddleware, fixRequestBody } = require('http-proxy-middleware'); +const { guardian } = require('./guardian'); + +const ORIGIN = process.env.ORIGIN_URL; +if (!ORIGIN) { console.error('ORIGIN_URL mancante'); process.exit(1); } + +const app = express(); +app.disable('x-powered-by'); +app.set('trust proxy', 1); // Caddy davanti + +// Corpo letto solo per l'ispezione del guardian (limite anti-abuso) +app.use(express.json({ limit: '100kb', strict: false })); +app.use(express.urlencoded({ extended: false, limit: '100kb' })); + +// ── Invisibilità: niente risposte che rivelino il sistema ── +// Metodi non web: connessione chiusa senza risposta (per gli scanner = host morto) +app.use((req, res, next) => { + const ok = ['GET', 'POST', 'HEAD', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']; + if (!ok.includes(req.method)) return req.socket.destroy(); + next(); +}); + +// ── Agente guardian: WAF, rate limit, blocklist, lockdown ── +app.use(guardian({ + rateLimit: { windowMs: 60_000, max: 120 }, + strictPaths: ['/login', '/api/auth', '/api/pay'], + lockdown: { threshold: 50, windowMs: 300_000, unlockAfterMs: 900_000 }, + skipPaths: ['/webhook'], + // Invisibilità: gli offender non ricevono un 403 "parlante", + // la connessione viene chiusa e basta (vedi silentMode in guardian.js) + silentMode: true, +})); + +// ── Proxy verso il server nascosto ── +app.use(createProxyMiddleware({ + target: ORIGIN, + changeOrigin: true, + xfwd: true, + proxyTimeout: 30_000, + on: { + // Obbligatorio: express.json() ha già consumato lo stream del body; + // senza fixRequestBody ogni POST/PUT con body resterebbe appesa in timeout. + proxyReq: fixRequestBody, + proxyRes: (proxyRes) => { + // Rimuove ogni impronta del backend + delete proxyRes.headers['server']; + delete proxyRes.headers['x-powered-by']; + delete proxyRes.headers['via']; + }, + error: (_err, _req, res) => { + if (res && !res.headersSent) { res.writeHead(502); res.end('Servizio non disponibile.'); } + }, + }, +})); + +app.listen(process.env.PORT || 8080, () => console.log('Scudo attivo')); diff --git a/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/vpn-wireguard/README-wireguard.md b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/vpn-wireguard/README-wireguard.md new file mode 100644 index 0000000..d926bfa --- /dev/null +++ b/plugins/web-security-guard/skills/architettura-fortezza/references/fortezza/vpn-wireguard/README-wireguard.md @@ -0,0 +1,60 @@ +# Livello 3 — Tunnel WireGuard (il "corridoio invisibile") + +WireGuard collega scudo, server origine e postazione di amministrazione su una +rete privata cifrata. È la chiave dell'invisibilità: un server WireGuard **non +risponde a nessun pacchetto** che non sia firmato con una chiave valida. Per uno +scanner (nmap, Shodan) la porta UDP appare chiusa/inesistente — non c'è nulla con +cui dialogare, nessun banner, nessun handshake. Solo chi possiede la chiave entra. + +## Schema indirizzi (esempio) +- Scudo → 10.8.0.1 +- Server origine → 10.8.0.2 +- Tua postazione → 10.8.0.3 + +## Installazione (su ogni macchina) +```bash +sudo apt update && sudo apt install -y wireguard +wg genkey | tee privatekey | wg pubkey > publickey # genera la coppia di chiavi +``` + +## Esempio config server origine — /etc/wireguard/wg0.conf +```ini +[Interface] +Address = 10.8.0.2/24 +ListenPort = 51820 +PrivateKey = + +# Scudo +[Peer] +PublicKey = +AllowedIPs = 10.8.0.1/32 + +# Postazione admin +[Peer] +PublicKey = +AllowedIPs = 10.8.0.3/32 +``` + +## Esempio config scudo — /etc/wireguard/wg0.conf +```ini +[Interface] +Address = 10.8.0.1/24 +PrivateKey = + +[Peer] +PublicKey = +AllowedIPs = 10.8.0.2/32 +Endpoint = :51820 +PersistentKeepalive = 25 +``` + +## Avvio +```bash +sudo systemctl enable --now wg-quick@wg0 +sudo wg # mostra i peer connessi +``` + +## Regole d'oro +- Le chiavi private NON lasciano mai la loro macchina. Backup della chiave admin solo nella cassaforte offline. +- Cambia la porta 51820 con una porta UDP alta e casuale: ulteriore riduzione della rilevabilità. +- Solo gli `AllowedIPs` elencati possono parlare: nessun altro, nemmeno con la porta giusta, viene accettato. diff --git a/plugins/web-security-guard/skills/autenticazione-sicura/SKILL.md b/plugins/web-security-guard/skills/autenticazione-sicura/SKILL.md new file mode 100644 index 0000000..a3096f6 --- /dev/null +++ b/plugins/web-security-guard/skills/autenticazione-sicura/SKILL.md @@ -0,0 +1,41 @@ +--- +name: autenticazione-sicura +description: Implementa autenticazione sicura quando un sito/app richiede registrazione, login o account utente. Copre verifica email con codice, autenticazione a due fattori (2FA/TOTP), passkey e chiavi di sicurezza, hashing password, sessioni e recupero account. Trigger - "login", "registrazione", "verifica email", "codice di verifica", "2FA", "doppio fattore", "autenticazione", "passkey", "account utente". +--- + +# Autenticazione Sicura + +Quando un sito/app prevede account utente, implementare l'autenticazione secondo queste regole. Se l'utente non specifica i dettagli, proporre di default: password + verifica email con codice + 2FA opzionale. + +## Password +- Hash con **Argon2id** (preferito) o bcrypt (cost ≥ 12). MAI MD5/SHA1/SHA256 semplice, mai password in chiaro. +- Lunghezza minima 8-12 caratteri; verificare contro liste di password compromesse se possibile. +- Rate limiting sul login: max ~5 tentativi per account/IP in 15 minuti, poi blocco temporaneo crescente. Risposta identica per "utente inesistente" e "password errata". + +## Verifica email con codice (OTP) +- Codice numerico a 6 cifre generato con CSPRNG (`crypto.randomInt`, `secrets.randbelow`), MAI `Math.random()`. +- Validità 10 minuti, monouso, max 5 tentativi di inserimento, poi invalidare e rigenerare. +- Salvare nel DB solo l'hash del codice, con scadenza. Rate limiting sull'invio (max 1 ogni 60s, 5/ora per indirizzo). +- Stesso schema per: conferma registrazione, reset password, conferma di azioni sensibili (cambio email, cambio IBAN, cancellazione account). + +## 2FA / doppio fattore +Offrire in ordine di preferenza: +1. **Passkey / chiavi di sicurezza (WebAuthn/FIDO2)** — resistenti al phishing. Librerie: `@simplewebauthn/server` (Node), `webauthn` (Python). +2. **TOTP** (Google Authenticator ecc.) — segreto generato server-side, mostrato via QR, verificato con finestra ±1 step. Librerie: `otplib`, `pyotp`. +3. **Codice via email** — minimo accettabile; SMS solo se richiesto esplicitamente. + +Regole: +- Alla attivazione del 2FA generare **10 codici di recupero** monouso (mostrati una sola volta, salvati hashati). +- Richiedere il 2FA a ogni login da dispositivo nuovo e per azioni sensibili (step-up). +- Disattivazione 2FA solo con ri-autenticazione completa. + +## Sessioni e token +- Vedi skill hardening-siti per i cookie. In più: invalidare tutte le sessioni al cambio password; lista "dispositivi connessi" con revoca. +- Se si usano JWT: scadenza breve (15 min) + refresh token revocabile salvato server-side; algoritmo fissato (no `alg: none`). + +## Recupero account +- Reset password tramite link/codice monouso a scadenza, MAI domande di sicurezza. +- Notificare via email ogni evento sensibile: login da nuovo dispositivo, cambio password/email, attivazione/disattivazione 2FA. + +## Cosa non fare mai +- Inviare password via email. Loggare password o codici OTP. Rivelare se un'email è registrata (enumerazione). Implementare crittografia "fatta in casa". diff --git a/plugins/web-security-guard/skills/difesa-attacchi/SKILL.md b/plugins/web-security-guard/skills/difesa-attacchi/SKILL.md new file mode 100644 index 0000000..a794195 --- /dev/null +++ b/plugins/web-security-guard/skills/difesa-attacchi/SKILL.md @@ -0,0 +1,38 @@ +--- +name: difesa-attacchi +description: Aggiunge a un sito/app un agente di difesa che rileva e blocca richieste malevole (SQL injection, XSS, path traversal, brute force, bot) con rate limiting, blocklist IP e modalità lockdown che chiude il sistema preservando i dati. Trigger - "blocca attacchi", "anti-attacco", "firewall", "WAF", "lockdown", "difendi il sito", "rate limiting", "sotto attacco". +--- + +# Difesa Attacchi (agente guardian) + +Quando l'utente vuole protezione attiva dagli attacchi, integrare nel progetto il middleware "guardian". Il codice pronto è in `references/guardian-express.js` (Node/Express); per altri stack (Flask/Django, PHP) adattare la stessa logica. + +## Cosa fa il guardian +1. **Rilevamento pattern malevoli** su URL, query, body e header: SQL injection, XSS, path traversal, command injection, scanner noti. +2. **Rate limiting** per IP (generale + più severo su login/pagamenti). +3. **Blocklist automatica**: un IP che supera la soglia di richieste malevole viene bloccato temporaneamente, con escalation se recidivo. +4. **Modalità lockdown**: se gli eventi malevoli superano la soglia globale, il sito entra in stato di chiusura controllata — risponde 503 con pagina di manutenzione, blocca ogni scrittura, mantiene i dati intatti e (se configurato) esegue un backup e invia un alert. +5. **Logging strutturato** di ogni evento in `security-events.log` per analisi successiva. + +## Come integrarlo (Express) +```js +const { guardian } = require('./security/guardian'); +app.use(guardian({ + rateLimit: { windowMs: 60000, max: 120 }, + strictPaths: ['/login', '/api/auth', '/api/pay'], // limite 10/min + lockdown: { threshold: 50, windowMs: 300000, unlockAfterMs: 900000 }, + onAlert: async (evento) => { /* email/webhook all'amministratore */ }, + onLockdown: async () => { /* backup DB, notifica */ } +})); +``` +Montarlo PRIMA delle route. Copiare `references/guardian-express.js` in `security/guardian.js` nel progetto e adattare le soglie. + +## Regole di implementazione +- Il guardian è una difesa in profondità, NON sostituisce query parametrizzate, validazione e hardening (skill hardening-siti): applicare comunque quelle regole. +- Dietro proxy/CDN ricavare l'IP reale da `X-Forwarded-For` solo se il proxy è fidato (`app.set('trust proxy', 1)`). +- Il lockdown NON deve mai cancellare dati: solo bloccare l'accesso in scrittura e servire la pagina di manutenzione. Lo sblocco è automatico dopo il timeout o manuale (file flag `LOCKDOWN` rimovibile / variabile env). +- Escludere dai controlli i webhook di pagamento verificati con firma (altrimenti payload legittimi possono sembrare sospetti) — la sicurezza lì è la firma (skill privacy-pagamenti). +- Consigliare SEMPRE anche una protezione a livello di piattaforma (Cloudflare/WAF dell'hosting): il middleware difende l'applicazione, non assorbe DDoS volumetrici. + +## Risposta agli incidenti +In caso di attacco rilevato suggerire all'utente: 1) esaminare `security-events.log`, 2) ruotare le chiavi/segreti se c'è sospetto di compromissione, 3) verificare integrità dati con il backup, 4) mantenere il lockdown finché la falla non è chiusa. \ No newline at end of file diff --git a/plugins/web-security-guard/skills/difesa-attacchi/references/guardian-express.js b/plugins/web-security-guard/skills/difesa-attacchi/references/guardian-express.js new file mode 100644 index 0000000..bdddf1a --- /dev/null +++ b/plugins/web-security-guard/skills/difesa-attacchi/references/guardian-express.js @@ -0,0 +1,155 @@ +/** + * guardian-express.js — Agente di difesa applicativa per Express + * Rileva richieste malevole, applica rate limiting, blocklist IP + * e modalità lockdown che chiude il sistema preservando i dati. + * + * Uso: + * const { guardian } = require('./security/guardian'); + * app.use(guardian({ ...opzioni })); + */ +const fs = require('fs'); +const path = require('path'); + +const PATTERNS = [ + // SQL injection + /(\b(union|select)\b[\s\S]{0,40}\b(from|sleep|benchmark)\b)|('\s*(or|and)\s+['\d])|(--\s*$)|(\bdrop\s+table\b)/i, + // XSS + / { try { return decodeURIComponent(s); } catch (_) { return null; } }; + +function guardian(userOpts) { + const opts = defaults(userOpts); + const hits = new Map(); // ip -> timestamp richieste (rate limit) + const offenses = new Map(); // ip -> { count, last } + const blocked = new Map(); // ip -> sbloccoA + let globalEvents = []; // timestamp eventi malevoli (per lockdown) + let lockdownUntil = 0; + + // Pulizia periodica: senza, le mappe crescono senza limite (esaurimento memoria) + const sweep = setInterval(() => { + const now = Date.now(); + for (const [k, arr] of hits) { + const a = arr.filter(t => now - t < opts.rateLimit.windowMs); + a.length ? hits.set(k, a) : hits.delete(k); + } + for (const [k, until] of blocked) if (now >= until) { blocked.delete(k); offenses.delete(k); } + for (const [k, o] of offenses) if (o.last && now - o.last > opts.blockMs) offenses.delete(k); + }, 60_000); + if (sweep.unref) sweep.unref(); + + const log = (entry) => { + const line = JSON.stringify({ ts: new Date().toISOString(), ...entry }) + '\n'; + fs.appendFile(opts.logFile, line, () => {}); + }; + + const inLockdown = () => Date.now() < lockdownUntil || fs.existsSync(opts.lockdownFlagFile); + + const triggerLockdown = async (reason) => { + lockdownUntil = Date.now() + opts.lockdown.unlockAfterMs; + try { fs.writeFileSync(opts.lockdownFlagFile, reason); } catch (_) {} + log({ type: 'LOCKDOWN_ATTIVATO', reason }); + if (opts.onLockdown) { try { await opts.onLockdown(reason); } catch (_) {} } + }; + + const registerOffense = async (ip, req, matched) => { + const ev = { type: 'RICHIESTA_MALEVOLA', ip, method: req.method, url: req.originalUrl, matched }; + log(ev); + if (opts.onAlert) { try { await opts.onAlert(ev); } catch (_) {} } + + const o = offenses.get(ip) || { count: 0 }; + o.count += 1; o.last = Date.now(); + offenses.set(ip, o); + if (o.count >= opts.blockAfter) { + blocked.set(ip, Date.now() + opts.blockMs); + log({ type: 'IP_BLOCCATO', ip, perMs: opts.blockMs }); + } + + const now = Date.now(); + globalEvents = globalEvents.filter(t => now - t < opts.lockdown.windowMs); + globalEvents.push(now); + if (globalEvents.length >= opts.lockdown.threshold && !inLockdown()) { + await triggerLockdown(`soglia eventi superata (${globalEvents.length})`); + } + }; + + const isMalicious = (req) => { + const decoded = safeDecode(req.originalUrl || ''); + if (decoded === null) return 'uri-malformata'; // encoding rotto = tecnica di evasione + const haystacks = [ + decoded, + JSON.stringify(req.query || {}), + typeof req.body === 'object' ? JSON.stringify(req.body || {}) : String(req.body || ''), + ]; + for (const p of PATTERNS) for (const h of haystacks) if (p.test(h)) return p.source.slice(0, 40); + if (SCANNER_UA.test(req.headers['user-agent'] || '')) return 'scanner-ua'; + return null; + }; + + return async function guardianMiddleware(req, res, next) { + const ip = req.ip || req.socket.remoteAddress || 'unknown'; + + // 0. Lockdown: solo lettura pagina manutenzione, nessuna scrittura + if (inLockdown()) { + res.set('Retry-After', '900'); + return res.status(503).send('

Manutenzione di sicurezza

Il servizio tornerà disponibile a breve. I tuoi dati sono al sicuro.

'); + } + + // 1. IP bloccato + const until = blocked.get(ip); + if (until) { + if (Date.now() < until) return res.status(403).send('Accesso negato.'); + blocked.delete(ip); offenses.delete(ip); + } + + // 2. Percorsi esclusi (es. webhook verificati con firma propria) + if (opts.skipPaths.some(p => req.path.startsWith(p))) return next(); + + // 3. Rate limiting + const now = Date.now(); + const arr = (hits.get(ip) || []).filter(t => now - t < opts.rateLimit.windowMs); + arr.push(now); hits.set(ip, arr); + const strict = opts.strictPaths.some(p => req.path.startsWith(p)); + if (arr.length > (strict ? opts.strictMax : opts.rateLimit.max)) { + log({ type: 'RATE_LIMIT', ip, path: req.path }); + res.set('Retry-After', String(Math.ceil(opts.rateLimit.windowMs / 1000))); + return res.status(429).send('Troppe richieste. Riprova più tardi.'); + } + + // 4. Rilevamento pattern malevoli + const matched = isMalicious(req); + if (matched) { + await registerOffense(ip, req, matched); + return res.status(403).send('Richiesta bloccata.'); + } + + next(); + }; +} + +module.exports = { guardian }; diff --git a/plugins/web-security-guard/skills/hardening-siti/SKILL.md b/plugins/web-security-guard/skills/hardening-siti/SKILL.md new file mode 100644 index 0000000..27b7ac6 --- /dev/null +++ b/plugins/web-security-guard/skills/hardening-siti/SKILL.md @@ -0,0 +1,57 @@ +--- +name: hardening-siti +description: Applica regole di sicurezza (hardening) ogni volta che si costruisce, modifica o revisiona un sito web o un'app. Copre security headers, CSP, HTTPS, validazione input, cookie, CORS, upload, gestione errori e OWASP Top 10. Trigger - "crea un sito", "costruisci un'app", "metti in sicurezza", "proteggi il sito", "hardening", "security headers", "build a website". +--- + +# Hardening Siti + +Quando si costruisce o si modifica un sito/app, applicare SEMPRE queste regole di sicurezza senza che l'utente debba chiederlo. Segnalare nel riepilogo finale quali protezioni sono state applicate. + +## Regole obbligatorie + +### 1. Security headers +Applicare su ogni risposta HTML: + +``` +Strict-Transport-Security: max-age=31536000; includeSubDomains +X-Content-Type-Options: nosniff +X-Frame-Options: DENY +Referrer-Policy: strict-origin-when-cross-origin +Permissions-Policy: camera=(), microphone=(), geolocation=() +Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none' +``` + +In Node/Express usare `helmet()`. In Django attivare `SECURE_*` settings. Su hosting statico (Netlify/Vercel/GitHub Pages) usare il file headers della piattaforma. Adattare la CSP alle risorse reali del sito (CDN, font, analytics) — mai usare `unsafe-inline` per gli script; preferire nonce o hash. + +### 2. Input e output +- Validare OGNI input lato server (tipo, lunghezza, formato, whitelist). La validazione client è solo UX. +- Query al database SOLO parametrizzate o via ORM. Mai concatenare stringhe in SQL. +- Escapare l'output nei template (autoescaping attivo). Mai inserire input utente in `innerHTML`, `eval`, attributi evento o URL senza sanitizzazione. +- Path file: mai costruire percorsi da input utente; usare ID mappati o `path.basename` + directory fissa. + +### 3. Cookie e sessioni +- Cookie di sessione: `HttpOnly; Secure; SameSite=Lax` (o `Strict` per azioni sensibili). +- Rigenerare l'ID sessione al login. Scadenza assoluta e per inattività. +- Protezione CSRF su ogni form/azione che modifica stato (token CSRF o SameSite + verifica Origin). + +### 4. CORS +- Mai `Access-Control-Allow-Origin: *` su endpoint autenticati. Whitelist esplicita di origin. + +### 5. Upload di file +- Whitelist di estensioni E content-type verificato sul contenuto reale (magic bytes). +- Rinominare i file con ID casuali, salvarli FUORI dalla web root o su storage separato, limitare la dimensione. + +### 6. Errori e log +- Mai mostrare stack trace o dettagli interni all'utente; pagina di errore generica + log lato server. +- Loggare login, errori di autenticazione, pagamenti e azioni amministrative (senza dati sensibili nei log). + +### 7. Segreti e dipendenze +- Mai chiavi/API key/password nel codice o nella repo: usare variabili d'ambiente e file `.env` in `.gitignore`. +- Generare sempre `.gitignore` con `.env`, `node_modules`, credenziali. +- Usare versioni aggiornate delle dipendenze; consigliare `npm audit` / `pip-audit` (vedi skill sicurezza-github per l'automazione). + +### 8. HTTPS +- Tutto il traffico in HTTPS, redirect da HTTP, HSTS attivo. In sviluppo locale va bene HTTP ma documentare la differenza. + +## Checklist finale +Prima di consegnare un sito/app, verificare: headers presenti, query parametrizzate, validazione server, cookie sicuri, CSRF, niente segreti nel codice, errori generici, `.gitignore` corretto. Elencare all'utente le protezioni applicate e gli eventuali punti che richiedono configurazione sul suo hosting. diff --git a/plugins/web-security-guard/skills/privacy-pagamenti/SKILL.md b/plugins/web-security-guard/skills/privacy-pagamenti/SKILL.md new file mode 100644 index 0000000..8e053d2 --- /dev/null +++ b/plugins/web-security-guard/skills/privacy-pagamenti/SKILL.md @@ -0,0 +1,48 @@ +--- +name: privacy-pagamenti +description: Protegge dati di pagamento e abbonamenti quando un sito/app gestisce checkout, carte, subscription o fatturazione. Copre integrazione sicura Stripe/PayPal, verifica firma webhook, PCI-DSS, GDPR, minimizzazione dati e ciclo di vita degli abbonamenti. Trigger - "pagamenti", "checkout", "abbonamento", "subscription", "Stripe", "PayPal", "carta di credito", "fatturazione", "privacy dei pagamenti". +--- + +# Privacy Pagamenti e Abbonamenti + +Quando un sito/app gestisce pagamenti o abbonamenti, applicare queste regole. + +## Regola d'oro: mai toccare i dati della carta +- Il numero di carta NON deve mai transitare dal proprio server né essere salvato nel proprio DB (requisito PCI-DSS). Usare sempre checkout/elementi ospitati dal provider: Stripe Checkout / Payment Element, PayPal Smart Buttons. +- Nel DB salvare SOLO: ID cliente del provider (es. `cus_...`), ID abbonamento, stato, ultime 4 cifre e brand se forniti dal provider. +- Le chiavi segrete (`sk_...`, client secret) vivono solo in variabili d'ambiente lato server. Nel frontend solo chiavi pubblicabili. + +## Webhook: sempre verificati +Lo stato di pagamenti/abbonamenti si aggiorna SOLO da webhook verificati, mai dal redirect del browser (falsificabile). + +```js +// Stripe (Express) — il body deve essere RAW +app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => { + let event; + try { + event = stripe.webhooks.constructEvent( + req.body, req.headers['stripe-signature'], process.env.STRIPE_WEBHOOK_SECRET + ); + } catch (err) { return res.status(400).send('Firma non valida'); } + // gestire event.type: checkout.session.completed, invoice.paid, + // invoice.payment_failed, customer.subscription.deleted ... + res.json({received: true}); +}); +``` + +Per PayPal usare l'API di verifica firma webhook. Rendere i gestori idempotenti (stesso evento ricevuto due volte = nessun doppio effetto): salvare gli `event.id` processati. + +## Abbonamenti +- Gestire sempre: pagamento fallito (grace period + email), cancellazione (accesso fino a fine periodo), upgrade/downgrade con proratazione del provider. +- La cancellazione deve essere facile quanto l'iscrizione (obbligo in UE/USA). Prevedere pagina "gestisci abbonamento" (Stripe Customer Portal è la via più semplice). +- Prezzi e importi: mai fidarsi di valori inviati dal client; il server usa solo i Price ID configurati. + +## Privacy e GDPR +- **Minimizzazione**: raccogliere solo i dati necessari alla transazione. Niente dati di pagamento nei log, negli URL, in analytics o in email. +- **Informativa**: privacy policy che dichiara provider di pagamento, dati trattati, conservazione. Cookie banner se ci sono cookie non tecnici. +- **Diritti**: prevedere export ed eliminazione dei dati su richiesta (l'eliminazione lato provider va richiesta via API del provider). Conservare i dati di fatturazione per gli obblighi fiscali (in Italia 10 anni) separandoli dal profilo eliminato. +- **Cifratura**: TLS ovunque; dati personali sensibili cifrati at-rest se il DB lo consente. +- **Audit log**: registrare chi/quando per ogni evento di pagamento e modifica di abbonamento (senza PAN o dati carta). + +## Checklist consegna +Chiavi in env, webhook con firma verificata e idempotente, nessun dato carta nel DB/log, portale di gestione abbonamento, privacy policy, flusso di cancellazione, gestione pagamento fallito. diff --git a/plugins/web-security-guard/skills/sicurezza-github/SKILL.md b/plugins/web-security-guard/skills/sicurezza-github/SKILL.md new file mode 100644 index 0000000..e39a15f --- /dev/null +++ b/plugins/web-security-guard/skills/sicurezza-github/SKILL.md @@ -0,0 +1,23 @@ +--- +name: sicurezza-github +description: Aggiunge alle repository GitHub dei siti workflow di sicurezza automatici - scansione dipendenze vulnerabili, ricerca di segreti/chiavi nel codice, analisi statica CodeQL e Dependabot. Trigger - "sicurezza repo", "GitHub Actions di sicurezza", "scansione repository", "metti la sicurezza nella repo", "dependabot", "secret scanning". +--- + +# Sicurezza GitHub + +Quando un progetto ha (o avrà) una repository GitHub, installare i file di sicurezza nella directory `.github/` della repo. I template pronti sono in `references/`. + +## File da installare nella repo + +1. `.github/workflows/security.yml` — da `references/security-workflow.yml`: a ogni push/PR e ogni notte esegue scansione segreti (Gitleaks), audit dipendenze (npm/pip) e analisi statica CodeQL. +2. `.github/dependabot.yml` — da `references/dependabot.yml`: PR automatiche di aggiornamento per dipendenze vulnerabili. + +Adattare al linguaggio del progetto: lasciare il job `npm audit` solo se c'è `package.json`, `pip-audit` solo se c'è `requirements.txt`/`pyproject.toml`, impostare i linguaggi CodeQL corretti (`javascript`, `python`, ecc.). + +## Regole +- Verificare che `.gitignore` escluda `.env` e credenziali PRIMA del primo push. Se un segreto è già finito nella history, va considerato compromesso: ruotarlo subito (cambiare la chiave dal provider), non basta cancellare il file. +- Consigliare all'utente di attivare nelle impostazioni GitHub della repo: Secret scanning + Push protection, Dependabot alerts, branch protection sul branch principale (review obbligatoria, status check del workflow security). +- I workflow falliscono la build se trovano segreti o vulnerabilità high/critical: spiegare all'utente che è voluto. + +## Limite da comunicare +Questi workflow proteggono il codice e le dipendenze, non bloccano gli attacchi al sito in esecuzione: per quello servono la skill difesa-attacchi (middleware) e un WAF/CDN davanti al sito. diff --git a/plugins/web-security-guard/skills/sicurezza-github/references/dependabot.yml b/plugins/web-security-guard/skills/sicurezza-github/references/dependabot.yml new file mode 100644 index 0000000..24babc3 --- /dev/null +++ b/plugins/web-security-guard/skills/sicurezza-github/references/dependabot.yml @@ -0,0 +1,19 @@ +# .github/dependabot.yml +# PR automatiche per aggiornare dipendenze vulnerabili. +version: 2 +updates: + - package-ecosystem: "npm" # rimuovere se non è un progetto Node + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + + - package-ecosystem: "pip" # rimuovere se non è un progetto Python + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/plugins/web-security-guard/skills/sicurezza-github/references/security-workflow.yml b/plugins/web-security-guard/skills/sicurezza-github/references/security-workflow.yml new file mode 100644 index 0000000..129e076 --- /dev/null +++ b/plugins/web-security-guard/skills/sicurezza-github/references/security-workflow.yml @@ -0,0 +1,62 @@ +# .github/workflows/security.yml +# Scansione di sicurezza automatica: segreti, dipendenze, analisi statica. +name: Security + +on: + push: + branches: [main, master] + pull_request: + schedule: + - cron: '0 3 * * *' # ogni notte alle 03:00 UTC + +permissions: + contents: read + security-events: write + +jobs: + secret-scan: + name: Scansione segreti (Gitleaks) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + dependency-audit: + name: Audit dipendenze + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # --- Node: rimuovere se il progetto non ha package.json --- + - uses: actions/setup-node@v4 + if: ${{ hashFiles('package.json') != '' }} + with: { node-version: 20 } + - name: npm audit + if: ${{ hashFiles('package-lock.json') != '' }} + run: npm audit --audit-level=high + # --- Python: rimuovere se il progetto non ha requirements --- + - uses: actions/setup-python@v5 + if: ${{ hashFiles('requirements.txt') != '' }} + with: { python-version: '3.12' } + - name: pip-audit + if: ${{ hashFiles('requirements.txt') != '' }} + run: | + pip install pip-audit + pip-audit -r requirements.txt + + codeql: + name: Analisi statica (CodeQL) + runs-on: ubuntu-latest + strategy: + matrix: + language: [javascript] # aggiungere/cambiare: python, java, ecc. + steps: + - uses: actions/checkout@v4 + - uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: security-extended + - uses: github/codeql-action/analyze@v3 From f583549d97637d8743c73dc7d9715dd2a3235d66 Mon Sep 17 00:00:00 2001 From: ayalaphiscan Date: Wed, 10 Jun 2026 22:18:01 +0200 Subject: [PATCH 2/2] Sync web-security-guard README (Built with Claude Fable 5 credit) Co-Authored-By: Claude Fable 5 --- plugins/web-security-guard/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/web-security-guard/README.md b/plugins/web-security-guard/README.md index 6777949..8f0abc7 100644 --- a/plugins/web-security-guard/README.md +++ b/plugins/web-security-guard/README.md @@ -3,6 +3,7 @@ [![Claude Code Plugin](https://img.shields.io/badge/Claude%20Code-plugin-d97757)](https://docs.claude.com/en/docs/claude-code) [![Version](https://img.shields.io/badge/version-0.2.1-blue)](https://github.com/ayalaphiscan/web-security-guard/releases) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) +[![Built with Claude Fable 5](https://img.shields.io/badge/built%20with-Claude%20Fable%205-d97757)](https://claude.com/claude-code) A security plugin for [Claude Code](https://docs.claude.com/en/docs/claude-code) and Claude Cowork that turns Claude into a security-aware engineer. It bundles 6 skills, 2 slash commands and ready-to-deploy infrastructure templates covering the full lifecycle: hardening, authentication, payments privacy, active defense, stealth architecture and CI security. @@ -50,3 +51,7 @@ The public web port can never be invisible — what can be made invisible are SS ## License [MIT](LICENSE) — © ayalaphiscan (Fede) + +--- + +🤖 Built with **Claude Fable 5** via [Claude Code](https://claude.com/claude-code) — skills, templates and this very README were developed together with the model the plugin runs on.